Modernize project

- Use PHP typings
- Update some front-end libraries

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2021-12-20 17:46:50 +01:00
parent 6144f33e9f
commit 7603bed6d9
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
97 changed files with 1648 additions and 11736 deletions

View File

@ -40,7 +40,7 @@ $is_admin = false;
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$inputService = new InputService(); $inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService); $notificationService = new NotificationService($mailService);
@ -63,7 +63,7 @@ if (!empty($_POST['poll_admin'])) {
if (!$poll) { if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !')); $message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) { } else if (!$is_admin && !$securityService->canAccessPoll($poll)) {
$message = new Message('error', __('Password', 'Wrong password')); $message = new Message('error', __('Password', 'Wrong password'));
} else { } else {
$name = $inputService->filterName($_POST['name']); $name = $inputService->filterName($_POST['name']);
@ -88,8 +88,10 @@ if (!$poll) {
$smarty->error_reporting = E_ALL & ~E_NOTICE; $smarty->error_reporting = E_ALL & ~E_NOTICE;
$smarty->assign('comments', $comments); $smarty->assign('comments', $comments);
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$comments_html = $smarty->fetch('part/comments_list.tpl'); $comments_html = $smarty->fetch('part/comments_list.tpl');
$response = ['result' => $result, 'message' => $message, 'comments' => $comments_html]; $response = ['result' => $result, 'message' => $message, 'comments' => $comments_html];
echo json_encode($response); echo json_encode($response, JSON_THROW_ON_ERROR);

View File

@ -29,7 +29,7 @@ include_once __DIR__ . '/../app/inc/init.php';
$logService = new LogService(); $logService = new LogService();
$sessionService = new SessionService(); $sessionService = new SessionService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$result = false; $result = false;
$message = null; $message = null;
@ -45,7 +45,7 @@ if (!empty($_POST['poll'])) {
$token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN); $token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN);
$token_form_value = empty($_POST['token']) ? null : $_POST['token']; $token_form_value = empty($_POST['token']) ? null : $_POST['token'];
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); $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) if ($config['use_smtp'] === false || is_null($poll) || is_null($token) || is_null($token_form_value)
|| !$token->check($token_form_value) || is_null($editedVoteUniqueId)) { || !$token->check($token_form_value) || is_null($editedVoteUniqueId)) {
$message = new Message('error', __('Error', 'Something is going wrong...')); $message = new Message('error', __('Error', 'Something is going wrong...'));
} }
@ -91,4 +91,4 @@ $smarty->error_reporting = E_ALL & ~E_NOTICE;
$response = ['result' => $result, 'message' => $message]; $response = ['result' => $result, 'message' => $message];
echo json_encode($response); echo json_encode($response, JSON_THROW_ON_ERROR);

View File

@ -20,7 +20,7 @@
use Framadate\Message; use Framadate\Message;
use Framadate\Utils; use Framadate\Utils;
define('ROOT_DIR', __DIR__ . '/../'); const ROOT_DIR = __DIR__ . '/../';
/** /**
* Checking for missing vendors. * Checking for missing vendors.
@ -58,7 +58,7 @@ require_once ROOT_DIR . 'app/inc/i18n.php';
* @param Message $b * @param Message $b
* @return int * @return int
*/ */
function compareCheckMessage(Message $a, Message $b) function compareCheckMessage(Message $a, Message $b): int
{ {
$values = [ $values = [
'danger' => 0, 'danger' => 0,
@ -90,7 +90,7 @@ $conf_filename = $inc_directory . 'config.php';
if (version_compare(PHP_VERSION, PHP_NEEDED_VERSION) >= 0) { if (version_compare(PHP_VERSION, PHP_NEEDED_VERSION) >= 0) {
$messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).', PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_NEEDED_VERSION)); $messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).', PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_NEEDED_VERSION));
} else { } else {
$messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.', phpversion(), PHP_NEEDED_VERSION)); $messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.', PHP_VERSION, PHP_NEEDED_VERSION));
} }
// INTL extension // INTL extension
@ -120,7 +120,7 @@ if (!file_exists(ROOT_DIR . COMPILE_DIR)) {
if (file_exists($conf_filename)) { if (file_exists($conf_filename)) {
$messages[] = new Message('info', __('Check','The config file exists.')); $messages[] = new Message('info', __('Check','The config file exists.'));
} elseif (is_writable($inc_directory)) { } elseif (is_writable($inc_directory)) {
$messages[] = new Message('info', __('Check','The config file directory (%s) is writable.', $inc_directory)); $messages[] = new Message('info', __f('Check','The config file directory (%s) is writable.', $inc_directory));
} else { } else {
$messages[] = new Message('danger', __f('Check','The config file directory (%s) is not writable and the config file (%s) does not exists.', $inc_directory, $conf_filename)); $messages[] = new Message('danger', __f('Check','The config file directory (%s) is not writable and the config file (%s) does not exists.', $inc_directory, $conf_filename));
} }
@ -187,7 +187,7 @@ usort($messages, 'compareCheckMessage');
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2"> <div class="input-group input-group-sm pull-right col-xs-12 col-sm-2">
<select name="lang" class="form-control" title="<?=__('Language selector', 'Select the language')?>" > <select name="lang" class="form-control" title="<?=__('Language selector', 'Select the language')?>" >
<?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?> <?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?>
<option lang="fr" <?php if (substr($lang_key, 0, 2)===$locale) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option> <option lang="fr" <?php if (strpos($lang_key, $locale) === 0) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
<?php } ?> <?php } ?>
</select> </select>
<span class="input-group-btn"> <span class="input-group-btn">

View File

@ -17,6 +17,7 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
use Framadate\FramaDB;
use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9; use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9;
use Framadate\Migration\AddColumn_receiveNewComments_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_uniqId_In_vote_For_0_9;
@ -57,7 +58,7 @@ $migrations = [
// --------------------------------------- // ---------------------------------------
// Check if MIGRATION_TABLE already exists // Check if MIGRATION_TABLE already exists
/** @var \Framadate\FramaDB $connect */ /** @var FramaDB $connect */
$tables = $connect->allTables(); $tables = $connect->allTables();
$pdo = $connect->getPDO(); $pdo = $connect->getPDO();
$prefixedMigrationTable = Utils::table(MIGRATION_TABLE); $prefixedMigrationTable = Utils::table(MIGRATION_TABLE);

View File

@ -50,7 +50,7 @@ $poll_to_delete = null;
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService); $adminPollService = new AdminPollService($connect, $pollService, $logService);
$superAdminService = new SuperAdminService(); $superAdminService = new SuperAdminService();
$securityService = new SecurityService(); $securityService = new SecurityService();

View File

@ -34,14 +34,14 @@ $message = null;
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$purgeService = new PurgeService($connect, $logService); $purgeService = new PurgeService($logService);
$securityService = new SecurityService(); $securityService = new SecurityService();
$inputService = new InputService(); $inputService = new InputService();
/* POST */ /* POST */
/*-----*/ /*-----*/
$action = $inputService->filterName(isset($_POST['action']) ? $_POST['action'] : null); $action = $inputService->filterName($_POST['action'] ?? null);
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */
@ -57,4 +57,4 @@ $smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->assign('title', __('Admin', 'Purge')); $smarty->assign('title', __('Admin', 'Purge'));
$smarty->display('admin/purge.tpl'); $smarty->display('admin/purge.tpl');

View File

@ -47,7 +47,7 @@ $editingVoteId = 0;
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService); $adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService(); $inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
@ -137,9 +137,10 @@ if (isset($_POST['update_poll_info'])) {
break; break;
} }
} elseif ($field === 'expiration_date') { } elseif ($field === 'expiration_date') {
$expiration_date = $inputService->validateDate($_POST['expiration_date'], $pollService->minExpiryDate(), $pollService->maxExpiryDate()); $givenExpirationDate = $inputService->parseDate($_POST['expiration_date']);
if ($expiration_date) { $expiration_date = $inputService->validateDate($givenExpirationDate, $pollService->minExpiryDate(), $pollService->maxExpiryDate());
$poll->end_date = $expiration_date->getTimestamp(); if ($poll->end_date !== $expiration_date->format('Y-m-d H:i:s')) {
$poll->end_date = $expiration_date->format('Y-m-d H:i:s');
$updated = true; $updated = true;
} }
} elseif ($field === 'name') { } elseif ($field === 'name') {
@ -151,26 +152,26 @@ if (isset($_POST['update_poll_info'])) {
$updated = true; $updated = true;
} }
} elseif ($field === 'hidden') { } elseif ($field === 'hidden') {
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false; $hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
if ($hidden !== $poll->hidden) { if ($hidden !== $poll->hidden) {
$poll->hidden = $hidden; $poll->hidden = $hidden;
$poll->results_publicly_visible = false; $poll->results_publicly_visible = false;
$updated = true; $updated = true;
} }
} elseif ($field === 'removePassword') { } elseif ($field === 'removePassword') {
$removePassword = isset($_POST['removePassword']) ? $inputService->filterBoolean($_POST['removePassword']) : false; $removePassword = isset($_POST['removePassword']) && $inputService->filterBoolean($_POST['removePassword']);
if ($removePassword) { if ($removePassword) {
$poll->results_publicly_visible = false; $poll->results_publicly_visible = false;
$poll->password_hash = null; $poll->password_hash = null;
$updated = true; $updated = true;
} }
} elseif ($field === 'password') { } elseif ($field === 'password') {
$password = isset($_POST['password']) ? $_POST['password'] : null; $password = $_POST['password'] ?? null;
/** /**
* Did the user choose results to be publicly visible ? * Did the user choose results to be publicly visible ?
*/ */
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false; $resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) && $inputService->filterBoolean($_POST['resultsPubliclyVisible']);
/** /**
* If there's one, save the password * If there's one, save the password
*/ */

View File

@ -24,34 +24,34 @@ class Choice
* Name of the Choice * Name of the Choice
*/ */
private $name; private $name;
/** /**
* All availables slots for this Choice. * All availables slots for this Choice.
*/ */
private $slots; private $slots;
public function __construct($name='') public function __construct($name='')
{ {
$this->name = $name; $this->name = $name;
$this->slots = []; $this->slots = [];
} }
public function addSlot($slot) public function addSlot($slot): void
{ {
$this->slots[] = $slot; $this->slots[] = $slot;
} }
public function getName() public function getName(): string
{ {
return $this->name; return $this->name;
} }
public function getSlots() public function getSlots(): array
{ {
return $this->slots; return $this->slots;
} }
static function compare(Choice $a, Choice $b) public static function compare(Choice $a, Choice $b): int
{ {
return strcmp($a->name, $b->name); return strcmp($a->name, $b->name);
} }

View File

@ -23,14 +23,13 @@ namespace Framadate;
* Class Editable * Class Editable
* *
* Is used to specify the poll's edition permissions. * Is used to specify the poll's edition permissions.
* @TODO : wait to use the SplEnum
* *
* @package Framadate * @package Framadate
*/ */
class Editable { // extends SplEnum class Editable { // extends SplEnum
const __default = self::EDITABLE_BY_ALL; const __default = self::EDITABLE_BY_ALL;
const NOT_EDITABLE = 0; public const NOT_EDITABLE = 0;
const EDITABLE_BY_ALL = 1; public const EDITABLE_BY_ALL = 1;
const EDITABLE_BY_OWN = 2; public const EDITABLE_BY_OWN = 2;
} }

View File

@ -2,6 +2,4 @@
namespace Framadate\Exception; namespace Framadate\Exception;
class AlreadyExistsException extends \Exception { class AlreadyExistsException extends \Exception {
function __construct() {
}
} }

View File

@ -2,6 +2,4 @@
namespace Framadate\Exception; namespace Framadate\Exception;
class ConcurrentEditionException extends \Exception { class ConcurrentEditionException extends \Exception {
function __construct() {
}
} }

View File

@ -7,6 +7,4 @@ namespace Framadate\Exception;
* Thrown when a poll has a maximum votes constraint for options, and a vote happened since the poll was rendered * Thrown when a poll has a maximum votes constraint for options, and a vote happened since the poll was rendered
*/ */
class ConcurrentVoteException extends \Exception { class ConcurrentVoteException extends \Exception {
function __construct() {
}
} }

View File

@ -2,6 +2,4 @@
namespace Framadate\Exception; namespace Framadate\Exception;
class MomentAlreadyExistsException extends \Exception { class MomentAlreadyExistsException extends \Exception {
function __construct() {
}
} }

View File

@ -0,0 +1,10 @@
<?php
namespace Framadate\Exception;
/**
* Class PollNotFoundException
*
* Thrown when a poll isn't found in a critical process
*/
class PollNotFoundException extends \Exception {
}

View File

@ -32,7 +32,7 @@ class Form
/** /**
* Tells if users can modify their choices. * Tells if users can modify their choices.
* @var \Framadate\Editable * @var int
*/ */
public $editable; public $editable;
@ -92,11 +92,12 @@ class Form
$this->clearChoices(); $this->clearChoices();
} }
public function clearChoices() { public function clearChoices(): void
{
$this->choices = []; $this->choices = [];
} }
public function addChoice(Choice $choice) public function addChoice(Choice $choice): void
{ {
$this->choices[] = $choice; $this->choices[] = $choice;
} }
@ -106,8 +107,8 @@ class Form
return $this->choices; return $this->choices;
} }
public function sortChoices() public function sortChoices(): void
{ {
usort($this->choices, ['Framadate\Choice', 'compare']); usort($this->choices, [Choice::class, 'compare']);
} }
} }

View File

@ -23,19 +23,21 @@ use PDO;
class FramaDB { class FramaDB {
/** /**
* PDO Object, connection to database. * PDO Object, connection to database.
* @var PDO
*/ */
private $pdo = null; private $pdo;
function __construct($connection_string, $user, $password) { public function __construct(string $connection_string, string $user, string $password) {
$this->pdo = new \PDO($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_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} }
/** /**
* @return \PDO Connection to database * @return PDO Connection to database
*/ */
function getPDO() { public function getPDO(): PDO
{
return $this->pdo; return $this->pdo;
} }
@ -44,42 +46,50 @@ class FramaDB {
* *
* @return array The array of table names * @return array The array of table names
*/ */
function allTables() { public function allTables(): array
$result = $this->pdo->query('SHOW TABLES'); {
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN); return $this->pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
return $schemas;
} }
function prepare($sql) { /**
* @return \PDOStatement|false
*/
public function prepare(string $sql) {
return $this->pdo->prepare($sql); return $this->pdo->prepare($sql);
} }
function beginTransaction() { public function beginTransaction(): void
{
$this->pdo->beginTransaction(); $this->pdo->beginTransaction();
} }
function commit() { public function commit(): void
{
$this->pdo->commit(); $this->pdo->commit();
} }
function rollback() { public function rollback(): void
{
$this->pdo->rollback(); $this->pdo->rollback();
} }
function errorCode() { public function errorCode(): ?string {
return $this->pdo->errorCode(); return $this->pdo->errorCode();
} }
function errorInfo() { public function errorInfo(): array
{
return $this->pdo->errorInfo(); return $this->pdo->errorInfo();
} }
function query($sql) { /**
* @return \PDOStatement|false
*/
public function query($sql) {
return $this->pdo->query($sql); return $this->pdo->query($sql);
} }
public function lastInsertId() { public function lastInsertId(): string {
return $this->pdo->lastInsertId(); return $this->pdo->lastInsertId();
} }
} }

View File

@ -1,38 +1,37 @@
<?php <?php
/** /**
* This software is governed by the CeCILL-B license. If a copy of this license * 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 * is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt * 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 STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft) * 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 * 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 * ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt * 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
namespace Framadate; namespace Framadate;
class Message { class Message {
var $type; var $type;
var $message; var $message;
var $link; var $link;
var $linkTitle; var $linkTitle;
var $linkIcon; var $linkIcon;
var $includeTemplate; var $includeTemplate;
function __construct($type, $message, $link=null, $linkTitle=null, $linkIcon=null, $includeTemplate=null) { public function __construct($type, $message, $link=null, $linkTitle=null, $linkIcon=null, $includeTemplate=null) {
$this->type = $type; $this->type = $type;
$this->message = $message; $this->message = $message;
$this->link = $link; $this->link = $link;
$this->linkTitle = $linkTitle; $this->linkTitle = $linkTitle;
$this->linkIcon = $linkIcon; $this->linkIcon = $linkIcon;
$this->includeTemplate = $includeTemplate; $this->includeTemplate = $includeTemplate;
} }
} }

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration adds the field Value_Max on the poll table. * This migration adds the field Value_Max on the poll table.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class AddColumn_ValueMax_In_poll_For_1_1 implements Migration { class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description():string {
return 'Add column "ValueMax" in table "vote" for version 0.9'; return 'Add column "ValueMax" in table "vote" for version 0.9';
} }
@ -43,26 +44,27 @@ class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
return true; return true;
} }
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
return true; return true;
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '` ALTER TABLE `' . Utils::table('poll') . '`
ADD `ValueMax` TINYINT NULL;'); ADD `ValueMax` TINYINT NULL;');

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration adds the field hidden on the poll table. * This migration adds the field hidden on the poll table.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class AddColumn_hidden_In_poll_For_0_9 implements Migration { class AddColumn_hidden_In_poll_For_0_9 implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,8 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string
{
return 'Add column "hidden" in table "vote" for version 0.9'; return 'Add column "hidden" in table "vote" for version 0.9';
} }
@ -43,12 +45,13 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents // Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
@ -58,16 +61,18 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
return true; return true;
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '` ALTER TABLE `' . Utils::table('poll') . '`
ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"'); ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"');

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration adds the field receiveNewComments on the poll table. * This migration adds the field receiveNewComments on the poll table.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class AddColumn_receiveNewComments_For_0_9 implements Migration { class AddColumn_receiveNewComments_For_0_9 implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,8 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string
{
return 'Add column "receiveNewComments" for version 0.9'; return 'Add column "receiveNewComments" for version 0.9';
} }
@ -43,12 +45,13 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents // Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
@ -58,16 +61,18 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
return true; return true;
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '` ALTER TABLE `' . Utils::table('poll') . '`
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\' ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration adds the field uniqId on the vote table. * This migration adds the field uniqId on the vote table.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class AddColumn_uniqId_In_vote_For_0_9 implements Migration { class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'Add column "uniqId" in table "vote" for version 0.9'; return 'Add column "uniqId" in table "vote" for version 0.9';
} }
@ -43,12 +44,12 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents // Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
@ -58,16 +59,17 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
return true; return true;
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '` ALTER TABLE `' . Utils::table('vote') . '`
ADD `uniqId` CHAR(16) NOT NULL ADD `uniqId` CHAR(16) NOT NULL

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration adds the fields password_hash and results_publicly_visible on the poll table. * This migration adds the fields password_hash and results_publicly_visible on the poll table.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 implements Migration { class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { function description(): string {
return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9'; return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9';
} }
@ -43,12 +44,12 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents // Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
@ -58,16 +59,17 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
return true; return true;
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '` ALTER TABLE `' . Utils::table('poll') . '`
ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL , ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL ,

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration alter the comment table to add a date column. * This migration alter the comment table to add a date column.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 1.0 * @version 1.0
*/ */
class Alter_Comment_table_adding_date implements Migration { class Alter_Comment_table_adding_date implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class Alter_Comment_table_adding_date implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description():string {
return 'Alter the comment table to add a date column.'; return 'Alter the comment table to add a date column.';
} }
@ -43,26 +44,27 @@ class Alter_Comment_table_adding_date implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
return true; return true;
} }
/** /**
* This methode is called only one time in the migration page. * This methode is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo); $this->alterCommentTable($pdo);
return true; return true;
} }
private function alterCommentTable(\PDO $pdo) { private function alterCommentTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '` ALTER TABLE `' . Utils::table('comment') . '`
ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;'); ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;');

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration alter the comment table to set a length to the name column. * This migration alter the comment table to set a length to the name column.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 1.0 * @version 1.0
*/ */
class Alter_Comment_table_for_name_length implements Migration { class Alter_Comment_table_for_name_length implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class Alter_Comment_table_for_name_length implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'Alter the comment table to set a length to the name column.'; return 'Alter the comment table to set a length to the name column.';
} }
@ -43,26 +44,27 @@ class Alter_Comment_table_for_name_length implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
return true; return true;
} }
/** /**
* This methode is called only one time in the migration page. * This methode is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo); $this->alterCommentTable($pdo);
return true; return true;
} }
private function alterCommentTable(\PDO $pdo) { private function alterCommentTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '` ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;'); CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;');

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration sets Poll.end_date to NULL by default * This migration sets Poll.end_date to NULL by default
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 1.1 * @version 1.1
*/ */
class Fix_MySQL_No_Zero_Date implements Migration { class Fix_MySQL_No_Zero_Date implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class Fix_MySQL_No_Zero_Date implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'Sets Poll end_date to NULL by default (work around MySQL NO_ZERO_DATE)'; return 'Sets Poll end_date to NULL by default (work around MySQL NO_ZERO_DATE)';
} }
@ -43,17 +44,17 @@ class Fix_MySQL_No_Zero_Date implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed. * @return bool true if the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->prepare("SELECT Column_Default from Information_Schema.Columns where Table_Name = ? AND Column_Name = ?;"); $stmt = $pdo->prepare("SELECT Column_Default from Information_Schema.Columns where Table_Name = ? AND Column_Name = ?;");
$stmt->bindValue(1, Utils::table('poll')); $stmt->bindValue(1, Utils::table('poll'));
$stmt->bindValue(2, 'end_date'); $stmt->bindValue(2, 'end_date');
$stmt->execute(); $stmt->execute();
$default = $stmt->fetch(\PDO::FETCH_COLUMN); $default = $stmt->fetch(PDO::FETCH_COLUMN);
$driver_name = $pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); $driver_name = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
return $default !== null && $driver_name === 'mysql'; return $default !== null && $driver_name === 'mysql';
} }
@ -61,10 +62,11 @@ class Fix_MySQL_No_Zero_Date implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool|void if the execution succeeded * @return bool if the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$pdo->exec('ALTER TABLE ' . Utils::table('poll') . ' MODIFY end_date TIMESTAMP NULL DEFAULT NULL;'); $pdo->exec('ALTER TABLE ' . Utils::table('poll') . ' MODIFY end_date TIMESTAMP NULL DEFAULT NULL;');
return true;
} }
} }

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* Class From_0_0_to_0_8_Migration * Class From_0_0_to_0_8_Migration
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.8 * @version 0.8
*/ */
class From_0_0_to_0_8_Migration implements Migration { class From_0_0_to_0_8_Migration implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class From_0_0_to_0_8_Migration implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'First installation of the Framadate application (v0.8)'; return 'First installation of the Framadate application (v0.8)';
} }
@ -43,12 +44,12 @@ class From_0_0_to_0_8_Migration implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES like \'' . TABLENAME_PREFIX . '%\''); //issue187 : pouvoir installer framadate dans une base contenant d'autres tables. $stmt = $pdo->query('SHOW TABLES like \'' . TABLENAME_PREFIX . '%\''); //issue187 : pouvoir installer framadate dans une base contenant d'autres tables.
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if there is no tables but the MIGRATION_TABLE one // Check if there is no tables but the MIGRATION_TABLE one
$diff = array_diff($tables, [Utils::table(MIGRATION_TABLE)]); $diff = array_diff($tables, [Utils::table(MIGRATION_TABLE)]);
@ -58,10 +59,10 @@ class From_0_0_to_0_8_Migration implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$pdo->exec(' $pdo->exec('
CREATE TABLE IF NOT EXISTS `sondage` ( CREATE TABLE IF NOT EXISTS `sondage` (
`id_sondage` char(16) NOT NULL, `id_sondage` char(16) NOT NULL,
@ -104,5 +105,6 @@ CREATE TABLE IF NOT EXISTS `user_studs` (
PRIMARY KEY (`id_users`), PRIMARY KEY (`id_users`),
KEY `id_sondage` (`id_sondage`) KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;'); ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
return true;
} }
} }

