Merge branch 'feature/ajax_submission_for_comments' into 'develop'

Feature - ajax submission for comments

The sending of comments has been ajaxified, in order to prevent the lose of vote data while commenting.

The comment list is also refreshed while commenting.

See merge request !72
This commit is contained in:
Olivier Perez 2015-10-26 17:00:40 +01:00
commit 3f1f957c5e
10 changed files with 274 additions and 113 deletions

88
action/add_comment.php Normal file
View File

@ -0,0 +1,88 @@
<?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)
*/
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\InputService;
use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
use Framadate\Message;
use Framadate\Utils;
use Framadate\Editable;
include_once __DIR__ . '/../app/inc/init.php';
/* Variables */
/* --------- */
$poll_id = null;
$poll = null;
$message = null;
$result = false;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService);
/* PAGE */
/* ---- */
if (!empty($_POST['poll'])) {
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if (strlen($poll_id) === 16) {
$poll = $pollService->findById($poll_id);
}
}
if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
} else {
$name = $inputService->filterName($_POST['name']);
$comment = $inputService->filterComment($_POST['comment']);
if ($name == null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if ($message == null) {
// Add comment
$result = $pollService->addComment($poll_id, $name, $comment);
if ($result) {
$message = new Message('success', __('Comments', 'Comment added'));
$notificationService->sendUpdateNotification($poll, $mailService, $name, NotificationService::ADD_COMMENT);
} else {
$message = new Message('danger', __('Error', 'Comment failed'));
}
}
}
$comments = $pollService->allCommentsByPollId($poll_id);
$smarty->error_reporting = E_ALL & ~E_NOTICE;
$smarty->assign('comments', $comments);
$comments_html = $smarty->fetch('part/comments_list.tpl');
$response = array('result' => $result, 'message' => $message, 'comments' => $comments_html);
echo json_encode($response);

View File

@ -23,15 +23,11 @@ use Framadate\Services\InputService;
use Framadate\Services\LogService; use Framadate\Services\LogService;
use Framadate\Services\MailService; use Framadate\Services\MailService;
use Framadate\Services\PollService; use Framadate\Services\PollService;
use Framadate\Services\NotificationService;
use Framadate\Utils; use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php'; include_once __DIR__ . '/app/inc/init.php';
/* Constants */
/* --------- */
const UPDATE_POLL = 1;
const DELETED_POLL = 2;
/* Variables */ /* Variables */
/* --------- */ /* --------- */
@ -49,40 +45,7 @@ $pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService); $adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService(); $inputService = new InputService();
$mailService = new MailService($config['use_smtp']); $mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService);
/* Functions */
/*-----------*/
/**
* Send a notification to the poll admin to notify him about an update.
*
* @param stdClass $poll The poll
* @param MailService $mailService The mail service
* @param int $type cf: Constants on the top of this page
*/
function sendUpdateNotification($poll, $mailService, $type) {
if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = [];
}
if ($poll->receiveNewVotes) {
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', 'Notification of poll: %s', $poll->title);
$message = '';
switch ($type) {
case UPDATE_POLL:
$message = __f('Mail', 'Someone just change your poll available at the following link %s.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
break;
case DELETED_POLL:
$message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
break;
}
$messageTypeKey = $type . '-' . $poll->id;
$mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */
@ -176,7 +139,7 @@ if (isset($_POST['update_poll_info'])) {
// Update poll in database // Update poll in database
if ($updated && $adminPollService->updatePoll($poll)) { if ($updated && $adminPollService->updatePoll($poll)) {
$message = new Message('success', __('adminstuds', 'Poll saved')); $message = new Message('success', __('adminstuds', 'Poll saved'));
sendUpdateNotification($poll, $mailService, UPDATE_POLL); $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_POLL);
} else { } else {
$message = new Message('danger', __('Error', 'Failed to save poll')); $message = new Message('danger', __('Error', 'Failed to save poll'));
$poll = $pollService->findById($poll_id); $poll = $pollService->findById($poll_id);
@ -341,7 +304,7 @@ if (isset($_POST['delete_poll'])) {
if (isset($_POST['confirm_delete_poll'])) { if (isset($_POST['confirm_delete_poll'])) {
if ($adminPollService->deleteEntirePoll($poll_id)) { if ($adminPollService->deleteEntirePoll($poll_id)) {
$message = new Message('success', __('adminstuds', 'Poll fully deleted')); $message = new Message('success', __('adminstuds', 'Poll fully deleted'));
sendUpdateNotification($poll, $mailService, DELETED_POLL); $notificationService->sendUpdateNotification($poll, NotificationService::DELETED_POLL);
} else { } else {
$message = new Message('danger', __('Error', 'Failed to delete the poll')); $message = new Message('danger', __('Error', 'Failed to delete the poll'));
} }

View File

@ -0,0 +1,88 @@
<?php
namespace Framadate\Services;
use Framadate\Services\MailService;
use Framadate\Utils;
use \stdClass;
class NotificationService {
const UPDATE_VOTE = 1;
const ADD_VOTE = 2;
const ADD_COMMENT = 3;
const UPDATE_POLL = 10;
const DELETED_POLL = 11;
private $mailService;
function __construct(MailService $mailService) {
$this->mailService = $mailService;
}
/**
* Send a notification to the poll admin to notify him about an update.
*
* @param $poll stdClass The poll
* @param $name string The name user who triggered the notification
* @param $type int cf: Constants on the top of this page
*/
function sendUpdateNotification(stdClass $poll, $type, $name='') {
if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = [];
}
if ($poll->receiveNewVotes) {
if (self::isParticipation($type)) {
$translationString = 'Poll\'s participation: %s';
} else {
$translationString = 'Notification of poll: %s';
}
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', $translationString, $poll->title);
$message = '';
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
$link = '<a href="' . $urlSondage . '">' . $urlSondage . '</a>' . "\n\n";
switch ($type) {
case self::UPDATE_VOTE:
$message .= $name . ' ';
$message .= __('Mail', "updated a vote.\nYou can find your poll at the link") . " :\n\n";
$message .= $link;
break;
case self::ADD_VOTE:
$message .= $name . ' ';
$message .= __('Mail', "filled a vote.\nYou can find your poll at the link") . " :\n\n";
$message .= $link;
break;
case self::ADD_COMMENT:
$message .= $name . ' ';
$message .= __('Mail', "wrote a comment.\nYou can find your poll at the link") . " :\n\n";
$message .= $link;
break;
case self::UPDATE_POLL:
$message = __f('Mail', 'Someone just change your poll available at the following link %s.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
break;
case self::DELETED_POLL:
$message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
break;
}
$messageTypeKey = $type . '-' . $poll->id;
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
function isParticipation($type)
{
return $type >= self::UPDATE_POLL;
}
}

7
css/jquery-ui.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -18,18 +18,19 @@
$(document).ready(function () { $(document).ready(function () {
$("#poll_form").submit(function (event) { $('#poll_form').submit(function (event) {
var name = $("#name").val(); var name = $('#name').val().trim();
name = name.trim();
if (name.length == 0) { if (name.length == 0) {
event.preventDefault(); event.preventDefault();
var newMessage = $("#nameErrorMessage").clone(); var newMessage = $('#nameErrorMessage').clone();
$("#message-container").empty(); var messageContainer = $('#message-container');
$("#message-container").append(newMessage); messageContainer
newMessage.removeClass("hidden"); .empty()
.append(newMessage);
newMessage.removeClass('hidden');
$('html, body').animate({ $('html, body').animate({
scrollTop: $("#message-container").offset().top scrollTop: messageContainer.offset().top
}, 750); }, 750);
} }
}); });
@ -44,4 +45,46 @@ $(document).ready(function () {
$(this).next().removeClass('startunchecked'); $(this).next().removeClass('startunchecked');
}); });
var form = $('#comment_form');
form.submit(function(event) {
event.preventDefault();
$.ajax({
type: 'POST',
url: form.attr('action'),
data: form.serialize(),
dataType: 'json',
success: function(data)
{
$('#comment').val('');
if (data.result) {
$('#comments_list')
.replaceWith(data.comments);
var lastComment = $('#comments_list')
.find('div.comment')
.last();
lastComment.effect('highlight', {color: 'green'}, 401);
$('html, body').animate({
scrollTop: lastComment.offset().top
}, 750);
} else {
var newMessage = $('#genericErrorTemplate').clone();
newMessage
.find('.contents')
.text(data.message.message);
var commentsAlert = $('#comments_alerts');
commentsAlert
.empty()
.append(newMessage);
$('html, body').animate({
scrollTop: commentsAlert.offset().top
}, 750);
}
}
});
return false;
});
}); });

6
js/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -20,18 +20,13 @@ use Framadate\Services\LogService;
use Framadate\Services\PollService; use Framadate\Services\PollService;
use Framadate\Services\InputService; use Framadate\Services\InputService;
use Framadate\Services\MailService; use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
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';
/* Constants */
/* --------- */
const UPDATE_VOTE = 1;
const ADD_VOTE = 2;
const ADD_COMMENT = 3;
/* Variables */ /* Variables */
/* --------- */ /* --------- */
@ -47,46 +42,8 @@ $logService = new LogService();
$pollService = new PollService($connect, $logService); $pollService = new PollService($connect, $logService);
$inputService = new InputService(); $inputService = new InputService();
$mailService = new MailService($config['use_smtp']); $mailService = new MailService($config['use_smtp']);
$notificationService = new NotificationService($mailService);
/* Functions */
/*-----------*/
/**
* Send a notification to the poll admin to notify him about an update.
*
* @param $poll stdClass The poll
* @param $mailService MailService The mail service
* @param $name string The name user who triggered the notification
* @param $type int cf: Constants on the top of this page
*/
function sendUpdateNotification($poll, $mailService, $name, $type) {
if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = [];
}
if ($poll->receiveNewVotes) {
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', 'Poll\'s participation: %s', $poll->title);
$message = $name . ' ';
switch ($type) {
case UPDATE_VOTE:
$message .= __('Mail', "updated a vote.\nYou can find your poll at the link") . " :\n\n";
break;
case ADD_VOTE:
$message .= __('Mail', "filled a vote.\nYou can find your poll at the link") . " :\n\n";
break;
case ADD_COMMENT:
$message .= __('Mail', "wrote a comment.\nYou can find your poll at the link") . " :\n\n";
break;
}
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
$message .= '<a href="' . $urlSondage . '">' . $urlSondage . '</a>' . "\n\n";
$messageTypeKey = $type . '-' . $poll->id;
$mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
/* PAGE */ /* PAGE */
/* ---- */ /* ---- */
@ -139,7 +96,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
} else { } else {
$message = new Message('success', __('studs', 'Update vote succeeded')); $message = new Message('success', __('studs', 'Update vote succeeded'));
} }
sendUpdateNotification($poll, $mailService, $name, UPDATE_VOTE); $notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_VOTE, $name);
} else { } else {
$message = new Message('danger', __('Error', 'Update vote failed')); $message = new Message('danger', __('Error', 'Update vote failed'));
} }
@ -165,7 +122,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
} else { } else {
$message = new Message('success', __('studs', 'Adding the vote succeeded')); $message = new Message('success', __('studs', 'Adding the vote succeeded'));
} }
sendUpdateNotification($poll, $mailService, $name, ADD_VOTE); $notificationService->sendUpdateNotification($poll, NotificationService::ADD_VOTE, $name);
} else { } else {
$message = new Message('danger', __('Error', 'Adding vote failed')); $message = new Message('danger', __('Error', 'Adding vote failed'));
} }
@ -189,12 +146,11 @@ if (isset($_POST['add_comment'])) {
$result = $pollService->addComment($poll_id, $name, $comment); $result = $pollService->addComment($poll_id, $name, $comment);
if ($result) { if ($result) {
$message = new Message('success', __('Comments', 'Comment added')); $message = new Message('success', __('Comments', 'Comment added'));
sendUpdateNotification($poll, $mailService, $name, ADD_COMMENT); $notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name);
} else { } else {
$message = new Message('danger', __('Error', 'Comment failed')); $message = new Message('danger', __('Error', 'Comment failed'));
} }
} }
} }
// Retrieve data // Retrieve data

