Merge branch 'develop' of git.framasoft.org:framasoft/framadate into develop

This commit is contained in:
Antonin 2016-03-05 16:03:57 +01:00
commit fdbb3d73c0
29 changed files with 804 additions and 188 deletions

View File

@ -26,6 +26,7 @@ use Framadate\Migration\Alter_Comment_table_for_name_length;
use Framadate\Migration\Alter_Comment_table_adding_date; use Framadate\Migration\Alter_Comment_table_adding_date;
use Framadate\Migration\Generate_uniqId_for_old_votes; use Framadate\Migration\Generate_uniqId_for_old_votes;
use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9; use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9;
use Framadate\Migration\Increase_pollId_size;
use Framadate\Migration\Migration; use Framadate\Migration\Migration;
use Framadate\Migration\RPadVotes_from_0_8; use Framadate\Migration\RPadVotes_from_0_8;
use Framadate\Utils; use Framadate\Utils;
@ -46,6 +47,7 @@ $migrations = [
new Alter_Comment_table_for_name_length(), new Alter_Comment_table_for_name_length(),
new Alter_Comment_table_adding_date(), new Alter_Comment_table_adding_date(),
new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(), new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(),
new Increase_pollId_size()
]; ];
// --------------------------------------- // ---------------------------------------

View File

