Upgrade to SF4

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2018-04-11 20:04:11 +02:00
parent b2fc3a9d53
commit 68feae33c2
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
360 changed files with 26497 additions and 34280 deletions

24
.env.dist Normal file
View 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
View file

@ -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 ###

View file

@ -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
View file

@ -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__)
)
;

View file

@ -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);

View file

@ -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);

View file

@ -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.)

View file

@ -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),
]);

View 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(),
]);

View file

@ -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'),
]);

View file

@ -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');

View file

@ -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'),
]);

View file

@ -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'),
]);

View file

@ -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');

View file

@ -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);
}
}

View file

@ -1,7 +0,0 @@
<?php
namespace Framadate\Exception;
class AlreadyExistsException extends \Exception {
function __construct() {
}
}

View file

@ -1,7 +0,0 @@
<?php
namespace Framadate\Exception;
class ConcurrentEditionException extends \Exception {
function __construct() {
}
}

View file

@ -1,7 +0,0 @@
<?php
namespace Framadate\Exception;
class MomentAlreadyExistsException extends \Exception {
function __construct() {
}
}

View file

@ -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']);
}
}

View file

@ -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();
}
}

View file

@ -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;');
}
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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]);
}
}

View file

@ -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;
}
}

View file

@ -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];
}
}
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}
}
}

View file

@ -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;
}
}

View file

@ -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'] = [];
}
}
}

View file

@ -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;

View file

@ -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
}
}

View file

@ -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';

View file

@ -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';
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -1,3 +0,0 @@
<?php
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');

0
assets/.gitignore vendored Normal file
View file

View file

@ -23,3 +23,7 @@
border-top: 0 none;
content: "";
}
.required-parameter::after {
content: " *";
}

View file

@ -1,4 +1,4 @@
@import url(./style.css);
@import url(style.scss);
body {
background:none;

View file

@ -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 {

View file

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before After
Before After

View file

@ -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;
});

View file

@ -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('[![' + text + '](' + img + ')](' + link + ')');
} else if (img != '') {
} else if (img !== '') {
element.val('![' + text + '](' + img + ')');
} 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();
});
})();
})();

View file

@ -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();
}

View file

@ -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 () {

View file

@ -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 () {
}
});
});
});

View file

@ -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>&nbsp;<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
View 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';

Some files were not shown because too many files have changed in this diff Show more