View File

@ -19,6 +19,7 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This class executes the aciton in database to migrate data from version 0.8 to 0.9. * This class executes the aciton in database to migrate data from version 0.8 to 0.9.
@ -27,7 +28,7 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class From_0_8_to_0_9_Migration implements Migration { class From_0_8_to_0_9_Migration implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -35,7 +36,7 @@ class From_0_8_to_0_9_Migration implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'From 0.8 to 0.9'; return 'From 0.8 to 0.9';
} }
@ -43,12 +44,12 @@ class From_0_8_to_0_9_Migration implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed. * @return bool true is the Migration should be executed.
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.8 are presents // Check if tables of v0.8 are presents
$diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables); $diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables);
@ -58,10 +59,10 @@ class From_0_8_to_0_9_Migration implements Migration {
/** /**
* This method is called only one time in the migration page. * This method is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->createPollTable($pdo); $this->createPollTable($pdo);
$this->createCommentTable($pdo); $this->createCommentTable($pdo);
$this->createSlotTable($pdo); $this->createSlotTable($pdo);
@ -79,7 +80,8 @@ class From_0_8_to_0_9_Migration implements Migration {
return true; return true;
} }
private function createPollTable(\PDO $pdo) { private function createPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` ( CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
`id` CHAR(16) NOT NULL, `id` CHAR(16) NOT NULL,
@ -100,7 +102,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
DEFAULT CHARSET = utf8'); DEFAULT CHARSET = utf8');
} }
private function migrateFromSondageToPoll(\PDO $pdo) { private function migrateFromSondageToPoll(PDO $pdo): void
{
$select = $pdo->query(' $select = $pdo->query('
SELECT SELECT
`id_sondage`, `id_sondage`,
@ -126,7 +129,7 @@ INSERT INTO `' . Utils::table('poll') . '`
(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`) (`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
VALUE (?,?,?,?,?,?,?,?,?,?,?,?)'); VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) { while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([ $insert->execute([
$row->id_sondage, $row->id_sondage,
$row->id_sondage_admin, $row->id_sondage_admin,
@ -144,7 +147,8 @@ VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
} }
} }
private function createSlotTable(\PDO $pdo) { private function createSlotTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` ( CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -158,7 +162,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
DEFAULT CHARSET = utf8'); DEFAULT CHARSET = utf8');
} }
private function migrateFromSujetStudsToSlot(\PDO $pdo) { private function migrateFromSujetStudsToSlot(PDO $pdo): void
{
$stmt = $pdo->query('SELECT * FROM sujet_studs'); $stmt = $pdo->query('SELECT * FROM sujet_studs');
$sujets = $stmt->fetchAll(); $sujets = $stmt->fetchAll();
$slots = []; $slots = [];
@ -180,7 +185,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
} }
} }
private function createCommentTable(\PDO $pdo) { private function createCommentTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` ( CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -194,7 +200,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
DEFAULT CHARSET = utf8'); DEFAULT CHARSET = utf8');
} }
private function migrateFromCommentsToComment(\PDO $pdo) { private function migrateFromCommentsToComment(PDO $pdo): void
{
$select = $pdo->query(' $select = $pdo->query('
SELECT SELECT
`id_sondage`, `id_sondage`,
@ -206,7 +213,7 @@ SELECT
INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`) INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`)
VALUE (?,?,?)'); VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) { while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([ $insert->execute([
$row->id_sondage, $row->id_sondage,
$this->unescape($row->usercomment), $this->unescape($row->usercomment),
@ -215,7 +222,8 @@ VALUE (?,?,?)');
} }
} }
private function createVoteTable(\PDO $pdo) { private function createVoteTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` ( CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -229,7 +237,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
DEFAULT CHARSET = utf8'); DEFAULT CHARSET = utf8');
} }
private function migrateFromUserStudsToVote(\PDO $pdo) { private function migrateFromUserStudsToVote(PDO $pdo): void
{
$select = $pdo->query(' $select = $pdo->query('
SELECT SELECT
`id_sondage`, `id_sondage`,
@ -241,7 +250,7 @@ SELECT
INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`) INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`)
VALUE (?,?,?)'); VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) { while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([ $insert->execute([
$row->id_sondage, $row->id_sondage,
$this->unescape($row->nom), $this->unescape($row->nom),
@ -250,7 +259,8 @@ VALUE (?,?,?)');
} }
} }
private function transformSujetToSlot($sujet) { private function transformSujetToSlot($sujet): array
{
$slots = []; $slots = [];
$ex = explode(',', $sujet->sujet); $ex = explode(',', $sujet->sujet);
$isDatePoll = strpos($sujet->sujet, '@'); $isDatePoll = strpos($sujet->sujet, '@');
@ -279,14 +289,16 @@ VALUE (?,?,?)');
return $slots; return $slots;
} }
private function dropOldTables(\PDO $pdo) { private function dropOldTables(PDO $pdo): void
{
$pdo->exec('DROP TABLE `comments`'); $pdo->exec('DROP TABLE `comments`');
$pdo->exec('DROP TABLE `sujet_studs`'); $pdo->exec('DROP TABLE `sujet_studs`');
$pdo->exec('DROP TABLE `user_studs`'); $pdo->exec('DROP TABLE `user_studs`');
$pdo->exec('DROP TABLE `sondage`'); $pdo->exec('DROP TABLE `sondage`');
} }
private function unescape($value) { private function unescape(string $value): string
{
return stripslashes(html_entity_decode($value, ENT_QUOTES)); return stripslashes(html_entity_decode($value, ENT_QUOTES));
} }
} }

View File

@ -20,6 +20,7 @@ namespace Framadate\Migration;
use Framadate\Security\Token; use Framadate\Security\Token;
use Framadate\Utils; use Framadate\Utils;
use PDO;
/** /**
* This migration generate uniqId for all legacy votes. * This migration generate uniqId for all legacy votes.
@ -28,16 +29,16 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class Generate_uniqId_for_old_votes implements Migration { class Generate_uniqId_for_old_votes implements Migration {
function __construct() { public function __construct() {
} }
function description() { public function description(): string {
return 'Generate "uniqId" in "vote" table for all legacy votes'; return 'Generate "uniqId" in "vote" table for all legacy votes';
} }
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents // Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables); $diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
@ -47,10 +48,10 @@ class Generate_uniqId_for_old_votes implements Migration {
/** /**
* This methode is called only one time in the migration page. * This methode is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true is the execution succeeded * @return bool true is the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$pdo->beginTransaction(); $pdo->beginTransaction();
$this->generateUniqIdsForEmptyOnes($pdo); $this->generateUniqIdsForEmptyOnes($pdo);
$pdo->commit(); $pdo->commit();
@ -58,7 +59,8 @@ class Generate_uniqId_for_old_votes implements Migration {
return true; return true;
} }
private function generateUniqIdsForEmptyOnes($pdo) { private function generateUniqIdsForEmptyOnes(PDO $pdo): void
{
$select = $pdo->query(' $select = $pdo->query('
SELECT `id` SELECT `id`
FROM `' . Utils::table('vote') . '` FROM `' . Utils::table('vote') . '`
@ -69,7 +71,7 @@ UPDATE `' . Utils::table('vote') . '`
SET `uniqid` = :uniqid SET `uniqid` = :uniqid
WHERE `id` = :id'); WHERE `id` = :id');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) { while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$token = Token::getToken(16); $token = Token::getToken(16);
$update->execute([ $update->execute([
'uniqid' => $token, 'uniqid' => $token,

View File

@ -2,9 +2,10 @@
namespace Framadate\Migration; namespace Framadate\Migration;
use Framadate\Utils; use Framadate\Utils;
use PDO;
class Increase_pollId_size implements Migration { class Increase_pollId_size implements Migration {
function __construct() { public function __construct() {
} }
/** /**
@ -12,7 +13,7 @@ class Increase_pollId_size implements Migration {
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description() { public function description(): string {
return 'Increase the size of id column in poll table'; return 'Increase the size of id column in poll table';
} }
@ -20,45 +21,50 @@ class Increase_pollId_size implements Migration {
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed * @return bool true if the Migration should be executed
*/ */
function preCondition(\PDO $pdo) { public function preCondition(PDO $pdo): bool {
return true; return true;
} }
/** /**
* This methode is called only one time in the migration page. * This methode is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true if the execution succeeded * @return bool true if the execution succeeded
*/ */
function execute(\PDO $pdo) { public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo); $this->alterCommentTable($pdo);
$this->alterPollTable($pdo); $this->alterPollTable($pdo);
$this->alterSlotTable($pdo); $this->alterSlotTable($pdo);
$this->alterVoteTable($pdo); $this->alterVoteTable($pdo);
return true;
} }
private function alterCommentTable(\PDO $pdo) { private function alterCommentTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '` ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;'); CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
} }
private function alterPollTable(\PDO $pdo) { private function alterPollTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '` ALTER TABLE `' . Utils::table('poll') . '`
CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;'); CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
} }
private function alterSlotTable(\PDO $pdo) { private function alterSlotTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('slot') . '` ALTER TABLE `' . Utils::table('slot') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;'); CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
} }
private function alterVoteTable(\PDO $pdo) { private function alterVoteTable(PDO $pdo): void
{
$pdo->exec(' $pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '` ALTER TABLE `' . Utils::table('vote') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;'); CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');

View File

@ -18,29 +18,30 @@
*/ */
namespace Framadate\Migration; namespace Framadate\Migration;
use PDO;
interface Migration { interface Migration {
/** /**
* This method should describe in english what is the purpose of the migration class. * This method should describe in english what is the purpose of the migration class.
* *
* @return string The description of the migration class * @return string The description of the migration class
*/ */
function description(); public function description(): string;
/** /**
* This method could check if the execute method should be called. * This method could check if the execute method should be called.
* It is called before the execute method. * It is called before the execute method.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed * @return bool true if the Migration should be executed
*/ */
function preCondition(\PDO $pdo); public function preCondition(PDO $pdo): bool;
/** /**
* This methode is called only one time in the migration page. * This methode is called only one time in the migration page.
* *
* @param \PDO $pdo The connection to database * @param PDO $pdo The connection to database
* @return bool true if the execution succeeded * @return bool true if the execution succeeded
*/ */
function execute(\PDO $pdo); public function execute(PDO $pdo): bool;
} }

View File

@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at * is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt * 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 STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphael DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft) * 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 * 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 * ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt * 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
namespace Framadate\Migration; namespace Framadate\Migration;
@ -28,11 +28,11 @@ use Framadate\Utils;
* @version 0.9 * @version 0.9
*/ */
class RPadVotes_from_0_8 implements Migration { class RPadVotes_from_0_8 implements Migration {
function description() { public function description(): string {
return 'RPad votes from version 0.8.'; return 'RPad votes from version 0.8.';
} }
function preCondition(\PDO $pdo) { public function preCondition(\PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES'); $stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN); $tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
@ -41,7 +41,7 @@ class RPadVotes_from_0_8 implements Migration {
return count($diff) === 0; return count($diff) === 0;
} }
function execute(\PDO $pdo) { public function execute(\PDO $pdo): bool {
$pdo->beginTransaction(); $pdo->beginTransaction();
$this->rpadVotes($pdo); $this->rpadVotes($pdo);
$pdo->commit(); $pdo->commit();
@ -49,7 +49,8 @@ class RPadVotes_from_0_8 implements Migration {
return true; return true;
} }
private function rpadVotes($pdo) { private function rpadVotes(\PDO $pdo): void
{
$pdo->exec('UPDATE ' . Utils::table('vote') . ' fv $pdo->exec('UPDATE ' . Utils::table('vote') . ' fv
INNER JOIN ( INNER JOIN (
SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices
@ -63,4 +64,4 @@ INNER JOIN (
) computed ON fv.id = computed.id ) computed ON fv.id = computed.id
SET fv.choices = computed.new_choices'); SET fv.choices = computed.new_choices');
} }
} }

View File

@ -13,31 +13,40 @@ abstract class AbstractRepository {
* PollRepository constructor. * PollRepository constructor.
* @param FramaDB $connect * @param FramaDB $connect
*/ */
function __construct(FramaDB $connect) { public function __construct(FramaDB $connect) {
$this->connect = $connect; $this->connect = $connect;
} }
public function beginTransaction() { public function beginTransaction(): void
{
$this->connect->beginTransaction(); $this->connect->beginTransaction();
} }
public function commit() { public function commit(): void
{
$this->connect->commit(); $this->connect->commit();
} }
function rollback() { public function rollback(): void
{
$this->connect->rollback(); $this->connect->rollback();
} }
public function prepare($sql) { /**
* @return \PDOStatement|false
*/
public function prepare(string $sql) {
return $this->connect->prepare($sql); return $this->connect->prepare($sql);
} }
function query($sql) { /**
* @return \PDOStatement|false
*/
public function query($sql) {
return $this->connect->query($sql); return $this->connect->query($sql);
} }
function lastInsertId() { public function lastInsertId(): string {
return $this->connect->lastInsertId(); return $this->connect->lastInsertId();
} }
} }

View File

@ -1,15 +1,13 @@
<?php <?php
namespace Framadate\Repositories; namespace Framadate\Repositories;
use Framadate\FramaDB;
use Framadate\Utils; use Framadate\Utils;
class CommentRepository extends AbstractRepository { class CommentRepository extends AbstractRepository {
function __construct(FramaDB $connect) { /**
parent::__construct($connect); * @return array|false
} */
public function findAllByPollId(string $poll_id) {
function findAllByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]); $prepared->execute([$poll_id]);
@ -19,18 +17,20 @@ class CommentRepository extends AbstractRepository {
/** /**
* Insert a new comment. * Insert a new comment.
* *
* @param $poll_id * @param string $poll_id
* @param $name * @param string $name
* @param $comment * @param string $comment
* @return bool * @return bool
*/ */
function insert($poll_id, $name, $comment) { public function insert(string $poll_id, string $name, string $comment): bool
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)'); $prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $name, $comment]); return $prepared->execute([$poll_id, $name, $comment]);
} }
function deleteById($poll_id, $comment_id) { public function deleteById(string $poll_id, int $comment_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $comment_id]); return $prepared->execute([$poll_id, $comment_id]);
@ -39,16 +39,18 @@ class CommentRepository extends AbstractRepository {
/** /**
* Delete all comments of a given poll. * Delete all comments of a given poll.
* *
* @param $poll_id int The ID of the given poll. * @param string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded. * @return bool|null true if action succeeded.
*/ */
function deleteByPollId($poll_id) { public function deleteByPollId(string $poll_id): ?bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]); return $prepared->execute([$poll_id]);
} }
public function exists($poll_id, $name, $comment) { public function exists(string $poll_id, string $name, string $comment): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?'); $prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?');
$prepared->execute([$poll_id, $name, $comment]); $prepared->execute([$poll_id, $name, $comment]);

View File

@ -6,11 +6,8 @@ use Framadate\Utils;
use PDO; use PDO;
class PollRepository extends AbstractRepository { class PollRepository extends AbstractRepository {
function __construct(FramaDB $connect) { public function insertPoll(string $poll_id, string $admin_poll_id, $form): void
parent::__construct($connect); {
}
public function insertPoll($poll_id, $admin_poll_id, $form) {
$sql = 'INSERT INTO `' . Utils::table('poll') . '` $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) (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(?),?,?,?,?,?,?,?,?)'; VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?)';
@ -18,7 +15,7 @@ class PollRepository extends AbstractRepository {
$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]); $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) { public function findById(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared->execute([$poll_id]); $prepared->execute([$poll_id]);
@ -28,7 +25,7 @@ class PollRepository extends AbstractRepository {
return $poll; return $poll;
} }
public function findByAdminId($admin_poll_id) { public function findByAdminId(string $admin_poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
$prepared->execute([$admin_poll_id]); $prepared->execute([$admin_poll_id]);
@ -38,7 +35,8 @@ class PollRepository extends AbstractRepository {
return $poll; return $poll;
} }
public function existsById($poll_id) { public function existsById(string $poll_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?'); $prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared->execute([$poll_id]); $prepared->execute([$poll_id]);
@ -46,7 +44,8 @@ class PollRepository extends AbstractRepository {
return $prepared->rowCount() > 0; return $prepared->rowCount() > 0;
} }
public function existsByAdminId($admin_poll_id) { public function existsByAdminId(string $admin_poll_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?'); $prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
$prepared->execute([$admin_poll_id]); $prepared->execute([$admin_poll_id]);
@ -54,13 +53,15 @@ class PollRepository extends AbstractRepository {
return $prepared->rowCount() > 0; return $prepared->rowCount() > 0;
} }
function update($poll) { public function update($poll): bool
{
$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 = ?'); $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 ? 1 : 0, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]); return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active ? 1 : 0, ($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) { public function deleteById($poll_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
return $prepared->execute([$poll_id]); return $prepared->execute([$poll_id]);
@ -71,7 +72,8 @@ class PollRepository extends AbstractRepository {
* *
* @return array Array of old polls * @return array Array of old polls
*/ */
public function findOldPolls() { public function findOldPolls(): array
{
$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 = $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([]); $prepared->execute([]);
@ -86,50 +88,50 @@ class PollRepository extends AbstractRepository {
* @param int $limit The number of entries to find * @param int $limit The number of entries to find
* @return array The found polls * @return array The found polls
*/ */
public function findAll($search, $start, $limit) { public function findAll(array $search, int $start, int $limit): array
{
// Polls // Polls
$request = ""; $request = "SELECT p.*,";
$request .= "SELECT p.*,";
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes"; $request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
$request .= " FROM `" . Utils::table('poll') . "` p"; $request .= " FROM `" . Utils::table('poll') . "` p";
$request .= " WHERE 1"; $request .= " WHERE 1";
$values = []; $values = [];
if (!empty($search["poll"])) { if (!empty($search["poll"])) {
$request .= " AND p.id LIKE :poll"; $request .= " AND p.id LIKE :poll";
$values["poll"] = "{$search["poll"]}%"; $values["poll"] = "{$search["poll"]}%";
} }
$fields = [ $fields = [
// key of $search => column name // key of $search => column name
"title" => "title", "title" => "title",
"name" => "admin_name", "name" => "admin_name",
"mail" => "admin_mail", "mail" => "admin_mail",
]; ];
foreach ($fields as $searchKey => $columnName) { foreach ($fields as $searchKey => $columnName) {
if (empty($search[$searchKey])) { if (empty($search[$searchKey])) {
continue; continue;
} }
$request .= " AND p.$columnName LIKE :$searchKey"; $request .= " AND p.$columnName LIKE :$searchKey";
$values[$searchKey] = "%{$search[$searchKey]}%"; $values[$searchKey] = "%$search[$searchKey]%";
} }
$request .= " ORDER BY p.title ASC"; $request .= " ORDER BY p.title ASC";
$request .= " LIMIT :start, :limit"; $request .= " LIMIT :start, :limit";
$prepared = $this->prepare($request); $prepared = $this->prepare($request);
foreach ($values as $searchKey => $value) { foreach ($values as $searchKey => $value) {
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR); $prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
} }
$prepared->bindParam(':start', $start, PDO::PARAM_INT); $prepared->bindParam(':start', $start, PDO::PARAM_INT);
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT); $prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
$prepared->execute(); $prepared->execute();
return $prepared->fetchAll(); return $prepared->fetchAll();
@ -141,7 +143,8 @@ class PollRepository extends AbstractRepository {
* @param string $mail Email address of the poll admin * @param string $mail Email address of the poll admin
* @return array The list of matching polls * @return array The list of matching polls
*/ */
public function findAllByAdminMail($mail) { public function findAllByAdminMail(string $mail): array
{
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
$prepared->execute(['admin_mail' => $mail]); $prepared->execute(['admin_mail' => $mail]);
@ -149,12 +152,13 @@ class PollRepository extends AbstractRepository {
} }
/** /**
* Get the total number of polls in databse. * Get the total number of polls in database.
* *
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...] * @param array|null $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @return int The number of polls * @return int The number of polls
*/ */
public function count($search = null) { public function count(array $search = null): int
{
// Total count // Total count
$prepared = $this->prepare(' $prepared = $this->prepare('
SELECT count(1) nb SELECT count(1) nb
@ -172,13 +176,7 @@ SELECT count(1) nb
$prepared->bindParam(':name', $name, PDO::PARAM_STR); $prepared->bindParam(':name', $name, PDO::PARAM_STR);
$prepared->execute(); $prepared->execute();
$count = $prepared->fetch();
/*echo '---'; return $prepared->fetch()->nb;
print_r($count);
echo '---';
exit;*/
return $count->nb;
} }
} }

View File

@ -31,14 +31,15 @@ class RepositoryFactory {
/** /**
* @param FramaDB $connect * @param FramaDB $connect
*/ */
static function init(FramaDB $connect) { public static function init(FramaDB $connect): void {
self::$connect = $connect; self::$connect = $connect;
} }
/** /**
* @return PollRepository The singleton of PollRepository * @return PollRepository The singleton of PollRepository
*/ */
static function pollRepository() { public static function pollRepository(): PollRepository
{
if (self::$pollRepository === null) { if (self::$pollRepository === null) {
self::$pollRepository = new PollRepository(self::$connect); self::$pollRepository = new PollRepository(self::$connect);
} }
@ -49,7 +50,8 @@ class RepositoryFactory {
/** /**
* @return SlotRepository The singleton of SlotRepository * @return SlotRepository The singleton of SlotRepository
*/ */
static function slotRepository() { public static function slotRepository(): SlotRepository
{
if (self::$slotRepository === null) { if (self::$slotRepository === null) {
self::$slotRepository = new SlotRepository(self::$connect); self::$slotRepository = new SlotRepository(self::$connect);
} }
@ -60,7 +62,8 @@ class RepositoryFactory {
/** /**
* @return VoteRepository The singleton of VoteRepository * @return VoteRepository The singleton of VoteRepository
*/ */
static function voteRepository() { public static function voteRepository(): VoteRepository
{
if (self::$voteRepository === null) { if (self::$voteRepository === null) {
self::$voteRepository = new VoteRepository(self::$connect); self::$voteRepository = new VoteRepository(self::$connect);
} }
@ -71,7 +74,8 @@ class RepositoryFactory {
/** /**
* @return CommentRepository The singleton of CommentRepository * @return CommentRepository The singleton of CommentRepository
*/ */
static function commentRepository() { public static function commentRepository(): CommentRepository
{
if (self::$commentRepository === null) { if (self::$commentRepository === null) {
self::$commentRepository = new CommentRepository(self::$connect); self::$commentRepository = new CommentRepository(self::$connect);
} }

View File

@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at * is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt * 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 STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphael DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft) * 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 * 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 * ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt * 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
namespace Framadate\Repositories; namespace Framadate\Repositories;
@ -22,17 +22,14 @@ use Framadate\FramaDB;
use Framadate\Utils; use Framadate\Utils;
class SlotRepository extends AbstractRepository { class SlotRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
/** /**
* Insert a bulk of slots. * Insert a bulk of slots.
* *
* @param int $poll_id * @param string $poll_id
* @param array $choices * @param array $choices
*/ */
public function insertSlots($poll_id, $choices) { public function insertSlots(string $poll_id, array $choices): void
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)'); $prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
foreach ($choices as $choice) { foreach ($choices as $choice) {
@ -57,7 +54,10 @@ class SlotRepository extends AbstractRepository {
} }
} }
function listByPollId($poll_id) { /**
* @return array|false
*/
public function listByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]); $prepared->execute([$poll_id]);
@ -67,11 +67,11 @@ class SlotRepository extends AbstractRepository {
/** /**
* Find the slot into poll for a given datetime. * Find the slot into poll for a given datetime.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $datetime int The datetime of the slot * @param $datetime int The datetime of the slot
* @return mixed Object The slot found, or null * @return mixed Object The slot found, or null
*/ */
function findByPollIdAndDatetime($poll_id, $datetime) { public function findByPollIdAndDatetime(string $poll_id, $datetime) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
$prepared->execute([$poll_id, $datetime]); $prepared->execute([$poll_id, $datetime]);
@ -84,12 +84,13 @@ class SlotRepository extends AbstractRepository {
/** /**
* Insert a new slot into a given poll. * Insert a new slot into a given poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $title mixed The title of the slot * @param $title mixed The title of the slot
* @param $moments mixed|null The moments joined with "," * @param $moments mixed|null The moments joined with ","
* @return bool true if action succeeded * @return bool true if action succeeded
*/ */
function insert($poll_id, $title, $moments) { public function insert(string $poll_id, string $title, ?string $moments): bool
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)'); $prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $title, $moments]); return $prepared->execute([$poll_id, $title, $moments]);
@ -98,12 +99,13 @@ class SlotRepository extends AbstractRepository {
/** /**
* Update a slot into a poll. * Update a slot into a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $datetime int The datetime of the slot to update * @param $datetime int The datetime of the slot to update
* @param $newMoments mixed The new moments * @param $newMoments mixed The new moments
* @return bool|null true if action succeeded. * @return bool|null true if action succeeded.
*/ */
function update($poll_id, $datetime, $newMoments) { public function update(string $poll_id, $datetime, $newMoments): ?bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?'); $prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
return $prepared->execute([$newMoments, $poll_id, $datetime]); return $prepared->execute([$newMoments, $poll_id, $datetime]);
@ -112,15 +114,17 @@ class SlotRepository extends AbstractRepository {
/** /**
* Delete a entire slot from a poll. * Delete a entire slot from a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id int The ID of the poll
* @param $datetime mixed The datetime of the slot * @param $datetime mixed The datetime of the slot
*/ */
function deleteByDateTime($poll_id, $datetime) { public function deleteByDateTime(string $poll_id, $datetime): void
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
$prepared->execute([$poll_id, $datetime]); $prepared->execute([$poll_id, $datetime]);
} }
function deleteByPollId($poll_id) { public function deleteByPollId(string $poll_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]); return $prepared->execute([$poll_id]);

View File

@ -5,24 +5,24 @@ use Framadate\FramaDB;
use Framadate\Utils; use Framadate\Utils;
class VoteRepository extends AbstractRepository { class VoteRepository extends AbstractRepository {
function __construct(FramaDB $connect) { /**
parent::__construct($connect); * @return array|false
} */
public function allUserVotesByPollId(string $poll_id) {
function allUserVotesByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id'); $prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]); $prepared->execute([$poll_id]);
return $prepared->fetchAll(); return $prepared->fetchAll();
} }
function insertDefault($poll_id, $insert_position) { public function insertDefault(string $poll_id, int $insert_position): bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote $prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]); return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
} }
function insert($poll_id, $name, $choices, $token) { public function insert(string $poll_id, string $name, string $choices, string $token): \stdClass {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices, uniqId) VALUES (?,?,?,?)'); $prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices, uniqId) VALUES (?,?,?,?)');
$prepared->execute([$poll_id, $name, $choices, $token]); $prepared->execute([$poll_id, $name, $choices, $token]);
@ -36,7 +36,8 @@ class VoteRepository extends AbstractRepository {
return $newVote; return $newVote;
} }
function deleteById($poll_id, $vote_id) { public function deleteById(string $poll_id, int $vote_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $vote_id]); return $prepared->execute([$poll_id, $vote_id]);
@ -45,10 +46,11 @@ class VoteRepository extends AbstractRepository {
/** /**
* Delete all votes of a given poll. * Delete all votes of a given poll.
* *
* @param $poll_id int The ID of the given poll. * @param string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded. * @return bool|null true if action succeeded.
*/ */
function deleteByPollId($poll_id) { public function deleteByPollId(string $poll_id): ?bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?'); $prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]); return $prepared->execute([$poll_id]);
@ -57,17 +59,19 @@ class VoteRepository extends AbstractRepository {
/** /**
* Delete all votes made on given moment index. * Delete all votes made on given moment index.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $index int The index of the vote into the poll * @param $index int The index of the vote into the poll
* @return bool|null true if action succeeded. * @return bool|null true if action succeeded.
*/ */
function deleteByIndex($poll_id, $index) { public function deleteByIndex(string $poll_id, int $index): ?bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE poll_id = ?'); $prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE poll_id = ?');
return $prepared->execute([$index, $index + 2, $poll_id]); return $prepared->execute([$index, $index + 2, $poll_id]);
} }
function update($poll_id, $vote_id, $name, $choices) { public function update(string $poll_id, string $vote_id, string $name, $choices): bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?'); $prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
return $prepared->execute([$choices, $name, $poll_id, $vote_id]); return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
@ -76,25 +80,27 @@ class VoteRepository extends AbstractRepository {
/** /**
* Check if name is already used for the given poll. * Check if name is already used for the given poll.
* *
* @param int $poll_id ID of the poll * @param string $poll_id ID of the poll
* @param string $name Name of the vote * @param string $name Name of the vote
* @return bool true if vote already exists * @return bool true if vote already exists
*/ */
public function existsByPollIdAndName($poll_id, $name) { public function existsByPollIdAndName(string $poll_id, string $name): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?'); $prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?');
$prepared->execute([$poll_id, $name]); $prepared->execute([$poll_id, $name]);
return $prepared->rowCount() > 0; return $prepared->rowCount() > 0;
} }
/** /**
* Check if name is already used for the given poll and another vote. * Check if name is already used for the given poll and another vote.
* *
* @param int $poll_id ID of the poll * @param string $poll_id ID of the poll
* @param string $name Name of the vote * @param string $name Name of the vote
* @param int $vote_id ID of the current vote * @param int $vote_id ID of the current vote
* @return bool true if vote already exists * @return bool true if vote already exists
*/ */
public function existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id) { public function existsByPollIdAndNameAndVoteId(string $poll_id, string $name, int $vote_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ? AND id != ?'); $prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ? AND id != ?');
$prepared->execute([$poll_id, $name, $vote_id]); $prepared->execute([$poll_id, $name, $vote_id]);
return $prepared->rowCount() > 0; return $prepared->rowCount() > 0;

View File

@ -16,7 +16,7 @@ class PasswordHasher {
* @param string $password the password to hash. * @param string $password the password to hash.
* @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash. * @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash.
*/ */
public static function hash($password) { public static function hash(string $password) {
return password_hash($password, PASSWORD_DEFAULT); return password_hash($password, PASSWORD_DEFAULT);
} }
@ -27,7 +27,8 @@ class PasswordHasher {
* @param string $hash the hash to compare. * @param string $hash the hash to compare.
* @return bool * @return bool
*/ */
public static function verify($password, $hash) { public static function verify(string $password, string $hash): bool
{
return password_verify($password, $hash); return password_verify($password, $hash);
} }
} }

View File

@ -2,31 +2,35 @@
namespace Framadate\Security; namespace Framadate\Security;
class Token { class Token {
const DEFAULT_LENGTH = 64; public const DEFAULT_LENGTH = 64;
private $time; private $time;
private $value; private $value;
private $length; private $length;
private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789'; private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
function __construct($length = self::DEFAULT_LENGTH) { public function __construct($length = self::DEFAULT_LENGTH) {
$this->length = $length; $this->length = $length;
$this->time = time() + TOKEN_TIME; $this->time = time() + TOKEN_TIME;
$this->value = $this->generate(); $this->value = $this->generate();
} }
public function getTime() { public function getTime(): int
{
return $this->time; return $this->time;
} }
public function getValue() { public function getValue(): string
{
return $this->value; return $this->value;
} }
public function isGone() { public function isGone(): bool
{
return $this->time < time(); return $this->time < time();
} }
public function check($value) { public function check($value): bool
{
return $value === $this->value; return $value === $this->value;
} }
@ -37,7 +41,8 @@ class Token {
* @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not. * @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not.
* @return string * @return string
*/ */
public static function getToken($length = self::DEFAULT_LENGTH, &$crypto_strong = false) { public static function getToken(int $length = self::DEFAULT_LENGTH, bool &$crypto_strong = false): string
{
if (function_exists('openssl_random_pseudo_bytes')) { if (function_exists('openssl_random_pseudo_bytes')) {
openssl_random_pseudo_bytes(1, $crypto_strong); // Fake use to see if the algorithm used was "cryptographically strong" openssl_random_pseudo_bytes(1, $crypto_strong); // Fake use to see if the algorithm used was "cryptographically strong"
return self::getSecureToken($length); return self::getSecureToken($length);
@ -45,7 +50,8 @@ class Token {
return self::getUnsecureToken($length); return self::getUnsecureToken($length);
} }
public static function getUnsecureToken($length) { public static function getUnsecureToken(int $length): string
{
$string = ''; $string = '';
mt_srand(); mt_srand();
for ($i = 0; $i < $length; $i++) { for ($i = 0; $i < $length; $i++) {
@ -58,7 +64,8 @@ class Token {
/** /**
* @author http://stackoverflow.com/a/13733588 * @author http://stackoverflow.com/a/13733588
*/ */
public static function getSecureToken($length){ public static function getSecureToken(int $length): string
{
$token = ""; $token = "";
for($i=0;$i<$length;$i++){ for($i=0;$i<$length;$i++){
$token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))]; $token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))];
@ -66,25 +73,33 @@ class Token {
return $token; return $token;
} }
private function generate() { private function generate(): string
{
return self::getToken($this->length); return self::getToken($this->length);
} }
/** /**
* @author http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322 * @author http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
*
* @param int $max
*
* @psalm-param 0 $min
* @psalm-param 0|positive-int $max
*/ */
private static function crypto_rand_secure($min, $max) { private static function crypto_rand_secure(int $min, $max): int {
$range = $max - $min; $range = $max - $min;
if ($range < 0) return $min; // not so random... // not so random...
if ($range < 0) {
return $min;
}
$log = log($range, 2); $log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes $bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits $bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1 $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do { do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))); $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits $rnd &= $filter; // discard irrelevant bits
} while ($rnd >= $range); } while ($rnd >= $range);
return $min + $rnd; return $min + $rnd;
} }
} }

View File

@ -21,7 +21,7 @@ class AdminPollService {
private $voteRepository; private $voteRepository;
private $commentRepository; private $commentRepository;
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) { public function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect; $this->connect = $connect;
$this->pollService = $pollService; $this->pollService = $pollService;
$this->logService = $logService; $this->logService = $logService;
@ -31,32 +31,34 @@ class AdminPollService {
$this->commentRepository = RepositoryFactory::commentRepository(); $this->commentRepository = RepositoryFactory::commentRepository();
} }
function updatePoll($poll) { public function updatePoll($poll): bool
{
global $config; global $config;
if ($poll->end_date > $poll->creation_date) { if ($poll->end_date > $poll->creation_date) {
return $this->pollRepository->update($poll); return $this->pollRepository->update($poll);
} }
return false; return false;
} }
/** /**
* Delete a comment from a poll. * Delete a comment from a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $comment_id int The ID of the comment * @param $comment_id int The ID of the comment
* @return mixed true is action succeeded * @return mixed true is action succeeded
*/ */
function deleteComment($poll_id, $comment_id) { public function deleteComment(string $poll_id, int $comment_id) {
return $this->commentRepository->deleteById($poll_id, $comment_id); return $this->commentRepository->deleteById($poll_id, $comment_id);
} }
/** /**
* Remove all comments of a poll. * Remove all comments of a poll.
* *
* @param $poll_id int The ID a the poll * @param string $poll_id The ID a the poll
* @return bool|null true is action succeeded * @return bool|null true is action succeeded
*/ */
function cleanComments($poll_id) { public function cleanComments(string $poll_id): ?bool
{
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id"); $this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
return $this->commentRepository->deleteByPollId($poll_id); return $this->commentRepository->deleteByPollId($poll_id);
} }
@ -64,21 +66,23 @@ class AdminPollService {
/** /**
* Delete a vote from a poll. * Delete a vote from a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $vote_id int The ID of the vote * @param $vote_id int The ID of the vote
* @return mixed true is action succeeded * @return bool true is action succeeded
*/ */
function deleteVote($poll_id, $vote_id) { public function deleteVote(string $poll_id, int $vote_id): bool
{
return $this->voteRepository->deleteById($poll_id, $vote_id); return $this->voteRepository->deleteById($poll_id, $vote_id);
} }
/** /**
* Remove all votes of a poll. * Remove all votes of a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @return bool|null true is action succeeded * @return bool|null true is action succeeded
*/ */
function cleanVotes($poll_id) { public function cleanVotes(string $poll_id): ?bool
{
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id); $this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
return $this->voteRepository->deleteByPollId($poll_id); return $this->voteRepository->deleteByPollId($poll_id);
} }
@ -86,10 +90,11 @@ class AdminPollService {
/** /**
* Delete the entire given poll. * Delete the entire given poll.
* *
* @param $poll_id int The ID of the poll * @param $poll_id string The ID of the poll
* @return bool true is action succeeded * @return bool true is action succeeded
*/ */
function deleteEntirePoll($poll_id) { public function deleteEntirePoll(string $poll_id): bool
{
$poll = $this->pollRepository->findById($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"); $this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
@ -109,7 +114,8 @@ class AdminPollService {
* @param object $slot The slot informations (datetime + moment) * @param object $slot The slot informations (datetime + moment)
* @return bool true if action succeeded * @return bool true if action succeeded
*/ */
public function deleteDateSlot($poll, $slot) { public function deleteDateSlot(object $poll, object $slot): bool
{
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot)); $this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$datetime = $slot->title; $datetime = $slot->title;
@ -120,7 +126,9 @@ class AdminPollService {
// We can't delete the last slot // We can't delete the last slot
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) { if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
return false; return false;
} elseif ($poll->format === 'A' && count($slots) === 1) { }
if ($poll->format === 'A' && count($slots) === 1) {
return false; return false;
} }
@ -157,7 +165,8 @@ class AdminPollService {
return true; return true;
} }
public function deleteClassicSlot($poll, $slot_title) { public function deleteClassicSlot($poll, string $slot_title): bool
{
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title); $this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
$slots = $this->pollService->allSlotsByPoll($poll); $slots = $this->pollService->allSlotsByPoll($poll);
@ -193,12 +202,13 @@ class AdminPollService {
* <li>Create a new moment if a slot already exists for the given date</li> * <li>Create a new moment if a slot already exists for the given date</li>
* </ul> * </ul>
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $datetime int The datetime * @param $datetime int The datetime
* @param $new_moment string The moment's name * @param $new_moment string The moment's name
* @throws MomentAlreadyExistsException When the moment to add already exists in database * @throws MomentAlreadyExistsException When the moment to add already exists in database
*/ */
public function addDateSlot($poll_id, $datetime, $new_moment) { public function addDateSlot(string $poll_id, int $datetime, string $new_moment): void
{
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment); $this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$slots = $this->slotRepository->listByPollId($poll_id); $slots = $this->slotRepository->listByPollId($poll_id);
@ -235,17 +245,18 @@ class AdminPollService {
* <li>Create a new slot if no one exists for the given title</li> * <li>Create a new slot if no one exists for the given title</li>
* </ul> * </ul>
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @param $title int The title * @param string $title The title
* @throws MomentAlreadyExistsException When the moment to add already exists in database * @throws MomentAlreadyExistsException When the moment to add already exists in database
*/ */
public function addClassicSlot($poll_id, $title) { public function addClassicSlot(string $poll_id, string $title): void
{
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title); $this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
$slots = $this->slotRepository->listByPollId($poll_id); $slots = $this->slotRepository->listByPollId($poll_id);
// Check if slot already exists // Check if slot already exists
$titles = array_map(function ($slot) { $titles = array_map(static function ($slot) {
return $slot->title; return $slot->title;
}, $slots); }, $slots);
if (in_array($title, $titles, true)) { if (in_array($title, $titles, true)) {
@ -274,7 +285,7 @@ class AdminPollService {
* @param $datetime int The datetime of the new slot * @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. * @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/ */
private function findInsertPosition($slots, $datetime) { private function findInsertPosition(array $slots, int $datetime) {
$result = new \stdClass(); $result = new \stdClass();
$result->slot = null; $result->slot = null;
$result->insert = 0; $result->insert = 0;
@ -292,14 +303,15 @@ class AdminPollService {
$result->insert += count($moments); $result->insert += count($moments);
$result->slot = $slot; $result->slot = $slot;
break; break;
} elseif ($datetime < $rowDatetime) { }
if ($datetime < $rowDatetime) {
// We have to insert before this slot // We have to insert before this slot
break; break;
} }
$result->insert += count($moments); $result->insert += count($moments);
} }
return $result; return $result;
} }
} }

View File

@ -19,37 +19,15 @@
namespace Framadate\Services; namespace Framadate\Services;
use DateTime; use DateTime;
use Framadate\Repositories\RepositoryFactory; use Framadate\Utils;
use Sabre\VObject; use Sabre\VObject;
class ICalService { class ICalService {
/**
* @var NotificationService
*/
private $notificationService;
/**
* @var SessionService
*/
private $sessionService;
/**
* @var LogService
*/
private $logService;
public function __construct(LogService $logService, NotificationService $notificationService, SessionService $sessionService) {
$this->logService = $logService;
$this->notificationService = $notificationService;
$this->sessionService = $sessionService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
$this->voteRepository = RepositoryFactory::voteRepository();
$this->commentRepository = RepositoryFactory::commentRepository();
}
/** /**
* Creates an ical-File and initiates the download. If possible, the provided time is used, else an all day event is created. * Creates an ical-File and initiates the download. If possible, the provided time is used, else an all day event is created.
*/ */
public function getEvent($poll, string $start_day, string $start_time) { public function getEvent($poll, string $start_day, string $start_time): void
{
if(!$this->dayIsReadable($start_day)) { if(!$this->dayIsReadable($start_day)) {
return; return;
} }
@ -83,7 +61,8 @@ class ICalService {
/** /**
* Calls getTimedEvent with one hour as a time slot, starting at $start_daytime * Calls getTimedEvent with one hour as a time slot, starting at $start_daytime
*/ */
function getTimedEvent1Hour($poll, string $start_daytime) { public function getTimedEvent1Hour($poll, string $start_daytime): string
{
$end_daytime = date(DATE_ATOM, strtotime('+1 hours', strtotime($start_daytime))); $end_daytime = date(DATE_ATOM, strtotime('+1 hours', strtotime($start_daytime)));
return $this->getTimedEvent($poll, $start_daytime, $end_daytime); return $this->getTimedEvent($poll, $start_daytime, $end_daytime);
} }
@ -91,13 +70,14 @@ class ICalService {
/** /**
* Generates the text for an ical event including the time * Generates the text for an ical event including the time
*/ */
function getTimedEvent($poll, string $start_daytime, string $end_daytime) { public function getTimedEvent($poll, string $start_daytime, string $end_daytime): string
{
$vcalendar = new VObject\Component\VCalendar([ $vcalendar = new VObject\Component\VCalendar([
'VEVENT' => [ 'VEVENT' => [
'SUMMARY' => $poll->title, 'SUMMARY' => $poll->title,
'DESCRIPTION' => $this->stripMD($poll->description), 'DESCRIPTION' => $this->stripMD($poll->description),
'DTSTART' => new \DateTime($start_daytime), 'DTSTART' => new DateTime($start_daytime),
'DTEND' => new \DateTime($end_daytime) 'DTEND' => new DateTime($end_daytime)
], ],
'PRODID' => ICAL_PRODID 'PRODID' => ICAL_PRODID
]); ]);
@ -107,7 +87,8 @@ class ICalService {
/** /**
* Generates the text for an ical event if the time is not known * Generates the text for an ical event if the time is not known
*/ */
function getAllDayEvent($poll, string $day) { public function getAllDayEvent($poll, string $day): string
{
$vcalendar = new VObject\Component\VCalendar(); $vcalendar = new VObject\Component\VCalendar();
$vevent = $vcalendar->add('VEVENT'); $vevent = $vcalendar->add('VEVENT');
$vevent->add('SUMMARY', $poll->title); $vevent->add('SUMMARY', $poll->title);
@ -121,9 +102,11 @@ class ICalService {
/** /**
* Creates a file and initiates the download * Creates a file and initiates the download
* @param string $title
* @param string $ical_text * @param string $ical_text
*/ */
function provideFile(string $title, string $ical_text) { public function provideFile(string $title, string $ical_text): void
{
header('Content-Description: File Transfer'); header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename=' . $this->stripTitle($title) . ICAL_ENDING); header('Content-Disposition: attachment; filename=' . $this->stripTitle($title) . ICAL_ENDING);
header('Expires: 0'); header('Expires: 0');
@ -139,31 +122,35 @@ class ICalService {
* @param string $time * @param string $time
* @return string the corrected value, null if the format is unknown * @return string the corrected value, null if the format is unknown
*/ */
function reviseTimeString(string $time) { public function reviseTimeString(string $time): ?string
{
// 24-hour clock / international format // 24-hour clock / international format
if (preg_match('/^\d\d(:)\d\d$/', $time)) { if (preg_match('/^\d\d(:)\d\d$/', $time)) {
return $time; return $time;
} }
// 12-hour clock / using am and pm // 12-hour clock / using am and pm
else if (preg_match('/^\d[0-2]?:?\d{0,2}\s?[aApP][mM]$/', $time)) {
if (preg_match('/^\d[0-2]?:?\d{0,2}\s?[aApP][mM]$/', $time)) {
return $this->formatTime($time); return $this->formatTime($time);
} }
// french format HHhMM or HHh // french format HHhMM or HHh
else if (preg_match('/^\d\d?[hH]\d?\d?$/', $time)) {
if (preg_match('/^\d\d?[hH]\d?\d?$/', $time)) {
return $this->formatTime(str_pad(str_ireplace("H", ":", $time), 5, "0")); return $this->formatTime(str_pad(str_ireplace("H", ":", $time), 5, "0"));
} }
// Number only // Number only
else if (preg_match('/^\d{1,4}$/', $time)) {
if (preg_match('/^\d{1,4}$/', $time)) {
return $this->formatTime(str_pad(str_pad($time, 2, "0", STR_PAD_LEFT), 4, "0")); return $this->formatTime(str_pad(str_pad($time, 2, "0", STR_PAD_LEFT), 4, "0"));
} }
return null; return null;
} }
/** /**
* @param string $time * @param string $day
* @return 1 if the day string can be parsed, 0 if not and false if an error occured * @return false|int 1 if the day string can be parsed, 0 if not and false if an error occured
*/ */
function dayIsReadable(string $day) { public function dayIsReadable(string $day) {
return preg_match('/^\d{2}-\d{2}-\d{4}$/', $day); return preg_match('/^\d{2}-\d{2}-\d{4}$/', $day);
} }
@ -171,21 +158,25 @@ class ICalService {
* @param string $time * @param string $time
* @return string date string in format H:i (e.g. 19:00) * @return string date string in format H:i (e.g. 19:00)
*/ */
function formatTime(string $time) { public function formatTime(string $time): string
{
return date("H:i", strtotime($time)); return date("H:i", strtotime($time));
} }
/** /**
* Converts MD Code to HTML, then strips HTML away * Converts MD Code to HTML, then strips HTML away
*/ */
function stripMD(string $string) { public function stripMD(string $string): string
return strip_tags(smarty_modifier_markdown($string)); {
return strip_tags(Utils::markdown($string));
} }
/** /**
* Strips a string so it's usable as a file name (only digits, letters and underline allowed) * Strips a string so it's usable as a file name (only digits, letters and underline allowed)
*
* @return null|string
*/ */
function stripTitle(string $string) { public function stripTitle(string $string): ?string {
return preg_replace('/[^a-z0-9_]+/', '-', strtolower($string)); return preg_replace('/[^a-z0-9_]+/', '-', strtolower($string));
} }
} }

View File

@ -17,25 +17,28 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
namespace Framadate\Services; namespace Framadate\Services;
use function __;
use DateTime; use DateTime;
use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation; use Egulias\EmailValidator\Validation\RFCValidation;
use o80\i18n\CantLoadDictionaryException;
/** /**
* This class helps to clean all inputs from the users or external services. * This class helps to clean all inputs from the users or external services.
*/ */
class InputService { class InputService {
function __construct() {} public function __construct() {}
/** /**
* This method filter an array calling "filter_var" on each items. * This method filter an array calling "filter_var" on each items.
* Only items validated are added at their own indexes, the others are not returned. * Only items validated are added at their own indexes, the others are not returned.
* @param array $arr The array to filter * @param array $arr The array to filter
* @param int $type The type of filter to apply * @param int $type The type of filter to apply
* @param array|null $options The associative array of options * @param array|int $options The associative array of options
* @return array The filtered array * @return array The filtered array
*/ */
function filterArray(array $arr, $type, $options = null) { public function filterArray(array $arr, int $type, $options = 0): array
{
$newArr = []; $newArr = [];
foreach($arr as $id=>$item) { foreach($arr as $id=>$item) {
@ -48,24 +51,32 @@ class InputService {
return $newArr; return $newArr;
} }
function filterAllowedValues($value, array $allowedValues) { public function filterAllowedValues($value, array $allowedValues) {
return in_array($value, $allowedValues, true) ? $value : null; return in_array($value, $allowedValues, true) ? $value : null;
} }
public function filterTitle($title) { public function filterTitle($title): ?string
{
return $this->returnIfNotBlank($title); return $this->returnIfNotBlank($title);
} }
/**
* @return false|string
*/
public function filterId($id) { public function filterId($id) {
$filtered = filter_var($id, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); $filtered = filter_var($id, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
return $filtered ? substr($filtered, 0, 64) : false; return $filtered ? substr($filtered, 0, 64) : false;
} }
public function filterName($name) { public function filterName($name): ?string
{
$filtered = trim($name); $filtered = trim($name);
return $this->returnIfNotBlank($filtered); return $this->returnIfNotBlank($filtered);
} }
/**
* @return false|string
*/
public function filterMail($mail) { public function filterMail($mail) {
/////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////
// formatting // formatting
@ -89,53 +100,67 @@ class InputService {
return $resultat; return $resultat;
} }
public function filterDescription($description) { public function filterDescription($description): string {
$description = str_replace("\r\n", "\n", $description); return str_replace("\r\n", "\n", $description);
return $description;
} }
/**
* @return false|string
*/
public function filterMD5($control) { public function filterMD5($control) {
return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]); return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]);
} }
/**
* @return false|int
*/
public function filterInteger($int) { public function filterInteger($int) {
return filter_var($int, FILTER_VALIDATE_INT); return filter_var($int, FILTER_VALIDATE_INT);
} }
/**
* @return false|int
*/
public function filterValueMax($int) public function filterValueMax($int)
{ {
return $this->filterInteger($int) >= 1 ? $this->filterInteger($int) : false; return $this->filterInteger($int) >= 1 ? $this->filterInteger($int) : false;
} }
public function filterBoolean($boolean) { public function filterBoolean($boolean): bool
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]); {
return (bool)filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
} }
/**
* @return false|string
*/
public function filterEditable($editable) { public function filterEditable($editable) {
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]); return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
} }
public function filterComment($comment) { public function filterComment($comment): ?string
{
$comment = str_replace("\r\n", "\n", $comment); $comment = str_replace("\r\n", "\n", $comment);
return $this->returnIfNotBlank($comment); return $this->returnIfNotBlank($comment);
} }
public function validateDate(string $date, DateTime $maxDate, DateTime $minDate): DateTime { public function validateDate(DateTime $date, DateTime $minDate, DateTime $maxDate): DateTime {
$dDate = $this->parseDate($date); if ($date < $minDate) {
if (!$dDate) return $maxDate;
if ($dDate < $minDate) {
return $minDate; return $minDate;
} elseif ($maxDate < $dDate) { }
if ($maxDate < $date) {
return $maxDate; return $maxDate;
} }
return $dDate; return $date;
} }
/** /**
* @throws CantLoadDictionaryException
* @return DateTime|false * @return DateTime|false
*/ */
private function parseDate(string $date) { public function parseDate(string $date) {
return DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0, 0); return DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0);
} }
/** /**
@ -144,7 +169,8 @@ class InputService {
* @param string $filtered The value * @param string $filtered The value
* @return string|null * @return string|null
*/ */
private function returnIfNotBlank($filtered) { private function returnIfNotBlank(string $filtered): ?string
{
if ($filtered) { if ($filtered) {
$withoutSpaces = str_replace(' ', '', $filtered); $withoutSpaces = str_replace(' ', '', $filtered);
if (!empty($withoutSpaces)) { if (!empty($withoutSpaces)) {

View File

@ -17,7 +17,10 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
namespace Framadate\Services; namespace Framadate\Services;
use function __f;
use Exception;
use Framadate\Utils; use Framadate\Utils;
use PDO;
use Smarty; use Smarty;
/** /**
@ -40,15 +43,17 @@ class InstallService {
'migrationTable' => 'framadate_migration' 'migrationTable' => 'framadate_migration'
]; ];
function __construct() {} public function __construct() {}
public function updateFields($data) { public function updateFields($data): void
{
foreach ($data as $field => $value) { foreach ($data as $field => $value) {
$this->fields[$field] = $value; $this->fields[$field] = $value;
} }
} }
public function install(Smarty &$smarty) { public function install(Smarty &$smarty): array
{
// Check values are present // Check values are present
if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbConnectionString']) || empty($this->fields['dbUser'])) { if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbConnectionString']) || empty($this->fields['dbUser'])) {
return $this->error('MISSING_VALUES'); return $this->error('MISSING_VALUES');
@ -57,7 +62,7 @@ class InstallService {
// Connect to database // Connect to database
try { try {
$connect = $this->connectTo($this->fields['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']); $connect = $this->connectTo($this->fields['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']);
} catch(\Exception $e) { } catch(Exception $e) {
return $this->error('CANT_CONNECT_TO_DATABASE', $e->getMessage()); return $this->error('CANT_CONNECT_TO_DATABASE', $e->getMessage());
} }
@ -75,16 +80,20 @@ class InstallService {
* @param string $connectionString * @param string $connectionString
* @param string $user * @param string $user
* @param string $password * @param string $password
* @return \PDO * @return PDO
*/ */
function connectTo($connectionString, $user, $password) { public function connectTo(string $connectionString, string $user, string $password): PDO
$pdo = @new \PDO($connectionString, $user, $password); {
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ); $pdo = @new PDO($connectionString, $user, $password);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo; return $pdo;
} }
function writeConfiguration(Smarty &$smarty) { /**
* @return false|int
*/
public function writeConfiguration(Smarty &$smarty) {
foreach($this->fields as $field=>$value) { foreach($this->fields as $field=>$value) {
$smarty->assign($field, $value); $smarty->assign($field, $value);
} }
@ -96,15 +105,17 @@ class InstallService {
/** /**
* @param $content * @param $content
* @return false|int
*/ */
function writeToFile($content) { public function writeToFile(string $content) {
return @file_put_contents(CONF_FILENAME, $content); return @file_put_contents(CONF_FILENAME, $content);
} }
/** /**
* @return array * @return array
*/ */
function ok() { public function ok(): array
{
return [ return [
'status' => 'OK', 'status' => 'OK',
'msg' => __f('Installation', 'Ended', Utils::get_server_name()) 'msg' => __f('Installation', 'Ended', Utils::get_server_name())
@ -112,10 +123,12 @@ class InstallService {
} }
/** /**
* @param $msg * @param string $msg
* @param string $details
* @return array * @return array
*/ */
function error($msg, $details = '') { public function error(string $msg, string $details = ''): array
{
return [ return [
'status' => 'ERROR', 'status' => 'ERROR',
'code' => $msg, 'code' => $msg,
@ -123,7 +136,8 @@ class InstallService {
]; ];
} }
public function getFields() { public function getFields(): array
{
return $this->fields; return $this->fields;
} }
} }

View File

@ -7,7 +7,7 @@ namespace Framadate\Services;
* @package Framadate\Services * @package Framadate\Services
*/ */
class LogService { class LogService {
function __construct() { public function __construct() {
} }
/** /**
@ -16,8 +16,8 @@ class LogService {
* @param $tag string A tag is used to quickly found a message when reading log file * @param $tag string A tag is used to quickly found a message when reading log file
* @param $message string some message * @param $message string some message
*/ */
function log($tag, $message) { public function log(string $tag, string $message): void
{
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE); error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
} }
} }

View File

@ -1,12 +1,13 @@
<?php <?php
namespace Framadate\Services; namespace Framadate\Services;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\PHPMailer;
class MailService { class MailService {
const DELAY_BEFORE_RESEND = 300; public const DELAY_BEFORE_RESEND = 300;
const MAILSERVICE_KEY = 'mailservice'; public const MAILSERVICE_KEY = 'mailservice';
private $smtp_allowed; private $smtp_allowed;
@ -14,7 +15,7 @@ class MailService {
private $logService; private $logService;
function __construct($smtp_allowed, $smtp_options = []) { public function __construct($smtp_allowed, $smtp_options = []) {
$this->logService = new LogService(); $this->logService = new LogService();
$this->smtp_allowed = $smtp_allowed; $this->smtp_allowed = $smtp_allowed;
if (true === is_array($smtp_options)) { if (true === is_array($smtp_options)) {
@ -22,11 +23,18 @@ class MailService {
} }
} }
/**
* @return false|string
*/
public function isValidEmail($email) { public function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL); return filter_var($email, FILTER_VALIDATE_EMAIL);
} }
public function send($to, $subject, $body, $msgKey = null) { /**
* @throws Exception
*/
public function send(string $to, string $subject, string $body, ?string $msgKey = null): void
{
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) { if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
$mail = new PHPMailer(true); $mail = new PHPMailer(true);
$this->configureMailer($mail); $this->configureMailer($mail);
@ -45,7 +53,7 @@ class MailService {
$mail->Subject = $subject; $mail->Subject = $subject;
// Bodies // Bodies
$body = $body . ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER'); $body .= ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
$mail->isHTML(true); $mail->isHTML(true);
$mail->msgHTML($body, ROOT_DIR, true); $mail->msgHTML($body, ROOT_DIR, true);
@ -61,19 +69,25 @@ class MailService {
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey); $this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
// Store the mail sending date // Store the mail sending date
$this->initializeSession();
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time(); $_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
} }
} }
public function canSendMsg($msgKey) { public function canSendMsg(?string $msgKey): bool
{
if ($msgKey === null) { if ($msgKey === null) {
return true; return true;
} }
$this->initializeSession();
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
}
private function initializeSession(): void {
if (!isset($_SESSION[self::MAILSERVICE_KEY])) { if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
$_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;
} }
/** /**
@ -81,7 +95,8 @@ class MailService {
* *
* @param PHPMailer $mailer * @param PHPMailer $mailer
*/ */
private function configureMailer(PHPMailer $mailer) { private function configureMailer(PHPMailer $mailer): void
{
$mailer->isSMTP(); $mailer->isSMTP();
$available_options = [ $available_options = [

View File

@ -3,19 +3,22 @@
namespace Framadate\Services; namespace Framadate\Services;
use \stdClass; use \stdClass;
use Framadate\Services\MailService; use function __;
use function __f;
use Framadate\Utils; use Framadate\Utils;
use o80\i18n\CantLoadDictionaryException;
use PHPMailer\PHPMailer\Exception;
class NotificationService { class NotificationService {
const UPDATE_VOTE = 1; public const UPDATE_VOTE = 1;
const ADD_VOTE = 2; public const ADD_VOTE = 2;
const ADD_COMMENT = 3; public const ADD_COMMENT = 3;
const UPDATE_POLL = 10; public const UPDATE_POLL = 10;
const DELETED_POLL = 11; public const DELETED_POLL = 11;
private $mailService; private $mailService;
function __construct(MailService $mailService) { public function __construct(MailService $mailService) {
$this->mailService = $mailService; $this->mailService = $mailService;
} }
@ -25,8 +28,10 @@ class NotificationService {
* @param $poll stdClass The poll * @param $poll stdClass The poll
* @param $name string The name user who triggered the notification * @param $name string The name user who triggered the notification
* @param $type int cf: Constants on the top of this page * @param $type int cf: Constants on the top of this page
* @throws Exception|CantLoadDictionaryException
*/ */
function sendUpdateNotification(stdClass $poll, $type, $name='') { public function sendUpdateNotification($poll, int $type, string $name=''): void
{
if (!isset($_SESSION['mail_sent'])) { if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = []; $_SESSION['mail_sent'] = [];
} }
@ -36,7 +41,7 @@ class NotificationService {
$isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT; $isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT;
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) { if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
if (self::isParticipation($type)) { if ($this->isParticipation($type)) {
$translationString = 'Poll\'s participation: %s'; $translationString = 'Poll\'s participation: %s';
} else { } else {
$translationString = 'Notification of poll: %s'; $translationString = 'Notification of poll: %s';
@ -74,11 +79,13 @@ class NotificationService {
} }
$messageTypeKey = $type . '-' . $poll->id; $messageTypeKey = $type . '-' . $poll->id;
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey); if ($poll->admin_mail) {
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
} }
} }
function isParticipation($type) public function isParticipation(int $type): bool
{ {
return $type >= self::UPDATE_POLL; return $type >= self::UPDATE_POLL;
} }

View File

@ -20,16 +20,17 @@ namespace Framadate\Services;
use DateInterval; use DateInterval;
use DateTime; use DateTime;
use Exception;
use Framadate\Exception\AlreadyExistsException; use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException; use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\ConcurrentVoteException; use Framadate\Exception\ConcurrentVoteException;
use Framadate\Exception\PollNotFoundException;
use Framadate\Form; use Framadate\Form;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory; use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token; use Framadate\Security\Token;
use stdClass;
class PollService { class PollService {
private $connect;
private $logService; private $logService;
private $pollRepository; private $pollRepository;
@ -37,8 +38,7 @@ class PollService {
private $voteRepository; private $voteRepository;
private $commentRepository; private $commentRepository;
function __construct(FramaDB $connect, LogService $logService) { public function __construct(LogService $logService) {
$this->connect = $connect;
$this->logService = $logService; $this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository(); $this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository(); $this->slotRepository = RepositoryFactory::slotRepository();
@ -49,10 +49,10 @@ class PollService {
/** /**
* Find a poll from its ID. * Find a poll from its ID.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @return \stdClass|null The found poll, or null * @return stdClass|null The found poll, or null
*/ */
function findById($poll_id) { public function findById(string $poll_id) {
if (preg_match(POLL_REGEX, $poll_id)) { if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id); return $this->pollRepository->findById($poll_id);
} }
@ -60,7 +60,7 @@ class PollService {
return null; return null;
} }
public function findByAdminId($admin_poll_id) { public function findByAdminId(string $admin_poll_id) {
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) { if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
return $this->pollRepository->findByAdminId($admin_poll_id); return $this->pollRepository->findByAdminId($admin_poll_id);
} }
@ -68,15 +68,15 @@ class PollService {
return null; return null;
} }
function allCommentsByPollId($poll_id) { public function allCommentsByPollId(string $poll_id) {
return $this->commentRepository->findAllByPollId($poll_id); return $this->commentRepository->findAllByPollId($poll_id);
} }
function allVotesByPollId($poll_id) { public function allVotesByPollId(string $poll_id) {
return $this->voteRepository->allUserVotesByPollId($poll_id); return $this->voteRepository->allUserVotesByPollId($poll_id);
} }
function allSlotsByPoll($poll) { public function allSlotsByPoll(stdClass $poll) {
$slots = $this->slotRepository->listByPollId($poll->id); $slots = $this->slotRepository->listByPollId($poll->id);
if ($poll->format === 'D') { if ($poll->format === 'D') {
$this->sortSlorts($slots); $this->sortSlorts($slots);
@ -85,44 +85,45 @@ class PollService {
} }
/** /**
* @param $poll_id * @param string $poll_id
* @param $vote_id * @param int $vote_id
* @param $name * @param string $name
* @param $choices * @param array $choices
* @param $slots_hash * @param string $slots_hash
* @throws AlreadyExistsException * @throws AlreadyExistsException
* @throws ConcurrentEditionException * @throws ConcurrentEditionException
* @throws ConcurrentVoteException * @throws ConcurrentVoteException
* @return bool * @return bool
*/ */
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash) { public function updateVote(string $poll_id, int $vote_id, string $name, array $choices, string $slots_hash): bool
{
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id); $this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
// Update vote // Update vote
$choices = implode($choices); return $this->voteRepository->update($poll_id, $vote_id, $name, implode($choices));
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
} }
/** /**
* @param $poll_id * @param string $poll_id
* @param $name * @param string $name
* @param $choices * @param array $choices
* @param $slots_hash * @param string $slots_hash
* @throws AlreadyExistsException
* @throws ConcurrentEditionException * @throws ConcurrentEditionException
* @throws ConcurrentVoteException * @throws ConcurrentVoteException
* @return \stdClass * @throws PollNotFoundException
* @throws AlreadyExistsException
* @return stdClass
*/ */
function addVote($poll_id, $name, $choices, $slots_hash) { public function addVote(string $poll_id, string $name, array $choices, string $slots_hash): stdClass
{
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name); $this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
// Insert new vote // Insert new vote
$choices = implode($choices); return $this->voteRepository->insert($poll_id, $name, implode($choices), $this->random(16));
$token = $this->random(16);
return $this->voteRepository->insert($poll_id, $name, $choices, $token);
} }
function addComment($poll_id, $name, $comment) { public function addComment($poll_id, $name, $comment): bool
{
if ($this->commentRepository->exists($poll_id, $name, $comment)) { if ($this->commentRepository->exists($poll_id, $name, $comment)) {
return true; return true;
} }
@ -134,7 +135,8 @@ class PollService {
* @param Form $form * @param Form $form
* @return array * @return array
*/ */
function createPoll(Form $form) { public function createPoll(Form $form): array
{
// Generate poll IDs, loop while poll ID already exists // Generate poll IDs, loop while poll ID already exists
if (empty($form->id)) { // User want us to generate an id for him if (empty($form->id)) { // User want us to generate an id for him
@ -160,16 +162,18 @@ class PollService {
return [$poll_id, $admin_poll_id]; return [$poll_id, $admin_poll_id];
} }
public function findAllByAdminMail($mail) { public function findAllByAdminMail($mail): array
{
return $this->pollRepository->findAllByAdminMail($mail); return $this->pollRepository->findAllByAdminMail($mail);
} }
/** /**
* @param array $votes * @param array $votes
* @param \stdClass $poll * @param stdClass $poll
* @return array * @return array
*/ */
public function computeBestChoices($votes, $poll) { public function computeBestChoices(array $votes, $poll): array
{
if (0 === count($votes)) { if (0 === count($votes)) {
return $this->computeEmptyBestChoices($poll); return $this->computeEmptyBestChoices($poll);
} }
@ -195,10 +199,11 @@ class PollService {
return $result; return $result;
} }
function splitSlots($slots) { public function splitSlots($slots): array
{
$splitted = []; $splitted = [];
foreach ($slots as $slot) { foreach ($slots as $slot) {
$obj = new \stdClass(); $obj = new stdClass();
$obj->day = $slot->title; $obj->day = $slot->title;
$obj->moments = explode(',', $slot->moments); $obj->moments = explode(',', $slot->moments);
@ -212,16 +217,18 @@ class PollService {
* @param $slots array The slots to hash * @param $slots array The slots to hash
* @return string The hash * @return string The hash
*/ */
public function hashSlots($slots) { public function hashSlots(array $slots): string
return md5(array_reduce($slots, function($carry, $item) { {
return md5(array_reduce($slots, static function($carry, $item) {
return $carry . $item->id . '@' . $item->moments . ';'; return $carry . $item->id . '@' . $item->moments . ';';
})); }));
} }
function splitVotes($votes) { public function splitVotes(array $votes): array
{
$splitted = []; $splitted = [];
foreach ($votes as $vote) { foreach ($votes as $vote) {
$obj = new \stdClass(); $obj = new stdClass();
$obj->id = $vote->id; $obj->id = $vote->id;
$obj->name = $vote->name; $obj->name = $vote->name;
$obj->uniqId = $vote->uniqId; $obj->uniqId = $vote->uniqId;
@ -234,6 +241,7 @@ class PollService {
} }
/** /**
* @throws Exception
* @return DateTime The max date allowed for expiry date * @return DateTime The max date allowed for expiry date
*/ */
public function maxExpiryDate(): DateTime { public function maxExpiryDate(): DateTime {
@ -244,15 +252,16 @@ class PollService {
/** /**
* @return DateTime The min date allowed for expiry date * @return DateTime The min date allowed for expiry date
*/ */
public function minExpiryDate() { public function minExpiryDate(): DateTime
{
return (new DateTime())->add(new DateInterval('P1D')); return (new DateTime())->add(new DateInterval('P1D'));
} }
/** /**
* @return mixed * @return mixed
*/ */
public function sortSlorts(&$slots) { public function sortSlorts(array &$slots): array {
uasort($slots, function ($a, $b) { uasort($slots, static function ($a, $b) {
if ($a->title === $b->title) { if ($a->title === $b->title) {
return 0; return 0;
} }
@ -262,10 +271,10 @@ class PollService {
} }
/** /**
* @param \stdClass $poll * @param stdClass $poll
* @return array * @return array
*/ */
private function computeEmptyBestChoices($poll) private function computeEmptyBestChoices($poll): array
{ {
$result = ['y' => [], 'inb' => []]; $result = ['y' => [], 'inb' => []];
// if there is no votes, calculates the number of slot // if there is no votes, calculates the number of slot
@ -275,7 +284,7 @@ class PollService {
if ($poll->format === 'A') { if ($poll->format === 'A') {
// poll format classic // poll format classic
for ($i = 0; $i < count($slots); $i++) { for ($i = 0, $iMax = count($slots); $i < $iMax; $i++) {
$result['y'][] = 0; $result['y'][] = 0;
$result['inb'][] = 0; $result['inb'][] = 0;
} }
@ -285,7 +294,7 @@ class PollService {
$slots = $this->splitSlots($slots); $slots = $this->splitSlots($slots);
foreach ($slots as $slot) { foreach ($slots as $slot) {
for ($i = 0; $i < count($slot->moments); $i++) { for ($i = 0, $iMax = count($slot->moments); $i < $iMax; $i++) {
$result['y'][] = 0; $result['y'][] = 0;
$result['inb'][] = 0; $result['inb'][] = 0;
} }
@ -294,23 +303,26 @@ class PollService {
return $result; return $result;
} }
private function random($length) { private function random(int $length): string
{
return Token::getToken($length); return Token::getToken($length);
} }
/** /**
* @param $choices * @param array $choices
* @param $poll_id * @param string $poll_id
* @param $slots_hash * @param string $slots_hash
* @param $name * @param string $name
* @param string $vote_id * @param bool|int $vote_id
* @throws AlreadyExistsException * @throws AlreadyExistsException
* @throws ConcurrentVoteException
* @throws ConcurrentEditionException * @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @throws PollNotFoundException
*/ */
private function checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id = FALSE) { private function checkVoteConstraints(array $choices, string $poll_id, string $slots_hash, string $name, $vote_id = false): void
{
// Check if vote already exists with the same name // Check if vote already exists with the same name
if (FALSE === $vote_id) { if (false === $vote_id) {
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name); $exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
} else { } else {
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id); $exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
@ -322,6 +334,10 @@ class PollService {
$poll = $this->findById($poll_id); $poll = $this->findById($poll_id);
if (!$poll) {
throw new PollNotFoundException();
}
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint // Check that no-one voted in the meantime and it conflicts the maximum votes constraint
$this->checkMaxVotes($choices, $poll, $poll_id); $this->checkMaxVotes($choices, $poll, $poll_id);
@ -336,7 +352,8 @@ class PollService {
* @param $slots_hash string The hash sent by the user * @param $slots_hash string The hash sent by the user
* @throws ConcurrentEditionException Thrown when hashes are differents * @throws ConcurrentEditionException Thrown when hashes are differents
*/ */
private function checkThatSlotsDidntChanged($poll, $slots_hash) { private function checkThatSlotsDidntChanged(stdClass $poll, string $slots_hash): void
{
$slots = $this->allSlotsByPoll($poll); $slots = $this->allSlotsByPoll($poll);
if ($slots_hash !== $this->hashSlots($slots)) { if ($slots_hash !== $this->hashSlots($slots)) {
throw new ConcurrentEditionException(); throw new ConcurrentEditionException();
@ -347,11 +364,12 @@ class PollService {
* This method checks if the votes doesn't conflicts the maximum votes constraint * This method checks if the votes doesn't conflicts the maximum votes constraint
* *
* @param $user_choice * @param $user_choice
* @param \stdClass $poll * @param stdClass $poll
* @param string $poll_id * @param string $poll_id
* @throws ConcurrentVoteException * @throws ConcurrentVoteException
*/ */
private function checkMaxVotes($user_choice, $poll, $poll_id) { private function checkMaxVotes(array $user_choice, $poll, string $poll_id): void
{
$votes = $this->allVotesByPollId($poll_id); $votes = $this->allVotesByPollId($poll_id);
if (count($votes) <= 0) { if (count($votes) <= 0) {
return; return;

View File

@ -15,7 +15,7 @@ class PurgeService {
private $voteRepository; private $voteRepository;
private $commentRepository; private $commentRepository;
function __construct(FramaDB $connect, LogService $logService) { public function __construct(LogService $logService) {
$this->logService = $logService; $this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository(); $this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository(); $this->slotRepository = RepositoryFactory::slotRepository();
@ -26,9 +26,10 @@ class PurgeService {
/** /**
* This methode purges all old polls (the ones with end_date in past). * This methode purges all old polls (the ones with end_date in past).
* *
* @return bool true is action succeeded * @return int number of purged polls
*/ */
function purgeOldPolls() { public function purgeOldPolls(): int
{
$oldPolls = $this->pollRepository->findOldPolls(); $oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls); $count = count($oldPolls);
@ -50,10 +51,11 @@ class PurgeService {
/** /**
* This methode delete all data about a poll. * This methode delete all data about a poll.
* *
* @param $poll_id int The ID of the poll * @param string $poll_id The ID of the poll
* @return bool true is action succeeded * @return bool true is action succeeded
*/ */
function purgePollById($poll_id) { public function purgePollById(string $poll_id): bool
{
$done = true; $done = true;
$this->pollRepository->beginTransaction(); $this->pollRepository->beginTransaction();
@ -71,4 +73,3 @@ class PurgeService {
return $done; return $done;
} }
} }

View File

@ -5,7 +5,7 @@ use Framadate\Security\PasswordHasher;
use Framadate\Security\Token; use Framadate\Security\Token;
class SecurityService { class SecurityService {
function __construct() { public function __construct() {
} }
/** /**
@ -18,9 +18,10 @@ class SecurityService {
* </ul> * </ul>
* *
* @param $tokan_name string The name of the CSRF token * @param $tokan_name string The name of the CSRF token
* @return Token The token * @return string The token
*/ */
function getToken($tokan_name) { function getToken(string $tokan_name): string
{
if (!isset($_SESSION['tokens'])) { if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens'] = []; $_SESSION['tokens'] = [];
} }
@ -38,7 +39,8 @@ class SecurityService {
* @param $csrf string Value to check * @param $csrf string Value to check
* @return bool true if the token is well checked * @return bool true if the token is well checked
*/ */
public function checkCsrf($tokan_name, $csrf) { public function checkCsrf(string $tokan_name, string $csrf): bool
{
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf; $checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
if($checked) { if($checked) {
@ -54,17 +56,18 @@ class SecurityService {
* @param $poll \stdClass The poll which we seek access * @param $poll \stdClass The poll which we seek access
* @return bool true if the current session can access this poll * @return bool true if the current session can access this poll
*/ */
public function canAccessPoll($poll) { public function canAccessPoll($poll): bool
{
if (is_null($poll->password_hash)) { if (is_null($poll->password_hash)) {
return true; return true;
} }
$this->ensureSessionPollSecurityIsCreated(); $this->ensureSessionPollSecurityIsCreated();
$currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null; $currentPassword = $_SESSION['poll_security'][$poll->id] ?? null;
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) { if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
return true; return true;
} }
unset($_SESSION['poll_security'][$poll->id]); unset($_SESSION['poll_security'][$poll->id]);
return false; return false;
} }
@ -75,17 +78,18 @@ class SecurityService {
* @param $poll \stdClass The poll which we seek access * @param $poll \stdClass The poll which we seek access
* @param $password string the password to compare * @param $password string the password to compare
*/ */
public function submitPollAccess($poll, $password) { public function submitPollAccess($poll, string $password): void
{
if (!empty($password)) { if (!empty($password)) {
$this->ensureSessionPollSecurityIsCreated(); $this->ensureSessionPollSecurityIsCreated();
$_SESSION['poll_security'][$poll->id] = $password; $_SESSION['poll_security'][$poll->id] = $password;
} }
} }
private function ensureSessionPollSecurityIsCreated() { private function ensureSessionPollSecurityIsCreated(): void
{
if (!isset($_SESSION['poll_security'])) { if (!isset($_SESSION['poll_security'])) {
$_SESSION['poll_security'] = []; $_SESSION['poll_security'] = [];
} }
} }
} }

View File

@ -17,12 +17,7 @@ class SessionService {
$this->initSectionIfNeeded($section); $this->initSectionIfNeeded($section);
$returnValue = $defaultValue; return $_SESSION[$section][$key] ?? $defaultValue;
if (isset($_SESSION[$section][$key])) {
$returnValue = $_SESSION[$section][$key];
}
return $returnValue;
} }
/** /**
@ -32,7 +27,8 @@ class SessionService {
* @param $key * @param $key
* @param $value * @param $value
*/ */
public function set($section, $key, $value) { public function set($section, $key, $value): void
{
assert(!empty($key)); assert(!empty($key));
assert(!empty($section)); assert(!empty($section));
@ -47,16 +43,18 @@ class SessionService {
* @param $section * @param $section
* @param $key * @param $key
*/ */
public function remove($section, $key) { public function remove($section, $key): void
{
assert(!empty($key)); assert(!empty($key));
assert(!empty($section)); assert(!empty($section));
unset($_SESSION[$section][$key]); unset($_SESSION[$section][$key]);
} }
private function initSectionIfNeeded($section) { private function initSectionIfNeeded($section): void
{
if (!isset($_SESSION[$section])) { if (!isset($_SESSION[$section])) {
$_SESSION[$section] = []; $_SESSION[$section] = [];
} }
} }
} }

View File

@ -11,7 +11,7 @@ use Framadate\Repositories\RepositoryFactory;
class SuperAdminService { class SuperAdminService {
private $pollRepository; private $pollRepository;
function __construct() { public function __construct() {
$this->pollRepository = RepositoryFactory::pollRepository(); $this->pollRepository = RepositoryFactory::pollRepository();
} }
@ -23,7 +23,8 @@ class SuperAdminService {
* @param int $limit The limit size * @param int $limit The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count] * @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
*/ */
public function findAllPolls($search, $page, $limit) { public function findAllPolls(array $search, int $page, int $limit): array
{
$start = $page * $limit; $start = $page * $limit;
$polls = $this->pollRepository->findAll($search, $start, $limit); $polls = $this->pollRepository->findAll($search, $start, $limit);
$count = $this->pollRepository->count($search); $count = $this->pollRepository->count($search);
@ -32,4 +33,3 @@ class SuperAdminService {
return ['polls' => $polls, 'count' => $count, 'total' => $total]; return ['polls' => $polls, 'count' => $count, 'total' => $total];
} }
} }

View File

@ -24,13 +24,13 @@ class Utils {
/** /**
* @return string Server name * @return string Server name
*/ */
public static function get_server_name() { public static function get_server_name(): string
{
$scheme = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) ? 'https' : 'http'; $scheme = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) ? 'https' : 'http';
$port = in_array($_SERVER['SERVER_PORT'], ['80', '443'], true) ? '' : ':' . $_SERVER['SERVER_PORT']; $port = in_array($_SERVER['SERVER_PORT'], ['80', '443'], true) ? '' : ':' . $_SERVER['SERVER_PORT'];
$dirname = dirname($_SERVER['SCRIPT_NAME']); $dirname = dirname($_SERVER['SCRIPT_NAME']);
$dirname = $dirname === '\\' ? '/' : $dirname . '/'; $dirname = $dirname === '\\' ? '/' : $dirname . '/';
$dirname = str_replace('/admin', '', $dirname); $dirname = str_replace(['/admin', '/action'], '', $dirname);
$dirname = str_replace('/action', '', $dirname);
$server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $dirname; $server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $dirname;
return $scheme . '://' . preg_replace('#//+#', '/', $server_name); return $scheme . '://' . preg_replace('#//+#', '/', $server_name);
@ -38,9 +38,10 @@ class Utils {
/** /**
* @param string $title * @param string $title
*
* @deprecated * @deprecated
*/ */
public static function print_header($title = '') { public static function print_header($title = ''): void {
global $locale; global $locale;
echo '<!DOCTYPE html> echo '<!DOCTYPE html>
@ -60,7 +61,7 @@ class Utils {
<link rel="stylesheet" href="' . self::get_server_name() . 'css/style.css" /> <link rel="stylesheet" href="' . self::get_server_name() . 'css/style.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/frama.css" /> <link rel="stylesheet" href="' . self::get_server_name() . 'css/frama.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/print.css" media="print" /> <link rel="stylesheet" href="' . self::get_server_name() . 'css/print.css" media="print" />
<script src="' . self::get_server_name() . 'js/jquery-1.12.4.min.js"></script> <script src="' . self::get_server_name() . 'js/jquery-3.6.0.min.js"></script>
<script src="' . self::get_server_name() . 'js/bootstrap.min.js"></script> <script src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
<script src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>'; <script src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>';
if ('en' !== $locale) { if ('en' !== $locale) {
@ -81,16 +82,17 @@ class Utils {
/** /**
* Function allowing to generate poll's url * Function allowing to generate poll's url
* @param string $id The poll's id * @param string $id The poll's id
* @param bool $admin True to generate an admin URL, false for a public one * @param bool $admin True to generate an admin URL, false for a public one
* @param string $vote_id (optional) The vote's unique id * @param string $vote_id (optional) The vote's unique id
* @param null $action * @param string|null $action
* @param null $action_value * @param string|null $action_value
* @return string The poll's URL. * @return string The poll's URL.
*/ */
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) { public static function getUrlSondage(string $id, bool $admin = false, string $vote_id = '', string $action = null, string $action_value = null): string
{
// URL-Encode $action_value // URL-Encode $action_value
$action_value = $action_value ? Utils::base64url_encode($action_value) : null; $action_value = $action_value ? self::base64url_encode($action_value) : null;
if (URL_PROPRE) { if (URL_PROPRE) {
if ($admin === true) { if ($admin === true) {
@ -132,17 +134,20 @@ class Utils {
* *
* @param mixed $object The object to print. * @param mixed $object The object to print.
*/ */
public static function debug($object) { public static function debug($object): void
{
echo '<pre>'; echo '<pre>';
print_r($object); print_r($object);
echo '</pre>'; echo '</pre>';
} }
public static function table($tableName) { public static function table(string $tableName): string
{
return TABLENAME_PREFIX . $tableName; return TABLENAME_PREFIX . $tableName;
} }
public static function markdown($md, $clear=false, $line=true) { public static function markdown(string $md, bool $clear=false, bool $line=true): string
{
$parseDown = new Parsedown(); $parseDown = new Parsedown();
$parseDown $parseDown
@ -155,7 +160,7 @@ class Utils {
} else { } else {
$md = preg_replace_callback( $md = preg_replace_callback(
'#( ){2,}#', '#( ){2,}#',
function ($m) { static function ($m) {
return str_repeat('&nbsp;', strlen($m[0])); return str_repeat('&nbsp;', strlen($m[0]));
}, },
$md $md
@ -168,39 +173,38 @@ class Utils {
return $clear ? $text : $html; return $clear ? $text : $html;
} }
public static function htmlEscape($html) { public static function htmlEscape(string $html): string {
return htmlentities($html, ENT_HTML5 | ENT_QUOTES); return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
} }
public static function htmlMailEscape($html) { public static function htmlMailEscape(string $html): string
{
return htmlspecialchars($html, ENT_HTML5 | ENT_QUOTES); return htmlspecialchars($html, ENT_HTML5 | ENT_QUOTES);
} }
public static function csvEscape($text) { public static function csvEscape(string $text): string
$escaped = str_replace('"', '""', $text); {
$escaped = str_replace("\r\n", '', $escaped); $escaped = str_replace(['"', "\r\n", "\n"], ['""', '', ''], $text);
$escaped = str_replace("\n", '', $escaped);
$escaped = preg_replace("/^(=|\+|\-|\@)/", "'$1", $escaped); $escaped = preg_replace("/^(=|\+|\-|\@)/", "'$1", $escaped);
return '"' . $escaped . '"'; return '"' . $escaped . '"';
} }
public static function cleanFilename($title) { public static function cleanFilename(string $title): string {
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title); $cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
$cleaned = preg_replace(' {2,}', ' ', $cleaned); return preg_replace(' {2,}', ' ', $cleaned);
return $cleaned;
} }
public static function fromPostOrDefault($postKey, $default = '') { public static function fromPostOrDefault(string $postKey, ?string $default = '') {
return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default; return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default;
} }
public static function base64url_encode($input) { public static function base64url_encode(string $input): string
{
return rtrim(strtr(base64_encode($input), '+/', '-_'), '='); return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
} }
public static function base64url_decode($input) { public static function base64url_decode(string $input): string {
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true); return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true);
} }
} }

View File

@ -18,12 +18,14 @@
*/ */
// Prepare I18N instance // Prepare I18N instance
$i18n = \o80\i18n\I18N::instance(); use o80\i18n\I18N;
$i18n = I18N::instance();
$i18n->setDefaultLang(DEFAULT_LANGUAGE); $i18n->setDefaultLang(DEFAULT_LANGUAGE);
$i18n->setPath(__DIR__ . '/../../locale'); $i18n->setPath(__DIR__ . '/../../locale');
// Change langauge when user asked for it // Change language when user asked for it
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES), true)) { if (isset($_POST['lang']) && is_string($_POST['lang']) && array_key_exists($_POST['lang'], $ALLOWED_LANGUAGES)) {
$_SESSION['lang'] = $_POST['lang']; $_SESSION['lang'] = $_POST['lang'];
} }
@ -38,7 +40,7 @@ $date_format['txt_day'] = __('Date', 'DAY');
$date_format['txt_date'] = __('Date', 'DATE'); $date_format['txt_date'] = __('Date', 'DATE');
$date_format['txt_month_year'] = __('Date', 'MONTH_YEAR'); $date_format['txt_month_year'] = __('Date', 'MONTH_YEAR');
$date_format['txt_datetime_short'] = __('Date', 'DATETIME'); $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 if (PHP_OS_FAMILY === 'Windows') { //%e can't be used on Windows platform, use %#d instead
foreach ($date_format as $k => $v) { foreach ($date_format as $k => $v) {
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows $date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
} }

View File

@ -18,6 +18,7 @@
*/ */
use Framadate\FramaDB; use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory; use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
// Autoloading of dependencies with Composer // Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php'; require_once __DIR__ . '/../../vendor/autoload.php';
@ -39,10 +40,17 @@ require_once __DIR__ . '/constants.php';
if (is_file(CONF_FILENAME)) { if (is_file(CONF_FILENAME)) {
@include_once __DIR__ . '/config.php'; @include_once __DIR__ . '/config.php';
// Connection to database try {
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD); // Connection to database
RepositoryFactory::init($connect); $connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
$err = 0; RepositoryFactory::init($connect);
} catch (PDOException $e) {
if ($_SERVER['SCRIPT_NAME'] !== '/maintenance.php') {
header(('Location: ' . Utils::get_server_name() . 'maintenance.php'));
exit;
}
$error = $e->getMessage();
}
} else { } else {
define('NOMAPPLICATION', 'Framadate'); define('NOMAPPLICATION', 'Framadate');
define('DEFAULT_LANGUAGE', 'fr'); define('DEFAULT_LANGUAGE', 'fr');

View File

@ -50,9 +50,10 @@ if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
$smarty->compile_check = false; $smarty->compile_check = false;
} }
function smarty_function_poll_url($params, Smarty_Internal_Template $template) { function smarty_function_poll_url($params, Smarty_Internal_Template $template): string
{
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); $poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$admin = (isset($params['admin']) && $params['admin']) ? true : false; $admin = isset($params['admin']) && $params['admin'];
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : 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; $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]]) : ''; $vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
@ -62,30 +63,40 @@ function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value); return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value);
} }
function smarty_modifier_markdown($md, $clear = false, $inline=true) { function smarty_modifier_markdown(string $md, bool $clear = false, bool $inline=true): string
{
return Utils::markdown($md, $clear, $inline); return Utils::markdown($md, $clear, $inline);
} }
function smarty_modifier_resource($link) { function smarty_modifier_resource(string $link): string
{
return Utils::get_server_name() . $link; return Utils::get_server_name() . $link;
} }
function smarty_modifier_addslashes_single_quote($string) { function smarty_modifier_addslashes_single_quote(string $string): string
{
return addcslashes($string, '\\\''); return addcslashes($string, '\\\'');
} }
function smarty_modifier_addslashes($string) { function smarty_modifier_addslashes(string $string): string
{
return addslashes($string); return addslashes($string);
} }
function smarty_modifier_html($html) { function smarty_modifier_html(?string $html): string
{
if (!$html) {
return '';
}
return Utils::htmlEscape($html); return Utils::htmlEscape($html);
} }
function smarty_modifier_html_special_chars($html) { function smarty_modifier_html_special_chars(string $html): string
{
return Utils::htmlMailEscape($html); return Utils::htmlMailEscape($html);
} }
function smarty_modifier_datepicker_path($lang) { function smarty_modifier_datepicker_path(string $lang): string
{
$i = 0; $i = 0;
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) { while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
$lang_arr = explode('-', $lang); $lang_arr = explode('-', $lang);
@ -94,12 +105,13 @@ function smarty_modifier_datepicker_path($lang) {
} else { } else {
$lang = 'en'; $lang = 'en';
} }
$i += 1; ++$i;
} }
return 'js/locales/bootstrap-datepicker.' . $lang . '.js'; return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
} }
function smarty_modifier_locale_2_lang($locale) { function smarty_modifier_locale_2_lang(string $locale): string
{
$lang_arr = explode('-', $locale); $lang_arr = explode('-', $locale);
if ($lang_arr && count($lang_arr) > 1) { if ($lang_arr && count($lang_arr) > 1) {
return $lang_arr[0]; return $lang_arr[0];
@ -107,6 +119,7 @@ function smarty_modifier_locale_2_lang($locale) {
return $locale; return $locale;
} }
function path_for_datepicker_locale($lang) { function path_for_datepicker_locale(string $lang): string
{
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js'; return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
} }

View File

@ -4,11 +4,12 @@ namespace Framadate;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
abstract class FramaTestCase extends TestCase { abstract class FramaTestCase extends TestCase {
protected function getTestResourcePath($resourcepath) { protected function getTestResourcePath(string $resourcepath): string
{
return __DIR__ . '/../resources/' . $resourcepath; return __DIR__ . '/../resources/' . $resourcepath;
} }
protected function readTestResource($resourcepath) { protected function readTestResource(string $resourcepath) {
return file_get_contents($this->getTestResourcePath($resourcepath)); return file_get_contents($this->getTestResourcePath($resourcepath));
} }

View File

@ -5,7 +5,8 @@ use Framadate\FramaTestCase;
class InputServiceUnitTest extends FramaTestCase class InputServiceUnitTest extends FramaTestCase
{ {
public function liste_emails() { public function liste_emails(): array
{
return [ return [
// valids addresses // valids addresses
"valid address" => ["example@example.com", "example@example.com"], "valid address" => ["example@example.com", "example@example.com"],
@ -23,7 +24,8 @@ class InputServiceUnitTest extends FramaTestCase
/** /**
* @dataProvider liste_emails * @dataProvider liste_emails
*/ */
public function test_filterMail($email, $expected) { public function test_filterMail($email, $expected): void
{
$inputService = new InputService(); $inputService = new InputService();
$filtered = $inputService->filterMail($email); $filtered = $inputService->filterMail($email);

View File

@ -4,9 +4,10 @@ namespace Framadate\Services;
use Framadate\FramaTestCase; use Framadate\FramaTestCase;
class MailServiceUnitTest extends FramaTestCase { class MailServiceUnitTest extends FramaTestCase {
const MSG_KEY = '666'; public const MSG_KEY = '666';
public function test_should_send_a_2nd_mail_after_a_good_interval() { public function test_should_send_a_2nd_mail_after_a_good_interval(): void
{
// Given // Given
$mailService = new MailService(true); $mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000]; $_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
@ -15,10 +16,11 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY); $canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then // Then
$this->assertSame(true, $canSendMsg); $this->assertTrue($canSendMsg);
} }
public function test_should_not_send_2_mails_in_a_short_interval() { public function test_should_not_send_2_mails_in_a_short_interval(): void
{
// Given // Given
$mailService = new MailService(true); $mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()]; $_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
@ -27,6 +29,6 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY); $canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then // Then
$this->assertSame(false, $canSendMsg); $this->assertFalse($canSendMsg);
} }
} }

View File

@ -54,14 +54,14 @@ function bandeau_titre($titre)
} }
} }
function liste_lang() function liste_lang(): string
{ {
global $ALLOWED_LANGUAGES; global $locale; global $ALLOWED_LANGUAGES; global $locale;
$str = ''; $str = '';
foreach ($ALLOWED_LANGUAGES as $k => $v ) { foreach ($ALLOWED_LANGUAGES as $k => $v ) {
if (substr($k,0,2)===$locale) { if (strpos($k, $locale) === 0) {
$str .= '<option lang="' . substr($k,0,2) . '" selected value="' . $k . '">' . $v . '</option>' . "\n" ; $str .= '<option lang="' . substr($k,0,2) . '" selected value="' . $k . '">' . $v . '</option>' . "\n" ;
} else { } else {
$str .= '<option lang="' . substr($k,0,2) . '" value="' . $k . '">' . $v . '</option>' . "\n" ; $str .= '<option lang="' . substr($k,0,2) . '" value="' . $k . '">' . $v . '</option>' . "\n" ;

View File

@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
$goodLang = $_GET['good']; $goodLang = $_GET['good'];
$otherLang = $_GET['other']; $otherLang = $_GET['other'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true); $good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true); $other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
foreach ($good as $sectionName => $section) { foreach ($good as $sectionName => $section) {
foreach ($section as $key => $value) { foreach ($section as $key => $value) {
@ -19,15 +19,15 @@ include_once __DIR__ . '/app/inc/init.php';
} }
} }
echo json_encode($good, JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS)); echo json_encode($good, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
function getFromOther($other, $goodKey, $default, $otherLang) { function getFromOther($other, $goodKey, $default, $otherLang): string {
foreach ($other as $sectionName => $section) { foreach ($other as $sectionName => $section) {
foreach ($section as $key => $value) { foreach ($section as $key => $value) {
if ( if (
strtolower($key) === strtolower($goodKey) || strtolower($key) === strtolower($goodKey) ||
stripos($key, strtolower($goodKey)) === 0 ||
strtolower(trim($key)) === strtolower($goodKey) || strtolower(trim($key)) === strtolower($goodKey) ||
strtolower(substr($key, 0, strlen($key) - 1)) === strtolower($goodKey) ||
strtolower(trim(substr(trim($key), 0, strlen($key) - 1))) === strtolower($goodKey) strtolower(trim(substr(trim($key), 0, strlen($key) - 1))) === strtolower($goodKey)
) { ) {
return $value; return $value;

View File

@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
$goodLang = $_GET['good']; $goodLang = $_GET['good'];
$testLang = $_GET['test']; $testLang = $_GET['test'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true); $good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true); $test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$diffSection = false; $diffSection = false;
@ -46,8 +46,8 @@ include_once __DIR__ . '/app/inc/init.php';
} }
} }
if (!$diffSection and array_keys($good[$sectionName]) !== array_keys($test[$sectionName])) { if (!$diffSection and array_keys($section) !== array_keys($test[$sectionName])) {
$diff[$sectionName]['order_good'] = array_keys($good[$sectionName]); $diff[$sectionName]['order_good'] = array_keys($section);
$diff[$sectionName]['order_test'] = array_keys($test[$sectionName]); $diff[$sectionName]['order_test'] = array_keys($test[$sectionName]);
} }
} }

View File

@ -62,6 +62,7 @@
"require": { "require": {
"php": ">=7.3.0", "php": ">=7.3.0",
"ext-pdo": "*", "ext-pdo": "*",
"ext-json": "*",
"smarty/smarty": "^4.0", "smarty/smarty": "^4.0",
"o80/i18n": "dev-develop", "o80/i18n": "dev-develop",
"phpmailer/phpmailer": "~6.2", "phpmailer/phpmailer": "~6.2",
@ -73,8 +74,15 @@
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^9", "phpunit/phpunit": "^9",
"friendsofphp/php-cs-fixer": "^3.2" "friendsofphp/php-cs-fixer": "^3.2",
"vimeo/psalm": "^4.15"
}, },
"repositories": [
{
"type": "git",
"url": "https://framagit.org/framasoft/framadate/o80-i18n"
}
],
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Framadate\\": "app/classes/Framadate/" "Framadate\\": "app/classes/Framadate/"

687
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "fac62d4321ada86d9ed4b66d6e160df4", "content-hash": "4cab1ad093ed4a0fc4a2ae861ba5bc5a",
"packages": [ "packages": [
{ {
"name": "doctrine/lexer", "name": "doctrine/lexer",
@ -256,20 +256,17 @@
"version": "dev-develop", "version": "dev-develop",
"source": { "source": {
"type": "git", "type": "git",
"url": "git@github.com:olivierperez/o80-i18n.git", "url": "https://framagit.org/framasoft/framadate/o80-i18n",
"reference": "ef98bd7bd733d23729999ac148f79ea1d7b9008c" "reference": "7b59cf9b2bc47b1084ac7e754d41ca595ff6c33d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/olivierperez/o80-i18n/zipball/ef98bd7bd733d23729999ac148f79ea1d7b9008c",
"reference": "ef98bd7bd733d23729999ac148f79ea1d7b9008c",
"shasum": ""
}, },
"require": { "require": {
"php": ">=5.3.0" "ext-intl": "*",
"ext-json": "*",
"php": ">=7.3.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^4.5" "friendsofphp/php-cs-fixer": "^3.4",
"phpunit/phpunit": "^9.5.10"
}, },
"default-branch": true, "default-branch": true,
"type": "library", "type": "library",
@ -278,7 +275,20 @@
"o80\\": "src/o80" "o80\\": "src/o80"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "scripts": {
"cs:check": [
"php-cs-fixer fix --dry-run --diff"
],
"cs:fix": [
"php-cs-fixer fix"
],
"lint": [
"find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -not -path './tests/integration/vendor/*' -print0 | xargs -0 -n1 php -l"
],
"test": [
"phpunit --bootstrap tests/bootstrap.php tests"
]
},
"license": [ "license": [
"Apache License 2.0" "Apache License 2.0"
], ],
@ -296,7 +306,7 @@
"internationalization", "internationalization",
"php" "php"
], ],
"time": "2020-10-05T17:26:16+00:00" "time": "2021-12-20T15:31:54+00:00"
}, },
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
@ -382,12 +392,12 @@
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git", "url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "b9421ed9de7b2a5f54f637a064dcd31922a82405" "reference": "fff53639bf1fa25f311c3e54932ac8c827f9a343"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/b9421ed9de7b2a5f54f637a064dcd31922a82405", "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/fff53639bf1fa25f311c3e54932ac8c827f9a343",
"reference": "b9421ed9de7b2a5f54f637a064dcd31922a82405", "reference": "fff53639bf1fa25f311c3e54932ac8c827f9a343",
"shasum": "" "shasum": ""
}, },
"conflict": { "conflict": {
@ -617,7 +627,7 @@
"pusher/pusher-php-server": "<2.2.1", "pusher/pusher-php-server": "<2.2.1",
"pwweb/laravel-core": "<=0.3.6-beta", "pwweb/laravel-core": "<=0.3.6-beta",
"rainlab/debugbar-plugin": "<3.1", "rainlab/debugbar-plugin": "<3.1",
"remdex/livehelperchat": "<=2", "remdex/livehelperchat": "<=3.90",
"rmccue/requests": ">=1.6,<1.8", "rmccue/requests": ">=1.6,<1.8",
"robrichards/xmlseclibs": "<3.0.4", "robrichards/xmlseclibs": "<3.0.4",
"sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1",
@ -806,7 +816,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2021-12-16T20:16:03+00:00" "time": "2021-12-17T20:13:17+00:00"
}, },
{ {
"name": "sabre/uri", "name": "sabre/uri",
@ -1353,6 +1363,245 @@
} }
], ],
"packages-dev": [ "packages-dev": [
{
"name": "amphp/amp",
"version": "v2.6.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/amp.git",
"reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/amp/zipball/c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae",
"reference": "c5fc66a78ee38d7ac9195a37bacaf940eb3f65ae",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "dev-master",
"amphp/phpunit-util": "^1",
"ext-json": "*",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^7 | ^8 | ^9",
"psalm/phar": "^3.11@dev",
"react/promise": "^2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
}
},
"autoload": {
"psr-4": {
"Amp\\": "lib"
},
"files": [
"lib/functions.php",
"lib/Internal/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Lowrey",
"email": "rdlowrey@php.net"
},
{
"name": "Aaron Piotrowski",
"email": "aaron@trowski.com"
},
{
"name": "Bob Weinand",
"email": "bobwei9@hotmail.com"
},
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
}
],
"description": "A non-blocking concurrency framework for PHP applications.",
"homepage": "http://amphp.org/amp",
"keywords": [
"async",
"asynchronous",
"awaitable",
"concurrency",
"event",
"event-loop",
"future",
"non-blocking",
"promise"
],
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/amp/issues",
"source": "https://github.com/amphp/amp/tree/v2.6.1"
},
"funding": [
{
"url": "https://github.com/amphp",
"type": "github"
}
],
"time": "2021-09-23T18:43:08+00:00"
},
{
"name": "amphp/byte-stream",
"version": "v1.8.1",
"source": {
"type": "git",
"url": "https://github.com/amphp/byte-stream.git",
"reference": "acbd8002b3536485c997c4e019206b3f10ca15bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd",
"reference": "acbd8002b3536485c997c4e019206b3f10ca15bd",
"shasum": ""
},
"require": {
"amphp/amp": "^2",
"php": ">=7.1"
},
"require-dev": {
"amphp/php-cs-fixer-config": "dev-master",
"amphp/phpunit-util": "^1.4",
"friendsofphp/php-cs-fixer": "^2.3",
"jetbrains/phpstorm-stubs": "^2019.3",
"phpunit/phpunit": "^6 || ^7 || ^8",
"psalm/phar": "^3.11.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Amp\\ByteStream\\": "lib"
},
"files": [
"lib/functions.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Aaron Piotrowski",
"email": "aaron@trowski.com"
},
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
}
],
"description": "A stream abstraction to make working with non-blocking I/O simple.",
"homepage": "http://amphp.org/byte-stream",
"keywords": [
"amp",
"amphp",
"async",
"io",
"non-blocking",
"stream"
],
"support": {
"irc": "irc://irc.freenode.org/amphp",
"issues": "https://github.com/amphp/byte-stream/issues",
"source": "https://github.com/amphp/byte-stream/tree/v1.8.1"
},
"funding": [
{
"url": "https://github.com/amphp",
"type": "github"
}
],
"time": "2021-03-30T17:13:30+00:00"
},
{
"name": "composer/package-versions-deprecated",
"version": "1.11.99.4",
"source": {
"type": "git",
"url": "https://github.com/composer/package-versions-deprecated.git",
"reference": "b174585d1fe49ceed21928a945138948cb394600"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b174585d1fe49ceed21928a945138948cb394600",
"reference": "b174585d1fe49ceed21928a945138948cb394600",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.1.0 || ^2.0",
"php": "^7 || ^8"
},
"replace": {
"ocramius/package-versions": "1.11.99"
},
"require-dev": {
"composer/composer": "^1.9.3 || ^2.0@dev",
"ext-zip": "^1.13",
"phpunit/phpunit": "^6.5 || ^7"
},
"type": "composer-plugin",
"extra": {
"class": "PackageVersions\\Installer",
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"PackageVersions\\": "src/PackageVersions"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
},
{
"name": "Jordi Boggiano",
"email": "j.boggiano@seld.be"
}
],
"description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)",
"support": {
"issues": "https://github.com/composer/package-versions-deprecated/issues",
"source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.4"
},
"funding": [
{
"url": "https://packagist.com",
"type": "custom"
},
{
"url": "https://github.com/composer",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
"type": "tidelift"
}
],
"time": "2021-09-13T08:41:34+00:00"
},
{ {
"name": "composer/pcre", "name": "composer/pcre",
"version": "1.0.0", "version": "1.0.0",
@ -1571,6 +1820,43 @@
], ],
"time": "2021-12-08T13:07:32+00:00" "time": "2021-12-08T13:07:32+00:00"
}, },
{
"name": "dnoegel/php-xdg-base-dir",
"version": "v0.1.1",
"source": {
"type": "git",
"url": "https://github.com/dnoegel/php-xdg-base-dir.git",
"reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
"reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35"
},
"type": "library",
"autoload": {
"psr-4": {
"XdgBaseDir\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "implementation of xdg base directory specification for php",
"support": {
"issues": "https://github.com/dnoegel/php-xdg-base-dir/issues",
"source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1"
},
"time": "2019-12-04T15:06:13+00:00"
},
{ {
"name": "doctrine/annotations", "name": "doctrine/annotations",
"version": "1.13.2", "version": "1.13.2",
@ -1712,6 +1998,107 @@
], ],
"time": "2020-11-10T18:47:58+00:00" "time": "2020-11-10T18:47:58+00:00"
}, },
{
"name": "felixfbecker/advanced-json-rpc",
"version": "v3.2.1",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-advanced-json-rpc.git",
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447",
"shasum": ""
},
"require": {
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"php": "^7.1 || ^8.0",
"phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0"
},
"require-dev": {
"phpunit/phpunit": "^7.0 || ^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"AdvancedJsonRpc\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Felix Becker",
"email": "felix.b@outlook.com"
}
],
"description": "A more advanced JSONRPC implementation",
"support": {
"issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues",
"source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1"
},
"time": "2021-06-11T22:34:44+00:00"
},
{
"name": "felixfbecker/language-server-protocol",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpstan/phpstan": "*",
"squizlabs/php_codesniffer": "^3.1",
"vimeo/psalm": "^4.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"LanguageServerProtocol\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"ISC"
],
"authors": [
{
"name": "Felix Becker",
"email": "felix.b@outlook.com"
}
],
"description": "PHP classes for the Language Server Protocol",
"keywords": [
"language",
"microsoft",
"php",
"server"
],
"support": {
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1"
},
"time": "2021-02-22T14:02:09+00:00"
},
{ {
"name": "friendsofphp/php-cs-fixer", "name": "friendsofphp/php-cs-fixer",
"version": "v3.4.0", "version": "v3.4.0",
@ -1859,6 +2246,57 @@
], ],
"time": "2020-11-13T09:40:50+00:00" "time": "2020-11-13T09:40:50+00:00"
}, },
{
"name": "netresearch/jsonmapper",
"version": "v4.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"reference": "8bbc021a8edb2e4a7ea2f8ad4fa9ec9dce2fcb8d",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~7.5 || ~8.0 || ~9.0",
"squizlabs/php_codesniffer": "~3.5"
},
"type": "library",
"autoload": {
"psr-0": {
"JsonMapper": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"OSL-3.0"
],
"authors": [
{
"name": "Christian Weiske",
"email": "cweiske@cweiske.de",
"homepage": "http://github.com/cweiske/jsonmapper/",
"role": "Developer"
}
],
"description": "Map nested JSON structures onto PHP classes",
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v4.0.0"
},
"time": "2020-12-01T19:48:11+00:00"
},
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v4.13.2", "version": "v4.13.2",
@ -1915,6 +2353,59 @@
}, },
"time": "2021-11-30T19:35:32+00:00" "time": "2021-11-30T19:35:32+00:00"
}, },
{
"name": "openlss/lib-array2xml",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/nullivex/lib-array2xml.git",
"reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
"reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"type": "library",
"autoload": {
"psr-0": {
"LSS": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Bryan Tong",
"email": "bryan@nullivex.com",
"homepage": "https://www.nullivex.com"
},
{
"name": "Tony Butler",
"email": "spudz76@gmail.com",
"homepage": "https://www.nullivex.com"
}
],
"description": "Array2XML conversion library credit to lalit.org",
"homepage": "https://www.nullivex.com",
"keywords": [
"array",
"array conversion",
"xml",
"xml conversion"
],
"support": {
"issues": "https://github.com/nullivex/lib-array2xml/issues",
"source": "https://github.com/nullivex/lib-array2xml/tree/master"
},
"time": "2019-03-29T20:06:56+00:00"
},
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
"version": "2.0.3", "version": "2.0.3",
@ -5237,6 +5728,112 @@
], ],
"time": "2021-07-28T10:34:58+00:00" "time": "2021-07-28T10:34:58+00:00"
}, },
{
"name": "vimeo/psalm",
"version": "v4.15.0",
"source": {
"type": "git",
"url": "https://github.com/vimeo/psalm.git",
"reference": "a1b5e489e6fcebe40cb804793d964e99fc347820"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vimeo/psalm/zipball/a1b5e489e6fcebe40cb804793d964e99fc347820",
"reference": "a1b5e489e6fcebe40cb804793d964e99fc347820",
"shasum": ""
},
"require": {
"amphp/amp": "^2.4.2",
"amphp/byte-stream": "^1.5",
"composer/package-versions-deprecated": "^1.8.0",
"composer/semver": "^1.4 || ^2.0 || ^3.0",
"composer/xdebug-handler": "^1.1 || ^2.0",
"dnoegel/php-xdg-base-dir": "^0.1.1",
"ext-ctype": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-simplexml": "*",
"ext-tokenizer": "*",
"felixfbecker/advanced-json-rpc": "^3.0.3",
"felixfbecker/language-server-protocol": "^1.5",
"netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0",
"nikic/php-parser": "^4.13",
"openlss/lib-array2xml": "^1.0",
"php": "^7.1|^8",
"sebastian/diff": "^3.0 || ^4.0",
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
"webmozart/path-util": "^2.3"
},
"provide": {
"psalm/psalm": "self.version"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.2",
"brianium/paratest": "^4.0||^6.0",
"ext-curl": "*",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpdocumentor/reflection-docblock": "^5",
"phpmyadmin/sql-parser": "5.1.0||dev-master",
"phpspec/prophecy": ">=1.9.0",
"phpunit/phpunit": "^9.0",
"psalm/plugin-phpunit": "^0.16",
"slevomat/coding-standard": "^7.0",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.3 || ^5.0 || ^6.0",
"weirdan/prophecy-shim": "^1.0 || ^2.0"
},
"suggest": {
"ext-curl": "In order to send data to shepherd",
"ext-igbinary": "^2.0.5 is required, used to serialize caching data"
},
"bin": [
"psalm",
"psalm-language-server",
"psalm-plugin",
"psalm-refactor",
"psalter"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.x-dev",
"dev-3.x": "3.x-dev",
"dev-2.x": "2.x-dev",
"dev-1.x": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psalm\\": "src/Psalm/"
},
"files": [
"src/functions.php",
"src/spl_object_id.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matthew Brown"
}
],
"description": "A static analysis tool for finding errors in PHP applications",
"keywords": [
"code",
"inspection",
"php"
],
"support": {
"issues": "https://github.com/vimeo/psalm/issues",
"source": "https://github.com/vimeo/psalm/tree/v4.15.0"
},
"time": "2021-12-07T11:25:29+00:00"
},
{ {
"name": "webmozart/assert", "name": "webmozart/assert",
"version": "1.10.0", "version": "1.10.0",
@ -5294,6 +5891,57 @@
"source": "https://github.com/webmozarts/assert/tree/1.10.0" "source": "https://github.com/webmozarts/assert/tree/1.10.0"
}, },
"time": "2021-03-09T10:59:23+00:00" "time": "2021-03-09T10:59:23+00:00"
},
{
"name": "webmozart/path-util",
"version": "2.3.0",
"source": {
"type": "git",
"url": "https://github.com/webmozart/path-util.git",
"reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
"reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"webmozart/assert": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.6",
"sebastian/version": "^1.0.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\PathUtil\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
"support": {
"issues": "https://github.com/webmozart/path-util/issues",
"source": "https://github.com/webmozart/path-util/tree/2.3.0"
},
"abandoned": "symfony/filesystem",
"time": "2015-12-17T08:42:14+00:00"
} }
], ],
"aliases": [], "aliases": [],
@ -5307,7 +5955,8 @@
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": ">=7.3.0", "php": ">=7.3.0",
"ext-pdo": "*" "ext-pdo": "*",
"ext-json": "*"
}, },
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {

View File

@ -30,9 +30,9 @@ include_once __DIR__ . '/app/inc/init.php';
/* Service */ /* Service */
/*---------*/ /*---------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($connect, $logService); $purgeService = new PurgeService($logService);
$sessionService = new SessionService(); $sessionService = new SessionService();
$inputService = new InputService(); $inputService = new InputService();
@ -45,7 +45,7 @@ if (is_file('bandeaux_local.php')) {
$form = unserialize($_SESSION['form']); $form = unserialize($_SESSION['form']);
// Step 1/4 : error if $_SESSION from info_sondage are not valid // Step 1/4 : error if $_SESSION from info_sondage are not valid
if (empty($form->title) || empty($form->admin_name) || (($config['use_smtp']) ? empty($form->admin_mail) : false)) { if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && empty($form->admin_mail))) {
$smarty->assign('title', __('Error', 'Error!')); $smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation.')); $smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation.'));
$smarty->display('error.tpl'); $smarty->display('error.tpl');
@ -64,8 +64,8 @@ if (empty($form->title) || empty($form->admin_name) || (($config['use_smtp']) ?
// Step 4 : Data prepare before insert in DB // Step 4 : Data prepare before insert in DB
if (isset($_POST['confirmation'])) { if (isset($_POST['confirmation'])) {
// Define expiration date // Define expiration date
$expiration_date = $inputService->validateDate($_POST['expiration_date'], $pollService->minExpiryDate(), $pollService->maxExpiryDate()); $expiration_date = $inputService->parseDate($_POST['enddate']);
$form->end_date = $expiration_date->getTimestamp(); $form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
// Insert poll in database // Insert poll in database
$ids = $pollService->createPoll($form); $ids = $pollService->createPoll($form);
@ -123,7 +123,7 @@ if (empty($form->title) || empty($form->admin_name) || (($config['use_smtp']) ?
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href) preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src) preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href) preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0] !== '' && isset($md_a_img[3][0]) && $md_a_img[3][0] !== '') { // [![alt](src)](href) if (isset($md_a_img[2][0], $md_a_img[3][0]) && $md_a_img[2][0] !== '' && $md_a_img[3][0] !== '') { // [![alt](src)](href)
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] !== '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1); $li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] !== '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
$li_subject_html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" /></a>'; $li_subject_html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0] !== '') { // ![alt](src) } elseif (isset($md_img[2][0]) && $md_img[2][0] !== '') { // ![alt](src)
@ -175,7 +175,7 @@ if (empty($form->title) || empty($form->admin_name) || (($config['use_smtp']) ?
$choices = $form->getChoices(); $choices = $form->getChoices();
$nb_choices = max(count($choices), 5); $nb_choices = max(count($choices), 5);
for ($i = 0; $i < $nb_choices; $i++) { for ($i = 0; $i < $nb_choices; $i++) {
$choice = isset($choices[$i]) ? $choices[$i] : new Choice(); $choice = $choices[$i] ?? new Choice();
echo ' echo '
<div class="form-group choice-field"> <div class="form-group choice-field">
<label for="choice' . $i . '" class="col-sm-2 control-label">' . __('Generic', 'Choice') . ' ' . ($i + 1) . '</label> <label for="choice' . $i . '" class="col-sm-2 control-label">' . __('Generic', 'Choice') . ' ' . ($i + 1) . '</label>

View File

@ -30,9 +30,9 @@ include_once __DIR__ . '/app/inc/init.php';
/* Service */ /* Service */
/*---------*/ /*---------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($connect, $logService); $purgeService = new PurgeService($logService);
$inputService = new InputService(); $inputService = new InputService();
$sessionService = new SessionService(); $sessionService = new SessionService();
@ -52,7 +52,7 @@ if (isset($form->format) && $form->format !== 'D') {
$form->clearChoices(); $form->clearChoices();
} }
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) { if (!isset($form->title, $form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
$step = 1; $step = 1;
} else if (!empty($_POST['confirmation'])) { } else if (!empty($_POST['confirmation'])) {
$step = 4; $step = 4;
@ -107,7 +107,7 @@ switch ($step) {
// Handle Step2 submission // Handle Step2 submission
if (!empty($_POST['days'])) { if (!empty($_POST['days'])) {
// Remove empty dates // Remove empty dates
$_POST['days'] = array_filter($_POST['days'], function ($d) { $_POST['days'] = array_filter($_POST['days'], static function ($d) {
return !empty($d); return !empty($d);
}); });
@ -135,7 +135,7 @@ switch ($step) {
$i++; $i++;
} }
for ($i = 0; $i < count($_POST['days']); $i++) { for ($i = 0, $iMax = count($_POST['days']); $i < $iMax; $i++) {
$day = $_POST['days'][$i]; $day = $_POST['days'][$i];
if (!empty($day)) { if (!empty($day)) {
@ -146,7 +146,7 @@ switch ($step) {
$form->addChoice($choice); $form->addChoice($choice);
$schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT); $schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT);
for ($j = 0; $j < count($schedules); $j++) { for ($j = 0, $jMax = count($schedules); $j < $jMax; $j++) {
if (!empty($schedules[$j])) { if (!empty($schedules[$j])) {
$choice->addSlot(strip_tags($schedules[$j])); $choice->addSlot(strip_tags($schedules[$j]));
} }
@ -188,8 +188,8 @@ switch ($step) {
// Step 4 : Data prepare before insert in DB // Step 4 : Data prepare before insert in DB
// Define expiration date // Define expiration date
$expiration_date = $inputService->validateDate($_POST['enddate'], $pollService->minExpiryDate(), $pollService->maxExpiryDate()); $expiration_date = $inputService->parseDate($_POST['enddate']);
$form->end_date = $expiration_date->getTimestamp(); $form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
// Insert poll in database // Insert poll in database
$ids = $pollService->createPoll($form); $ids = $pollService->createPoll($form);

View File

@ -42,8 +42,8 @@ if ($form === null && !($form instanceof Form)) {
} }
// Type de sondage // Type de sondage
if (isset($_GET['type']) && $_GET['type'] === 'date' || if ((isset($_GET['type']) && $_GET['type'] === 'date') ||
isset($_POST['type']) && $_POST['type'] === 'date' (isset($_POST['type']) && $_POST['type'] === 'date')
) { ) {
$poll_type = 'date'; $poll_type = 'date';
$form->choix_sondage = $poll_type; $form->choix_sondage = $poll_type;
@ -57,21 +57,21 @@ $goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['op
if ($goToStep2) { if ($goToStep2) {
$title = $inputService->filterTitle($_POST['title']); $title = $inputService->filterTitle($_POST['title']);
$use_ValueMax = isset($_POST['use_ValueMax']) ? $inputService->filterBoolean($_POST['use_ValueMax']) : false; $use_ValueMax = isset($_POST['use_ValueMax']) && $inputService->filterBoolean($_POST['use_ValueMax']);
$ValueMax = $use_ValueMax === true ? $inputService->filterValueMax($_POST['ValueMax']) : null; $ValueMax = $use_ValueMax === true ? $inputService->filterValueMax($_POST['ValueMax']) : null;
$use_customized_url = isset($_POST['use_customized_url']) ? $inputService->filterBoolean($_POST['use_customized_url']) : false; $use_customized_url = isset($_POST['use_customized_url']) && $inputService->filterBoolean($_POST['use_customized_url']);
$customized_url = $use_customized_url === true ? $inputService->filterId($_POST['customized_url']) : null; $customized_url = $use_customized_url === true ? $inputService->filterId($_POST['customized_url']) : null;
$name = mb_substr($inputService->filterName($_POST['name']), 0, 32); $name = mb_substr($inputService->filterName($_POST['name']), 0, 32);
$mail = $config['use_smtp'] === true ? $inputService->filterMail($_POST['mail']) : null; $mail = $config['use_smtp'] === true ? $inputService->filterMail($_POST['mail']) : null;
$description = $inputService->filterDescription($_POST['description']); $description = $inputService->filterDescription($_POST['description']);
$editable = $inputService->filterEditable($_POST['editable']); $editable = $inputService->filterEditable($_POST['editable']);
$receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false; $receiveNewVotes = isset($_POST['receiveNewVotes']) && $inputService->filterBoolean($_POST['receiveNewVotes']);
$receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false; $receiveNewComments = isset($_POST['receiveNewComments']) && $inputService->filterBoolean($_POST['receiveNewComments']);
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false; $hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); $use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
$password = isset($_POST['password']) ? $_POST['password'] : null; $password = $_POST['password'] ?? null;
$password_repeat = isset($_POST['password_repeat']) ? $_POST['password_repeat'] : null; $password_repeat = $_POST['password_repeat'] ?? null;
$results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]); $results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
// On initialise également les autres variables // On initialise également les autres variables
@ -235,7 +235,7 @@ if (!empty($_POST[GO_TO_STEP_2])) {
if ($error_on_customized_url) { if ($error_on_customized_url) {
$errors['customized_url']['aria'] = 'aria-describeby="customized_url" '; $errors['customized_url']['aria'] = 'aria-describeby="customized_url" ';
$errors['customized_url']['class'] = ' has-error'; $errors['customized_url']['class'] = ' has-error';
$errors['customized_url']['msg'] = isset($error_on_customized_url_msg) ? $error_on_customized_url_msg : __('Error', "Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens."); $errors['customized_url']['msg'] = $error_on_customized_url_msg ?? __('Error', "Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.");
} }
if ($error_on_description) { if ($error_on_description) {

View File

@ -35,7 +35,7 @@ $poll = null;
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$securityService = new SecurityService(); $securityService = new SecurityService();
/* PAGE */ /* PAGE */
@ -110,7 +110,7 @@ foreach ($votes as $vote) {
$text = __('Generic', 'Yes'); $text = __('Generic', 'Yes');
break; break;
default: default:
$text = 'unkown'; $text = __('Generic', 'Unknown');
} }
echo Utils::csvEscape($text); echo Utils::csvEscape($text);
echo ','; echo ',';

View File

@ -27,7 +27,7 @@ include_once __DIR__ . '/app/inc/init.php';
/* SERVICES */ /* SERVICES */
/* -------- */ /* -------- */
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
/* PAGE */ /* PAGE */

View File

@ -1,4 +1,6 @@
<?php <?php
use Framadate\Services\LogService;
/** /**
* This software is governed by the CeCILL-B license. If a copy of this license * 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 * is not distributed with this file, you can obtain one at
@ -29,8 +31,7 @@ if (!is_file(CONF_FILENAME)) {
/* SERVICES */ /* SERVICES */
/* -------- */ /* -------- */
$logService = '\Framadate\Services\LogService'; $pollService = new PollService(new LogService());
$pollService = new PollService($connect, new $logService());
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */

View File

@ -66,8 +66,7 @@ $(document).ready(function () {
url: form.attr('action'), url: form.attr('action'),
data: form.serialize(), data: form.serialize(),
dataType: 'json', dataType: 'json',
success: function(data) success: function(data) {
{
$('#comment').val(''); $('#comment').val('');
if (data.result) { if (data.result) {
$('#comments_list') $('#comments_list')
@ -95,6 +94,9 @@ $(document).ready(function () {
}, 750); }, 750);
} }
}, },
error: function (data) {
console.error(data);
},
complete: function() { complete: function() {
$('#add_comment').removeAttr("disabled"); $('#add_comment').removeAttr("disabled");
} }

File diff suppressed because one or more lines are too long

11008
js/jquery-1.12.4.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2
js/jquery-3.6.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,7 @@ function myPreviewRender(text) {
text = text.replace(/[\u00A0-\u9999<>\&]/gim, function (i) { text = text.replace(/[\u00A0-\u9999<>\&]/gim, function (i) {
return "&#" + i.charCodeAt(0) + ";"; return "&#" + i.charCodeAt(0) + ";";
}); });
text = SimpleMDE.prototype.markdown(text); text = EasyMDE.prototype.markdown(text);
text = DOMPurify.sanitize(text); text = DOMPurify.sanitize(text);
return text; return text;
@ -35,7 +35,12 @@ MDEWrapper.prototype.enable = function () {
element: wrapper.element, element: wrapper.element,
forceSync: true, forceSync: true,
status: true, status: true,
previewRender: myPreviewRender, // previewRender: myPreviewRender,
renderingConfig: {
sanitizerFunction: function (text) {
return DOMPurify.sanitize(text);
},
},
spellChecker: false, spellChecker: false,
promptURLs: true, promptURLs: true,
minHeight: "200px", minHeight: "200px",

3
js/purify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
js/purify.min.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -208,7 +208,8 @@
"seconds": "ثواني", "seconds": "ثواني",
"vote": "vote", "vote": "vote",
"votes": "أصوات", "votes": "أصوات",
"with": "with" "with": "with",
"Unknown": "مجهول"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Make a standard poll", "Make a classic poll": "Make a standard poll",

View File

@ -208,7 +208,8 @@
"seconds": "eilenn", "seconds": "eilenn",
"vote": "vouezh", "vote": "vouezh",
"votes": "a vouezhioù", "votes": "a vouezhioù",
"with": "gant" "with": "gant",
"Unknown": "Dianav"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Krouiñ ur sontadeg klasel", "Make a classic poll": "Krouiñ ur sontadeg klasel",

View File

@ -281,7 +281,8 @@
"Make your polls": "Feu el vostre sondeig", "Make your polls": "Feu el vostre sondeig",
"Framadate is an online service for planning an appointment or make a decision quickly and easily.": "Framadate és un servei en línia per planificar una cita o prendre una decisió de forma ràpida i senzilla.", "Framadate is an online service for planning an appointment or make a decision quickly and easily.": "Framadate és un servei en línia per planificar una cita o prendre una decisió de forma ràpida i senzilla.",
"Caption": "Títol", "Caption": "Títol",
"ASTERISK": "*" "ASTERISK": "*",
"Unknown": "Desconegut"
}, },
"FindPolls": { "FindPolls": {
"Send me my polls": "Envia'm les enquestes", "Send me my polls": "Envia'm les enquestes",

View File

@ -208,7 +208,8 @@
"seconds": "Sekunden", "seconds": "Sekunden",
"vote": "Stimme", "vote": "Stimme",
"votes": "Stimmen", "votes": "Stimmen",
"with": "mit" "with": "mit",
"Unknown": "Unbekannt"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Klassische Umfrage", "Make a classic poll": "Klassische Umfrage",

View File

@ -212,7 +212,8 @@
"seconds": "seconds", "seconds": "seconds",
"vote": "vote", "vote": "vote",
"votes": "votes", "votes": "votes",
"with": "with" "with": "with",
"Unknown": "Unknown"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Make a standard poll", "Make a classic poll": "Make a standard poll",

View File

@ -208,7 +208,8 @@
"seconds": "seconds", "seconds": "seconds",
"vote": "vote", "vote": "vote",
"votes": "votes", "votes": "votes",
"with": "with" "with": "with",
"Unknown": "Nekonata"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Make a standard poll", "Make a classic poll": "Make a standard poll",

View File

@ -208,7 +208,8 @@
"seconds": "segundos", "seconds": "segundos",
"vote": "voto", "vote": "voto",
"votes": "votos", "votes": "votos",
"with": "con" "with": "con",
"Unknown": "Desconocido"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Crear una encuesta clásica", "Make a classic poll": "Crear una encuesta clásica",

View File

@ -208,7 +208,8 @@
"seconds": "secondes", "seconds": "secondes",
"vote": "vote", "vote": "vote",
"votes": "votes", "votes": "votes",
"with": "avec" "with": "avec",
"Unknown": "Inconnu"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Créer un sondage classique", "Make a classic poll": "Créer un sondage classique",
@ -317,14 +318,14 @@
"Remove the poll": "Supprimer le sondage", "Remove the poll": "Supprimer le sondage",
"Results are hidden": "Les résultats sont cachés", "Results are hidden": "Les résultats sont cachés",
"Results are visible": "Les résultats sont visibles", "Results are visible": "Les résultats sont visibles",
"Rich editor": "Editeur avancé", "Rich editor": "Éditeur avancé",
"Save the description": "Enregistrer la description", "Save the description": "Enregistrer la description",
"Save the email address": "Enregistrer le courriel", "Save the email address": "Enregistrer le courriel",
"Save the new expiration date": "Enregistrer la date d'expiration", "Save the new expiration date": "Enregistrer la date d'expiration",
"Save the new name": "Enregistrer l'auteur·rice", "Save the new name": "Enregistrer l'auteur·rice",
"Save the new rules": "Enregistrer les nouvelles permissions", "Save the new rules": "Enregistrer les nouvelles permissions",
"Save the new title": "Enregistrer le nouveau titre", "Save the new title": "Enregistrer le nouveau titre",
"Simple editor": "Editeur simple", "Simple editor": "Éditeur simple",
"Title": "Titre du sondage", "Title": "Titre du sondage",
"Votes and comments are locked": "Il n'est plus possible de voter", "Votes and comments are locked": "Il n'est plus possible de voter",
"Votes protected by password": "Votes protégés par mot de passe" "Votes protected by password": "Votes protégés par mot de passe"

View File

@ -211,7 +211,8 @@
"seconds": "secondes", "seconds": "secondes",
"vote": "vote", "vote": "vote",
"votes": "votes", "votes": "votes",
"with": "avec" "with": "avec",
"Unknown": "Inconnu"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Créer un sondage classique", "Make a classic poll": "Créer un sondage classique",
@ -320,14 +321,14 @@
"Remove the poll": "Supprimer le sondage", "Remove the poll": "Supprimer le sondage",
"Results are hidden": "Les résultats sont cachés", "Results are hidden": "Les résultats sont cachés",
"Results are visible": "Les résultats sont visibles", "Results are visible": "Les résultats sont visibles",
"Rich editor": "Editeur avancé", "Rich editor": "Éditeur avancé",
"Save the description": "Enregistrer la description", "Save the description": "Enregistrer la description",
"Save the email address": "Enregistrer le courriel", "Save the email address": "Enregistrer le courriel",
"Save the new expiration date": "Enregistrer la date d'expiration", "Save the new expiration date": "Enregistrer la date d'expiration",
"Save the new name": "Enregistrer l'auteur·rice", "Save the new name": "Enregistrer l'auteur·rice",
"Save the new rules": "Enregistrer les nouvelles permissions", "Save the new rules": "Enregistrer les nouvelles permissions",
"Save the new title": "Enregistrer le nouveau titre", "Save the new title": "Enregistrer le nouveau titre",
"Simple editor": "Editeur simple", "Simple editor": "Éditeur simple",
"Title": "Titre du sondage", "Title": "Titre du sondage",
"Votes and comments are locked": "Il n'est plus possible de voter", "Votes and comments are locked": "Il n'est plus possible de voter",
"Votes protected by password": "Votes protégés par mot de passe" "Votes protected by password": "Votes protégés par mot de passe"

View File

@ -208,7 +208,8 @@
"seconds": "secondi", "seconds": "secondi",
"vote": "voto", "vote": "voto",
"votes": "voti", "votes": "voti",
"with": "con" "with": "con",
"Unknown": "Sconosciuto"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Creare un sondaggio", "Make a classic poll": "Creare un sondaggio",

View File

@ -208,7 +208,8 @@
"seconds": "seconden", "seconds": "seconden",
"vote": "stem", "vote": "stem",
"votes": "stemmen", "votes": "stemmen",
"with": "met" "with": "met",
"Unknown": "Onbekend"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Maak een standaard poll", "Make a classic poll": "Maak een standaard poll",

View File

@ -208,7 +208,8 @@
"seconds": "segondas", "seconds": "segondas",
"vote": "vòte", "vote": "vòte",
"votes": "vòtes", "votes": "vòtes",
"with": "amb" "with": "amb",
"Unknown": "Inconnu"
}, },
"Homepage": { "Homepage": {
"Make a classic poll": "Crear un sondatge classic", "Make a classic poll": "Crear un sondatge classic",

View File

@ -18,5 +18,7 @@
*/ */
include_once __DIR__ . '/app/inc/init.php'; include_once __DIR__ . '/app/inc/init.php';
if (isset($error)) {
$smarty->assign('error', $error);
}
$smarty->display('maintenance.tpl'); $smarty->display('maintenance.tpl');

21
psalm.xml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<psalm
errorLevel="3"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="app/classes/Framadate" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<extraFiles>
<directory name="vendor"/>
<ignoreFiles>
<file name="vendor/symfony/polyfill-mbstring/bootstrap80.php"/>
</ignoreFiles>
</extraFiles>
</psalm>

View File

@ -57,13 +57,13 @@ $selectedNewVotes = [];
/*----------*/ /*----------*/
$logService = new LogService(); $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($logService);
$inputService = new InputService(); $inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']); $mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService); $notificationService = new NotificationService($mailService);
$securityService = new SecurityService(); $securityService = new SecurityService();
$sessionService = new SessionService(); $sessionService = new SessionService();
$icalService = new ICalService($logService, $notificationService, $sessionService); $icalService = new ICalService();
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */
@ -87,7 +87,7 @@ $editedVoteUniqueId = $sessionService->get(USER_REMEMBER_VOTES_KEY, $poll_id, ''
if (!is_null($poll->password_hash)) { if (!is_null($poll->password_hash)) {
// If we came from password submission // If we came from password submission
$password = isset($_POST['password']) ? $_POST['password'] : null; $password = $_POST['password'] ?? null;
if (!empty($password)) { if (!empty($password)) {
$securityService->submitPollAccess($poll, $password); $securityService->submitPollAccess($poll, $password);
} }
@ -173,7 +173,7 @@ if ($accessGranted) {
try { try {
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash); $result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
if ($result) { if ($result) {
if (intval($poll->editable) === Editable::EDITABLE_BY_OWN) { if ((int)$poll->editable === Editable::EDITABLE_BY_OWN) {
$editedVoteUniqueId = $result->uniqId; $editedVoteUniqueId = $result->uniqId;
$message = getMessageForOwnVoteEditableVote($sessionService, $smarty, $editedVoteUniqueId, $config['use_smtp'], $poll_id, $name); $message = getMessageForOwnVoteEditableVote($sessionService, $smarty, $editedVoteUniqueId, $config['use_smtp'], $poll_id, $name);
} else { } else {
@ -196,7 +196,8 @@ if ($accessGranted) {
} }
// Functions // Functions
function getMessageForOwnVoteEditableVote(SessionService &$sessionService, Smarty &$smarty, $editedVoteUniqueId, $canUseSMTP, $poll_id, $name) { function getMessageForOwnVoteEditableVote(SessionService &$sessionService, Smarty &$smarty, $editedVoteUniqueId, $canUseSMTP, $poll_id, $name): Message
{
$sessionService->set(USER_REMEMBER_VOTES_KEY, $poll_id, $editedVoteUniqueId); $sessionService->set(USER_REMEMBER_VOTES_KEY, $poll_id, $editedVoteUniqueId);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId); $urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
$message = new Message( $message = new Message(
@ -221,11 +222,11 @@ function getMessageForOwnVoteEditableVote(SessionService &$sessionService, Smart
// Get iCal file // Get iCal file
// ------------------------------- // -------------------------------
if (isset($_GET['get_ical_file'])) { if (isset($_GET['get_ical_file'])) {
$dayAndTime = strval(filter_input(INPUT_GET, 'get_ical_file', FILTER_DEFAULT)); $dayAndTime = (string)filter_input(INPUT_GET, 'get_ical_file');
$dayAndTime = strval(Utils::base64url_decode($dayAndTime)); $dayAndTime = Utils::base64url_decode($dayAndTime);
$elements = explode("|", $dayAndTime); $elements = explode("|", $dayAndTime);
if(count($elements) > 1) { if(count($elements) > 1) {
$icalService->getEvent($poll, strval($elements[0]), strval($elements[1])); $icalService->getEvent($poll, (string)$elements[0], (string)$elements[1]);
} }
header('HTTP/1.1 500 Internal Server Error'); header('HTTP/1.1 500 Internal Server Error');
echo 'Internal error'; echo 'Internal error';

View File

@ -2,7 +2,7 @@
{block name="header"} {block name="header"}
<script src="{"js/easymde.min.js"|resource}"></script> <script src="{"js/easymde.min.js"|resource}"></script>
<script src="{"js/dompurify.js"|resource}"></script> <script src="{"js/purify.min.js"|resource}"></script>
<script src="{"js/mde-wrapper.js"|resource}"></script> <script src="{"js/mde-wrapper.js"|resource}"></script>
<script src="{"js/app/create_poll.js"|resource}"></script> <script src="{"js/app/create_poll.js"|resource}"></script>
<link rel="stylesheet" href="{"css/app/create_poll.css"|resource}"> <link rel="stylesheet" href="{"css/app/create_poll.css"|resource}">

View File

@ -3,6 +3,9 @@
{block name=main} {block name=main}
<div class="alert alert-warning text-center"> <div class="alert alert-warning text-center">
<h2>{__('Maintenance', 'The application')} {$APPLICATION_NAME} {__('Maintenance', 'is currently under maintenance.')}</h2> <h2>{__('Maintenance', 'The application')} {$APPLICATION_NAME} {__('Maintenance', 'is currently under maintenance.')}</h2>
{if isset($error)}
<pre>{$error}</pre>
{/if}
<p>{__('Maintenance', 'Thank you for your understanding.')}</p> <p>{__('Maintenance', 'Thank you for your understanding.')}</p>
</div> </div>
{/block} {/block}

View File

@ -23,7 +23,7 @@
{if $provide_fork_awesome} {if $provide_fork_awesome}
<link rel="stylesheet" href="{'css/fork-awesome.min.css'|resource}"> <link rel="stylesheet" href="{'css/fork-awesome.min.css'|resource}">
{/if} {/if}
<script src="{'js/jquery-1.12.4.min.js'|resource}"></script> <script src="{'js/jquery-3.6.0.min.js'|resource}"></script>
<script src="{'js/bootstrap.min.js'|resource}"></script> <script src="{'js/bootstrap.min.js'|resource}"></script>
<script src="{'js/bootstrap-datepicker.js'|resource}"></script> <script src="{'js/bootstrap-datepicker.js'|resource}"></script>
{if 'en' != $locale} {if 'en' != $locale}

View File

@ -1,10 +1,10 @@
<div id="comments_list"> <div id="comments_list">
<form action="{if $admin}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST"> <form action="{if isset($admin)}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST">
{if $comments|count > 0} {if $comments|count > 0}
<h3>{__('Comments', 'Comments of polled people')}</h3> <h3>{__('Comments', 'Comments of polled people')}</h3>
{foreach $comments as $comment} {foreach $comments as $comment}
<div class="comment"> <div class="comment">
{if $admin && !$expired} {if isset($admin) && !$expired}
<button type="submit" name="delete_comment" value="{$comment->id|html}" class="btn btn-link" title="{__('Comments', 'Remove the comment')}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{__('Generic', 'Remove')}</span></button> <button type="submit" name="delete_comment" value="{$comment->id|html}" class="btn btn-link" title="{__('Comments', 'Remove the comment')}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{__('Generic', 'Remove')}</span></button>
{/if} {/if}
<span class="comment_date">{$comment->date|date_format:$date_format['txt_datetime_short']}</span> <span class="comment_date">{$comment->date|date_format:$date_format['txt_datetime_short']}</span>
@ -15,4 +15,4 @@
{/if} {/if}
</form> </form>
<div id="comments_alerts"></div> <div id="comments_alerts"></div>
</div> </div>

View File

@ -8,7 +8,7 @@
{if $admin} {if $admin}
<script src="{"js/easymde.min.js"|resource}"></script> <script src="{"js/easymde.min.js"|resource}"></script>
<script src="{"js/dompurify.js"|resource}"></script> <script src="{"js/purify.min.js"|resource}"></script>
<script src="{"js/mde-wrapper.js"|resource}"></script> <script src="{"js/mde-wrapper.js"|resource}"></script>
<script src="{"js/app/adminstuds.js"|resource}"></script> <script src="{"js/app/adminstuds.js"|resource}"></script>
<link rel="stylesheet" href="{'css/easymde.min.css'|resource}"> <link rel="stylesheet" href="{'css/easymde.min.css'|resource}">