@ -17,6 +17,8 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft) * Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/ */
use Framadate\Editable; use Framadate\Editable;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\MomentAlreadyExistsException; use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\Message; use Framadate\Message;
use Framadate\Services\AdminPollService; use Framadate\Services\AdminPollService;
@ -184,6 +186,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
$name = $inputService->filterName($_POST['name']); $name = $inputService->filterName($_POST['name']);
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT); $editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
if (empty($editedVote)) { if (empty($editedVote)) {
$message = new Message('danger', __('Error', 'Something is going wrong...')); $message = new Message('danger', __('Error', 'Something is going wrong...'));
@ -194,16 +197,21 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message == null) { if ($message == null) {
// Update vote // Update vote
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); try {
if ($result) { $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
$message = new Message('success', __('adminstuds', 'Vote updated')); if ($result) {
} else { $message = new Message('success', __('adminstuds', 'Vote updated'));
$message = new Message('danger', __('Error', 'Update vote failed')); } else {
$message = new Message('danger', __('Error', 'Update vote failed'));
}
} catch (ConcurrentEditionException $cee) {
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
} }
} }
} elseif (isset($_POST['save'])) { // Add a new vote } elseif (isset($_POST['save'])) { // Add a new vote
$name = $inputService->filterName($_POST['name']); $name = $inputService->filterName($_POST['name']);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]); $choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
if ($name == null) { if ($name == null) {
$message = new Message('danger', __('Error', 'The name is invalid.')); $message = new Message('danger', __('Error', 'The name is invalid.'));
@ -214,11 +222,17 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message == null) { if ($message == null) {
// Add vote // Add vote
$result = $pollService->addVote($poll_id, $name, $choices); try {
if ($result) { $result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
$message = new Message('success', __('adminstuds', 'Vote added')); if ($result) {
} else { $message = new Message('success', __('adminstuds', 'Vote added'));
$message = new Message('danger', __('Error', 'Adding vote failed')); } else {
$message = new Message('danger', __('Error', 'Adding vote failed'));
}
} catch (AlreadyExistsException $aee) {
$message = new Message('danger', __('Error', 'You already voted'));
} catch (ConcurrentEditionException $cee) {
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
} }
} }
} }
@ -228,12 +242,12 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
// ------------------------------- // -------------------------------
if (!empty($_GET['delete_vote'])) { if (!empty($_GET['delete_vote'])) {
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_INT); $vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BASE64_REGEX]]);
$vote_id = Utils::base64url_decode($vote_id); $vote_id = Utils::base64url_decode($vote_id);
if ($adminPollService->deleteVote($poll_id, $vote_id)) { if ($vote_id && $adminPollService->deleteVote($poll_id, $vote_id)) {
$message = new Message('success', __('adminstuds', 'Vote deleted')); $message = new Message('success', __('adminstuds', 'Vote deleted'));
} else { } else {
$message = new Message('danger', __('Error', 'Failed to delete the vote')); $message = new Message('danger', __('Error', 'Failed to delete the vote!'));
} }
} }
@ -387,6 +401,7 @@ $smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->assign('expired', strtotime($poll->end_date) < time()); $smarty->assign('expired', strtotime($poll->end_date) < time());
$smarty->assign('deletion_date', strtotime($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('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
$smarty->assign('votes', $pollService->splitVotes($votes)); $smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes)); $smarty->assign('best_choices', $pollService->computeBestChoices($votes));
$smarty->assign('comments', $comments); $smarty->assign('comments', $comments);

View File

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

View File

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

View File

@ -22,6 +22,7 @@ class Form
{ {
public $title; public $title;
public $id;
public $description; public $description;
public $admin_name; public $admin_name;
public $admin_mail; public $admin_mail;

View File

@ -0,0 +1,46 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
class Increase_pollId_size implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'Increase the size of id column in poll table';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true if the Migration should be executed
*/
function preCondition(\PDO $pdo) {
return true;
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true if the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
}

View File

@ -52,13 +52,13 @@ class RPadVotes_from_0_8 implements Migration {
} }
private function rpadVotes($pdo) { private function rpadVotes($pdo) {
$pdo->exec('UPDATE fd_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
FROM fd_vote v FROM '. Utils::table('vote') .' v
INNER JOIN INNER JOIN
(SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count (SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count
FROM fd_slot s FROM '. Utils::table('slot') .' s
GROUP BY s.poll_id GROUP BY s.poll_id
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
WHERE LENGTH(v.choices) != inn.slots_count WHERE LENGTH(v.choices) != inn.slots_count

View File

@ -47,6 +47,14 @@ class PollRepository extends AbstractRepository {
return $prepared->rowCount() > 0; return $prepared->rowCount() > 0;
} }
public function existsByAdminId($admin_poll_id) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
$prepared->execute(array($admin_poll_id));
return $prepared->rowCount() > 0;
}
function update($poll) { function update($poll) {
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=?, password_hash=?, results_publicly_visible=? WHERE id = ?'); $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 = ?');

View File

@ -284,7 +284,11 @@ class AdminPollService {
$result->slot = null; $result->slot = null;
$result->insert = 0; $result->insert = 0;
foreach ($slots as $slot) { // Sort slots before searching where to insert
$this->pollService->sortSlorts($slots);
// Search where to insert new column
foreach ($slots as $k=>$slot) {
$rowDatetime = $slot->title; $rowDatetime = $slot->title;
$moments = explode(',', $slot->moments); $moments = explode(',', $slot->moments);

View File

@ -54,6 +54,11 @@ class InputService {
return $this->returnIfNotBlank($title); return $this->returnIfNotBlank($title);
} }
public function filterId($id) {
$filtered = filter_var($id, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
return $filtered ? substr($filtered, 0, 64) : false;
}
public function filterName($name) { public function filterName($name) {
$filtered = trim($name); $filtered = trim($name);
return $this->returnIfNotBlank($filtered); return $this->returnIfNotBlank($filtered);
@ -68,6 +73,10 @@ class InputService {
return $description; return $description;
} }
public function filterMD5($control) {
return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]);
}
public function filterBoolean($boolean) { public function filterBoolean($boolean) {
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]); return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
} }

View File

@ -18,11 +18,13 @@
*/ */
namespace Framadate\Services; namespace Framadate\Services;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Form; use Framadate\Form;
use Framadate\FramaDB; use Framadate\FramaDB;
use Framadate\Utils;
use Framadate\Security\Token;
use Framadate\Repositories\RepositoryFactory; use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token;
use Framadate\Utils;
class PollService { class PollService {
@ -50,7 +52,7 @@ class PollService {
* @return \stdClass|null The found poll, or null * @return \stdClass|null The found poll, or null
*/ */
function findById($poll_id) { function findById($poll_id) {
if (preg_match('/^[\w\d]{16}$/i', $poll_id)) { if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id); return $this->pollRepository->findById($poll_id);
} }
@ -58,7 +60,7 @@ class PollService {
} }
public function findByAdminId($admin_poll_id) { public function findByAdminId($admin_poll_id) {
if (preg_match('/^[\w\d]{24}$/i', $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);
} }
@ -76,24 +78,34 @@ class PollService {
function allSlotsByPoll($poll) { function allSlotsByPoll($poll) {
$slots = $this->slotRepository->listByPollId($poll->id); $slots = $this->slotRepository->listByPollId($poll->id);
if ($poll->format == 'D') { if ($poll->format == 'D') {
uasort($slots, function ($a, $b) { $this->sortSlorts($slots);
return $a->title > $b->title;
});
} }
return $slots; return $slots;
} }
public function updateVote($poll_id, $vote_id, $name, $choices) { public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash) {
$choices = implode($choices); $poll = $this->findById($poll_id);
// Check if slots are still the same
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
// Update vote
$choices = implode($choices);
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices); return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
} }
function addVote($poll_id, $name, $choices) { function addVote($poll_id, $name, $choices, $slots_hash) {
$poll = $this->findById($poll_id);
// Check if slots are still the same
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
// Check if vote already exists
if ($this->voteRepository->existsByPollIdAndName($poll_id, $name)) { if ($this->voteRepository->existsByPollIdAndName($poll_id, $name)) {
return false; throw new AlreadyExistsException();
} }
// Insert new vote
$choices = implode($choices); $choices = implode($choices);
$token = $this->random(16); $token = $this->random(16);
return $this->voteRepository->insert($poll_id, $name, $choices, $token); return $this->voteRepository->insert($poll_id, $name, $choices, $token);
@ -112,12 +124,21 @@ class PollService {
* @return string * @return string
*/ */
function createPoll(Form $form) { function createPoll(Form $form) {
// Generate poll IDs, loop while poll ID already exists // Generate poll IDs, loop while poll ID already exists
do {
$poll_id = $this->random(16); if (empty($form->id)) { // User want us to generate an id for him
} while ($this->pollRepository->existsById($poll_id)); do {
$admin_poll_id = $poll_id . $this->random(8); $poll_id = $this->random(16);
} while ($this->pollRepository->existsById($poll_id));
$admin_poll_id = $poll_id . $this->random(8);
} else { // User have choosen the poll id
$poll_id = $form->id;
do {
$admin_poll_id = $this->random(24);
} while ($this->pollRepository->existsByAdminId($admin_poll_id));
}
// Insert poll + slots // Insert poll + slots
$this->pollRepository->beginTransaction(); $this->pollRepository->beginTransaction();
@ -168,6 +189,16 @@ class PollService {
return $splitted; return $splitted;
} }
/**
* @param $slots array The slots to hash
* @return string The hash
*/
public function hashSlots($slots) {
return md5(array_reduce($slots, function($carry, $item) {
return $carry . $item->id . '@' . $item->moments . ';';
}));
}
function splitVotes($votes) { function splitVotes($votes) {
$splitted = array(); $splitted = array();
foreach ($votes as $vote) { foreach ($votes as $vote) {
@ -202,4 +233,28 @@ class PollService {
return time() + 86400; return time() + 86400;
} }
/**
* This method checks if the hash send by the user is the same as the computed hash.
*
* @param $poll /stdClass The poll
* @param $slots_hash string The hash sent by the user
* @throws ConcurrentEditionException Thrown when hashes are differents
*/
private function checkThatSlotsDidntChanged($poll, $slots_hash) {
$slots = $this->allSlotsByPoll($poll);
if ($slots_hash !== $this->hashSlots($slots)) {
throw new ConcurrentEditionException();
}
}
/**
* @return mixed
*/
public function sortSlorts(&$slots) {
uasort($slots, function ($a, $b) {
return $a->title > $b->title;
});
return $slots;
}
} }

View File

@ -0,0 +1,53 @@
<?php
namespace Framadate\Services;
class SessionService {
/**
* Get value of $key in $section, or $defaultValue
*
* @param $section
* @param $key
* @param null $defaultValue
* @return mixed
*/
public function get($section, $key, $defaultValue=null) {
assert(!empty($key));
assert(!empty($section));
$this->initSectionIfNeeded($section);
$returnValue = $defaultValue;
if (isset($_SESSION[$section][$key])) {
$returnValue = $_SESSION[$section][$key];
}
return $returnValue;
}
/**
* Set a $value for $key in $section
*
* @param $section
* @param $key
* @param $value
*/
public function set($section, $key, $value) {
assert(!empty($key));
assert(!empty($section));
$this->initSectionIfNeeded($section);
$_SESSION[$section][$key] = $value;
}
private function initSectionIfNeeded($section) {
if (!isset($_SESSION[$section])) {
$_SESSION[$section] = array();
}
}
}

View File

@ -1,88 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// Fully qualified domain name of your webserver.
// If this is unset or empty, the servername is determined automatically.
// You *have to set this* if you are running Framedate behind a reverse proxy.
// const APP_URL = '<www.mydomain.fr>';
// Application name
const NOMAPPLICATION = 'Développement OPZ';
// Database administrator email
const ADRESSEMAILADMIN = 'framadate-dev@olivierperez.fr';
// Email for automatic responses (you should set it to "no-reply")
const ADRESSEMAILREPONSEAUTO = 'no-reply@olivierperez.fr';
// Database user
const DB_USER= 'dev_framadate';
// Database password
const DB_PASSWORD = 'dev_framadate';
// Database server name, leave empty to use a socket
const DB_CONNECTION_STRING = 'mysql:host=localhost;dbname=framadate_dev;port=3306';
// Name of the table that store migration script already executed
const MIGRATION_TABLE = 'framadate_migration';
// Table name prefix
const TABLENAME_PREFIX = 'fd_';
// Default Language using POSIX variant of BC P47 standard (choose in $ALLOWED_LANGUAGES)
const DEFAULT_LANGUAGE = 'fr';
// List of supported languages, fake constant as arrays can be used as constants only in PHP >=5.6
$ALLOWED_LANGUAGES = [
'fr' => 'Français',
'en' => 'English',
'es' => 'Español',
'de' => 'Deutsch',
'it' => 'Italiano',
];
// Nom et emplacement du fichier image contenant le titre
const IMAGE_TITRE = 'images/logo-framadate.png';
// Clean URLs, boolean
const URL_PROPRE = false;
// Use REMOTE_USER data provided by web server
const USE_REMOTE_USER = true;
// Path to the log file
const LOG_FILE = 'admin/stdout.log';
// Days (after expiration date) before purge a poll
const PURGE_DELAY = 60;
// Config
$config = [
/* general config */
'use_smtp' => false, // use email for polls creation/modification/responses notification
/* home */
'show_what_is_that' => true, // display "how to use" section
'show_the_software' => true, // display technical information about the software
'show_cultivate_your_garden' => true, // display "developpement and administration" information
/* create_classic_poll.php / create_date_poll.php */
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
/* create_classic_poll.php */
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
];

View File

@ -53,6 +53,7 @@ const DEFAULT_LANGUAGE = 'fr';
$ALLOWED_LANGUAGES = [ $ALLOWED_LANGUAGES = [
'fr' => 'Français', 'fr' => 'Français',
'en' => 'English', 'en' => 'English',
'oc' => 'Occitan',
'es' => 'Español', 'es' => 'Español',
'de' => 'Deutsch', 'de' => 'Deutsch',
'it' => 'Italiano', 'it' => 'Italiano',
@ -86,4 +87,3 @@ $config = [
/* create_classic_poll.php */ /* create_classic_poll.php */
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll. 'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
]; ];

View File

@ -18,14 +18,17 @@
*/ */
// FRAMADATE version // FRAMADATE version
const VERSION = '0.9'; const VERSION = '1.0';
// Regex // Regex
const POLL_REGEX = '/^[a-z0-9]+$/i'; const POLL_REGEX = '/^[a-z0-9-]*$/i';
const ADMIN_POLL_REGEX = '/^[a-z0-9]{24}$/i';
const CHOICE_REGEX = '/^[012]$/'; const CHOICE_REGEX = '/^[012]$/';
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i'; const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i';
const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i'; const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i';
const EDITABLE_CHOICE_REGEX = '/^[0-2]$/'; const EDITABLE_CHOICE_REGEX = '/^[0-2]$/';
const BASE64_REGEX = '/^[A-Za-z0-9]+$/';
const MD5_REGEX = '/^[A-Fa-f0-9]{32}$/';
// CSRF (300s = 5min) // CSRF (300s = 5min)
const TOKEN_TIME = 300; const TOKEN_TIME = 300;

View File

@ -18,10 +18,10 @@
*/ */
use Framadate\Form; use Framadate\Form;
use Framadate\Services\InputService; use Framadate\Repositories\RepositoryFactory;
use Framadate\Editable;
use Framadate\Utils;
use Framadate\Security\PasswordHasher; use Framadate\Security\PasswordHasher;
use Framadate\Services\InputService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php'; include_once __DIR__ . '/app/inc/init.php';
@ -32,6 +32,7 @@ const GO_TO_STEP_2 = 'gotostep2';
/*----------*/ /*----------*/
$inputService = new InputService(); $inputService = new InputService();
$pollRepository = RepositoryFactory::pollRepository();
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */
@ -55,6 +56,7 @@ if (isset($_GET['type']) && $_GET['type'] == 'date' ||
$goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(date|classic)$/']]); $goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(date|classic)$/']]);
if ($goToStep2) { if ($goToStep2) {
$title = $inputService->filterTitle($_POST['title']); $title = $inputService->filterTitle($_POST['title']);
$id = $inputService->filterId($_POST['id']);
$name = $inputService->filterName($_POST['name']); $name = $inputService->filterName($_POST['name']);
$mail = $inputService->filterMail($_POST['mail']); $mail = $inputService->filterMail($_POST['mail']);
$description = $inputService->filterDescription($_POST['description']); $description = $inputService->filterDescription($_POST['description']);
@ -76,6 +78,7 @@ if ($goToStep2) {
$error_on_password_repeat = false; $error_on_password_repeat = false;
$_SESSION['form']->title = $title; $_SESSION['form']->title = $title;
$_SESSION['form']->id = $id;
$_SESSION['form']->admin_name = $name; $_SESSION['form']->admin_name = $name;
$_SESSION['form']->admin_mail = $mail; $_SESSION['form']->admin_mail = $mail;
$_SESSION['form']->description = $description; $_SESSION['form']->description = $description;
@ -97,6 +100,13 @@ if ($goToStep2) {
$error_on_title = true; $error_on_title = true;
} }
if ($id === false) {
$error_on_id = true;
} else if ($pollRepository->existsById($id)) {
$error_on_id = true;
$error_on_id_msg = __('Error', 'Poll id already used');
}
if ($name !== $_POST['name']) { if ($name !== $_POST['name']) {
$error_on_name = true; $error_on_name = true;
} }
@ -120,8 +130,9 @@ if ($goToStep2) {
} }
} }
if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name if ($title && $name && $email_OK && !$error_on_title && !$error_on_id && !$error_on_description && !$error_on_name
&& !$error_on_password && !$error_on_password_repeat) { && !$error_on_password && !$error_on_password_repeat
) {
// If no errors, we hash the password if needed // If no errors, we hash the password if needed
if ($_SESSION['form']->use_password) { if ($_SESSION['form']->use_password) {
@ -157,6 +168,11 @@ $errors = array(
'aria' => '', 'aria' => '',
'class' => '' 'class' => ''
), ),
'id' => array(
'msg' => '',
'aria' => '',
'class' => ''
),
'description' => array( 'description' => array(
'msg' => '', 'msg' => '',
'aria' => '', 'aria' => '',
@ -195,6 +211,12 @@ if (!empty($_POST[GO_TO_STEP_2])) {
$errors['title']['msg'] = __('Error', 'Something is wrong with the format'); $errors['title']['msg'] = __('Error', 'Something is wrong with the format');
} }
if ($error_on_id) {
$errors['id']['aria'] = 'aria-describeby="poll_comment_error" ';
$errors['id']['class'] = ' has-error';
$errors['id']['msg'] = isset($error_on_id_msg) ? $error_on_id_msg : __('Error', 'Something is wrong with the format');
}
if ($error_on_description) { if ($error_on_description) {
$errors['description']['aria'] = 'aria-describeby="poll_comment_error" '; $errors['description']['aria'] = 'aria-describeby="poll_comment_error" ';
$errors['description']['class'] = ' has-error'; $errors['description']['class'] = ' has-error';
@ -243,6 +265,7 @@ $smarty->assign('goToStep2', GO_TO_STEP_2);
$smarty->assign('poll_type', $poll_type); $smarty->assign('poll_type', $poll_type);
$smarty->assign('poll_title', Utils::fromPostOrDefault('title', $_SESSION['form']->title)); $smarty->assign('poll_title', Utils::fromPostOrDefault('title', $_SESSION['form']->title));
$smarty->assign('poll_id', Utils::fromPostOrDefault('id', $_SESSION['form']->id));
$smarty->assign('poll_description', Utils::fromPostOrDefault('description', $_SESSION['form']->description)); $smarty->assign('poll_description', Utils::fromPostOrDefault('description', $_SESSION['form']->description));
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $_SESSION['form']->admin_name)); $smarty->assign('poll_name', Utils::fromPostOrDefault('name', $_SESSION['form']->admin_name));
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $_SESSION['form']->admin_mail)); $smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $_SESSION['form']->admin_mail));

View File

@ -8,10 +8,10 @@
RewriteCond %{REQUEST_FILENAME} -d RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [L] RewriteRule . - [L]
RewriteRule ^([a-zA-Z0-9]{16})$ studs.php?poll=$1 [L] RewriteRule ^([a-zA-Z0-9-]+)$ studs.php?poll=$1 [L]
RewriteRule ^([a-zA-Z0-9]{16})/action/([a-zA-Z_-]+)/(.+)$ studs.php?poll=$1&$2=$3 RewriteRule ^([a-zA-Z0-9-]+)/action/([a-zA-Z_-]+)/(.+)$ studs.php?poll=$1&$2=$3
RewriteRule ^([a-zA-Z0-9]{16})/vote/([a-zA-Z0-9]{16})$ studs.php?poll=$1&vote=$2 RewriteRule ^([a-zA-Z0-9-]+)/vote/([a-zA-Z0-9]{16})$ studs.php?poll=$1&vote=$2
RewriteRule ^([a-zA-Z0-9]{24})/admin$ adminstuds.php?poll=$1 RewriteRule ^([a-zA-Z0-9-]{24})/admin$ adminstuds.php?poll=$1
RewriteRule ^([a-zA-Z0-9]{24})/admin/vote/([a-zA-Z0-9]{16})$ adminstuds.php?poll=$1&vote=$2 RewriteRule ^([a-zA-Z0-9-]{24})/admin/vote/([a-zA-Z0-9]{16})$ adminstuds.php?poll=$1&vote=$2
RewriteRule ^([a-zA-Z0-9]{24})/admin/action/([a-zA-Z_-]+)(/(.+))?$ adminstuds.php?poll=$1&$2=$4 RewriteRule ^([a-zA-Z0-9-]{24})/admin/action/([a-zA-Z_-]+)(/(.+))?$ adminstuds.php?poll=$1&$2=$4
</IfModule> </IfModule>

View File

@ -33,6 +33,31 @@ $(document).ready(function () {
} }
}); });
/**
* Enable/Disable custom id options
*/
var $pollId = $("#poll_id");
var $customId = $("#custom_id");
// Init checkbox + input
if (($pollId.val() || $pollId.attr('value') || "").length > 0) {
$customId.attr('checked', 'checked');
$pollId.removeAttr("disabled");
}
// Listen for checkbox changes
$customId.change(function () {
if ($(this).prop("checked")) {
$pollId
.removeAttr("disabled")
.val($pollId.attr("tmp") || $pollId.attr('value'));
} else {
$pollId
.attr("disabled", "disabled")
.attr("tmp", $pollId.val())
.val("");
}
});
/** /**
* Hide/Show password options * Hide/Show password options
*/ */

View File

@ -110,11 +110,11 @@ $(document).ready(function () {
'</div>'; '</div>';
// After 11 + button is disable // After 11 + button is disable
if (hj < 10) { if (hj < 99) {
last_hour.after(new_hour_html); last_hour.after(new_hour_html);
$('#d' + di + '-h' + (hj + 1)).focus(); $('#d' + di + '-h' + (hj + 1)).focus();
$(this).prev('.remove-an-hour').removeClass('disabled'); $(this).prev('.remove-an-hour').removeClass('disabled');
if (hj == 9) { if (hj == 98) {
$(this).addClass('disabled'); $(this).addClass('disabled');
} }
} }

View File

@ -216,6 +216,9 @@
"You are in the poll creation section.": "Hier erstellen Sie die Umfrage", "You are in the poll creation section.": "Hier erstellen Sie die Umfrage",
"Required fields cannot be left blank.": "Mit * markierte Felder müssen ausgefüllt sein.", "Required fields cannot be left blank.": "Mit * markierte Felder müssen ausgefüllt sein.",
"Poll title": "Titel der Umfrage", "Poll title": "Titel der Umfrage",
"Poll id": "DE_Identifiant",
"Poll id rules": "DE_L'identifiant peut contenir des lettres, des chiffres et des tirets \"-\".",
"Poll id warning": "DE_En définissant un identifiant cela peut faciliter l'accès à ce sondage pour des personnes non désirées. Il est recommandé de le protéger par mot de passe.",
"Votes cannot be modified.": "Wertungen können nicht verändert werden.", "Votes cannot be modified.": "Wertungen können nicht verändert werden.",
"All voters can modify any vote": "Jeder Teilnehmer kann jede abgegebene Wertung ändern.", "All voters can modify any vote": "Jeder Teilnehmer kann jede abgegebene Wertung ändern.",
"Voters can modify their vote themselves": "Teilnehmer können ihre Wertungen verändern", "Voters can modify their vote themselves": "Teilnehmer können ihre Wertungen verändern",
@ -269,7 +272,9 @@
"Create the poll": "Umfrage erstellen", "Create the poll": "Umfrage erstellen",
"Your poll will be automatically archived in %d days.": "Ihre Umfrage wird automatisch in %d Tage archiviert werden.", "Your poll will be automatically archived in %d days.": "Ihre Umfrage wird automatisch in %d Tage archiviert werden.",
"You can set a closer archiving date for it.": "Sie können das Datum der Archivierung vorverlegen.", "You can set a closer archiving date for it.": "Sie können das Datum der Archivierung vorverlegen.",
"Archiving date:": "Tag der Archivierung:" "Archiving date:": "Tag der Archivierung:",
"Your poll will automatically be archived": "DE_Your poll will automatically be archived",
"after the last date of your poll.": "DE_after the last date of your poll."
}, },
"Admin": { "Admin": {
"Back to administration": "Zurück zur Verwaltung", "Back to administration": "Zurück zur Verwaltung",
@ -345,18 +350,21 @@
"The name you've chosen already exist in this poll!": "Der von Ihnen eingegebenen Name existiert schon in dieser Umfrage", "The name you've chosen already exist in this poll!": "Der von Ihnen eingegebenen Name existiert schon in dieser Umfrage",
"Enter a name and a comment!": "Geben Sie einen Namen und ein Kommentar ein!", "Enter a name and a comment!": "Geben Sie einen Namen und ein Kommentar ein!",
"Failed to insert the comment!": "Einfügen des Kommentars gescheitert!", "Failed to insert the comment!": "Einfügen des Kommentars gescheitert!",
"Failed to delete the vote!": "DE_Échec de la suppression du vote !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate ist nicht richtig installiert. Bevor Sie fortfahren, beachten Sie bitte die Angaben zum Aufsetzen der Datenbank in \"INSTALL\".", "Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate ist nicht richtig installiert. Bevor Sie fortfahren, beachten Sie bitte die Angaben zum Aufsetzen der Datenbank in \"INSTALL\".",
"Failed to save poll": "Speichern der Umfrage fehlgeschlagen", "Failed to save poll": "Speichern der Umfrage fehlgeschlagen",
"Update vote failed": "Aktualisierung der Wertung fehlgeschlagen", "Update vote failed": "Aktualisierung der Wertung fehlgeschlagen",
"Adding vote failed": "Stimmabgabe fehlgeschlagen", "Adding vote failed": "Stimmabgabe fehlgeschlagen",
"You already voted": "DE_Vous avez déjà voté",
"Poll has been updated before you vote": "DE_Le sondage a été mis à jour avant votre vote",
"Comment failed": "Abgabe des Kommentars gescheitert", "Comment failed": "Abgabe des Kommentars gescheitert",
"You can't create a poll with hidden results with the following edition option:": "Sie können mit der folgenden Editier-Option keine Umfrage mit versteckten Ergebnissen erzeugen:", "You can't create a poll with hidden results with the following edition option:": "Sie können mit der folgenden Editier-Option keine Umfrage mit versteckten Ergebnissen erzeugen:",
"Failed to delete column": "Löschen der Spalte fehlgeschlagen", "Failed to delete column": "Löschen der Spalte fehlgeschlagen",
"The column already exists": "DE_La colonne existe déjà", "The column already exists": "DE_La colonne existe déjà",
"MISSING_VALUES": "Fehlende Werte", "MISSING_VALUES": "Fehlende Werte",
"CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden", "CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden",
"Password is empty": "DE_Le mot de passe est vide.", "Password is empty": "DE_Le mot de passe est vide.",
"Passwords do not match": "DE_Les mot de passes ne correspondent pas." "Passwords do not match": "DE_Les mot de passes ne correspondent pas.",
"Poll id already used": "DE_L'identifiant est déjà utilisé"
} }
} }

View File

@ -61,7 +61,7 @@
}, },
"Homepage": { "Homepage": {
"Schedule an event": "Schedule an event", "Schedule an event": "Schedule an event",
"Make a classic poll": "Make a classic poll", "Make a classic poll": "Make a standard poll",
"Where are my polls": "Where are my polls?" "Where are my polls": "Where are my polls?"
}, },
"1st section": { "1st section": {
@ -78,28 +78,28 @@
"2nd section": { "2nd section": {
"The software": "The software", "The software": "The software",
"Framadate was initially based on ": "Framadate was initially based on ", "Framadate was initially based on ": "Framadate was initially based on ",
"a software developed by the University of Strasbourg. Today, it is devevoped by the association Framasoft.": "software developed by the University of Strasbourg. These days, it is developed by the Framasoft association.", "a software developed by the University of Strasbourg. Today, it is developed by Framasoft association.": "software developed by the University of Strasbourg. These days, it is developed by the Framasoft association.",
"This software needs javascript and cookies enabled. It is compatible with the following web browsers:": "This software needs JavaScript and cookies enabled. It is compatible with the following web browsers:", "This software needs JavaScript and cookies enabled. It is compatible with the following web browsers:": "This software needs JavaScript and cookies enabled. It is compatible with the following web browsers:",
"It is governed by the": "Framadate is licensed under the", "It is governed by the": "Framadate is licensed under the",
"CeCILL-B license": "CeCILL-B license" "CeCILL-B license": "CeCILL-B license"
}, },
"3rd section": { "3rd section": {
"Cultivate your garden": "Cultivate your garden", "Cultivate your garden": "Grow your own",
"To participate in the software development, suggest improvements or simply download it, please visit ": "To participate in the software development, suggest improvements or simply download it, please visit ", "To participate in the software development, suggest improvements or simply download it, please visit ": "To participate in the software development, suggest improvements or simply download it, please visit ",
"the development site": "the development site", "the development site": "the development site",
"If you want to install the software for your own use and thus increase your independence, we help you on:": "If you want to install the software for your own use and thus increase your independence, we can help you at:" "If you want to install the software for your own use and thus increase your independence, we help you on:": "If you want to install the software for your own use and thus increase your independence, we can help you at:"
}, },
"PollInfo": { "PollInfo": {
"Remove the poll": "Remove the poll", "Remove the poll": "Remove the poll",
"Remove all the comments": "Remove all the comments", "Remove all the comments": "Remove all comments",
"Remove all the votes": "Remove all the votes", "Remove all the votes": "Remove all votes",
"Print": "Print", "Print": "Print",
"Export to CSV": "Export to CSV", "Export to CSV": "Export to CSV",
"Title": "Title of the poll", "Title": "Title of the poll",
"Edit the title": "Edit title", "Edit the title": "Edit title",
"Save the new title": "Save the new title", "Save the new title": "Save the new title",
"Cancel the title edit": "Cancel the title edit", "Cancel the title edit": "Cancel the title edit",
"Initiator of the poll": "Initiator of the poll", "Initiator of the poll": "Creator of the poll",
"Edit the name": "Edit name", "Edit the name": "Edit name",
"Save the new name": "Save the new name", "Save the new name": "Save the new name",
"Cancel the name edit": "Cancel the name edit", "Cancel the name edit": "Cancel the name edit",
@ -216,6 +216,9 @@
"You are in the poll creation section.": "You are in the poll creation section.", "You are in the poll creation section.": "You are in the poll creation section.",
"Required fields cannot be left blank.": "Required fields cannot be left blank.", "Required fields cannot be left blank.": "Required fields cannot be left blank.",
"Poll title": "Poll title", "Poll title": "Poll title",
"Poll id": "Identifier",
"Poll id rules": "The identifier can contain letters, numbers and dashes \"-\".",
"Poll id warning": "By defining an identifier that can facilitate access to the poll for unwanted people. It is recommended to protect it with a password.",
"Votes cannot be modified.": "Votes cannot be modified", "Votes cannot be modified.": "Votes cannot be modified",
"All voters can modify any vote": "All voters can modify any vote", "All voters can modify any vote": "All voters can modify any vote",
"Voters can modify their vote themselves": "Voters can modify their vote themselves", "Voters can modify their vote themselves": "Voters can modify their vote themselves",
@ -269,7 +272,9 @@
"Create the poll": "Create the poll", "Create the poll": "Create the poll",
"Your poll will be automatically archived in %d days.": "Your poll will be automatically archived in %d days.", "Your poll will be automatically archived in %d days.": "Your poll will be automatically archived in %d days.",
"You can set a closer archiving date for it.": "You can set a specific expiry date for the poll.", "You can set a closer archiving date for it.": "You can set a specific expiry date for the poll.",
"Archiving date:": "Expiry date:" "Archiving date:": "Expiry date:",
"Your poll will automatically be archived": "Your poll will automatically be archived",
"after the last date of your poll.": "after the last date of your poll."
}, },
"Admin": { "Admin": {
"Back to administration": "Back to administration", "Back to administration": "Back to administration",
@ -345,10 +350,13 @@
"The name you've chosen already exist in this poll!": "The name you've chosen already exists in this poll!", "The name you've chosen already exist in this poll!": "The name you've chosen already exists in this poll!",
"Enter a name and a comment!": "Enter a name and a comment!", "Enter a name and a comment!": "Enter a name and a comment!",
"Failed to insert the comment!": "Failed to insert the comment!", "Failed to insert the comment!": "Failed to insert the comment!",
"Failed to delete the vote!": "Failed to delete the vote!",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate is not properly installed, please see the 'INSTALL' file for instructions on setting up the database before continuing.", "Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate is not properly installed, please see the 'INSTALL' file for instructions on setting up the database before continuing.",
"Failed to save poll": "Failed to save poll", "Failed to save poll": "Failed to save poll",
"Update vote failed": "Update vote failed", "Update vote failed": "Update vote failed",
"Adding vote failed": "Adding vote failed", "Adding vote failed": "Adding vote failed",
"You already voted": "You already voted",
"Poll has been updated before you vote": "Poll has been updated before you vote",
"Comment failed": "Comment 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 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 option: ",
"Failed to delete column": "Failed to delete column", "Failed to delete column": "Failed to delete column",
@ -356,6 +364,7 @@
"MISSING_VALUES": "Missing values", "MISSING_VALUES": "Missing values",
"CANT_CONNECT_TO_DATABASE": "Unable to connect to database", "CANT_CONNECT_TO_DATABASE": "Unable to connect to database",
"Password is empty": "Password is empty.", "Password is empty": "Password is empty.",
"Passwords do not match": "Passwords do not match." "Passwords do not match": "Passwords do not match.",
"Poll id already used": "Identifier is already used"
} }
} }

View File

@ -216,6 +216,9 @@
"You are in the poll creation section.": "Usted ha eligiendo de crear une nueva encuesta!", "You are in the poll creation section.": "Usted ha eligiendo de crear une nueva encuesta!",
"Required fields cannot be left blank.": "Gracias por completar los campos con una *.", "Required fields cannot be left blank.": "Gracias por completar los campos con una *.",
"Poll title": "ES_Titre du sondage", "Poll title": "ES_Titre du sondage",
"Poll id": "ES_Identifiant",
"Poll id rules": "ES_L'identifiant peut contenir des lettres, des chiffres et des tirets \"-\".",
"Poll id warning": "ES_En définissant un identifiant cela peut faciliter l'accès à ce sondage pour des personnes non désirées. Il est recommandé de le protéger par mot de passe.",
"Votes cannot be modified.": "ES_Aucun vote ne peut être modifié", "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", "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", "Voters can modify their vote themselves": "Los encuentados pueden cambiar su línea ellos mismos",
@ -269,7 +272,9 @@
"Create the poll": "Crear la encuesta", "Create the poll": "Crear la encuesta",
"Your poll will be automatically archived in %d days.": "ES_Votre sondage sera automatiquement archivé dans %d jours.", "Your poll will be automatically archived in %d days.": "ES_Votre sondage sera automatiquement archivé dans %d jours.",
"You can set a closer archiving date for it.": "ES_Vous pouvez décider d'une date d'archivage plus proche.", "You can set a closer archiving date for it.": "ES_Vous pouvez décider d'une date d'archivage plus proche.",
"Archiving date:": "ES_Date d'archivage :" "Archiving date:": "ES_Date d'archivage :",
"Your poll will automatically be archived": "ES_Your poll will automatically be archived",
"after the last date of your poll.": "ES_after the last date of your poll."
}, },
"Admin": { "Admin": {
"Back to administration": "ES_Retour à l'administration", "Back to administration": "ES_Retour à l'administration",
@ -345,18 +350,21 @@
"The name you've chosen already exist in this poll!": "El nombre entrado existe ya!", "The name you've chosen already exist in this poll!": "El nombre entrado existe ya!",
"Enter a name and a comment!": "Introduzca su nombre y un comentario!", "Enter a name and a comment!": "Introduzca su nombre y un comentario!",
"Failed to insert the comment!": "ES_Échec à l'insertion du commentaire !", "Failed to insert the comment!": "ES_Échec à l'insertion du commentaire !",
"Failed to delete the vote!": "ES_Échec de la suppression du vote !",
"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.", "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_Echec 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é", "Update vote failed": "ES_Mise à jour du vote échoué",
"Adding vote failed": "ES_Ajout d'un vote échoué", "Adding vote failed": "ES_Ajout d'un vote échoué",
"You already voted": "ES_Vous avez déjà voté",
"Poll has been updated before you vote": "ES_Le sondage a été mis à jour avant votre vote",
"Comment failed": "ES_Commentaire é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", "Failed to delete column": "Error al eliminar la columna",
"The column already exists": "ES_La colonne existe déjà", "The column already exists": "ES_La colonne existe déjà",
"MISSING_VALUES": "Los valores perdidos", "MISSING_VALUES": "Los valores perdidos",
"CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos", "CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos",
"Password is empty": "ES_Le mot de passe est vide.", "Password is empty": "ES_Le mot de passe est vide.",
"Passwords do not match": "ES_Les mot de passes ne correspondent pas." "Passwords do not match": "ES_Les mot de passes ne correspondent pas.",
"Poll id already used": "ES_L'identifiant est déjà utilisé"
} }
} }

