diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0281d56..09ef086 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
- Amélioration : Mise à jour des fichiers .md pour faciliter la collaboration
- Amélioration : Le nom de l'auteur et la date d'expiration sont modifiables
- Amélioration : Le nom de vote est modifiable
+ - Amélioration : Affichage du comptage des "Si nécessaire" entre parenthèses
- Fix : Purge en 2 étapes → 1. Verrouillage du sondage → 2. 60 jours plus tard suppression du sondage
- Fix : Date d'expiration qui devient nulle quand on ajoute une colonne
- Fix : clic/focus sur oui/non/si nécessaire → retour à gauche de la barre de scroll sur Chromium
diff --git a/admin/purge.php b/admin/purge.php
index 5a8e268..fb817f1 100644
--- a/admin/purge.php
+++ b/admin/purge.php
@@ -21,7 +21,6 @@ use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\PurgeService;
use Framadate\Services\SecurityService;
-use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
diff --git a/adminstuds.php b/adminstuds.php
index 5d7e2d6..d5f9cab 100644
--- a/adminstuds.php
+++ b/adminstuds.php
@@ -16,15 +16,22 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
-use Framadate\Services\PollService;
+use Framadate\Editable;
+use Framadate\Message;
use Framadate\Services\AdminPollService;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
-use Framadate\Message;
-use Framadate\Editable;
+use Framadate\Services\MailService;
+use Framadate\Services\PollService;
+use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
+/* Constants */
+/* --------- */
+const UPDATE_POLL = 1;
+const DELETED_POLL = 2;
+
/* Variables */
/* --------- */
@@ -41,6 +48,41 @@ $logService = new LogService();
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService();
+$mailService = new MailService($config['use_smtp']);
+
+/* Functions */
+/*-----------*/
+
+/**
+ * Send a notification to the poll admin to notify him about an update.
+ *
+ * @param stdClass $poll The poll
+ * @param MailService $mailService The mail service
+ * @param int $type cf: Constants on the top of this page
+ */
+function sendUpdateNotification($poll, $mailService, $type) {
+ if (!isset($_SESSION['mail_sent'])) {
+ $_SESSION['mail_sent'] = [];
+ }
+
+ if ($poll->receiveNewVotes) {
+
+ $subject = '[' . NOMAPPLICATION . '] ' . __('Mail', 'Notification of poll') . ' : ' . $poll->title;
+
+ $message = '';
+ switch ($type) {
+ case UPDATE_POLL:
+ $message = __f('Mail', 'Someone just change your poll available at the following link %s.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
+ break;
+ case DELETED_POLL:
+ $message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
+ break;
+ }
+
+ $messageTypeKey = $type . '-' . $poll->id;
+ $mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
+ }
+}
/* PAGE */
/* ---- */
@@ -111,7 +153,8 @@ if (isset($_POST['update_poll_info'])) {
break;
}
} elseif ($field == 'expiration_date') {
- $expiration_date = filter_input(INPUT_POST, 'expiration_date', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]+[-/][0-9]+[-/][0-9]+#']]);
+ $expiration_date = filter_input(INPUT_POST, 'expiration_date', FILTER_VALIDATE_REGEXP,
+ ['options' => ['regexp' => '#^[0-9]{4}-[0-9]{2}-[0-9]{2}$#']]);
if ($expiration_date) {
$poll->end_date = $expiration_date;
$updated = true;
@@ -133,6 +176,7 @@ if (isset($_POST['update_poll_info'])) {
// Update poll in database
if ($updated && $adminPollService->updatePoll($poll)) {
$message = new Message('success', __('adminstuds', 'Poll saved'));
+ sendUpdateNotification($poll, $mailService, UPDATE_POLL);
} else {
$message = new Message('danger', __('Error', 'Failed to save poll'));
$poll = $pollService->findById($poll_id);
@@ -297,6 +341,7 @@ if (isset($_POST['delete_poll'])) {
if (isset($_POST['confirm_delete_poll'])) {
if ($adminPollService->deleteEntirePoll($poll_id)) {
$message = new Message('success', __('adminstuds', 'Poll fully deleted'));
+ sendUpdateNotification($poll, $mailService, DELETED_POLL);
} else {
$message = new Message('danger', __('Error', 'Failed to delete the poll'));
}
@@ -322,15 +367,15 @@ if (!empty($_GET['delete_column'])) {
$slot->title = $ex[0];
$slot->moment = $ex[1];
- $result = $adminPollService->deleteDateSlot($poll_id, $slot);
+ $result = $adminPollService->deleteDateSlot($poll, $slot);
} else {
- $result = $adminPollService->deleteClassicSlot($poll_id, $column);
+ $result = $adminPollService->deleteClassicSlot($poll, $column);
}
if ($result) {
$message = new Message('success', __('adminstuds', 'Column removed'));
} else {
- $message = new Message('danger', __('Error', 'Failed to delete the column'));
+ $message = new Message('danger', __('Error', 'Failed to delete column'));
}
}
@@ -352,10 +397,10 @@ if (isset($_POST['confirm_add_slot'])) {
$newmoment = strip_tags($_POST['newmoment']);
$ex = explode('/', $newdate);
- $result = $adminPollService->addSlot($poll_id, mktime(0, 0, 0, $ex[1], $ex[0], $ex[2]), $newmoment);
+ $result = $adminPollService->addDateSlot($poll_id, mktime(0, 0, 0, $ex[1], $ex[0], $ex[2]), $newmoment);
} else {
$newslot = strip_tags($_POST['choice']);
- $result = $adminPollService->addSlot($poll_id, $newslot, null);
+ $result = $adminPollService->addClassicSlot($poll_id, $newslot);
}
if ($result) {
@@ -366,7 +411,7 @@ if (isset($_POST['confirm_add_slot'])) {
}
// Retrieve data
-$slots = $pollService->allSlotsByPollId($poll_id);
+$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
@@ -377,7 +422,7 @@ $smarty->assign('admin_poll_id', $admin_poll_id);
$smarty->assign('poll', $poll);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->assign('expired', strtotime($poll->end_date) < time());
-$smarty->assign('deletion_date', $poll->end_date + PURGE_DELAY * 86400);
+$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
diff --git a/app/classes/Framadate/Form.php b/app/classes/Framadate/Form.php
index ce802ba..f73cd9e 100644
--- a/app/classes/Framadate/Form.php
+++ b/app/classes/Framadate/Form.php
@@ -18,8 +18,6 @@
*/
namespace Framadate;
-use Framadate\Editable;
-
class Form
{
diff --git a/app/classes/Framadate/Repositories/SlotRepository.php b/app/classes/Framadate/Repositories/SlotRepository.php
index fcd11c2..7d69e00 100644
--- a/app/classes/Framadate/Repositories/SlotRepository.php
+++ b/app/classes/Framadate/Repositories/SlotRepository.php
@@ -61,7 +61,7 @@ class SlotRepository extends AbstractRepository {
}
function listByPollId($poll_id) {
- $prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY title');
+ $prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
return $prepared->fetchAll();
diff --git a/app/classes/Framadate/Services/AdminPollService.php b/app/classes/Framadate/Services/AdminPollService.php
index b4b47d5..fb020bc 100644
--- a/app/classes/Framadate/Services/AdminPollService.php
+++ b/app/classes/Framadate/Services/AdminPollService.php
@@ -105,17 +105,21 @@ class AdminPollService {
/**
* Delete a slot from a poll.
*
- * @param $poll_id int The ID of the poll
- * @param $slot object The slot informations (datetime + moment)
+ * @param object $poll The ID of the poll
+ * @param object $slot The slot informations (datetime + moment)
* @return bool true if action succeeded
*/
- public function deleteDateSlot($poll_id, $slot) {
- $this->logService->log('DELETE_SLOT', 'id:' . $poll_id . ', slot:' . json_encode($slot));
+ public function deleteDateSlot($poll, $slot) {
+ $this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$datetime = $slot->title;
$moment = $slot->moment;
- $slots = $this->pollService->allSlotsByPollId($poll_id);
+ $slots = $this->pollService->allSlotsByPoll($poll);
+
+ if (count($slots) === 1) {
+ return false;
+ }
$index = 0;
$indexToDelete = -1;
@@ -139,21 +143,25 @@ class AdminPollService {
// Remove votes
$this->connect->beginTransaction();
- $this->voteRepository->deleteByIndex($poll_id, $indexToDelete);
+ $this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
if (count($newMoments) > 0) {
- $this->slotRepository->update($poll_id, $datetime, implode(',', $newMoments));
+ $this->slotRepository->update($poll->id, $datetime, implode(',', $newMoments));
} else {
- $this->slotRepository->deleteByDateTime($poll_id, $datetime);
+ $this->slotRepository->deleteByDateTime($poll->id, $datetime);
}
$this->connect->commit();
return true;
}
- public function deleteClassicSlot($poll_id, $slot_title) {
- $this->logService->log('DELETE_SLOT', 'id:' . $poll_id . ', slot:' . $slot_title);
+ public function deleteClassicSlot($poll, $slot_title) {
+ $this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
- $slots = $this->pollService->allSlotsByPollId($poll_id);
+ $slots = $this->pollService->allSlotsByPoll($poll);
+
+ if (count($slots) === 1) {
+ return false;
+ }
$index = 0;
$indexToDelete = -1;
@@ -168,15 +176,15 @@ class AdminPollService {
// Remove votes
$this->connect->beginTransaction();
- $this->voteRepository->deleteByIndex($poll_id, $indexToDelete);
- $this->slotRepository->deleteByDateTime($poll_id, $slot_title);
+ $this->voteRepository->deleteByIndex($poll->id, $indexToDelete);
+ $this->slotRepository->deleteByDateTime($poll->id, $slot_title);
$this->connect->commit();
return true;
}
/**
- * Add a new slot to the poll. And insert default values for user's votes.
+ * Add a new slot to a date poll. And insert default values for user's votes.
*
*
Create a new slot if no one exists for the given date
*
Create a new moment if a slot already exists for the given date
@@ -187,7 +195,7 @@ class AdminPollService {
* @param $new_moment string The moment's name
* @return bool true if added
*/
- public function addSlot($poll_id, $datetime, $new_moment) {
+ public function addDateSlot($poll_id, $datetime, $new_moment) {
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime, $new_moment);
@@ -224,6 +232,44 @@ class AdminPollService {
}
+ /**
+ * Add a new slot to a classic poll. And insert default values for user's votes.
+ *
+ *
Create a new slot if no one exists for the given title
+ *
+ *
+ * @param $poll_id int The ID of the poll
+ * @param $title int The title
+ * @return bool true if added
+ */
+ public function addClassicSlot($poll_id, $title) {
+ $slots = $this->slotRepository->listByPollId($poll_id);
+
+ // Check if slot already exists
+ $titles = array_map(function ($slot) {
+ return $slot->title;
+ }, $slots);
+ if (in_array($title, $titles)) {
+ // The moment already exists
+ return false;
+ }
+
+
+ // Begin transaction
+ $this->connect->beginTransaction();
+
+ // New slot
+ $this->slotRepository->insert($poll_id, $title, null);
+ // Set default votes
+ $this->voteRepository->insertDefault($poll_id, count($slots));
+
+ // Commit transaction
+ $this->connect->commit();
+
+ return true;
+
+ }
+
/**
* This method find where to insert a datatime+moment into a list of slots.
* Return the {insert:X}, where X is the index of the moment into the whole poll (ex: X=0 => Insert to the first column).
diff --git a/app/classes/Framadate/Services/InputService.php b/app/classes/Framadate/Services/InputService.php
index c7de298..31b3103 100644
--- a/app/classes/Framadate/Services/InputService.php
+++ b/app/classes/Framadate/Services/InputService.php
@@ -51,8 +51,7 @@ class InputService {
}
public function filterTitle($title) {
- $filtered = filter_var($title, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => TITLE_REGEX]]);
- return $this->returnIfNotBlank($filtered);
+ return $this->returnIfNotBlank($title);
}
public function filterName($name) {
@@ -66,7 +65,7 @@ class InputService {
public function filterDescription($description) {
$description = str_replace("\r\n", "\n", $description);
- return filter_var($description, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => DESCRIPTION_REGEX]]);
+ return $description;
}
public function filterBoolean($boolean) {
@@ -79,8 +78,7 @@ class InputService {
public function filterComment($comment) {
$comment = str_replace("\r\n", "\n", $comment);
- $filtered = filter_var($comment, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => COMMENT_REGEX]]);
- return $this->returnIfNotBlank($filtered);
+ return $this->returnIfNotBlank($comment);
}
/**
diff --git a/app/classes/Framadate/Services/MailService.php b/app/classes/Framadate/Services/MailService.php
index 2b7dd66..18807b2 100644
--- a/app/classes/Framadate/Services/MailService.php
+++ b/app/classes/Framadate/Services/MailService.php
@@ -5,7 +5,14 @@ class MailService {
private $smtp_allowed;
+ const DELAY_BEFORE_RESEND = 300;
+
+ const MAILSERVICE_KEY = 'mailservice';
+
+ private $logService;
+
function __construct($smtp_allowed) {
+ $this->logService = new LogService();
$this->smtp_allowed = $smtp_allowed;
}
@@ -13,10 +20,12 @@ class MailService {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
- function send($to, $subject, $body, $param = '') {
- if ($this->smtp_allowed == true) {
+ function send($to, $subject, $body, $msgKey = null) {
+ if ($this->smtp_allowed == true && $this->canSendMsg($msgKey)) {
mb_internal_encoding('UTF-8');
+ // Build headers
+
$subject = mb_encode_mimeheader(html_entity_decode($subject, ENT_QUOTES, 'UTF-8'), 'UTF-8', 'B', "\n", 9);
$encoded_app = mb_encode_mimeheader(NOMAPPLICATION, 'UTF-8', 'B', "\n", 6);
@@ -39,11 +48,38 @@ class MailService {
$headers .= "Auto-Submitted:auto-generated\n";
$headers .= 'Return-Path: <>';
+ // Build body
+
$body = $body . '
('. __('Date', 'dd/mm/yyyy') .')
@@ -221,17 +221,15 @@ if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) ||
} else {
// Prefill form->choices
- if (count($_SESSION['form']->getChoices()) == 0) {
- $c = new Choice('');
- $c->addSlot('');
- $c->addSlot('');
- $c->addSlot('');
- $_SESSION['form']->addChoice($c);
- $c = new Choice('');
- $c->addSlot('');
- $c->addSlot('');
- $c->addSlot('');
- $_SESSION['form']->addChoice($c);
+ foreach ($_SESSION['form']->getChoices() as $c) {
+ $count = 3 - count($c->getSlots());
+ for($i=0; $i< $count; $i++) {
+ $c->addSlot('');
+ }
+ }
+
+ $count = 3 - count($_SESSION['form']->getChoices());
+ for($i=0; $i< $count; $i++) {
$c = new Choice('');
$c->addSlot('');
$c->addSlot('');
diff --git a/create_poll.php b/create_poll.php
index 8682c74..43c5b4a 100644
--- a/create_poll.php
+++ b/create_poll.php
@@ -90,7 +90,7 @@ if ($goToStep2) {
$error_on_name = true;
}
- if ($description !== $_POST['description']) {
+ if ($description === false) {
$error_on_description = true;
}
diff --git a/exportcsv.php b/exportcsv.php
index f5b5d0b..15fd9cb 100644
--- a/exportcsv.php
+++ b/exportcsv.php
@@ -18,9 +18,6 @@
*/
use Framadate\Services\LogService;
use Framadate\Services\PollService;
-use Framadate\Services\InputService;
-use Framadate\Services\MailService;
-use Framadate\Message;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
@@ -54,7 +51,7 @@ if (!$poll) {
}
-$slots = $pollService->allSlotsByPollId($poll_id);
+$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
// CSV header
diff --git a/find_polls.php b/find_polls.php
index e3ca6dd..c4ced25 100644
--- a/find_polls.php
+++ b/find_polls.php
@@ -43,7 +43,7 @@ if (!empty($_POST['mail'])) {
$smarty->assign('polls', $polls);
$body = $smarty->fetch('mail/find_polls.tpl');
- $mailService->send($mail, __('Homepage', 'Where are my polls'), $body);
+ $mailService->send($mail, __('Homepage', 'Where are my polls'), $body, 'SEND_POLLS');
$message = new Message('success', __('FindPolls', 'Polls sent'));
} else {
$message = new Message('warning', __('Error', 'No polls found'));
diff --git a/images/question.png b/images/question.png
deleted file mode 100644
index 73d1d94..0000000
Binary files a/images/question.png and /dev/null differ
diff --git a/index.php b/index.php
index e756076..364c90c 100644
--- a/index.php
+++ b/index.php
@@ -17,9 +17,7 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
-use Framadate\Services\LogService;
use Framadate\Services\PollService;
-use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
diff --git a/js/app/classic_poll.js b/js/app/classic_poll.js
index cd3938c..b196822 100644
--- a/js/app/classic_poll.js
+++ b/js/app/classic_poll.js
@@ -28,11 +28,21 @@
});
if (nb_filled_choices >= 1) {
$('button[name="fin_sondage_autre"]').removeClass('disabled');
+ return true;
} else {
$('button[name="fin_sondage_autre"]').addClass('disabled');
+ return false;
}
};
+ // Handle form submission
+ $(document.formulaire).on('submit', function (e) {
+ if (!submitChoicesAvalaible()) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+
// Button "Add a choice"
$('#add-a-choice').on('click', function () {
diff --git a/js/app/date_poll.js b/js/app/date_poll.js
index 67c71a0..d63684d 100644
--- a/js/app/date_poll.js
+++ b/js/app/date_poll.js
@@ -42,11 +42,21 @@ $(document).ready(function () {
if (nb_filled_days >= 1 && nb_filled_hours >= 1) {
$('button[name="choixheures"]').removeClass('disabled');
+ return true;
} else {
$('button[name="choixheures"]').addClass('disabled');
+ return false;
}
};
+ // Handle form submission
+ $(document.formulaire).on('submit', function (e) {
+ if (!submitDaysAvalaible()) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+
// Button "Remove all hours"
$(document).on('click', '#resethours', function () {
diff --git a/js/app/framadatepicker.js b/js/app/framadatepicker.js
index 20ebca0..c09a30b 100644
--- a/js/app/framadatepicker.js
+++ b/js/app/framadatepicker.js
@@ -47,7 +47,7 @@ $(document).ready(function () {
});
};
- $(document).on('click', '.input-group.date .input-group-addon', function () {
+ $(document).on('click', '.input-group.date .input-group-addon, .input-group.date input', function () {
// Re-init datepicker config before displaying
init_datepicker();
$(this).parent().datepicker('show');
diff --git a/locale/de.json b/locale/de.json
index 40a12cb..d70c502 100644
--- a/locale/de.json
+++ b/locale/de.json
@@ -189,7 +189,6 @@
"Remove the comments": "Entfernen Sie die Kommentare",
"Comment deleted": "Kommentar gelöscht",
"All comments deleted": "Alle Kommentare gelöscht",
- "The poll has been deleted": "Die Umfrage wurde gelöscht",
"Keep votes": "Halten Stimmen",
"Keep comments": "Halten Sie Kommentare",
"Keep this poll": "Halten Sie diese Umfrage"
@@ -200,11 +199,11 @@
"Required fields cannot be left blank.": "Mit * markierte Felder müssen ausgefüllt sein.",
"Poll title": "Umfragetitel",
"Votes cannot be modified.": "DE_Aucun vote ne peut être modifié",
- "All voters can modify any vote.": "DE_Tous les sondés peuvent modifier tous les votes",
- "Voters can modify their vote themselves.": "Teilnehmer können ihre Antworten verändern",
- "To receive an email for each new vote.": "Bei jeder neuen Abstimmung eine E-Mail erhalten.",
- "To receive an email for each new comment.": "Um eine E-Mail für jede neue Kommentar zu empfangen.",
- "Only the poll maker can see the poll's results.": "DE_Seul le créateur du sondage peut voir les résultats.",
+ "All voters can modify any vote": "DE_Tous les sondés peuvent modifier tous les votes",
+ "Voters can modify their vote themselves": "Teilnehmer können ihre Antworten verändern",
+ "To receive an email for each new vote": "Bei jeder neuen Abstimmung eine E-Mail erhalten",
+ "To receive an email for each new comment": "Um eine E-Mail für jede neue Kommentar zu empfangen",
+ "Only the poll maker can see the poll's results": "DE_Seul le créateur du sondage peut voir les résultats",
"Go to step 2": "Weiter zum 2. Schritt"
},
"Step 2": {
@@ -289,9 +288,12 @@
},
"Mail": {
"Poll's participation": "Beteiligung an der Umfrage",
+ "Notification of poll": "Mitteilung bezüglich der Umfrage",
"filled a vote.\nYou can find your poll at the link": "füllte eine Stimme. Sie können Ihre Umfrage unter dem Link zu finden",
"updated a vote.\nYou can find your poll at the link": "eine Abstimmung regelmäßig aktualisiert. Sie können Ihre Umfrage unter dem Link zu finden",
"wrote a comment.\nYou can find your poll at the link": "hat einen Kommentar. Sie können Ihre Umfrage unter dem Link zu finden",
+ "Someone just change your poll available at the following link %s.": "Jemand ändern Sie einfach Ihre Umfrage finden Sie unter dem folgenden Link %1$s.",
+ "Someone just delete your poll %s.": "Jemand hat gerade Ihre Umfrage löschen \"%s\".",
"Thanks for your trust.": "Danke für Ihr Vertrauen.",
"FOOTER": "",
"[ADMINISTRATOR] New settings for your poll": "[ADMINISTRATOR] Neue Einstellungen für Ihre Umfrage ",
@@ -326,6 +328,7 @@
"Update vote failed": "Update vote failed",
"Adding vote failed": "Adding vote failed",
"Comment failed": "Kommentar gescheitert",
- "You can't create a poll with hidden results with the following edition option:": "Sie können nicht eine Umfrage erstellen mit versteckten Ergebnisse mit der folgenden Ausgabe-Option:"
+ "You can't create a poll with hidden results with the following edition option:": "Sie können nicht eine Umfrage erstellen mit versteckten Ergebnisse mit der folgenden Ausgabe-Option:",
+ "Failed to delete column": "Fehler beim Spalte löschen"
}
}
diff --git a/locale/en.json b/locale/en.json
index 8847640..bc4d4ed 100644
--- a/locale/en.json
+++ b/locale/en.json
@@ -189,7 +189,6 @@
"All comments deleted": "All comments deleted",
"Keep the comments": "Keep the comments",
"Remove the comments": "Remove the comments",
- "The poll has been deleted": "The poll has been deleted",
"Keep votes": "Keep votes",
"Keep comments": "Keep comments",
"Keep this poll": "Keep this poll"
@@ -200,11 +199,11 @@
"Required fields cannot be left blank.": "Required fields cannot be left blank.",
"Poll title": "Poll title",
"Votes cannot be modified.": "Votes cannot be modified",
- "All voters can modify any vote.": "All voters can modify any vote.",
- "Voters can modify their vote themselves.": "Voters can modify their vote themselves.",
- "To receive an email for each new vote.": "To receive an email for each new vote.",
- "To receive an email for each new comment.": "To receive an email for each new comment.",
- "Only the poll maker can see the poll's results.": "Only the poll maker can see the poll's results.",
+ "All voters can modify any vote": "All voters can modify any vote",
+ "Voters can modify their vote themselves": "Voters can modify their vote themselves",
+ "To receive an email for each new vote": "To receive an email for each new vote",
+ "To receive an email for each new comment": "To receive an email for each new comment",
+ "Only the poll maker can see the poll's results": "Only the poll maker can see the poll's results",
"Go to step 2": "Go to step 2"
},
"Step 2": {
@@ -289,9 +288,12 @@
},
"Mail" : {
"Poll's participation": "Poll's participation",
+ "Notification of poll": "Notification of poll",
"filled a vote.\nYou can find your poll at the link": "filled a vote. You can find your poll at the link",
"updated a vote.\nYou can find your poll at the link": "updated a vote. You can find your poll at the link",
"wrote a comment.\nYou can find your poll at the link": "wrote a comment. You can find your poll at the link",
+ "Someone just change your poll available at the following link %s.": "Someone just change your poll available at the following link %1$s.",
+ "Someone just delete your poll %s.": "Someone just delete your poll \"%s\".",
"Thanks for your trust.": "Thanks for your trust.",
"FOOTER": "",
"[ADMINISTRATOR] New settings for your poll": "[ADMINISTRATOR] New settings for your poll",
@@ -326,6 +328,7 @@
"Update vote failed": "Update vote failed",
"Adding vote failed": "Adding vote failed",
"Comment failed": "Comment failed",
- "You can't create a poll with hidden results with the following edition option:": "You can't create a poll with hidden results with the following edition option: "
+ "You can't create a poll with hidden results with the following edition option:": "You can't create a poll with hidden results with the following edition option: ",
+ "Failed to delete column": "Failed to delete column"
}
}
diff --git a/locale/es.json b/locale/es.json
index 89e5942..357ff31 100644
--- a/locale/es.json
+++ b/locale/es.json
@@ -189,7 +189,6 @@
"Remove the comments": "ES_Supprimer les commentaires",
"Comment deleted": "ES_Commentaire supprimé",
"All comments deleted": "ES_Tous les commentaires ont été supprimés",
- "The poll has been deleted": "ES_Le sondage a été supprimé",
"Keep votes": "ES_Garder les votes",
"Keep comments": "ES_Garder les commentaires",
"Keep this poll": "Dejar este encuesta!"
@@ -200,11 +199,11 @@
"Required fields cannot be left blank.": "Gracias por completar los campos con una *.",
"Poll title": "ES_Titre du sondage",
"Votes cannot be modified.": "ES_Aucun vote ne peut être modifié",
- "All voters can modify any vote.": "ES_Tous les sondés peuvent modifier tous les votes",
- "Voters can modify their vote themselves.": "Los encuentados pueden cambiar su línea ellos mismos.",
- "To receive an email for each new vote.": "Usted quiere recibir un correo electónico cada vez que alguien participe a la encuesta.",
- "To receive an email for each new comment.": "ES_Recevoir un courriel à chaque commentaire.",
- "Only the poll maker can see the poll's results.": "ES_Seul le créateur du sondage peut voir les résultats.",
+ "All voters can modify any vote": "ES_Tous les sondés peuvent modifier tous les votes",
+ "Voters can modify their vote themselves": "Los encuentados pueden cambiar su línea ellos mismos",
+ "To receive an email for each new vote": "Usted quiere recibir un correo electónico cada vez que alguien participe a la encuesta",
+ "To receive an email for each new comment": "ES_Recevoir un courriel à chaque commentaire",
+ "Only the poll maker can see the poll's results": "ES_Seul le créateur du sondage peut voir les résultats",
"Go to step 2": "ES_Aller à l'étape 2"
},
"Step 2": {
@@ -244,7 +243,7 @@
"Confirm the creation of your poll": "ES_Confirmez la création de votre sondage",
"List of your choices": "ES_Liste de vos choix",
"Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.": "ES_Une fois que vous aurez confirmé la création du sondage, vous serez redirigé automatiquement vers la page d'administration de votre sondage.",
- "Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.": "ES_En même temps, vous recevrez deux courriels : l'un contenant le lien vers votre sondage pour le faire suivre aux futurs sondés, l'autre contenant le lien vers la page d'administraion du sondage.",
+ "Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.": "ES_En même temps, vous recevrez deux courriels : l'un contenant le lien vers votre sondage pour le faire suivre aux futurs sondés, l'autre contenant le lien vers la page d'administration du sondage.",
"Create the poll": "Crear la encuesta",
"Your poll will automatically be archived": "ES_Votre sondage sera automatiquement archivé",
"after the last date of your poll.": "ES_après la date la plus tardive.",
@@ -288,10 +287,13 @@
"Polls sent": "ES_Sondages envoyés"
},
"Mail": {
- "Poll's participation": "ES_Participation au sondage",
+ "Poll's participation": "Participación Encuesta",
+ "Notification of poll": "Notificación de la encuesta",
"filled a vote.\nYou can find your poll at the link": "ES_vient de voter. Vous pouvez retrouver votre sondage avec le lien suivant",
"updated a vote.\nYou can find your poll at the link": "ES_vient de mettre à jour un vote. Vous pouvez retrouver votre sondage avec le lien suivant",
"wrote a comment.\nYou can find your poll at the link": "ES_vient de rédiger un commentaire. Vous pouvez retrouver votre sondage avec le lien suivant",
+ "Someone just change your poll available at the following link %s.": "Alguien acaba de cambiar su encuesta disponible en el siguiente enlace %1$s.",
+ "Someone just delete your poll %s.": "Alguien acaba de borrar tu encuesta \"%s\".",
"Thanks for your trust.": "ES_Merci de votre confiance.",
"FOOTER": "",
"[ADMINISTRATOR] New settings for your poll": "ES_[ADMINISTRATEUR] Changement de configuration du sondage",
@@ -306,8 +308,8 @@
"Error": {
"Error!": "Error!",
"Enter a title": "Introducza un título",
- "Something is going wrong...": "ES_Quelque chose ne va pas...",
- "Something is wrong with the format": "Something is wrong with the format",
+ "Something is going wrong...": "Algo va mal...",
+ "Something is wrong with the format": "Algo está mal con el formato",
"Enter an email address": "Introduzca una dirección electrónica",
"The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.": "ES_L'adresse saisie n'est pas correcte ! Il faut une adresse électronique valide (par exemple r.stallman@outlock.com) pour recevoir le lien vers le sondage.",
"No polls found": "ES_Aucun sondage n'a été trouvé",
@@ -322,10 +324,11 @@
"Enter a name and a comment!": "Introduzca su nombre y un comentario!",
"Failed to insert the comment!": "ES_Échec à l'insertion du commentaire !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "ES_Framadate n'est pas installé correctement, lisez le fichier INSTALL pour configurer la base de données avant de continuer.",
- "Failed to save poll": "ES_Echèc de la sauvegarde du sondage",
+ "Failed to save poll": "ES_Echec de la sauvegarde du sondage",
"Update vote failed": "ES_Mise à jour du vote échoué",
"Adding vote failed": "ES_Ajout d'un vote échoué",
"Comment failed": "ES_Commentaire échoué",
- "You can't create a poll with hidden results with the following edition option:": "ES_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : "
+ "You can't create a poll with hidden results with the following edition option:": "ES_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ",
+ "Failed to delete column": "Error al eliminar la columna"
}
}
diff --git a/locale/fr.json b/locale/fr.json
index 11417ec..cc0f5e6 100644
--- a/locale/fr.json
+++ b/locale/fr.json
@@ -189,7 +189,6 @@
"Remove the comments": "Supprimer les commentaires",
"Comment deleted": "Commentaire supprimé",
"All comments deleted": "Tous les commentaires ont été supprimés",
- "The poll has been deleted": "Le sondage a été supprimé",
"Keep votes": "Garder les votes",
"Keep comments": "Garder les commentaires",
"Keep this poll": "Garder ce sondage"
@@ -200,11 +199,11 @@
"Required fields cannot be left blank.": "Merci de remplir les champs obligatoires, marqués d'une *.",
"Poll title": "Titre du sondage",
"Votes cannot be modified.": "Aucun vote ne peut être modifié",
- "All voters can modify any vote.": "Tous les sondés peuvent modifier tous les votes",
- "Voters can modify their vote themselves.": "Chaque sondé peut modifier son propre vote.",
- "To receive an email for each new vote.": "Recevoir un courriel à chaque participation d'un sondé.",
- "To receive an email for each new comment.": "Recevoir un courriel à chaque commentaire.",
- "Only the poll maker can see the poll's results.": "Seul le créateur du sondage peut voir les résultats.",
+ "All voters can modify any vote": "Tous les sondés peuvent modifier tous les votes",
+ "Voters can modify their vote themselves": "Chaque sondé peut modifier son propre vote",
+ "To receive an email for each new vote": "Recevoir un courriel à chaque participation d'un sondé",
+ "To receive an email for each new comment": "Recevoir un courriel à chaque commentaire",
+ "Only the poll maker can see the poll's results": "Seul le créateur du sondage peut voir les résultats",
"Go to step 2": "Aller à l'étape 2"
},
"Step 2": {
@@ -244,7 +243,7 @@
"Confirm the creation of your poll": "Confirmez la création de votre sondage",
"List of your choices": "Liste de vos choix",
"Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.": "Une fois que vous aurez confirmé la création du sondage, vous serez redirigé automatiquement vers la page d'administration de votre sondage.",
- "Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.": "En même temps, vous recevrez deux courriels : l'un contenant le lien vers votre sondage pour le faire suivre aux futurs sondés, l'autre contenant le lien vers la page d'administraion du sondage.",
+ "Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.": "En même temps, vous recevrez deux courriels : l'un contenant le lien vers votre sondage pour le faire suivre aux futurs sondés, l'autre contenant le lien vers la page d'administration du sondage.",
"Create the poll": "Créer le sondage",
"Your poll will automatically be archived": "Votre sondage sera automatiquement archivé",
"after the last date of your poll.": "après la date la plus tardive.",
@@ -289,9 +288,12 @@
},
"Mail": {
"Poll's participation": "Participation au sondage",
+ "Notification of poll": "Notification d'un sondage",
"filled a vote.\nYou can find your poll at the link": "vient de voter. Vous pouvez retrouver votre sondage avec le lien suivant",
"updated a vote.\nYou can find your poll at the link": "vient de mettre à jour un vote. Vous pouvez retrouver votre sondage avec le lien suivant",
"wrote a comment.\nYou can find your poll at the link": "vient de rédiger un commentaire. Vous pouvez retrouver votre sondage avec le lien suivant",
+ "Someone just change your poll available at the following link %s.": "Quelqu'un vient de modifier votre sondage accessible au lien suivant %1$s.",
+ "Someone just delete your poll %s.": "Quelqu'un vient de supprimer votre sondage \"%s\".",
"Thanks for your trust.": "Merci de votre confiance.",
"FOOTER": "« La route est longue, mais la voie est libre… » Framasoft ne vit que par vos dons (déductibles des impôts). Merci d'avance pour votre soutien http://soutenir.framasoft.org.",
"[ADMINISTRATOR] New settings for your poll": "[ADMINISTRATEUR] Changement de configuration du sondage",
@@ -322,10 +324,11 @@
"Enter a name and a comment!": "Merci de remplir les deux champs !",
"Failed to insert the comment!": "Échec à l'insertion du commentaire !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate n'est pas installé correctement, lisez le fichier INSTALL pour configurer la base de données avant de continuer.",
- "Failed to save poll": "Echèc de la sauvegarde du sondage",
+ "Failed to save poll": "Echec de la sauvegarde du sondage",
"Update vote failed": "Mise à jour du vote échoué",
"Adding vote failed": "Ajout d'un vote échoué",
"Comment failed": "Commentaire échoué",
- "You can't create a poll with hidden results with the following edition option:": "Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : "
+ "You can't create a poll with hidden results with the following edition option:": "Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ",
+ "Failed to delete column": "Échec de la suppression de colonne"
}
}
diff --git a/locale/it.json b/locale/it.json
index fa3d0d6..c41a72e 100644
--- a/locale/it.json
+++ b/locale/it.json
@@ -189,7 +189,6 @@
"Remove the comments": "Eliminare i commenti",
"Comment deleted": "Commento rimosso",
"All comments deleted": "Tutti i commenti sono stati cancellati",
- "The poll has been deleted": "Il sondaggio è stato eliminato",
"Keep votes": "Tenere voti",
"Keep comments": "Tenere commenti",
"Keep this poll": "Mantengo questo sondaggio !"
@@ -200,11 +199,11 @@
"Required fields cannot be left blank.": "Riempire i campi obbligatori, segnati con *.",
"Poll title": "Titolo del sondaggio",
"Votes cannot be modified.": "No voto non può essere modificato",
- "All voters can modify any vote.": "Tutti i sondaggi possono cambiare tutti i voti",
- "Voters can modify their vote themselves.": "I partecipanti possono modificare il loro voto in autonomia.",
- "To receive an email for each new vote.": "Per ricevere un'email per ogni voto nuovo",
- "To receive an email for each new comment.": "Ricevi una e-mail ogni commento.",
- "Only the poll maker can see the poll's results.": "Solo il creatore sondaggio possono vedere i risultati.",
+ "All voters can modify any vote": "Tutti i sondaggi possono cambiare tutti i voti",
+ "Voters can modify their vote themselves": "I partecipanti possono modificare il loro voto in autonomia",
+ "To receive an email for each new vote": "Per ricevere un'email per ogni voto nuovo",
+ "To receive an email for each new comment": "Ricevi una e-mail ogni commento",
+ "Only the poll maker can see the poll's results": "Solo il creatore sondaggio possono vedere i risultati",
"Go to step 2": "Andare al punto 2"
},
"Step 2": {
@@ -245,7 +244,7 @@
"List of your choices": "Liste delle vostre scelte",
"Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.": "Un volta che avrete confermato la creazione del sondaggio, sarete rediretti automaticamente sulla pagina di amministrazione del vostro sondaggio.",
"Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.": "Nello stesso momento, riceverete 2 email : una con il Link verso il vostro sondaggio da inoltrare ai partecipanti, l'altra con il Link verso la pagina di amministrazione del sondaggio.",
- "Create the poll": "Creare le sondaggio",
+ "Create the poll": "Creare il sondaggio",
"Your poll will automatically be archived": "La vostra indagine verrà archiviata automaticamente",
"after the last date of your poll.": "dopo l'ultima data.",
"You can set a closer archiving date for it.": "Si può decidere su una data più vicina di archiviazione.",
@@ -289,9 +288,12 @@
},
"Mail": {
"Poll's participation": "Partecipazione al sondaggio",
+ "Notification of poll": "Notifica di sondaggio",
"filled a vote.\nYou can find your poll at the link": "IT_vient de voter.\nVous pouvez retrouver votre sondage avec le lien suivant",
"updated a vote.\nYou can find your poll at the link": "IT_vient de mettre à jour un vote.\nVous pouvez retrouver votre sondage avec le lien suivant",
"wrote a comment.\nYou can find your poll at the link": "IT_vient de rédiger un commentaire.\nVous pouvez retrouver votre sondage avec le lien suivant",
+ "Someone just change your poll available at the following link %s.": "Qualcuno basta cambiare il vostro sondaggio disponibile al seguente link %1$s.",
+ "Someone just delete your poll %s.": "Qualcuno ha appena cancellare il tuo sondaggio \"%s\".",
"Thanks for your trust.": "Grazie per la vostra fiducia.",
"FOOTER": "IT_« La route est longue, mais la voie est libre… »\nFramasoft ne vit que par vos dons (déductibles des impôts).\nMerci d'avance pour votre soutien http://soutenir.framasoft.org.",
"[ADMINISTRATOR] New settings for your poll": "[AMMINISTRATORE] Modifica di configurazione del sondaggio",
@@ -322,10 +324,11 @@
"Enter a name and a comment!": "Inserire un nome e un commento!",
"Failed to insert the comment!": "Errore nell'inserimento del commento !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "IT_Framadate n'est pas installé correctement, lisez le fichier INSTALL pour configurer la base de données avant de continuer.",
- "Failed to save poll": "IT_Echèc de la sauvegarde du sondage",
+ "Failed to save poll": "IT_Echec de la sauvegarde du sondage",
"Update vote failed": "IT_Mise à jour du vote échoué",
"Adding vote failed": "IT_Ajout d'un vote échoué",
"Comment failed": "IT_Commentaire échoué",
- "You can't create a poll with hidden results with the following edition option:": "IT_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : "
+ "You can't create a poll with hidden results with the following edition option:": "IT_Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ",
+ "Failed to delete column": "Impossibile eliminare colonna"
}
}
\ No newline at end of file
diff --git a/phpunit.bat b/phpunit.bat
new file mode 100644
index 0000000..7691614
--- /dev/null
+++ b/phpunit.bat
@@ -0,0 +1 @@
+vendor\bin\phpunit.bat --bootstrap app\tests\bootstrap.php app/tests
\ No newline at end of file
diff --git a/phpunit.sh b/phpunit.sh
new file mode 100644
index 0000000..ce4446f
--- /dev/null
+++ b/phpunit.sh
@@ -0,0 +1 @@
+vendor\bin\phpunit --bootstrap app\tests\bootstrap.php app/tests
\ No newline at end of file
diff --git a/studs.php b/studs.php
index 0bcf460..7270fd1 100644
--- a/studs.php
+++ b/studs.php
@@ -64,7 +64,7 @@ function sendUpdateNotification($poll, $mailService, $name, $type) {
$_SESSION['mail_sent'] = [];
}
- if ($poll->receiveNewVotes && (!isset($_SESSION['mail_sent'][$poll->id]) || $_SESSION['mail_sent'][$poll->id] !== true)) {
+ if ($poll->receiveNewVotes) {
$subject = '[' . NOMAPPLICATION . '] ' . __('Mail', 'Poll\'s participation') . ' : ' . $poll->title;
@@ -82,9 +82,8 @@ function sendUpdateNotification($poll, $mailService, $name, $type) {
}
$message .= Utils::getUrlSondage($poll->admin_id, true) . "\n\n";
- $mailService->send($poll->admin_mail, $subject, $message);
-
- $_SESSION['mail_sent'][$poll->id] = true;
+ $messageTypeKey = $type . '-' . $poll->id;
+ $mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
@@ -152,7 +151,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if (count($choices) != count($_POST['choices'])) {
- $message = new Message('danger', __('There is a problem with your choices'));
+ $message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
@@ -198,7 +197,7 @@ if (isset($_POST['add_comment'])) {
}
// Retrieve data
-$slots = $pollService->allSlotsByPollId($poll_id);
+$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
@@ -207,7 +206,7 @@ $smarty->assign('poll_id', $poll_id);
$smarty->assign('poll', $poll);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->assign('expired', strtotime($poll->end_date) < time());
-$smarty->assign('deletion_date', $poll->end_date + PURGE_DELAY * 86400);
+$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
diff --git a/tpl/admin/purge.tpl b/tpl/admin/purge.tpl
index 3033c08..ad13913 100644
--- a/tpl/admin/purge.tpl
+++ b/tpl/admin/purge.tpl
@@ -2,12 +2,12 @@
{block 'admin_main'}
{if $message}
-
- {__('Error', "You can't create a poll with hidden results with the following edition option:")}"{__('Step 1', 'All voters can modify any vote.')}"
+ {__('Error', 'You can\'t create a poll with hidden results with the following edition option:')}"{__('Step 1', 'All voters can modify any vote')}"