Merge branch 'master' into develop

Conflicts:
	CHANGELOG.md
	htaccess.txt
	locale/it.json
	studs.php
	tpl/part/vote_table_classic.tpl
	tpl/part/vote_table_date.tpl
This commit is contained in:
Antonin 2015-11-30 21:55:56 +01:00
commit c78613481e
16 changed files with 122 additions and 71 deletions

View File

@ -10,6 +10,15 @@
- Amélioration : Les commentaires sont horodatés
- Amélioration : Auto wrapping de la description du sondage
## Version 0.9.3 (Antonin - Olivier - Nikos)
- Fix : Traduction de textes en Italien
- Fix : Empêchement de la suppression de la dernière colonne
- Fix : Possiblité de supprimer des colonnes contenant des caractères spéciaux (par exemple "&")
- Fix : Correction de l'exemple d'URL rewriting (des efforts restent à faire)
- Amélioration : (Mode chacun son vote) Possiblité d'éditer son vote directement après un vote
- Amélioration : Message plus parlant lors de la création d'une colonne
## Version 0.9.2 (Olivier)
- Fix : Completion d'un manque de contrôle sur les ID
## Version 0.9.1 (JosephK - Olivier - Antonin - Michael - Paul)
- Fix : Correction des lenteurs de défilement
- Fix : Arrêt du défilement auto à gauche qu'on clique sur un choix

View File