View File

@ -79,7 +79,7 @@
"The software": "Le logiciel", "The software": "Le logiciel",
"Framadate was initially based on ": "Framadate est initialement basé sur ", "Framadate was initially based on ": "Framadate est initialement basé sur ",
"a software developed by the University of Strasbourg. Today, it is devevoped by the association Framasoft.": "un logiciel développé par l'Université de Strasbourg. Aujourd'hui, son développement est assuré par lassociation Framasoft.", "a software developed by the University of Strasbourg. Today, it is devevoped by the association Framasoft.": "un logiciel développé par l'Université de Strasbourg. Aujourd'hui, son développement est assuré par lassociation Framasoft.",
"This software needs javascript and cookies enabled. It is compatible with the following web browsers:": "Ce logiciel requiert lactivation du javascript et des cookies. Il est compatible avec les navigateurs web suivants :", "This software needs javascript and cookies enabled. It is compatible with the following web browsers:": "Ce logiciel requiert lactivation du JavaScript et des cookies. Il est compatible avec les navigateurs web suivants :",
"It is governed by the": "Il est régi par la", "It is governed by the": "Il est régi par la",
"CeCILL-B license": "licence CeCILL-B" "CeCILL-B license": "licence CeCILL-B"
}, },
@ -138,7 +138,7 @@
"Vote no for": "Voter « non » pour", "Vote no for": "Voter « non » pour",
"Vote yes for": "Voter « oui » pour", "Vote yes for": "Voter « oui » pour",
"Vote ifneedbe for": "Voter « Si nécessaire » pour", "Vote ifneedbe for": "Voter « Si nécessaire » pour",
"Save the choices": "Enregister les choix", "Save the choices": "Enregistrer les choix",
"Addition": "Somme", "Addition": "Somme",
"Best choice": "Meilleur choix", "Best choice": "Meilleur choix",
"Best choices": "Meilleurs choix", "Best choices": "Meilleurs choix",
@ -172,7 +172,7 @@
"POLL_LOCKED_WARNING": "L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer", "POLL_LOCKED_WARNING": "L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer",
"The poll is expired, it will be deleted soon.": "Le sondage a expiré, il sera bientôt supprimé.", "The poll is expired, it will be deleted soon.": "Le sondage a expiré, il sera bientôt supprimé.",
"Deletion date:": "Date de suppression :", "Deletion date:": "Date de suppression :",
"Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:": "Votre vote a bien été pris en compte, mais faites attention : ce sondage n'autorise l'édition de votre vote qu'avec le lien personnalisé suivant ; conservez le précieusement ! ", "Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:": "Votre vote a bien été pris en compte, mais faites attention : ce sondage n'autorise l'édition de votre vote qu'avec le lien personnalisé suivant ; conservez-le précieusement ! ",
"Update vote succeeded": "Mise à jour du vote réussi", "Update vote succeeded": "Mise à jour du vote réussi",
"Adding the vote succeeded": "Ajout du vote réussi" "Adding the vote succeeded": "Ajout du vote réussi"
}, },
@ -216,6 +216,9 @@
"You are in the poll creation section.": "Vous avez choisi de créer un nouveau sondage.", "You are in the poll creation section.": "Vous avez choisi de créer un nouveau sondage.",
"Required fields cannot be left blank.": "Merci de remplir les champs obligatoires, marqués d'une *.", "Required fields cannot be left blank.": "Merci de remplir les champs obligatoires, marqués d'une *.",
"Poll title": "Titre du sondage", "Poll title": "Titre du sondage",
"Poll id": "Identifiant",
"Poll id rules": "L'identifiant peut contenir des lettres, des chiffres et des tirets \"-\".",
"Poll id warning": "En définissant un identifiant cela peut faciliter l'accès à ce sondage pour des personnes non désirées. Il est recommandé de le protéger par mot de passe.",
"Votes cannot be modified.": "Aucun vote ne peut être modifié", "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", "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", "Voters can modify their vote themselves": "Chaque sondé peut modifier son propre vote",
@ -269,7 +272,9 @@
"Create the poll": "Créer le sondage", "Create the poll": "Créer le sondage",
"Your poll will be automatically archived in %d days.": "Votre sondage sera automatiquement archivé dans %d jours.", "Your poll will be automatically archived in %d days.": "Votre sondage sera automatiquement archivé dans %d jours.",
"You can set a closer archiving date for it.": "Vous pouvez décider d'une date d'archivage plus proche.", "You can set a closer archiving date for it.": "Vous pouvez décider d'une date d'archivage plus proche.",
"Archiving date:": "Date d'archivage :" "Archiving date:": "Date d'archivage :",
"Your poll will automatically be archived": "Votre sondage sera automatiquement archivé",
"after the last date of your poll.": "après le dernier jour de votre sondage."
}, },
"Admin": { "Admin": {
"Back to administration": "Retour à l'administration", "Back to administration": "Retour à l'administration",
@ -293,11 +298,11 @@
"Success": "Réussite", "Success": "Réussite",
"Fail": "Échec", "Fail": "Échec",
"Nothing": "Rien", "Nothing": "Rien",
"Succeeded:": "Réussit:", "Succeeded:": "Succès :",
"Failed:": "Échoué:", "Failed:": "Échec ::",
"Skipped:": "Passé:", "Skipped:": "Passé :",
"Pages:": "Pages :", "Pages:": "Pages :",
"Purged:": "Purgés :", "Purged:": "Purgés :",
"Confirm removal of the poll": "Confirmer la suppression du sondage", "Confirm removal of the poll": "Confirmer la suppression du sondage",
"polls in the database at this time": "sondages dans la base actuellement", "polls in the database at this time": "sondages dans la base actuellement",
"Purge the polls": "Purger les sondages" "Purge the polls": "Purger les sondages"
@ -351,7 +356,7 @@
"No polls found": "Aucun sondage n'a été trouvé", "No polls found": "Aucun sondage n'a été trouvé",
"There is a problem with your choices": "Il y a un problème avec vos choix", "There is a problem with your choices": "Il y a un problème avec vos choix",
"You haven't filled the first section of the poll creation.": "Vous n'avez pas renseigné la première page du sondage", "You haven't filled the first section of the poll creation.": "Vous n'avez pas renseigné la première page du sondage",
"Javascript is disabled on your browser. Its activation is required to create a poll.": "Javascript est désactivé sur votre navigateur. Son activation est requise pour la création d'un sondage.", "Javascript is disabled on your browser. Its activation is required to create a poll.": "JavaScript est désactivé sur votre navigateur. Son activation est requise pour la création d'un sondage.",
"Cookies are disabled on your browser. Theirs activation is required to create a poll.": "Les cookies sont désactivés sur votre navigateur. Leur activation est requise pour la création d'un sondage.", "Cookies are disabled on your browser. Theirs activation is required to create a poll.": "Les cookies sont désactivés sur votre navigateur. Leur activation est requise pour la création d'un sondage.",
"This poll doesn't exist !": "Ce sondage n'existe pas !", "This poll doesn't exist !": "Ce sondage n'existe pas !",
"Enter a name": "Vous n'avez pas saisi de nom !", "Enter a name": "Vous n'avez pas saisi de nom !",
@ -359,17 +364,21 @@
"The name you've chosen already exist in this poll!": "Le nom que vous avez choisi existe déjà !", "The name you've chosen already exist in this poll!": "Le nom que vous avez choisi existe déjà !",
"Enter a name and a comment!": "Merci de remplir les deux champs !", "Enter a name and a comment!": "Merci de remplir les deux champs !",
"Failed to insert the comment!": "Échec à l'insertion du commentaire !", "Failed to insert the comment!": "Échec à l'insertion du commentaire !",
"Failed to delete the vote!": "Échec de la suppression du vote !",
"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.", "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": "Echec de la sauvegarde du sondage", "Failed to save poll": "Échec de la sauvegarde du sondage",
"Update vote failed": "Mise à jour du vote échoué", "Update vote failed": "Échec de de la mise à jour du vote",
"Adding vote failed": "Ajout d'un vote échoué", "Adding vote failed": "Échec de l'ajout d'un vote",
"Comment failed": "Commentaire échoué", "You already voted": "Vous avez déjà voté",
"Poll has been updated before you vote": "Le sondage a été mis à jour avant votre vote",
"Comment failed": "Échec du commentaire",
"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", "Failed to delete column": "Échec de la suppression de colonne",
"The column already exists": "La colonne existe déjà", "The column already exists": "La colonne existe déjà",
"MISSING_VALUES": "Il manque des valeurs", "MISSING_VALUES": "Il manque des valeurs",
"CANT_CONNECT_TO_DATABASE": "Impossible de se connecter à la base de données", "CANT_CONNECT_TO_DATABASE": "Impossible de se connecter à la base de données",
"Password is empty": "Le mot de passe est vide.", "Password is empty": "Le mot de passe est vide.",
"Passwords do not match": "Les mots de passe ne correspondent pas." "Passwords do not match": "Les mots de passe ne correspondent pas.",
"Poll id already used": "L'identifiant est déjà utilisé"
} }
} }

