Applying the password restriction in studs.

This commit is contained in:
Antonin 2015-10-28 17:30:42 +01:00
parent bb37d16dff
commit c6d6ad2393
15 changed files with 271 additions and 114 deletions

View File

@ -21,6 +21,7 @@ use Framadate\Services\PollService;
use Framadate\Services\InputService;
use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
use Framadate\Services\SecurityService;
use Framadate\Message;
use Framadate\Utils;
use Framadate\Editable;
@ -34,6 +35,7 @@ $poll_id = null;
$poll = null;
$message = null;
$result = false;
$comments = array();
/* Services */
/*----------*/
@ -43,7 +45,7 @@ $pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService);
$securityService = new SecurityService();
/* PAGE */
/* ---- */
@ -57,7 +59,9 @@ if (!empty($_POST['poll'])) {
if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
} else {
} else if ($poll && !$securityService->canAccessPoll($poll)) {
$message = new Message('error', __('Password', 'Wrong password'));
} else if ($poll) {
$name = $inputService->filterName($_POST['name']);
$comment = $inputService->filterComment($_POST['comment']);
@ -75,10 +79,9 @@ if (!$poll) {
$message = new Message('danger', __('Error', 'Comment failed'));
}
}
$comments = $pollService->allCommentsByPollId($poll_id);
}
$comments = $pollService->allCommentsByPollId($poll_id);
$smarty->error_reporting = E_ALL & ~E_NOTICE;
$smarty->assign('comments', $comments);
$comments_html = $smarty->fetch('part/comments_list.tpl');

View File

@ -371,5 +371,7 @@ $smarty->assign('editingVoteId', $editingVoteId);
$smarty->assign('message', $message);
$smarty->assign('admin', true);
$smarty->assign('hidden', false);
$smarty->assign('accessGranted', true);
$smarty->assign('resultPubliclyVisible', true);
$smarty->display('studs.tpl');

View File

@ -2,6 +2,7 @@
namespace Framadate\Services;
use Framadate\Security\Token;
use Framadate\Security\PasswordHasher;
class SecurityService {
@ -48,5 +49,46 @@ class SecurityService {
return $checked;
}
/**
* Verify if the current session allows to access given poll.
*
* @param $poll \stdClass The poll which we seek access
* @return bool true if the current session can access this poll
*/
public function canAccessPoll($poll) {
if (is_null($poll->password_hash)) {
return true;
}
$this->ensureSessionPollSecurityIsCreated();
$currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null;
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
return true;
} else {
unset($_SESSION['poll_security'][$poll->id]);
return false;
}
}
/**
* Submit to the session a poll password
*
* @param $poll \stdClass The poll which we seek access
* @param $password string the password to compare
*/
public function submitPollAccess($poll, $password) {
if (!empty($password)) {
$this->ensureSessionPollSecurityIsCreated();
$_SESSION['poll_security'][$poll->id] = $password;
}
}
private function ensureSessionPollSecurityIsCreated() {
if (!isset($_SESSION['poll_security'])) {
$_SESSION['poll_security'] = array();
}
}
}

View File

@ -488,4 +488,10 @@ table.results > tbody > tr:hover > td .glyphicon {
/* Admin */
#poll_search {
cursor: pointer;
}
/* Studs */
.password_request {
padding-top: 15px;
padding-bottom: 15px;
}

View File

@ -156,6 +156,13 @@
"anonyme": "anonym",
"Comment added": "Kommentar hinzugefügt"
},
"Password": {
"Password": "DE_Mot de passe",
"Wrong password": "DE_Mot de passe incorrect.",
"Submit access": "DE_Accèder",
"You have to provide a password to access the poll.": "DE_Vous devez donner le mot de passe pour avoir accès à ce sondage.",
"You have to provide a password so you can participate to the poll.": "DE_Vous devez donner le mot de passe pour pouvoir participer à ce sondage."
},
"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.": "Wenn Sie an dieser Umfrage teilnehmen möchten, müssen Sie ihren Namen angeben, die Auswahl treffen, die Ihnen am ehesten zusagt und mit dem Plus-Button am Ende der Zeile bestätigen.",
"POLL_LOCKED_WARNING": "Die Abstimmung wurde vom Administrator gesperrt. Bewertungen und Kommentare werden eingefroren; es ist nicht mehr möglich, teilzunehmen",

View File

@ -156,6 +156,13 @@
"anonyme": "anonyme",
"Comment added": "Comment saved"
},
"Password": {
"Password": "Password",
"Wrong password": "Wrong password",
"Submit access": "Submit access",
"You have to provide a password to access the poll.": "You have to provide a password to access the poll.",
"You have to provide a password so you can participate to the poll.": "You have to provide a password so you can participate to the poll."
},
"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.": "If you want to vote in this poll, you have to give your name, make your choice, and submit it with the plus button at the end of the line.",
"POLL_LOCKED_WARNING": "The administrator locked this poll. Votes and comments are frozen, it is no longer possible to participate",

