Some work on vote saving
This commit is contained in:
parent
e941cf1822
commit
80b06d65cd
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,6 +1,5 @@
|
|||||||
.htaccess
|
.htaccess
|
||||||
admin/.htaccess
|
.htpasswd
|
||||||
admin/.htpasswd
|
|
||||||
admin/logs_studs.txt
|
admin/logs_studs.txt
|
||||||
composer.lock
|
composer.lock
|
||||||
composer.phar
|
composer.phar
|
||||||
@ -8,7 +7,10 @@ framanav
|
|||||||
nav
|
nav
|
||||||
app/inc/constants.php
|
app/inc/constants.php
|
||||||
vendor
|
vendor
|
||||||
.settings/
|
|
||||||
.project
|
|
||||||
cache/
|
cache/
|
||||||
tpl_c/
|
tpl_c/
|
||||||
|
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
@ -39,19 +39,13 @@ class FramaDB
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findPollById($poll_id) {
|
function findPollById($poll_id) {
|
||||||
|
$prepared = $this->prepare('SELECT * FROM sondage WHERE sondage.poll_id = ?');
|
||||||
|
|
||||||
// Open database
|
$prepared->execute([$poll_id]);
|
||||||
if (preg_match(';^[\w\d]{16}$;i', $poll_id)) {
|
$poll = $prepared->fetch();
|
||||||
$prepared = $this->prepare('SELECT * FROM sondage WHERE sondage.poll_id = ?');
|
$prepared->closeCursor();
|
||||||
|
|
||||||
$prepared->execute([$poll_id]);
|
return $poll;
|
||||||
$poll = $prepared->fetch();
|
|
||||||
$prepared->closeCursor();
|
|
||||||
|
|
||||||
return $poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function allCommentsByPollId($poll_id) {
|
function allCommentsByPollId($poll_id) {
|
||||||
@ -72,15 +66,15 @@ class FramaDB
|
|||||||
return $prepared->fetchAll();
|
return $prepared->fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertVote($name, $poll_id, $votes) {
|
function insertVote($poll_id, $name, $choices) {
|
||||||
$prepared = $this->prepare('INSERT INTO user_studs (nom,id_sondage,reponses) VALUES (?,?,?)');
|
$prepared = $this->prepare('INSERT INTO user_studs (id_sondage,nom,reponses) VALUES (?,?,?)');
|
||||||
$prepared->execute([$name, $poll_id, $votes]);
|
$prepared->execute([$poll_id, $name, $choices]);
|
||||||
|
|
||||||
$newVote = new \stdClass();
|
$newVote = new \stdClass();
|
||||||
$newVote->id_sondage = $poll_id;
|
$newVote->id_sondage = $poll_id;
|
||||||
$newVote->id_users = $this->pdo->lastInsertId();
|
$newVote->id_users = $this->pdo->lastInsertId();
|
||||||
$newVote->nom = $name;
|
$newVote->nom = $name;
|
||||||
$newVote->reponse = $votes;
|
$newVote->reponse = $choices;
|
||||||
|
|
||||||
return $newVote;
|
return $newVote;
|
||||||
}
|
}
|
||||||
|
15
app/classes/Framadate/Message.php
Normal file
15
app/classes/Framadate/Message.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
namespace Framadate;
|
||||||
|
|
||||||
|
class Message {
|
||||||
|
|
||||||
|
var $type;
|
||||||
|
var $message;
|
||||||
|
|
||||||
|
function __construct($type, $message) {
|
||||||
|
$this->type = $type;
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
28
app/classes/Framadate/Services/InputService.php
Normal file
28
app/classes/Framadate/Services/InputService.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
namespace Framadate\Services;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class helps to clean all inputs from the users or external services.
|
||||||
|
*/
|
||||||
|
class InputService {
|
||||||
|
|
||||||
|
function __construct() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method filter an array calling "filter_var" on each items.
|
||||||
|
* Only items validated are added at their own indexes, the others are not returned.
|
||||||
|
*/
|
||||||
|
function filterArray($arr, $type, $options) {
|
||||||
|
$newArr = [];
|
||||||
|
|
||||||
|
foreach($arr as $id=>$item) {
|
||||||
|
$item = filter_var($item, $type, $options);
|
||||||
|
if ($item !== false) {
|
||||||
|
$newArr[$id] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $newArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
app/classes/Framadate/Services/PollService.php
Normal file
83
app/classes/Framadate/Services/PollService.php
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<?php
|
||||||
|
namespace Framadate\Services;
|
||||||
|
|
||||||
|
class PollService {
|
||||||
|
|
||||||
|
private $connect;
|
||||||
|
|
||||||
|
function __construct($connect) {
|
||||||
|
$this->connect = $connect;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findById($poll_id) {
|
||||||
|
if (preg_match('/^[\w\d]{16}$/i', $poll_id)) {
|
||||||
|
return $this->connect->findPollById($poll_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function allCommentsByPollId($poll_id) {
|
||||||
|
return $this->connect->allCommentsByPollId($poll_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function allUserVotesByPollId($poll_id) {
|
||||||
|
return $this->connect->allUserVotesByPollId($poll_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function allSlotsByPollId($poll_id) {
|
||||||
|
return $this->connect->allSlotsByPollId($poll_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateVote($poll_id, $vote_id, $choices) {
|
||||||
|
$choices = implode($choices);
|
||||||
|
return $this->connect->updateVote($poll_id, $vote_id, $choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addVote($poll_id, $name, $choices) {
|
||||||
|
$choices = implode($choices);
|
||||||
|
return $this->connect->insertVote($poll_id, $name, $choices);
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeBestMoments($votes) {
|
||||||
|
$result = [];
|
||||||
|
foreach ($votes as $vote) {
|
||||||
|
$choices = str_split($vote->reponses);
|
||||||
|
foreach ($choices as $i=>$choice) {
|
||||||
|
if (empty($result[$i])) {
|
||||||
|
$result[$i] = 0;
|
||||||
|
}
|
||||||
|
if ($choice == 2) {
|
||||||
|
$result[$i]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitSlots($slots) {
|
||||||
|
$splitted = array();
|
||||||
|
foreach ($slots as $slot) {
|
||||||
|
$ex = explode('@', $slot->sujet);
|
||||||
|
$obj = new \stdClass();
|
||||||
|
$obj->day = $ex[0];
|
||||||
|
$obj->moments = explode(',', $ex[1]);
|
||||||
|
|
||||||
|
$splitted[] = $obj;
|
||||||
|
}
|
||||||
|
return $splitted;
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitVotes($votes) {
|
||||||
|
$splitted = array();
|
||||||
|
foreach ($votes as $vote) {
|
||||||
|
$obj = new \stdClass();
|
||||||
|
$obj->id = $vote->id_users;
|
||||||
|
$obj->name = $vote->nom;
|
||||||
|
$obj->choices = str_split($vote->reponses);
|
||||||
|
|
||||||
|
$splitted[] = $obj;
|
||||||
|
}
|
||||||
|
return $splitted;
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,6 @@ if (ini_get('date.timezone') == '') {
|
|||||||
}
|
}
|
||||||
include_once __DIR__ . '/constants.php';
|
include_once __DIR__ . '/constants.php';
|
||||||
include_once __DIR__ . '/i18n.php';
|
include_once __DIR__ . '/i18n.php';
|
||||||
include_once __DIR__ . '/studs.inc.php';
|
|
||||||
|
|
||||||
// Autoloading of dependencies with Composer
|
// Autoloading of dependencies with Composer
|
||||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
function countStuds($subjects)
|
|
||||||
{
|
|
||||||
$nb = 0;
|
|
||||||
foreach($subjects as $subject) {
|
|
||||||
$nb += substr_count($subject->sujet, ',')+1;
|
|
||||||
}
|
|
||||||
return $nb;
|
|
||||||
}
|
|
100
studs.php
100
studs.php
@ -17,59 +17,21 @@
|
|||||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||||
*/
|
*/
|
||||||
use Framadate\Services\PollService;
|
use Framadate\Services\PollService;
|
||||||
|
use Framadate\Services\InputService;
|
||||||
use Framadate\Utils;
|
use Framadate\Utils;
|
||||||
|
use Framadate\Message;
|
||||||
|
|
||||||
include_once __DIR__ . '/app/inc/init.php';
|
include_once __DIR__ . '/app/inc/init.php';
|
||||||
|
|
||||||
/* Functions */
|
/* Variables */
|
||||||
/* --------- */
|
/* --------- */
|
||||||
|
$message = null;
|
||||||
function split_slots($slots) {
|
|
||||||
$splitted = array();
|
|
||||||
foreach ($slots as $slot) {
|
|
||||||
$ex = explode('@', $slot->sujet);
|
|
||||||
$obj = new \stdClass();
|
|
||||||
$obj->day = $ex[0];
|
|
||||||
$obj->moments = explode(',', $ex[1]);
|
|
||||||
|
|
||||||
$splitted[] = $obj;
|
|
||||||
}
|
|
||||||
return $splitted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function split_votes($votes) {
|
|
||||||
$splitted = array();
|
|
||||||
foreach ($votes as $vote) {
|
|
||||||
$obj = new \stdClass();
|
|
||||||
$obj->id = $vote->id_users;
|
|
||||||
$obj->name = $vote->nom;
|
|
||||||
$obj->choices = str_split($vote->reponses);
|
|
||||||
|
|
||||||
$splitted[] = $obj;
|
|
||||||
}
|
|
||||||
return $splitted;
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeBestMoments($votes) {
|
|
||||||
$result = [];
|
|
||||||
foreach ($votes as $vote) {
|
|
||||||
$choices = str_split($vote->reponses);
|
|
||||||
foreach ($choices as $i=>$choice) {
|
|
||||||
if (empty($result[$i])) {
|
|
||||||
$result[$i] = 0;
|
|
||||||
}
|
|
||||||
if ($choice == 2) {
|
|
||||||
$result[$i]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Services */
|
/* Services */
|
||||||
/*----------*/
|
/*----------*/
|
||||||
|
|
||||||
$pollService = new PollService($connect);
|
$pollService = new PollService($connect);
|
||||||
|
$inputService = new InputService();
|
||||||
|
|
||||||
/* PAGE */
|
/* PAGE */
|
||||||
/* ---- */
|
/* ---- */
|
||||||
@ -78,7 +40,6 @@ if(!empty($_GET['poll'])) {
|
|||||||
$poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[a-z0-9]+$/']]);
|
$poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[a-z0-9]+$/']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$poll = $pollService->findById($poll_id);
|
$poll = $pollService->findById($poll_id);
|
||||||
|
|
||||||
if (!$poll) {
|
if (!$poll) {
|
||||||
@ -96,27 +57,47 @@ if (!empty($_POST['edit_vote'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Something to save (edit or add)
|
||||||
if (!empty($_POST['save'])) { // Save edition of an old vote
|
if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||||
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
|
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
|
||||||
$newChoices = [];
|
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
|
||||||
|
|
||||||
// TODO Do this verification into a Service (maybe called 'InputService')
|
if (empty($name)) {
|
||||||
foreach($_POST['choices'] as $id=>$choice) {
|
$message = new Message('danger', _('Name is incorrect.'));
|
||||||
$choice = filter_var($choice, FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
|
}
|
||||||
if ($choice !== false) {
|
if (count($choices) != count($_POST['choices'])) {
|
||||||
$newChoices[$id] = $choice;
|
$message = new Message('danger', _('There is a problem with your choices.'));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($newChoices) == count($_POST['choices'])) {
|
if ($message == null) {
|
||||||
$result = $pollService->updatePoll($poll_id, $editedVote, $newChoices);
|
// Update vote
|
||||||
|
$result = $pollService->updateVote($poll_id, $editedVote, $choices);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$message = ['type'=>'success', 'message'=>_('Update vote successfully!')];
|
$message = new Message('success', _('Update vote successfully!'));
|
||||||
} else {
|
} else {
|
||||||
$message = ['type'=>'success', 'message'=>_('Update vote successfully!')];
|
$message = new Message('danger', _('Update vote failed!'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif (isset($_POST['save'])) { // Add a new vote
|
||||||
|
$name = filter_input(INPUT_POST, 'name', FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[a-z0-9_ -]+$/i']]);
|
||||||
|
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options'=>['regexp'=>'/^[012]$/']]);
|
||||||
|
|
||||||
|
if (empty($name)) {
|
||||||
|
$message = new Message('danger', _('Name is incorrect.'));
|
||||||
|
}
|
||||||
|
if (count($choices) != count($_POST['choices'])) {
|
||||||
|
$message = new Message('danger', _('There is a problem with your choices.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($message == null) {
|
||||||
|
// Add vote
|
||||||
|
$result = $pollService->addVote($poll_id, $name, $choices);
|
||||||
|
if ($result) {
|
||||||
|
$message = new Message('success', _('Update vote successfully!'));
|
||||||
|
} else {
|
||||||
|
$message = new Message('danger', _('Update vote failed!'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (isset($_POST[''])) { // Add a new vote
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve data
|
// Retrieve data
|
||||||
@ -129,11 +110,12 @@ $comments = $pollService->allCommentsByPollId($poll_id);
|
|||||||
$smarty->assign('poll_id', $poll_id);
|
$smarty->assign('poll_id', $poll_id);
|
||||||
$smarty->assign('poll', $poll);
|
$smarty->assign('poll', $poll);
|
||||||
$smarty->assign('title', _('Poll') . ' - ' . $poll->title);
|
$smarty->assign('title', _('Poll') . ' - ' . $poll->title);
|
||||||
$smarty->assign('slots', split_slots($slots));
|
$smarty->assign('slots', $pollService->splitSlots($slots));
|
||||||
$smarty->assign('votes', split_votes($votes));
|
$smarty->assign('votes', $pollService->splitVotes($votes));
|
||||||
$smarty->assign('best_moments', computeBestMoments($votes));
|
$smarty->assign('best_moments', $pollService->computeBestMoments($votes));
|
||||||
$smarty->assign('comments', $comments);
|
$smarty->assign('comments', $comments);
|
||||||
$smarty->assign('editingVoteId', $editingVoteId);
|
$smarty->assign('editingVoteId', $editingVoteId);
|
||||||
|
$smarty->assign('message', $message);
|
||||||
|
|
||||||
//Utils::debug(computeBestMoments($votes));exit;
|
//Utils::debug(computeBestMoments($votes));exit;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
</main>
|
</main>
|
||||||
</div> <!-- .container -->
|
</div> <!-- .container -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
{* Global informations about the current poll *}
|
{* Global informations about the current poll *}
|
||||||
|
|
||||||
|
{if !empty($message)}
|
||||||
|
<div class="alert alert-{$message->type}" role="alert">{$message->message}</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-7">
|
<div class="col-md-7">
|
||||||
@ -12,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-5">
|
<div class="col-md-5">
|
||||||
<div class="btn-group pull-right">
|
<div class="btn-group pull-right">
|
||||||
<button onclick="javascript:print(); return false;" class="btn btn-default"><span class="glyphicon glyphicon-print"></span>{_('Print')}</button>
|
<button onclick="print(); return false;" class="btn btn-default"><span class="glyphicon glyphicon-print"></span>{_('Print')}</button>
|
||||||
<a href="{$SERVER_URL}export.php?poll={$poll_id}&mode=csv" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span>{_('Export to CSV')}</a>
|
<a href="{$SERVER_URL}export.php?poll={$poll_id}&mode=csv" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span>{_('Export to CSV')}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -172,7 +176,7 @@
|
|||||||
<td class="bg-info" style="padding:5px">
|
<td class="bg-info" style="padding:5px">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
|
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
|
||||||
<input type="text" id="nom" name="nom" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
|
<input type="text" id="name" name="name" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
{$i = 0}
|
{$i = 0}
|
||||||
@ -181,19 +185,19 @@
|
|||||||
<td class="bg-info" headers="M{$headersM[$i]} D{$headersD[$i]} H{$i}">
|
<td class="bg-info" headers="M{$headersM[$i]} D{$headersD[$i]} H{$i}">
|
||||||
<ul class="list-unstyled choice">
|
<ul class="list-unstyled choice">
|
||||||
<li class="yes">
|
<li class="yes">
|
||||||
<input type="radio" id="y-choice-{$i}" name="choice{$i}" value="2" />
|
<input type="radio" id="y-choice-{$i}" name="choices[{$i}]" value="2" />
|
||||||
<label class="btn btn-default btn-xs" for="y-choice-{$i}" title="{_('Vote yes for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
<label class="btn btn-default btn-xs" for="y-choice-{$i}" title="{_('Vote yes for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
||||||
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
|
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li class="ifneedbe">
|
<li class="ifneedbe">
|
||||||
<input type="radio" id="i-choice-{$i}" name="choice{$i}" value="1" />
|
<input type="radio" id="i-choice-{$i}" name="choices[{$i}]" value="1" />
|
||||||
<label class="btn btn-default btn-xs" for="i-choice-{$i}" title="{_('Vote ifneedbe for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
<label class="btn btn-default btn-xs" for="i-choice-{$i}" title="{_('Vote ifneedbe for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
||||||
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
|
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li class="no">
|
<li class="no">
|
||||||
<input type="radio" id="n-choice{$i}" name="choice{$i}" value="0" checked/>
|
<input type="radio" id="n-choice{$i}" name="choices[{$i}]" value="0" checked/>
|
||||||
<label class="btn btn-default btn-xs" for="n-choice-{$i}" title="{_('Vote no for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
<label class="btn btn-default btn-xs" for="n-choice-{$i}" title="{_('Vote no for')} {$slot->day|date_format:$date_format.txt_short} - {$moment}">
|
||||||
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
|
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
|
||||||
</label>
|
</label>
|
||||||
@ -203,7 +207,7 @@
|
|||||||
{$i = $i+1}
|
{$i = $i+1}
|
||||||
{/foreach}
|
{/foreach}
|
||||||
{/foreach}
|
{/foreach}
|
||||||
<td><button type="submit" class="btn btn-success btn-sm" name="add_vote" title="{_('Save the choices')}">{_('Save')}</button></td>
|
<td><button type="submit" class="btn btn-success btn-sm" name="save" title="{_('Save the choices')}">{_('Save')}</button></td>
|
||||||
</tr>
|
</tr>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user