View File

@ -216,6 +216,9 @@
"You are in the poll creation section.": "Avete scelto di creare un nuovo sondaggio.", "You are in the poll creation section.": "Avete scelto di creare un nuovo sondaggio.",
"Required fields cannot be left blank.": "Riempire i campi obbligatori, segnati con *.", "Required fields cannot be left blank.": "Riempire i campi obbligatori, segnati con *.",
"Poll title": "Titolo del sondaggio", "Poll title": "Titolo del sondaggio",
"Poll id": "IT_Identifiant",
"Poll id rules": "IT_L'identifiant peut contenir des lettres, des chiffres et des tirets \"-\".",
"Poll id warning": "IT_En définissant un identifiant cela peut faciliter l'accès à ce sondage pour des personnes non désirées. Il est recommandé de le protéger par mot de passe.",
"Votes cannot be modified.": "Nessun voto può essere modificato", "Votes cannot be modified.": "Nessun voto può essere modificato",
"All voters can modify any vote": "Tutti i votanti possono cambiare tutti i voti", "All voters can modify any vote": "Tutti i votanti possono cambiare tutti i voti",
"Voters can modify their vote themselves": "I partecipanti possono modificare il loro voto in autonomia", "Voters can modify their vote themselves": "I partecipanti possono modificare il loro voto in autonomia",
@ -269,7 +272,9 @@
"Create the poll": "Creare il sondaggio", "Create the poll": "Creare il sondaggio",
"Your poll will be automatically archived in %d days.": "Il vostro sondaggio verrà archiviata automaticamente in %d giorni.", "Your poll will be automatically archived in %d days.": "Il vostro sondaggio verrà archiviata automaticamente in %d giorni.",
"You can set a closer archiving date for it.": "Si può decidere su una data più vicina di archiviazione.", "You can set a closer archiving date for it.": "Si può decidere su una data più vicina di archiviazione.",
"Archiving date:": "Archivio Data:" "Archiving date:": "Archivio Data:",
"Your poll will automatically be archived": "IT_Your poll will automatically be archived",
"after the last date of your poll.": "IT_after the last date of your poll."
}, },
"Admin": { "Admin": {
"Back to administration": "Ritorna all'amministrazione", "Back to administration": "Ritorna all'amministrazione",
@ -345,15 +350,19 @@
"The name you've chosen already exist in this poll!": "Il nome che avete scelto esiste già !", "The name you've chosen already exist in this poll!": "Il nome che avete scelto esiste già !",
"Enter a name and a comment!": "Inserire un nome e un commento!", "Enter a name and a comment!": "Inserire un nome e un commento!",
"Failed to insert the comment!": "Errore nell'inserimento del commento !", "Failed to insert the comment!": "Errore nell'inserimento del commento !",
"Failed to delete the vote!": "IT_Échec de la suppression du vote !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate non è installato correttamente, leggete la cartella INSTALL per configurare il database prima di continuare.", "Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate non è installato correttamente, leggete la cartella INSTALL per configurare il database prima di continuare.",
"Failed to save poll": "Errore nel salvataggio del sondaggio", "Failed to save poll": "Errore nel salvataggio del sondaggio",
"Update vote failed": "Aggiornamento del voto fallito", "Update vote failed": "Aggiornamento del voto fallito",
"Adding vote failed": "Aggiunta del voto fallito", "Adding vote failed": "Aggiunta del voto fallito",
"You already voted": "IT_Vous avez déjà voté",
"Poll has been updated before you vote": "IT_Le sondage a été mis à jour avant votre vote",
"Comment failed": "Commento fallito", "Comment failed": "Commento fallito",
"You can't create a poll with hidden results with the following edition option:": "Non potete creare un sondaggio con i risultati nascosti con queste opzioni: ", "You can't create a poll with hidden results with the following edition option:": "Non potete creare un sondaggio con i risultati nascosti con queste opzioni: ",
"Failed to delete column": "Impossibile eliminare la colonna", "Failed to delete column": "Impossibile eliminare la colonna",
"The column already exists": "IT_La colonne existe déjà", "The column already exists": "IT_La colonne existe déjà",
"MISSING_VALUES": "Valori mancanti", "MISSING_VALUES": "Valori mancanti",
"CANT_CONNECT_TO_DATABASE": "Impossibile connettersi al database" "CANT_CONNECT_TO_DATABASE": "Impossibile connettersi al database",
"Poll id already used": "IT_L'identifiant est déjà utilisé"
} }
} }