View File

@ -156,6 +156,13 @@
"anonyme": "ES_anonyme",
"Comment added": "ES_Commentaire ajouté"
},
"Password": {
"Password": "ES_Mot de passe",
"Wrong password": "ES_Mot de passe incorrect.",
"Submit access": "ES_Accèder",
"You have to provide a password to access the poll.": "ES_Vous devez donner le mot de passe pour avoir accès à ce sondage.",
"You have to provide a password so you can participate to the poll.": "ES_Vous devez donner le mot de passe pour pouvoir participer à ce sondage."
},
"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.": "Para participar a esta encuesta, introduzca su nombre, elige todas las valores que son apriopriadas y validar su seleccion con el botón verde a la fin de línea.",
"POLL_LOCKED_WARNING": "ES_L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer",

View File

@ -156,6 +156,13 @@
"anonyme": "anonyme",
"Comment added": "Commentaire ajouté"
},
"Password": {
"Password": "Mot de passe",
"Wrong password": "Mot de passe incorrect.",
"Submit access": "Accèder",
"You have to provide a password to access the poll.": "Vous devez donner le mot de passe pour avoir accès à ce sondage.",
"You have to provide a password so you can participate to the poll.": "Vous devez donner le mot de passe pour pouvoir participer à ce sondage."
},
"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.": "Pour participer à ce sondage, veuillez entrer votre nom, choisir toutes les valeurs qui vous conviennent et valider votre choix avec le bouton en bout de ligne.",
"POLL_LOCKED_WARNING": "L'administrateur a verrouillé ce sondage. Les votes et commentaires sont gelés, il n'est plus possible de participer",

View File

@ -156,6 +156,13 @@
"anonyme": "anonimo",
"Comment added": "Commento aggiunto"
},
"Password": {
"Password": "IT_Mot de passe",
"Wrong password": "IT_Mot de passe incorrect.",
"Submit access": "IT_Accèder",
"You have to provide a password to access the poll.": "IT_Vous devez donner le mot de passe pour avoir accès à ce sondage.",
"You have to provide a password so you can participate to the poll.": "IT_Vous devez donner le mot de passe pour pouvoir participer à ce sondage."
},
"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 partecipare a questo sondaggio, è necessario inserire vostro nome, scegliere tutti i parametri che preferite e confermare la vostra scelta con il pulsante alla fine della riga.",
"POLL_LOCKED_WARNING": "L'amministratore ha bloccato questa indagine. Voti e commenti sono congelati, non è più possibile partecipare",

161
studs.php
View File