@ -17,6 +17,7 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Editable;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\Message;
use Framadate\Services\AdminPollService;
use Framadate\Services\InputService;
@ -227,6 +228,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if (!empty($_GET['delete_vote'])) {
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_INT);
$vote_id = Utils::base64url_decode($vote_id);
if ($adminPollService->deleteVote($poll_id, $vote_id)) {
$message = new Message('success', __('adminstuds', 'Vote deleted'));
} else {
@ -318,6 +320,7 @@ if (isset($_POST['confirm_delete_poll'])) {
if (!empty($_GET['delete_column'])) {
$column = filter_input(INPUT_GET, 'delete_column', FILTER_DEFAULT);
$column = Utils::base64url_decode($column);
if ($poll->format === 'D') {
$ex = explode('@', $column);
@ -342,30 +345,30 @@ if (!empty($_GET['delete_column'])) {
// Add a slot
// -------------------------------
if (isset($_GET['add_slot'])) {
if (isset($_GET['add_column'])) {
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$smarty->assign('format', $poll->format);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->display('add_slot.tpl');
$smarty->display('add_column.tpl');
exit;
}
if (isset($_POST['confirm_add_slot'])) {
if ($poll->format === 'D') {
$newdate = strip_tags($_POST['newdate']);
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
if (isset($_POST['confirm_add_column'])) {
try {
if ($poll->format === 'D') {
$newdate = strip_tags($_POST['newdate']);
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
$ex = explode('/', $newdate);
$result = $adminPollService->addDateSlot($poll_id, mktime(0, 0, 0, $ex[1], $ex[0], $ex[2]), $newmoment);
} else {
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
$result = $adminPollService->addClassicSlot($poll_id, $newslot);
}
$ex = explode('/', $newdate);
$adminPollService->addDateSlot($poll_id, mktime(0, 0, 0, $ex[1], $ex[0], $ex[2]), $newmoment);
} else {
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
$adminPollService->addClassicSlot($poll_id, $newslot);
}
if ($result) {
$message = new Message('success', __('adminstuds', 'Choice added'));
} else {
$message = new Message('danger', __('Error', 'Failed to add the column'));
} catch (MomentAlreadyExistsException $e) {
$message = new Message('danger', __('Error', 'The column already exists'));
}
}

View File

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

View File

@ -1,6 +1,7 @@
<?php
namespace Framadate\Services;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
@ -118,7 +119,7 @@ class AdminPollService {
$slots = $this->pollService->allSlotsByPoll($poll);
// We can't delete the last slot
if ($poll->format == 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === -1) {
if ($poll->format == 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
return false;
} elseif ($poll->format == 'A' && count($slots) === 1) {
return false;
@ -196,10 +197,10 @@ class AdminPollService {
* @param $poll_id int The ID of the poll
* @param $datetime int The datetime
* @param $new_moment string The moment's name
* @return bool true if added
* @throws MomentAlreadyExistsException When the moment to add already exists in database
*/
public function addDateSlot($poll_id, $datetime, $new_moment) {
$this->logService->log('ADD_SLOT', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
@ -207,16 +208,13 @@ class AdminPollService {
// Begin transaction
$this->connect->beginTransaction();
if ($result == null) {
// The moment already exists
return false;
} elseif ($result->slot != null) {
if ($result->slot != null) {
$slot = $result->slot;
$moments = explode(',', $slot->moments);
// Check if moment already exists (maybe not necessary)
if (in_array($new_moment, $moments)) {
return false;
throw new MomentAlreadyExistsException();
}
// Update found slot
@ -232,8 +230,6 @@ class AdminPollService {
// Commit transaction
$this->connect->commit();
return true;
}
/**
@ -244,10 +240,10 @@ class AdminPollService {
*
* @param $poll_id int The ID of the poll
* @param $title int The title
* @return bool true if added
* @throws MomentAlreadyExistsException When the moment to add already exists in database
*/
public function addClassicSlot($poll_id, $title) {
$this->logService->log('ADD_SLOT', 'id:' . $poll_id . ', title:' . $title);
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
$slots = $this->slotRepository->listByPollId($poll_id);
@ -257,7 +253,7 @@ class AdminPollService {
}, $slots);
if (in_array($title, $titles)) {
// The moment already exists
return false;
throw new MomentAlreadyExistsException();
}
@ -272,8 +268,6 @@ class AdminPollService {
// Commit transaction
$this->connect->commit();
return true;
}
/**
@ -283,34 +277,29 @@ class AdminPollService {
*
* @param $slots array All the slots of the poll
* @param $datetime int The datetime of the new slot
* @return null|\stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/
private function findInsertPosition($slots, $datetime) {
$result = new \stdClass();
$result->slot = null;
$result->insert = -1;
$i = 0;
$result->insert = 0;
foreach ($slots as $slot) {
$rowDatetime = $slot->title;
$moments = explode(',', $slot->moments);
if ($datetime == $rowDatetime) {
$i += count($moments);
// Here we have to insert at the end of a slot
$result->insert += count($moments);
$result->slot = $slot;
$result->insert = $i;
break;
} elseif ($datetime < $rowDatetime) {
// Here we have to insert a new slot
// We have to insert before this slot
break;
} else {
$i += count($moments);
$result->insert += count($moments);
}
}
$result->insert = $i;
return $result;
}

View File

@ -104,6 +104,9 @@ class Utils {
* @return string The poll's URL.
*/
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) {
// URL-Encode $action_value
$action_value = $action_value == null ? null : Utils::base64url_encode($action_value);
if (URL_PROPRE) {
if ($admin === true) {
$url = self::get_server_name() . $id . '/admin';
@ -112,8 +115,12 @@ class Utils {
}
if ($vote_id != '') {
$url .= '/vote/' . $vote_id . "#edit";
} elseif ($action != null && $action_value != null) {
$url .= '/action/' . $action . '/' . $action_value;
} elseif ($action != null) {
if ($action_value != null) {
$url .= '/action/' . $action . '/' . $action_value;
} else {
$url .= '/action/' . $action;
}
}
} else {
if ($admin === true) {
@ -123,8 +130,12 @@ class Utils {
}
if ($vote_id != '') {
$url .= '&vote=' . $vote_id . "#edit";
} elseif ($action != null && $action_value != null) {
$url .= '&' . $action . "=" . $action_value;
} elseif ($action != null) {
if ($action_value != null) {
$url .= '&' . $action . "=" . $action_value;
} else {
$url .= '&' . $action . "=";
}
}
}
@ -197,4 +208,12 @@ class Utils {
public static function fromPostOrDefault($postKey, $default = '') {
return !empty($_POST[$postKey]) ? Utils::htmlEscape($_POST[$postKey]) : $default;
}
public static function base64url_encode($input) {
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
}
public static function base64url_decode($input) {
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT));
}
}

View File

@ -49,7 +49,7 @@ function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$admin = (isset($params['admin']) && $params['admin']) ? true : false;
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : false;
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? Utils::htmlEscape($params['action_value']) : false;
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? $params['action_value'] : false;
$vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
// If filter_var fails (i.e.: hack tentative), it will return false. At least no leak is possible from this.

View File

@ -6,10 +6,12 @@
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule . - [L]
RewriteRule ^([a-zA-Z0-9]{16})$ studs.php?poll=$1
RewriteRule ^([a-zA-Z0-9]{16})$ 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]{16})/vote/([a-zA-Z0-9]{16})$ studs.php?poll=$1&vote_id=$2
RewriteRule ^([a-zA-Z0-9]{16})/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/action/([a-zA-Z_-]+)/(.+)$ adminstuds.php?poll=$1&$2=$3
</IfModule>
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
</IfModule>

View File

@ -352,6 +352,7 @@
"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:",
"Failed to delete column": "Löschen der Spalte fehlgeschlagen",
"The column already exists": "DE_La colonne existe déjà",
"MISSING_VALUES": "Fehlende Werte",
"CANT_CONNECT_TO_DATABASE": "Kann nicht mit der Datenbank verbinden",
"Password is empty": "DE_Le mot de passe est vide.",

View File

@ -352,6 +352,7 @@
"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: ",
"Failed to delete column": "Failed to delete column",
"The column already exists": "The column already exists",
"MISSING_VALUES": "Missing values",
"CANT_CONNECT_TO_DATABASE": "Unable to connect to database",
"Password is empty": "Password is empty.",

View File

@ -352,6 +352,7 @@
"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 : ",
"Failed to delete column": "Error al eliminar la columna",
"The column already exists": "ES_La colonne existe déjà",
"MISSING_VALUES": "Los valores perdidos",
"CANT_CONNECT_TO_DATABASE": "No se puede conectar a la base de datos",
"Password is empty": "ES_Le mot de passe est vide.",

View File

@ -366,6 +366,7 @@
"Comment failed": "Commentaire échoué",
"You can't create a poll with hidden results with the following edition option:": "Vous ne pouvez pas créer de sondage avec résulats cachés avec les options d'éditions suivantes : ",
"Failed to delete column": "Échec de la suppression de colonne",
"The column already exists": "La colonne existe déjà",
"MISSING_VALUES": "Il manque des valeurs",
"CANT_CONNECT_TO_DATABASE": "Impossible de se connecter à la base de données",
"Password is empty": "Le mot de passe est vide.",

View File

@ -35,7 +35,7 @@
"seconds": "secondi",
"Choice": "Scelta",
"Link": "Link",
"Search": "Ricerca",
"Search": "Cercare",
"Creation date:": "Data di creazione:",
"Caption": "Titolo",
"ASTERISK": "*"
@ -100,9 +100,9 @@
"Save the new title": "Salvare il nuovo titolo",
"Cancel the title edit": "Annullare la modifica del titolo",
"Initiator of the poll": "Autore del sondaggio",
"Edit the name": "Modifica dell'autore",
"Save the new name": "Salvare l'autore",
"Cancel the name edit": "Annulla il cambio di dell'autore",
"Edit the name": "Modifica il nome",
"Save the new name": "Salvare il nuovo nome",
"Cancel the name edit": "Annulla il cambio di nome",
"Email": "Email",
"Edit the email adress": "Modificare l'email",
"Save the email address": "Salvare l'email",
@ -146,9 +146,9 @@
"The bests choices at this time are:": "Le migliori scelte per ora sono :",
"Scroll to the left": "Spostare a sinistra",
"Scroll to the right": "Spostare a destra",
"polled user": "votante",
"polled users": "votanti",
"Display the chart of the results": "Visualizzare il grafico dei risultati",
"polled user": "Votante",
"polled users": "Votanti",
"Display the chart of the results": "Mostra il grafico dei risultati",
"Chart": "Grafico"
},
"Comments": {
@ -272,11 +272,11 @@
"Archiving date:": "Archivio Data:"
},
"Admin": {
"Back to administration": "Ritornare all'amministrazione",
"Back to administration": "Ritorna all'amministrazione",
"Administration": "Amministrazione",
"Polls": "Sondaggi",
"Migration": "Migrazione",
"Purge": "Depurazione",
"Purge": "Depurare",
"Logs": "Log",
"Installation": "Installazione",
"Poll ID": "ID del sondaggio",
@ -317,7 +317,7 @@
"Someone just change your poll available at the following link %s.": "Qualcuno ha appena cambiato il vostro sondaggio disponibile al seguente link <a href=\"%1$s\">%1$s</a>.",
"Someone just delete your poll %s.": "Qualcuno ha appena cancellato il vostro sondaggio \"%s\".",
"Thanks for your trust.": "Grazie per la vostra fiducia.",
"FOOTER": La strada è lunga, ma la via è libera… »\nFramasoft vive solo grazie alle vostre donazioni (deducibili dalle imposte).\nGrazie in anticipo per il vostro sostegno http://soutenir.framasoft.org.",
"FOOTER": La strada è lunga, ma la via è libera… »\nFramasoft vive solo grazie alle vostre donazioni (deducibili dalle imposte).\nGrazie in anticipo per il vostro sostegno http://soutenir.framasoft.org.",
"[ADMINISTRATOR] New settings for your poll": "[AMMINISTRATORE] Modifica di configurazione del sondaggio",
"You have changed the settings of your poll. \nYou can modify this poll with this link": "Avete modificato la configurazione del vostro sondaggio. \nPotete modificare questo sondaggio con le Link seguente",
"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.": "Questo è il messaggio che deve essere inviato ai partecipanti. \nPotete ora inviare questo messaggio a tutte le persone a cui volete chiedere il voto.",
@ -341,17 +341,18 @@
"Cookies are disabled on your browser. Theirs activation is required to create a poll.": "I cookies non sono permessi sul vostro browser. E' necessario permetterli per creare un sondaggio.",
"This poll doesn't exist !": "Questo sondaggio non esiste più !",
"Enter a name": "Non avete inserito il nome !",
"The name is invalid.": "Le nome non è valido.",
"The name is invalid.": "Nome non valido.",
"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!",
"Failed to insert the comment!": "Errore nell'inserimento del commento !",
"Framadate is not properly installed, please check the \"INSTALL\" to setup the database before continuing.": "Framadate non è installato correttamente, leggete la cartella INSTALL per configurare il database prima di continuare.",
"Failed to save poll": "Errore nel salvataggio del sondaggio",
"Update vote failed": "Aggiornamento del voto fallito",
"Adding vote failed": "Aggiunta del voto fallita",
"Adding vote failed": "Aggiunta del voto 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",
"The column already exists": "IT_La colonne existe déjà",
"MISSING_VALUES": "Valori mancanti",
"CANT_CONNECT_TO_DATABASE": "Impossibile connettersi al database"
}

View File

@ -40,6 +40,7 @@ $resultPubliclyVisible = true;
$slots = array();
$votes = array();
$comments = array();
$editedVoteUniqueId = null;
/* Services */
/*----------*/
@ -189,5 +190,6 @@ $smarty->assign('admin', false);
$smarty->assign('hidden', $poll->hidden);
$smarty->assign('accessGranted', $accessGranted);
$smarty->assign('resultPubliclyVisible', $resultPubliclyVisible);
$smarty->assign('editedVoteUniqueId', $editedVoteUniqueId);
$smarty->display('studs.tpl');

View File

@ -32,7 +32,7 @@
{/if}
<div class="form-group">
<button class="btn btn-default" type="submit" name="back">{__('adminstuds', 'Back to the poll')}</button>
<button type="submit" name="confirm_add_slot" class="btn btn-success">{__('adminstuds', 'Add a column')}</button>
<button type="submit" name="confirm_add_column" class="btn btn-success">{__('adminstuds', 'Add a column')}</button>
</div>
</div>
</form>

View File

@ -26,8 +26,8 @@
</td>
{/foreach}
<td>
<a href="{poll_url id=$admin_poll_id admin=true action='add_slot' action_value=true}"
class="btn btn-link btn-sm" title="{__('adminstuds', 'Add a column')} {$slot->title|html}">
<a href="{poll_url id=$admin_poll_id admin=true action='add_column'}"
class="btn btn-link btn-sm" title="{__('adminstuds', 'Add a column')}">
<i class="glyphicon glyphicon-plus text-success"></i><span class="sr-only">{__('Poll results', 'Add a column')}</span>
</a>
</td>
@ -99,7 +99,14 @@
{/foreach}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted}
{if $active && !$expired && $accessGranted &&
(
$poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL')
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
or $admin
)
}
<td class="hidden-print">
<a href="{if $admin}{poll_url id=$poll->admin_id vote_id=$vote->uniqId admin=true}{else}{poll_url id=$poll->id vote_id=$vote->uniqId}{/if}" class="btn btn-default btn-sm" title="{__('Poll results', 'Edit the line:')|html} {$vote->name|html}">
<i class="glyphicon glyphicon-pencil"></i><span class="sr-only">{__('Generic', 'Edit')}</span>

View File

@ -22,7 +22,7 @@
{foreach $slots as $slot}
{foreach $slot->moments as $id=>$moment}
<td headers="M{$slot@key} D{$headersDCount} H{$headersDCount}">
<a href="{poll_url id=$admin_poll_id admin=true action='delete_column' action_value=$slot->day|cat:'@'|cat:urlencode($moment)}"
<a href="{poll_url id=$admin_poll_id admin=true action='delete_column' action_value=$slot->day|cat:'@'|cat:$moment}"
class="btn btn-link btn-sm"
title="{__('adminstuds', 'Remove the column')} {$slot->day|date_format:$date_format.txt_short|html} - {$moment|html}">
<i class="glyphicon glyphicon-remove text-danger"></i><span class="sr-only">{__('Generic', 'Remove')}</span>
@ -32,8 +32,8 @@
{/foreach}
{/foreach}
<td>
<a href="{poll_url id=$admin_poll_id admin=true action='add_slot' action_value=true}"
class="btn btn-link btn-sm" title="{__('adminstuds', 'Add a column')} {$slot->day|html}">
<a href="{poll_url id=$admin_poll_id admin=true action='add_column'}"
class="btn btn-link btn-sm" title="{__('adminstuds', 'Add a column')}">
<i class="glyphicon glyphicon-plus text-success"></i><span class="sr-only">{__('Poll results', 'Add a column')}</span>
</a>
</td>
@ -150,7 +150,13 @@
{/foreach}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted}
{if $active && !$expired && $accessGranted &&
(
$poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL')
or ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_OWN') && $editedVoteUniqueId == $vote->uniqId)
or $admin
)
}
<td class="hidden-print">
<a href="{if $admin}{poll_url id=$poll->admin_id vote_id=$vote->uniqId admin=true}{else}{poll_url id=$poll->id vote_id=$vote->uniqId}{/if}" class="btn btn-default btn-sm" title="{__('Poll results', 'Edit the line:')|escape} {$vote->name|html}">
<i class="glyphicon glyphicon-pencil"></i><span class="sr-only">{__('Generic', 'Edit')}</span>
@ -172,7 +178,7 @@
{* Line to add a new vote *}
{if $active && $editingVoteId === 0 && !$expired && $accessGranted}
{if $active && $editingVoteId === 0 && !$expired}
<tr id="vote-form" class="hidden-print">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">