362
locale/oc.json Normal file
View File

@ -0,0 +1,362 @@
{
"Generic": {
"Make your polls": "Organizar de rencontres simplament, liurament",
"Home": "Acuèlh",
"Poll": "Sondage",
"Save": "Enregistrar",
"Cancel": "Anullar",
"Add": "Apondre",
"Remove": "Suprimir",
"Validate": "Validar",
"Edit": "Modificar",
"Next": "Contunhar",
"Back": "Tornar",
"Close": "Tampar",
"Your name": "Vòstre nom",
"Your email address": "Vòstre adreça",
"(in the format name@mail.com)": "(al format nom@mail.com)",
"Description": "Descripcion",
"Back to the homepage of": "Tornar a la pagina d'acuèlh de",
"days": "jors",
"months": "meses",
"Day": "Jorn",
"Time": "Ora",
"with": "amb",
"vote": "vòte",
"votes": "vòtes",
"for": "per",
"Yes": "Òc",
"Ifneedbe": "Se cal",
"No": "Non",
"Legend:": "Legenda :",
"Date": "Data",
"Classic": "Classic",
"Page generated in": "Pagina generada en",
"seconds": "segondas",
"Choice": "Causida",
"Link": "Ligam",
"Search": "Cercar",
"Creation date:": "Data de creacion :",
"ASTERISK": "*"
},
"Date": {
"dd/mm/yyyy": "jj/mm/aaaa",
"%A, den %e. %B %Y": "%A %e %B %Y",
"FULL": "%A %e %B %Y",
"SHORT": "%A %e %B %Y",
"DAY": "%a %e",
"DATE": "%Y-%m-%d",
"MONTH_YEAR": "%B %Y"
},
"Language selector": {
"Select the language": "Triar la lengar",
"Change the language": "Cambiar la lenga"
},
"Homepage": {
"Schedule an event": "Crear un sondatge especial datas",
"Make a classic poll": "Crear un sondatge classic",
"Where are my polls": "Ont son mos sondatges ?"
},
"Maintenance": {
"The application": "L'aplicacion",
"is currently under maintenance.": "es en mantenença.",
"Thank you for your understanding.": "Mercés de tornar dins un momenton."
},
"1st section": {
"What is that?": "Primièrs passes",
"Framadate is an online service for planning an appointment or make a decision quickly and easily. No registration is required.": "Framadate es un servici en linha que permet d'organizar un rendètz-vos o de prendre de decisions rapidament e simplament. Cap inscripcion es demandada.",
"Here is how it works:": "Vaquí cossí marcha :",
"Make a poll": "Creatz un sondatge",
"Define dates or subjects to choose": "Determinatz las datas o los tèmas a causir",
"Send the poll link to your friends or colleagues": "Mandatz lo ligam del sondatge a vos amisc o companhs",
"Discuss and make a decision": "Charratz e prenètz vòstra decision",
"Do you want to": "Volètz",
"view an example?": "veire un example ?"
},
"2nd section": {
"The software": "Lo logicial",
"Framadate was initially based on ": "Framadate es a sa debuta basat sus ",
"a software developed by the University of Strasbourg. Today, it is devevoped by the association Framasoft.": "un logicial desvolopat per l'Universitat d'Estrasborg. Uèi son devolopament es fach per lassociacion Framasoft.",
"This software needs javascript and cookies enabled. It is compatible with the following web browsers:": "Aqueste logicial requerís lactivacion del javascript e dels cookies. Es compatible amb los navigadors web seguents :",
"It is governed by the": "Es regit per la",
"CeCILL-B license": "licéncia CeCILL-B"
},
"3rd section": {
"Cultivate your garden": "Cultivatz vòstre òrt",
"To participate in the software development, suggest improvements or simply download it, please visit ": "Per participar al devolopament del logicial, prepausar de milhoraments o simplament lo telecargar, anatz sus ",
"the development site": "lo siti de devolopament",
"If you want to install the software for your own use and thus increase your independence, we help you on:": "Se volètz installar aqueste logicial per la vòstra pròpia utilizacion e aital aver mai d'automonia, nos podètz ajudar sus :"
},
"PollInfo": {
"Remove the poll": "Suprimir lo sondatge",
"Remove all the comments": "Suprimir tots los comentaris",
"Remove all the votes": "Suprimir tots los vòtes",
"Print": "Imprimir",
"Export to CSV": "Exportar en CSV",
"Title": "Títol del sondatge",
"Edit the title": "Modificar lo títol",
"Save the new title": "Enregistrar lo nòu títol",
"Cancel the title edit": "Anullar lo cambi de títol",
"Initiator of the poll": "Autor del sondatge",
"Edit the name": "Modificacion de l'autor",
"Save the new name": "Enregistrar l'autor",
"Cancel the name edit": "Anullar lo cambi d'autor",
"Email": "Adreça",
"Edit the email adress": "Modificar l'adreça",
"Save the email address": "Enregistrar l'adreça",
"Cancel the email address edit": "Anullar lo cambi d'adreça",
"Edit the description": "Modificar la descripcion",
"Save the description": "Enregistrar la descripcion",
"Cancel the description edit": "Anullar lo cambi de descripcion",
"Public link of the poll": "Ligam public del sondatge",
"Admin link of the poll": "Ligam d'administracion del sondatge",
"Expiration date": "Data d'expiracion",
"Edit the expiration date": "Modificar la data d'expiracion",
"Save the new expiration date": "Enregistrar la data d'expiracion",
"Cancel the expiration date edit": "Anullar lo cambi de data d'expiracion",
"Poll rules": "Permissions del sondatge",
"Edit the poll rules": "Modificar las permissions del sondatge",
"Votes and comments are locked": "Los vòtes e comentaris son clavats",
"Votes and comments are open": "Los vòtes e comentaris son doberts",
"Votes are editable": "Los vòtes son modificables",
"Votes are editable solely by their owner.": "Los vòtes son solament modificables pel creator",
"Save the new rules": "Enregistrar las nòvas permissions",
"Cancel the rules edit": "Anullar lo cambi de permissions",
"Results are hidden.": "Las resultats son amagadas.",
"Results are visible.": "Las resultats son visiblas."
},
"Poll results": {
"Votes of the poll": "Vòtes del sondatge",
"Edit the line:": "Modificar la linha :",
"Remove the line:": "Suprimir la linha :",
"Vote no for": "Votar « non » per",
"Vote yes for": "Votar « òc » per",
"Vote ifneedbe for": "Votar « Se cal » per",
"Save the choices": "Enregistar las causidas",
"Addition": "Soma",
"Best choice": "Milhora causida",
"Best choices": "Milhoras causidas",
"The best choice at this time is:": "Pel moment, la causida amb lo mai de vòtes es :",
"The bests choices at this time are:": "Pel moment, las causidas amb lo mai de vòtes son :",
"Scroll to the left": "Far desfilar a man esquèrra",
"Scroll to the right": "Far desfilar a man drecha",
"polled user": "votant",
"polled users": "votants",
"Display the chart of the results": "Afichar lo graphic de las resultats",
"Chart": "Graphic"
},
"Comments": {
"Comments of polled people": "Comentaris dels opinaires",
"Remove the comment": "Suprimir lo comentari",
"Add a comment to the poll": "Apondre un commntari al sondatge",
"Your comment": "Vòstre comentari",
"Send the comment": "Mandar lo comentari",
"anonyme": "anonim",
"Comment added": "Comentari apondut"
},
"studs": {
"If you want to vote in this poll, you have to give your name, choose the values that fit best for you and validate with the plus button at the end of the line.": "Per participar a aqueste sondatge, picatz vòstre nom, triatz totas las valors que vos agradan e validatz amb lo boton al cap de linha.",
"POLL_LOCKED_WARNING": "L'administrator a clavat aqueste sondatge. Los vòtes e comentaris son gelats, es pus mai possible de participar",
"The poll is expired, it will be deleted soon.": "Lo sondatge a expirat, serà lèu suprimit.",
"Deletion date:": "Data de supression :",
"Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:": "Vòstre vòte foguèt ben enregistrat, mas mèfi : aqueste sondatge permet l'edicion de vòstre vòte solament amb lo ligam personalizat seguent ; gardatz-lo preció ! ",
"Update vote succeeded": "Misa a jorn del vòte amb succès",
"Adding the vote succeeded": "Ajust del vòte capitat"
},
"adminstuds": {
"As poll administrator, you can change all the lines of this poll with this button": "En qualitat d'administrator podètz modificar totas las linhas d'aqueste sondatge avec aqueste boton",
"remove a column or a line with": "escafar una colomna o una linha amb",
"and add a new column with": "E se avètz doblidat de picar una causida, podètz tornar apondre una colona en clicant sus",
"Finally, you can change the informations of this poll like the title, the comments or your email address.": "Per acabar podètz tanben modificar las informacions tocant aqueste sondatge coma lo títol, los comentaris o vòstre corrièl.",
"Column's adding": "Apondi de colomna",
"You can add a new scheduling date to your poll.": "Podètz apondre una data a vòstre sondatge.",
"If you just want to add a new hour to an existant date, put the same date and choose a new hour.": "Se volètz apondre un orari a una data existenta, botatz la meteissa data e triatz un orari mai. Serà normalament integrat al sondatge existent.",
"Confirm removal of the poll": "Confirmar la supression del sondatge",
"Delete the poll": "Suprimi lo sondatge",
"Keep the poll": "Gardi lo sondatge",
"Your poll has been removed!": "Vòstre sondatge es estat suprimit !",
"Poll saved": "Sondatge salvagardat",
"Poll fully deleted": "Sondatge totalament suprimit",
"Vote added": "Vòte ajustat",
"Vote updated": "Vòte mes a jorn",
"Vote deleted": "Vòte suprimit",
"All votes deleted": "Tots los vòtes son estats suprimits",
"Back to the poll": "Tornar al sondatge",
"Add a column": "Ajustar una colomna",
"Remove the column": "Suprimir la colomna",
"Column removed": "Colomna suprimida",
"Choice added": "Causida ajustada",
"Confirm removal of all votes of the poll": "Confirmar la supression de tots los vòtes d'aqueste sondatge",
"Keep the votes": "Gardar los vòtes",
"Remove the votes": "Suprimir los vòtes",
"Confirm removal of all comments of the poll": "Confirmar la supression de tots los comentaris d'aqueste sondatge",
"Keep the comments": "Gardar los comentaris",
"Remove the comments": "Suprimir los comentaris",
"Comment deleted": "Comentari suprimit",
"All comments deleted": "Tots los comentaris son estats suprimits",
"Keep votes": "Gardar lòs vòtes",
"Keep comments": "Gardar los comentaris",
"Keep this poll": "Gardar aqueste sondatge"
},
"Step 1": {
"Poll creation (1 on 3)": "Creacion de sondatge (1 sus 3)",
"You are in the poll creation section.": "Avètz causit de crear un nòu sondatge.",
"Required fields cannot be left blank.": "Mercés de garnir tots los formularis obligatòris, marcats amb una *.",
"Poll title": "Títol del sondatge",
"Votes cannot be modified.": "Cap vòte pòt èsser modificat",
"All voters can modify any vote": "Tot lo mond pòt modificar sos vòtes",
"Voters can modify their vote themselves": "Cadun pòt modificar son pròpi vòte",
"To receive an email for each new vote": "Recebre un messatge per cada participacion",
"To receive an email for each new comment": "Recebre un messatge per cada comentari",
"Only the poll maker can see the poll's results": "Solament lo creator del sondatge pòt veire las resultats",
"Go to step 2": "Anar a l'etapa 2"
},
"Step 2": {
"Back to step 1": "Tornar a l'etapa 1",
"Go to step 3": "Anar a l'etapa 3"
},
"Step 2 date": {
"Poll dates (2 on 3)": "Causida de las datas (2 sus 3)",
"Choose the dates of your poll": "Triatz las datas de vòstre sondatge",
"To schedule an event you need to propose at least two choices (two hours for one day or two days).": "Per crear un sondatge especial datas vos cal prepausar almens doas causidas (dos oraris per la meteissa jornada o dos jorns).",
"You can add or remove additionnal days and hours with the buttons": "Podètz ajustar o suprimir de jorns e oraris suplementaris amb los botons",
"For each selected day, you can choose, or not, meeting hours (e.g.: \"8h\", \"8:30\", \"8h-10h\", \"evening\", etc.)": "Per cada jorn selectionat avètz la possibilitat de causir o non d'oras de reünion (per exemple : \"8o\", \"8o30\", \"8o-10o\", \"ser\", etc.)",
"Remove an hour": "Suprimir lo darrièr orari",
"Add an hour": "Ajustar un orari",
"Copy hours of the first day": "Reportar los oraris del premièr jorn suls autres jorns",
"Remove a day": "Suprimir lo darrièr jorn",
"Add a day": "Ajustar un jorn",
"Remove all days": "Suprimir tots los jorns",
"Remove all hours": "Suprimir tots los oraris"
},
"Step 2 classic": {
"Poll subjects (2 on 3)": "Causida dels tèmas (2 sus 3)",
"To make a generic poll you need to propose at least two choices between differents subjects.": "Per crear un sondatge classic, devètz prepausar almens doas causidas diferentas.",
"You can add or remove additional choices with the buttons": "Podètz apondre o suprimir de causidas mai amb los botons",
"It's possible to propose links or images by using": "Es possible d'inserir de ligams o d'imatge n'emplegant ",
"the Markdown syntax": "la sintaxi Markdown",
"Add a link or an image": "Apondre un ligam o un imatge",
"These fields are optional. You can add a link, an image or both.": "Aquestes camps son facultatius. Podètz apondre un ligam, un image o los dos.",
"URL of the image": "URL de l'imatge",
"Alternative text": "Tèxte alternatiu",
"Remove a choice": "Suprimir la darrièra causida",
"Add a choice": "Ajutar una causida"
},
"Step 3": {
"Back to step 2": "Tornar a l'etapa 2",
"Removal date and confirmation (3 on 3)": "Data d'expiracion e confirmacion (3 sus 3)",
"Confirm the creation of your poll": "Confirmatz la creacion de vòstre sondatge",
"List of your choices": "Lista de vòstras causidas",
"Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.": "Un còp la creacion del sondatge confirmada, seretz mandat automaticament de vòstre sondatge.",
"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.": "D'aquel temps, recebretz dos corrièls : un amb lo ligam cap a vòstre sondatge per o mandar als opinaires, l'autre amb lo ligam per la pagina d'administracion del sondatge.",
"Create the poll": "Crear lo sondatge",
"Your poll will be automatically archived in %d days.": "Vòstre sondatge serà automaticament archivat dins %d jorns.",
"You can set a closer archiving date for it.": "Podètz decidir d'una data de mesa en archiva mai prèpa.",
"Archiving date:": "Data de mesa en archiva :",
"Your poll will automatically be archived": "Vòstre sondatge sera automaticament mesa en archiva",
"after the last date of your poll.": "aprèp lo darrièr jorn del sondatge."
},
"Admin": {
"Back to administration": "Tornar a l'administracion",
"Administration": "Administracion",
"Polls": "Sondatges",
"Migration": "Migracion",
"Purge": "Purga",
"Logs": "Istoric",
"Installation": "Installacion",
"Poll ID": "ID sondatge",
"Format": "Format",
"Title": "Títol",
"Author": "Autor",
"Email": "Corrièl",
"Expiration date": "Expiracion",
"Votes": "Vòtes",
"Actions": "Accions",
"See the poll": "Veire lo sondatge",
"Change the poll": "Modificar lo sondatge",
"Deleted the poll": "Suprimir lo sondatge",
"Summary": "Resumit",
"Success": "Capidada",
"Fail": "Fracàs",
"Nothing": "Res",
"Succeeded:": "Capitat:",
"Failed:": "Fracassat:",
"Skipped:": "Passat:",
"Pages:": "Paginas :",
"Purged:": "Purgats :",
"Confirm removal of the poll": "Confirmar la supression del sondatge",
"polls in the database at this time": "sondatges dins la banca de donadas actualament",
"Purge the polls": "Purgar los sondatges"
},
"FindPolls": {
"Here are your polls": "Vaquí tos sondatges",
"Send me my polls": "Mandar mos sondatges",
"Polls sent": "Sondatges mandats"
},
"Mail": {
"Poll's participation: %s": "Participacion al sondatge : %s",
"Notification of poll: %s": "Notificacion d'un sondatge : %s",
"filled a vote.\nYou can find your poll at the link": "ven de votar.<br/>Podètz tornar a vòstre sondatge amb lo ligam seguent",
"updated a vote.\nYou can find your poll at the link": "ven de metre a jorn un vòte.<br/>Podètz tornar a vòstre sondatge amb lo ligam seguent",
"wrote a comment.\nYou can find your poll at the link": "ven de redigir un comentari.<br/>Podètz tornar a vòstre sondatge amb lo ligam seguent",
"Someone just change your poll available at the following link %s.": "Qualqu'un ven de modificar vòstre sondatge accessible amb lo ligam seguent <a href=\"%1$s\">%1$s</a>.",
"Someone just delete your poll %s.": "Qualqu'un ven de suprimir vòstre sondatge \"%s\".",
"Thanks for your trust.": "Mercé per la vòstre confiança.",
"FOOTER": "« La rota es longa, mai lo camin es liure… »<br/>Framasoft solament viu amb vòstres dons (qu'òm pòt tirar de vòstres impòstes).<br/> Mercé d'avança pel vòstre sosten http://soutenir.framasoft.org.",
"[ADMINISTRATOR] New settings for your poll": "[ADMINISTRATOR] Cambi de configuracion del sondatge",
"You have changed the settings of your poll. \nYou can modify this poll with this link": "Avètz modificat la configuracion del sondatge.<br/>Podètz modificar aqueste sondatge amb lo ligam seguent",
"This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.": "Aiçò es lo messatge que serà mandat als opinaires.<br/>Podètz ara trasmetre aqueste messatge a totas las personas susceptiblas de participar al vòte.",
"hast just created a poll called": " ven de crear un sondatge titolat ",
"Thanks for filling the poll at the link above": "Mercés de ben voler participar al sondatge a l'adreça seguenta",
"This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above": "Aqueste messatge cal PAS èsser difusit alx opinaires. Es reservat a l'autor del sondatge.<br/><br/>Podètz modificar aqueste sondatge amb lo ligam seguent ",
"Author's message": "Reservat a l'autor",
"For sending to the polled users": "Per difusion als opinaires"
},
"Installation": {
"AppMail": "Adreça de corrièl de l'aplicacion",
"AppName": "Nom de l'aplicacion",
"CleanUrl": "URL clars",
"Database": "Banca de donadas",
"DbConnectionString": "Cadena de connexion",
"DbPassword": "Senhal",
"DbPrefix": "Prefixe",
"DbUser": "Utilizaire",
"DefaultLanguage": "Lenga per defaut",
"General": "General",
"Install": "Installar",
"MigrationTable": "Taula de migracion",
"ResponseMail": "Adreça de responsa"
},
"Error": {
"Error!": "Error !",
"Enter a title": "Cal picar un títol !",
"Something is going wrong...": "I a quicòm que truca...",
"Something is wrong with the format": "I a quicòm que truca amb lo format.",
"Enter an email address": "Cal picar una adreça de messatjariá !",
"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.": "L'adreça picada es pas corrècta ! Cal una adreça valida (per exemple r.stallman@outlock.com) per recebre lo ligam cap al sondatge.",
"No polls found": "Cap sondatge trobat",
"There is a problem with your choices": "I a un problèma amb vòstras causidas",
"You haven't filled the first section of the poll creation.": "Avètz pas garnit la premièra pagina del sondatge",
"Javascript is disabled on your browser. Its activation is required to create a poll.": "Javascript es desactivat sus vòstre navigador. Son activacion es requesida per la creacion d'un sondatge.",
"Cookies are disabled on your browser. Theirs activation is required to create a poll.": "Los cookies son desactivats su vòstre navigador. Lor activacion es requesida per la creacion d'un sondatge.",
"This poll doesn't exist !": "Aqueste sondatge existís pas !",
"Enter a name": "Avètz pas picat de nom !",
"The name is invalid.": "Lo nom es pas valide.",
"The name you've chosen already exist in this poll!": "Lo nom qu'avètz triat existís ja !",
"Enter a name and a comment!": "Mercé de garnir los dos camps !",
"Failed to insert the comment!": "Fracàs de l'ajust del comentari !",
"Failed to delete the vote!": "Fracàs de la supression del vòte !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate es pas installat coma cal, legissètz lo fichièr per configurar la banca de donadas abans de contunhar.",
"Failed to save poll": "Fracàs del salvament del sondatge",
"Update vote failed": "Misa a jorn del vòte fracassat",
"Adding vote failed": "Ajust d'un vòte fracassat",
"You already voted": "Avètz ja votat",
"Poll has been updated before you vote": "Lo sondatge es estat mes a jorn abans vòstre vòte",
"Comment failed": "Comentari fracassat",
"You can't create a poll with hidden results with the following edition option:": "Podètz pas crear de sondatges amb resultats amagadas amb las opcions d'edicion seguentas : ",
"Failed to delete column": "Fracàs de la supression de colomna",
"The column already exists": "La colomna existís ja",
"MISSING_VALUES": "Mancan de valors",
"CANT_CONNECT_TO_DATABASE": "Impossible de se connectar a la banca de donadas"
}
}

