Merge branch 'olivierperez/framadate-master' into develop

Conflicts:
	admin/index.php
	adminstuds.php
	app/classes/Framadate/Utils.php
	app/inc/config.template.php
	app/inc/init.php
	choix_autre.php
	choix_date.php
	composer.json
	composer.lock
	creation_sondage.php
	locale/de_DE/LC_MESSAGES/Studs.mo
	locale/de_DE/LC_MESSAGES/Studs.po
	locale/en_GB/LC_MESSAGES/Studs.mo
	locale/fr_FR/LC_MESSAGES/Studs.mo
	studs.php
This commit is contained in:
JosephK 2015-03-04 10:53:43 +01:00
commit c1d4bed4d5
76 changed files with 5523 additions and 3579 deletions

12
.gitignore vendored
View File

@ -1,12 +1,13 @@
.htaccess
admin/.htaccess
admin/.htpasswd
.htpasswd
admin/logs_studs.txt
composer.phar
framanav
nav
app/inc/constants.php
app/inc/config.php
vendor
cache/
tpl_c/
# Temp files
*~
@ -15,3 +16,8 @@ vendor
# Cache
Thumbs.db
# IDE
.settings/
.project
.idea/
*.iml

233
README.md
View File

@ -7,112 +7,179 @@ If you want to work with us, **fork us on [git.framasoft.org](https://git.framas
Si vous souhaitez travailler avec nous, **forkez-nous sur [git.framasoft.org](https://git.framasoft.org)**. (l'inscription n'est pas nécessaire, vous pouvez vous connecter avec votre compte Github)
* * *
![English:](http://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png)
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](http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt)
Framadate est un fork du projet STUdS : https://sourcesup.cru.fr/projects/studs/
Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
Authors of Framadate/OpenSondate: [Framasoft](https://git.framasoft.org/framasoft/framadate)
Framadate est le projet qui motorise framadate.org pour framasoft.org
![Français :](http://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png)
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](http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt)
Les auteurs principaux de Framadate sont :
- Simon LEBLANC
- Pierre-Yves GOSSET
Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
Auteurs de Framadate/OpenSondage : [Framasoft](https://git.framasoft.org/framasoft/framadate)
Les auteurs principaux du projet STUdS sont :
- Guilhem BORGHESI
- Raphaël DROZ
* * *
#Framadate
[Framadate](https://framadate.org) est un fork du projet [STUdS](https://sourcesup.cru.fr/projects/studs/).
Il est développé par l'association [Framasoft](http://framasoft.org).
==========================================================================
##Fichiers de l'application
Université de Strasbourg - Direction Informatique
Auteur : Guilhem BORGHESI
Création : Février 2008
### Administration
* `/admin`
Le répertoire réservé à l'administrateur de l'application
* `admin/index.php`
La page présentant tous les sondages actuellement dans la base à l'administrateur
* `admin/log_studs.txt`
Le fichier contenant un historique de toutes les creations/suppressions de sondage dans la base
borghesi@unistra.fr
* `install/` (pas utilisé - en développement)
Le répertoire qui contient les scripts chargés de simplifier la procédure d'installation
* `scripts/` (pas utilisé)
Le répertoire qui contient quelques vieux scripts pour la maintenance de l'application
Ce logiciel est régi par la licence CeCILL-B soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL-B telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".
### Application
* `app/inc/constants.php.template`
Le fichier contenant les constantes à changer en fonction de la machine locale
* `app/classes/Framadate/Utils.php`
Le fichier contenant quelques fonctions récurrentes de l'application
* `app/inc/i18n.php`
Le fichier contenant quelques fonctions récurrentes de l'application relatives à l'internationalisation
* `app/inc/init.php`
Le fichier qui charge les dépendances et ouvre la connexion à la base de données
Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL-B, et que vous en avez accepté les
termes. Vous pouvez trouver une copie de la licence dans le fichier LICENCE.
* `css/`
Les fichiers CSS de l'application (dont ceux de Bootstrap)
* `fonts/`
Les fichiers des icônes de Bootstrap
* `images/`
Logo et images de la page d'accueil
* `js/`
Les fichiers javascript de l'application (dont ceux de Bootstrap et de jQuery)
==========================================================================
* `locale/`
Université de Strasbourg - Direction Informatique
Author : Guilhem BORGHESI
Creation : Feb 2008
borghesi@unistra.fr
This software is governed by the CeCILL-B license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/ or redistribute the software under the terms of the CeCILL-B
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".
The fact that you are presently reading this means that you have had
knowledge of the CeCILL-B license and that you accept its terms. You can
find a copy of this license in the file LICENSE.
==========================================================================
=============================================================================
Fichiers de l'application
=============================================================================
index.php
La page d'accueil de STUdS
studs.php
La page de présentation de sondage
adminstuds.php
La page d'administration réservée à l'auteur du sondage
infos_sondage.php
La page (1/2) de création de sondage récupérant les informations générales
choix_date.php
La page de création (2/2) pour un sondage pour déterminer une date
choix_autre.php
La page de création (2/2) pour un sondage sur un sujet quelconque
creation_sondage.php
Le fichier qui récupérent les informations des pages précédentes pour procéder à l'insertion du nouveau sondage dans la base PostgreSQL
style.css
Le fichier CSS de style pour toute l'application
app/inc/constants.php
Le fichier contenant les constantes à changer en fonction de la machine locale
app/inc/functions.php
Le fichier contenant quelques fonctions récurrentes de l'application
app/inc/i18n.php
Le fichier contenant quelques fonctions récurrentes de l'application relatives à l'internationalisation
README
Ce fichier
INSTALL
Le fichier contenant les informations d'installation sur l'application
CHANGELOG
Le fichier contenant toutes les modifications de l'application entre les différentes versions
contacts.php
La page permettant aux usagers de poser une question à l'administrateur de l'application
apropos.php
La page expliquant les détails techniques relatifs à l'application et les dernieres modifications et celles à venir sur l'application
bandeaux.php
Le fichier contenant tous les bandeaux des pages PHP de l'application
favicon.ico
L'icone de favoris de l'application
sources.php
La page qui propose les sources de l'application
exportics.php
Le fichier d'export de la meilleure date au format iCAL (fichier .ICS)
exportcsv.php
Le fichier d'export de tous le tableau des participants avec leurs réponses dans un tableur (format .CSV)
exportpdf.php
Le fichier d'export de la lettre de convocation que le créateur du sondage pourra envoyer aux participants (format .PDF)
admin/
Le répertoire réservé à l'administrateur de l'application
admin/.htaccess
Le fichier gérant les droits restreints du répertoire ADMIN
admin/.htpasswd
Le fichier contenant les passwd des logins ayant accès au répertoire ADMIN
admin/index.php
La page présentant tous les sondages actuellement dans la base à l'administrateur
admin/log_studs.txt
Le fichier contenant un historique de toutes les creations/suppressions de sondage dans la base
errors/
Le répertoire contenant toutes les pages d'erreurs
errors/error-forbidden.php
La page qui indique dans la charte graphique de l'application l'erreur "501 forbidden"
errors/maintenance.php
La page qui indique que l'application est en maintenance temporaire
export/
Le répertoire qui contient tous les exports ICS
iCalcreator/
Le répertoire qui contient les librairies d'export en iCal
php2pdf/
Le répertoire qui contient les librairies d'export en PDF
scripts/
Le répertoire qui contient tous les scripts de l'application
sources/
Le répertoire qui contient les sources de l'application disponible sur la page sources.php
locale/
Le répertoire qui contient les fichiers de traduction modifiables (.po) et compilés (.mo)
au format gettext
* `index.php`
La page d'accueil de STUdS
* `studs.php`
La page de présentation de sondage
* `adminstuds.php`
La page d'administration réservée à l'auteur du sondage
* `infos_sondage.php`
La page (1/2) de création de sondage récupérant les informations générales
* `choix_date.php`
La page de création (2/2) pour un sondage pour déterminer une date
* `choix_autre.php`
La page de création (2/2) pour un sondage sur un sujet quelconque
* `creation_sondage.php`
Le fichier qui récupérent les informations des pages précédentes pour procéder à l'insertion du nouveau sondage dans la base PostgreSQL
=============================================================================
Validations des pages
=============================================================================
* `bandeaux.php`
Le fichier contenant les éléments de l'entête et du pied de page de l'application
* `exportcsv.php`
Le fichier d'export de tous le tableau des participants avec leurs réponses dans un tableur (format .CSV)
* `favicon.ico`
L'icone de favoris de l'application
Toutes les pages de STUdS sont validées HTML 4.01 Strict.
La CSS de STUdS est validée CSS 2.1.
### Infos
* `AUTHORS.md`
Liste des principaux développeurs du logiciel
* `README.md`
Ce fichier
* `INSTALL.md`
Le fichier contenant les informations d'installation sur l'application
* `CHANGELOG.md`
Le fichier contenant la liste des principale modifications de l'application entre les différentes versions
##Technologies utilisées
=============================================================================
Technologies utilisées
=============================================================================
- PHP 5.4.4, php-adodb, php-gettext, composer
- Bootstrap, jQuery, Bootstrap Datepicker
- MySQL
- Nginx, Apache
- PHP 5.4.4, php-fpdf, php-adodb, php-gettext
- PostgreSQL, mysql
- Apache
- iCalcreator
- POedit
- Icônes : Deleket (http://deleket.deviantart.com/) et DryIcons (http://dryicons.com)
##Compatibilités des navigateurs
(Dernière mise à jour le 21 avril 2014)
=============================================================================
Compatibilités des navigateurs
Dernière mise à jour le 21 avril 2014
=============================================================================
- Firefox : Ubuntu 13.10/FF28
- Chrome : Ubuntu 13.10/Chromium33
- Opera (non testé)
- Konqueror
- Links (non testé, inutile)
- Safari (non testé)
- IE : Win7/IE9
-----------------
Janvier 2008
Guilhem BORGHESI
Université de Strasbourg
Mai 2010
Raphaël DROZ, raphael.droz@gmail.com

View File

@ -16,100 +16,9 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
session_start();
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
require_once '../app/inc/init.php';
// Ce fichier index.php se trouve dans le sous-repertoire ADMIN de Studs. Il sert à afficher l'intranet de studs
// pour modifier les sondages directement sans avoir reçu les mails. C'est l'interface d'aministration
// de l'application.
// Affichage des balises standards
Utils::print_header( _("Polls administrator") );
bandeau_titre(_("Polls administrator"));
$sondage=$connect->Execute("select * from sondage");
echo'
<form action="' . Utils::get_server_name() . 'admin/index.php" method="POST">'."\n";
// Test et affichage du bouton de confirmation en cas de suppression de sondage
while($dsondage = $sondage->FetchNextObject(false)) {
if (Utils::issetAndNoEmpty('supprimersondage'.$dsondage->id_sondage) === true) {
echo '
<div class="alert alert-warning text-center">
<h3>'. _("Confirm removal of the poll ") .'"'.$dsondage->id_sondage.'</h3>
<p><button class="btn btn-default" type="submit" value="1" name="annullesuppression">'._("Keep this poll!").'</button>
<button type="submit" name="confirmesuppression'.$dsondage->id_sondage.'" value="1" class="btn btn-danger">'._("Remove this poll!").'</button></p>
</div>';
}
// Traitement de la confirmation de suppression
if (Utils::issetAndNoEmpty('confirmesuppression'.$dsondage->id_sondage) === true) {
// On inclut la routine de suppression
$date=date('H:i:s d/m/Y');
if (Utils::remove_sondage($connect, $dsondage->id_sondage)) {
// ecriture des traces dans le fichier de logs
error_log($date . " SUPPRESSION: $dsondage->id_sondage\t$dsondage->format\t$dsondage->nom_admin\t$dsondage->mail_admin\n", 3, 'logs_studs.txt');
}
}
}
$sondage=$connect->Execute("select * from sondage WHERE date_fin > DATE_SUB(now(), INTERVAL 3 MONTH) ORDER BY date_fin ASC");
$nbsondages=$sondage->RecordCount();
$btn_logs = (is_readable('logs_studs.txt')) ? '<a role="button" class="btn btn-default btn-xs pull-right" href="'.str_replace('/admin','', Utils::get_server_name()).'admin/logs_studs.txt">'. _("Logs") .'</a>' : '';
echo '<p>' . $nbsondages. ' ' . _("polls in the database at this time") . $btn_logs .'</p>'."\n";
// tableau qui affiche tous les sondages de la base
echo '<table class="table table-bordered">
<tr align="center">
<th scope="col">'. _("Poll ID") .'</th>
<th scope="col">'. _("Format") .'</th>
<th scope="col">'. _("Title") .'</th>
<th scope="col">'. _("Author") .'</th>
<th scope="col">'. _("Email") .'</th>
<th scope="col">'. _("Expiration's date") .'</th>
<th scope="col">'. _("Users") .'</th>
<th scope="col" colspan="3">'. _("Actions") .'</th>
</tr>'."\n";
$i = 0;
while($dsondage = $sondage->FetchNextObject(false)) {
/* possible en 1 bonne requête dans $sondage */
$sujets=$connect->Execute( "select * from sujet_studs where id_sondage='$dsondage->id_sondage'");
$dsujets=$sujets->FetchObject(false);
$user_studs=$connect->Execute( "select * from user_studs where id_sondage='$dsondage->id_sondage'");
$nbuser=$user_studs->RecordCount();
echo '
<tr align="center">
<td>'.$dsondage->id_sondage.'</td>
<td>'.$dsondage->format.'</td>
<td>'. stripslashes($dsondage->titre).'</td>
<td>'.stripslashes($dsondage->nom_admin).'</td>
<td>'.stripslashes($dsondage->mail_admin).'</td>';
if (strtotime($dsondage->date_fin) > time()) {
echo '
<td>'.date("d/m/y",strtotime($dsondage->date_fin)).'</td>';
} else {
echo '
<td><span class="text-danger">'.date("d/m/y",strtotime($dsondage->date_fin)).'</span></td>';
}
echo '
<td>'.$nbuser.'</td>
<td><a href="' . Utils::getUrlSondage($dsondage->id_sondage) . '" class="btn btn-link" title="'. _("See the poll") .'"><span class="glyphicon glyphicon-eye-open"></span><span class="sr-only">' . _("See the poll") . '</span></a></td>
<td><a href="' . Utils::getUrlSondage($dsondage->id_sondage_admin, true) . '" class="btn btn-link" title="'. _("Change the poll") .'"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">' . _("Change the poll") . '</span></a></td>
<td><button type="submit" name="supprimersondage'.$dsondage->id_sondage.'" value="'. _("Remove the poll") .'" class="btn btn-link" title="'. _("Remove the poll") .'"><span class="glyphicon glyphicon-trash text-danger"></span><span class="sr-only">' . _("Remove the poll") . '</span></td>
</tr>'."\n";
$i++;
}
echo '</table></form>'."\n";
bandeau_pied(true);
$smarty->assign('title', _('Administration'));
$smarty->assign('logsAreReadable', is_readable('../' . LOG_FILE));
$smarty->display('admin/index.tpl');

28
admin/logs.php Normal file
View File

@ -0,0 +1,28 @@
<?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)
*/
require_once '../app/inc/init.php';
ob_start();
is_readable('../' . LOG_FILE) ? readfile('../' . LOG_FILE) : null;
$content = ob_get_clean();
$smarty->assign('title', _('Administration'));
$smarty->assign('logs', $content);
$smarty->display('admin/logs.tpl');

107
admin/migration.php Normal file
View File

@ -0,0 +1,107 @@
<?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\Migration\From_0_0_to_0_8_Migration;
use Framadate\Migration\From_0_8_to_0_9_Migration;
use Framadate\Migration\From_0_9_to_0_9_1_Migration;
use Framadate\Migration\Migration;
use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
set_time_limit(300);
// List a Migration sub classes to execute
$migrations = [
new From_0_0_to_0_8_Migration(),
new From_0_8_to_0_9_Migration(),
new From_0_9_to_0_9_1_Migration()
];
// ---------------------------------------
// Check if MIGRATION_TABLE already exists
$tables = $connect->allTables();
$pdo = $connect->getPDO();
$prefixedMigrationTable = Utils::table(MIGRATION_TABLE);
if (!in_array($prefixedMigrationTable, $tables)) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . $prefixedMigrationTable . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL,
`execute_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8;');
}
$selectStmt = $pdo->prepare('SELECT id FROM ' . $prefixedMigrationTable . ' WHERE name=?');
$insertStmt = $pdo->prepare('INSERT INTO ' . $prefixedMigrationTable . ' (name) VALUES (?)');
$countSucceeded = 0;
$countFailed = 0;
$countSkipped = 0;
// Loop on every Migration sub classes
$success = [];
$fail = [];
foreach ($migrations as $migration) {
$className = get_class($migration);
// Check if $className is a Migration sub class
if (!$migration instanceof Migration) {
$smarty->assign('error', 'The class ' . $className . ' is not a sub class of Framadate\\Migration\\Migration.');
$smarty->display('error.tpl');
exit;
}
// Check if the Migration is already executed
$selectStmt->execute([$className]);
$executed = $selectStmt->rowCount();
$selectStmt->closeCursor();
if (!$executed && $migration->preCondition($pdo)) {
$migration->execute($pdo);
if ($insertStmt->execute([$className])) {
$countSucceeded++;
$success[] = $migration->description();
} else {
$countFailed++;
$fail[] = $migration->description();
}
} else {
$countSkipped++;
}
}
$countTotal = $countSucceeded + $countFailed + $countSkipped;
$smarty->assign('success', $success);
$smarty->assign('fail', $fail);
$smarty->assign('countSucceeded', $countSucceeded);
$smarty->assign('countFailed', $countFailed);
$smarty->assign('countSkipped', $countSkipped);
$smarty->assign('countTotal', $countTotal);
$smarty->assign('time', $total_time = round((microtime(true)-$_SERVER['REQUEST_TIME_FLOAT']), 4));
$smarty->assign('title', _('Migration'));
$smarty->display('admin/migration.tpl');

78
admin/polls.php Normal file
View File

@ -0,0 +1,78 @@
<?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\AdminPollService;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\SecurityService;
use Framadate\Services\SuperAdminService;
use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
const POLLS_PER_PAGE = 30;
/* Variables */
/* --------- */
$polls = null;
$poll_to_delete = null;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$superAdminService = new SuperAdminService($connect);
$securityService = new SecurityService();
/* GET */
/*-----*/
$page = (int)filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);
$page = ($page >= 1) ? $page : 1;
/* PAGE */
/* ---- */
if (!empty($_POST['delete_poll']) && $securityService->checkCsrf('admin', $_POST['csrf'])) {
$delete_id = filter_input(INPUT_POST, 'delete_poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$poll_to_delete = $pollService->findById($delete_id);
}
// Traitement de la confirmation de suppression
if (!empty($_POST['delete_confirm']) && $securityService->checkCsrf('admin', $_POST['csrf'])) {
$poll_id = filter_input(INPUT_POST, 'delete_confirm', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$adminPollService->deleteEntirePoll($poll_id);
}
$found = $superAdminService->findAllPolls($page-1, POLLS_PER_PAGE);
$polls = $found['polls'];
$count = $found['count'];
// Assign data to template
$smarty->assign('polls', $polls);
$smarty->assign('count', $count);
$smarty->assign('page', $page);
$smarty->assign('pages', ceil($count / POLLS_PER_PAGE));
$smarty->assign('poll_to_delete', $poll_to_delete);
$smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->display('admin/polls.tpl');

56
admin/purge.php Normal file
View File

@ -0,0 +1,56 @@
<?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\PurgeService;
use Framadate\Services\SecurityService;
use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
/* Variables */
/* --------- */
$message = null;
/* Services */
/*----------*/
$logService = new LogService();
$purgeService = new PurgeService($connect, $logService);
$securityService = new SecurityService();
/* POST */
/*-----*/
$action = filter_input(INPUT_POST, 'action', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => NAME_REGEX]]);
/* PAGE */
/* ---- */
if ($action === 'purge' && $securityService->checkCsrf('admin', $_POST['csrf'])) {
$count = $purgeService->purgeOldPolls();
$message = _('Purged:') . ' ' . $count;
}
// Assign data to template
$smarty->assign('message', $message);
$smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->display('admin/purge.tpl');

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
<?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)
*/
namespace Framadate;
class Choice
{
/**
* Name of the Choice
*/
private $name;
/**
* All availables slots for this Choice.
*/
private $slots;
public function __construct($name='')
{
$this->name = $name;
$this->slots = array();
}
public function addSlot($slot)
{
$this->slots[] = $slot;
}
public function getName()
{
return $this->name;
}
public function getSlots()
{
return $this->slots;
}
static function compare(Choice $a, Choice $b)
{
return strcmp($a->name, $b->name);
}
}

View File

@ -0,0 +1,81 @@
<?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)
*/
namespace Framadate;
class Form
{
public $title;
public $description;
public $admin_name;
public $admin_mail;
public $format;
public $end_date;
public $choix_sondage;
/**
* Tells if users can modify their choices.
*/
public $editable;
/**
* If true, notify poll administrator when new vote is made.
*/
public $receiveNewVotes;
/**
* If true, notify poll administrator when new comment is posted.
*/
public $receiveNewComments;
/**
* List of available choices
*/
private $choices;
public function __construct(){
$this->editable = true;
$this->clearChoices();
}
public function clearChoices() {
$this->choices = array();
}
public function addChoice(Choice $choice)
{
$this->choices[] = $choice;
}
public function getChoices()
{
return $this->choices;
}
public function sortChoices()
{
usort($this->choices, array('Framadate\Choice', 'compare'));
}
public function lastChoice()
{
return end($this->choices);
}
}

View File

@ -0,0 +1,314 @@
<?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)
*/
namespace Framadate;
use PDO;
class FramaDB {
/**
* PDO Object, connection to database.
*/
private $pdo = null;
function __construct($connection_string, $user, $password) {
$this->pdo = new \PDO($connection_string, $user, $password);
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
/**
* @return \PDO Connection to database
*/
function getPDO() {
return $this->pdo;
}
/**
* Find all tables in database.
*
* @return array The array of table names
*/
function allTables() {
$result = $this->pdo->query('SHOW TABLES');
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
return $schemas;
}
function prepare($sql) {
return $this->pdo->prepare($sql);
}
function beginTransaction() {
$this->pdo->beginTransaction();
}
function commit() {
$this->pdo->commit();
}
function rollback() {
$this->pdo->rollback();
}
function errorCode() {
return $this->pdo->errorCode();
}
function errorInfo() {
return $this->pdo->errorInfo();
}
function query($sql) {
return $this->pdo->query($sql);
}
function findPollById($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared->execute([$poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
return $poll;
}
function updatePoll($poll) {
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=FROM_UNIXTIME(?), active=?, editable=? WHERE id = ?');
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, $poll->editable, $poll->id]);
}
function allCommentsByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
return $prepared->fetchAll();
}
function allUserVotesByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
return $prepared->fetchAll();
}
function allSlotsByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY title');
$prepared->execute(array($poll_id));
return $prepared->fetchAll();
}
function insertDefaultVote($poll_id, $insert_position) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), "0", SUBSTRING(choices, ?)) WHERE poll_id = ?');
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
}
function insertVote($poll_id, $name, $choices) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices) VALUES (?,?,?)');
$prepared->execute([$poll_id, $name, $choices]);
$newVote = new \stdClass();
$newVote->poll_id = $poll_id;
$newVote->id = $this->pdo->lastInsertId();
$newVote->name = $name;
$newVote->choices = $choices;
return $newVote;
}
function deleteVote($poll_id, $vote_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $vote_id]);
}
/**
* Delete all votes of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @return bool|null true if action succeeded.
*/
function deleteVotesByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
}
/**
* Delete all votes made on given moment index.
*
* @param $poll_id int The ID of the poll
* @param $index int The index of the vote into the poll
* @return bool|null true if action succeeded.
*/
function deleteVotesByIndex($poll_id, $index) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE poll_id = ?');
return $prepared->execute([$index, $index + 2, $poll_id]);
}
/**
* Find the slot into poll for a given datetime.
*
* @param $poll_id int The ID of the poll
* @param $datetime int The datetime of the slot
* @return mixed Object The slot found, or null
*/
function findSlotByPollIdAndDatetime($poll_id, $datetime) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
$prepared->execute([$poll_id, $datetime]);
$slot = $prepared->fetch();
$prepared->closeCursor();
return $slot;
}
/**
* Insert a new slot into a given poll.
*
* @param $poll_id int The ID of the poll
* @param $title mixed The title of the slot
* @param $moments mixed|null The moments joined with ","
* @return bool true if action succeeded
*/
function insertSlot($poll_id, $title, $moments) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $title, $moments]);
}
/**
* Update a slot into a poll.
*
* @param $poll_id int The ID of the poll
* @param $datetime int The datetime of the slot to update
* @param $newMoments mixed The new moments
* @return bool|null true if action succeeded.
*/
function updateSlot($poll_id, $datetime, $newMoments) {
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
return $prepared->execute([$newMoments, $poll_id, $datetime]);
}
/**
* Delete a entire slot from a poll.
*
* @param $poll_id int The ID of the poll
* @param $datetime mixed The datetime of the slot
*/
function deleteSlot($poll_id, $datetime) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
$prepared->execute([$poll_id, $datetime]);
}
function deleteSlotsByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
}
/**
* Delete all comments of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @return bool|null true if action succeeded.
*/
function deleteCommentsByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
}
function updateVote($poll_id, $vote_id, $name, $choices) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
}
function insertComment($poll_id, $name, $comment) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $name, $comment]);
}
function deleteComment($poll_id, $comment_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $comment_id]);
}
function deletePollById($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
return $prepared->execute([$poll_id]);
}
/**
* Find old polls. Limit: 20.
*
* @return array Array of old polls
*/
public function findOldPolls() {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE DATE_ADD(`end_date`, INTERVAL ' . PURGE_DELAY . ' DAY) < NOW() AND `end_date` != 0 LIMIT 20');
$prepared->execute([]);
return $prepared->fetchAll();
}
/**
* @param $start int The index of the first poll to return
* @param $limit int The limit size
* @return array
*/
public function findAllPolls($start, $limit) {
// Polls
$prepared = $this->prepare('
SELECT p.*,
(SELECT count(1) FROM `' . Utils::table('vote') . '` v WHERE p.id=v.poll_id) votes
FROM ' . Utils::table('poll') . ' p
ORDER BY p.title ASC
LIMIT :start, :limit');
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
$prepared->execute();
$polls = $prepared->fetchAll();
// Total count
$stmt = $this->query('SELECT count(1) nb FROM `' . Utils::table('poll') . '`');
$count = $stmt->fetch();
$stmt->closeCursor();
return ['polls' => $polls, 'count' => $count->nb];
}
public function countVotesByPollId($poll_id) {
$prepared = $this->prepare('SELECT count(1) nb FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
$prepared->execute([$poll_id]);
$result = $prepared->fetch();
$prepared->closeCursor();
return $result->nb;
}
}

View File

@ -0,0 +1,32 @@
<?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)
*/
namespace Framadate;
class Message {
var $type;
var $message;
function __construct($type, $message) {
$this->type = $type;
$this->message = $message;
}
}

View File

@ -0,0 +1,86 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
class From_0_0_to_0_8_Migration 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 "First installation of the Framadate application (v0.8)";
}
/**
* 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 is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if there is no tables but the MIGRATION_TABLE one
$diff = array_diff($tables, [Utils::table(MIGRATION_TABLE)]);
return count($diff) === 0;
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `sondage` (
`id_sondage` char(16) NOT NULL,
`commentaires` text,
`mail_admin` varchar(128) DEFAULT NULL,
`nom_admin` varchar(64) DEFAULT NULL,
`titre` text,
`id_sondage_admin` char(24) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_fin` timestamp NOT NULL DEFAULT \'0000-00-00 00:00:00\',
`format` varchar(2) DEFAULT NULL,
`mailsonde` tinyint(1) DEFAULT \'0\',
`statut` int(11) NOT NULL DEFAULT \'1\' COMMENT \'1 = actif ; 0 = inactif ; \',
UNIQUE KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `sujet_studs` (
`id_sondage` char(16) NOT NULL,
`sujet` text,
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `comments` (
`id_comment` int(11) unsigned NOT NULL AUTO_INCREMENT,
`id_sondage` char(16) NOT NULL,
`comment` text NOT NULL,
`usercomment` text,
PRIMARY KEY (`id_comment`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `user_studs` (
`id_users` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nom` varchar(64) NOT NULL,
`id_sondage` char(16) NOT NULL,
`reponses` text NOT NULL,
PRIMARY KEY (`id_users`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
}
}

View File

@ -0,0 +1,273 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
*
* @package Framadate\Migration
*/
class From_0_8_to_0_9_Migration 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 "From 0.8 to 0.9";
}
/**
* 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 is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.8 are presents
$diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables);
return count($diff) === 0;
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->createPollTable($pdo);
$this->createCommentTable($pdo);
$this->createSlotTable($pdo);
$this->createVoteTable($pdo);
$pdo->beginTransaction();
$this->migrateFromSondageToPoll($pdo);
$this->migrateFromCommentsToComment($pdo);
$this->migrateFromSujetStudsToSlot($pdo);
$this->migrateFromUserStudsToVote($pdo);
$pdo->commit();
$this->dropOldTables($pdo);
return true;
}
private function createPollTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
`id` CHAR(16) NOT NULL,
`admin_id` CHAR(24) NOT NULL,
`title` TEXT NOT NULL,
`description` TEXT,
`admin_name` VARCHAR(64) DEFAULT NULL,
`admin_mail` VARCHAR(128) DEFAULT NULL,
`creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`end_date` TIMESTAMP NOT NULL DEFAULT \'0000-00-00 00:00:00\',
`format` VARCHAR(1) DEFAULT NULL,
`editable` TINYINT(1) DEFAULT \'0\',
`receiveNewVotes` TINYINT(1) DEFAULT \'0\',
`active` TINYINT(1) DEFAULT \'1\',
PRIMARY KEY (`id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromSondageToPoll(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`id_sondage_admin`,
`titre`,
`commentaires`,
`nom_admin`,
`mail_admin`,
`date_creation`,
`date_fin`,
SUBSTR(`format`, 1, 1) AS `format`,
CASE SUBSTR(`format`, 2, 1)
WHEN \'+\' THEN 1
ELSE 0 END AS `editable`,
`mailsonde`,
CASE SUBSTR(`format`, 2, 1)
WHEN \'-\' THEN 0
ELSE 1 END AS `active`
FROM sondage');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('poll') . '`
(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$row->id_sondage_admin,
$this->unescape($row->titre),
$this->unescape($row->commentaires),
$this->unescape($row->nom_admin),
$this->unescape($row->mail_admin),
$row->date_creation,
$row->date_fin,
$row->format,
$row->editable,
$row->mailsonde,
$row->active
]);
}
}
private function createSlotTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`title` TEXT,
`moments` TEXT,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromSujetStudsToSlot(\PDO $pdo) {
$stmt = $pdo->query('SELECT * FROM sujet_studs');
$sujets = $stmt->fetchAll();
$slots = [];
foreach ($sujets as $sujet) {
$newSlots = $this->transformSujetToSlot($sujet);
$slots = array_merge($slots, $newSlots);
}
$prepared = $pdo->prepare('INSERT INTO ' . Utils::table('slot') . ' (`poll_id`, `title`, `moments`) VALUE (?,?,?)');
foreach ($slots as $slot) {
$prepared->execute([
$slot->poll_id,
$this->unescape($slot->title),
!empty($slot->moments) ? $this->unescape($slot->moments) : null
]);
}
}
private function createCommentTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` TEXT,
`comment` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromCommentsToComment(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`usercomment`,
`comment`
FROM `comments`');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->usercomment),
$this->unescape($row->comment)
]);
}
}
private function createVoteTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`choices` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromUserStudsToVote(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`nom`,
REPLACE(REPLACE(REPLACE(`reponses`, 1, \'X\'), 2, 1), \'X\', 2) reponses
FROM `user_studs`');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->nom),
$row->reponses
]);
}
}
private function transformSujetToSlot($sujet) {
$slots = [];
$ex = explode(',', $sujet->sujet);
$isDatePoll = strpos($sujet->sujet, '@');
$lastSlot = null;
foreach ($ex as $atomicSlot) {
if ($isDatePoll === false) { // Classic poll
$slot = new \stdClass();
$slot->poll_id = $sujet->id_sondage;
$slot->title = $atomicSlot;
$slots[] = $slot;
} else { // Date poll
$values = explode('@', $atomicSlot);
if ($lastSlot == null || $lastSlot->title !== $values[0]) {
$lastSlot = new \stdClass();
$lastSlot->poll_id = $sujet->id_sondage;
$lastSlot->title = $values[0];
$lastSlot->moments = count($values) == 2 ? $values[1] : '-';
$slots[] = $lastSlot;
} else {
$lastSlot->moments .= ',' . (count($values) == 2 ? $values[1] : '-');
}
}
}
return $slots;
}
private function dropOldTables(\PDO $pdo) {
$pdo->exec('DROP TABLE `comments`');
$pdo->exec('DROP TABLE `sujet_studs`');
$pdo->exec('DROP TABLE `user_studs`');
$pdo->exec('DROP TABLE `sondage`');
}
private function unescape($value) {
return stripslashes(html_entity_decode($value, ENT_QUOTES));
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This class executes the aciton in database to migrate data from version 0.9 to 0.9.1.
*
* @package Framadate\Migration
*/
class From_0_9_to_0_9_1_Migration 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 "From 0.9 to 0.9.1";
}
/**
* 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 is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'
AFTER `receiveNewVotes`');
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Framadate\Migration;
interface Migration {
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description();
/**
* 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);
/**
* 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);
}

View File

@ -0,0 +1,35 @@
<?php
namespace Framadate\Security;
class Token {
private $time;
private $value;
function __construct() {
$this->time = time() + TOKEN_TIME;
$this->value = $this->generate();
}
private function generate() {
return sha1(uniqid(mt_rand(), true));
}
public function getTime() {
return $this->time;
}
public function getValue() {
return $this->value;
}
public function isGone() {
return $this->time < time();
}
public function check($value) {
return $value === $this->value;
}
}

View File

@ -0,0 +1,270 @@
<?php
namespace Framadate\Services;
use Framadate\FramaDB;
use Framadate\Utils;
/**
* Class AdminPollService
*
* @package Framadate\Services
*/
class AdminPollService {
private $connect;
private $pollService;
private $logService;
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect;
$this->pollService = $pollService;
$this->logService = $logService;
}
function updatePoll($poll) {
global $config;
if ($poll->end_date > $poll->creation_date && $poll->end_date <= strtotime($poll->creation_date) + (86400 * $config['default_poll_duration'])) {
return $this->connect->updatePoll($poll);
} else {
return false;
}
}
/**
* Delete a comment from a poll.
*
* @param $poll_id int The ID of the poll
* @param $comment_id int The ID of the comment
* @return mixed true is action succeeded
*/
function deleteComment($poll_id, $comment_id) {
return $this->connect->deleteComment($poll_id, $comment_id);
}
/**
* Remove all comments of a poll.
*
* @param $poll_id int The ID a the poll
* @return bool|null true is action succeeded
*/
function cleanComments($poll_id) {
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
return $this->connect->deleteCommentsByPollId($poll_id);
}
/**
* Delete a vote from a poll.
*
* @param $poll_id int The ID of the poll
* @param $vote_id int The ID of the vote
* @return mixed true is action succeeded
*/
function deleteVote($poll_id, $vote_id) {
return $this->connect->deleteVote($poll_id, $vote_id);
}
/**
* Remove all votes of a poll.
*
* @param $poll_id int The ID of the poll
* @return bool|null true is action succeeded
*/
function cleanVotes($poll_id) {
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
return $this->connect->deleteVotesByPollId($poll_id);
}
/**
* Delete the entire given poll.
*
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
function deleteEntirePoll($poll_id) {
$poll = $this->connect->findPollById($poll_id);
$this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
// Delete the entire poll
$this->connect->deleteVotesByPollId($poll_id);
$this->connect->deleteCommentsByPollId($poll_id);
$this->connect->deleteSlotsByPollId($poll_id);
$this->connect->deletePollById($poll_id);
return true;
}
/**
* Delete a slot from a poll.
*
* @param $poll_id int The ID of the poll
* @param $slot object The slot informations (datetime + moment)
* @return bool true if action succeeded
*/
public function deleteDateSlot($poll_id, $slot) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll_id . ', slot:' . json_encode($slot));
$datetime = $slot->title;
$moment = $slot->moment;
$slots = $this->pollService->allSlotsByPollId($poll_id);
$index = 0;
$indexToDelete = -1;
$newMoments = [];
// Search the index of the slot to delete
foreach ($slots as $aSlot) {
$moments = explode(',', $aSlot->moments);
foreach ($moments as $rowMoment) {
if ($datetime == $aSlot->title) {
if ($moment == $rowMoment) {
$indexToDelete = $index;
} else {
$newMoments[] = $rowMoment;
}
}
$index++;
}
}
// Remove votes
$this->connect->beginTransaction();
$this->connect->deleteVotesByIndex($poll_id, $indexToDelete);
if (count($newMoments) > 0) {
$this->connect->updateSlot($poll_id, $datetime, implode(',', $newMoments));
} else {
$this->connect->deleteSlot($poll_id, $datetime);
}
$this->connect->commit();
return true;
}
public function deleteClassicSlot($poll_id, $slot_title) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll_id . ', slot:' . $slot_title);
$slots = $this->pollService->allSlotsByPollId($poll_id);
$index = 0;
$indexToDelete = -1;
// Search the index of the slot to delete
foreach ($slots as $aSlot) {
if ($slot_title == $aSlot->title) {
$indexToDelete = $index;
}
$index++;
}
// Remove votes
$this->connect->beginTransaction();
$this->connect->deleteVotesByIndex($poll_id, $indexToDelete);
$this->connect->deleteSlot($poll_id, $slot_title);
$this->connect->commit();
return true;
}
/**
* Add a new slot to the poll. And insert default values for user's votes.
* <ul>
* <li>Create a new slot if no one exists for the given date</li>
* <li>Create a new moment if a slot already exists for the given date</li>
* </ul>
*
* @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
*/
public function addSlot($poll_id, $datetime, $new_moment) {
$slots = $this->connect->allSlotsByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime, $new_moment);
// Begin transaction
$this->connect->beginTransaction();
if ($result == null) {
// The moment already exists
return false;
} elseif ($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;
}
// Update found slot
$moments[] = $new_moment;
sort($moments);
$this->connect->updateSlot($poll_id, $datetime, implode(',', $moments));
} else {
$this->connect->insertSlot($poll_id, $datetime, $new_moment);
}
$this->connect->insertDefaultVote($poll_id, $result->insert);
// Commit transaction
$this->connect->commit();
return true;
}
/**
* This method find where to insert a datatime+moment into a list of slots.<br/>
* Return the {insert:X}, where X is the index of the moment into the whole poll (ex: X=0 => Insert to the first column).
* Return {slot:Y}, where Y is not null if there is a slot existing for the given datetime.
*
* @param $slots array All the slots of the poll
* @param $datetime int The datetime of the new slot
* @param $moment string The moment's name
* @return null|\stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/
private function findInsertPosition($slots, $datetime, $moment) {
$result = new \stdClass();
$result->slot = null;
$result->insert = -1;
$i = 0;
foreach ($slots as $slot) {
$rowDatetime = $slot->title;
$moments = explode(',', $slot->moments);
if ($datetime == $rowDatetime) {
$result->slot = $slot;
foreach ($moments as $rowMoment) {
$strcmp = strcmp($moment, $rowMoment);
if ($strcmp < 0) {
// Here we have to insert at First place or middle of the slot
break(2);
} elseif ($strcmp == 0) {
// Here we dont have to insert at all
return null;
}
$i++;
}
// Here we have to insert at the end of a slot
$result->insert = $i;
break;
} elseif ($datetime < $rowDatetime) {
// Here we have to insert a new slot
break;
} else {
$i += count($moments);
}
}
$result->insert = $i;
return $result;
}
}

View File

@ -0,0 +1,49 @@
<?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)
*/
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(array $arr, $type, $options = null) {
$newArr = [];
foreach($arr as $id=>$item) {
$item = filter_var($item, $type, $options);
if ($item !== false) {
$newArr[$id] = $item;
}
}
return $newArr;
}
function filterAllowedValues($value, array $allowedValues) {
return in_array($value, $allowedValues, true) ? $value : null;
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Framadate\Services;
/**
* This service provides a standard way to log some informations.
*
* @package Framadate\Services
*/
class LogService {
function __construct() {
}
/**
* Log a message to the log file.
*
* @param $tag string A tag is used to quickly found a message when reading log file
* @param $message string some message
*/
function log($tag, $message) {
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Framadate\Services;
class MailService {
private $smtp_allowed;
function __construct($smtp_allowed) {
$this->smtp_allowed = $smtp_allowed;
}
public function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
function send($to, $subject, $body, $param = '') {
if($this->smtp_allowed == true) {
mb_internal_encoding('UTF-8');
$subject = mb_encode_mimeheader(html_entity_decode($subject, ENT_QUOTES, 'UTF-8'), 'UTF-8', 'B', "\n", 9);
$encoded_app = mb_encode_mimeheader(NOMAPPLICATION, 'UTF-8', 'B', "\n", 6);
$size_encoded_app = (6 + strlen($encoded_app)) % 75;
$size_admin_email = strlen(ADRESSEMAILADMIN);
if (($size_encoded_app + $size_admin_email + 9) > 74) {
$folding = "\n";
} else {
$folding = '';
};
$from = sprintf("From: %s%s <%s>\n", $encoded_app, $folding, ADRESSEMAILADMIN);
$headers = $from;
$headers .= 'Reply-To: ' . ADRESSEMAILREPONSEAUTO . "\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Auto-Submitted:auto-generated\n";
$headers .= 'Return-Path: <>';
$body = html_entity_decode($body, ENT_QUOTES, 'UTF-8') . _("\n--\n\n« La route est longue, mais la voie est libre… »\nFramasoft ne vit que par vos dons (déductibles des impôts).\nMerci d'avance pour votre soutien http://soutenir.framasoft.org.");
mail($to, $subject, $body, $headers, $param);
}
}
}

View File

@ -0,0 +1,190 @@
<?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)
*/
namespace Framadate\Services;
use Framadate\Form;
use Framadate\FramaDB;
use Framadate\Utils;
class PollService {
private $connect;
private $logService;
function __construct(FramaDB $connect, LogService $logService) {
$this->connect = $connect;
$this->logService = $logService;
}
/**
* Find a poll from its ID.
*
* @param $poll_id int The ID of the poll
* @return \stdClass|null The found poll, or null
*/
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 allVotesByPollId($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, $name, $choices) {
$choices = implode($choices);
return $this->connect->updateVote($poll_id, $vote_id, $name, $choices);
}
function addVote($poll_id, $name, $choices) {
$choices = implode($choices);
return $this->connect->insertVote($poll_id, $name, $choices);
}
function addComment($poll_id, $name, $comment) {
// TODO Check if there is no duplicate before to add a new comment
return $this->connect->insertComment($poll_id, $name, $comment);
}
public function countVotesByPollId($poll_id) {
return $this->connect->countVotesByPollId($poll_id);
}
function computeBestChoices($votes) {
$result = [];
foreach ($votes as $vote) {
$choices = str_split($vote->choices);
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) {
$obj = new \stdClass();
$obj->day = $slot->title;
$obj->moments = explode(',', $slot->moments);
$splitted[] = $obj;
}
return $splitted;
}
function splitVotes($votes) {
$splitted = array();
foreach ($votes as $vote) {
$obj = new \stdClass();
$obj->id = $vote->id;
$obj->name = $vote->name;
$obj->choices = str_split($vote->choices);
$splitted[] = $obj;
}
return $splitted;
}
/**
* @param Form $form
* @return string
*/
function createPoll(Form $form) {
// Generate poll IDs
$poll_id = $this->random(16);
$admin_poll_id = $poll_id . $this->random(8);
// Insert poll + slots
$this->connect->beginTransaction();
// TODO Extract this to FramaDB (or repository layer)
$sql = 'INSERT INTO ' . Utils::table('poll') . '
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments)
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?)';
$prepared = $this->connect->prepare($sql);
$prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments));
$prepared = $this->connect->prepare('INSERT INTO ' . Utils::table('slot') . ' (poll_id, title, moments) VALUES (?, ?, ?)');
foreach ($form->getChoices() as $choice) {
// We prepared the slots (joined by comas)
$joinedSlots = '';
$first = true;
foreach ($choice->getSlots() as $slot) {
if ($first) {
$joinedSlots = $slot;
$first = false;
} else {
$joinedSlots .= ',' . $slot;
}
}
// We execute the insertion
if (empty($joinedSlots)) {
$prepared->execute(array($poll_id, $choice->getName(), null));
} else {
$prepared->execute(array($poll_id, $choice->getName(), $joinedSlots));
}
}
$this->connect->commit();
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
return [$poll_id, $admin_poll_id];
}
private function random($car) {
// TODO Better random ?
$string = '';
$chaine = 'abcdefghijklmnopqrstuvwxyz123456789';
mt_srand();
for ($i = 0; $i < $car; $i++) {
$string .= $chaine[mt_rand() % strlen($chaine)];
}
return $string;
}
}

View File

@ -0,0 +1,69 @@
<?php
namespace Framadate\Services;
use Framadate\FramaDB;
/**
* This service helps to purge data.
*
* @package Framadate\Services
*/
class PurgeService {
private $connect;
private $logService;
function __construct(FramaDB $connect, LogService $logService) {
$this->connect = $connect;
$this->logService = $logService;
}
/**
* This methode purges all old polls (the ones with end_date in past).
*
* @return bool true is action succeeded
*/
function purgeOldPolls() {
$oldPolls = $this->connect->findOldPolls();
$count = count($oldPolls);
if ($count > 0) {
$this->logService->log('EXPIRATION', 'Going to purge ' . $count . ' poll(s)...');
foreach ($oldPolls as $poll) {
if ($this->purgePollById($poll->id)) {
$this->logService->log('EXPIRATION_SUCCESS', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: '.$poll->format . ', admin: ' . $poll->admin_name);
} else {
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: '.$poll->format . ', admin: ' . $poll->admin_name);
}
}
}
return $count;
}
/**
* This methode delete all data about a poll.
*
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
function purgePollById($poll_id) {
$done = true;
$this->connect->beginTransaction();
$done &= $this->connect->deleteCommentsByPollId($poll_id);
$done &= $this->connect->deleteVotesByPollId($poll_id);
$done &= $this->connect->deleteSlotsByPollId($poll_id);
$done &= $this->connect->deletePollById($poll_id);
if ($done) {
$this->connect->commit();
} else {
$this->connect->rollback();
}
return $done;
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Framadate\Services;
use Framadate\Security\Token;
class SecurityService {
function __construct() {
}
/**
* Get a CSRF token by name, or (re)create it.
*
* It creates a new token if :
* <ul>
* <li>There no token with the given name in session</li>
* <li>The token time is in past</li>
* </ul>
*
* @param $tokan_name string The name of the CSRF token
* @return Token The token
*/
function getToken($tokan_name) {
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens'] = [];
}
if (!isset($_SESSION['tokens'][$tokan_name]) || $_SESSION['tokens'][$tokan_name]->isGone()) {
$_SESSION['tokens'][$tokan_name] = new Token();
}
return $_SESSION['tokens'][$tokan_name]->getValue();
}
/**
* Check if a given value is corresponding to the token in session.
*
* @param $tokan_name string Name of the token
* @param $csrf string Value to check
* @return bool true if the token is well checked
*/
public function checkCsrf($tokan_name, $csrf) {
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
if($checked) {
unset($_SESSION['tokens'][$tokan_name]);
}
return $checked;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Framadate\Services;
use Framadate\FramaDB;
/**
* The class provides action for application administrators.
*
* @package Framadate\Services
*/
class SuperAdminService {
private $connect;
function __construct(FramaDB $connect) {
$this->connect = $connect;
}
/**
* Return the list of all polls.
*
* @param $page int The page index (O = first page)
* @param $limit int The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Total count]
* polls, 'count' => Total count]
*/
public function findAllPolls($page, $limit) {
return $this->connect->findAllPolls($page * $limit, $limit);
}
}

View File

@ -18,45 +18,21 @@
*/
namespace Framadate;
class Utils
{
public static function get_server_name()
{
$scheme = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
$port = in_array($_SERVER['SERVER_PORT'], [80, 443]) ? '/' : ':' . $_SERVER['SERVER_PORT'] . '/';
$server_name = $_SERVER['SERVER_NAME'] . $port . dirname($_SERVER['SCRIPT_NAME']) . '/';
class Utils {
/**
* @return string Server name
*/
public static function get_server_name() {
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
$port = in_array($_SERVER['SERVER_PORT'], [80, 443]) ? '' : ':' . $_SERVER['SERVER_PORT'];
$dirname = dirname($_SERVER['SCRIPT_NAME']);
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
$server_name = $_SERVER['SERVER_NAME'] . $port . $dirname;
return $scheme . '://' . str_replace('/admin','',str_replace('//','/',str_replace('///','/',$server_name)));
return $scheme . '://' . str_replace('/admin', '', str_replace('//', '/', str_replace('///', '/', $server_name)));
}
public static function get_sondage_from_id($id)
{
global $connect;
// Open database
if (preg_match(';^[\w\d]{16}$;i', $id)) {
$sql = 'SELECT sondage.*,sujet_studs.sujet FROM sondage
LEFT OUTER JOIN sujet_studs ON sondage.id_sondage = sujet_studs.id_sondage
WHERE sondage.id_sondage = ' . $connect->Param('id_sondage');
$sql = $connect->Prepare($sql);
$sondage = $connect->Execute($sql, [$id]);
if ($sondage === false) {
return false;
}
$psondage = $sondage->FetchObject(false);
$psondage->date_fin = strtotime($psondage->date_fin);
return $psondage;
}
return false;
}
public static function is_error($cerr)
{
public static function is_error($cerr) {
global $err;
if ($err == 0) {
return false;
@ -65,38 +41,40 @@ class Utils
return ($err & $cerr) != 0;
}
public static function is_user()
{
public static function is_user() {
return (USE_REMOTE_USER && isset($_SERVER['REMOTE_USER'])) || isset($_SESSION['nom']);
}
public static function print_header($title = '')
{
global $lang;
/**
* @param string $title
* @deprecated
*/
public static function print_header($title = '') {
global $html_lang;
echo '<!DOCTYPE html>
<html lang="'.$lang.'">
<html lang="' . $html_lang . '">
<head>
<meta charset="utf-8">';
<meta charset="utf-8" />';
if (! empty($title)) {
if (!empty($title)) {
echo '<title>' . stripslashes($title) . ' - ' . NOMAPPLICATION . '</title>';
} else {
echo '<title>' . NOMAPPLICATION . '</title>';
}
echo '
<link rel="stylesheet" href="' . self::get_server_name() . 'css/bootstrap.min.css">
<link rel="stylesheet" href="' . self::get_server_name() . 'css/datepicker3.css">
<link rel="stylesheet" href="' . self::get_server_name() . 'css/style.css">
<link rel="stylesheet" href="' . self::get_server_name() . 'css/frama.css">
<link rel="stylesheet" href="' . self::get_server_name() . 'css/print.css" media="print">
<link rel="stylesheet" href="' . self::get_server_name() . 'css/bootstrap.min.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/datepicker3.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/style.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/frama.css" />
<link rel="stylesheet" href="' . self::get_server_name() . 'css/print.css" media="print" />
<script type="text/javascript" src="' . self::get_server_name() . 'js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.'.$lang.'.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $html_lang . '.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/core.js"></script>';
if (file_exists($_SERVER['DOCUMENT_ROOT']."/nav/nav.js")) {
if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/nav/nav.js")) {
echo '<script src="/nav/nav.js" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->';
}
@ -104,107 +82,26 @@ class Utils
</head>
<body>
<div class="container ombre">';
}
public static function check_table_sondage()
{
global $connect;
if (in_array('sondage', $connect->MetaTables('TABLES'))) {
return true;
}
return false;
}
/**
* Check if an email address is valid using PHP filters
*
* @param string $email Email address to check
* @param string $email Email address to check
* @return bool True if valid. False if not valid.
* @deprecated
*/
public static function isValidEmail($email)
{
public static function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
/**
* Envoi un courrier avec un codage correct de To et Subject
* Les en-têtes complémentaires ne sont pas gérés
*
*/
public static function sendEmail( $to, $subject, $body, $headers='', $param='')
{
mb_internal_encoding('UTF-8');
$subject = mb_encode_mimeheader(html_entity_decode($subject, ENT_QUOTES, 'UTF-8'), 'UTF-8', 'B', "\n", 9);
$encoded_app = mb_encode_mimeheader(NOMAPPLICATION, 'UTF-8', 'B', "\n", 6);
$size_encoded_app = (6 + strlen($encoded_app)) % 75;
$size_admin_email = strlen(ADRESSEMAILADMIN);
if (($size_encoded_app + $size_admin_email + 9) > 74 ) {
$folding = "\n";
} else {
$folding = '';
};
/*
Si $headers ne contient qu'une adresse email, on la considère comme
adresse de reply-to, sinon on met l'adresse de no-reply definie
dans constants.php
*/
if (self::isValidEmail($headers)) {
$replyTo = $headers;
$headers = ''; // on reinitialise $headers
} else {
$replyTo = ADRESSEMAILREPONSEAUTO;
}
$from = sprintf( "From: %s%s <%s>\n", $encoded_app, $folding, ADRESSEMAILADMIN);
if ($headers) {
$headers .= "\n" ;
}
$headers .= $from;
$headers .= "Reply-To: $replyTo\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/plain; charset=UTF-8\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Auto-Submitted:auto-generated\n";
$headers .= "Return-Path: <>";
$body = html_entity_decode($body, ENT_QUOTES, 'UTF-8')._("\n--\n\n« La route est longue, mais la voie est libre… »\nFramasoft ne vit que par vos dons (déductibles des impôts).\nMerci d'avance pour votre soutien http://soutenir.framasoft.org.");
mail($to, $subject, $body, $headers, $param);
}
/**
* Fonction vérifiant l'existance et la valeur non vide d'une clé d'un tableau
* @param string $name La clé à tester
* @param array $tableau Le tableau rechercher la clé ($_POST par défaut)
* @return bool Vrai si la clé existe et renvoie une valeur non vide
*/
public static function issetAndNoEmpty($name, $tableau = null)
{
if (is_null($tableau)) {
$tableau = $_POST;
}
return isset($tableau[$name]) && ! empty($tableau[$name]);
}
/**
* Fonction permettant de générer les URL pour les sondage
* @param string $id L'identifiant du sondage
* @param bool $admin True pour générer une URL pour l'administration d'un sondage, False pour un URL publique
* @param string $id L'identifiant du sondage
* @param bool $admin True pour générer une URL pour l'administration d'un sondage, False pour un URL publique
* @return string L'url pour le sondage
*/
public static function getUrlSondage($id, $admin = false)
{
public static function getUrlSondage($id, $admin = false) {
if (URL_PROPRE) {
if ($admin === true) {
$url = str_replace('/admin', '', self::get_server_name()) . $id . '/admin';
@ -213,52 +110,75 @@ class Utils
}
} else {
if ($admin === true) {
$url = str_replace('/admin', '', self::get_server_name()) . 'adminstuds.php?sondage=' . $id;
$url = str_replace('/admin', '', self::get_server_name()) . 'adminstuds.php?poll=' . $id;
} else {
$url = str_replace('/admin', '', self::get_server_name()) . 'studs.php?sondage=' . $id;
$url = str_replace('/admin', '', self::get_server_name()) . 'studs.php?poll=' . $id;
}
}
return $url;
}
public static function remove_sondage($connect, $numsondage)
{
$connect->StartTrans();
$req = 'DELETE FROM sondage WHERE id_sondage = ' . $connect->Param('numsondage') ;
$sql = $connect->Prepare($req);
$connect->Execute($sql, [$numsondage]);
$req = 'DELETE FROM sujet_studs WHERE id_sondage = ' . $connect->Param('numsondage') ;
$sql = $connect->Prepare($req);
$connect->Execute($sql, [$numsondage]);
$req = 'DELETE FROM user_studs WHERE id_sondage = ' . $connect->Param('numsondage') ;
$sql = $connect->Prepare($req);
$connect->Execute($sql, [$numsondage]);
$req = 'DELETE FROM comments WHERE id_sondage = ' . $connect->Param('numsondage') ;
$sql = $connect->Prepare($req);
$connect->Execute($sql, [$numsondage]);
$suppression_OK = ! $connect->HasFailedTrans();
$connect->CompleteTrans();
return $suppression_OK ;
/**
* This method pretty prints an object to the page framed by pre tags.
*
* @param mixed $object The object to print.
*/
public static function debug($object) {
echo '<pre>';
print_r($object);
echo '</pre>';
}
public static function cleaning_polls($connect, $log_txt) {
$connect->StartTrans();
$req = 'SELECT * FROM sondage WHERE date_fin < NOW() AND date_fin != 0 LIMIT 20';
$sql = $connect->Prepare($req);
$cleaning = $connect->Execute($sql);
public static function table($tableName) {
return TABLENAME_PREFIX . $tableName;
}
public static function markdown($md, $clear) {
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $md, $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $md, $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $md, $md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
$text = stripslashes($md_a_img[1][0]);
$html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
$text = stripslashes($md_img[1][0]);
$html = '<img src="' . $md_img[2][0] . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" />';
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
$text = stripslashes($md_a[1][0]);
$html = '<a href="' . $md_a[2][0] . '">' . $text . '</a>';
} else { // text only
$text = stripslashes($md);
$html = $text;
while ($dcleaning = $cleaning->FetchNextObject(false)) {
if (self::remove_sondage($connect, $dcleaning->id_sondage)) {
error_log(date('H:i:s d/m/Y:') . ' EXPIRATION: '. $dcleaning->id_sondage."\t".$dcleaning->format."\t".$dcleaning->nom_admin."\t".$dcleaning->mail_admin."\n", 3, $log_txt);
}
}
$connect->CompleteTrans();
return $clear ? $text : $html;
}
public static function htmlEscape($html) {
return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
}
public static function csvEscape($text) {
$escaped = str_replace('"', '""', $text);
$escaped = str_replace("\r\n", '', $escaped);
$escaped = str_replace("\n", '', $escaped);
return '"' . $escaped . '"';
}
public static function cleanFilename($title) {
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
$cleaned = preg_replace(' {2,}', ' ', $cleaned);
return $cleaned;
}
}

View File

@ -17,14 +17,8 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// FRAMADATE version
const VERSION = 0.8;
// Server name
const STUDS_URL = '<www.mydomain.com>';
// Application name
const NOMAPPLICATION = "<Application name>";
const NOMAPPLICATION = '<Application name>';
// Database administrator email
const ADRESSEMAILADMIN = '<email address>';
@ -32,20 +26,20 @@ const ADRESSEMAILADMIN = '<email address>';
// Email for automatic responses (you should set it to "no-reply")
const ADRESSEMAILREPONSEAUTO = '<no-reply@mydomain.com>';
// Database name
const BASE = '<database name>';
// Database user
const USERBASE = "<database user>";
const DB_USER= '<database user>';
// Database password
const USERPASSWD = '<database password>';
const DB_PASSWORD = '<database password>';
// Database server name, leave empty to use a socket
const SERVEURBASE = '<database server>';
const DB_CONNECTION_STRING = 'mysql:host=<database host>;dbname=<database name>;port=<database port>';
// Database type (mysql, postgres…) http://phplens.com/lens/adodb/docs-adodb.htm#drivers
const BASE_TYPE = '<database type>';
// 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 LANGUE = 'fr_FR';
@ -73,16 +67,11 @@ const URL_PROPRE = false;
// Use REMOTE_USER data provided by web server
const USE_REMOTE_USER = true;
const COMMENT_EMPTY = 0x0000000001;
const COMMENT_USER_EMPTY = 0x0000000010;
const COMMENT_INSERT_FAILED = 0x0000000100;
const NAME_EMPTY = 0x0000001000;
const NAME_TAKEN = 0x0000010000;
const NO_POLL = 0x0000100000;
const NO_POLL_ID = 0x0001000000;
const INVALID_EMAIL = 0x0010000000;
const TITLE_EMPTY = 0x0100000000;
const INVALID_DATE = 0x1000000000;
// 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 = [
@ -93,8 +82,8 @@ $config = [
'show_the_software' => true, // display technical information about the software
'show_cultivate_your_garden' => true, // display "developpement and administration" information
/* choix_autre.php / choix_date.php */
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
/* choix_autre.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.
];

42
app/inc/constants.php Normal file
View File

@ -0,0 +1,42 @@
<?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)
*/
// FRAMADATE version
const VERSION = '0.9.1';
// Regex
const POLL_REGEX = '/^[a-z0-9]+$/';
const CHOICE_REGEX = '/^[012]$/';
const NAME_REGEX = '/^[áàâäãåçéèêëíìîïñóòôöõúùûüýÿæœa-z0-9_ -]+$/i';
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/';
// CSRF (300s = 5min)
const TOKEN_TIME = 300;
// Errors
const COMMENT_EMPTY = 0x0000000001;
const COMMENT_USER_EMPTY = 0x0000000010;
const COMMENT_INSERT_FAILED = 0x0000000100;
const NAME_EMPTY = 0x0000001000;
const NAME_TAKEN = 0x0000010000;
const NO_POLL = 0x0000100000;
const NO_POLL_ID = 0x0001000000;
const INVALID_EMAIL = 0x0010000000;
const TITLE_EMPTY = 0x0100000000;
const INVALID_DATE = 0x1000000000;

View File

@ -19,60 +19,62 @@
asort($ALLOWED_LANGUAGES);
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES)) ) {
$mlocale = $_POST['lang'] ;
setcookie('lang' , $_POST['lang'], time()+60*5);
} elseif ( isset($_COOKIE['lang']) && is_string($_COOKIE['lang']) && in_array($_COOKIE['lang'], array_keys($ALLOWED_LANGUAGES)) ) {
$mlocale = $_COOKIE['lang'] ;
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES))) {
$mlocale = $_POST['lang'];
$_SESSION['lang'] = $_POST['lang'];
} elseif (isset($_SESSION['lang']) && is_string($_SESSION['lang']) && in_array($_SESSION['lang'], array_keys($ALLOWED_LANGUAGES))) {
$mlocale = $_SESSION['lang'];
} else {
$mlocale = LANGUE;
// Replace config language by browser language if possible
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
if (substr($k,0,2)==substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2)) {
foreach ($ALLOWED_LANGUAGES as $k => $v) {
if (substr($k, 0, 2) == substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2)) {
$mlocale = $k;
break;
}
}
}
$locale = $mlocale . '.utf8';//unix format
if (strtoupper(substr(PHP_OS,0,3))=='WIN'){
putenv("LC_ALL=$mlocale"); //Windows env. needed to switch between languages
switch ($mlocale){
case 'fr_FR' : $locale = "fra";break; //$locale in windows locale format, needed to use php function that handle text : strftime()
case 'en_GB' : $locale = "english";break; //see http://msdn.microsoft.com/en-us/library/39cwe7zf%28v=vs.90%29.aspx
case 'de_DE' : $locale = "deu";break;
case 'es_ES' : $locale = "esp";break;
}
}
putenv('LANGUAGE=');//sert à quoi?
setlocale(LC_ALL, $locale);
setlocale(LC_TIME, $locale);
setlocale(LC_MESSAGES, $locale);
/* Tell PHP which locale to use */
$domain = 'Studs';
bindtextdomain($domain, 'locale');
$locale = $mlocale . '.utf8'; //unix format
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
putenv("LC_ALL=$mlocale"); //Windows env. needed to switch between languages
switch ($mlocale) {
case 'fr_FR' :
$locale = "fra";
break; //$locale in windows locale format, needed to use php function that handle text : strftime()
case 'en_GB' :
$locale = "english";
break; //see http://msdn.microsoft.com/en-us/library/39cwe7zf%28v=vs.90%29.aspx
case 'de_DE' :
$locale = "deu";
break;
case 'es_ES' :
$locale = "esp";
break;
}
}
putenv('LANG=' . $locale);
setlocale(LC_ALL, $locale);
bindtextdomain($domain, ROOT_DIR . 'locale');
bind_textdomain_codeset($domain, 'UTF-8');
textdomain($domain);
/* temp, for compatibility :*/
$a = explode('_', $mlocale);
$_SESSION['langue'] = strtoupper($a[0]);
/* <html lang="$lang"> */
$lang = ($_SESSION['langue']!='') ? strtolower($_SESSION['langue']) : 'fr';
/* <html lang="$html_lang"> */
$html_lang = substr($locale, 0, 2);
/* Date Format */
$date_format['txt_full'] = _("%A, den %e. %B %Y"); //summary in choix_date.php and removal date in choix_(date|autre).php
$date_format['txt_short'] = "%A %e %B %Y"; // radio title
$date_format['txt_day'] = "%a %e";
if (strtoupper(substr(PHP_OS,0,3))=='WIN'){ //%e can't be used on Windows platform, use %#d instead
foreach($date_format as $k => $v) {
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#','\1%#d', $v); //replace %e by %#d for windows
$date_format['txt_full'] = _('%A, den %e. %B %Y'); //summary in choix_date.php and removal date in choix_(date|autre).php
$date_format['txt_short'] = _('%A %e %B %Y'); // radio title
$date_format['txt_day'] = _('%a %e');
$date_format['txt_date'] = _('%Y-%m-%d');
$date_format['txt_year_month'] = _('%B %Y');
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { //%e can't be used on Windows platform, use %#d instead
foreach ($date_format as $k => $v) {
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
}
}

View File

@ -16,16 +16,28 @@
* 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\FramaDB;
// Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php';
if (session_id() == '') {
session_start();
}
if (ini_get('date.timezone') == '') {
date_default_timezone_set('Europe/Paris');
}
// Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php';
include_once __DIR__ . '/constants.php';
include_once __DIR__ . '/i18n.php';
define('ROOT_DIR', __DIR__ . '/../../');
$connect = NewADOConnection(BASE_TYPE);
$connect->Connect(SERVEURBASE, USERBASE, USERPASSWD, BASE);
require_once __DIR__ . '/constants.php';
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/i18n.php';
// Smarty
require_once __DIR__ . '/smarty.php';
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
$err = 0;

51
app/inc/smarty.php Normal file
View File

@ -0,0 +1,51 @@
<?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\Utils;
require_once __DIR__ . '/../../vendor/smarty/smarty/libs/Smarty.class.php';
$smarty = new \Smarty();
$smarty->setTemplateDir(ROOT_DIR . '/tpl/');
$smarty->setCompileDir(ROOT_DIR . '/tpl_c/');
$smarty->setCacheDir(ROOT_DIR . '/cache/');
$smarty->caching = false;
$smarty->assign('APPLICATION_NAME', NOMAPPLICATION);
$smarty->assign('SERVER_URL', Utils::get_server_name());
$smarty->assign('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']);
$smarty->assign('TITLE_IMAGE', IMAGE_TITRE);
$smarty->assign('use_nav_js', file_exists($_SERVER['DOCUMENT_ROOT'] . '/nav/nav.js'));
$smarty->assign('html_lang', $html_lang);
$smarty->assign('langs', $ALLOWED_LANGUAGES);
$smarty->assign('date_format', $date_format);
function smarty_modifier_poll_url($poll_id, $admin = false) {
return Utils::getUrlSondage($poll_id, $admin);
}
function smarty_modifier_markdown($md, $clear = false) {
return Utils::markdown($md, $clear);
}
function smarty_modifier_resource($link) {
return Utils::get_server_name() . $link;
}
function smarty_modifier_html($html) {
return Utils::htmlEscape($html);
}

View File

@ -16,7 +16,7 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
@ -28,7 +28,7 @@ function bandeau_titre($titre)
echo '
<header role="banner">';
if(count($ALLOWED_LANGUAGES)>1){
echo '<form method="post" action="#">
echo '<form method="post" action="" class="hidden-print">
<div class="input-group input-group-sm pull-right col-md-2 col-xs-4">
<select name="lang" class="form-control" title="'. _("Select the language") .'" >' . liste_lang() . '</select>
<span class="input-group-btn">
@ -43,16 +43,26 @@ function bandeau_titre($titre)
<hr class="trait" role="presentation" />
</header>
<main role="main">';
global $connect;
$tables = $connect->allTables();
$diff = array_diff([Utils::table('comment'), Utils::table('poll'), Utils::table('slot'), Utils::table('vote')], $tables);
if (0 != count($diff)) {
echo '<div class="alert alert-danger">'. _('Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') .'</div>';
bandeau_pied();
die();
}
}
function liste_lang()
{
global $ALLOWED_LANGUAGES; global $lang;
global $ALLOWED_LANGUAGES; global $html_lang;
$str = '';
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
if (substr($k,0,2)==$lang) {
if (substr($k,0,2)==$html_lang) {
$str .= '<option lang="'.substr($k,0,2).'" selected value="' . $k . '">' . $v . '</option>' . "\n" ;
} else {
$str .= '<option lang="'.substr($k,0,2).'" value="' . $k . '">' . $v . '</option>' . "\n" ;

View File

@ -16,10 +16,21 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\MailService;
use Framadate\Services\PurgeService;
use Framadate\Utils;
use Framadate\Choice;
session_start();
include_once('creation_sondage.php');
include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp']);
$purgeService = new PurgeService($connect, $logService);
if (file_exists('bandeaux_local.php')) {
include_once('bandeaux_local.php');
@ -27,151 +38,190 @@ if (file_exists('bandeaux_local.php')) {
include_once('bandeaux.php');
}
// Step 1/3 : error if $_SESSION from info_sondage are not valid
if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmpty('nom', $_SESSION) === false || (($config['use_smtp']) ? Utils::issetAndNoEmpty('adresse', $_SESSION) === false : false)) {
// Step 1/4 : error if $_SESSION from info_sondage are not valid
if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (($config['use_smtp']) ? empty($_SESSION['form']->admin_mail) : false)) {
Utils::print_header ( _("Error!") );
Utils::print_header(_("Error!"));
bandeau_titre(_("Error!"));
echo '
<div class="alert alert-danger">
<h3>' . _("You haven't filled the first section of the poll creation.") . ' !</h3>
<p>' . _("Back to the homepage of ") . ' <a href="' . Utils::get_server_name() . '"> ' . NOMAPPLICATION . '</a></p>
</div>'."\n";
<h3>' . _('You haven\'t filled the first section of the poll creation.') . ' !</h3>
<p>' . _('Back to the homepage of') . ' <a href="' . Utils::get_server_name() . '"> ' . NOMAPPLICATION . '</a></p>
</div>' . "\n";
bandeau_pied();
} else {
$min_time = time() + 86400;
$max_time = time() + (86400 * $config['default_poll_duration']);
// Step 4 : Data prepare before insert in DB
if (isset($_POST["confirmecreation"])) {
//recuperation des données de champs textes
$temp_results = '';
if (isset($_SESSION['choices'])) {
for ($i = 0; $i < count($_SESSION['choices']); $i++) {
if ($_SESSION['choices'][$i]!="") {
$temp_results.=','.str_replace(",", " ", htmlentities(html_entity_decode($_SESSION['choices'][$i], ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8'));
if (isset($_POST['confirmecreation'])) {
// Define expiration date
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
$min_time = time() + (24 * 60 * 60);
$max_time = time() + (86400 * $config['default_poll_duration']);
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
if (is_array($registredate) && count($registredate) == 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
if ($time < $min_time) {
$_SESSION['form']->end_date = $min_time;
} elseif ($max_time < $time) {
$_SESSION['form']->end_date = $max_time;
} else {
$_SESSION['form']->end_date = $time;
}
}
}
$temp_results=substr($temp_results,1);
$_SESSION["toutchoix"]=$temp_results;
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_time;
}
// format du sondage AUTRE
$_SESSION['form']->format = 'A';
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
if (Utils::issetAndNoEmpty('champdatefin')) {
$registredate = explode("/",$_POST["champdatefin"]);
if (is_array($registredate) == true && count($registredate) == 3) {
$time = mktime(0,0,0,$registredate[1],$registredate[0],$registredate[2]);
if ($time > time() + (24*60*60)) {
$_SESSION["champdatefin"]=$time;
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = _("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.");
$message .= "\n\n";
$message .= stripslashes(html_entity_decode($_SESSION['form']->admin_name, ENT_QUOTES, "UTF-8")) . ' ' . _('hast just created a poll called') . ' : "' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)) . "\".\n";
$message .= _('Thanks for filling the poll at the link above') . " :\n\n%s\n\n" . _('Thanks for your confidence.') . "\n" . NOMAPPLICATION;
$message_admin = _("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");
$message_admin .= " :\n\n" . "%s \n\n" . _('Thanks for your confidence.') . "\n" . NOMAPPLICATION;
$message = sprintf($message, Utils::getUrlSondage($poll_id));
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . _('Author\'s message') . '] ' . _('Poll') . ' : ' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)), $message_admin);
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . _('For sending to the polled users') . '] ' . _('Poll') . ' : ' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)), $message);
}
}
// Clean Form data in $_SESSION
unset($_SESSION['form']);
// Delete old polls
$purgeService->purgeOldPolls();
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
exit;
} // Step 3/4 : Confirm poll creation and choose a removal date
else if (isset($_POST['fin_sondage_autre'])) {
Utils::print_header(_('Removal date and confirmation (3 on 3)'));
bandeau_titre(_('Removal date and confirmation (3 on 3)'));
// Store choices in $_SESSION
if (isset($_POST['choices'])) {
$_SESSION['form']->clearChoices();
foreach ($_POST['choices'] as $c) {
if (!empty($c)) {
$c = strip_tags($c);
$choice = new Choice($c);
$_SESSION['form']->addChoice($choice);
}
}
}
//format du sondage AUTRE
$_SESSION["formatsondage"]="A".$_SESSION["studsplus"];
ajouter_sondage();
}
// recuperation des sujets pour sondage AUTRE
if (isset($_POST['choices'])) {
$k = 0;
for ($i = 0; $i < count($_POST['choices']); $i++) {
if (Utils::issetAndNoEmpty($i, $_POST['choices'])) {
$_SESSION['choices'][$k]=htmlentities(html_entity_decode($_POST['choices'][$i], ENT_QUOTES, 'UTF-8'), ENT_QUOTES, 'UTF-8');
$k++;
}
}
}
// Step 3/3 : Confirm poll creation and choose a removal date
if (isset($_POST["fin_sondage_autre"])) {
Utils::print_header ( _("Removal date and confirmation (3 on 3)") );
bandeau_titre(_("Removal date and confirmation (3 on 3)"));
// Expiration date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
$_SESSION["champdatefin"]= time()+ (86400 * $config['default_poll_duration']); //60 sec * 60 min * 24 hours * config
$removal_date= utf8_encode(strftime($date_format['txt_full'], ($_SESSION["champdatefin"])));//textual date
$_SESSION['form']->end_date = time() + (86400 * $config['default_poll_duration']); //60 sec * 60 min * 24 hours * config
// Summary
$summary = '<ol>';
for ($i=0;$i<count($_SESSION['choices']);$i++) {
foreach ($_SESSION['form']->getChoices() as $choice) {
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/',$_SESSION['choices'][$i],$md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/',$_SESSION['choices'][$i],$md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/',$_SESSION['choices'][$i],$md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0]!='' && isset($md_a_img[3][0]) && $md_a_img[3][0]!='') { // [![alt](src)](href)
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0]!='') ? stripslashes($md_a_img[1][0]) : _("Choice") .' '.($i+1);
$li_subject_html = '<a href="'.$md_a_img[3][0].'"><img src="'.$md_a_img[2][0].'" class="img-responsive" alt="'.$li_subject_text.'" /></a>';
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] != '') ? stripslashes($md_a_img[1][0]) : _('Choice') . ' ' . ($i + 1);
$li_subject_html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0]!='') { // ![alt](src)
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0]!='') ? stripslashes($md_img[1][0]) : _("Choice") .' '.($i+1);
$li_subject_html = '<img src="'.$md_img[2][0].'" class="img-responsive" alt="'.$li_subject_text.'" />';
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] != '') ? stripslashes($md_img[1][0]) : _('Choice') . ' ' . ($i + 1);
$li_subject_html = '<img src="' . $md_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" />';
} elseif (isset($md_a[2][0]) && $md_a[2][0]!='') { // [text](href)
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0]!='') ? stripslashes($md_a[1][0]) : _("Choice") .' '.($i+1);
$li_subject_html = '<a href="'.$md_a[2][0].'">'.$li_subject_text.'</a>';
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] != '') ? stripslashes($md_a[1][0]) : _('Choice') . ' ' . ($i + 1);
$li_subject_html = '<a href="' . $md_a[2][0] . '">' . $li_subject_text . '</a>';
} else { // text only
$li_subject_text = stripslashes($_SESSION['choices'][$i]);
$li_subject_text = stripslashes($choice->getName());
$li_subject_html = $li_subject_text;
}
$summary .= '<li>'.$li_subject_html.'</li>'."\n";
$summary .= '<li>' . $li_subject_html . '</li>' . "\n";
}
$summary .= '</ol>';
$end_date_str = utf8_encode(strftime('%d/%m/%Y', $max_time)); //textual date
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'choix_autre.php" method="POST" class="form-horizontal" role="form">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="well summary">
<h4>'. _("List of your choices").'</h4>
'. $summary .'
<h4>' . _('List of your choices') . '</h4>
' . $summary . '
</div>
<div class="alert alert-info">
<p>' . _("Your poll will be automatically removed after"). " " . $config['default_poll_duration'] . " " . _("days") . ': <strong>'.$removal_date.'</strong>.<br />' . _("You can fix another removal date for it.") .'</p>
<p>' . _('Your poll will be automatically removed after') . ' ' . $config['default_poll_duration'] . ' ' . _('days') . '.<br />' . _('You can set a closer removal date for it.') . '</p>
<div class="form-group">
<label for="champdatefin" class="col-sm-5 control-label">'. _("Removal date (optional)") .'</label>
<label for="enddate" class="col-sm-5 control-label">' . _('Removal date (optional)') . '</label>
<div class="col-sm-6">
<div class="input-group date">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar text-info"></i></span>
<input type="text" class="form-control" id="champdatefin" data-date-format="'. _("dd/mm/yyyy") .'" aria-describedby="dateformat" name="champdatefin" value="" size="10" maxlength="10" placeholder="'. _("dd/mm/yyyy") .'" />
<input type="text" class="form-control" id="enddate" data-date-format="' . _('dd/mm/yyyy') . '" aria-describedby="dateformat" name="enddate" value="' . $end_date_str . '" size="10" maxlength="10" placeholder="' . _("dd/mm/yyyy") . '" />
</div>
</div>
<span id="dateformat" class="sr-only">'. _("(dd/mm/yyyy)") .'</span>
<span id="dateformat" class="sr-only">' . _('(dd/mm/yyyy)') . '</span>
</div>
</div>
<div class="alert alert-warning">
<p>'. _("Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll."). '</p>';
if($config['use_smtp']==true){
<p>' . _('Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.') . '</p>';
if ($config['use_smtp'] == true) {
echo '
<p>' . _("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.") .'</p>';
<p>' . _('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.') . '</p>';
}
echo '
</div>
<p class="text-right">
<button class="btn btn-default" onclick="javascript:window.history.back();" title="'. _('Back to step 2') . '">'. _('Back') . '</button>
<button name="confirmecreation" value="confirmecreation" type="submit" class="btn btn-success">'. _('Create the poll') . '</button>
<button class="btn btn-default" onclick="javascript:window.history.back();" title="' . _('Back to step 2') . '">' . _('Back') . '</button>
<button name="confirmecreation" value="confirmecreation" type="submit" class="btn btn-success">' . _('Create the poll') . '</button>
</p>
</div>
</div>
</form>'."\n";
</form>' . "\n";
bandeau_pied();
// Step 2/3 : Select choices of the poll
// Step 2/4 : Select choices of the poll
} else {
Utils::print_header( _("Poll subjects (2 on 3)"));
bandeau_titre(_("Poll subjects (2 on 3)"));
Utils::print_header(_('Poll subjects (2 on 3)'));
bandeau_titre(_('Poll subjects (2 on 3)'));
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'choix_autre.php" method="POST" class="form-horizontal" role="form">
@ -179,40 +229,41 @@ if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmp
<div class="col-md-8 col-md-offset-2">';
echo '
<div class="alert alert-info">
<p>'. _("To make a generic poll, it's better to propose at least two choices between differents subjects.") .'</p>
<p>'. _("You can add or remove additional choices with the buttons") .' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _("Remove") .'</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _("Add") .'</span></p>';
if($config['user_can_add_img_or_link']){
echo ' <p>'. _("It's possible to propose links or images by using "). '<a href="http://'.$lang.'.wikipedia.org/wiki/Markdown">'. _("the Markdown syntax") .'</a>.</p>';
<p>' . _("To make a generic poll you need to propose at least two choices between differents subjects.") . '</p>
<p>' . _("You can add or remove additional choices with the buttons") . ' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . _("Remove") . '</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . _("Add") . '</span></p>';
if ($config['user_can_add_img_or_link']) {
echo ' <p>' . _("It's possible to propose links or images by using ") . '<a href="http://' . $html_lang . '.wikipedia.org/wiki/Markdown">' . _("the Markdown syntax") . '</a>.</p>';
}
echo ' </div>'."\n";
echo ' </div>' . "\n";
// Fields choices : 5 by default
$nb_choices = (isset($_SESSION['choices'])) ? max(count($_SESSION['choices']), 5) : 5;
$choices = $_SESSION['form']->getChoices();
$nb_choices = max(count($choices), 5);
for ($i = 0; $i < $nb_choices; $i++) {
$choice_value = (isset($_SESSION['choices'][$i])) ? str_replace("\\","",$_SESSION['choices'][$i]) : '';
$choice = isset($choices[$i]) ? $choices[$i] : new Choice();
echo '
<div class="form-group choice-field">
<label for="choice'.$i.'" class="col-sm-2 control-label">'. _("Choice") .' '.($i+1).'</label>
<label for="choice' . $i . '" class="col-sm-2 control-label">' . _('Choice') . ' ' . ($i + 1) . '</label>
<div class="col-sm-10 input-group">
<input type="text" class="form-control" name="choices[]" size="40" value="'.$choice_value.'" id="choice'.$i.'" />';
if($config['user_can_add_img_or_link']){
echo '<span class="input-group-addon btn-link md-a-img" title="'. _("Add a link or an image") .' - '. _("Choice") .' '.($i+1).'" ><span class="glyphicon glyphicon-picture"></span> <span class="glyphicon glyphicon-link"></span></span>';
}
<input type="text" class="form-control" name="choices[]" size="40" value="' . $choice->getName() . '" id="choice' . $i . '" />';
if ($config['user_can_add_img_or_link']) {
echo '<span class="input-group-addon btn-link md-a-img" title="' . _('Add a link or an image') . ' - ' . _('Choice') . ' ' . ($i + 1) . '" ><span class="glyphicon glyphicon-picture"></span> <span class="glyphicon glyphicon-link"></span></span>';
}
echo '
</div>
</div>'."\n";
</div>' . "\n";
}
echo '
<div class="col-md-4">
<div class="btn-group btn-group">
<button type="button" id="remove-a-choice" class="btn btn-default" title="'. _("Remove a choice") .'"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _("Remove") .'</span></button>
<button type="button" id="add-a-choice" class="btn btn-default" title="'. _("Add a choice") .'"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _("Add") .'</span></button>
<button type="button" id="remove-a-choice" class="btn btn-default" title="' . _('Remove a choice') . '"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . _('Remove') . '</span></button>
<button type="button" id="add-a-choice" class="btn btn-default" title="' . _('Add a choice') . '"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . _('Add') . '</span></button>
</div>
</div>
<div class="col-md-8 text-right">
<a class="btn btn-default" href="'.Utils::get_server_name().'infos_sondage.php?choix_sondage=autre" title="'. _('Back to step 1') . '">'. _('Back') . '</a>
<button name="fin_sondage_autre" value="'._('Next').'" type="submit" class="btn btn-success disabled" title="'. _('Go to step 3') . '">'. _('Next') . '</button>
<a class="btn btn-default" href="' . Utils::get_server_name() . 'infos_sondage.php?choix_sondage=autre" title="' . _('Back to step 1') . '">' . _('Back') . '</a>
<button name="fin_sondage_autre" value="' . _('Next') . '" type="submit" class="btn btn-success disabled" title="' . _('Go to step 3') . '">' . _('Next') . '</button>
</div>
</div>
</div>
@ -220,32 +271,32 @@ if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmp
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">'. _('Close') . '</span></button>
<p class="modal-title" id="md-a-imgModalLabel">'. _("Add a link or an image") .'</p>
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">' . _('Close') . '</span></button>
<p class="modal-title" id="md-a-imgModalLabel">' . _("Add a link or an image") . '</p>
</div>
<div class="modal-body">
<p class="alert alert-info">'. _("These fields are optional. You can add a link, an image or both.") .'</p>
<p class="alert alert-info">' . _("These fields are optional. You can add a link, an image or both.") . '</p>
<div class="form-group">
<label for="md-img"><span class="glyphicon glyphicon-picture"></span> '. _('URL of the image') . '</label>
<label for="md-img"><span class="glyphicon glyphicon-picture"></span> ' . _('URL of the image') . '</label>
<input id="md-img" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-a"><span class="glyphicon glyphicon-link"></span> '. _('Link') . '</label>
<label for="md-a"><span class="glyphicon glyphicon-link"></span> ' . _('Link') . '</label>
<input id="md-a" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-text">'. _('Alternative text') . '</label>
<label for="md-text">' . _('Alternative text') . '</label>
<input id="md-text" type="text" class="form-control" size="40" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">'. _('Cancel') . '</button>
<button type="button" class="btn btn-primary">'. _('Add') . '</button>
<button type="button" class="btn btn-default" data-dismiss="modal">' . _('Cancel') . '</button>
<button type="button" class="btn btn-primary">' . _('Add') . '</button>
</div>
</div>
</div>
</div>
</form>'."\n";
</form>' . "\n";
bandeau_pied();

View File

@ -5,7 +5,7 @@
* 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)
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
*
* =============================
*
@ -14,13 +14,25 @@
* 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)
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
*/
namespace Framadate;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\MailService;
use Framadate\Services\PurgeService;
use Framadate\Utils;
use Framadate\Choice;
session_start();
include_once __DIR__ . '/app/inc/init.php';
include_once('creation_sondage.php');
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp']);
$purgeService = new PurgeService($connect, $logService);
$inputService = new InputService();
if (is_readable('bandeaux_local.php')) {
include_once('bandeaux_local.php');
@ -28,145 +40,169 @@ if (is_readable('bandeaux_local.php')) {
include_once('bandeaux.php');
}
// Step 1/3 : error if $_SESSION from info_sondage are not valid
if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmpty('nom', $_SESSION) === false || (($config['use_smtp']) ? Utils::issetAndNoEmpty('adresse', $_SESSION) === false : false)) {
// Step 1/4 : error if $_SESSION from info_sondage are not valid
if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) || ($config['use_smtp'] && !isset($_SESSION['form']->admin_mail))) {
Utils::print_header ( _("Error!") );
bandeau_titre(_("Error!"));
Utils::print_header ( _('Error!') );
bandeau_titre(_('Error!'));
echo '
<div class="alert alter-danger">
<h3>' . _("You haven't filled the first section of the poll creation.") . ' !</h3>
<p>' . _("Back to the homepage of ") . ' ' . '<a href="' . Utils::get_server_name() . '">' . NOMAPPLICATION . '</a>.</p>
<h3>' . _('You haven\'t filled the first section of the poll creation.') . ' !</h3>
<p>' . _('Back to the homepage of ') . ' ' . '<a href="' . Utils::get_server_name() . '">' . NOMAPPLICATION . '</a>.</p>
</div>';
bandeau_pied();
} else {
$min_time = time() + 86400;
$max_time = time() + (86400 * $config['default_poll_duration']);
// Step 4 : Data prepare before insert in DB
if (Utils::issetAndNoEmpty('confirmation')) {
$temp_results = array();
$choixdate='';
if (Utils::issetAndNoEmpty('totalchoixjour', $_SESSION) === true) {
for ($i = 0; $i < count($_SESSION["totalchoixjour"]); $i++) {
if(count($_SESSION['horaires'.$i])!=0) {
for ($j=0;$j< min(count($_SESSION['horaires'.$i]),12);$j++) {
if ($_SESSION['horaires'.$i][$j]!="") {
array_push($temp_results, $_SESSION["totalchoixjour"][$i].'@'.$_SESSION['horaires'.$i][$j]);
} else {
array_push($temp_results, $_SESSION["totalchoixjour"][$i]);
}
}
if (!empty($_POST['confirmation'])) {
// Define expiration date
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
if (is_array($registredate) && count($registredate) == 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
if ($time < $min_time) {
$_SESSION['form']->end_date = $min_time;
} elseif ($max_time < $time) {
$_SESSION['form']->end_date = $max_time;
} else {
array_push($temp_results, $_SESSION["totalchoixjour"][$i]);
}
}
}
// Sort and remove doublons
$temp_results = array_unique($temp_results);
sort($temp_results);
for ($i=0;$i<count($temp_results);$i++) {
if (isset($temp_results[$i])) {
$choixdate.=','.$temp_results[$i];
}
}
$_SESSION["toutchoix"]=substr($choixdate,1);
// Expiration date → 6 months after last day if not filled or in bad format
$_SESSION["champdatefin"]=end($temp_results)+(86400 * $config['default_poll_duration']);
if (Utils::issetAndNoEmpty('champdatefin')) {
$registredate = explode("/",$_POST["champdatefin"]);
if (is_array($registredate) == true && count($registredate) == 3) {
$time = mktime(0,0,0,$registredate[1],$registredate[0],$registredate[2]);
if ($time > time() + (24*60*60)) {
$_SESSION["champdatefin"]=$time;
$_SESSION['form']->end_date = $time;
}
}
}
ajouter_sondage();
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_time;
}
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = _("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.");
$message .= "\n\n";
$message .= stripslashes(html_entity_decode($_SESSION['form']->admin_name, ENT_QUOTES, 'UTF-8')) . ' ' . _("hast just created a poll called") . ' : "' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)) . "\".\n";
$message .= _('Thanks for filling the poll at the link above') . " :\n\n%s\n\n" . _('Thanks for your confidence.') . "\n" . NOMAPPLICATION;
$message_admin = _("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");
$message_admin .= " :\n\n" . "%s \n\n" . _('Thanks for your confidence.') . "\n" . NOMAPPLICATION;
$message = sprintf($message, Utils::getUrlSondage($poll_id));
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . _('Author\'s message') . '] ' . _('Poll') . ' : ' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)), $message_admin);
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . _('For sending to the polled users') . '] ' . _('Poll') . ' : ' . stripslashes(htmlspecialchars_decode($_SESSION['form']->title, ENT_QUOTES)), $message);
}
}
// Clean Form data in $_SESSION
unset($_SESSION['form']);
// Delete old polls
$purgeService->purgeOldPolls();
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
exit;
} else {
if (Utils::issetAndNoEmpty('days')) {
if (!isset($_SESSION["totalchoixjour"])) {
$_SESSION["totalchoixjour"]=array();
}
$k = 0;
for ($i = 0; $i < count($_POST["days"]); $i++) {
if (isset($_POST["days"][$i]) && $_POST["days"][$i] !='') {
$_SESSION['totalchoixjour'][$k] = mktime(0, 0, 0, substr($_POST["days"][$i],3,2),substr($_POST["days"][$i],0,2),substr($_POST["days"][$i],6,4));
$l = 0;
for($j = 0; $j < count($_POST['horaires'.$i]); $j++) {
if (isset($_POST['horaires'.$i][$j]) && $_POST['horaires'.$i][$j] != '') {
$_SESSION['horaires'.$k][$l] = $_POST['horaires'.$i][$j];
$l++;
if (!empty($_POST['days'])) {
// Clear previous choices
$_SESSION['form']->clearChoices();
for ($i = 0; $i < count($_POST['days']); $i++) {
$day = $_POST['days'][$i];
if (!empty($day)) {
// Add choice to Form data
$time = mktime(0, 0, 0, substr($_POST["days"][$i],3,2),substr($_POST["days"][$i],0,2),substr($_POST["days"][$i],6,4));
$choice = new Choice($time);
$_SESSION['form']->addChoice($choice);
$schedules = $inputService->filterArray($_POST['horaires'.$i], FILTER_DEFAULT);
for($j = 0; $j < count($schedules); $j++) {
if (!empty($schedules[$j])) {
$choice->addSlot(strip_tags($schedules[$j]));
}
}
$k++;
}
}
}
}
//le format du sondage est DATE
$_SESSION["formatsondage"] = "D".$_SESSION["studsplus"];
$_SESSION['form']->format = 'D';
// Step 3/3 : Confirm poll creation
if (Utils::issetAndNoEmpty('choixheures') && Utils::issetAndNoEmpty('totalchoixjour', $_SESSION)) {
// Step 3/4 : Confirm poll creation
if (!empty($_POST['choixheures']) && !isset($_SESSION['form']->totalchoixjour)) {
Utils::print_header ( _("Removal date and confirmation (3 on 3)") );
bandeau_titre(_("Removal date and confirmation (3 on 3)"));
Utils::print_header ( _('Removal date and confirmation (3 on 3)') );
bandeau_titre(_('Removal date and confirmation (3 on 3)'));
$temp_array = array_unique($_SESSION["totalchoixjour"]);
sort($temp_array);
$removal_date=utf8_encode(strftime($date_format['txt_full'], end($temp_array)+ (86400 * $config['default_poll_duration'])));
$_SESSION['form']->sortChoices();
$last_date = $_SESSION['form']->lastChoice()->getName();
$removal_date = $last_date + (86400 * $config['default_poll_duration']);
// Sumary
// Summary
$summary = '<ul>';
for ($i=0;$i<count($_SESSION["totalchoixjour"]);$i++) {
$summary .= '<li>'.strftime($date_format['txt_full'], $_SESSION["totalchoixjour"][$i]);
for ($j=0;$j<count($_SESSION['horaires'.$i]);$j++) {
if (isset($_SESSION['horaires'.$i][$j])) {
$summary .= ($j==0) ? ' : ' : ', ';
$summary .= $_SESSION['horaires'.$i][$j];
}
foreach ($_SESSION['form']->getChoices() as $choice) {
$summary .= '<li>'.strftime($date_format['txt_full'], $choice->getName());
$first = true;
foreach ($choice->getSlots() as $slots) {
$summary .= $first ? ' : ' : ', ';
$summary .= $slots;
$first = false;
}
$summary .= '</li>'."\n";
$summary .= '</li>';
}
$summary .= '</ul>';
$end_date_str = utf8_encode(strftime('%d/%m/%Y', $max_time)); //textual date
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'choix_date.php" method="POST" class="form-horizontal" role="form">
<div class="row" id="selected-days">
<div class="col-md-8 col-md-offset-2">
<h3>'. _("Confirm the creation of your poll") .'</h3>
<h3>'. _('Confirm the creation of your poll') .'</h3>
<div class="well summary">
<h4>'. _("List of your choices").'</h4>
<h4>'. _('List of your choices').'</h4>
'. $summary .'
</div>
<div class="alert alert-info clearfix">
<p>' . _("Your poll will be automatically removed "). $config['default_poll_duration'] . ' ' . _("days") ._(" after the last date of your poll:") . ' <strong>'.$removal_date.'</strong>.<br />' . _("You can fix another removal date for it.") .'</p>
<p>' . _('Your poll will be automatically removed '). $config['default_poll_duration'] . ' ' . _('days') . ' ' ._('after the last date of your poll') . '.<br />' . _('You can set a closer removal date for it.') .'</p>
<div class="form-group">
<label for="champdatefin" class="col-sm-5 control-label">'. _("Removal date (optional)") .'</label>
<label for="enddate" class="col-sm-5 control-label">'. _('Removal date') .'</label>
<div class="col-sm-6">
<div class="input-group date">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar text-info"></i></span>
<input type="text" class="form-control" id="champdatefin" data-date-format="'. _("dd/mm/yyyy") .'" aria-describedby="dateformat" name="champdatefin" value="" size="10" maxlength="10" placeholder="'. _("dd/mm/yyyy") .'" />
<input type="text" class="form-control" id="enddate" data-date-format="'. _('dd/mm/yyyy') .'" aria-describedby="dateformat" name="enddate" value="'.$end_date_str.'" size="10" maxlength="10" placeholder="'. _('dd/mm/yyyy') .'" />
</div>
</div>
<span id="dateformat" class="sr-only">'. _("(dd/mm/yyyy)") .'</span>
</div>
</div>
<div class="alert alert-warning">
<p>'. _("Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll."). '</p>';
if($config['use_smtp']==true){
echo '
<p>' . _("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.") .'</p>';
<p>'. _('Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.'). '</p>';
if($config['use_smtp'] == true) {
echo '<p>' . _('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.') .'</p>';
}
echo '
</div>
@ -180,45 +216,45 @@ if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmp
bandeau_pied();
// Step 2/3 : Select dates of the poll
// Step 2/4 : Select dates of the poll
} else {
Utils::print_header ( _("Poll dates (2 on 3)") );
bandeau_titre(_("Poll dates (2 on 3)"));
Utils::print_header ( _('Poll dates (2 on 3)') );
bandeau_titre(_('Poll dates (2 on 3)'));
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'choix_date.php" method="POST" class="form-horizontal" role="form">
<div class="row" id="selected-days">
<div class="col-md-10 col-md-offset-1">
<h3>'. _("Choose the dates of your poll") .'</h3>
<h3>'. _('Choose the dates of your poll') .'</h3>
<div class="alert alert-info">
<p>'. _("To schedule an event, it's better to propose at least two choices (two hours for one day or two days).").'</p>
<p>'. _("You can add or remove additionnal days and hours with the buttons") .' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _("Remove") .'</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _("Add") .'</span></p>
<p>'. _("For each selected day, you can choose, or not, meeting hours (e.g.: \"8h\", \"8:30\", \"8h-10h\", \"evening\", etc.)").'</p>
<p>'. _('To schedule an event you need to propose at least two choices (two hours for one day or two days).').'</p>
<p>'. _('You can add or remove additionnal days and hours with the buttons') .' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _('Remove') .'</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _('Add') .'</span></p>
<p>'. _('For each selected day, you can choose, or not, meeting hours (e.g.: "8h", "8:30", "8h-10h", "evening", etc.)').'</p>
</div>';
// Fields days : 3 by default
$nb_days = (isset($_SESSION["totalchoixjour"])) ? count($_SESSION["totalchoixjour"]) : 3;
$nb_days = (isset($_SESSION['totalchoixjour'])) ? count($_SESSION['totalchoixjour']) : 3;
for ($i=0;$i<$nb_days;$i++) {
$day_value = isset($_SESSION["totalchoixjour"][$i]) ? strftime( "%d/%m/%Y", $_SESSION["totalchoixjour"][$i]) : '';
$day_value = isset($_SESSION['totalchoixjour'][$i]) ? strftime('%d/%m/%Y', $_SESSION['totalchoixjour'][$i]) : '';
echo '
<fieldset>
<div class="form-group">
<legend>
<label class="sr-only" for="day'.$i.'">'. _("Day") .' '. ($i+1) .'</label>
<label class="sr-only" for="day'.$i.'">'. _('Day') .' '. ($i+1) .'</label>
<div class="input-group date col-xs-7">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar text-info"></i></span>
<input type="text" class="form-control" id="day'.$i.'" title="'. _("Day") .' '. ($i+1) .'" data-date-format="'. _("dd/mm/yyyy") .'" aria-describedby="dateformat'.$i.'" name="days[]" value="'.$day_value.'" size="10" maxlength="10" placeholder="'. _("dd/mm/yyyy") .'" />
<input type="text" class="form-control" id="day'.$i.'" title="'. _("Day") .' '. ($i+1) .'" data-date-format="'. _('dd/mm/yyyy') .'" aria-describedby="dateformat'.$i.'" name="days[]" value="'.$day_value.'" size="10" maxlength="10" placeholder="'. _("dd/mm/yyyy") .'" />
</div>
<span id="dateformat'.$i.'" class="sr-only">'. _("(dd/mm/yyyy)") .'</span>
<span id="dateformat'.$i.'" class="sr-only">'. _('(dd/mm/yyyy)') .'</span>
</legend>'."\n";
// Fields hours : 3 by default
for ($j=0;$j<max(count(isset($_SESSION["horaires".$i]) ? $_SESSION["horaires".$i] : 0),3);$j++) {
$hour_value = isset($_SESSION["horaires".$i][$j]) ? $_SESSION["horaires".$i][$j] : '';
for ($j=0;$j<max(count(isset($_SESSION['horaires'.$i]) ? $_SESSION['horaires'.$i] : 0),3);$j++) {
$hour_value = isset($_SESSION['horaires'.$i][$j]) ? $_SESSION['horaires'.$i][$j] : '';
echo '
<div class="col-sm-2">
<label for="d'.$i.'-h'.$j.'" class="sr-only control-label">'. _("Time") .' '. ($j+1) .'</label>
<input type="text" class="form-control hours" title="'.$day_value.' - '. _("Time") .' '. ($j+1) .'" placeholder="'. _("Time") .' '. ($j+1) .'" id="d'.$i.'-h'.$j.'" name="horaires'.$i.'[]" value="'.$hour_value.'" />
<label for="d'.$i.'-h'.$j.'" class="sr-only control-label">'. _('Time') .' '. ($j+1) .'</label>
<input type="text" class="form-control hours" title="'.$day_value.' - '. _('Time') .' '. ($j+1) .'" placeholder="'. _('Time') .' '. ($j+1) .'" id="d'.$i.'-h'.$j.'" name="horaires'.$i.'[]" value="'.$hour_value.'" />
</div>'."\n";
}
echo '
@ -231,24 +267,24 @@ if (Utils::issetAndNoEmpty('titre', $_SESSION) === false || Utils::issetAndNoEmp
}
echo '
<div class="col-md-4">
<button type="button" id="copyhours" class="btn btn-default disabled" title="'. _("Copy hours of the first day") .'"><span class="glyphicon glyphicon-sort-by-attributes-alt text-info"></span><span class="sr-only">'. _("Copy hours of the first day") .'</span></button>
<button type="button" id="copyhours" class="btn btn-default disabled" title="'. _('Copy hours of the first day') .'"><span class="glyphicon glyphicon-sort-by-attributes-alt text-info"></span><span class="sr-only">'. _("Copy hours of the first day") .'</span></button>
<div class="btn-group btn-group">
<button type="button" id="remove-a-day" class="btn btn-default disabled" title="'. _("Remove a day") .'"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _("Remove a day") .'</span></button>
<button type="button" id="add-a-day" class="btn btn-default" title="'. _("Add a day") .'"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _("Add a day") .'</span></button>
<button type="button" id="remove-a-day" class="btn btn-default disabled" title="'. _('Remove a day') .'"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">'. _("Remove a day") .'</span></button>
<button type="button" id="add-a-day" class="btn btn-default" title="'. _('Add a day') .'"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">'. _("Add a day") .'</span></button>
</div>
</div>
<div class="col-md-8 text-right">
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-remove text-danger"></span> '. _("Remove") . ' <span class="caret"></span>
<span class="glyphicon glyphicon-remove text-danger"></span> '. _('Remove') . ' <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><a id="resetdays" href="javascript:void(0)">'. _("Remove all days") .'</a></li>
<li><a id="resethours" href="javascript:void(0)">'. _("Remove all hours") .'</a></li>
<li><a id="resetdays" href="javascript:void(0)">'. _('Remove all days') .'</a></li>
<li><a id="resethours" href="javascript:void(0)">'. _('Remove all hours') .'</a></li>
</ul>
</div>
<a class="btn btn-default" href="'.Utils::get_server_name().'infos_sondage.php?choix_sondage=date" title="'. _('Back to step 1') . '">'. _('Back') . '</a>
<button name="choixheures" value="'. _("Next") .'" type="submit" class="btn btn-success disabled" title="'. _('Go to step 3') . '">'. _("Next") .'</button>
<button name="choixheures" value="'. _('Next') .'" type="submit" class="btn btn-success disabled" title="'. _('Go to step 3') . '">'. _("Next") .'</button>
</div>
</div>
</div>

View File

@ -4,16 +4,18 @@
"keywords": [
"poll"
],
"version": "0.8.0",
"version": "0.9.0",
"license": "CeCILL-B",
"type": "project",
"require": {
"adodb/adodb-php": "5.19"
"smarty/smarty": "3.1.21"
},
"autoload": {
"psr-0": {"Framadate": "app/classes/"}
"psr-4": {
"Framadate\\": "app/classes/Framadate/"
}
}
}

72
composer.lock generated
View File

@ -4,70 +4,70 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "3cdb051814926a50182b00bbf5297ed2",
"hash": "4bf9a3fa30eb400c9ed140dd2d6fa266",
"packages": [
{
"name": "adodb/adodb-php",
"version": "v5.19",
"name": "smarty/smarty",
"version": "v3.1.21",
"source": {
"type": "git",
"url": "https://github.com/ADOdb/ADOdb.git",
"reference": "773bb9b7ccd81b403bf75f7ef476c3f2d7ee04de"
"url": "https://github.com/smarty-php/smarty.git",
"reference": "1f878e6d746ecc8ec8d00d9c75044cf7d23ad94a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ADOdb/ADOdb/zipball/773bb9b7ccd81b403bf75f7ef476c3f2d7ee04de",
"reference": "773bb9b7ccd81b403bf75f7ef476c3f2d7ee04de",
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/1f878e6d746ecc8ec8d00d9c75044cf7d23ad94a",
"reference": "1f878e6d746ecc8ec8d00d9c75044cf7d23ad94a",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
"php": ">=5.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev"
}
},
"autoload": {
"files": [
"adodb.inc.php"
"classmap": [
"libs/Smarty.class.php",
"libs/SmartyBC.class.php",
"libs/sysplugins/smarty_security.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1",
" BSD-2-Clause"
"LGPL-3.0"
],
"authors": [
{
"name": "John Lim",
"email": "jlim@natsoft.com",
"role": "Author"
"name": "Monte Ohrt",
"email": "monte@ohrt.com"
},
{
"name": "Damien Regad",
"role": "Current maintainer"
"name": "Uwe Tews",
"email": "uwe.tews@googlemail.com"
},
{
"name": "Rodney Rehm",
"email": "rodney.rehm@medialize.de"
}
],
"description": "ADOdb is a PHP database abstraction layer library",
"homepage": "http://adodb.sourceforge.net/",
"description": "Smarty - the compiling PHP template engine",
"homepage": "http://www.smarty.net",
"keywords": [
"database"
"templating"
],
"time": "2014-04-23 14:37:03"
"time": "2014-10-31 04:22:20"
}
],
"packages-dev": [
],
"aliases": [
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [
],
"stability-flags": [],
"prefer-stable": false,
"platform": [
],
"platform-dev": [
]
"prefer-lowest": false,
"platform": [],
"platform-dev": []
}

View File

@ -47,7 +47,7 @@ a.text-info:focus {
.text-warning,
.text-warning a,
a.text-warning {
color: #8A6E3B;
color: #C05827;
}
.text-warning a:hover,
.text-warning a:focus,
@ -85,7 +85,7 @@ a.bg-primary:hover {
background-color: #FCF3E3;
}
.bg-danger,a.bg-danger:hover {
background-color: #F2E7E5;
background-color: #FDE0DC;
}
/* Alerts */
@ -210,6 +210,7 @@ fieldset[disabled] .btn-primary.active {
color: #fff;
background-color: #849551;
border-color: #748544;
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.7);
}
.btn-success:hover,
.btn-success:focus,
@ -434,3 +435,8 @@ fieldset[disabled] .btn-link:focus {
.modal-header h4,.modal-header h5,.modal-header h6 {
font-size: 24px;
}
/* Admin > Polls */
.table-polls .cell-format {
vertical-align: middle
}

View File

@ -4,16 +4,12 @@ body {
background:none;
}
.jumbotron {
page-break-after:always;
table {
page-break-after:always}
}
table {
page-break-after:always;
}
table {
page-break-inside:auto;
page-break-inside:auto
}
tr {
@ -29,26 +25,6 @@ tfoot {
display:table-footer-group;
}
#tableContainer {
overflow-x:visible !important;
}
#vote-form, .scroll-buttons, form .alert.alert-info {
display:none;
}
table.results td {
border:2px solid #333;
}
table.results tr td:last-child {
display:none;
}
.container {
width:auto;
}
@page {
size:landscape;
}

View File

@ -120,7 +120,9 @@ caption {
#title-form h3 .btn-edit,
#email-form .btn-edit,
#description-form .btn-edit,
#poll-rules-form .btn-edit {
#poll-rules-form .btn-edit,
#expiration-form .btn-edit,
#name-form .btn-edit {
position:absolute;
left:-2000px;
}
@ -132,7 +134,11 @@ caption {
#description-form .btn-edit:focus,
#description-form:hover .btn-edit,
#poll-rules-form .btn-edit:focus,
#poll-rules-form:hover .btn-edit {
#poll-rules-form:hover .btn-edit,
#expiration-form .btn-edit:focus,
#expiration-form:hover .btn-edit,
#name-form .btn-edit:focus,
#name-form:hover .btn-edit {
position:relative !important;
left:0;
padding: 0px 10px;
@ -245,18 +251,13 @@ table.results .btn-link.btn-sm {
#vote-form td {
border-top:2px solid white;
vertical-align:top;
}
.yes input, .ifneedbe input,.no input {
position: absolute;
width: 1px;
height: 1px;
padding: 0px;
margin: -1px;
overflow: hidden;
clip: rect(0px, 0px, 0px, 0px);
border: 0px none;
position:absolute;
left:0;
margin-left:-15px;
margin-top:0;
}
.choice input:focus + label {
@ -274,47 +275,45 @@ table.results .btn-link.btn-sm {
.yes .btn, .ifneedbe .btn, .no .btn {
width:32px;
color:#555;
}
.yes .btn,.yes .btn:hover {
.yes .btn {
border-bottom-right-radius:0px !important;
border-bottom-left-radius:0px !important;
margin-bottom:-1px !important;
margin-top:4px !important;
color:#677835;
}
.ifneedbe .btn,.ifneedbe .btn:hover {
.ifneedbe .btn {
border-radius:0px;
color:#C48A1B;
}
.no .btn,.no .btn:hover{
.no .btn{
border-top-right-radius:0px !important;
border-top-left-radius:0px !important;
margin-bottom:4px !important;
margin-top:-1px !important;
color:#AD220F;
}
.yes input[type="radio"]:checked + label { /* =.btn-success.active */
color: #FFF;
background-color: #7D8C3F;
border-color: #7D8C3F;
color: #fff;
background-color: #768745;
border-color: #67753C;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.125) inset;
}
.ifneedbe input[type="radio"]:checked + label { /* =.btn-warning.active */
color: #FFF;
background-color: #C48A1B;
border-color: #C48A1B;
color: #fff;
background-color: #CF9800;
border-color: #BD8A00;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.125) inset;
}
.no input[type="radio"]:checked + label { /* =.btn-danger.active */
color: #FFF;
background-color: #B82E12;
border-color: #B82E12;
color: #fff;
background-color: #BF2511;
border-color: #AD220F;
box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.125) inset;
}

View File

@ -16,89 +16,103 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\InputService;
use Framadate\Services\MailService;
use Framadate\Message;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
if(!isset($_GET['numsondage']) || ! preg_match(";^[\w\d]{16}$;i", $_GET['numsondage'])) {
header('Location: studs.php');
ob_start();
/* Variables */
/* --------- */
$poll_id = null;
$poll = null;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
/* PAGE */
/* ---- */
if (!empty($_GET['poll'])) {
$poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^[a-z0-9]+$/']]);
$poll = $pollService->findById($poll_id);
}
$sql = 'SELECT * FROM user_studs WHERE id_sondage='.$connect->Param('numsondage').' ORDER BY id_users';
$sql = $connect->Prepare($sql);
$user_studs = $connect->Execute($sql, array($_GET['numsondage']));
if (!$poll) {
$smarty->assign('error', _('This poll doesn\'t exist !'));
$smarty->display('error.tpl');
exit;
}
$dsondage = Utils::get_sondage_from_id($_GET['numsondage']);
$nbcolonnes=substr_count($dsondage->sujet,',')+1;
$toutsujet=explode(",",$dsondage->sujet);
$slots = $pollService->allSlotsByPollId($poll_id);
$votes = $pollService->allVotesByPollId($poll_id);
//affichage des sujets du sondage
$input =",";
foreach ($toutsujet as $value) {
if ($dsondage->format=="D"||$dsondage->format=="D+") {
if (strpos($dsondage->sujet,'@') !== false) {
$days=explode("@",$value);
$input.= '"'.date("j/n/Y",$days[0]).'",';
} else {
$input.= '"'.date("j/n/Y",$values).'",';
}
} else {
// CSV header
if ($poll->format === 'D') {
$titles_line = ',';
$moments_line = ',';
foreach ($slots as $slot) {
$title = Utils::csvEscape(strftime($date_format['txt_date'], $slot->title));
$moments = explode(',', $slot->moments);
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/',$value,$md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/',$value,$md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/',$value,$md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0]!='' && isset($md_a_img[3][0]) && $md_a_img[3][0]!='') { // [![alt](src)](href)
$subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0]!='') ? stripslashes($md_a_img[1][0]) : _("Choice") .' '.($i+1);
} elseif (isset($md_img[2][0]) && $md_img[2][0]!='') { // ![alt](src)
$subject_text = (isset($md_img[1][0]) && $md_img[1][0]!='') ? stripslashes($md_img[1][0]) : _("Choice") .' '.($i+1);
} elseif (isset($md_a[2][0]) && $md_a[2][0]!='') { // [text](href)
$subject_text = (isset($md_a[1][0]) && $md_a[1][0]!='') ? stripslashes($md_a[1][0]) : _("Choice") .' '.($i+1);
} else { // text only
$subject_text = stripslashes($value);
}
$input.= '"'.html_entity_decode($subject_text).'",';
$titles_line .= str_repeat($title . ',', count($moments));
$moments_line .= implode(',', array_map('\Framadate\Utils::csvEscape', $moments)) . ',';
}
}
$input.="\r\n";
if (strpos($dsondage->sujet,'@') !== false) {
$input.=",";
foreach ($toutsujet as $value) {
$heures=explode("@",$value);
$input.= '"'.$heures[1].'",';
echo $titles_line . "\r\n";
echo $moments_line . "\r\n";
} else {
echo ',';
foreach ($slots as $slot) {
echo Utils::markdown($slot->title, true) . ',';
}
$input.="\r\n";
echo "\r\n";
}
// END - CSV header
while ( $data=$user_studs->FetchNextObject(false)) {
// Le nom de l'utilisateur
$nombase=html_entity_decode(str_replace("°","'",$data->nom));
$input.= '"'.$nombase.'",';
//affichage des resultats
$ensemblereponses=$data->reponses;
for ($k=0;$k<$nbcolonnes;$k++) {
$car=substr($ensemblereponses,$k,1);
switch ($car) {
case "1": $input .= '"'._('Yes').'",'; $somme[$k]++; break;
case "2": $input .= '"'._('Ifneedbe').'",'; break;
default: $input .= '"'._('No').'",'; break;
// Vote lines
foreach ($votes as $vote) {
echo Utils::csvEscape($vote->name) . ',';
$choices = str_split($vote->choices);
foreach ($choices as $choice) {
switch ($choice) {
case 0:
$text = _('No');
break;
case 1:
$text = _('Ifneedbe');
break;
case 2:
$text = _('Yes');
break;
default:
$text = 'unkown';
}
echo Utils::csvEscape($text);
echo ',';
}
$input.="\r\n";
echo "\r\n";
}
// END - Vote lines
$filesize = strlen( $input );
$filename=$_GET["numsondage"].".csv";
// HTTP headers
$content = ob_get_clean();
$filesize = strlen($content);
$filename = Utils::cleanFilename($poll->title) . '.csv';
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Length: '.$filesize );
header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
header( 'Cache-Control: max-age=10' );
header('Content-Type: text/csv; charset=utf-8');
header('Content-Length: ' . $filesize);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: max-age=10');
// END - HTTP headers
echo str_replace('&quot;','""',$input);
die();
echo $content;

View File

@ -18,8 +18,6 @@
*/
namespace Framadate;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
if (is_readable('bandeaux_local.php')) {
@ -28,12 +26,9 @@ if (is_readable('bandeaux_local.php')) {
include_once('bandeaux.php');
}
session_start();
// affichage de la page
Utils::print_header( _("Home") );
bandeau_titre(_("Make your polls"));
echo '
<div class="row">
<div class="col-md-6 text-center">

View File

@ -18,127 +18,112 @@
*/
namespace Framadate;
session_start();
include_once __DIR__ . '/app/inc/init.php';
function fromPostOrEmpty($postKey) {
return isset($_POST[$postKey]) ? Utils::htmlEscape($_POST[$postKey]) : '';
}
if (!isset($_SESSION['form'])) {
$_SESSION['form'] = new Form();
}
if (file_exists('bandeaux_local.php')) {
include_once('bandeaux_local.php');
} else {
include_once('bandeaux.php');
}
// Type de sondage : <button value="$_SESSION["choix_sondage"]">
// Type de sondage : <button value="$_SESSION['form']->choix_sondage">
if ((isset($_GET['choix_sondage']) && $_GET['choix_sondage'] == 'date') ||
(isset($_POST["choix_sondage"]) && $_POST["choix_sondage"] == 'creation_sondage_date')) {
$choix_sondage = "creation_sondage_date";
$_SESSION["choix_sondage"] = $choix_sondage;
$_SESSION['form']->choix_sondage = $choix_sondage;
} else {
$choix_sondage = "creation_sondage_autre";
$_SESSION["choix_sondage"] = $choix_sondage;
$_SESSION['form']->choix_sondage = $choix_sondage;
}
// On teste toutes les variables pour supprimer l'ensemble des warnings PHP
// On transforme en entites html les données afin éviter les failles XSS
$post_var = array('poursuivre', 'titre', 'nom', 'adresse', 'commentaires', 'studsplus', 'mailsonde', 'creation_sondage_date', 'creation_sondage_autre');
foreach ($post_var as $var) {
if (isset($_POST[$var]) === true) {
$$var = htmlentities($_POST[$var], ENT_QUOTES, 'UTF-8');
} else {
$$var = null;
}
}
// We clean the data
$poursuivre = filter_input(INPUT_POST, 'poursuivre', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(creation_sondage_date|creation_sondage_autre)$/']]);
$title = filter_input(INPUT_POST, 'title', FILTER_SANITIZE_STRING);
$name = filter_input(INPUT_POST, 'name', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => NAME_REGEX]]);
$mail = filter_input(INPUT_POST, 'mail', FILTER_VALIDATE_EMAIL);
$description = filter_input(INPUT_POST, 'description', FILTER_SANITIZE_STRING);
$editable = filter_input(INPUT_POST, 'editable', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
$receiveNewVotes = filter_input(INPUT_POST, 'receiveNewVotes', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
$receiveNewComments = filter_input(INPUT_POST, 'receiveNewComments', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
// On initialise egalement la session car sinon bonjour les warning :-)
$session_var = array('titre', 'nom', 'adresse', 'commentaires', 'mailsonde', 'studsplus', );
foreach ($session_var as $var) {
if (Utils::issetAndNoEmpty($var, $_SESSION) === false) {
$_SESSION[$var] = null;
}
}
// On initialise également les autres variables
$erreur_adresse = false;
$erreur_injection_titre = false;
$erreur_injection_nom = false;
$erreur_injection_commentaires = false;
$cocheplus = '';
$cochemail = '';
$error_on_mail = false;
$error_on_title = false;
$error_on_name = false;
$error_on_description = false;
#tests
if (Utils::issetAndNoEmpty("poursuivre")){
$_SESSION["titre"] = $titre;
$_SESSION["nom"] = $nom;
$_SESSION["adresse"] = $adresse;
$_SESSION["commentaires"] = $commentaires;
if (!empty($_POST['poursuivre'])) {
$_SESSION['form']->title = $title;
$_SESSION['form']->admin_name = $name;
$_SESSION['form']->admin_mail = $mail;
$_SESSION['form']->description = $description;
$_SESSION['form']->editable = ($editable !== null) ? true : false;
$_SESSION['form']->receiveNewVotes = ($receiveNewVotes !== null) ? true : false;
$_SESSION['form']->receiveNewComments = ($receiveNewComments !== null) ? true : false;
unset($_SESSION["studsplus"]);
$_SESSION["studsplus"] = ($studsplus !== null) ? '+' : $_SESSION["studsplus"] = '';
unset($_SESSION["mailsonde"]);
$_SESSION["mailsonde"] = ($mailsonde !== null) ? true : false;
if ($config['use_smtp']==true){
if (Utils::isValidEmail($adresse) === false) {
$erreur_adresse = true;
if ($config['use_smtp']==true) {
if (empty($mail)) {
$error_on_mail = true;
}
}
if (preg_match(';<|>|";',$titre)) {
$erreur_injection_titre = true;
if ($title !== $_POST['title']) {
$error_on_title = true;
}
if (preg_match(';<|>|";',$nom)) {
$erreur_injection_nom = true;
if ($name !== $_POST['name']) {
$error_on_name = true;
}
if (preg_match(';<|>|";',$commentaires)) {
$erreur_injection_commentaires = true;
if ($description !== $_POST['description']) {
$error_on_description = true;
}
// Si pas d'erreur dans l'adresse alors on change de page vers date ou autre
if($config['use_smtp']==true){
$email_OK = $adresse && !$erreur_adresse;
} else{
if ($config['use_smtp'] == true) {
$email_OK = $mail && !$error_on_mail;
} else {
$email_OK = true;
}
if ($titre && $nom && $email_OK && ! $erreur_injection_titre && ! $erreur_injection_commentaires && ! $erreur_injection_nom) {
if ($title && $name && $email_OK && ! $error_on_title && ! $error_on_description && ! $error_on_name) {
if ( $poursuivre == "creation_sondage_date" ) {
header("Location:choix_date.php");
if ( $poursuivre == 'creation_sondage_date' ) {
header('Location:choix_date.php');
exit();
}
if ( $poursuivre == "creation_sondage_autre" ) {
header("Location:choix_autre.php");
if ( $poursuivre == 'creation_sondage_autre' ) {
header('Location:choix_autre.php');
exit();
}
} else {
// Title Erreur !
Utils::print_header( _("Error!").' - '._("Poll creation (1 on 3)") );
Utils::print_header( _('Error!').' - '._('Poll creation (1 on 3)') );
}
} else {
// Title OK (formulaire pas encore rempli)
Utils::print_header( _("Poll creation (1 on 3)") );
Utils::print_header( _('Poll creation (1 on 3)') );
}
bandeau_titre( _("Poll creation (1 on 3)") );
// premier sondage ? test l'existence des schémas SQL avant d'aller plus loin
if(!Utils::check_table_sondage()) {
echo '<div class="alert alert-danger text-center">' . _("Framadate is not properly installed, please check the 'INSTALL' to setup the database before continuing.") . "</div>"."\n";
bandeau_pied();
die();
}
bandeau_titre( _('Poll creation (1 on 3)') );
/*
* Préparation des messages d'erreur
*/
$errors = array(
$errors = array (
'title' => array (
'msg' => '',
'aria' => '',
@ -161,100 +146,108 @@ $errors = array(
)
);
if (!$_SESSION["titre"] && Utils::issetAndNoEmpty("poursuivre") ) {
$errors['title']['aria'] = 'aria-describeby="poll_title_error" '; $errors['title']['class'] = ' has-error';
$errors['title']['msg'] = '<div class="alert alert-danger" ><p id="poll_title_error">' . _("Enter a title") . '</p></div>';
} elseif ($erreur_injection_titre) {
$errors['title']['aria'] = 'aria-describeby="poll_title_error" '; $errors['title']['class'] = ' has-error';
$errors['title']['inject'] = '<div class="alert alert-danger"><p id="poll_title_error">' . _("Characters < > and \" are not permitted") . '</p></div>';
}
if (!empty($_POST['poursuivre'])) {
if (empty($_POST['title'])) {
$errors['title']['aria'] = 'aria-describeby="poll_title_error" ';
$errors['title']['class'] = ' has-error';
$errors['title']['msg'] = '<div class="alert alert-danger" ><p id="poll_title_error">' . _('Enter a title') . '</p></div>';
} elseif ($error_on_title) {
$errors['title']['aria'] = 'aria-describeby="poll_title_error" ';
$errors['title']['class'] = ' has-error';
$errors['title']['msg'] = '<div class="alert alert-danger"><p id="poll_title_error">' . _('Something is wrong with the format') . '</p></div>';
}
if ($erreur_injection_commentaires) {
$errors['description']['aria'] = 'aria-describeby="poll_comment_error" '; $errors['description']['class'] = ' has-error';
$errors['description']['msg'] = '<div class="alert alert-danger"><p id="poll_comment_error">' . _("Characters < > and \" are not permitted") . '</p></div>';
}
if ($error_on_description) {
$errors['description']['aria'] = 'aria-describeby="poll_comment_error" ';
$errors['description']['class'] = ' has-error';
$errors['description']['msg'] = '<div class="alert alert-danger"><p id="poll_comment_error">' . _('Something is wrong with the format') . '</p></div>';
}
if (!$_SESSION["nom"] && Utils::issetAndNoEmpty("poursuivre")) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" '; $errors['name']['class'] = ' has-error';
$errors['name']['msg'] = '<div class="alert alert-danger"><p id="poll_name_error">' . _("Enter a name") . '</p></div>';
} elseif ($erreur_injection_nom) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" '; $errors['name']['class'] = ' has-error';
$errors['name']['msg'] = '<div class="alert alert-danger"><p id="poll_name_error">' . _("Characters < > and \" are not permitted") . '</p></div>';
}
if (empty($_POST['name'])) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['name']['class'] = ' has-error';
$errors['name']['msg'] = '<div class="alert alert-danger"><p id="poll_name_error">' . _('Enter a name') . '</p></div>';
} elseif ($error_on_name) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['name']['class'] = ' has-error';
$errors['name']['msg'] = '<div class="alert alert-danger"><p id="poll_name_error">' . _('Something is wrong with the format') . '</p></div>';
}
if (!$_SESSION["adresse"] && Utils::issetAndNoEmpty("poursuivre")) {
$errors['email']['aria'] = 'aria-describeby="poll_name_error" '; $errors['email']['class'] = ' has-error';
$errors['email']['msg'] = '<div class="alert alert-danger"><p id="poll_email_error">' . _("Enter an email address") . '</p></div>';
} elseif ($erreur_adresse && Utils::issetAndNoEmpty("poursuivre")) {
$errors['email']['aria'] = 'aria-describeby="poll_email_error" '; $errors['email']['class'] = ' has-error';
$errors['email']['msg'] = '<div class="alert alert-danger"><p id="poll_email_error">' . _("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.") . '</p></div>';
if (empty($_POST['mail'])) {
$errors['email']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['email']['class'] = ' has-error';
$errors['email']['msg'] = '<div class="alert alert-danger"><p id="poll_email_error">' . _('Enter an email address') . '</p></div>';
} elseif ($error_on_mail) {
$errors['email']['aria'] = 'aria-describeby="poll_email_error" ';
$errors['email']['class'] = ' has-error';
$errors['email']['msg'] = '<div class="alert alert-danger"><p id="poll_email_error">' . _('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.') . '</p></div>';
}
}
/*
* Préparation en fonction des paramètres de session
*/
// REMOTE_USER ?
if (USE_REMOTE_USER && isset($_SERVER['REMOTE_USER'])) {
$input_name = '<input type="hidden" name="nom" value="'.$_SESSION["nom"].'" />'.stripslashes($_SESSION["nom"]);
} else {
$input_name = '<input id="yourname" type="text" name="nom" class="form-control" '.$errors['name']['aria'].' value="'.stripslashes($_SESSION["nom"]).'" />';
}
/**
* @return string
*/
if (USE_REMOTE_USER && isset($_SERVER['REMOTE_USER'])) {
$input_email = '<input type="hidden" name="adresse" value="'.$_SESSION["adresse"].'">'.$_SESSION["adresse"];
$input_name = '<input type="hidden" name="name" value="'.Utils::htmlEscape($_POST['name']).'" />'.$_SESSION['form']->admin_name;
$input_email = '<input type="hidden" name="mail" value="'.Utils::htmlEscape($_POST['mail']).'">'.$_SESSION['form']->admin_mail;
} else {
$input_email = '<input id="email" type="text" name="adresse" class="form-control" '.$errors['email']['aria'].' value="'.$_SESSION["adresse"].'" />';
$input_name = '<input id="yourname" type="text" name="name" class="form-control" '.$errors['name']['aria'].' value="'. fromPostOrEmpty('name') .'" />';
$input_email = '<input id="email" type="text" name="mail" class="form-control" '.$errors['email']['aria'].' value="'. fromPostOrEmpty('mail') .'" />';
}
// Checkbox checked ?
if (!$_SESSION["studsplus"] && !Utils::issetAndNoEmpty('creation_sondage_date') && !Utils::issetAndNoEmpty('creation_sondage_autre')) {
$_SESSION["studsplus"]="+";
if ($_SESSION['form']->editable) {
$editable = 'checked';
}
if ($_SESSION["studsplus"]=="+") {
$cocheplus="checked";
if ($_SESSION['form']->receiveNewVotes) {
$receiveNewVotes = 'checked';
}
if ($_SESSION["mailsonde"]) {
$cochemail="checked";
if ($_SESSION['form']->receiveNewComments) {
$receiveNewComments = 'checked';
}
// Affichage du formulaire
// Display form
echo '
<div class="row">
<div class="col-md-8 col-md-offset-2" >
<form name="formulaire" id="formulaire" action="' . Utils::get_server_name() . 'infos_sondage.php" method="POST" class="form-horizontal" role="form">
<div class="alert alert-info">
<p>'. _("You are in the poll creation section.").' <br /> '._("Required fields cannot be left blank.") .'</p>
<p>'. _('You are in the poll creation section.').' <br /> '._('Required fields cannot be left blank.') .'</p>
</div>
<div class="form-group'.$errors['title']['class'].'">
<label for="poll_title" class="col-sm-4 control-label">' . _("Poll title") . ' *</label>
<label for="poll_title" class="col-sm-4 control-label">' . _('Poll title') . ' *</label>
<div class="col-sm-8">
<input id="poll_title" type="text" name="titre" class="form-control" '.$errors['title']['aria'].' value="'.stripslashes($_SESSION["titre"]).'" />
<input id="poll_title" type="text" name="title" class="form-control" '.$errors['title']['aria'].' value="'. fromPostOrEmpty('title') .'" />
</div>
</div>
'.$errors['title']['msg'].'
<div class="form-group'.$errors['description']['class'].'">
<label for="poll_comments" class="col-sm-4 control-label">'. _("Description") .'</label>
<label for="poll_comments" class="col-sm-4 control-label">'. _('Description') .'</label>
<div class="col-sm-8">
<textarea id="poll_comments" name="commentaires" class="form-control" '.$errors['description']['aria'].' rows="5">'.stripslashes($_SESSION["commentaires"]).'</textarea>
<textarea id="poll_comments" name="description" class="form-control" '.$errors['description']['aria'].' rows="5">'. fromPostOrEmpty('description') .'</textarea>
</div>
</div>
'.$errors['description']['msg'].'
<div class="form-group'.$errors['name']['class'].'">
<label for="yourname" class="col-sm-4 control-label">'. _("Your name") .' *</label>
<label for="yourname" class="col-sm-4 control-label">'. _('Your name') .' *</label>
<div class="col-sm-8">
'.$input_name.'
</div>
</div>
'.$errors['name']['msg'];
if($config['use_smtp']==true){
if ($config['use_smtp']==true) {
echo '
<div class="form-group'.$errors['email']['class'].'">
<label for="email" class="col-sm-4 control-label">'. _("Your email address") .' *<br /><span class="small">'. _("(in the format name@mail.com)") .'</span></label>
<label for="email" class="col-sm-4 control-label">'. _('Your email address') .' *<br /><span class="small">'. _('(in the format name@mail.com)') .'</span></label>
<div class="col-sm-8">
'.$input_email.'
</div>
@ -263,20 +256,29 @@ if($config['use_smtp']==true){
}
echo '
<div class="form-group">
<div class="col-sm-offset-1 col-sm-11">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label>
<input type=checkbox name=studsplus '.$cocheplus.' id="studsplus">'. _("Voters can modify their vote themselves.") .'
<input type=checkbox name="editable" '.$editable.' id="editable">'. _('Voters can modify their vote themselves.') .'
</label>
</div>
</div>
</div>';
if($config['use_smtp']==true){
if ($config['use_smtp']==true) {
echo '<div class="form-group">
<div class="col-sm-offset-1 col-sm-11">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label>
<input type=checkbox name=mailsonde '.$cochemail.' id="mailsonde">'. _("To receive an email for each new vote.") .'
<input type=checkbox name="receiveNewVotes" '.$receiveNewVotes.' id="receiveNewVotes">'. _('To receive an email for each new vote.') .'
</label>
</div>
</div>
</div>';
echo '<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<div class="checkbox">
<label>
<input type=checkbox name="receiveNewComments" '.$receiveNewComments.' id="receiveNewComments">'. _('To receive an email for each new comment.') .'
</label>
</div>
</div>
@ -288,7 +290,7 @@ echo '
<button name="poursuivre" value="'. $choix_sondage .'" type="submit" class="btn btn-success" title="'. _('Go to step 2') . '">'. _('Next') . '</button>
</p>
<script type="text/javascript"> document.formulaire.titre.focus(); </script>
<script type="text/javascript">document.formulaire.title.focus();</script>
</form>
</div>

View File

@ -1,100 +1,112 @@
-- Base de données: `opensondage`
-- --------------------------------------------------------
--
-- Table structure `poll`
--
CREATE TABLE IF NOT EXISTS `poll` (
`id` CHAR(16) NOT NULL,
`admin_id` CHAR(24) NOT NULL,
`title` TEXT NOT NULL,
`description` TEXT,
`admin_name` VARCHAR(64) DEFAULT NULL,
`admin_mail` VARCHAR(128) DEFAULT NULL,
`creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`end_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
`format` VARCHAR(1) DEFAULT NULL,
`editable` TINYINT(1) DEFAULT '0',
`receiveNewVotes` TINYINT(1) DEFAULT '0',
`active` TINYINT(1) DEFAULT '1',
PRIMARY KEY (`id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `comments`
-- Table structure `slot`
--
CREATE TABLE IF NOT EXISTS `comments` (
`id_comment` int(11) unsigned NOT NULL AUTO_INCREMENT,
`id_sondage` char(16) NOT NULL,
`comment` text NOT NULL,
`usercomment` text,
PRIMARY KEY (`id_comment`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `slot` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`title` TEXT,
`moments` TEXT,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `sondage`
-- Table structure `comment`
--
CREATE TABLE IF NOT EXISTS `sondage` (
`id_sondage` char(16) NOT NULL,
`commentaires` text,
`mail_admin` varchar(128) DEFAULT NULL,
`nom_admin` varchar(64) DEFAULT NULL,
`titre` text,
`id_sondage_admin` char(24) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_fin` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`format` varchar(2) DEFAULT NULL,
`mailsonde` tinyint(1) DEFAULT '0',
`statut` int(11) NOT NULL DEFAULT '1' COMMENT '1 = actif ; 0 = inactif ; ',
UNIQUE KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `comment` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` TEXT,
`comment` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `sujet_studs`
-- Table structure `vote`
--
CREATE TABLE IF NOT EXISTS `sujet_studs` (
`id_sondage` char(16) NOT NULL,
`sujet` text,
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `vote` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`choices` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `user_studs`
-- Data for Name: poll; Type: TABLE DATA;
--
CREATE TABLE IF NOT EXISTS `user_studs` (
`id_users` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nom` varchar(64) NOT NULL,
`id_sondage` char(16) NOT NULL,
`reponses` text NOT NULL,
PRIMARY KEY (`id_users`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=160284 ;
INSERT INTO `sondage`
(`id_sondage`, `commentaires`, `mail_admin`, `nom_admin`,
`titre`, `id_sondage_admin`,
`date_fin`, `format`)
INSERT INTO `poll`
(`id`, `description`, `admin_mail`, `admin_name`, `title`, `admin_id`, `end_date`, `format`)
VALUES
('aqg259dth55iuhwm','Repas de Noel du service','Stephanie@retaillard.com','Stephanie',
'Repas de Noel','aqg259dth55iuhwmy9d8jlwk',
FROM_UNIXTIME('1627100361'),'D+');
('aqg259dth55iuhwm', 'Repas de Noel du service', 'Stephanie@retaillard.com', 'Stephanie', 'Repas de Noel',
'aqg259dth55iuhwmy9d8jlwk', FROM_UNIXTIME('1627100361'), 'D');
--
-- Data for Name: sujet_studs; Type: TABLE DATA;
-- Data for Name: slot; Type: TABLE DATA;
--
INSERT INTO `sujet_studs` (`id_sondage`, `sujet`) VALUES
('aqg259dth55iuhwm','1225839600@12h,1225839600@19h,1226012400@12h,1226012400@19h,1226876400@12h,1226876400@19h,1227049200@12h,1227049200@19h,1227826800@12h,1227826800@19h');
INSERT INTO `slot` (`poll_id`, `title`, `moments`) VALUES
('aqg259dth55iuhwm', '1225839600', '12h,19h'),
('aqg259dth55iuhwm', '1226012400', '12h,19h'),
('aqg259dth55iuhwm', '1226876400', '12h,19h'),
('aqg259dth55iuhwm', '1227826800', '12h,19h');
--
-- Data for Name: user_studs; Type: TABLE DATA;
-- Data for Name: vote; Type: TABLE DATA;
--
INSERT INTO `user_studs` (`nom`, `id_sondage`, `reponses`, `id_users`) VALUES
('marcel','aqg259dth55iuhwm','0110111101','933'),
('paul','aqg259dth55iuhwm','1011010111','935'),
('sophie','aqg259dth55iuhwm','1110110000','945'),
('barack','aqg259dth55iuhwm','0110000','948'),
('takashi','aqg259dth55iuhwm','0000110100','951'),
('albert','aqg259dth55iuhwm','1010110','975'),
('alfred','aqg259dth55iuhwm','0110010','1135'),
('marcs','aqg259dth55iuhwm','0100001010','1143'),
('laure','aqg259dth55iuhwm','0011000','1347'),
('benda','aqg259dth55iuhwm','1101101100','1667'),
('Albert','aqg259dth55iuhwm','1111110011','1668');
INSERT INTO `vote` (`name`, `poll_id`, `choices`) VALUES
('marcel', 'aqg259dth55iuhwm', '02202222'),
('paul', 'aqg259dth55iuhwm', '20220202'),
('sophie', 'aqg259dth55iuhwm', '22202200'),
('barack', 'aqg259dth55iuhwm', '02200000'),
('takashi', 'aqg259dth55iuhwm', '00002202'),
('albert', 'aqg259dth55iuhwm', '20202200'),
('alfred', 'aqg259dth55iuhwm', '02200200'),
('marcs', 'aqg259dth55iuhwm', '02000020'),
('laure', 'aqg259dth55iuhwm', '00220000'),
('benda', 'aqg259dth55iuhwm', '22022022'),
('albert', 'aqg259dth55iuhwm', '22222200');

View File

@ -1,6 +1,6 @@
$(document).ready(function() {
var lang = $('html').attr('lang');
// Datepicker
var framadatepicker = function() {
$('.input-group.date').datepicker({
@ -32,25 +32,13 @@ $(document).ready(function() {
var datepickerfocus = false; // a11y : datepicker not display on focus until there is one click on the button
var lastDate = new Date();
$(document).on('click','.input-group.date .input-group-addon', function() {
datepickerfocus = true;
// Re-init datepicker config before displaying
$(this).parent().datepicker(framadatepicker());
$(this).parent().datepicker('show');
// Trick to keep the last datepicker view
if ($(this).parent().find('input').val() == '') {
$('.input-group.date input').each(function(){
if($(this).val()!='') {
lastDate = $(this).val();
}
});
$(this).parent().datepicker('setDate', lastDate);
$(this).parent().datepicker('setDate', '');
}
// Trick to refresh calendar
$('.datepicker-days .prev').trigger('click');
$('.datepicker-days .next').trigger('click');
@ -60,9 +48,8 @@ $(document).ready(function() {
$(document).on('focus','.input-group.date input', function() {
if(datepickerfocus) {
// unfocus and click instead (because we are not in a11y mode anymore)
$(this).blur();
$(this).parent().find('.input-group-addon').trigger('click');
$(this).parent('.input-group.date').datepicker(framadatepicker());
$(this).parent('.input-group.date').datepicker('show');
}
});
/**
@ -201,17 +188,25 @@ $(document).ready(function() {
});
});
// 1 day filled and you can submit
// 1 day and 2 hours or 2 days and you can submit
function SubmitDaysAvalaible() {
var nb_filled_days = 0;
var nb_filled_hours = 0;
$('#selected-days fieldset legend input').each(function() {
if($(this).val()!='') {
nb_filled_days++;
}
});
$('#selected-days .hours').each(function() {
if($(this).val()!='') {
nb_filled_hours++;
}
});
if (nb_filled_days>0) {
if (nb_filled_days>1) {
$('button[name="choixheures"]').removeClass('disabled');
} else if (nb_filled_hours>1 && nb_filled_days==1) {
$('button[name="choixheures"]').removeClass('disabled');
} else {
$('button[name="choixheures"]').addClass('disabled');
@ -223,8 +218,8 @@ $(document).ready(function() {
});
SubmitDaysAvalaible();
// 1 days and you can remove a day or copy hours
if($('#selected-days fieldset').length>0) {
// 2 days and you can remove a day or copy hours
if($('#selected-days fieldset').length>1) {
$('#remove-a-day, #copyhours').removeClass('disabled');
}
@ -261,13 +256,13 @@ $(document).ready(function() {
$('.choice-field:last').remove();
var nb_choices = $('.choice-field').length;
$('#choice'+(nb_choices-1)).focus();
if (nb_choices == 1) {
if (nb_choices == 2) {
$('#remove-a-choice').addClass('disabled');
};
SubmitChoicesAvalaible();
});
// 1 choice filled and you can submit
// 2 choices filled and you can submit
function SubmitChoicesAvalaible() {
var nb_filled_choices = 0;
$('.choice-field input').each(function() {
@ -275,7 +270,7 @@ $(document).ready(function() {
nb_filled_choices++;
}
});
if(nb_filled_choices>0) {
if(nb_filled_choices>1) {
$('button[name="fin_sondage_autre"]').removeClass('disabled');
} else {
$('button[name="fin_sondage_autre"]').addClass('disabled');
@ -327,6 +322,20 @@ $(document).ready(function() {
return false;
});
$('#name-form .btn-edit').on('click', function() {
$('#name-form p').hide();
$('.js-name').removeClass("hidden");
$('.js-name input').focus();
return false;
});
$('#name-form .btn-cancel').on('click', function() {
$('#name-form p').show();
$('#name-form .js-name').addClass("hidden");
$('#name-form .btn-edit').focus();
return false;
});
$('#email-form .btn-edit').on('click', function() {
$('#email-form p').hide();
$('#email-form .js-email').removeClass("hidden");
@ -369,6 +378,20 @@ $(document).ready(function() {
return false;
});
$('#expiration-form .btn-edit').on('click', function() {
$('#expiration-form p').hide();
$('.js-expiration').removeClass("hidden");
$('.js-expiration input').focus();
return false;
});
$('#expiration-form .btn-cancel').on('click', function() {
$('#expiration-form p').show();
$('#expiration-form .js-expiration').addClass("hidden");
$('#expiration-form .btn-edit').focus();
return false;
});
// Horizontal scroll buttons
if($('.results').width() > $('.container').width()) {
$('.scroll-buttons').removeClass('hidden');

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -66,8 +66,8 @@ msgstr "(in the format name@mail.com)"
msgid "Description"
msgstr "Description"
msgid "Back to the homepage of "
msgstr "Back to the homepage of "
msgid "Back to the homepage of"
msgstr "Back to the homepage of"
msgid "Error!"
msgstr "Error!"
@ -81,9 +81,6 @@ msgstr "dd/mm/yyyy"
msgid "%A, den %e. %B %Y"
msgstr "%A, den %e. %B %Y"
msgid "Expiration's date"
msgstr "Expiration's date"
msgid "days"
msgstr "days"
@ -199,14 +196,23 @@ msgstr "Cancel the title edit"
msgid "Initiator of the poll"
msgstr "Initiator of the poll"
msgid "Edit the name"
msgstr "Edit the name"
msgid "Save the new name"
msgstr "Save the new name"
msgid "Cancel the name edit"
msgstr "Cancel the name edit"
msgid "Email"
msgstr "Email"
msgid "Edit the email adress"
msgstr "Edit the email adress"
msgid "Save the email address "
msgstr "Save the email address "
msgid "Save the email address"
msgstr "Save the email address"
msgid "Cancel the email address edit"
msgstr "Cancel the email address edit"
@ -226,6 +232,18 @@ msgstr "Public link of the poll"
msgid "Admin link of the poll"
msgstr "Admin link of the poll"
msgid "Expiration's date"
msgstr "Expiration's date"
msgid "Edit the expiration's date"
msgstr "Edit the expiration's date"
msgid "Save the new expiration's date"
msgstr "Save the new expiration's date"
msgid "Cancel the expiration's date edit"
msgstr "Cancel the expiration's date edit"
msgid "Poll rules"
msgstr "Poll rules"
@ -247,15 +265,18 @@ msgstr "Save the new rules"
msgid "Cancel the rules edit"
msgstr "Cancel the rules edit"
msgid "The name is invalid."
msgstr "Le nom n'est pas valide."
# Help text adminstuds.php
msgid "As poll administrator, you can change all the lines of this poll with this button "
msgstr "As poll administrator, you can change all the lines of this poll with this button "
msgid "As poll administrator, you can change all the lines of this poll with this button"
msgstr "As poll administrator, you can change all the lines of this poll with this button"
msgid " remove a column or a line with "
msgstr " remove a column or a line with "
msgid "remove a column or a line with"
msgstr "remove a column or a line with"
msgid "and add a new column with "
msgstr "and add a new column with "
msgid "and add a new column with"
msgstr "and add a new column with"
msgid "Finally, you can change the informations of this poll like the title, the comments or your email address."
msgstr "Finally, you can change the informations of this poll like the title, the comments or your email address."
@ -437,8 +458,8 @@ msgstr "Go to step 2"
msgid "Enter a title"
msgstr "Enter a title"
msgid "Characters < > and \" are not permitted"
msgstr "Characters < > and \" are not permitted"
msgid "Something is wrong with the format"
msgstr "Something is wrong with the format"
msgid "Enter an email address"
msgstr "Enter an email address"
@ -461,8 +482,8 @@ msgstr "Poll dates (2 on 3)"
msgid "Choose the dates of your poll"
msgstr "Choose the dates of your poll"
msgid "To schedule an event, it's better to propose at least two choices (two hours for one day or two days)."
msgstr "To schedule an event, it's better to propose at least two choices (two hours for one day or two days)."
msgid "To schedule an event you need to propose at least two choices (two hours for one day or two days)."
msgstr "To schedule an event you need to propose at least two choices (two hours for one day or two days)."
msgid "You can add or remove additionnal days and hours with the buttons"
msgstr "You can add or remove additionnal days and hours with the buttons"
@ -501,8 +522,8 @@ msgstr "Remove all hours"
msgid "Poll subjects (2 on 3)"
msgstr "Poll subjects (2 on 3)"
msgid "To make a generic poll, it's better to propose at least two choices between differents subjects."
msgstr "To make a generic poll, it's better to propose at least two choices between differents subjects."
msgid "To make a generic poll you need to propose at least two choices between differents subjects."
msgstr "To make a generic poll you need to propose at least two choices between differents subjects."
msgid "You can add or remove additional choices with the buttons"
msgstr "You can add or remove additional choices with the buttons"
@ -576,15 +597,18 @@ msgstr "Removal date:"
msgid "Your poll will be automatically removed after"
msgstr "Your poll will be automatically removed after"
msgid "You can fix another removal date for it."
msgstr "You can fix another removal date for it."
msgid "You can set a closer removal date for it."
msgstr "You can set a closer removal date for it."
msgid "Removal date (optional)"
msgstr "Removal date (optional)"
############# Admin #############
msgid "Polls administrator"
msgstr "Polls administrator"
msgid "Polls"
msgstr "Polls"
msgid "Migration"
msgstr "Migration"
msgid "Confirm removal of the poll "
msgstr "Confirm removal of the poll "
@ -619,16 +643,54 @@ msgstr "Change the poll"
msgid "Logs"
msgstr "Logs"
msgid "Summary"
msgstr "Summary"
msgid "Success"
msgstr "Success"
msgid "Fail"
msgstr "Fail"
msgid "Nothing"
msgstr "Nothing"
msgid "Succeeded:"
msgstr "Succeeded:"
msgid "Failed:"
msgstr "Failed:"
msgid "Skipped:"
msgstr "Skipped:"
msgid "Pages:"
msgstr "Pages:"
########### Mails ###########
# Mails studs.php
msgid "Poll's participation"
msgstr "Poll's participation"
msgid ""
"has filled a line.\n"
"filled a vote.\n"
"You can find your poll at the link"
msgstr ""
"has filled a line.\n"
"filled a vote.\n"
"You can find your poll at the link"
msgid ""
"updated a vote.\n"
"You can find your poll at the link"
msgstr ""
"updated a vote.\n"
"You can find your poll at the link"
msgid ""
"wrote a comment.\n"
"You can find your poll at the link"
msgstr ""
"wrote a comment.\n"
"You can find your poll at the link"
msgid "Thanks for your confidence."

Binary file not shown.

View File

@ -130,7 +130,7 @@ msgstr "Usted no habia llenado la primera pagina dela encuesta"
#: choix_autre.php:63
#: adminstuds.php:79
#: adminstuds.php:1044
msgid "Back to the homepage of "
msgid "Back to the homepage of"
msgstr "Retroceder al inicio de"
#: choix_date.php:220
@ -255,8 +255,8 @@ msgstr "Introducza un t&iacute;tulo"
#: infos_sondage.php:133
#: infos_sondage.php:138
#: infos_sondage.php:150
msgid "Characters < > and \" are not permitted"
msgstr "Los caracteres < > y \" no estan autorizados!"
msgid "Something is wrong with the format"
msgstr "Something is wrong with the format"
#: infos_sondage.php:136
msgid "Comments: "
@ -393,12 +393,26 @@ msgstr "Participaci&oacute;n a la encuesta"
#: studs.php:171
#: studs.php:211
msgid ""
"has filled a line.\n"
"filled a vote.\n"
"You can find your poll at the link"
msgstr ""
"acaba de llenar una l&iacute;nea.\n"
"Usted puede retroceder a su encuesta con el enlace siguiente"
msgid ""
"updated a vote.\n"
"You can find your poll at the link"
msgstr ""
"updated a vote.\n"
"Usted puede retroceder a su encuesta con el enlace siguiente"
msgid ""
"wrote a comment.\n"
"You can find your poll at the link"
msgstr ""
"wrote a comment.\n"
"Usted puede retroceder a su encuesta con el enlace siguiente"
#: studs.php:246
#: adminstuds.php:567
msgid "Initiator of the poll"
@ -526,7 +540,7 @@ msgid "Characters \" < and > are not permitted"
msgstr "Los caracteres \" < y > no estan autorizados!"
#: choix_autre.php:191
msgid "Your poll will be automatically removed after 6 months.<br> You can fix another removal date for it."
msgid "Your poll will be automatically removed after 6 months.<br> You can set a closer removal date for it."
msgstr "Su encuesta ser&aacute; automaticamente borrado dentro de 6 meses.<br> Mientras, usted puede cambiar este fecha aqu&iacute;."
#: choix_autre.php:193
@ -612,8 +626,8 @@ msgstr ""
"Usted puede cambiar su encuesta al enlace siguiente"
#: adminstuds.php:582
msgid "As poll administrator, you can change all the lines of this poll with this button : "
msgstr "En calidad de administrador, Usted puede cambiar todas la l&iacute;neas de este encuesta con este botón :"
msgid "As poll administrator, you can change all the lines of this poll with this button"
msgstr "En calidad de administrador, Usted puede cambiar todas la l&iacute;neas de este encuesta con este botón"
msgid "Edit"
msgstr "Cambio"

Binary file not shown.

View File

@ -66,7 +66,7 @@ msgstr "(au format nom@mail.com)"
msgid "Description"
msgstr "Description"
msgid "Back to the homepage of "
msgid "Back to the homepage of"
msgstr "Retourner à la page d'accueil de"
msgid "Error!"
@ -81,9 +81,6 @@ msgstr "jj/mm/aaaa"
msgid "%A, den %e. %B %Y"
msgstr "%A %e %B %Y"
msgid "Expiration's date"
msgstr "Date d'expiration"
msgid "days"
msgstr "jours"
@ -199,13 +196,22 @@ msgstr "Annuler le changement de titre"
msgid "Initiator of the poll"
msgstr "Auteur du sondage"
msgid "Edit the name"
msgstr "Modification de l'auteur"
msgid "Save the new name"
msgstr "Enregistrer l'auteur"
msgid "Cancel the name edit"
msgstr "Annuler le changement d'auteur"
msgid "Email"
msgstr "Courriel"
msgid "Edit the email adress"
msgstr "Modifier le courriel"
msgid "Save the email address "
msgid "Save the email address"
msgstr "Enregistrer le courriel"
msgid "Cancel the email address edit"
@ -226,6 +232,18 @@ msgstr "Lien public du sondage"
msgid "Admin link of the poll"
msgstr "Lien d'administration du sondage"
msgid "Expiration's date"
msgstr "Date d'expiration"
msgid "Edit the expiration's date"
msgstr "Modifier la date d'expiration"
msgid "Save the new expiration's date"
msgstr "Enregistrer la date d'expiration"
msgid "Cancel the expiration's date edit"
msgstr "Annuler le changement de date d'expiration"
msgid "Poll rules"
msgstr "Permissions du sondage"
@ -247,15 +265,18 @@ msgstr "Enregistrer les nouvelles permissions"
msgid "Cancel the rules edit"
msgstr "Annuler le changement de permissions"
msgid "The name is invalid."
msgstr "Le nom n'est pas valide."
# Help text adminstuds.php
msgid "As poll administrator, you can change all the lines of this poll with this button "
msgstr "En tant qu'administrateur, vous pouvez modifier toutes les lignes de ce sondage avec ce bouton "
msgid "As poll administrator, you can change all the lines of this poll with this button"
msgstr "En tant qu'administrateur, vous pouvez modifier toutes les lignes de ce sondage avec ce bouton"
msgid " remove a column or a line with "
msgstr " effacer une colonne ou une ligne avec "
msgid "remove a column or a line with"
msgstr "effacer une colonne ou une ligne avec"
msgid "and add a new column with "
msgstr "et si vous avez oublié de saisir un choix, vous pouvez rajouter une colonne en cliquant sur "
msgid "and add a new column with"
msgstr "et si vous avez oublié de saisir un choix, vous pouvez rajouter une colonne en cliquant sur"
msgid "Finally, you can change the informations of this poll like the title, the comments or your email address."
msgstr "Vous pouvez enfin également modifier les informations relatives à ce sondage comme le titre, les commentaires ou encore votre courriel."
@ -437,8 +458,8 @@ msgstr "Aller à l'étape 2"
msgid "Enter a title"
msgstr "Il faut saisir un titre !"
msgid "Characters < > and \" are not permitted"
msgstr "Les caractères < > et \" ne sont pas autorisés !"
msgid "Something is wrong with the format"
msgstr "Quelque chose ne va pas avec le format"
msgid "Enter an email address"
msgstr "Il faut saisir une adresse électronique !"
@ -461,8 +482,8 @@ msgstr "Choix des dates (2 sur 3)"
msgid "Choose the dates of your poll"
msgstr "Choisissez les dates de votre sondage"
msgid "To schedule an event, it's better to propose at least two choices (two hours for one day or two days)."
msgstr "Pour créer un sondage spécial dates, il est préférable de proposer au moins deux choix (deux horaires pour une même journée ou deux jours)."
msgid "To schedule an event you need to propose at least two choices (two hours for one day or two days)."
msgstr "Pour créer un sondage spécial dates vous devez proposer au moins deux choix (deux horaires pour une même journée ou deux jours)."
msgid "You can add or remove additionnal days and hours with the buttons"
msgstr "Vous pouvez ajouter ou supprimer des jours et horaires supplémentaires avec les boutons"
@ -501,8 +522,8 @@ msgstr "Effacer tous les horaires"
msgid "Poll subjects (2 on 3)"
msgstr "Choix des sujets (2 sur 3)"
msgid "To make a generic poll, it's better to propose at least two choices between differents subjects."
msgstr "Pour créer un sondage classique, il est préférable de proposer au moins deux choix différents."
msgid "To make a generic poll you need to propose at least two choices between differents subjects."
msgstr "Pour créer un sondage classique, vous devez proposer au moins deux choix différents."
msgid "You can add or remove additional choices with the buttons"
msgstr "Vous pouvez ajouter ou supprimer des choix supplémentaires avec les boutons"
@ -576,15 +597,18 @@ msgstr "Date de suppression :"
msgid "Your poll will be automatically removed after"
msgstr "Votre sondage sera automatiquement effacé dans"
msgid "You can fix another removal date for it."
msgstr "Néanmoins vous pouvez décider ci-dessous d'une date plus rapprochée pour la suppression de votre sondage."
msgid "You can set a closer removal date for it."
msgstr "Vous pouvez décider d'une date de suppression plus proche."
msgid "Removal date (optional)"
msgstr "Date de suppression (facultative)"
msgstr "Date de fin (facultative)"
############# Admin #############
msgid "Polls administrator"
msgstr "Administrateur de la base"
msgid "Polls"
msgstr "Sondages"
msgid "Migration"
msgstr "Migration"
msgid "Confirm removal of the poll "
msgstr "Confirmer la suppression du sondage "
@ -619,16 +643,54 @@ msgstr "Modifier le sondage"
msgid "Logs"
msgstr "Historique"
msgid "Summary"
msgstr "Résumé"
msgid "Success"
msgstr "Réussite"
msgid "Fail"
msgstr "Échèc"
msgid "Nothing"
msgstr "Rien"
msgid "Succeeded:"
msgstr "Réussit:"
msgid "Failed:"
msgstr "Échoué:"
msgid "Skipped:"
msgstr "Passé:"
msgid "Pages:"
msgstr "Pages :"
########### Mails ###########
# Mails studs.php
msgid "Poll's participation"
msgstr "Participation au sondage"
msgid ""
"has filled a line.\n"
"filled a vote.\n"
"You can find your poll at the link"
msgstr ""
" vient de remplir une ligne.\n"
"vient de voter.\n"
"Vous pouvez retrouver votre sondage avec le lien suivant"
msgid ""
"updated a vote.\n"
"You can find your poll at the link"
msgstr ""
"vient de mettre à jour un vote.\n"
"Vous pouvez retrouver votre sondage avec le lien suivant"
msgid ""
"vient de rédiger un commentaire.\n"
"You can find your poll at the link"
msgstr ""
"wrote a comment.\n"
"Vous pouvez retrouver votre sondage avec le lien suivant"
msgid "Thanks for your confidence."

790
studs.php
View File

@ -16,694 +16,192 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
session_start();
if (file_exists('bandeaux_local.php')) {
include_once('bandeaux_local.php');
} else {
include_once('bandeaux.php');
}
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\InputService;
use Framadate\Services\MailService;
use Framadate\Message;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
// Le fichier studs.php sert a afficher les résultats d'un sondage à un simple utilisateur.
// C'est également l'interface pour ajouter une valeur à un sondage deja créé.
$numsondage = false;
/* Constants */
/* --------- */
const UPDATE_VOTE = 1;
const ADD_VOTE = 2;
const ADD_COMMENT = 3;
//On récupère le numéro de sondage par le lien web.
if(Utils::issetAndNoEmpty('sondage', $_GET) === true) {
$numsondage = $_GET["sondage"];
$_SESSION["numsondage"] = $numsondage;
}
/* Variables */
/* --------- */
if(Utils::issetAndNoEmpty('sondage') === true) {
$numsondage = $_POST["sondage"];
$_SESSION["numsondage"] = $numsondage;
} elseif(Utils::issetAndNoEmpty('sondage', $_COOKIE) === true) {
$numsondage = $_COOKIE["sondage"];
} elseif(Utils::issetAndNoEmpty('numsondage', $_SESSION) === true) {
$numsondage = $_SESSION["numsondage"];
}
$poll_id = null;
$poll = null;
$message = null;
$editingVoteId = 0;
$dsondage = ($numsondage != false) ? Utils::get_sondage_from_id($numsondage) : false;
if (!$dsondage || $dsondage->id_sondage == ''){
Utils::print_header( _("Error!"));
/* Services */
/*----------*/
bandeau_titre(_("Error!"));
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp']);
echo '
<div class="alert alert-warning">
<h2>' . _("This poll doesn't exist !") . '</h2>
<p>' . _('Back to the homepage of ') . ' <a href="' . Utils::get_server_name() . '"> ' . NOMAPPLICATION . '</a></p>
</div>'."\n";
/* Functions */
/*-----------*/
bandeau_pied();
die();
}
//output a CSV and die()
if(Utils::issetAndNoEmpty('export', $_GET) && $dsondage !== false) {
if($_GET['export'] == 'csv') {
require_once('exportcsv.php');
/**
* 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'] = [];
}
die();
}
if ($poll->receiveNewVotes && (!isset($_SESSION['mail_sent'][$poll->id]) || $_SESSION['mail_sent'][$poll->id] !== true)) {
// quand on ajoute un commentaire utilisateur
if(isset($_POST['ajoutcomment'])) {
if (isset($_SESSION['nom']) && Utils::issetAndNoEmpty('commentuser') === false) {
// Si le nom vient de la session, on le de-htmlentities
$comment_user = html_entity_decode($_SESSION['nom'], ENT_QUOTES, 'UTF-8');
} elseif(Utils::issetAndNoEmpty('commentuser')) {
$comment_user = $_POST["commentuser"];
} elseif(isset($_POST["commentuser"])) {
$err |= COMMENT_USER_EMPTY;
} else {
$comment_user = _('anonyme');
}
$subject = '[' . NOMAPPLICATION . '] ' . _('Poll\'s participation') . ' : ' . $poll->title;
if(Utils::issetAndNoEmpty('comment') === false) {
$err |= COMMENT_EMPTY;
}
if (isset($_POST["comment"]) && !Utils::is_error(COMMENT_EMPTY) && !Utils::is_error(NO_POLL) && !Utils::is_error(COMMENT_USER_EMPTY)) {
// protection contre les XSS : htmlentities
$comment = htmlentities($_POST['comment'], ENT_QUOTES, 'UTF-8');
$comment_user = htmlentities($comment_user, ENT_QUOTES, 'UTF-8');
// Check for doublons
$comment_doublon = false;
$req = 'SELECT * FROM comments WHERE id_sondage='.$connect->Param('numsondage').' ORDER BY id_comment';
$sql = $connect->Prepare($req);
$comment_user_doublon = $connect->Execute($sql, array($numsondage));
if ($comment_user_doublon->RecordCount() != 0) {
while ( $dcomment_user_doublon=$comment_user_doublon->FetchNextObject(false)) {
if($dcomment_user_doublon->comment == $comment && $dcomment_user_doublon->usercomment == $comment_user) {
$comment_doublon = true;
};
}
$message = $name . ' ';
switch ($type) {
case UPDATE_VOTE:
$message .= _('updated a vote.\nYou can find your poll at the link') . " :\n\n";
break;
case ADD_VOTE:
$message .= _('filled a vote.\nYou can find your poll at the link') . " :\n\n";
break;
case ADD_COMMENT:
$message .= _('wrote a comment.\nYou can find your poll at the link') . " :\n\n";
break;
}
$message .= Utils::getUrlSondage($poll->admin_id, true) . "\n\n";
$message .= _('Thanks for your confidence.') . "\n" . NOMAPPLICATION;
if(!$comment_doublon) {
$req = 'INSERT INTO comments (id_sondage, comment, usercomment) VALUES ('.
$connect->Param('id_sondage').','.
$connect->Param('comment').','.
$connect->Param('comment_user').')';
$sql = $connect->Prepare($req);
$mailService->send($poll->admin_mail, $subject, $message);
$comments = $connect->Execute($sql, array($numsondage, $comment, $comment_user));
if ($comments === false) {
$err |= COMMENT_INSERT_FAILED;
}
}
$_SESSION['mail_sent'][$poll->id] = true;
}
}
/* PAGE */
/* ---- */
// Action quand on clique le bouton participer
$sql = 'SELECT * FROM user_studs WHERE id_sondage='.$connect->Param('numsondage').' ORDER BY id_users';
$sql = $connect->Prepare($sql);
$user_studs = $connect->Execute($sql, array($numsondage));
$nbcolonnes = substr_count($dsondage->sujet, ',') + 1;
if (!Utils::is_error(NO_POLL) && (isset($_POST["boutonp"]))) {
//Si le nom est bien entré
if (Utils::issetAndNoEmpty('nom') === false) {
$err |= NAME_EMPTY;
}
if(!Utils::is_error(NAME_EMPTY) && (! ( USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']) ) || $_POST["nom"] == $_SESSION["nom"])) {
$nouveauchoix = '';
for ($i=0;$i<$nbcolonnes;$i++) {
// radio checked 1 = Yes, 2 = Ifneedbe, 0 = No
if (isset($_POST["choix$i"])) {
switch ($_POST["choix$i"]) {
case 1: $nouveauchoix .= "1";break;
case 2: $nouveauchoix .= "2";break;
default: $nouveauchoix .= "0";break;
}
}
}
$nom=substr($_POST["nom"],0,64);
// protection contre les XSS : htmlentities
$nom = htmlentities($nom, ENT_QUOTES, 'UTF-8');
while($user = $user_studs->FetchNextObject(false)) {
if ($nom == $user->nom) {
$err |= NAME_TAKEN;
}
}
// Ecriture des choix de l'utilisateur dans la base
if (!Utils::is_error(NAME_TAKEN) && !Utils::is_error(NAME_EMPTY)) {
$sql = 'INSERT INTO user_studs (nom,id_sondage,reponses) VALUES ('.
$connect->Param('nom').', '.
$connect->Param('numsondage').', '.
$connect->Param('nouveauchoix').')';
$sql = $connect->Prepare($sql);
// Todo : Il faudrait lever une erreur en cas d'erreur d'insertion
$connect->Execute($sql, array($nom, $numsondage, $nouveauchoix));
if ($dsondage->mailsonde || /* compatibility for non boolean DB */ $dsondage->mailsonde=="yes" || $dsondage->mailsonde=="true") {
if($config['use_smtp']==true){
Utils::sendEmail( "$dsondage->mail_admin",
"[".NOMAPPLICATION."] "._("Poll's participation")." : ".html_entity_decode($dsondage->titre, ENT_QUOTES, 'UTF-8')."",
html_entity_decode("\"$nom\" ", ENT_QUOTES, 'UTF-8').
_("has filled a line.\nYou can find your poll at the link") . " :\n\n".
Utils::getUrlSondage($numsondage) . " \n\n" .
_("Thanks for your confidence.") . "\n". NOMAPPLICATION );
}
}
}
} else {
$err |= NAME_EMPTY;
}
if (!empty($_GET['poll'])) {
$poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$poll = $pollService->findById($poll_id);
}
if($err != 0) {
Utils::print_header(_("Error!").' - '.$dsondage->titre);
bandeau_titre(_("Error!"));
echo '<div class="alert alert-danger"><ul class="list-unstyled">'."\n";
if(Utils::is_error(NAME_EMPTY)) {
echo '<li>' . _("Enter a name") . "</li>\n";
}
if(Utils::is_error(NAME_TAKEN)) {
echo '<li>' . _("The name you've chosen already exist in this poll!") . "</li>\n";
}
if(Utils::is_error(COMMENT_EMPTY) || Utils::is_error(COMMENT_USER_EMPTY)) {
echo '<li>' . _("Enter a name and a comment!") . "</li>\n";
}
if(Utils::is_error(COMMENT_INSERT_FAILED) ) {
echo '<li>' . _("Failed to insert the comment!") . "</li>\n";
}
echo '</ul></div>';
} else {
Utils::print_header(_('Poll').' - '.$dsondage->titre);
bandeau_titre(_('Poll').' - '.$dsondage->titre);
if (!$poll) {
$smarty->assign('error', _('This poll doesn\'t exist !'));
$smarty->display('error.tpl');
exit;
}
$title=stripslashes(str_replace("\\","",$dsondage->titre));
echo '
<div class="jumbotron">
<div class="row">
<div class="col-md-7">
<h3>'.$title.'</h3>
</div>
<div class="col-md-5">
<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="window.location.href=\'' . Utils::get_server_name() . 'exportcsv.php?numsondage=' . $numsondage . '\';return false;" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span> ' . _('Export to CSV') . '</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-5">
<div class="form-group">
<h4 class="control-label">'. _("Initiator of the poll") .'</h4>
<p class="form-control-static"> '.stripslashes($dsondage->nom_admin).'</p>
</div>
<div class="form-group">
<label for="public-link"><a class="public-link" href="' . Utils::getUrlSondage($dsondage->id_sondage) . '">'._("Public link of the poll") .' <span class="btn-link glyphicon glyphicon-link"></span></a></label>
<input class="form-control" id="public-link" type="text" readonly="readonly" value="' . Utils::getUrlSondage($dsondage->id_sondage) . '" />
</div>
</div>'."\n";
// -------------------------------
// A vote is going to be edited
// -------------------------------
//affichage de la description du sondage
if ($dsondage->commentaires) {
$commentaires = $dsondage->commentaires;
$commentaires=nl2br(str_replace("\\","",$commentaires));
echo '
<div class="form-group col-md-7">
<h4 class="control-label">'._("Description") .'</h4><br />
<p class="form-control-static well">'. $commentaires .'</p>
</div>';
}
echo '
</div>
</div>'."\n"; // .jumbotron
//On récupere les données et les sujets du sondage
$nblignes = $user_studs->RecordCount();
//on teste pour voir si une ligne doit etre modifiée
$testmodifier = false;
$ligneamodifier = -1;
for ($i=0;$i<$nblignes;$i++) {
if (isset($_POST["modifierligne$i"])) {
$ligneamodifier = $i;
}
//test pour voir si une ligne est a modifier
if (isset($_POST['validermodifier'.$i])) {
$modifier = $i;
$testmodifier = true;
}
if (!empty($_POST['edit_vote'])) {
$editingVoteId = filter_input(INPUT_POST, 'edit_vote', FILTER_VALIDATE_INT);
}
//si le test est valide alors on affiche des checkbox pour entrer de nouvelles valeurs
if ($testmodifier) {
$nouveauchoix = '';
for ($i=0;$i<$nbcolonnes;$i++) {
// radio checked 1 = Yes, 2 = Ifneedbe, 0 = No
if (isset($_POST["choix$i"])) {
switch ($_POST["choix$i"]) {
case 1: $nouveauchoix .= "1";break;
case 2: $nouveauchoix .= "2";break;
default: $nouveauchoix .= "0";break;
}
}
// -------------------------------
// Something to save (edit or add)
// -------------------------------
if (!empty($_POST['save'])) { // Save edition of an old vote
$name = filter_input(INPUT_POST, 'name', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => NAME_REGEX]]);
$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', _('Something is going wrong...'));
}
if (count($choices) != count($_POST['choices'])) {
$message = new Message('danger', _('There is a problem with your choices.'));
}
$compteur=0;
while ($data = $user_studs->FetchNextObject(false) ) {
//mise a jour des données de l'utilisateur dans la base SQL
if ($compteur == $modifier) {
$sql = 'UPDATE user_studs SET reponses='.$connect->Param('nouveauchoix').' WHERE nom='.$connect->Param('nom').' AND id_users='.$connect->Param('id_users');
$sql = $connect->Prepare($sql);
$connect->Execute($sql, array($nouveauchoix, $data->nom, $data->id_users));
if ($dsondage->mailsonde=="yes") {
Utils::sendEmail( "$dsondage->mail_admin", "[".NOMAPPLICATION."] " . _("Poll's participation") . " : ".html_entity_decode($dsondage->titre, ENT_QUOTES, 'UTF-8'), "\"".html_entity_decode($data->nom, ENT_QUOTES, 'UTF-8')."\""."" . _("has filled a line.\nYou can find your poll at the link") . " :\n\n" . Utils::getUrlSondage($numsondage) . " \n\n" . _("Thanks for your confidence.") . "\n".NOMAPPLICATION );
}
}
$compteur++;
}
}
//recuperation des utilisateurs du sondage
$sql = 'SELECT * FROM user_studs WHERE id_sondage='.$connect->Param('numsondage').' ORDER BY id_users';
$sql = $connect->Prepare($sql);
$user_studs = $connect->Execute($sql, array($numsondage));
//reformatage des données des sujets du sondage
$toutsujet = explode(",",$dsondage->sujet);
// Table headers
$thead = '<thead>';
// Button in the first td to avoid remove col on "Return" keypress)
$border = array(); // bordure pour distinguer les mois
$td_headers = array(); // for a11y, headers="M1 D4 H5" on each td
$radio_title = array(); // date for
// Dates poll
if ($dsondage->format=="D"||$dsondage->format=="D+"||$dsondage->format=="D-") {
$tr_months = '<tr><th role="presentation"></th>';
$tr_days = '<tr><th role="presentation"></th>';
$tr_hours = '<tr><th role="presentation"></th>';
// Headers
$colspan_month = 1;
$colspan_day = 1;
for ($i = 0; $i < count($toutsujet); $i++) {
// Current date
$current = $toutsujet[$i];
$horoCur = explode("@",$current); //horoCur[0] = date, horoCur[1] = hour
if (isset($toutsujet[$i+1])){
$next = $toutsujet[$i+1];
$horoNext = explode("@",$next);
}
$border[$i] = false;
$radio_title[$i] = strftime($date_format['txt_short'],$horoCur[0]);
// Months
$td_headers[$i] = 'M'.($i+1-$colspan_month);
if (isset($toutsujet[$i+1]) && strftime("%B", $horoCur[0]) == strftime("%B", $horoNext[0]) && strftime("%Y", $horoCur[0]) == strftime("%Y", $horoNext[0])){
$colspan_month++;
if ($message == null) {
// Update vote
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices);
if ($result) {
$message = new Message('success', _('Update vote successfully.'));
sendUpdateNotification($poll, $mailService, $name, UPDATE_VOTE);
} else {
$border[$i] = true;
$tr_months .= '<th colspan="'.$colspan_month.'" class="bg-primary month" id="M'.($i+1-$colspan_month).'">'.strftime("%B",$horoCur[0]).' '.strftime("%Y", $horoCur[0]).'</th>';
$colspan_month=1;
$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' => NAME_REGEX]]);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
// Days
$td_headers[$i] .= ' D'.($i+1-$colspan_day);
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 (isset($toutsujet[$i+1]) && strftime($date_format['txt_day'],$horoCur[0])==strftime($date_format['txt_day'],$horoNext[0])&&strftime("%B",$horoCur[0])==strftime("%B",$horoNext[0])){
$colspan_day++;
if ($message == null) {
// Add vote
$result = $pollService->addVote($poll_id, $name, $choices);
if ($result) {
$message = new Message('success', _('Update vote successfully.'));
sendUpdateNotification($poll, $mailService, $name, ADD_VOTE);
} else {
$rbd = ($border[$i]) ? ' rbd' : '';
$tr_days .= '<th colspan="'.$colspan_day.'" class="bg-primary day'.$rbd.'" id="D'.($i+1-$colspan_day).'">'.strftime($date_format['txt_day'],$horoCur[0]).'</th>';
$colspan_day=1;
$message = new Message('danger', _('Update vote failed.'));
}
}
}
// Hours
$rbd = ($border[$i]) ? ' rbd' : '';
if (isset($horoCur[1]) && $horoCur[1] !== '') {
$tr_hours .= '<th class="bg-info'.$rbd.'" id="H'.$i.'" title="'.$horoCur[1].'">'.$horoCur[1].'</th>';
$radio_title[$i] .= ' - '.$horoCur[1];
$td_headers[$i] .= ' H'.$i;
// -------------------------------
// Add a comment
// -------------------------------
if (isset($_POST['add_comment'])) {
$name = filter_input(INPUT_POST, 'name', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => NAME_REGEX]]);
$comment = strip_tags($_POST['comment']);
if (empty($name)) {
$message = new Message('danger', _('Name is incorrect.'));
}
if ($message == null) {
// Add comment
$result = $pollService->addComment($poll_id, $name, $comment);
if ($result) {
$message = new Message('success', _('Comment added.'));
sendUpdateNotification($poll, $mailService, $name, ADD_COMMENT);
} else {
$tr_hours .= '<th class="bg-info'.$rbd.'"></th>';
$message = new Message('danger', _('Comment failed.'));
}
}
$border[count($border)-1] = false; // suppression de la bordure droite du dernier mois
$tr_months .= '<th></th></tr>';
$tr_days .= '<th></th></tr>';
$tr_hours .= '<th></th></tr>';
$thead = "\n".$tr_months."\n".$tr_days."\n".$tr_hours."\n";
// Subjects poll
} else {
$toutsujet=str_replace("@","<br />",$toutsujet);
$tr_subjects = '<tr><th role="presentation"></th>';
for ($i = 0; isset($toutsujet[$i]); $i++) {
$td_headers[$i]='';$radio_title[$i]=''; // init before concatenate
// Subjects
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/',$toutsujet[$i],$md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/',$toutsujet[$i],$md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/',$toutsujet[$i],$md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0]!='' && isset($md_a_img[3][0]) && $md_a_img[3][0]!='') { // [![alt](src)](href)
$th_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0]!='') ? stripslashes($md_a_img[1][0]) : _("Choice") .' '.($i+1);
$th_subject_html = '<a href="'.$md_a_img[3][0].'"><img src="'.$md_a_img[2][0].'" class="img-responsive" alt="'.$th_subject_text.'" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0]!='') { // ![alt](src)
$th_subject_text = (isset($md_img[1][0]) && $md_img[1][0]!='') ? stripslashes($md_img[1][0]) : _("Choice") .' '.($i+1);
$th_subject_html = '<img src="'.$md_img[2][0].'" class="img-responsive" alt="'.$th_subject_text.'" />';
} elseif (isset($md_a[2][0]) && $md_a[2][0]!='') { // [text](href)
$th_subject_text = (isset($md_a[1][0]) && $md_a[1][0]!='') ? stripslashes($md_a[1][0]) : _("Choice") .' '.($i+1);
$th_subject_html = '<a href="'.$md_a[2][0].'">'.$th_subject_text.'</a>';
} else { // text only
$th_subject_text = stripslashes($toutsujet[$i]);
$th_subject_html = $th_subject_text;
}
$tr_subjects .= '<th class="bg-info" id="S'.$i.'" title="'.$th_subject_text.'">'.$th_subject_html.'</th>';
$border[$i] = false;
$td_headers[$i] .= 'S'.$i;
$radio_title[$i] .= $th_subject_text;
}
$thead = $tr_subjects.'<th></th></tr>';
}
// Print headers
echo '
<form name="formulaire" action="' . Utils::getUrlSondage($dsondage->id_sondage) . '" method="POST">
<input type="hidden" name="sondage" value="' . $numsondage . '"/>
';
if ($dsondage->format=="A-" || $dsondage->format=="D-") {
echo '
<div class="alert alert-danger">
<p>' . _("The administrator locked this poll, votes and comments are frozen, it's not possible to participate anymore.") . '</p>
<p aria-hidden="true"><b>' . _('Legend:'). '</b> <span class="glyphicon glyphicon-ok"></span> =' . _('Yes') . ', <b>(<span class="glyphicon glyphicon-ok"></span>)</b> = ' . _('Ifneedbe') . ', <span class="glyphicon glyphicon-ban-circle"></span> = ' . _('No') . '</p>
</div>';
} else {
echo '
<div class="alert alert-info">
<p>' . _("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.") . '</p>
<p aria-hidden="true"><b>' . _('Legend:'). '</b> <span class="glyphicon glyphicon-ok"></span> =' . _('Yes') . ', <b>(<span class="glyphicon glyphicon-ok"></span>)</b> = ' . _('Ifneedbe') . ', <span class="glyphicon glyphicon-ban-circle"></span> = ' . _('No') . '</p>
</div>';
}
echo'
<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="' . _('Scroll to the left') . '">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button class="btn btn-sm btn-link scroll-right" title="' . _('Scroll to the right') . '">
<span class="glyphicon glyphicon-chevron-right"></span>
</button>
</div>
</div>
// Retrieve data
$slots = $pollService->allSlotsByPollId($poll_id);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
<h3>'._('Votes of the poll ').'</h3>
<div id="tableContainer" class="tableContainer">
<table class="results">
<caption class="sr-only">'._('Votes of the poll ').$title.'</caption>
<thead>'. $thead . '</thead>
<tbody>';
// Print poll results
// Assign data to template
$smarty->assign('poll_id', $poll_id);
$smarty->assign('poll', $poll);
$smarty->assign('title', _('Poll') . ' - ' . $poll->title);
$smarty->assign('expired', $poll->end_date < time());
$smarty->assign('deletion_date', $poll->end_date + PURGE_DELAY * 86400);
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
$smarty->assign('comments', $comments);
$smarty->assign('editingVoteId', $editingVoteId);
$smarty->assign('message', $message);
$smarty->assign('admin', false);
//Usager pré-authentifié dans la liste?
$user_mod = false;
//affichage des resultats actuels
$somme[] = 0;
$compteur = 0;
while ($data = $user_studs->FetchNextObject(false)) {
$ensemblereponses = $data->reponses;
//affichage du nom
$nombase=str_replace("°","'",$data->nom);
echo '<tr>
<th class="bg-info">'.stripslashes($nombase).'</th>'."\n";
// ligne d'un usager pré-authentifié
$mod_ok = !( USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']) ) || ($nombase == $_SESSION['nom']);
$user_mod |= $mod_ok;
// pour chaque colonne
for ($k=0; $k < $nbcolonnes; $k++) {
// on remplace les choix de l'utilisateur par une ligne de checkbox pour recuperer de nouvelles valeurs
if ($compteur == $ligneamodifier) {
$car = substr($ensemblereponses, $k , 1);
// variable pour afficher la valeur cochée
$car_html[0]='value="0"';$car_html[1]='value="1"';$car_html[2]='value="2"';
switch ($car) {
case "1": $car_html[1]='value="1" checked';break;
case "2": $car_html[2]='value="2" checked';break;
default: $car_html[0]='value="0" checked';break;
}
echo '
<td class="bg-info" headers="'.$td_headers[$k ].'">
<ul class="list-unstyled choice">
<li class="yes">
<input type="radio" id="y-choice-'.$k.'" name="choix'.$k.'" '.$car_html[1].' />
<label class="btn btn-default btn-xs" for="y-choice-'.$k.'" title="' . _('Vote "yes" for ') . $radio_title[$k] . '">
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">' . _('Yes') . '</span>
</label>
</li>
<li class="ifneedbe">
<input type="radio" id="i-choice-'.$k.'" name="choix'.$k.'" '.$car_html[2].' />
<label class="btn btn-default btn-xs" for="i-choice-'.$k.'" title="' . _('Vote "ifneedbe" for ') . $radio_title[$k] . '">
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">' . _('Ifneedbe') . '</span>
</label>
</li>
<li class="no">
<input type="radio" id="n-choice-'.$k.'" name="choix'.$k.'" '.$car_html[0].'/>
<label class="btn btn-default btn-xs" for="n-choice-'.$k.'" title="' . _('Vote "no" for ') . $radio_title[$k] . '">
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">' . _('No') . '</span>
</label>
</li>
</ul>
</td>'."\n";
} else {
$rbd = ($border[$k]) ? ' rbd' : '';
$car = substr($ensemblereponses, $k, 1);
switch ($car) {
case "1": echo '<td class="bg-success text-success'.$rbd.'" headers="'.$td_headers[$k].'"><span class="glyphicon glyphicon-ok"></span><span class="sr-only"> ' . _('Yes') . '</span></td>'."\n";
if (isset($somme[$k]) === false) {
$somme[$k] = 0;
}
$somme[$k]++; break;
case "2": echo '<td class="bg-warning text-warning'.$rbd.'" headers="'.$td_headers[$k].'">(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only"> ' . _('Yes') . _(', ifneedbe') . '</span></td>'."\n"; break;
default: echo '<td class="bg-danger'.$rbd.'" headers="'.$td_headers[$k].'"><span class="sr-only">' . _('No') . '</span></td>'."\n";
}
}
}
//a la fin de chaque ligne se trouve les boutons modifier
if ($compteur != $ligneamodifier && ($dsondage->format=="A+"||$dsondage->format=="D+") && $mod_ok) {
echo '
<td>
<button type="submit" class="btn btn-link btn-sm" name="modifierligne'.$compteur.'" title="'. _('Edit the line:') .' '.stripslashes($nombase).'">
<span class="glyphicon glyphicon-pencil"></span><span class="sr-only">' . _('Edit') . '</span>
</button>
</td>'."\n";
}
//demande de confirmation pour modification de ligne
for ($i=0;$i<$nblignes;$i++) {
if (isset($_POST["modifierligne$i"])) {
if ($compteur == $i) {
echo '<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs" name="validermodifier'.$compteur.'" title="'. _('Save the choices') .' '.stripslashes($nombase).'">'. _('Save') .'</button></td>'."\n";
}
}
}
$compteur++;
echo '</tr>'."\n";
}
// affichage de la ligne pour un nouvel utilisateur
if (( !(USE_REMOTE_USER && isset($_SERVER['REMOTE_USER'])) || !$user_mod) && $ligneamodifier==-1 && ($dsondage->format!="A-" && $dsondage->format!="D-")) {
//affichage de la case vide de texte pour un nouvel utilisateur
echo '<tr id="vote-form">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">
<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') .'" />
</div>
</td>'."\n";
//une ligne de checkbox pour le choix du nouvel utilisateur
for ($i = 0; $i < $nbcolonnes; $i++) {
echo '
<td class="bg-info" headers="'.$td_headers[$i].'">
<ul class="list-unstyled choice">
<li class="yes">
<input type="radio" id="y-choice-'.$i.'" name="choix'.$i.'" value="1" />
<label class="btn btn-default btn-xs" for="y-choice-'.$i.'" title="' . _('Vote "yes" for ') . $radio_title[$i] . '">
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">' . _('Yes') . '</span>
</label>
</li>
<li class="ifneedbe">
<input type="radio" id="i-choice-'.$i.'" name="choix'.$i.'" value="2" />
<label class="btn btn-default btn-xs" for="i-choice-'.$i.'" title="' . _('Vote "ifneedbe" for ') . $radio_title[$i] . '">
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">' . _('Ifneedbe') . '</span>
</label>
</li>
<li class="no">
<input type="radio" id="n-choice-'.$i.'" name="choix'.$i.'" value="0" checked/>
<label class="btn btn-default btn-xs" for="n-choice-'.$i.'" title="' . _('Vote "no" for ') . $radio_title[$i] . '">
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">' . _('No') . '</span>
</label>
</li>
</ul>
</td>'."\n";
}
// Affichage du bouton de formulaire pour inscrire un nouvel utilisateur dans la base
echo '<td><button type="submit" class="btn btn-success btn-sm" name="boutonp" title="'. _('Save the choices') .'">'. _('Save') .'</button></td>
</tr>'."\n";
}
// Addition and Best choice
//affichage de la ligne contenant les sommes de chaque colonne
$tr_addition = '<tr id="addition"><td>'. _("Addition") .'</td>';
$meilleurecolonne = max($somme);
$compteursujet = 0;
$meilleursujet = '<ul style="list-style:none">';
for ($i = 0; $i < $nbcolonnes; $i++) {
if (isset($somme[$i]) && $somme[$i] > 0 ) {
if (in_array($i, array_keys($somme, max($somme)))){
$tr_addition .= '<td><span class="glyphicon glyphicon-star text-warning"></span><span>'.$somme[$i].'</span></td>';
$meilleursujet.= '<li><b>'.$radio_title[$i].'</b></li>';
$compteursujet++;
} else {
$tr_addition .= '<td>'.$somme[$i].'</td>';
}
} else {
$tr_addition .= '<td></td>';
}
}
$tr_addition .= '<td></td></tr>';
$meilleursujet = str_replace("°", "'", $meilleursujet).'</ul>';
$vote_str = ($meilleurecolonne > 1) ? $vote_str = _('votes') : _('vote');
// Print Addition and Best choice
echo $tr_addition.'
</tbody>
</table>
</div>
<div class="row">'."\n";
if ($compteursujet == 1) {
echo '
<div class="col-sm-12"><h3>' . _("Best choice") . '</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span> ' . _("The best choice at this time is:") . '</p>
' . $meilleursujet . '
<p>' . _("with") . ' <b>' . $meilleurecolonne . '</b> ' . $vote_str . '.</p>
</div>'."\n";
} elseif ($compteursujet > 1) {
echo '
<div class="col-sm-12"><h3>' . _("Best choices") . '</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span> ' . _("The bests choices at this time are:") . '</p>
' . $meilleursujet . '
<p>' . _("with") . ' <b>' . $meilleurecolonne . '</b> ' . $vote_str . '.</p>
</div>'."\n";
}
echo '
</div>
<hr role="presentation" />';
// Comments
$sql = 'select * from comments where id_sondage='.$connect->Param('numsondage').' order by id_comment';
$sql = $connect->Prepare($sql);
$comment_user=$connect->Execute($sql, array($numsondage));
if ($comment_user->RecordCount() != 0) {
echo '<div><h3>' . _("Comments of polled people") . '</h3>'."\n";
while($dcomment = $comment_user->FetchNextObject(false)) {
echo '
<div class="comment">
<b>'.stripslashes($dcomment->usercomment). ' :</b>
<span class="comment">' . stripslashes(nl2br($dcomment->comment)) . '</span>
</div>';
}
echo '</div>';
}
if ($dsondage->format!="A-" && $dsondage->format!="D-") {
echo '
<div class="hidden-print alert alert-info">
<div class="col-md-6 col-md-offset-3">
<fieldset id="add-comment"><legend>' . _("Add a comment in the poll") . '</legend>
<div class="form-group">
<p><label for="commentuser">'. _("Your name") .'</label><input type=text class="form-control" name="commentuser" id="commentuser" /></p>
</div>
<div class="form-group">
<p><label for="comment">'. _("Your comment") .'</label><br />
<textarea name="comment" id="comment" class="form-control" rows="2" cols="40"></textarea></p>
</div>
<p class="text-center"><input type="submit" name="ajoutcomment" value="'. _("Send the comment") .'" class="btn btn-success"></p>
</fieldset>
</div>
<div class="clearfix"></div>
</div>';
}
echo '
</form>';
bandeau_pied();
$smarty->display('studs.tpl');

39
tpl/add_slot.tpl Normal file
View File

@ -0,0 +1,39 @@
{extends file='page.tpl'}
{block name=main}
<form action="{$admin_poll_id|poll_url:true}" method="POST">
<div class="alert alert-info text-center">
<h2>{_('Column\'s adding')}</h2>
{if $format === 'D'}
<div class="form-group">
<label for="newdate" class="col-md-4">{_('Day')}</label>
<div class="col-md-8">
<div class="input-group date">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar"></i></span>
<input type="text" id="newdate" data-date-format="{_('dd/mm/yyyy')}" aria-describedby="dateformat" name="newdate" class="form-control" placeholder="{_('dd/mm/yyyy')}" />
</div>
<span id="dateformat" class="sr-only">{_('(dd/mm/yyyy)')}</span>
</div>
</div>
<div class="form-group">
<label for="newmoment" class="col-md-4">{_('Time')}</label>
<div class="col-md-8">
<input type="text" id="newmoment" name="newmoment" class="form-control" />
</div>
</div>
{else}
<div class="form-group">
<label for="choice" class="col-md-4">{_('Choice')}</label>
<div class="col-md-8">
<input type="text" id="choice" name="choice" class="form-control" />
</div>
</div>
{/if}
<div class="form-group">
<button class="btn btn-default" type="submit" name="back">{_('Back to the poll')}</button>
<button type="submit" name="confirm_add_slot" class="btn btn-success">{_('Add a column')}</button>
</div>
</div>
</form>
{/block}

10
tpl/admin/admin_page.tpl Normal file
View File

@ -0,0 +1,10 @@
{extends 'page.tpl'}
{block 'main'}
<div class="row">
<div class="col-xs-12">
<a href="{'admin'|resource}">{_('Back to administration')}</a>
</div>
</div>
{block 'admin_main'}{/block}
{/block}

20
tpl/admin/index.tpl Normal file
View File

@ -0,0 +1,20 @@
{extends 'admin/admin_page.tpl'}
{block 'main'}
<div class="row">
<div class="col-md-6 col-xs-12">
<a href="./polls.php"><h2>{_('Polls')}</h2></a>
</div>
<div class="col-md-6 col-xs-12">
<a href="./migration.php"><h2>{_('Migration')}</h2></a>
</div>
<div class="col-md-6 col-xs-12">
<a href="./purge.php"><h2>{_('Purge')}</h2></a>
</div>
{if $logsAreReadable}
<div class="col-md-6 col-xs-12">
<a href="./logs.php"><h2>{_('Logs')}</h2></a>
</div>
{/if}
</div>
{/block}

5
tpl/admin/logs.tpl Normal file
View File

@ -0,0 +1,5 @@
{extends 'admin/admin_page.tpl'}
{block 'admin_main'}
<pre>{$logs|html}</pre>
{/block}

39
tpl/admin/migration.tpl Normal file
View File

@ -0,0 +1,39 @@
{extends 'admin/admin_page.tpl'}
{block 'admin_main'}
<div class="row">
<div class="col-xs-12 col-md-4">
<h2>{_('Summary')}</h2>
{_('Succeeded:')} <span class="label label-warning">{$countSucceeded|html} / {$countTotal|html}</span>
<br/>
{_('Failed:')} <span class="label label-danger">{$countFailed|html} / {$countTotal|html}</span>
<br/>
{_('Skipped:')} <span class="label label-info">{$countSkipped|html} / {$countTotal|html}</span>
</div>
<div class="col-xs-12 col-md-4">
<h2>{_('Success')}</h2>
<ul>
{foreach $success as $s}
<li>{$s|html}</li>
{foreachelse}
<li>{_('Nothing')}</li>
{/foreach}
</ul>
</div>
<div class="col-xs-12 col-md-4">
<h2>{_('Fail')}</h2>
<ul>
{foreach $fail as $f}
<li>{$f|html}</li>
{foreachelse}
<li>{_('Nothing')}</li>
{/foreach}
</ul>
</div>
<div class="col-xs-12 well well-sm">
{_('Page generated in')} {$time} {_('secondes')}
</div>
</div>
{/block}

75
tpl/admin/polls.tpl Normal file
View File

@ -0,0 +1,75 @@
{extends 'admin/admin_page.tpl'}
{block 'admin_main'}
<form action="" method="POST">
<input type="hidden" name="csrf" value="{$crsf}"/>
{if $poll_to_delete}
<div class="alert alert-warning text-center">
<h3>{_("Confirm removal of the poll ")}"{$poll_to_delete->id|html}"</h3>
<p>
<button class="btn btn-default" type="submit" value="1"
name="annullesuppression">{_('Keep this poll!')}</button>
<button type="submit" name="delete_confirm" value="{$poll_to_delete->id|html}"
class="btn btn-danger">{_('Remove this poll!')}</button>
</p>
</div>
{/if}
<div class="panel panel-default">
<div class="panel-heading">
{$polls|count} / {$count} {_('polls in the database at this time')}
</div>
<table class="table table-bordered table-polls">
<tr align="center">
<th scope="col"></th>
<th scope="col">{_('Title')}</th>
<th scope="col">{_('Author')}</th>
<th scope="col">{_('Email')}</th>
<th scope="col">{_('Expiration\'s date')}</th>
<th scope="col">{_('Users')}</th>
<th scope="col">{_('Poll ID')}</th>
<th scope="col" colspan="3">{_('Actions')}</th>
</tr>
{foreach $polls as $poll}
<tr align="center">
<td class="cell-format">
{if $poll->format === 'D'}
<span class="glyphicon glyphicon-calendar" aria-hidden="true" title="{_('Date')}"></span><span class="sr-only">{_('Date')}</span>
{else}
<span class="glyphicon glyphicon-list-alt" aria-hidden="true" title="{_('Classic')}"></span><span class="sr-only">{_('Classic')}</span>
{/if}
</td>
<td>{$poll->title|html}</td>
<td>{$poll->admin_name|html}</td>
<td>{$poll->admin_mail|html}</td>
{if strtotime($poll->end_date) > time()}
<td>{date('d/m/y', strtotime($poll->end_date))}</td>
{else}
<td><span class="text-danger">{strtotime($poll->end_date)|date_format:'d/m/Y'}</span></td>
{/if}
<td>{$poll->votes|html}</td>
<td>{$poll->id|html}</td>
<td><a href="{$poll->id|poll_url|html}" class="btn btn-link" title="{_('See the poll')}"><span class="glyphicon glyphicon-eye-open"></span><span class="sr-only">{_('See the poll')}</span></a></td>
<td><a href="{$poll->admin_id|poll_url:true|html}" class="btn btn-link" title="{_('Change the poll')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Change the poll')}</span></a></td>
<td><button type="submit" name="delete_poll" value="{$poll->id|html}" class="btn btn-link" title="{_('Remove the poll')}"><span class="glyphicon glyphicon-trash text-danger"></span><span class="sr-only">{_('Remove the poll')}</span></td>
</tr>
{/foreach}
</table>
<div class="panel-heading">
{_('Pages:')}
{for $p=1 to $pages}
{if $p===$page}
<a href="{$SERVER_URL}{$SCRIPT_NAME}?page={$p}" class="btn btn-danger" disabled="disabled">{$p}</a>
{else}
<a href="{$SERVER_URL}{$SCRIPT_NAME}?page={$p}" class="btn btn-info">{$p}</a>
{/if}
{/for}
</div>
</div>
</form>
{/block}

13
tpl/admin/purge.tpl Normal file
View File

@ -0,0 +1,13 @@
{extends 'admin/admin_page.tpl'}
{block 'admin_main'}
{if $message}
<div class="alert alert-dismissible alert-info" role="alert">{$message|html}<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>
{/if}
<form action="" method="POST">
<input type="hidden" name="csrf" value="{$crsf}"/>
<div class="text-center">
<button type="submit" name="action" value="purge" class="btn btn-warning">{_('Purge all polls')} <span class="glyphicon glyphicon-trash text-danger"></span><span class="sr-only">{_('Purge all polls')}</span></button>
</div>
</form>
{/block}

View File

@ -0,0 +1,11 @@
{extends file='page.tpl'}
{block name=main}
<form action="{$admin_poll_id|poll_url:true|html}" method="POST">
<div class="alert alert-danger text-center">
<h2>{_('Confirm removal of all comments of the poll')}</h2>
<p><button class="btn btn-default" type="submit" name="cancel">{_('Keep comments')}</button>
<button type="submit" name="confirm_remove_all_comments" class="btn btn-danger">{_('Remove all comments!')}</button></p>
</div>
</form>
{/block}

View File

@ -0,0 +1,11 @@
{extends file='page.tpl'}
{block name=main}
<form action="{$admin_poll_id|poll_url:true|html}" method="POST">
<div class="alert alert-danger text-center">
<h2>{_('Confirm removal of your poll')}</h2>
<p><button class="btn btn-default" type="submit" name="cancel">{_('Keep this poll')}</button>
<button type="submit" name="confirm_delete_poll" class="btn btn-danger">{_('Remove this poll!')}</button></p>
</div>
</form>
{/block}

View File

@ -0,0 +1,11 @@
{extends file='page.tpl'}
{block name=main}
<form action="{$admin_poll_id|poll_url:true|html}" method="POST">
<div class="alert alert-danger text-center">
<h2>{_('Confirm removal of all votes of the poll')}</h2>
<p><button class="btn btn-default" type="submit" name="cancel">{_('Keep votes')}</button>
<button type="submit" name="confirm_remove_all_votes" class="btn btn-danger">{_('Remove all votes!')}</button></p>
</div>
</form>
{/block}

8
tpl/error.tpl Normal file
View File

@ -0,0 +1,8 @@
{extends file='page.tpl'}
{block name=main}
<div class="alert alert-warning">
<h2>{$error|html}</h2>
<p>{_('Back to the homepage of')} <a href="{$SERVER_URL|html}">{$APPLICATION_NAME|html}</a></p>
</div>
{/block}

4
tpl/footer.tpl Normal file
View File

@ -0,0 +1,4 @@
</main>
</div> <!-- .container -->
</body>
</html>

29
tpl/head.tpl Normal file
View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="{$html_lang}">
<head>
<meta charset="utf-8">
{if !empty($title)}
<title>{$title|html} - {$APPLICATION_NAME|html}</title>
{else}
<title>{$APPLICATION_NAME|html}</title>
{/if}
<link rel="stylesheet" href="{'css/bootstrap.min.css'|resource}">
<link rel="stylesheet" href="{'css/datepicker3.css'|resource}">
<link rel="stylesheet" href="{'css/style.css'|resource}">
<link rel="stylesheet" href="{'css/frama.css'|resource}">
<link rel="stylesheet" href="{'css/print.css'|resource}" media="print">
<script type="text/javascript" src="{'js/jquery-1.11.1.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap.min.js'|resource}"></script>
<script type="text/javascript" src="{'js/bootstrap-datepicker.js'|resource}"></script>
<script type="text/javascript" src="{"js/locales/bootstrap-datepicker.$html_lang.js"|resource}"></script>
<script type="text/javascript" src="{'js/core.js'|resource}"></script>
{if !empty($nav_js)}
<script src="{'nav/nav.js'|resource}" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->
{/if}
</head>
<body>
<div class="container ombre">

21
tpl/header.tpl Normal file
View File

@ -0,0 +1,21 @@
<header role="banner">
{if count($langs)>1}
<form method="post" action="" class="hidden-print">
<div class="input-group input-group-sm pull-right col-md-2 col-xs-4">
<select name="lang" class="form-control" title="{_("Select the language")}" >
{foreach $langs as $lang_key=>$lang_value}
<option lang="{substr($lang_key, 0, 2)}" {if substr($lang_key, 0, 2)==$html_lang}selected{/if} value="{$lang_key|html}">{$lang_value|html}</option>
{/foreach}
</select>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="{_("Change the language")}">OK</button>
</span>
</div>
</form>
{/if}
<h1><a href="{$SERVER_URL|html}" title="{_("Home")} - {$APPLICATION_NAME|html}"><img src="{$TITLE_IMAGE|resource}" alt="{$APPLICATION_NAME|html}"/></a></h1>
{if !empty($title)}<h2 class="lead"><i>{$title|html}</i></h2>{/if}
<hr class="trait" role="presentation" />
</header>
<main role="main">

6
tpl/page.tpl Normal file
View File

@ -0,0 +1,6 @@
{include file='head.tpl'}
{include file='header.tpl'}
{block name=main}{/block}
{include file='footer.tpl'}

40
tpl/part/comments.tpl Normal file
View File

@ -0,0 +1,40 @@
<hr role="presentation" id="comments" class="hidden-print"/>
<form action="#comments" method="POST">
{* Comment list *}
{if $comments|count > 0}
<h3>{_("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="{_('Remove the comment')}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span></button>
{/if}
<b>{$comment->name|html}</b>&nbsp;
<span class="comment">{nl2br($comment->comment|html)}</span>
</div>
{/foreach}
{/if}
{* Add comment form *}
{if $active && !$expired}
<div class="hidden-print alert alert-info">
<div class="col-md-6 col-md-offset-3">
<fieldset id="add-comment"><legend>{_("Add a comment to the poll")}</legend>
<div class="form-group">
<label for="name" class="control-label">{_("Your name")}</label>
<input type="text" name="name" id="name" class="form-control" />
</div>
<div class="form-group">
<label for="comment" class="control-label">{_("Your comment")}</label>
<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="{_("Send the comment")}" class="btn btn-success">
</div>
</fieldset>
</div>
<div class="clearfix"></div>
</div>
{/if}
</form>

11
tpl/part/poll_hint.tpl Normal file
View File

@ -0,0 +1,11 @@
{if $active}
<div class="alert alert-info">
<p>{_("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.")}</p>
<p aria-hidden="true"><b>{_('Legend:')}</b> <span class="glyphicon glyphicon-ok"></span> = {_('Yes')}, <b>(<span class="glyphicon glyphicon-ok"></span>)</b> = {_('Ifneedbe')}, <span class="glyphicon glyphicon-ban-circle"></span> = {_('No')}</p>
</div>
{else}
<div class="alert alert-danger">
<p>{_("The administrator locked this poll, votes and comments are frozen, it's not possible to participate anymore.")}</p>
<p aria-hidden="true"><b>{_('Legend:')}</b> <span class="glyphicon glyphicon-ok"></span> = {_('Yes')}, <b>(<span class="glyphicon glyphicon-ok"></span>)</b> = {_('Ifneedbe')}, <span class="glyphicon glyphicon-ban-circle"></span> = {_('No')}</p>
</div>
{/if}

View File

@ -0,0 +1,7 @@
<div class="alert alert-info">
<p>{_('As poll administrator, you can change all the lines of this poll with this button')} <span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span>,
{_('remove a column or a line with')} <span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span>
{_('and add a new column with')} <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">{_('Add a column')}</span>.</p>
<p>{_('Finally, you can change the informations of this poll like the title, the comments or your email address.')}</p>
<p aria-hidden="true"><strong>{_('Legend:')}</strong> <span class="glyphicon glyphicon-ok"></span> = {_('Yes')}, <b>(<span class="glyphicon glyphicon-ok"></span>)</b> = {_('Ifneedbe')}, <span class="glyphicon glyphicon-ban-circle"></span> = {_('No')}</p>
</div>

160
tpl/part/poll_info.tpl Normal file
View File

@ -0,0 +1,160 @@
{$admin = $admin|default:false}
{if $admin}<form action="{$admin_poll_id|poll_url:true}" method="POST">{/if}
<div class="jumbotron{if $admin} bg-danger{/if}">
<div class="row">
<div id="title-form" class="col-md-7">
<h3>{$poll->title|html}{if $admin && !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the title')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</h3>
{if $admin && !$expired}
<div class="hidden js-title">
<label class="sr-only" for="newtitle">{_('Title')}</label>
<div class="input-group">
<input type="text" class="form-control" id="newtitle" name="title" size="40" value="{$poll->title|html}" />
<span class="input-group-btn">
<button type="submit" class="btn btn-success" name="update_poll_info" value="title" title="{_('Save the new title')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-link btn-cancel" title="{_('Cancel the title edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</span>
</div>
</div>
{/if}
</div>
<div class="col-md-5 hidden-print">
<div class="btn-group pull-right">
<button onclick="print(); return false;" class="btn btn-default"><span class="glyphicon glyphicon-print"></span> {_('Print')}</button>
<a href="{$SERVER_URL|html}exportcsv.php?poll={$poll_id|html}" class="btn btn-default"><span class="glyphicon glyphicon-download-alt"></span> {_('Export to CSV')}</a>
{if $admin && !$expired}
<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-trash"></span> <span class="sr-only">{_("Remove")}</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li><button class="btn btn-link" type="submit" name="remove_all_votes">{_('Remove all the votes') }</button></li>
<li><button class="btn btn-link" type="submit" name="remove_all_comments">{_('Remove all the comments')}</button></li>
<li class="divider" role="presentation"></li>
<li><button class="btn btn-link" type="submit" name="delete_poll">{_('Remove the poll')}</button></li>
</ul>
{/if}
</div>
</div>
</div>
<div class="row">
<div id="name-form" class="form-group col-md-5">
<h4 class="control-label">{_('Initiator of the poll')}</h4>
<p class="form-control-static">{$poll->admin_name|html}{if $admin && !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the initiator')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</p>
{if $admin && !$expired}
<div class="hidden js-name">
<label class="sr-only" for="newname">{_('Initiator of the poll')}</label>
<div class="input-group">
<input type="text" class="form-control" id="newname" name="name" size="40" value="{$poll->admin_name|html}" />
<span class="input-group-btn">
<button type="submit" class="btn btn-success" name="update_poll_info" value="name" title="{_('Save the new name')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-link btn-cancel" title="{_('Cancel the name edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</span>
</div>
</div>
{/if}
</div>
</div>
<div class="row">
{if $admin}
<div class="form-group col-md-5">
<div id="email-form">
<p>{$poll->admin_mail|html}{if !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the email adress')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</p>
{if !$expired}
<div class="hidden js-email">
<label class="sr-only" for="admin_mail">{_('Email adress')}</label>
<div class="input-group">
<input type="text" class="form-control" id="admin_mail" name="admin_mail" size="40" value="{$poll->admin_mail|html}" />
<span class="input-group-btn">
<button type="submit" name="update_poll_info" value="admin_mail" class="btn btn-success" title="{_('Save the email address')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-link btn-cancel" title="{_('Cancel the email address edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</span>
</div>
</div>
{/if}
</div>
</div>
{/if}
<div class="form-group col-md-7" id="description-form">
<h4 class="control-label">{_("Description")}{if $admin && !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the description')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</h4>
<p class="form-control-static well">{$poll->description|html}</p>
{if $admin && !$expired}
<div class="hidden js-desc text-right">
<label class="sr-only" for="newdescription">{_('Description')}</label>
<textarea class="form-control" id="newdescription" name="comment" rows="2" cols="40">{$poll->description|html}</textarea>
<button type="submit" id="btn-new-desc" name="update_poll_info" value="comment" class="btn btn-sm btn-success" title="{_('Save the description')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-default btn-sm btn-cancel" title="{_('Cancel the description edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</div>
{/if}
</div>
</div>
<div class="row">
<div class="form-group form-group {if $admin}col-md-4{else}col-md-6{/if}">
<label for="public-link"><a class="public-link" href="{$poll_id|poll_url|html}">{_('Public link of the poll')} <span class="btn-link glyphicon glyphicon-link"></span></a></label>
<input class="form-control" id="public-link" type="text" readonly="readonly" value="{$poll_id|poll_url}" />
</div>
{if $admin}
<div class="form-group col-md-4">
<label for="admin-link"><a class="admin-link" href="{$admin_poll_id|poll_url:true|html}">{_('Admin link of the poll')} <span class="btn-link glyphicon glyphicon-link"></span></a></label>
<input class="form-control" id="admin-link" type="text" readonly="readonly" value="{$admin_poll_id|poll_url:true|html}" />
</div>
<div id="expiration-form" class="form-group col-md-4">
<h4 class="control-label">{_('Expiration\'s date')}</h4>
<p>{$poll->end_date|date_format:$date_format['txt_date']|html}{if !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the expiration\'s date')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</p>
{if !$expired}
<div class="hidden js-expiration">
<label class="sr-only" for="newexpirationdate">{_('Expiration\'s date')}</label>
<div class="input-group">
<input type="text" class="form-control" id="newexpirationdate" name="expiration_date" size="40" value="{$poll->end_date|date_format:$date_format['txt_date']|html}" />
<span class="input-group-btn">
<button type="submit" class="btn btn-success" name="update_poll_info" value="expiration_date" title="{_('Save the new expiration date')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-link btn-cancel" title="{_('Cancel the expiration date edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</span>
</div>
</div>
{/if}
</div>
{/if}
</div>
{if $admin}
<div class="row">
<div class="col-md-4 col-md-offset-8" >
<div id="poll-rules-form">
{if $poll->active}
{if $poll->editable}
{$rule_id = 2}
{$rule_icon = '<span class="glyphicon glyphicon-edit"></span>'}
{$rule_txt = _('Votes are editable')}
{else}
{$rule_id = 1}
{$rule_icon = '<span class="glyphicon glyphicon-check"></span>'}
{$rule_txt = _('Votes and comments are open')}
{/if}
{else}
{$rule_id = 0}
{$rule_icon = '<span class="glyphicon glyphicon-lock"></span>'}
{$rule_txt = _('Votes and comments are locked')}
{/if}
<p class="">{$rule_icon} {$rule_txt|html}{if !$expired} <button class="btn btn-link btn-sm btn-edit" title="{_('Edit the poll rules')}"><span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span></button>{/if}</p>
{if !$expired}
<div class="hidden js-poll-rules">
<label class="sr-only" for="rules">{_('Poll rules')}</label>
<div class="input-group">
<select class="form-control" id="rules" name="rules">
<option value="0"{if $rule_id==0} selected="selected"{/if}>{_("Votes and comments are locked")}</option>
<option value="1"{if $rule_id==1} selected="selected"{/if}>{_("Votes and comments are open")}</option>
<option value="2"{if $rule_id==2} selected="selected"{/if}>{_("Votes are editable")}</option>
</select>
<span class="input-group-btn">
<button type="submit" name="update_poll_info" value="rules" class="btn btn-success" title="{_('Save the new rules')}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Save')}</span></button>
<button class="btn btn-link btn-cancel" title="{_('Cancel the rules edit')}"><span class="glyphicon glyphicon-remove"></span><span class="sr-only">{_('Cancel')}</span></button>
</span>
</div>
</div>
{/if}
</div>
</div>
</div>
{/if}
</div>
{if $admin}</form>{/if}

View File

@ -0,0 +1,196 @@
{if !is_array($best_choices) || empty($best_choices)}
{$best_choices = [0]}
{/if}
<h3>{_('Votes of the poll')}</h3>
<div id="tableContainer" class="tableContainer">
<form action="" method="POST">
<table class="results">
<caption class="sr-only">{_('Votes of the poll')} {$poll->title|html}</caption>
<thead>
{if $admin && !$expired}
<tr class="hidden-print">
<th role="presentation"></th>
{foreach $slots as $id=>$slot}
<td headers="C{$id}">
<button type="submit" name="delete_column" value="{$slot->title|html}" class="btn btn-link btn-sm" title="{_('Remove the column')} {$slot->title|html}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span></button>
</td>
{/foreach}
<td>
<button type="submit" name="add_slot" class="btn btn-link btn-sm" title="{_('Add a column')}"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">{_('Add a column')}</span></button>
</td>
</tr>
{/if}
<tr>
<th role="presentation"></th>
{foreach $slots as $id=>$slot}
<th class="bg-info" id="C{$id}">{$slot->title|html|markdown}</th>
{/foreach}
<th></th>
</tr>
</thead>
<tbody>
{foreach $votes as $vote}
<tr>
{* Edited line *}
{if $editingVoteId == $vote->id}
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" id="name" name="name" value="{$vote->name|html}" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
</div>
</td>
{foreach $vote->choices as $id=>$choice}
<td class="bg-info" headers="C{$id}">
<ul class="list-unstyled choice">
<li class="yes">
<input type="radio" id="y-choice-{$id}" name="choices[{$id}]" value="2" {if $choice==2}checked {/if}/>
<label class="btn btn-default btn-xs" for="y-choice-{$id}" title="{_('Vote yes for ')} . $radio_title[$id] . '">{* TODO Replace $radio_title *}
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
</label>
</li>
<li class="ifneedbe">
<input type="radio" id="i-choice-{$id}" name="choices[{$id}]" value="1" {if $choice==1}checked {/if}/>
<label class="btn btn-default btn-xs" for="i-choice-{$id}" title="{_('Vote ifneedbe for ')} . $radio_title[$id] . '">{* TODO Replace $radio_title *}
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
</label>
</li>
<li class="no">
<input type="radio" id="n-choice-{$id}" name="choices[{$id}]" value="0" {if $choice==0}checked {/if}/>
<label class="btn btn-default btn-xs" for="n-choice-{$id}" title="{_('Vote no for ')} . $radio_title[$id] . '">{* TODO Replace $radio_title *}
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
</label>
</li>
</ul>
</td>
{/foreach}
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs" name="save" value="{$vote->id|html}" title="{_('Save the choices')} {$vote->name|html}">{_('Save')}</button></td>
{else}
{* Voted line *}
<th class="bg-info">{$vote->name|html}</th>
{foreach $vote->choices as $id=>$choice}
{if $choice==2}
<td class="bg-success text-success" headers="C{$id}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span></td>
{elseif $choice==1}
<td class="bg-warning text-warning" headers="C{$id}">(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span></td>
{else}
<td class="bg-danger" headers="C{$id}"><span class="sr-only">{_('No')}</span></td>
{/if}
{/foreach}
{if $active && $poll->editable && !$expired}
<td>
<button type="submit" class="btn btn-link btn-sm" name="edit_vote" value="{$vote->id|html}" title="{_('Edit the line:')} {$vote->name|html}">
<span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span>
</button>
{if $admin}
<button type="submit" class="btn btn-link btn-sm" name="delete_vote" value="{$vote->id|html}" title="{_('Remove the line:')} {$vote->name|html}">
<span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span>
</button>
{/if}
</td>
{else}
<td></td>
{/if}
{/if}
</tr>
{/foreach}
{* Line to add a new vote *}
{if $active && $editingVoteId == 0 && !$expired}
<tr id="vote-form">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" id="name" name="name" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
</div>
</td>
{foreach $slots as $id=>$slot}
<td class="bg-info" headers="C{$id}">
<ul class="list-unstyled choice">
<li class="yes">
<input type="radio" id="y-choice-{$id}" name="choices[{$id}]" value="2" />
<label class="btn btn-default btn-xs" for="y-choice-{$id}" title="{_('Vote yes for')} {$slot->title|html}">
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
</label>
</li>
<li class="ifneedbe">
<input type="radio" id="i-choice-{$id}" name="choices[{$id}]" value="1" />
<label class="btn btn-default btn-xs" for="i-choice-{$id}" title="{_('Vote ifneedbe for')} {$slot->title|html}">
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
</label>
</li>
<li class="no">
<input type="radio" id="n-choice-{$id}" name="choices[{$id}]" value="0" checked/>
<label class="btn btn-default btn-xs" for="n-choice-{$id}" title="{_('Vote no for')} {$slot->title|html}">
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
</label>
</li>
</ul>
</td>
{/foreach}
<td><button type="submit" class="btn btn-success btn-md" name="save" title="{_('Save the choices')}">{_('Save')}</button></td>
</tr>
{/if}
{* Line displaying best moments *}
{$count_bests = 0}
{$max = max($best_choices)}
{if $max > 0}
<tr id="addition">
<td>{_("Addition")}</td>
{foreach $best_choices as $best_choice}
{if $max == $best_choice}
{$count_bests = $count_bests +1}
<td><span class="glyphicon glyphicon-star text-warning"></span>{$best_choice|html}</td>
{else}
<td>{$best_choice|html}</td>
{/if}
{/foreach}
</tr>
{/if}
</tbody>
</table>
</form>
</div>
{* Best votes listing *}
{$max = max($best_choices)}
{if $max > 0}
<div class="row">
{if $count_bests == 1}
<div class="col-sm-12"><h3>{_("Best choice")}</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span>{_('The best choice at this time is:')}</p>
{elseif $count_bests > 1}
<div class="col-sm-12"><h3>{_("Best choices")}</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span>{_('The bests choices at this time are:')}</p>
{/if}
{$i = 0}
<ul style="list-style:none">
{foreach $slots as $slot}
{if $best_choices[$i] == $max}
<li><strong>{$slot->title|html|markdown:true}</strong></li>
{/if}
{$i = $i+1}
{/foreach}
</ul>
<p>{_('with')} <b>{$max|html}</b> {if $max==1}{_('vote')}{else}{_('votes')}{/if}.</p>
</div>
</div>
{/if}

View File

@ -0,0 +1,246 @@
{if !is_array($best_choices) || empty($best_choices)}
{$best_choices = [0]}
{/if}
<h3>{_('Votes of the poll')}</h3>
<div id="tableContainer" class="tableContainer">
<form action="" method="POST">
<table class="results">
<caption class="sr-only">{_('Votes of the poll')} {$poll->title|html}</caption>
<thead>
{if $admin && !$expired}
<tr class="hidden-print">
<th role="presentation"></th>
{$headersDCount=0}
{foreach $slots as $slot}
{foreach $slot->moments as $id=>$moment}
<td headers="M{$slot@key} D{$headersDCount} H{$headersDCount}">
<button type="submit" name="delete_column" value="{$slot->day|html}@{$moment|html}" class="btn btn-link btn-sm" title="{_('Remove the column')} {$slot->day|date_format:$date_format.txt_short|html} - {$moment|html}"><span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span></button>
</td>
{$headersDCount = $headersDCount+1}
{/foreach}
{/foreach}
<td>
<button type="submit" name="add_slot" class="btn btn-link btn-sm" title="{_('Add a column')}"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">{_("Add a column")}</span></button>
</td>
</tr>
{/if}
<tr>
<th role="presentation"></th>
{$count_same = 0}
{$previous = 0}
{foreach $slots as $id=>$slot}
{$display = $slot->day|date_format:$date_format.txt_year_month|html}
{if $previous !== 0 && $previous != $display}
<th colspan="{$count_same}" class="bg-primary month" id="M{$id}">{$previous}</th>
{$count_same = 0}
{/if}
{$count_same = $count_same + $slot->moments|count}
{if $slot@last}
<th colspan="{$count_same}" class="bg-primary month" id="M{$id}">{$display}</th>
{/if}
{$previous = $display}
{for $foo=0 to ($slot->moments|count)-1}
{append var='headersM' value=$id}
{/for}
{/foreach}
<th></th>
</tr>
<tr>
<th role="presentation"></th>
{foreach $slots as $id=>$slot}
<th colspan="{$slot->moments|count}" class="bg-primary day" id="D{$id}">{$slot->day|date_format:$date_format.txt_day|html}</th>
{for $foo=0 to ($slot->moments|count)-1}
{append var='headersD' value=$id}
{/for}
{/foreach}
<th></th>
</tr>
<tr>
<th role="presentation"></th>
{$headersDCount=0}
{foreach $slots as $slot}
{foreach $slot->moments as $id=>$moment}
<th colspan="1" class="bg-info" id="H{$headersDCount}">{$moment|html}</th>
{append var='headersH' value=$headersDCount}
{$headersDCount = $headersDCount+1}
{/foreach}
{/foreach}
<th></th>
</tr>
</thead>
<tbody>
{foreach $votes as $vote}
<tr>
{* Edited line *}
{if $editingVoteId == $vote->id && !$expired}
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" id="name" name="name" value="{$vote->name|html}" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
</div>
</td>
{foreach $vote->choices as $k=>$choice}
<td class="bg-info" headers="M{$headersM[$k]} D{$headersD[$k]} H{$headersH[$k]}">
<ul class="list-unstyled choice">
<li class="yes">
<input type="radio" id="y-choice-{$k}" name="choices[{$k}]" value="2" {if $choice==2}checked {/if}/>
<label class="btn btn-default btn-xs" for="y-choice-{$k}" title="{_('Vote yes for ')} . $radio_title[$k] . '">{* TODO Replace $radio_title *}
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
</label>
</li>
<li class="ifneedbe">
<input type="radio" id="i-choice-{$k}" name="choices[{$k}]" value="1" {if $choice==1}checked {/if}/>
<label class="btn btn-default btn-xs" for="i-choice-{$k}" title="{_('Vote ifneedbe for ')} . $radio_title[$k] . '">{* TODO Replace $radio_title *}
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
</label>
</li>
<li class="no">
<input type="radio" id="n-choice-{$k}" name="choices[{$k}]" value="0" {if $choice==0}checked {/if}/>
<label class="btn btn-default btn-xs" for="n-choice-{$k}" title="{_('Vote no for ')} . $radio_title[$k] . '">{* TODO Replace $radio_title *}
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
</label>
</li>
</ul>
</td>
{/foreach}
<td style="padding:5px"><button type="submit" class="btn btn-success btn-xs" name="save" value="{$vote->id|html}" title="{_('Save the choices')} {$vote->name|html}">{_('Save')}</button></td>
{else}
{* Voted line *}
<th class="bg-info">{$vote->name|html}</th>
{foreach $vote->choices as $k=>$choice}
{if $choice==2}
<td class="bg-success text-success" headers="M{$headersM[$k]} D{$headersD[$k]} H{$k}"><span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span></td>
{elseif $choice==1}
<td class="bg-warning text-warning" headers="M{$headersM[$k]} D{$headersD[$k]} H{$k}">(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span></td>
{else}
<td class="bg-danger" headers="M{$headersM[$k]} D{$headersD[$k]} H{$k}"><span class="sr-only">{_('No')}</span></td>
{/if}
{/foreach}
{if $active && $poll->editable && !$expired}
<td>
<button type="submit" class="btn btn-link btn-sm" name="edit_vote" value="{$vote->id|html}" title="{_('Edit the line:')} {$vote->name|html}">
<span class="glyphicon glyphicon-pencil"></span><span class="sr-only">{_('Edit')}</span>
</button>
{if $admin}
<button type="submit" class="btn btn-link btn-sm" name="delete_vote" value="{$vote->id|html}" title="{_('Remove the line:')} {$vote->name|html}">
<span class="glyphicon glyphicon-remove text-danger"></span><span class="sr-only">{_('Remove')}</span>
</button>
{/if}
</td>
{else}
<td></td>
{/if}
{/if}
</tr>
{/foreach}
{* Line to add a new vote *}
{if $active && $editingVoteId == 0 && !$expired}
<tr id="vote-form">
<td class="bg-info" style="padding:5px">
<div class="input-group input-group-sm">
<span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
<input type="text" id="name" name="name" class="form-control" title="{_('Your name')}" placeholder="{_('Your name')}" />
</div>
</td>
{$i = 0}
{foreach $slots as $slot}
{foreach $slot->moments as $moment}
<td class="bg-info" headers="M{$headersM[$i]} D{$headersD[$i]} H{$headersH[$i]}">
<ul class="list-unstyled choice">
<li class="yes">
<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|html} - {$moment|html}">
<span class="glyphicon glyphicon-ok"></span><span class="sr-only">{_('Yes')}</span>
</label>
</li>
<li class="ifneedbe">
<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|html} - {$moment|html}">
(<span class="glyphicon glyphicon-ok"></span>)<span class="sr-only">{_('Ifneedbe')}</span>
</label>
</li>
<li class="no">
<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|html} - {$moment|html}">
<span class="glyphicon glyphicon-ban-circle"></span><span class="sr-only">{_('No')}</span>
</label>
</li>
</ul>
</td>
{$i = $i+1}
{/foreach}
{/foreach}
<td><button type="submit" class="btn btn-success btn-md" name="save" title="{_('Save the choices')}">{_('Save')}</button></td>
</tr>
{/if}
{* Line displaying best moments *}
{$count_bests = 0}
{$max = max($best_choices)}
{if $max > 0}
<tr id="addition">
<td>{_("Addition")}</td>
{foreach $best_choices as $best_moment}
{if $max == $best_moment}
{$count_bests = $count_bests +1}
<td><i class="glyphicon glyphicon-star text-warning"></i>{$best_moment|html}</td>
{else}
<td>{$best_moment|html}</td>
{/if}
{/foreach}
</tr>
{/if}
</tbody>
</table>
</form>
</div>
{* Best votes listing *}
{$max = max($best_choices)}
{if $max > 0}
<div class="row">
{if $count_bests == 1}
<div class="col-sm-12"><h3>{_('Best choice')}</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span>{_('The best choice at this time is:')}</p>
{elseif $count_bests > 1}
<div class="col-sm-12"><h3>{_('Best choices')}</h3></div>
<div class="col-sm-6 col-sm-offset-3 alert alert-success">
<p><span class="glyphicon glyphicon-star text-warning"></span>{_('The bests choices at this time are:')}</p>
{/if}
{$i = 0}
<ul style="list-style:none">
{foreach $slots as $slot}
{foreach $slot->moments as $moment}
{if $best_choices[$i] == $max}
<li><strong>{$slot->day|date_format:$date_format.txt_full|html} - {$moment|html}</strong></li>
{/if}
{$i = $i+1}
{/foreach}
{/foreach}
</ul>
<p>{_('with')} <b>{$max|html}</b> {if $max==1}{_('vote')}{else}{_('votes')}{/if}.</p>
</div>
</div>
{/if}

8
tpl/poll_deleted.tpl Normal file
View File

@ -0,0 +1,8 @@
{extends file='page.tpl'}
{block name=main}
<div class="alert alert-success text-center">
<h2>{_("Your poll has been removed!")}</h2>
<p>{_('Back to the homepage of')} <a href="{$SERVER_URL|html}">{$APPLICATION_NAME|html}</a></p>
</div>
{/block}

52
tpl/studs.tpl Normal file
View File

@ -0,0 +1,52 @@
{extends file='page.tpl'}
{block name=main}
{if !empty($message)}
<div class="alert alert-dismissible alert-{$message->type|html}" role="alert">{$message->message|html}<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>
{/if}
{* Global informations about the current poll *}
{include 'part/poll_info.tpl' admin=$admin}
{* Information about voting *}
{if $expired}
<div class="alert alert-danger">
<p>{_('The poll is expired, it will be deleted soon.')}</p>
<p>{_('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="{_('Scroll to the left')}">
<span class="glyphicon glyphicon-chevron-left"></span>
</button>
<button class="btn btn-sm btn-link scroll-right" title="{_('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}