@ -21,6 +21,7 @@ use Framadate\Services\PollService;
use Framadate\Services\InputService;
use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
use Framadate\Services\SecurityService;
use Framadate\Message;
use Framadate\Utils;
use Framadate\Editable;
@ -34,6 +35,11 @@ $poll_id = null;
$poll = null;
$message = null;
$editingVoteId = 0;
$accessGranted = true;
$resultPubliclyVisible = true;
$slots = array();
$votes = array();
$comments = array();
/* Services */
/*----------*/
@ -43,6 +49,7 @@ $pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService);
$securityService = new SecurityService();
/* PAGE */
@ -62,78 +69,114 @@ if (!$poll) {
}
// -------------------------------
// A vote is going to be edited
// Password verification
// -------------------------------
if (!empty($_GET['vote'])) {
$editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
// TEMP, for testing purpose
if (isset($_GET['reset']) && $_GET['reset']) {
unset($_SESSION['poll_security']);
}
// -------------------------------
// Something to save (edit or add)
// -------------------------------
if (!is_null($poll->password_hash)) {
if (!empty($_POST['save'])) { // Save edition of an old vote
$name = $inputService->filterName($_POST['name']);
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
if (empty($editedVote)) {
$message = new Message('danger', __('Error', 'Something is going wrong...'));
}
if (count($choices) != count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
// If we came from password submission
$password = isset($_POST['password']) ? $_POST['password'] : null;
if (!empty($password)) {
$securityService->submitPollAccess($poll, $password);
}
if ($message == null) {
// Update vote
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices);
if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId);
$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 {
$message = new Message('success', __('studs', 'Update vote succeeded'));
}
$notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name);
} else {
$message = new Message('danger', __('Error', 'Update vote failed'));
}
if (!$securityService->canAccessPoll($poll)) {
$accessGranted = false;
}
} elseif (isset($_POST['save'])) { // Add a new vote
$name = $inputService->filterName($_POST['name']);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$resultPubliclyVisible = $poll->results_publicly_visible;
if ($name == null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if (count($choices) != count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
// Add vote
$result = $pollService->addVote($poll_id, $name, $choices);
if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId);
$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 {
$message = new Message('success', __('studs', 'Adding the vote succeeded'));
}
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name);
} else {
$message = new Message('danger', __('Error', 'Adding vote failed'));
}
if (!$accessGranted && !empty($password)) {
$message = new Message('danger', __('Password', 'Wrong password'));
} else if (!$accessGranted && !$resultPubliclyVisible) {
$message = new Message('danger', __('Password', 'You have to provide a password to access the poll.'));
} else if (!$accessGranted && $resultPubliclyVisible) {
$message = new Message('danger', __('Password', 'You have to provide a password so you can participate to the poll.'));
}
}
// We allow actions only if access is granted
if ($accessGranted) {
// -------------------------------
// A vote is going to be edited
// -------------------------------
if (!empty($_GET['vote'])) {
$editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
}
// -------------------------------
// Something to save (edit or add)
// -------------------------------
if (!empty($_POST['save'])) { // Save edition of an old vote
$name = $inputService->filterName($_POST['name']);
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
if (empty($editedVote)) {
$message = new Message('danger', __('Error', 'Something is going wrong...'));
}
if (count($choices) != count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
// Update vote
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices);
if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$editedVoteUniqId = filter_input(INPUT_POST, 'edited_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$urlEditVote = Utils::getUrlSondage($poll_id, false, $editedVoteUniqId);
$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 {
$message = new Message('success', __('studs', 'Update vote succeeded'));
}
$notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name);
} else {
$message = new Message('danger', __('Error', 'Update vote failed'));
}
}
} elseif (isset($_POST['save'])) { // Add a new vote
$name = $inputService->filterName($_POST['name']);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
if ($name == null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if (count($choices) != count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
// Add vote
$result = $pollService->addVote($poll_id, $name, $choices);
if ($result) {
if ($poll->editable == Editable::EDITABLE_BY_OWN) {
$urlEditVote = Utils::getUrlSondage($poll_id, false, $result->uniqId);
$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 {
$message = new Message('success', __('studs', 'Adding the vote succeeded'));
}
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name);
} else {
$message = new Message('danger', __('Error', 'Adding vote failed'));
}
}
}
}
// Retrieve data
$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
if ($resultPubliclyVisible) {
$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
}
// Assign data to template
$smarty->assign('poll_id', $poll_id);
@ -149,5 +192,7 @@ $smarty->assign('editingVoteId', $editingVoteId);
$smarty->assign('message', $message);
$smarty->assign('admin', false);
$smarty->assign('hidden', $poll->hidden);
$smarty->assign('accessGranted', $accessGranted);
$smarty->assign('resultPubliclyVisible', $resultPubliclyVisible);
$smarty->display('studs.tpl');

View File

@ -3,12 +3,11 @@
{* Comment list *}
{include 'part/comments_list.tpl'}
<form action="action/add_comment.php" method="POST" id="comment_form">
{* Add comment form *}
{if $active && !$expired && $accessGranted}
<form action="action/add_comment.php" method="POST" id="comment_form">
<input type="hidden" name="poll" value="{$poll_id}"/>
{* Add comment form *}
{if $active && !$expired}
<input type="hidden" name="poll" value="{$poll_id}"/>
<div class="hidden-print jumbotron">
<div class="col-md-6 col-md-offset-3">
<fieldset id="add-comment"><legend>{__('Comments', 'Add a comment to the poll')}</legend>
@ -21,11 +20,11 @@
<textarea name="comment" id="comment" class="form-control" rows="2" cols="40"></textarea>
</div>
<div class="pull-right">
<input type="submit" name="add_comment" value="{__('Comments', 'Send the comment')}" class="btn btn-success">
<input type="submit" id="add_comment" name="add_comment" value="{__('Comments', 'Send the comment')}" class="btn btn-success">
</div>
</fieldset>
</div>
<div class="clearfix"></div>
</div>
{/if}
</form>
</form>
{/if}

View File

@ -0,0 +1,18 @@
{if !$expired && ($active || $resultPubliclyVisible)}
<hr role="presentation" id="password_request" class="hidden-print"/>
<div class="panel panel-danger password_request alert-danger">
<div class="col-md-6 col-md-offset-3">
<form action="" method="POST" class="form-inline">
<input type="hidden" name="poll" value="{$poll_id}"/>
<div class="form-group">
<label for="password" class="control-label">{__('Password', 'Password')}</label>
<input type="password" name="password" id="password" class="form-control" />
<input type="submit" value="{__('Password', 'Submit access')}" class="btn btn-success">
</div>
</form>
</div>
<div class="clearfix"></div>
</div>
{/if}