View File

@ -22,12 +22,19 @@ use Framadate\Services\InputService;
use Framadate\Services\MailService; use Framadate\Services\MailService;
use Framadate\Services\NotificationService; use Framadate\Services\NotificationService;
use Framadate\Services\SecurityService; use Framadate\Services\SecurityService;
use Framadate\Services\SessionService;
use Framadate\Message; use Framadate\Message;
use Framadate\Utils; use Framadate\Utils;
use Framadate\Editable; use Framadate\Editable;
include_once __DIR__ . '/app/inc/init.php'; include_once __DIR__ . '/app/inc/init.php';
/* Constantes */
/* ---------- */
const USER_REMEMBER_VOTES_KEY = 'UserVotes';
/* Variables */ /* Variables */
/* --------- */ /* --------- */
@ -40,7 +47,6 @@ $resultPubliclyVisible = true;
$slots = array(); $slots = array();
$votes = array(); $votes = array();
$comments = array(); $comments = array();
$editedVoteUniqueId = null;
/* Services */ /* Services */
/*----------*/ /*----------*/
@ -51,6 +57,7 @@ $inputService = new InputService();
$mailService = new MailService($config['use_smtp']); $mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService); $notificationService = new NotificationService($mailService);
$securityService = new SecurityService(); $securityService = new SecurityService();
$sessionService = new SessionService();
/* PAGE */ /* PAGE */
@ -58,9 +65,7 @@ $securityService = new SecurityService();
if (!empty($_GET['poll'])) { if (!empty($_GET['poll'])) {
$poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); $poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if (strlen($poll_id) === 16) { $poll = $pollService->findById($poll_id);
$poll = $pollService->findById($poll_id);
}
} }
if (!$poll) { if (!$poll) {
@ -69,6 +74,8 @@ if (!$poll) {
exit; exit;
} }
$editedVoteUniqueId = $sessionService->get(USER_REMEMBER_VOTES_KEY, $poll_id, '');
// ------------------------------- // -------------------------------
// Password verification // Password verification
// ------------------------------- // -------------------------------
@ -127,8 +134,9 @@ if ($accessGranted) {
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices); $result = $pollService->updateVote($poll_id, $editedVote, $name, $choices);
if ($result) { if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) { if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]); $editedVoteUniqueId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId); $sessionService->set(USER_REMEMBER_VOTES_KEY, $poll_id, $editedVoteUniqueId);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
$message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote);
} else { } else {
$message = new Message('success', __('studs', 'Update vote succeeded')); $message = new Message('success', __('studs', 'Update vote succeeded'));
@ -154,7 +162,9 @@ if ($accessGranted) {
$result = $pollService->addVote($poll_id, $name, $choices); $result = $pollService->addVote($poll_id, $name, $choices);
if ($result) { if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) { if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId); $editedVoteUniqueId = $result->uniqId;
$sessionService->set(USER_REMEMBER_VOTES_KEY, $poll_id, $editedVoteUniqueId);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
$message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote); $message = new Message('success', __('studs', 'Your vote has been registered successfully, but be careful: regarding this poll options, you need to keep this personal link to edit your own vote:'), $urlEditVote);
} else { } else {
$message = new Message('success', __('studs', 'Adding the vote succeeded')); $message = new Message('success', __('studs', 'Adding the vote succeeded'));
@ -168,7 +178,7 @@ if ($accessGranted) {
} }
// Retrieve data // Retrieve data
if ($resultPubliclyVisible) { if ($resultPubliclyVisible || $accessGranted) {
$slots = $pollService->allSlotsByPoll($poll); $slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id); $votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id); $comments = $pollService->allCommentsByPollId($poll_id);

