Upgrade to SF4
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
24
.env.dist
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# This file is a "template" of which env vars need to be defined for your application
|
||||
# Copy this file to .env file for development, create environment variables when deploying to production
|
||||
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_ENV=dev
|
||||
APP_SECRET=e168e005fbbc528f5e4f8b75a0dffce8
|
||||
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
|
||||
#TRUSTED_HOSTS=localhost,example.com
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
###> symfony/swiftmailer-bundle ###
|
||||
# For Gmail as a transport, use: "gmail://username:password@localhost"
|
||||
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
|
||||
# Delivery is disabled by default via "null://localhost"
|
||||
MAILER_URL=null://localhost
|
||||
###< symfony/swiftmailer-bundle ###
|
||||
|
||||
###> doctrine/doctrine-bundle ###
|
||||
# Format described at http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
|
||||
# Configure your db driver and server_version in config/packages/doctrine.yaml
|
||||
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
|
||||
###< doctrine/doctrine-bundle ###
|
||||
33
.gitignore
vendored
|
|
@ -6,21 +6,30 @@ composer.phar
|
|||
framanav
|
||||
nav
|
||||
app/inc/config.php
|
||||
vendor
|
||||
cache/
|
||||
tpl_c/
|
||||
.php_cs.cache
|
||||
.zanata-cache/
|
||||
|
||||
# Temp files
|
||||
*~
|
||||
\#*\#
|
||||
cover/
|
||||
|
||||
# Cache
|
||||
Thumbs.db
|
||||
###> symfony/framework-bundle ###
|
||||
.env
|
||||
/public/bundles/
|
||||
/var/
|
||||
/vendor/
|
||||
###< symfony/framework-bundle ###
|
||||
|
||||
# IDE
|
||||
.settings/
|
||||
.project
|
||||
.idea/
|
||||
*.iml
|
||||
###> symfony/webpack-encore-pack ###
|
||||
/node_modules/
|
||||
/public/build/
|
||||
###< symfony/webpack-encore-pack ###
|
||||
|
||||
###> symfony/phpunit-bridge ###
|
||||
.phpunit
|
||||
/phpunit.xml
|
||||
###< symfony/phpunit-bridge ###
|
||||
|
||||
###> friendsofphp/php-cs-fixer ###
|
||||
/.php_cs
|
||||
/.php_cs.cache
|
||||
###< friendsofphp/php-cs-fixer ###
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
image: framasoft/framadate-ci
|
||||
image: tcitworld/framadate-sf4-ci
|
||||
stages:
|
||||
- test
|
||||
- deploy
|
||||
|
|
@ -8,11 +8,29 @@ stages:
|
|||
# Run php-cs-fixer and phpunit on all branches
|
||||
test:
|
||||
stage: test
|
||||
services:
|
||||
- mysql:latest
|
||||
variables:
|
||||
MYSQL_DATABASE: framadate
|
||||
MYSQL_ROOT_PASSWORD: framadate_password
|
||||
APP_ENV: test
|
||||
APP_SECRET: e168e005fbbc9b6f5e4f8b15a0dffce8
|
||||
DATABASE_URL: mysql://root:framadate_password@mysql/framadate
|
||||
APP_LOCALE: fr_FR
|
||||
APP_NAME: Framadate
|
||||
APP_ADMIN_EMAIL: tcit@tcit.fr
|
||||
APP_SEND_EMAILS: "true"
|
||||
APP_EMAIL_FROM_NAME: admin
|
||||
APP_EMAIL_FROM_EMAIL: admin@domain.tld
|
||||
APP_DEFAULT_POLL_DURATION: 180
|
||||
script:
|
||||
- npm i
|
||||
- ./node_modules/.bin/encore production
|
||||
- composer install -o --no-interaction --no-progress --prefer-dist
|
||||
- mkdir tpl_c
|
||||
- php vendor/bin/php-cs-fixer fix --verbose --dry-run
|
||||
- vendor/bin/phpunit --bootstrap app/tests/bootstrap.php --debug app/tests
|
||||
- php vendor/bin/php-cs-fixer fix --verbose --dry-run src/
|
||||
- bin/console doctrine:migrations:migrate --env="test"
|
||||
- bin/console framadate:tests:fixtures --env="test"
|
||||
- bin/phpunit --debug --coverage-text --colors=never
|
||||
cache:
|
||||
paths:
|
||||
- vendor/
|
||||
|
|
|
|||
45
.php_cs
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
return PhpCsFixer\Config::create()
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'combine_consecutive_unsets' => true,
|
||||
'heredoc_to_nowdoc' => true,
|
||||
'no_extra_consecutive_blank_lines' => [
|
||||
'break',
|
||||
'continue',
|
||||
'extra',
|
||||
'return',
|
||||
'throw',
|
||||
'use',
|
||||
'parenthesis_brace_block',
|
||||
'square_brace_block',
|
||||
'curly_brace_block'
|
||||
],
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_useless_return' => true,
|
||||
'ordered_class_elements' => true,
|
||||
'ordered_imports' => true,
|
||||
'php_unit_strict' => true,
|
||||
'phpdoc_order' => true,
|
||||
// 'psr4' => true,
|
||||
'strict_comparison' => true,
|
||||
'strict_param' => true,
|
||||
'concat_space' => [
|
||||
'spacing' => 'one'
|
||||
],
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude([
|
||||
'vendor',
|
||||
'var',
|
||||
'web'
|
||||
])
|
||||
->in(__DIR__)
|
||||
)
|
||||
;
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
use Framadate\Message;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\NotificationService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SecurityService;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
/* Variables */
|
||||
/* --------- */
|
||||
|
||||
$poll_id = null;
|
||||
$poll = null;
|
||||
$message = null;
|
||||
$result = false;
|
||||
$comments = [];
|
||||
$is_admin = false;
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$securityService = new SecurityService();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
||||
if (!empty($_POST['poll'])) {
|
||||
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
|
||||
if (!empty($_POST['poll_admin'])) {
|
||||
$admin_poll_id = filter_input(INPUT_POST, 'poll_admin', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (strlen($admin_poll_id) === 24) {
|
||||
$is_admin = ($pollService->findByAdminId($admin_poll_id) !== null);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$poll) {
|
||||
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
|
||||
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) {
|
||||
$message = new Message('error', __('Password', 'Wrong password'));
|
||||
} else {
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$comment = $inputService->filterComment($_POST['comment']);
|
||||
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Add comment
|
||||
$result = $pollService->addComment($poll_id, $name, $comment);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('Comments', 'Comment added'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Comment failed'));
|
||||
}
|
||||
}
|
||||
$comments = $pollService->allCommentsByPollId($poll_id);
|
||||
}
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
$smarty->assign('comments', $comments);
|
||||
$comments_html = $smarty->fetch('part/comments_list.tpl');
|
||||
|
||||
$response = ['result' => $result, 'message' => $message, 'comments' => $comments_html];
|
||||
|
||||
echo json_encode($response);
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
use Framadate\Message;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
$logService = new LogService();
|
||||
$sessionService = new SessionService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$pollService = new PollService($connect, $logService);
|
||||
|
||||
$result = false;
|
||||
$message = null;
|
||||
$poll = null;
|
||||
$poll_id = null;
|
||||
$email = null;
|
||||
|
||||
if (!empty($_POST['poll'])) {
|
||||
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
|
||||
$token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$token_form_value = empty($_POST['token']) ? null : $_POST['token'];
|
||||
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (is_null($poll) || $config['use_smtp'] === false || is_null($token) || is_null($token_form_value)
|
||||
|| !$token->check($token_form_value) || is_null($editedVoteUniqueId)) {
|
||||
$message = new Message('error', __('Error', 'Something is going wrong...'));
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$email = $mailService->isValidEmail($_POST['email']);
|
||||
if (is_null($email)) {
|
||||
$message = new Message('error', __('EditLink', 'The email address is not correct.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$time = $sessionService->get("Common", SESSION_EDIT_LINK_TIME);
|
||||
|
||||
if (!empty($time)) {
|
||||
$remainingTime = TIME_EDIT_LINK_EMAIL - (time() - $time);
|
||||
|
||||
if ($remainingTime > 0) {
|
||||
$message = new Message('error', __f('EditLink', 'Please wait %d seconds before we can send an email to you then try again.', $remainingTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$url = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
|
||||
|
||||
$smarty->assign('poll', $poll);
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('editedVoteUniqueId', $editedVoteUniqueId);
|
||||
$body = $smarty->fetch('mail/remember_edit_link.tpl');
|
||||
|
||||
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
|
||||
|
||||
$mailService->send($email, $subject, $body);
|
||||
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());
|
||||
|
||||
$message = new Message('success', __('EditLink', 'Your reminder has been successfully sent!'));
|
||||
$result = true;
|
||||
}
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
|
||||
$response = ['result' => $result, 'message' => $message];
|
||||
|
||||
echo json_encode($response);
|
||||
|
|
@ -48,7 +48,7 @@ $ALLOWED_LANGUAGES = [
|
|||
'br' => 'Brezhoneg',
|
||||
];
|
||||
const DEFAULT_LANGUAGE = 'en';
|
||||
require_once ROOT_DIR . 'app/inc/i18n.php';
|
||||
require_once ROOT_DIR . 'app/inc/I18nWrapper.php';
|
||||
|
||||
/**
|
||||
* Function to sort messages by type (priorise errors on warning, warning on info, etc.)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
require_once '../app/inc/init.php';
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Administration'));
|
||||
$smarty->assign('logsAreReadable', is_readable('../' . LOG_FILE));
|
||||
$smarty->display('admin/index.tpl');
|
||||
echo $twig->render('admin/index.twig', [
|
||||
'title' => __('Admin', 'Administration'),
|
||||
'logsAreReadable' => is_readable('../' . LOG_FILE),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -32,16 +32,17 @@ $installService = new InstallService();
|
|||
|
||||
if (!empty($_POST)) {
|
||||
$installService->updateFields($_POST);
|
||||
$result = $installService->install($smarty);
|
||||
$result = $installService->install($twig);
|
||||
|
||||
if ($result['status'] === 'OK') {
|
||||
header(('Location: ' . Utils::get_server_name() . 'admin/migration.php'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
$error = __('Error', $result['code']);
|
||||
}
|
||||
|
||||
$smarty->assign('error', $error);
|
||||
$smarty->assign('title', __('Admin', 'Installation'));
|
||||
$smarty->assign('fields', $installService->getFields());
|
||||
$smarty->display('admin/install.tpl');
|
||||
echo $twig->render('admin/install.twig', [
|
||||
'error' => $error,
|
||||
'title' => __('Admin', 'Installation'),
|
||||
'fields' => $installService->getFields(),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ ob_start();
|
|||
is_readable('../' . LOG_FILE) ? readfile('../' . LOG_FILE) : null;
|
||||
$content = ob_get_clean();
|
||||
|
||||
$smarty->assign('logs', $content);
|
||||
$smarty->assign('title', __('Admin', 'Logs'));
|
||||
|
||||
$smarty->display('admin/logs.tpl');
|
||||
echo $twig->render('admin/logs.twig', [
|
||||
'logs' => $content,
|
||||
'title' => __('Admin', 'Logs'),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9;
|
||||
use Framadate\Migration\AddColumn_receiveNewComments_For_0_9;
|
||||
use Framadate\Migration\AddColumn_uniqId_In_vote_For_0_9;
|
||||
use Framadate\Migration\AddColumn_ValueMax_In_poll_For_1_1;
|
||||
use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9;
|
||||
use Framadate\Migration\Alter_Comment_table_adding_date;
|
||||
use Framadate\Migration\Alter_Comment_table_for_name_length;
|
||||
use Framadate\Migration\Fix_MySQL_No_Zero_Date;
|
||||
use Framadate\Migration\From_0_0_to_0_8_Migration;
|
||||
use Framadate\Migration\From_0_8_to_0_9_Migration;
|
||||
use Framadate\Migration\Generate_uniqId_for_old_votes;
|
||||
use Framadate\Migration\Increase_pollId_size;
|
||||
use Framadate\Migration\Migration;
|
||||
use Framadate\Migration\RPadVotes_from_0_8;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
set_time_limit(300);
|
||||
|
||||
// List a Migration sub classes to execute
|
||||
$migrations = [
|
||||
new From_0_0_to_0_8_Migration(),
|
||||
new From_0_8_to_0_9_Migration(),
|
||||
new AddColumn_receiveNewComments_For_0_9(),
|
||||
new AddColumn_uniqId_In_vote_For_0_9(),
|
||||
new AddColumn_hidden_In_poll_For_0_9(),
|
||||
new AddColumn_ValueMax_In_poll_For_1_1(),
|
||||
new Generate_uniqId_for_old_votes(),
|
||||
new RPadVotes_from_0_8(),
|
||||
new Alter_Comment_table_for_name_length(),
|
||||
new Alter_Comment_table_adding_date(),
|
||||
new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(),
|
||||
new Increase_pollId_size(),
|
||||
new AddColumn_ValueMax_In_poll_For_1_1(),
|
||||
new Fix_MySQL_No_Zero_Date(),
|
||||
];
|
||||
// ---------------------------------------
|
||||
|
||||
// Check if MIGRATION_TABLE already exists
|
||||
/** @var \Framadate\FramaDB $connect */
|
||||
$tables = $connect->allTables();
|
||||
$pdo = $connect->getPDO();
|
||||
$prefixedMigrationTable = Utils::table(MIGRATION_TABLE);
|
||||
|
||||
if (!in_array($prefixedMigrationTable, $tables, true)) {
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . $prefixedMigrationTable . '` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` TEXT NOT NULL,
|
||||
`execute_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
ENGINE = MyISAM
|
||||
DEFAULT CHARSET = utf8;');
|
||||
}
|
||||
|
||||
$selectStmt = $pdo->prepare('SELECT id FROM `' . $prefixedMigrationTable . '` WHERE name=?');
|
||||
$insertStmt = $pdo->prepare('INSERT INTO `' . $prefixedMigrationTable . '` (name) VALUES (?)');
|
||||
$countSucceeded = 0;
|
||||
$countFailed = 0;
|
||||
$countSkipped = 0;
|
||||
|
||||
// Loop on every Migration sub classes
|
||||
$success = [];
|
||||
$fail = [];
|
||||
foreach ($migrations as $migration) {
|
||||
$className = get_class($migration);
|
||||
|
||||
// Check if $className is a Migration sub class
|
||||
if (!$migration instanceof Migration) {
|
||||
$smarty->assign('error', 'The class ' . $className . ' is not a sub class of Framadate\\Migration\\Migration.');
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check if the Migration is already executed
|
||||
$selectStmt->execute([$className]);
|
||||
$executed = $selectStmt->rowCount();
|
||||
$selectStmt->closeCursor();
|
||||
|
||||
if (!$executed && $migration->preCondition($pdo)) {
|
||||
$migration->execute($pdo);
|
||||
if ($insertStmt->execute([$className])) {
|
||||
$countSucceeded++;
|
||||
$success[] = $migration->description();
|
||||
} else {
|
||||
$countFailed++;
|
||||
$fail[] = $migration->description();
|
||||
}
|
||||
} else {
|
||||
$countSkipped++;
|
||||
}
|
||||
}
|
||||
|
||||
$countTotal = $countSucceeded + $countFailed + $countSkipped;
|
||||
|
||||
$smarty->assign('success', $success);
|
||||
$smarty->assign('fail', $fail);
|
||||
|
||||
$smarty->assign('countSucceeded', $countSucceeded);
|
||||
$smarty->assign('countFailed', $countFailed);
|
||||
$smarty->assign('countSkipped', $countSkipped);
|
||||
$smarty->assign('countTotal', $countTotal);
|
||||
$smarty->assign('time', $total_time = round((microtime(true)-$_SERVER['REQUEST_TIME_FLOAT']), 4));
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Migration'));
|
||||
|
||||
$smarty->display('admin/migration.tpl');
|
||||
|
|
@ -86,16 +86,15 @@ $count = $found['count'];
|
|||
$total = $found['total'];
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('polls', $polls);
|
||||
$smarty->assign('count', $count);
|
||||
$smarty->assign('total', $total);
|
||||
$smarty->assign('page', $page);
|
||||
$smarty->assign('pages', ceil($count / POLLS_PER_PAGE));
|
||||
$smarty->assign('poll_to_delete', $poll_to_delete);
|
||||
$smarty->assign('crsf', $securityService->getToken('admin'));
|
||||
$smarty->assign('search', $search);
|
||||
$smarty->assign('search_query', buildSearchQuery($search));
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Polls'));
|
||||
|
||||
$smarty->display('admin/polls.tpl');
|
||||
echo $twig->render('admin/polls.twig', [
|
||||
'polls' => $polls,
|
||||
'count' => $count,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'pages' => ceil($count / POLLS_PER_PAGE),
|
||||
'poll_to_delete' => $poll_to_delete,
|
||||
'crsf' => $securityService->getToken('admin'),
|
||||
'search' => $search,
|
||||
'search_query' => buildSearchQuery($search),
|
||||
'title' => __('Admin', 'Polls'),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ if ($action === 'purge' && $securityService->checkCsrf('admin', $_POST['csrf']))
|
|||
}
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->assign('crsf', $securityService->getToken('admin'));
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Purge'));
|
||||
|
||||
$smarty->display('admin/purge.tpl');
|
||||
echo $twig->render('admin/purge.twig', [
|
||||
'message' => $message,
|
||||
'crsf' => $securityService->getToken('admin'),
|
||||
'title' => __('Admin', 'Purge'),
|
||||
]);
|
||||
|
|
|
|||
469
adminstuds.php
|
|
@ -1,469 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
use Framadate\Editable;
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Exception\MomentAlreadyExistsException;
|
||||
use Framadate\Message;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Services\AdminPollService;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\NotificationService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
/* Variables */
|
||||
/* --------- */
|
||||
|
||||
$admin_poll_id = null;
|
||||
$poll_id = null;
|
||||
$poll = null;
|
||||
$message = null;
|
||||
$editingVoteId = 0;
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$adminPollService = new AdminPollService($connect, $pollService, $logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$sessionService = new SessionService();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
||||
if (!empty($_GET['poll'])) {
|
||||
$admin_poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (strlen($admin_poll_id) === 24) {
|
||||
$poll = $pollService->findByAdminId($admin_poll_id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($poll) {
|
||||
$poll_id = $poll->id;
|
||||
} else {
|
||||
$smarty->assign('error', __('Error', 'This poll doesn\'t exist !'));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// creation message
|
||||
// -------------------------------
|
||||
|
||||
$messagePollCreated = $sessionService->get("Framadate", "messagePollCreated", FALSE);
|
||||
|
||||
if ($messagePollCreated) {
|
||||
$sessionService->remove("Framadate", "messagePollCreated");
|
||||
|
||||
$message = new Message('success', __('adminstuds', 'The poll is created.'));
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Update poll info
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['update_poll_info'])) {
|
||||
$updated = false;
|
||||
$field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description',
|
||||
'rules', 'expiration_date', 'name', 'hidden', 'removePassword', 'password']);
|
||||
|
||||
// Update the right poll field
|
||||
if ($field === 'title') {
|
||||
$title = $inputService->filterTitle($_POST['title']);
|
||||
if ($title) {
|
||||
$poll->title = $title;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'admin_mail') {
|
||||
$admin_mail = $inputService->filterMail($_POST['admin_mail']);
|
||||
if ($admin_mail) {
|
||||
$poll->admin_mail = $admin_mail;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'description') {
|
||||
$description = $inputService->filterDescription($_POST['description']);
|
||||
if ($description) {
|
||||
$poll->description = $description;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'rules') {
|
||||
$rules = strip_tags($_POST['rules']);
|
||||
switch ($rules) {
|
||||
case 0:
|
||||
$poll->active = false;
|
||||
$poll->editable = Editable::NOT_EDITABLE;
|
||||
$updated = true;
|
||||
break;
|
||||
case 1:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::NOT_EDITABLE;
|
||||
$updated = true;
|
||||
break;
|
||||
case 2:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::EDITABLE_BY_ALL;
|
||||
$updated = true;
|
||||
break;
|
||||
case 3:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::EDITABLE_BY_OWN;
|
||||
$updated = true;
|
||||
break;
|
||||
}
|
||||
} elseif ($field === 'expiration_date') {
|
||||
$expiration_date = $inputService->filterDate($_POST['expiration_date']);
|
||||
if ($expiration_date) {
|
||||
$poll->end_date = $expiration_date;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'name') {
|
||||
$admin_name = $inputService->filterName($_POST['name']);
|
||||
if ($admin_name) {
|
||||
$poll->admin_name = $admin_name;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'hidden') {
|
||||
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
|
||||
if ($hidden !== $poll->hidden) {
|
||||
$poll->hidden = $hidden;
|
||||
$poll->results_publicly_visible = false;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'removePassword') {
|
||||
$removePassword = isset($_POST['removePassword']) ? $inputService->filterBoolean($_POST['removePassword']) : false;
|
||||
if ($removePassword) {
|
||||
$poll->results_publicly_visible = false;
|
||||
$poll->password_hash = null;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'password') {
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
|
||||
/**
|
||||
* Did the user choose results to be publicly visible ?
|
||||
*/
|
||||
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false;
|
||||
/**
|
||||
* If there's one, save the password
|
||||
*/
|
||||
if (!empty($password)) {
|
||||
$poll->password_hash = PasswordHasher::hash($password);
|
||||
$updated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If not pasword was set and the poll should be hidden, hide the results
|
||||
*/
|
||||
if ($poll->password_hash === null || $poll->hidden === true) {
|
||||
$poll->results_publicly_visible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't have a password, the poll is hidden and we change the results public visibility
|
||||
*/
|
||||
if ($resultsPubliclyVisible !== $poll->results_publicly_visible && $poll->password_hash !== null && $poll->hidden === false) {
|
||||
$poll->results_publicly_visible = $resultsPubliclyVisible;
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update poll in database
|
||||
if ($updated && $adminPollService->updatePoll($poll)) {
|
||||
$message = new Message('success', __('adminstuds', 'Poll saved'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_POLL);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to save poll'));
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// A vote is going to be edited
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_GET['vote'])) {
|
||||
$editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Something to save (edit or add)
|
||||
// -------------------------------
|
||||
|
||||
$selectedNewVotes = [];
|
||||
|
||||
if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
|
||||
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
|
||||
$slots_hash = $inputService->filterMD5($_POST['control']);
|
||||
|
||||
if (empty($editedVote)) {
|
||||
$message = new Message('danger', __('Error', 'Something is going wrong...'));
|
||||
}
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Update vote
|
||||
try {
|
||||
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote updated'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Update vote failed'));
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', 'The name you\'ve chosen already exist in this poll!'));
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['save'])) { // Add a new vote
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
|
||||
$slots_hash = $inputService->filterMD5($_POST['control']);
|
||||
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Add vote
|
||||
try {
|
||||
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote added'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Adding vote failed'));
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', 'You already voted'));
|
||||
$selectedNewVotes = $choices;
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a votes
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_GET['delete_vote'])) {
|
||||
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BASE64_REGEX]]);
|
||||
$vote_id = Utils::base64url_decode($vote_id);
|
||||
if ($vote_id && $adminPollService->deleteVote($poll_id, $vote_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the vote!'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Remove all votes
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['remove_all_votes'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_votes.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_remove_all_votes'])) {
|
||||
if ($adminPollService->cleanVotes($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'All votes deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete all votes'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a comment
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_POST['delete_comment'])) {
|
||||
$comment_id = filter_input(INPUT_POST, 'delete_comment', FILTER_VALIDATE_INT);
|
||||
|
||||
if ($adminPollService->deleteComment($poll_id, $comment_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Comment deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the comment'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Remove all comments
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['remove_all_comments'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_comments.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_remove_all_comments'])) {
|
||||
if ($adminPollService->cleanComments($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'All comments deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete all comments'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete the entire poll
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['delete_poll'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_poll.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_delete_poll'])) {
|
||||
if ($adminPollService->deleteEntirePoll($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Poll fully deleted'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::DELETED_POLL);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the poll'));
|
||||
}
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->display('poll_deleted.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a slot
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_GET['delete_column'])) {
|
||||
$column = filter_input(INPUT_GET, 'delete_column', FILTER_DEFAULT);
|
||||
$column = Utils::base64url_decode($column);
|
||||
|
||||
if ($poll->format === 'D') {
|
||||
$ex = explode('@', $column);
|
||||
|
||||
$slot = new stdClass();
|
||||
$slot->title = $ex[0];
|
||||
$slot->moment = $ex[1];
|
||||
|
||||
$result = $adminPollService->deleteDateSlot($poll, $slot);
|
||||
} else {
|
||||
$result = $adminPollService->deleteClassicSlot($poll, $column);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Column removed'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete column'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Add a slot
|
||||
// -------------------------------
|
||||
|
||||
function exit_displaying_add_column($message = null) {
|
||||
global $smarty, $poll_id, $admin_poll_id, $poll;
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('format', $poll->format);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->display('add_column.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['add_column'])) {
|
||||
exit_displaying_add_column();
|
||||
}
|
||||
|
||||
if (isset($_POST['confirm_add_column'])) {
|
||||
try {
|
||||
if (($poll->format === 'D' && empty($_POST['newdate']))
|
||||
|| ($poll->format === 'A' && empty($_POST['choice']))) {
|
||||
exit_displaying_add_column(new Message('danger', __('Error', "Can't create an empty column.")));
|
||||
}
|
||||
if ($poll->format === 'D') {
|
||||
$date = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $_POST['newdate'])->setTime(0, 0, 0);
|
||||
$time = $date->getTimestamp();
|
||||
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
|
||||
$adminPollService->addDateSlot($poll_id, $time, $newmoment);
|
||||
} else {
|
||||
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
|
||||
$adminPollService->addClassicSlot($poll_id, $newslot);
|
||||
}
|
||||
|
||||
$message = new Message('success', __('adminstuds', 'Choice added'));
|
||||
} catch (MomentAlreadyExistsException $e) {
|
||||
exit_displaying_add_column(new Message('danger', __('Error', 'The column already exists')));
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve data
|
||||
$slots = $pollService->allSlotsByPoll($poll);
|
||||
$votes = $pollService->allVotesByPollId($poll_id);
|
||||
$comments = $pollService->allCommentsByPollId($poll_id);
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('poll', $poll);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('expired', strtotime($poll->end_date) < time());
|
||||
$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
|
||||
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
|
||||
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
|
||||
$smarty->assign('votes', $pollService->splitVotes($votes));
|
||||
$smarty->assign('best_choices', $pollService->computeBestChoices($votes, $poll));
|
||||
$smarty->assign('comments', $comments);
|
||||
$smarty->assign('editingVoteId', $editingVoteId);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->assign('admin', true);
|
||||
$smarty->assign('hidden', false);
|
||||
$smarty->assign('accessGranted', true);
|
||||
$smarty->assign('resultPubliclyVisible', true);
|
||||
$smarty->assign('editedVoteUniqueId', '');
|
||||
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
|
||||
$smarty->assign('selectedNewVotes', $selectedNewVotes);
|
||||
|
||||
$smarty->display('studs.tpl');
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate;
|
||||
|
||||
class Choice
|
||||
{
|
||||
/**
|
||||
* Name of the Choice
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* All availables slots for this Choice.
|
||||
*/
|
||||
private $slots;
|
||||
|
||||
public function __construct($name='')
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->slots = [];
|
||||
}
|
||||
|
||||
public function addSlot($slot)
|
||||
{
|
||||
$this->slots[] = $slot;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getSlots()
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
static function compare(Choice $a, Choice $b)
|
||||
{
|
||||
return strcmp($a->name, $b->name);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class AlreadyExistsException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class ConcurrentEditionException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class MomentAlreadyExistsException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate;
|
||||
|
||||
class Form
|
||||
{
|
||||
public $title;
|
||||
public $id;
|
||||
public $description;
|
||||
public $admin_name;
|
||||
public $admin_mail;
|
||||
public $format;
|
||||
public $end_date;
|
||||
public $choix_sondage;
|
||||
public $ValueMax;
|
||||
|
||||
/**
|
||||
* Tells if users can modify their choices.
|
||||
* @var \Framadate\Editable
|
||||
*/
|
||||
public $editable;
|
||||
|
||||
/**
|
||||
* If true, notify poll administrator when new vote is made.
|
||||
*/
|
||||
public $receiveNewVotes;
|
||||
|
||||
/**
|
||||
* If true, notify poll administrator when new comment is posted.
|
||||
*/
|
||||
public $receiveNewComments;
|
||||
|
||||
/**
|
||||
* If true, only the poll maker can see the poll's results
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_ValueMax;
|
||||
|
||||
/**
|
||||
* if true, there will be a limit of voters per option
|
||||
* @var boolean
|
||||
*/
|
||||
public $hidden;
|
||||
|
||||
/**
|
||||
* If true, the author want to customize the URL
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_customized_url;
|
||||
|
||||
/**
|
||||
* If true, a password will be needed to access the poll
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_password;
|
||||
|
||||
/**
|
||||
* The password needed to access the poll, hashed. Only used if $use_password is set to true
|
||||
* @var string
|
||||
*/
|
||||
public $password_hash;
|
||||
|
||||
/**
|
||||
* If true, the polls results will be also visible for those without password
|
||||
* @var boolean
|
||||
*/
|
||||
public $results_publicly_visible;
|
||||
|
||||
/**
|
||||
* List of available choices
|
||||
*/
|
||||
private $choices;
|
||||
|
||||
public function __construct(){
|
||||
$this->editable = Editable::EDITABLE_BY_ALL;
|
||||
$this->clearChoices();
|
||||
}
|
||||
|
||||
public function clearChoices() {
|
||||
$this->choices = [];
|
||||
}
|
||||
|
||||
public function addChoice(Choice $choice)
|
||||
{
|
||||
$this->choices[] = $choice;
|
||||
}
|
||||
|
||||
public function getChoices()
|
||||
{
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
public function sortChoices()
|
||||
{
|
||||
usort($this->choices, ['Framadate\Choice', 'compare']);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate;
|
||||
|
||||
use PDO;
|
||||
|
||||
class FramaDB {
|
||||
/**
|
||||
* PDO Object, connection to database.
|
||||
*/
|
||||
private $pdo = null;
|
||||
|
||||
function __construct($connection_string, $user, $password) {
|
||||
$this->pdo = new \PDO($connection_string, $user, $password);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PDO Connection to database
|
||||
*/
|
||||
function getPDO() {
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all tables in database.
|
||||
*
|
||||
* @return array The array of table names
|
||||
*/
|
||||
function allTables() {
|
||||
$result = $this->pdo->query('SHOW TABLES');
|
||||
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
return $schemas;
|
||||
}
|
||||
|
||||
function prepare($sql) {
|
||||
return $this->pdo->prepare($sql);
|
||||
}
|
||||
|
||||
function beginTransaction() {
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
|
||||
function commit() {
|
||||
$this->pdo->commit();
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
|
||||
function errorCode() {
|
||||
return $this->pdo->errorCode();
|
||||
}
|
||||
|
||||
function errorInfo() {
|
||||
return $this->pdo->errorInfo();
|
||||
}
|
||||
|
||||
function query($sql) {
|
||||
return $this->pdo->query($sql);
|
||||
}
|
||||
|
||||
public function lastInsertId() {
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
|
||||
class Increase_pollId_size implements Migration {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
return 'Increase the size of id column in poll table';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @return bool true if the Migration should be executed
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @return bool true if the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
$this->alterCommentTable($pdo);
|
||||
$this->alterPollTable($pdo);
|
||||
$this->alterSlotTable($pdo);
|
||||
$this->alterVoteTable($pdo);
|
||||
}
|
||||
|
||||
private function alterCommentTable(\PDO $pdo) {
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('comment') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterSlotTable(\PDO $pdo) {
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('slot') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterVoteTable(\PDO $pdo) {
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('vote') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Migration;
|
||||
|
||||
interface Migration {
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description();
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @return bool true if the Migration should be executed
|
||||
*/
|
||||
function preCondition(\PDO $pdo);
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @return bool true if the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo);
|
||||
}
|
||||
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
|
||||
abstract class AbstractRepository {
|
||||
/**
|
||||
* @var FramaDB
|
||||
*/
|
||||
private $connect;
|
||||
|
||||
/**
|
||||
* PollRepository constructor.
|
||||
* @param FramaDB $connect
|
||||
*/
|
||||
function __construct(FramaDB $connect) {
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
public function beginTransaction() {
|
||||
$this->connect->beginTransaction();
|
||||
}
|
||||
|
||||
public function commit() {
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
$this->connect->rollback();
|
||||
}
|
||||
|
||||
public function prepare($sql) {
|
||||
return $this->connect->prepare($sql);
|
||||
}
|
||||
|
||||
function query($sql) {
|
||||
return $this->connect->query($sql);
|
||||
}
|
||||
|
||||
function lastInsertId() {
|
||||
return $this->connect->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
|
||||
class CommentRepository extends AbstractRepository {
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
function findAllByPollId($poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new comment.
|
||||
*
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $comment
|
||||
* @return bool
|
||||
*/
|
||||
function insert($poll_id, $name, $comment) {
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
|
||||
|
||||
return $prepared->execute([$poll_id, $name, $comment]);
|
||||
}
|
||||
|
||||
function deleteById($poll_id, $comment_id) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id, $comment_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all comments of a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the given poll.
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function deleteByPollId($poll_id) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
|
||||
public function exists($poll_id, $name, $comment) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?');
|
||||
$prepared->execute([$poll_id, $name, $comment]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,184 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
class PollRepository extends AbstractRepository {
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
public function insertPoll($poll_id, $admin_poll_id, $form) {
|
||||
$sql = 'INSERT INTO `' . Utils::table('poll') . '`
|
||||
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible,ValueMax)
|
||||
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?)';
|
||||
$prepared = $this->prepare($sql);
|
||||
$prepared->execute([$poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0, $form->receiveNewVotes ? 1 : 0, $form->receiveNewComments ? 1 : 0, $form->hidden ? 1 : 0, $form->password_hash, $form->results_publicly_visible ? 1 : 0,$form->ValueMax]);
|
||||
}
|
||||
|
||||
function findById($poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
$prepared->execute([$poll_id]);
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
|
||||
|
||||
$prepared->execute([$admin_poll_id]);
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
public function existsById($poll_id) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
public function existsByAdminId($admin_poll_id) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
|
||||
|
||||
$prepared->execute([$admin_poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
function update($poll) {
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=?, password_hash=?, results_publicly_visible=? WHERE id = ?');
|
||||
|
||||
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]);
|
||||
}
|
||||
|
||||
function deleteById($poll_id) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find old polls. Limit: 20.
|
||||
*
|
||||
* @return array Array of old polls
|
||||
*/
|
||||
public function findOldPolls() {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE DATE_ADD(`end_date`, INTERVAL ' . PURGE_DELAY . ' DAY) < NOW() AND `end_date` != 0 LIMIT 20');
|
||||
$prepared->execute([]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search polls in database.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
|
||||
* @param int $start The number of first entry to select
|
||||
* @param int $limit The number of entries to find
|
||||
* @return array The found polls
|
||||
*/
|
||||
public function findAll($search, $start, $limit) {
|
||||
// Polls
|
||||
|
||||
$request = "";
|
||||
$request .= "SELECT p.*,";
|
||||
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
|
||||
$request .= " FROM `" . Utils::table('poll') . "` p";
|
||||
$request .= " WHERE 1";
|
||||
|
||||
$values = [];
|
||||
|
||||
if (!empty($search["poll"])) {
|
||||
$request .= " AND p.id LIKE :poll";
|
||||
$values["poll"] = "{$search["poll"]}%";
|
||||
}
|
||||
|
||||
$fields = [
|
||||
// key of $search => column name
|
||||
"title" => "title",
|
||||
"name" => "admin_name",
|
||||
"mail" => "admin_mail",
|
||||
];
|
||||
|
||||
foreach ($fields as $searchKey => $columnName) {
|
||||
if (empty($search[$searchKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$request .= " AND p.$columnName LIKE :$searchKey";
|
||||
$values[$searchKey] = "%{$search[$searchKey]}%";
|
||||
}
|
||||
|
||||
$request .= " ORDER BY p.title ASC";
|
||||
$request .= " LIMIT :start, :limit";
|
||||
|
||||
$prepared = $this->prepare($request);
|
||||
|
||||
foreach ($values as $searchKey => $value) {
|
||||
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
|
||||
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||
|
||||
$prepared->execute();
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all polls that are created with the given admin mail.
|
||||
*
|
||||
* @param string $mail Email address of the poll admin
|
||||
* @return array The list of matching polls
|
||||
*/
|
||||
public function findAllByAdminMail($mail) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
|
||||
$prepared->execute(['admin_mail' => $mail]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of polls in databse.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
|
||||
* @return int The number of polls
|
||||
*/
|
||||
public function count($search = null) {
|
||||
// Total count
|
||||
$prepared = $this->prepare('
|
||||
SELECT count(1) nb
|
||||
FROM `' . Utils::table('poll') . '` p
|
||||
WHERE (:id = "" OR p.id LIKE :id)
|
||||
AND (:title = "" OR p.title LIKE :title)
|
||||
AND (:name = "" OR p.admin_name LIKE :name)
|
||||
ORDER BY p.title ASC');
|
||||
|
||||
$poll = $search === null ? '' : $search['poll'] . '%';
|
||||
$title = $search === null ? '' : '%' . $search['title'] . '%';
|
||||
$name = $search === null ? '' : '%' . $search['name'] . '%';
|
||||
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
|
||||
|
||||
$prepared->execute();
|
||||
$count = $prepared->fetch();
|
||||
|
||||
/*echo '---';
|
||||
print_r($count);
|
||||
echo '---';
|
||||
exit;*/
|
||||
|
||||
return $count->nb;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
|
||||
class RepositoryFactory {
|
||||
private static $connect;
|
||||
|
||||
private static $pollRepository;
|
||||
private static $slotRepository;
|
||||
private static $voteRepository;
|
||||
private static $commentRepository;
|
||||
|
||||
/**
|
||||
* @param FramaDB $connect
|
||||
*/
|
||||
static function init(FramaDB $connect) {
|
||||
self::$connect = $connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PollRepository The singleton of PollRepository
|
||||
*/
|
||||
static function pollRepository() {
|
||||
if (self::$pollRepository === null) {
|
||||
self::$pollRepository = new PollRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$pollRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SlotRepository The singleton of SlotRepository
|
||||
*/
|
||||
static function slotRepository() {
|
||||
if (self::$slotRepository === null) {
|
||||
self::$slotRepository = new SlotRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$slotRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VoteRepository The singleton of VoteRepository
|
||||
*/
|
||||
static function voteRepository() {
|
||||
if (self::$voteRepository === null) {
|
||||
self::$voteRepository = new VoteRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$voteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommentRepository The singleton of CommentRepository
|
||||
*/
|
||||
static function commentRepository() {
|
||||
if (self::$commentRepository === null) {
|
||||
self::$commentRepository = new CommentRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$commentRepository;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
|
||||
class SlotRepository extends AbstractRepository {
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a bulk of slots.
|
||||
*
|
||||
* @param int $poll_id
|
||||
* @param array $choices
|
||||
*/
|
||||
public function insertSlots($poll_id, $choices) {
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
|
||||
|
||||
foreach ($choices as $choice) {
|
||||
// We prepared the slots (joined by comas)
|
||||
$joinedSlots = '';
|
||||
$first = true;
|
||||
foreach ($choice->getSlots() as $slot) {
|
||||
if ($first) {
|
||||
$joinedSlots = $slot;
|
||||
$first = false;
|
||||
} else {
|
||||
$joinedSlots .= ',' . $slot;
|
||||
}
|
||||
}
|
||||
|
||||
// We execute the insertion
|
||||
if (empty($joinedSlots)) {
|
||||
$prepared->execute([$poll_id, $choice->getName(), null]);
|
||||
} else {
|
||||
$prepared->execute([$poll_id, $choice->getName(), $joinedSlots]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listByPollId($poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the slot into poll for a given datetime.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $datetime int The datetime of the slot
|
||||
* @return mixed Object The slot found, or null
|
||||
*/
|
||||
function findByPollIdAndDatetime($poll_id, $datetime) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
|
||||
|
||||
$prepared->execute([$poll_id, $datetime]);
|
||||
$slot = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new slot into a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $title mixed The title of the slot
|
||||
* @param $moments mixed|null The moments joined with ","
|
||||
* @return bool true if action succeeded
|
||||
*/
|
||||
function insert($poll_id, $title, $moments) {
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
|
||||
|
||||
return $prepared->execute([$poll_id, $title, $moments]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a slot into a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $datetime int The datetime of the slot to update
|
||||
* @param $newMoments mixed The new moments
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function update($poll_id, $datetime, $newMoments) {
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
|
||||
|
||||
return $prepared->execute([$newMoments, $poll_id, $datetime]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a entire slot from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $datetime mixed The datetime of the slot
|
||||
*/
|
||||
function deleteByDateTime($poll_id, $datetime) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
|
||||
$prepared->execute([$poll_id, $datetime]);
|
||||
}
|
||||
|
||||
function deleteByPollId($poll_id) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Exception\MomentAlreadyExistsException;
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* Class AdminPollService
|
||||
*
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class AdminPollService {
|
||||
private $connect;
|
||||
private $pollService;
|
||||
private $logService;
|
||||
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
$this->pollService = $pollService;
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
function updatePoll($poll) {
|
||||
global $config;
|
||||
if ($poll->end_date > $poll->creation_date) {
|
||||
return $this->pollRepository->update($poll);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a comment from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $comment_id int The ID of the comment
|
||||
* @return mixed true is action succeeded
|
||||
*/
|
||||
function deleteComment($poll_id, $comment_id) {
|
||||
return $this->commentRepository->deleteById($poll_id, $comment_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all comments of a poll.
|
||||
*
|
||||
* @param $poll_id int The ID a the poll
|
||||
* @return bool|null true is action succeeded
|
||||
*/
|
||||
function cleanComments($poll_id) {
|
||||
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
|
||||
return $this->commentRepository->deleteByPollId($poll_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a vote from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $vote_id int The ID of the vote
|
||||
* @return mixed true is action succeeded
|
||||
*/
|
||||
function deleteVote($poll_id, $vote_id) {
|
||||
return $this->voteRepository->deleteById($poll_id, $vote_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all votes of a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return bool|null true is action succeeded
|
||||
*/
|
||||
function cleanVotes($poll_id) {
|
||||
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
|
||||
return $this->voteRepository->deleteByPollId($poll_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the entire given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function deleteEntirePoll($poll_id) {
|
||||
$poll = $this->pollRepository->findById($poll_id);
|
||||
$this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
|
||||
|
||||
// Delete the entire poll
|
||||
$this->voteRepository->deleteByPollId($poll_id);
|
||||
$this->commentRepository->deleteByPollId($poll_id);
|
||||
$this->slotRepository->deleteByPollId($poll_id);
|
||||
$this->pollRepository->deleteById($poll_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a slot from a poll.
|
||||
*
|
||||
* @param object $poll The ID of the poll
|
||||
* @param object $slot The slot informations (datetime + moment)
|
||||
* @return bool true if action succeeded
|
||||
*/
|
||||
public function deleteDateSlot($poll, $slot) {
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
|
||||
|
||||
$datetime = $slot->title;
|
||||
$moment = $slot->moment;
|
||||
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
|
||||
// We can't delete the last slot
|
||||
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
|
||||
return false;
|
||||
} elseif ($poll->format === 'A' && count($slots) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$index = 0;
|
||||
$indexToDelete = -1;
|
||||
$newMoments = [];
|
||||
|
||||
// Search the index of the slot to delete
|
||||
foreach ($slots as $aSlot) {
|
||||
$moments = explode(',', $aSlot->moments);
|
||||
|
||||
foreach ($moments as $rowMoment) {
|
||||
if ($datetime === $aSlot->title) {
|
||||
if ($moment === $rowMoment) {
|
||||
$indexToDelete = $index;
|
||||
} else {
|
||||
$newMoments[] = $rowMoment;
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove votes
|
||||
$this->connect->beginTransaction();
|
||||
$this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
|
||||
if (count($newMoments) > 0) {
|
||||
$this->slotRepository->update($poll->id, $datetime, implode(',', $newMoments));
|
||||
} else {
|
||||
$this->slotRepository->deleteByDateTime($poll->id, $datetime);
|
||||
}
|
||||
$this->connect->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteClassicSlot($poll, $slot_title) {
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
|
||||
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
|
||||
if (count($slots) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$index = 0;
|
||||
$indexToDelete = -1;
|
||||
|
||||
// Search the index of the slot to delete
|
||||
foreach ($slots as $aSlot) {
|
||||
if ($slot_title === $aSlot->title) {
|
||||
$indexToDelete = $index;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
// Remove votes
|
||||
$this->connect->beginTransaction();
|
||||
$this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
|
||||
$this->slotRepository->deleteByDateTime($poll->id, $slot_title);
|
||||
$this->connect->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new slot to a date poll. And insert default values for user's votes.
|
||||
* <ul>
|
||||
* <li>Create a new slot if no one exists for the given date</li>
|
||||
* <li>Create a new moment if a slot already exists for the given date</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $datetime int The datetime
|
||||
* @param $new_moment string The moment's name
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
*/
|
||||
public function addDateSlot($poll_id, $datetime, $new_moment) {
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
|
||||
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
$result = $this->findInsertPosition($slots, $datetime);
|
||||
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
if ($result->slot !== null) {
|
||||
$slot = $result->slot;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
// Check if moment already exists (maybe not necessary)
|
||||
if (in_array($new_moment, $moments, true)) {
|
||||
throw new MomentAlreadyExistsException();
|
||||
}
|
||||
|
||||
// Update found slot
|
||||
$moments[] = $new_moment;
|
||||
$this->slotRepository->update($poll_id, $datetime, implode(',', $moments));
|
||||
} else {
|
||||
$this->slotRepository->insert($poll_id, $datetime, $new_moment);
|
||||
}
|
||||
|
||||
$this->voteRepository->insertDefault($poll_id, $result->insert);
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new slot to a classic poll. And insert default values for user's votes.
|
||||
* <ul>
|
||||
* <li>Create a new slot if no one exists for the given title</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $title int The title
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
*/
|
||||
public function addClassicSlot($poll_id, $title) {
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
|
||||
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
|
||||
// Check if slot already exists
|
||||
$titles = array_map(function ($slot) {
|
||||
return $slot->title;
|
||||
}, $slots);
|
||||
if (in_array($title, $titles, true)) {
|
||||
// The moment already exists
|
||||
throw new MomentAlreadyExistsException();
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
// New slot
|
||||
$this->slotRepository->insert($poll_id, $title, null);
|
||||
// Set default votes
|
||||
$this->voteRepository->insertDefault($poll_id, count($slots));
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method find where to insert a datatime+moment into a list of slots.<br/>
|
||||
* Return the {insert:X}, where X is the index of the moment into the whole poll (ex: X=0 => Insert to the first column).
|
||||
* Return {slot:Y}, where Y is not null if there is a slot existing for the given datetime.
|
||||
*
|
||||
* @param $slots array All the slots of the poll
|
||||
* @param $datetime int The datetime of the new slot
|
||||
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
|
||||
*/
|
||||
private function findInsertPosition($slots, $datetime) {
|
||||
$result = new \stdClass();
|
||||
$result->slot = null;
|
||||
$result->insert = 0;
|
||||
|
||||
// Sort slots before searching where to insert
|
||||
$this->pollService->sortSlorts($slots);
|
||||
|
||||
// Search where to insert new column
|
||||
foreach ($slots as $k=>$slot) {
|
||||
$rowDatetime = $slot->title;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
if ($datetime === $rowDatetime) {
|
||||
// Here we have to insert at the end of a slot
|
||||
$result->insert += count($moments);
|
||||
$result->slot = $slot;
|
||||
break;
|
||||
} elseif ($datetime < $rowDatetime) {
|
||||
// We have to insert before this slot
|
||||
break;
|
||||
}
|
||||
$result->insert += count($moments);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use PHPMailer;
|
||||
|
||||
class MailService {
|
||||
const DELAY_BEFORE_RESEND = 300;
|
||||
|
||||
const MAILSERVICE_KEY = 'mailservice';
|
||||
|
||||
private $smtp_allowed;
|
||||
|
||||
private $smtp_options = [];
|
||||
|
||||
private $logService;
|
||||
|
||||
function __construct($smtp_allowed, $smtp_options = []) {
|
||||
$this->logService = new LogService();
|
||||
$this->smtp_allowed = $smtp_allowed;
|
||||
if (true === is_array($smtp_options)) {
|
||||
$this->smtp_options = $smtp_options;
|
||||
}
|
||||
}
|
||||
|
||||
public function isValidEmail($email) {
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public function send($to, $subject, $body, $msgKey = null) {
|
||||
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
|
||||
$mail = new PHPMailer(true);
|
||||
$this->configureMailer($mail);
|
||||
|
||||
// From
|
||||
$mail->FromName = NOMAPPLICATION;
|
||||
$mail->From = ADRESSEMAILADMIN;
|
||||
if ($this->isValidEmail(ADRESSEMAILREPONSEAUTO)) {
|
||||
$mail->addReplyTo(ADRESSEMAILREPONSEAUTO);
|
||||
}
|
||||
|
||||
// To
|
||||
$mail->addAddress($to);
|
||||
|
||||
// Subject
|
||||
$mail->Subject = $subject;
|
||||
|
||||
// Bodies
|
||||
$body = $body . ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
|
||||
$mail->isHTML(true);
|
||||
$mail->msgHTML($body, ROOT_DIR, true);
|
||||
|
||||
// Build headers
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
|
||||
$mail->addCustomHeader('Return-Path', '<>');
|
||||
|
||||
// Send mail
|
||||
$mail->send();
|
||||
|
||||
// Log
|
||||
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
|
||||
|
||||
// Store the mail sending date
|
||||
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
|
||||
}
|
||||
}
|
||||
|
||||
public function canSendMsg($msgKey) {
|
||||
if ($msgKey === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
|
||||
$_SESSION[self::MAILSERVICE_KEY] = [];
|
||||
}
|
||||
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the mailer with the options
|
||||
*
|
||||
* @param PHPMailer $mailer
|
||||
*/
|
||||
private function configureMailer(PHPMailer $mailer) {
|
||||
$mailer->isSMTP();
|
||||
|
||||
$available_options = [
|
||||
'host' => 'Host',
|
||||
'auth' => 'SMTPAuth',
|
||||
'username' => 'Username',
|
||||
'password' => 'Password',
|
||||
'secure' => 'SMTPSecure',
|
||||
'port' => 'Port',
|
||||
];
|
||||
|
||||
foreach ($available_options as $config_option => $mailer_option) {
|
||||
if (true === isset($this->smtp_options[$config_option]) && false === empty($this->smtp_options[$config_option])) {
|
||||
$mailer->{$mailer_option} = $this->smtp_options[$config_option];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
use \stdClass;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Utils;
|
||||
|
||||
class NotificationService {
|
||||
const UPDATE_VOTE = 1;
|
||||
const ADD_VOTE = 2;
|
||||
const ADD_COMMENT = 3;
|
||||
const UPDATE_POLL = 10;
|
||||
const DELETED_POLL = 11;
|
||||
|
||||
private $mailService;
|
||||
|
||||
function __construct(MailService $mailService) {
|
||||
$this->mailService = $mailService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the poll admin to notify him about an update.
|
||||
*
|
||||
* @param $poll stdClass The poll
|
||||
* @param $name string The name user who triggered the notification
|
||||
* @param $type int cf: Constants on the top of this page
|
||||
*/
|
||||
function sendUpdateNotification(stdClass $poll, $type, $name='') {
|
||||
if (!isset($_SESSION['mail_sent'])) {
|
||||
$_SESSION['mail_sent'] = [];
|
||||
}
|
||||
|
||||
$isVoteAndCanSendIt = ($type === self::UPDATE_VOTE || $type === self::ADD_VOTE) && $poll->receiveNewVotes;
|
||||
$isCommentAndCanSendIt = $type === self::ADD_COMMENT && $poll->receiveNewComments;
|
||||
$isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT;
|
||||
|
||||
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
|
||||
if (self::isParticipation($type)) {
|
||||
$translationString = 'Poll\'s participation: %s';
|
||||
} else {
|
||||
$translationString = 'Notification of poll: %s';
|
||||
}
|
||||
|
||||
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', $translationString, $poll->title);
|
||||
|
||||
$message = '';
|
||||
|
||||
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
|
||||
$link = '<a href="' . $urlSondage . '">' . $urlSondage . '</a>' . "\n\n";
|
||||
|
||||
switch ($type) {
|
||||
case self::UPDATE_VOTE:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "updated a vote.\nYou can find your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::ADD_VOTE:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "filled a vote.\nYou can find your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::ADD_COMMENT:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "wrote a comment.\nYou can find your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::UPDATE_POLL:
|
||||
$message = __f('Mail', 'Someone just change your poll available at the following link %s.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
|
||||
break;
|
||||
case self::DELETED_POLL:
|
||||
$message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
$messageTypeKey = $type . '-' . $poll->id;
|
||||
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
|
||||
}
|
||||
}
|
||||
|
||||
function isParticipation($type)
|
||||
{
|
||||
return $type >= self::UPDATE_POLL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Form;
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Security\Token;
|
||||
|
||||
class PollService {
|
||||
private $connect;
|
||||
private $logService;
|
||||
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a poll from its ID.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return \stdClass|null The found poll, or null
|
||||
*/
|
||||
function findById($poll_id) {
|
||||
if (preg_match(POLL_REGEX, $poll_id)) {
|
||||
return $this->pollRepository->findById($poll_id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
|
||||
return $this->pollRepository->findByAdminId($admin_poll_id);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function allCommentsByPollId($poll_id) {
|
||||
return $this->commentRepository->findAllByPollId($poll_id);
|
||||
}
|
||||
|
||||
function allVotesByPollId($poll_id) {
|
||||
return $this->voteRepository->allUserVotesByPollId($poll_id);
|
||||
}
|
||||
|
||||
function allSlotsByPoll($poll) {
|
||||
$slots = $this->slotRepository->listByPollId($poll->id);
|
||||
if ($poll->format === 'D') {
|
||||
$this->sortSlorts($slots);
|
||||
}
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $vote_id
|
||||
* @param $name
|
||||
* @param $choices
|
||||
* @param $slots_hash
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @return bool
|
||||
*/
|
||||
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash) {
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
|
||||
|
||||
// Update vote
|
||||
$choices = implode($choices);
|
||||
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $choices
|
||||
* @param $slots_hash
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @return \stdClass
|
||||
*/
|
||||
function addVote($poll_id, $name, $choices, $slots_hash) {
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
|
||||
|
||||
// Insert new vote
|
||||
$choices = implode($choices);
|
||||
$token = $this->random(16);
|
||||
return $this->voteRepository->insert($poll_id, $name, $choices, $token);
|
||||
}
|
||||
|
||||
function addComment($poll_id, $name, $comment) {
|
||||
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->commentRepository->insert($poll_id, $name, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
function createPoll(Form $form) {
|
||||
// Generate poll IDs, loop while poll ID already exists
|
||||
|
||||
if (empty($form->id)) { // User want us to generate an id for him
|
||||
do {
|
||||
$poll_id = $this->random(16);
|
||||
} while ($this->pollRepository->existsById($poll_id));
|
||||
$admin_poll_id = $poll_id . $this->random(8);
|
||||
} else { // User have choosen the poll id
|
||||
$poll_id = $form->id;
|
||||
do {
|
||||
$admin_poll_id = $this->random(24);
|
||||
} while ($this->pollRepository->existsByAdminId($admin_poll_id));
|
||||
}
|
||||
|
||||
// Insert poll + slots
|
||||
$this->pollRepository->beginTransaction();
|
||||
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
|
||||
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
|
||||
$this->pollRepository->commit();
|
||||
|
||||
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
|
||||
|
||||
return [$poll_id, $admin_poll_id];
|
||||
}
|
||||
|
||||
public function findAllByAdminMail($mail) {
|
||||
return $this->pollRepository->findAllByAdminMail($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $votes
|
||||
* @param \stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
public function computeBestChoices($votes, $poll) {
|
||||
if (0 === count($votes)) {
|
||||
return $this->computeEmptyBestChoices($poll);
|
||||
}
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
|
||||
// if there are votes
|
||||
foreach ($votes as $vote) {
|
||||
$choices = str_split($vote->choices);
|
||||
foreach ($choices as $i => $choice) {
|
||||
if (!isset($result['y'][$i])) {
|
||||
$result['inb'][$i] = 0;
|
||||
$result['y'][$i] = 0;
|
||||
}
|
||||
if ($choice === "1") {
|
||||
$result['inb'][$i]++;
|
||||
}
|
||||
if ($choice === "2") {
|
||||
$result['y'][$i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function splitSlots($slots) {
|
||||
$splitted = [];
|
||||
foreach ($slots as $slot) {
|
||||
$obj = new \stdClass();
|
||||
$obj->day = $slot->title;
|
||||
$obj->moments = explode(',', $slot->moments);
|
||||
|
||||
$splitted[] = $obj;
|
||||
}
|
||||
|
||||
return $splitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $slots array The slots to hash
|
||||
* @return string The hash
|
||||
*/
|
||||
public function hashSlots($slots) {
|
||||
return md5(array_reduce($slots, function($carry, $item) {
|
||||
return $carry . $item->id . '@' . $item->moments . ';';
|
||||
}));
|
||||
}
|
||||
|
||||
function splitVotes($votes) {
|
||||
$splitted = [];
|
||||
foreach ($votes as $vote) {
|
||||
$obj = new \stdClass();
|
||||
$obj->id = $vote->id;
|
||||
$obj->name = $vote->name;
|
||||
$obj->uniqId = $vote->uniqId;
|
||||
$obj->choices = str_split($vote->choices);
|
||||
|
||||
$splitted[] = $obj;
|
||||
}
|
||||
|
||||
return $splitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The max timestamp allowed for expiry date
|
||||
*/
|
||||
public function maxExpiryDate() {
|
||||
global $config;
|
||||
return time() + (86400 * $config['default_poll_duration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The min timestamp allowed for expiry date
|
||||
*/
|
||||
public function minExpiryDate() {
|
||||
return time() + 86400;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function sortSlorts(&$slots) {
|
||||
uasort($slots, function ($a, $b) {
|
||||
return $a->title > $b->title;
|
||||
});
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
private function computeEmptyBestChoices($poll)
|
||||
{
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
// if there is no votes, calculates the number of slot
|
||||
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
|
||||
if ($poll->format === 'A') {
|
||||
// poll format classic
|
||||
|
||||
for ($i = 0; $i < count($slots); $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
} else {
|
||||
// poll format date
|
||||
|
||||
$slots = $this->splitSlots($slots);
|
||||
|
||||
foreach ($slots as $slot) {
|
||||
for ($i = 0; $i < count($slot->moments); $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function random($length) {
|
||||
return Token::getToken($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $choices
|
||||
* @param $poll_id
|
||||
* @param $slots_hash
|
||||
* @param $name
|
||||
* @param string $vote_id
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentVoteException
|
||||
* @throws ConcurrentEditionException
|
||||
*/
|
||||
private function checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id = FALSE) {
|
||||
// Check if vote already exists with the same name
|
||||
if (FALSE === $vote_id) {
|
||||
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
|
||||
} else {
|
||||
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
throw new AlreadyExistsException();
|
||||
}
|
||||
|
||||
$poll = $this->findById($poll_id);
|
||||
|
||||
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint
|
||||
$this->checkMaxVotes($choices, $poll, $poll_id);
|
||||
|
||||
// Check if slots are still the same
|
||||
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the hash send by the user is the same as the computed hash.
|
||||
*
|
||||
* @param $poll /stdClass The poll
|
||||
* @param $slots_hash string The hash sent by the user
|
||||
* @throws ConcurrentEditionException Thrown when hashes are differents
|
||||
*/
|
||||
private function checkThatSlotsDidntChanged($poll, $slots_hash) {
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
if ($slots_hash !== $this->hashSlots($slots)) {
|
||||
throw new ConcurrentEditionException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the votes doesn't conflicts the maximum votes constraint
|
||||
*
|
||||
* @param $user_choice
|
||||
* @param \stdClass $poll
|
||||
* @param string $poll_id
|
||||
* @throws ConcurrentVoteException
|
||||
*/
|
||||
private function checkMaxVotes($user_choice, $poll, $poll_id) {
|
||||
$votes = $this->allVotesByPollId($poll_id);
|
||||
if (count($votes) <= 0) {
|
||||
return;
|
||||
}
|
||||
$best_choices = $this->computeBestChoices($votes, $poll);
|
||||
foreach ($best_choices['y'] as $i => $nb_choice) {
|
||||
// if for this option we have reached maximum value and user wants to add itself too
|
||||
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
|
||||
throw new ConcurrentVoteException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
/**
|
||||
* This service helps to purge data.
|
||||
*
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class PurgeService {
|
||||
private $logService;
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, LogService $logService) {
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode purges all old polls (the ones with end_date in past).
|
||||
*
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function purgeOldPolls() {
|
||||
$oldPolls = $this->pollRepository->findOldPolls();
|
||||
$count = count($oldPolls);
|
||||
|
||||
if ($count > 0) {
|
||||
$this->logService->log('EXPIRATION', 'Going to purge ' . $count . ' poll(s)...');
|
||||
|
||||
foreach ($oldPolls as $poll) {
|
||||
if ($this->purgePollById($poll->id)) {
|
||||
$this->logService->log('EXPIRATION_SUCCESS', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
|
||||
} else {
|
||||
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode delete all data about a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function purgePollById($poll_id) {
|
||||
$done = true;
|
||||
|
||||
$this->pollRepository->beginTransaction();
|
||||
$done &= $this->commentRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->voteRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->slotRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->pollRepository->deleteById($poll_id);
|
||||
|
||||
if ($done) {
|
||||
$this->pollRepository->commit();
|
||||
} else {
|
||||
$this->pollRepository->rollback();
|
||||
}
|
||||
|
||||
return $done;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Security\Token;
|
||||
|
||||
class SecurityService {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a CSRF token by name, or (re)create it.
|
||||
*
|
||||
* It creates a new token if :
|
||||
* <ul>
|
||||
* <li>There no token with the given name in session</li>
|
||||
* <li>The token time is in past</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $tokan_name string The name of the CSRF token
|
||||
* @return Token The token
|
||||
*/
|
||||
function getToken($tokan_name) {
|
||||
if (!isset($_SESSION['tokens'])) {
|
||||
$_SESSION['tokens'] = [];
|
||||
}
|
||||
if (!isset($_SESSION['tokens'][$tokan_name]) || $_SESSION['tokens'][$tokan_name]->isGone()) {
|
||||
$_SESSION['tokens'][$tokan_name] = new Token();
|
||||
}
|
||||
|
||||
return $_SESSION['tokens'][$tokan_name]->getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given value is corresponding to the token in session.
|
||||
*
|
||||
* @param $tokan_name string Name of the token
|
||||
* @param $csrf string Value to check
|
||||
* @return bool true if the token is well checked
|
||||
*/
|
||||
public function checkCsrf($tokan_name, $csrf) {
|
||||
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
|
||||
|
||||
if($checked) {
|
||||
unset($_SESSION['tokens'][$tokan_name]);
|
||||
}
|
||||
|
||||
return $checked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the current session allows to access given poll.
|
||||
*
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @return bool true if the current session can access this poll
|
||||
*/
|
||||
public function canAccessPoll($poll) {
|
||||
if (is_null($poll->password_hash)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
|
||||
$currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null;
|
||||
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
|
||||
return true;
|
||||
}
|
||||
unset($_SESSION['poll_security'][$poll->id]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit to the session a poll password
|
||||
*
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @param $password string the password to compare
|
||||
*/
|
||||
public function submitPollAccess($poll, $password) {
|
||||
if (!empty($password)) {
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
$_SESSION['poll_security'][$poll->id] = $password;
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureSessionPollSecurityIsCreated() {
|
||||
if (!isset($_SESSION['poll_security'])) {
|
||||
$_SESSION['poll_security'] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
// FRAMADATE version
|
||||
const VERSION = '1.1.1';
|
||||
|
||||
// PHP Needed version
|
||||
const PHP_NEEDED_VERSION = '5.6';
|
||||
|
||||
// Config constants
|
||||
const COMPILE_DIR = '/tpl_c/';
|
||||
|
||||
// Regex
|
||||
const POLL_REGEX = '/^[a-z0-9-]*$/i';
|
||||
const ADMIN_POLL_REGEX = '/^[a-z0-9]{24}$/i';
|
||||
const CHOICE_REGEX = '/^[ 012]$/';
|
||||
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i';
|
||||
const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i';
|
||||
const EDITABLE_CHOICE_REGEX = '/^[0-2]$/';
|
||||
const BASE64_REGEX = '/^[A-Za-z0-9]+$/';
|
||||
const MD5_REGEX = '/^[A-Fa-f0-9]{32}$/';
|
||||
|
||||
// Session constants
|
||||
const SESSION_EDIT_LINK_TOKEN = 'EditLinkToken';
|
||||
const SESSION_EDIT_LINK_TIME = "EditLinkMail";
|
||||
|
||||
// CSRF (300s = 5min)
|
||||
const TOKEN_TIME = 300;
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
// Prepare I18N instance
|
||||
$i18n = \o80\i18n\I18N::instance();
|
||||
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
|
||||
$i18n->setPath(__DIR__ . '/../../locale');
|
||||
|
||||
// Change langauge when user asked for it
|
||||
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES), true)) {
|
||||
$_SESSION['lang'] = $_POST['lang'];
|
||||
}
|
||||
|
||||
/* <html lang="$locale"> */
|
||||
$i18n->get('', 'Something, just to load the dictionary');
|
||||
$locale = str_replace('_', '-', $i18n->getLoadedLang());
|
||||
|
||||
/* Date Format */
|
||||
$date_format['txt_full'] = __('Date', 'FULL'); //summary in create_date_poll.php and removal date in choix_(date|autre).php
|
||||
$date_format['txt_short'] = __('Date', 'SHORT'); // radio title
|
||||
$date_format['txt_day'] = __('Date', 'DAY');
|
||||
$date_format['txt_date'] = __('Date', 'DATE');
|
||||
$date_format['txt_month_year'] = __('Date', 'MONTH_YEAR');
|
||||
$date_format['txt_datetime_short'] = __('Date', 'DATETIME');
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { //%e can't be used on Windows platform, use %#d instead
|
||||
foreach ($date_format as $k => $v) {
|
||||
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
// Autoloading of dependencies with Composer
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../vendor/o80/i18n/src/shortcuts.php';
|
||||
|
||||
if (session_id() === '') {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (ini_get('date.timezone') === '') {
|
||||
date_default_timezone_set('Europe/Paris');
|
||||
}
|
||||
|
||||
define('ROOT_DIR', __DIR__ . '/../../');
|
||||
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
|
||||
|
||||
require_once __DIR__ . '/constants.php';
|
||||
|
||||
if (is_file(CONF_FILENAME)) {
|
||||
@include_once __DIR__ . '/config.php';
|
||||
|
||||
// Connection to database
|
||||
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
|
||||
RepositoryFactory::init($connect);
|
||||
$err = 0;
|
||||
} else {
|
||||
define('NOMAPPLICATION', 'Framadate');
|
||||
define('DEFAULT_LANGUAGE', 'fr');
|
||||
define('IMAGE_TITRE', 'images/logo-framadate.png');
|
||||
define('LOG_FILE', 'admin/stdout.log');
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
];
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/i18n.php';
|
||||
// Smarty
|
||||
require_once __DIR__ . '/smarty.php';
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
use Framadate\Utils;
|
||||
|
||||
require_once __DIR__ . '/../../vendor/smarty/smarty/libs/Smarty.class.php';
|
||||
$smarty = new \Smarty();
|
||||
$smarty->setTemplateDir(ROOT_DIR . '/tpl/');
|
||||
$smarty->setCompileDir(ROOT_DIR . COMPILE_DIR);
|
||||
$smarty->setCacheDir(ROOT_DIR . '/cache/');
|
||||
$smarty->caching = false;
|
||||
|
||||
$smarty->assign('APPLICATION_NAME', NOMAPPLICATION);
|
||||
$smarty->assign('SERVER_URL', Utils::get_server_name());
|
||||
$smarty->assign('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']);
|
||||
$smarty->assign('TITLE_IMAGE', IMAGE_TITRE);
|
||||
$smarty->assign('use_nav_js', strstr($_SERVER['SERVER_NAME'], 'framadate.org'));
|
||||
$smarty->assign('locale', $locale);
|
||||
$smarty->assign('langs', $ALLOWED_LANGUAGES);
|
||||
$smarty->assign('date_format', $date_format);
|
||||
if (isset($config['tracking_code'])) {
|
||||
$smarty->assign('tracking_code', $config['tracking_code']);
|
||||
}
|
||||
if (defined('FAVICON')) {
|
||||
$smarty->assign('favicon', FAVICON);
|
||||
}
|
||||
|
||||
// Dev Mode
|
||||
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
|
||||
$smarty->force_compile = true;
|
||||
$smarty->compile_check = true;
|
||||
} else {
|
||||
$smarty->force_compile = false;
|
||||
$smarty->compile_check = false;
|
||||
}
|
||||
|
||||
function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
|
||||
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$admin = (isset($params['admin']) && $params['admin']) ? true : false;
|
||||
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : false;
|
||||
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? $params['action_value'] : false;
|
||||
$vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
|
||||
|
||||
// If filter_var fails (i.e.: hack tentative), it will return false. At least no leak is possible from this.
|
||||
|
||||
return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value);
|
||||
}
|
||||
|
||||
function smarty_modifier_markdown($md, $clear = false, $inline=true) {
|
||||
return Utils::markdown($md, $clear, $inline);
|
||||
}
|
||||
|
||||
function smarty_modifier_resource($link) {
|
||||
return Utils::get_server_name() . $link;
|
||||
}
|
||||
function smarty_modifier_addslashes_single_quote($string) {
|
||||
return addcslashes($string, '\\\'');
|
||||
}
|
||||
|
||||
function smarty_modifier_html($html) {
|
||||
return Utils::htmlEscape($html);
|
||||
}
|
||||
|
||||
function smarty_modifier_datepicker_path($lang) {
|
||||
$i = 0;
|
||||
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
|
||||
$lang_arr = explode('-', $lang);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
$lang = $lang_arr[0];
|
||||
} else {
|
||||
$lang = 'en';
|
||||
}
|
||||
$i += 1;
|
||||
}
|
||||
return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
||||
|
||||
function smarty_modifier_locale_2_lang($locale) {
|
||||
$lang_arr = explode('-', $locale);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
return $lang_arr[0];
|
||||
}
|
||||
return $locale;
|
||||
}
|
||||
|
||||
function path_for_datepicker_locale($lang) {
|
||||
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class FramaTestCase extends TestCase {
|
||||
protected function getTestResourcePath($resourcepath) {
|
||||
return __DIR__ . '/../resources/' . $resourcepath;
|
||||
}
|
||||
|
||||
protected function readTestResource($resourcepath) {
|
||||
return file_get_contents($this->getTestResourcePath($resourcepath));
|
||||
}
|
||||
|
||||
protected function invoke(&$object, $methodName) {
|
||||
$reflectionClass = new \ReflectionClass($object);
|
||||
$reflectionMethod = $reflectionClass->getMethod($methodName);
|
||||
$reflectionMethod->setAccessible(true);
|
||||
|
||||
$params = array_slice(func_get_args(), 2); // get all the parameters after $methodName
|
||||
return $reflectionMethod->invokeArgs($object, $params);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\FramaTestCase;
|
||||
|
||||
class MailServiceUnitTest extends FramaTestCase {
|
||||
const MSG_KEY = '666';
|
||||
|
||||
public function test_should_send_a_2nd_mail_after_a_good_interval() {
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
|
||||
|
||||
// When
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertSame(true, $canSendMsg);
|
||||
}
|
||||
|
||||
public function test_should_not_send_2_mails_in_a_short_interval() {
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
|
||||
|
||||
// When
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertSame(false, $canSendMsg);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<?php
|
||||
$loader = require __DIR__ . '/../../vendor/autoload.php';
|
||||
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');
|
||||
0
assets/.gitignore
vendored
Normal file
|
|
@ -23,3 +23,7 @@
|
|||
border-top: 0 none;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.required-parameter::after {
|
||||
content: " *";
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
@import url(./style.css);
|
||||
@import url(style.scss);
|
||||
|
||||
body {
|
||||
background:none;
|
||||
|
|
@ -14,6 +14,14 @@
|
|||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs d'OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
@import '~bootstrap/dist/css/bootstrap.css';
|
||||
@import '~bootstrap/dist/css/bootstrap-theme.css';
|
||||
// @import 'jquery-ui.min.css';
|
||||
@import '~bootstrap-datepicker/dist/css/bootstrap-datepicker3.css';
|
||||
@import '~simplemde/dist/simplemde.min.css';
|
||||
@import 'app/create_poll.css';
|
||||
|
||||
@font-face {
|
||||
font-family: "DejaVu Sans";
|
||||
src: url('../fonts/DejaVuSans.ttf');
|
||||
|
|
@ -91,10 +99,6 @@ header h1 {
|
|||
margin-top:0;
|
||||
}
|
||||
|
||||
.summary {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.summary img {
|
||||
max-width:100px;
|
||||
}
|
||||
|
|
@ -272,10 +276,11 @@ table.results thead th {
|
|||
border:2px solid white;
|
||||
padding: 5px;
|
||||
min-width:40px;
|
||||
font-size:12px;
|
||||
font-size: 13px;
|
||||
max-width:100px;
|
||||
overflow:hidden;
|
||||
text-overflow:ellipsis;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
table.results thead th img {
|
||||
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
|
@ -1,7 +1,12 @@
|
|||
import MDEWrapper from '../mde-wrapper';
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
wrapper = new MDEWrapper($('.js-desc textarea')[0], $('#rich-editor-button'), $('#simple-editor-button'));
|
||||
if (document.getElementById('add-comment')) {
|
||||
const wrapper = new MDEWrapper($('.js-desc textarea')[0], $('#rich-editor-button'), $('#simple-editor-button'));
|
||||
}
|
||||
var firstOpening = true;
|
||||
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
function createNode(text) {
|
||||
|
|
@ -59,17 +64,19 @@ $(document).ready(function() {
|
|||
}
|
||||
});
|
||||
|
||||
$('#title-form .btn-edit').on('click', function() {
|
||||
$('#title-form h3').hide();
|
||||
const titleForm = $('#title-form');
|
||||
|
||||
titleForm.find('.btn-edit').on('click', function() {
|
||||
titleForm.find('h3').hide();
|
||||
$('.js-title').removeClass('hidden');
|
||||
$('.js-title input').focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#title-form .btn-cancel').on('click', function() {
|
||||
$('#title-form h3').show();
|
||||
$('#title-form .js-title').addClass('hidden');
|
||||
$('#title-form .btn-edit').focus();
|
||||
titleForm.find('.btn-cancel').on('click', function() {
|
||||
titleForm.find('h3').show();
|
||||
titleForm.find('.js-title').addClass('hidden');
|
||||
titleForm.find('.btn-edit').focus();
|
||||
return false;
|
||||
});
|
||||
|
||||
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
// 2 choices filled and you can submit
|
||||
|
||||
var submitChoicesAvalaible = function () {
|
||||
var submitChoicesAvailable = function () {
|
||||
var nb_filled_choices = 0;
|
||||
$('.choice-field input').each(function () {
|
||||
if ($(this).val() != '') {
|
||||
if ($(this).val() !== '') {
|
||||
nb_filled_choices++;
|
||||
}
|
||||
});
|
||||
|
|
@ -36,8 +36,10 @@
|
|||
};
|
||||
|
||||
// Handle form submission
|
||||
$(document.formulaire).on('submit', function (e) {
|
||||
if (!submitChoicesAvalaible()) {
|
||||
$('form[name="formulaire-classic"]').on('submit', (e) => {
|
||||
console.log("Submit form");
|
||||
console.log(e);
|
||||
if (!submitChoicesAvailable()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
|
@ -74,23 +76,23 @@
|
|||
$('.choice-field:last').remove();
|
||||
var nb_choices = $('.choice-field').length;
|
||||
$('#choice' + (nb_choices - 1)).focus();
|
||||
if (nb_choices == 1) {
|
||||
if (nb_choices === 1) {
|
||||
$('#remove-a-choice').addClass('disabled');
|
||||
}
|
||||
submitChoicesAvalaible();
|
||||
submitChoicesAvailable();
|
||||
});
|
||||
|
||||
$(document).on('keyup, change', '.choice-field input', function () {
|
||||
submitChoicesAvalaible();
|
||||
$(document).on('keyup, change', '.choice-field input', () => {
|
||||
submitChoicesAvailable();
|
||||
});
|
||||
submitChoicesAvalaible();
|
||||
submitChoicesAvailable();
|
||||
|
||||
// Button to build markdown from: link + image-url + text
|
||||
|
||||
var md_a_imgModal = $('#md-a-imgModal');
|
||||
var md_text = $('#md-text');
|
||||
var md_img = $('#md-img');
|
||||
var md_val = $('#md-a');
|
||||
const md_a_imgModal = $('#md-a-imgModal');
|
||||
const md_text = $('#md-text');
|
||||
const md_img = $('#md-img');
|
||||
const md_val = $('#md-a');
|
||||
|
||||
$(document).on('click', '.md-a-img', function () {
|
||||
md_a_imgModal.modal('show');
|
||||
|
|
@ -98,16 +100,16 @@
|
|||
$('#md-a-imgModalLabel').text($(this).attr('title'));
|
||||
});
|
||||
md_a_imgModal.find('.btn-primary').on('click', function () {
|
||||
var text = md_text.val();
|
||||
var img = md_img.val();
|
||||
var link = md_val.val();
|
||||
var element = $('#' + $(this).val());
|
||||
const text = md_text.val();
|
||||
const img = md_img.val();
|
||||
const link = md_val.val();
|
||||
const element = $('#' + $(this).val());
|
||||
|
||||
if (img != '' && link != '') {
|
||||
if (img !== '' && link !== '') {
|
||||
element.val('[](' + link + ')');
|
||||
} else if (img != '') {
|
||||
} else if (img !== '') {
|
||||
element.val('');
|
||||
} else if (link != '') {
|
||||
} else if (link !== '') {
|
||||
element.val('[' + (text?text:link) + '](' + link + ')');
|
||||
} else {
|
||||
element.val(text);
|
||||
|
|
@ -116,6 +118,6 @@
|
|||
md_img.val('');
|
||||
md_val.val('');
|
||||
md_text.val('');
|
||||
submitChoicesAvalaible();
|
||||
submitChoicesAvailable();
|
||||
});
|
||||
})();
|
||||
})();
|
||||
|
|
@ -16,14 +16,16 @@
|
|||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
$(document).ready(function () {
|
||||
import MDEWrapper from '../mde-wrapper';
|
||||
|
||||
$(document).ready(() => {
|
||||
|
||||
/**
|
||||
* Error check when submitting form
|
||||
*/
|
||||
$("#formulaire").submit(function (event) {
|
||||
var isHidden = $("#hidden").prop('checked');
|
||||
var isOptionAllUserCanModifyEverything = $("#editableByAll").is(":checked");
|
||||
$('form[name="poll"]').submit((event) => {
|
||||
const isHidden = $("#hidden").prop('checked');
|
||||
const isOptionAllUserCanModifyEverything = $("#editableByAll").is(":checked");
|
||||
|
||||
if (isHidden && isOptionAllUserCanModifyEverything) {
|
||||
event.preventDefault();
|
||||
|
|
@ -36,60 +38,71 @@ $(document).ready(function () {
|
|||
/**
|
||||
* Enable/Disable custom url options
|
||||
*/
|
||||
$("#use_customized_url").change(function () {
|
||||
if ($(this).prop("checked")) {
|
||||
function toggle_customized_url() {
|
||||
if ($("#poll_use_customized_url").prop("checked")) {
|
||||
$("#customized_url_options").removeClass("hidden");
|
||||
} else {
|
||||
$("#customized_url_options").addClass("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Enable/Disable ValueMax options
|
||||
*/
|
||||
$("#use_ValueMax").change(function () {
|
||||
if ($(this).prop("checked")) {
|
||||
function toggle_use_value_max() {
|
||||
if ($("#poll_use_ValueMax").prop("checked")) {
|
||||
$("#ValueMax").removeClass("hidden");
|
||||
} else {
|
||||
$("#ValueMax").addClass("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Hide/Show password options
|
||||
*/
|
||||
$("#use_password").change(function(){
|
||||
if ($(this).prop("checked")) {
|
||||
function toggle_use_password() {
|
||||
if ($("#poll_use_password").prop("checked")) {
|
||||
$("#password_options").removeClass("hidden");
|
||||
} else {
|
||||
$("#password_options").addClass("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$("#poll_use_customized_url").change(toggle_customized_url);
|
||||
$("#poll_use_ValueMax").change(toggle_use_value_max);
|
||||
$("#poll_use_password").change(toggle_use_password);
|
||||
|
||||
toggle_customized_url();
|
||||
toggle_use_value_max();
|
||||
toggle_use_password();
|
||||
|
||||
// Check cookies are enabled too
|
||||
var cookieEnabled = function () {
|
||||
var cookieEnabled = navigator.cookieEnabled;
|
||||
const cookieEnabled = () => {
|
||||
let cookieEnabled = navigator.cookieEnabled;
|
||||
|
||||
// if not IE4+ nor NS6+
|
||||
if (!cookieEnabled && typeof navigator.cookieEnabled === "undefined") {
|
||||
document.cookie = "testcookie";
|
||||
cookieEnabled = document.cookie.indexOf("testcookie") != -1;
|
||||
cookieEnabled = document.cookie.indexOf("testcookie") !== -1;
|
||||
}
|
||||
|
||||
return cookieEnabled;
|
||||
};
|
||||
|
||||
if (cookieEnabled()) {
|
||||
// Show the form block
|
||||
document.getElementById("form-block").setAttribute("style", "");
|
||||
} else {
|
||||
// Show the warning about cookies
|
||||
document.getElementById("cookie-warning").setAttribute("style", "");
|
||||
if (document.getElementById("form-block")) {
|
||||
if (cookieEnabled()) {
|
||||
// Show the form block
|
||||
document.getElementById("form-block").setAttribute("style", "");
|
||||
} else {
|
||||
// Show the warning about cookies
|
||||
document.getElementById("cookie-warning").setAttribute("style", "");
|
||||
}
|
||||
}
|
||||
|
||||
var wrapper = new MDEWrapper($('#poll_comments')[0], $('#rich-editor-button'), $('#simple-editor-button'));
|
||||
if ($('#rich-editor-button').hasClass('active')) {
|
||||
const rich_editor_button = document.getElementById('rich-editor-button');
|
||||
const wrapper = new MDEWrapper(document.getElementById('poll_description'), rich_editor_button, document.getElementById('simple-editor-button'));
|
||||
if ($(rich_editor_button).hasClass('active')) {
|
||||
wrapper.enable();
|
||||
}
|
||||
|
||||
|
|
@ -16,31 +16,35 @@
|
|||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
// Global variables
|
||||
|
||||
var $selected_days = $('#selected-days');
|
||||
var $removeaday = $('#remove-a-day');
|
||||
var $copyhours = $('#copyhours');
|
||||
var $next = $('button[name="choixheures"]');
|
||||
const $selected_days = $('#selected-days');
|
||||
const $removeaday = $('#remove-a-day');
|
||||
const $copyhours = $('#copyhours');
|
||||
const $next = $('button[name="choixheures"]');
|
||||
|
||||
const $collectionHolder = $('#poll_date_choices_choices');
|
||||
|
||||
|
||||
var updateButtonState = function () {
|
||||
const updateButtonState = () => {
|
||||
$removeaday.toggleClass('disabled', $selected_days.find('fieldset').length <= 1);
|
||||
$copyhours.toggleClass('disabled', !hasFirstDayFilledHours());
|
||||
$next.toggleClass('disabled', countFilledDays() < 1)
|
||||
};
|
||||
|
||||
// at least 1 day filled and you can submit
|
||||
var isSubmitDaysAvaillable = function() {
|
||||
const isSubmitDaysAvailable = () => {
|
||||
return (countFilledDays() >= 1);
|
||||
};
|
||||
|
||||
var countFilledDays = function () {
|
||||
var nb_filled_days = 0;
|
||||
$selected_days.find('fieldset legend input').each(function () {
|
||||
if ($(this).val() != '') {
|
||||
const countFilledDays = () => {
|
||||
let nb_filled_days = 0;
|
||||
$selected_days.find('fieldset legend input').each(() => {
|
||||
if ($(this).val() !== '') {
|
||||
nb_filled_days++;
|
||||
}
|
||||
});
|
||||
|
|
@ -48,76 +52,49 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
|
||||
var hasFirstDayFilledHours = function () {
|
||||
var hasFilledHours = false;
|
||||
$selected_days.find('fieldset').first().find('.hours').each(function () {
|
||||
if ($(this).val() != '') {
|
||||
const hasFirstDayFilledHours = () => {
|
||||
let hasFilledHours = false;
|
||||
$selected_days.find('fieldset').first().find('.hours').each((index, element) => {
|
||||
if ($(element).val() !== '') {
|
||||
hasFilledHours = true;
|
||||
}
|
||||
});
|
||||
return hasFilledHours;
|
||||
};
|
||||
|
||||
function addDayForm($collectionHolder, value) {
|
||||
|
||||
let newForm = $collectionHolder.data('prototype');
|
||||
const index = $collectionHolder.find('input[type="date"]').length;
|
||||
newForm = newForm.replace(/__name__/g, index);
|
||||
|
||||
/**
|
||||
* Parse a string date
|
||||
* @param dateStr The string date
|
||||
* @param format The format PHP style (allowed: %Y, %m and %d)
|
||||
*/
|
||||
var parseDate = function (dateStr, format) {
|
||||
var dtsplit = dateStr.split(/[\/ .:-]/);
|
||||
var dfsplit = format.split(/[\/ .:-]/);
|
||||
|
||||
if (dfsplit.length != dtsplit.length) {
|
||||
return null;
|
||||
const last_day = $selected_days.find('fieldset').filter(':last');
|
||||
const new_day = last_day.after(newForm).next();
|
||||
if (value) {
|
||||
new_day.find('input[type="date"]').val(value);
|
||||
}
|
||||
const moments_div = new_day.find('div#poll_date_choices_choices_'+ index +'_moments');
|
||||
|
||||
// creates assoc array for date
|
||||
var df = [];
|
||||
for (var dc = 0; dc < dtsplit.length; dc++) {
|
||||
df[dfsplit[dc]] = dtsplit[dc];
|
||||
}
|
||||
const hour_form = moments_div.data('prototype');
|
||||
const new_hour = moments_div.html(hour_form);
|
||||
new_hour.after(hour_form);
|
||||
new_hour.after(hour_form);
|
||||
|
||||
// Build date
|
||||
return new Date(parseInt(df['%Y']), parseInt(df['%m']) - 1, parseInt(df['%d']), 0, 0, 0, 0);
|
||||
};
|
||||
|
||||
var formatDate = function (date, format) {
|
||||
return format
|
||||
.replace('%d', ("00" +date.getDate()).slice(-2))
|
||||
.replace('%m', ("00" + (date.getMonth() + 1)).slice(-2))
|
||||
.replace('%Y', ("0000" + date.getFullYear()).slice(-4));
|
||||
};
|
||||
|
||||
function getLastDayNumber(last_day) {
|
||||
if (last_day == null) {
|
||||
last_day = $selected_days.find('fieldset').filter(':last');
|
||||
}
|
||||
return parseInt(/^d([0-9]+)-h[0-9]+$/.exec($(last_day).find('.hours').filter(':first').attr('id'))[1])
|
||||
}
|
||||
|
||||
function newDateFields(dateStr) {
|
||||
var last_day = $selected_days.find('fieldset').filter(':last');
|
||||
var last_day_title = last_day.find('legend input').attr('title');
|
||||
var new_day_number = getLastDayNumber(last_day) + 1;
|
||||
function addHourForm($collectionHolder) {
|
||||
// Get the data-prototype explained earlier
|
||||
let newForm = $collectionHolder.data('prototype');
|
||||
|
||||
var re_id_hours = new RegExp('"d' + (new_day_number - 1) + '-h', 'g');
|
||||
var re_name_hours = new RegExp('name="horaires' + (new_day_number - 1), 'g');
|
||||
newForm = newForm.replace(/__name__/g, $collectionHolder.find('input.hours').length);
|
||||
|
||||
var new_day_html = last_day.html().replace(re_id_hours, '"d' + new_day_number + '-h')
|
||||
.replace('id="day' + (new_day_number - 1) + '"', 'id="day' + new_day_number + '"')
|
||||
.replace('for="day' + (new_day_number - 1) + '"', 'for="day' + new_day_number + '"')
|
||||
.replace(re_name_hours, 'name="horaires' + new_day_number)
|
||||
.replace(/value="(.*?)"/g, 'value=""')
|
||||
.replace(/hours" title="(.*?)"/g, 'hours" title="" p')
|
||||
.replace('title="' + last_day_title + '"', 'title="' + last_day_title.substring(0, last_day_title.indexOf(' ')) + ' ' + (new_day_number + 1) + '"');
|
||||
// Display the form in the page in an li, before the "Add a tag" link li
|
||||
const last_hour = $collectionHolder.find('.col-sm-2').filter(':last');
|
||||
last_hour.after(newForm);
|
||||
}
|
||||
|
||||
last_day
|
||||
.after('<fieldset>' + new_day_html + '</fieldset>')
|
||||
.next().find('legend input').val(dateStr);
|
||||
$('#day' + (new_day_number)).focus();
|
||||
updateButtonState();
|
||||
function newDateFields(date) {
|
||||
addDayForm($collectionHolder, date);
|
||||
}
|
||||
|
||||
function manageRemoveadayAndCopyhoursButtons() {
|
||||
|
|
@ -128,12 +105,18 @@ $(document).ready(function () {
|
|||
}
|
||||
}
|
||||
|
||||
var useFirstEmptyDateField = function (dateStr) {
|
||||
var used = false;
|
||||
$selected_days.find('fieldset legend input').each(function () {
|
||||
/**
|
||||
* Fills the first field
|
||||
*
|
||||
* @param dateStr
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const useFirstEmptyDateField = (dateStr) => {
|
||||
let used = false;
|
||||
$selected_days.find('fieldset legend input[type="date"]').each((index, elem) => {
|
||||
if (!used) {
|
||||
if ($(this).val() == '') {
|
||||
$(this).val(dateStr);
|
||||
if ($(elem).val() == '') {
|
||||
$(elem).val(dateStr);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,8 +126,8 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
// Handle form submission
|
||||
$(document.formulaire).on('submit', function (e) {
|
||||
if (!isSubmitDaysAvaillable()) {
|
||||
$('form[name="formulaire-classic"]').on('submit', function (e) {
|
||||
if (!isSubmitDaysAvailable()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
|
@ -164,7 +147,7 @@ $(document).ready(function () {
|
|||
|
||||
$('#resetdays').on('click', function () {
|
||||
$selected_days.find('fieldset:gt(0)').remove();
|
||||
$('#day0').focus();
|
||||
$('input[type="date"]').val('').focus();
|
||||
updateButtonState();
|
||||
});
|
||||
|
||||
|
|
@ -225,27 +208,27 @@ $(document).ready(function () {
|
|||
|
||||
// Buttons "Add an hour"
|
||||
|
||||
$(document).on('click', '.add-an-hour', function () {
|
||||
var last_hour = $(this).parent('div').parent('div').prev();
|
||||
addHour(last_hour, $(this));
|
||||
$(document).on('click', '.add-an-hour', function (e) {
|
||||
const last_hour = $(e.target).parent('div').parent('div').prev();
|
||||
addHourForm(last_hour);
|
||||
});
|
||||
|
||||
// Buttons "Remove an hour"
|
||||
|
||||
$(document).on('click', '.remove-an-hour', function () {
|
||||
var last_hour = $(this).parent('div').parent('div').prev();
|
||||
$(document).on('click', '.remove-an-hour', function (e) {
|
||||
const last_hour = $(e.target).parent('div').parent('div').prev().children('.col-sm-2').last();
|
||||
// for and id
|
||||
var di_hj = last_hour.children('.hours').attr('id').split('-');
|
||||
var di = parseInt(di_hj[0].replace('d', ''));
|
||||
var hj = parseInt(di_hj[1].replace('h', ''));
|
||||
const di_hj = last_hour.find('input').attr('id').split('_');
|
||||
const hj = parseInt(di_hj[di_hj.length - 1]);
|
||||
const di = parseInt(di_hj[di_hj.length - 3]);
|
||||
|
||||
// The first hour must not be removed
|
||||
if (hj > 0) {
|
||||
last_hour.remove();
|
||||
$('#d' + di + '-h' + (hj - 1)).focus();
|
||||
$(this).next('.add-an-hour').removeClass('disabled');
|
||||
if (hj == 1) {
|
||||
$(this).addClass('disabled');
|
||||
$(e.target).next('.add-an-hour').removeClass('disabled');
|
||||
if (hj === 1) {
|
||||
$(e.target).addClass('disabled');
|
||||
}
|
||||
}
|
||||
updateButtonState();
|
||||
|
|
@ -254,23 +237,23 @@ $(document).ready(function () {
|
|||
// Button "Add a day"
|
||||
|
||||
$('#add-a-day').on('click', function () {
|
||||
newDateFields();
|
||||
//newDateFields();
|
||||
addDayForm($collectionHolder);
|
||||
});
|
||||
|
||||
// Button "Remove a day"
|
||||
|
||||
$removeaday.on('click', function () {
|
||||
$selected_days.find('fieldset:last').remove();
|
||||
$('#day' + (getLastDayNumber() - 1)).focus();
|
||||
|
||||
updateButtonState();
|
||||
});
|
||||
|
||||
// Button "Remove the current day"
|
||||
|
||||
$(document).on('click', '.remove-day', function () {
|
||||
if ($('#days_container').find('fieldset').length > 1) {
|
||||
$(this).parents('fieldset').remove();
|
||||
$(document).on('click', '.remove-day', (e) => {
|
||||
if ($('#poll_date_choices_choices').find('fieldset').length > 1) {
|
||||
$(e.target).parents('fieldset').remove();
|
||||
}
|
||||
updateButtonState();
|
||||
});
|
||||
|
|
@ -280,30 +263,30 @@ $(document).ready(function () {
|
|||
$('#interval_add').on('click', function (ev) {
|
||||
var startDateField = $('#range_start');
|
||||
var endDateField = $('#range_end');
|
||||
var startDate = parseDate(startDateField.val(), window.date_formats.DATE);
|
||||
var endDate = parseDate(endDateField.val(), window.date_formats.DATE);
|
||||
const startDate = moment(startDateField.val());
|
||||
const endDate = moment(endDateField.val());
|
||||
|
||||
// Clear error classes
|
||||
startDateField.parent().removeClass('has-error');
|
||||
endDateField.parent().removeClass('has-error');
|
||||
|
||||
var maxDates = 123; // 123 = 4 months
|
||||
var tooMuchDates = endDate - startDate > maxDates * 86400 * 1000;
|
||||
const maxDates = 123; // 123 = 4 months
|
||||
const tooMuchDates = endDate.diff(startDate) > moment.duration(maxDates, 'days');
|
||||
|
||||
if (startDate != null && endDate != null && !tooMuchDates) {
|
||||
if (startDate <= endDate) {
|
||||
while (startDate <= endDate) {
|
||||
var dateStr = formatDate(startDate, window.date_formats.DATE);
|
||||
if (!useFirstEmptyDateField(dateStr)) {
|
||||
newDateFields(dateStr);
|
||||
if (startDate.isSameOrBefore(endDate)) {
|
||||
while (startDate.isSameOrBefore(endDate)) {
|
||||
if (!useFirstEmptyDateField(startDate.format('YYYY-MM-DD'))) {
|
||||
newDateFields(startDate.format('YYYY-MM-DD'));
|
||||
}
|
||||
startDate.setDate(startDate.getDate() + 1);
|
||||
startDate.add(1, 'day');
|
||||
}
|
||||
|
||||
// Hide modal
|
||||
startDateField.val('');
|
||||
endDateField.val('');
|
||||
$('#add_days').modal('hide');
|
||||
// Dirty fix for modal not hiding
|
||||
updateButtonState();
|
||||
|
||||
} else {
|
||||
|
|
@ -327,6 +310,14 @@ $(document).ready(function () {
|
|||
|
||||
});
|
||||
|
||||
$('.modal').on('hide.bs.modal', (e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
$('.modal').on('hidden.bs.modal', (e) => {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// Title update on hours and buttons -/+ hours
|
||||
|
||||
$(document).on('change', '.input-group.date input', function () {
|
||||
|
|
@ -15,11 +15,17 @@
|
|||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
import $ from 'jquery';
|
||||
import 'bootstrap-datepicker';
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
require(`../locales/bootstrap-datepicker.${lang}.js`);
|
||||
|
||||
var init_datepicker = function () {
|
||||
$('.input-group.date').datepicker({
|
||||
format: window.date_formats.DATEPICKER || "dd/mm/yyyy",
|
||||
todayBtn: "linked",
|
||||
todayBtn: true,
|
||||
orientation: "top left",
|
||||
autoclose: true,
|
||||
language: lang,
|
||||
|
|
@ -95,4 +101,4 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -16,6 +16,9 @@
|
|||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
import 'jquery-ui-bundle';
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#poll_form').submit(function (event) {
|
||||
|
|
@ -65,40 +68,37 @@ $(document).ready(function () {
|
|||
type: 'POST',
|
||||
url: form.attr('action'),
|
||||
data: form.serialize(),
|
||||
dataType: 'json',
|
||||
success: function(data)
|
||||
{
|
||||
$('#comment').val('');
|
||||
if (data.result) {
|
||||
$('#comments_list')
|
||||
.replaceWith(data.comments);
|
||||
var lastComment = $('#comments_list')
|
||||
.find('div.comment')
|
||||
.last();
|
||||
// TODO : replace old jQuery UI Effect with Modern CSS
|
||||
// lastComment.effect('highlight', {color: 'green'}, 401);
|
||||
$('html, body').animate({
|
||||
scrollTop: lastComment.offset().top
|
||||
}, 750);
|
||||
} else {
|
||||
var newMessage = $('#genericErrorTemplate').clone();
|
||||
newMessage
|
||||
.find('.contents')
|
||||
.text(data.message.message);
|
||||
newMessage.removeClass('hidden');
|
||||
var commentsAlert = $('#comments_alerts');
|
||||
commentsAlert
|
||||
.empty()
|
||||
.append(newMessage);
|
||||
$('html, body').animate({
|
||||
scrollTop: commentsAlert.offset().top
|
||||
}, 750);
|
||||
}
|
||||
},
|
||||
complete: function() {
|
||||
$('#add_comment').removeAttr("disabled");
|
||||
dataType: 'json'
|
||||
}).done(function (data) {
|
||||
$('#comment').val('');
|
||||
if (data.result) {
|
||||
const comment_list = $('#comments_list');
|
||||
comment_list.append('<div class="comment"><span class="comment_date">' + data.comment.created_at + '</span><b>' + data.comment.name + '</b> <span>' + data.comment.content + '</span></div>');
|
||||
const lastComment = comment_list.find('div.comment').last();
|
||||
// TODO : replace old jQuery UI Effect with Modern CSS
|
||||
// lastComment.effect('highlight', {color: 'green'}, 401);
|
||||
/*$('html, body').animate({
|
||||
scrollTop: lastComment.offset().top
|
||||
}, 750);*/
|
||||
} else {
|
||||
var newMessage = $('#genericErrorTemplate').clone();
|
||||
newMessage
|
||||
.find('.contents')
|
||||
.text(data.message.message);
|
||||
newMessage.removeClass('hidden');
|
||||
var commentsAlert = $('#comments_alerts');
|
||||
commentsAlert
|
||||
.empty()
|
||||
.append(newMessage);
|
||||
$('html, body').animate({
|
||||
scrollTop: commentsAlert.offset().top
|
||||
}, 750);
|
||||
}
|
||||
});
|
||||
}).fail(function(err) {
|
||||
console.error(err);
|
||||
}).always(function() {
|
||||
$('#add_comment').removeAttr("disabled");
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
11
assets/js/index.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import './core';
|
||||
import './app/studs';
|
||||
import './app/create_poll';
|
||||
// import './app/framadatepicker';
|
||||
import './app/classic_poll';
|
||||
import './app/date_poll';
|
||||
import './app/adminstuds';
|
||||
|
||||
import '../images/date.png';
|
||||
import '../images/logo-framadate.png';
|
||||
import '../images/classic.png';
|
||||