View File

@ -1,21 +1,11 @@
<hr role="presentation" id="comments" class="hidden-print"/> <hr role="presentation" id="comments" class="hidden-print"/>
<form action="#comments" method="POST">
{* Comment list *} {* Comment list *}
{include 'part/comments_list.tpl'}
{if $comments|count > 0} <form action="action/add_comment.php" method="POST" id="comment_form">
<h3>{__('Comments', 'Comments of polled people')}</h3>
{foreach $comments as $comment} <input type="hidden" name="poll" value="{$poll_id}"/>
<div class="comment">
{if $admin && !$expired}
<button type="submit" name="delete_comment" value="{$comment->id|html}" class="btn btn-link" title="{__('Comments', 'Remove the comment')}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{__('Generic', 'Remove')}</span></button>
{/if}
<span class="comment_date">{$comment->date|date_format:$date_format['txt_datetime_short']}</span>
<b>{$comment->name|html}</b>&nbsp;
<span class="comment">{$comment->comment|escape|nl2br}</span>
</div>
{/foreach}
{/if}
{* Add comment form *} {* Add comment form *}
{if $active && !$expired} {if $active && !$expired}

View File

@ -0,0 +1,16 @@
<div id="comments_list">
{if $comments|count > 0}
<h3>{__('Comments', 'Comments of polled people')}</h3>
{foreach $comments as $comment}
<div class="comment">
{if $admin && !$expired}
<button type="submit" name="delete_comment" value="{$comment->id|html}" class="btn btn-link" title="{__('Comments', 'Remove the comment')}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{__('Generic', 'Remove')}</span></button>
{/if}
<span class="comment_date">{$comment->date|date_format:$date_format['txt_datetime_short']}</span>
<b>{$comment->name|html}</b>&nbsp;
<span>{$comment->comment|escape|nl2br}</span>
</div>
{/foreach}
{/if}
<div id="comments_alerts"></div>
</div>

View File

@ -1,9 +1,12 @@
{extends file='page.tpl'} {extends file='page.tpl'}
{block name="header"} {block name="header"}
<script src="{"js/jquery-ui.min.js"|resource}" type="text/javascript"></script>
<script src="{"js/Chart.min.js"|resource}" type="text/javascript"></script> <script src="{"js/Chart.min.js"|resource}" type="text/javascript"></script>
<script src="{"js/Chart.StackedBar.js"|resource}" type="text/javascript"></script> <script src="{"js/Chart.StackedBar.js"|resource}" type="text/javascript"></script>
<script src="{"js/app/studs.js"|resource}" type="text/javascript"></script> <script src="{"js/app/studs.js"|resource}" type="text/javascript"></script>
<link rel="stylesheet" href="{'css/jquery-ui.min.css'|resource}">
{/block} {/block}
{block name=main} {block name=main}
@ -15,6 +18,7 @@
{/if} {/if}
</div> </div>
<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="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>
{* Global informations about the current poll *} {* Global informations about the current poll *}