View File

@ -16,7 +16,8 @@
{__('Step 1', 'Required fields cannot be left blank.')} {__('Step 1', 'Required fields cannot be left blank.')}
</p> </p>
</div> </div>
<div class="form-group '.$errors['title']['class'].'">
<div class="form-group {$errors['title']['class']}">
<label for="poll_title" class="col-sm-4 control-label">{__('Step 1', 'Poll title')} *</label> <label for="poll_title" class="col-sm-4 control-label">{__('Step 1', 'Poll title')} *</label>
<div class="col-sm-8"> <div class="col-sm-8">
@ -32,7 +33,31 @@
</div> </div>
{/if} {/if}
<div class="form-group '.$errors['description']['class'].'"> <div class="form-group {$errors['id']['class']}">
<label for="poll_id" class="col-sm-4 control-label">{__('Step 1', 'Poll id')}</label>
<div class="col-sm-8">
<div class="input-group">
<span class="input-group-addon">
<input id="custom_id" type="checkbox"/>
</span>
<input id="poll_id" type="text" name="id" class="form-control" {$errors['id']['aria']}
value="{$poll_id}" aria-describedBy="pollIdDesc" disabled="disabled" maxlength="64"
pattern="[A-Za-z0-9-]+"/>
</div>
<span id="pollIdDesc" class="help-block">{__('Step 1', 'Poll id rules')}</span>
<span class="help-block text-warning">{__('Step 1', 'Poll id warning')}</span>
</div>
</div>
{if !empty($errors['id']['msg'])}
<div class="alert alert-danger">
<p id="poll_title_error">
{$errors['id']['msg']}
</p>
</div>
{/if}
<div class="form-group {$errors['description']['class']}">
<label for="poll_comments" class="col-sm-4 control-label">{__('Generic', 'Description')}</label> <label for="poll_comments" class="col-sm-4 control-label">{__('Generic', 'Description')}</label>
<div class="col-sm-8"> <div class="col-sm-8">