View File

@ -97,7 +97,7 @@
{/foreach}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted}
<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>
@ -119,7 +119,7 @@
{* Line to add a new vote *}
{if $active && $editingVoteId === 0 && !$expired}
{if $active && $editingVoteId === 0 && !$expired && $accessGranted}
<tr id="vote-form" class="hidden-print">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">

View File

@ -148,7 +148,7 @@
{/foreach}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin)}
{if $active && !$expired && ($poll->editable == constant('Framadate\Editable::EDITABLE_BY_ALL') or $admin) && $accessGranted}
<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>
@ -170,7 +170,7 @@
{* Line to add a new vote *}
{if $active && $editingVoteId === 0 && !$expired}
{if $active && $editingVoteId === 0 && !$expired && $accessGranted}
<tr id="vote-form" class="hidden-print">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">

View File

@ -12,6 +12,7 @@
{block name=main}
{* Messages *}
<div id="message-container">
{if !empty($message)}
<div class="alert alert-dismissible alert-{$message->type|html} hidden-print" role="alert">{$message->message|html}{if $message->link != null}<br/><a href="{$message->link}">{$message->link}</a>{/if}<button type="button" class="close" data-dismiss="alert" aria-label="{__('Generic', 'CLose')}"><span aria-hidden="true">&times;</span></button></div>
@ -20,49 +21,55 @@
<div id="nameErrorMessage" class="hidden alert alert-dismissible alert-danger hidden-print" role="alert">{__('Error', 'The name is invalid.')}<button type="button" class="close" data-dismiss="alert" aria-label="{__('Generic', 'CLose')}"><span aria-hidden="true">&times;</span></button></div>
<div id="genericErrorTemplate" class="hidden alert alert-dismissible alert-danger hidden-print" role="alert"><span class="contents"></span><button type="button" class="close" data-dismiss="alert" aria-label="{__('Generic', 'CLose')}"><span aria-hidden="true">&times;</span></button></div>
{if !$accessGranted && !$resultPubliclyVisible}
{* Global informations about the current poll *}
{include 'part/password_request.tpl' active=$poll->active}
{include 'part/poll_info.tpl' admin=$admin}
{* Information about voting *}
{if $expired}
<div class="alert alert-danger">
<p>{__('studs', 'The poll is expired, it will be deleted soon.')}</p>
<p>{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}</p>
</div>
{else}
{if $admin}
{include 'part/poll_hint_admin.tpl'}
{else}
{include 'part/poll_hint.tpl' active=$poll->active}
{* Global informations about the current poll *}
{include 'part/poll_info.tpl' admin=$admin}
{* Information about voting *}
{if $expired}
<div class="alert alert-danger">
<p>{__('studs', 'The poll is expired, it will be deleted soon.')}</p>
<p>{__('studs', 'Deletion date:')} {$deletion_date|date_format:$date_format['txt_short']|html}</p>
</div>
{else}
{if $admin}
{include 'part/poll_hint_admin.tpl'}
{else}
{include 'part/poll_hint.tpl' active=$poll->active}
{/if}
{/if}
{* Scroll left and right *}
<div class="hidden row scroll-buttons" aria-hidden="true">
<div class="btn-group pull-right">
<button class="btn btn-sm btn-link scroll-left" title="{__('Poll results', 'Scroll to the left')}">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button class="btn btn-sm btn-link scroll-right" title="{__('Poll results', 'Scroll to the right')}">
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
</div>
{if !$accessGranted && $resultPubliclyVisible}
{include 'part/password_request.tpl' active=$poll->active}
{/if}
{* Vote table *}
{if $poll->format === 'D'}
{include 'part/vote_table_date.tpl' active=$poll->active}
{else}
{include 'part/vote_table_classic.tpl' active=$poll->active}
{/if}
{* Comments *}
{include 'part/comments.tpl' active=$poll->active comments=$comments}
{/if}
{/if}
{* Scroll left and right *}
<div class="hidden row scroll-buttons" aria-hidden="true">
<div class="btn-group pull-right">
<button class="btn btn-sm btn-link scroll-left" title="{__('Poll results', 'Scroll to the left')}">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button class="btn btn-sm btn-link scroll-right" title="{__('Poll results', 'Scroll to the right')}">
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
</div>
{* Vote table *}
{if $poll->format === 'D'}
{include 'part/vote_table_date.tpl' active=$poll->active}
{else}
{include 'part/vote_table_classic.tpl' active=$poll->active}
{/if}
{* Comments *}
{include 'part/comments.tpl' active=$poll->active comments=$comments}
{/block}