View File

@ -11,6 +11,7 @@
<div id="tableContainer" class="tableContainer"> <div id="tableContainer" class="tableContainer">
<form action="{if $admin}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST" id="poll_form"> <form action="{if $admin}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST" id="poll_form">
<input type="hidden" name="control" value="{$slots_hash}"/>
<table class="results"> <table class="results">
<caption class="sr-only">{__('Poll results', 'Votes of the poll')} {$poll->title|html}</caption> <caption class="sr-only">{__('Poll results', 'Votes of the poll')} {$poll->title|html}</caption>
<thead> <thead>
@ -36,7 +37,7 @@
<tr> <tr>
<th role="presentation"></th> <th role="presentation"></th>
{foreach $slots as $id=>$slot} {foreach $slots as $id=>$slot}
<th class="bg-info" id="C{$id}">{$slot->title|markdown}</th> <th class="bg-info" id="C{$id}" title="{$slot->title|markdown:true}">{$slot->title|markdown}</th>
{/foreach} {/foreach}
<th></th> <th></th>
</tr> </tr>
@ -102,8 +103,8 @@
{if $active && !$expired && $accessGranted && {if $active && !$expired && $accessGranted &&
( (
$poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') $poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL')
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
or $admin or $admin
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
) )
} }

View File

@ -12,6 +12,7 @@
<div id="tableContainer" class="tableContainer"> <div id="tableContainer" class="tableContainer">
<form action="{if $admin}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST" id="poll_form"> <form action="{if $admin}{poll_url id=$admin_poll_id admin=true}{else}{poll_url id=$poll_id}{/if}" method="POST" id="poll_form">
<input type="hidden" name="control" value="{$slots_hash}"/>
<table class="results"> <table class="results">
<caption class="sr-only">{__('Poll results', 'Votes of the poll')} {$poll->title|html}</caption> <caption class="sr-only">{__('Poll results', 'Votes of the poll')} {$poll->title|html}</caption>
<thead> <thead>
@ -153,8 +154,8 @@
{if $active && !$expired && $accessGranted && {if $active && !$expired && $accessGranted &&
( (
$poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') $poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL')
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
or $admin or $admin
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
) )
} }
<td class="hidden-print"> <td class="hidden-print">