Récupère l'ancien framadate
à la racine, quand on lance le serveur, on a le framadate tel qu'il est aujourd'hui en ajoutant sur l'url, on accède à la maquette interactive dans son état actuel
This commit is contained in:
parent
b1ab16bd11
commit
50e56a0396
|
@ -0,0 +1,5 @@
|
|||
tpl_c/*.php
|
||||
vendor/
|
||||
|
||||
po/*.po
|
||||
po/*.json
|
|
@ -0,0 +1,17 @@
|
|||
######################
|
||||
# .htaccess example. #
|
||||
######################
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule . - [L]
|
||||
|
||||
RewriteRule ^([a-zA-Z0-9-]+)$ studs.php?poll=$1 [L]
|
||||
RewriteRule ^([a-zA-Z0-9-]+)/action/([a-zA-Z_-]+)/(.+)$ studs.php?poll=$1&$2=$3
|
||||
RewriteRule ^([a-zA-Z0-9-]+)/vote/([a-zA-Z0-9]{16})$ studs.php?poll=$1&vote=$2
|
||||
RewriteRule ^([a-zA-Z0-9-]{24})/admin$ adminstuds.php?poll=$1
|
||||
RewriteRule ^([a-zA-Z0-9-]{24})/admin/vote/([a-zA-Z0-9]{16})$ adminstuds.php?poll=$1&vote=$2
|
||||
RewriteRule ^([a-zA-Z0-9-]{24})/admin/action/([a-zA-Z_-]+)(/(.+))?$ adminstuds.php?poll=$1&$2=$4
|
||||
</IfModule>
|
|
@ -0,0 +1,14 @@
|
|||
# [Framasoft](http://framadate.org)
|
||||
* Simon Leblanc (development),
|
||||
* Pierre-Yves Gosset (development, graphism)
|
||||
* Pascal Chevrel (development)
|
||||
* Armony Altinier (accessibility)
|
||||
* JosephK (development)
|
||||
* Framasoft community
|
||||
*For a list of people who have contributed to the codebase, see [GitHub's list of contributors](https://github.com/framasoft/OpenSondage/graphs/contributors).*
|
||||
|
||||
## [STUdS](http://studs.u-strasbg.fr)
|
||||
* Guilhem Borghesi (borghesi@unistra.fr)
|
||||
* Raphaël Droz
|
||||
* Contributors from the University of Strasbourg: Guy, Christophe, Julien, Pierre, Romaric, Matthieu, Catherine, Christine, Olivier, Emmanuel and Florence
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
This changelog file is **deprecated**. For an up-to-date changelog, please check [the tags](https://framagit.org/framasoft/framadate/tags).
|
||||
|
||||
---------------------
|
||||
|
||||
# Changelog de framadate
|
||||
|
||||
## Version 1.0 (Erik - Markus - Ecmu - Julien - Imre - Luc - Pierre - Antonin - Olivier)
|
||||
- Amélioration : Conserver les votes en cours lors que l'utilisateur envoie un commentaire
|
||||
- Amélioration : Les mails sont envoyés en multipart pour les lecteurs ne supportant pas HTML
|
||||
- Amélioration : Masquer l'encart au dessus du tableau des votes, maintenant visible grâce à un bouton
|
||||
- Amélioration : Les commentaires sont horodatés
|
||||
- Amélioration : Auto wrapping de la description du sondage
|
||||
- Amélioration : Protection de sondages par mots de passe
|
||||
- Amélioration : Un click dans les champs URL sélectionne le contenu
|
||||
- Amélioration : Choix du lien du sondage
|
||||
- Amélioration : Possibilité de modifier un sondage après expiration
|
||||
- Amélioration : Confirmation demandée pour supprimer une colonne
|
||||
- Amélioration : Création d'une sondage par intervale de dates
|
||||
- Amélioration : Possibilité de ne pas faire de choix sur une colonne
|
||||
- Amélioration : Amélioration du format des mails
|
||||
- Amélioration : Amélioration du mode où chaque votant ne peut modifier que son vote
|
||||
- Amélioration : Fichier check.php pour vérifier la possibilité d'installation
|
||||
- Amélioration : Changements de libellés
|
||||
- Amélioration : Admin - Rechercher un sondage grâce à l'adresse mail
|
||||
- Amélioration : Fluidification du défilement de la page
|
||||
- Amélioration : Simplification de l'écran de création de sondage
|
||||
- Fix : Correction de traductions
|
||||
- Fix : Corrections diverses sur les dates et leurs formats
|
||||
- Fix : Impossible d'ajouter une colonne vide
|
||||
- Fix : Possibilité de supprimer des colonnes vides
|
||||
- Fix : Correction du formulaire de commentaires
|
||||
- Fix : Correction d'échappements de caractères
|
||||
- Fix : Rectification des contraintes sur les sondage expirés
|
||||
- Fix : Interdiction d'exporter les résultats lorsque l'utilisateur ne peut pas les voir
|
||||
- Technique : Travail sur le service des notifications
|
||||
- Technique : Prise en compte de l'entête X-Forwarded-Proto
|
||||
- Technique : Utilisation de PHPMailer pour l'envoi de mails
|
||||
- Technique : Encore de la Smartization
|
||||
- Technique : Pas mal de nettoyage de code
|
||||
|
||||
## Version 0.9.6 (goofy-bz - Olivier - Quentin - Vincent)
|
||||
- Fix : Corrections mineures de langues
|
||||
- Amélioration : Nouvelle langue - Occitan
|
||||
- Amélioration : Blocage d'un vote si l'admin a changé les possibilités entre temps
|
||||
|
||||
## Version 0.9.5 (Olivier)
|
||||
- Fix : Corrections mineures de langues
|
||||
- Fix : Correction de la suppresion de votes
|
||||
- Amélioration : Possibilité d'ajouter plus de "moments" à une date
|
||||
|
||||
## Version 0.9.4 (JosephK - Olivier - Nikos)
|
||||
- Fix : Correction de l'échappement des tables Vote et Slot
|
||||
- Fix : Encodage des "actions" en base64 pour fonctionner avec l'UrlRewriting
|
||||
- Fix : Correction d'attributs "title"
|
||||
- Fix : Un seul jour est requis pour faire un sondage
|
||||
- Fix : Correction de l'UrlRewriting
|
||||
- Amélioration : Traduction en Italien
|
||||
|
||||
## Version 0.9.3 (Antonin - Olivier - Nikos)
|
||||
- Fix : Traduction de textes en Italien
|
||||
- Fix : Empêchement de la suppression de la dernière colonne
|
||||
- Fix : Possiblité de supprimer des colonnes contenant des caractères spéciaux (par exemple "&")
|
||||
- Fix : Correction de l'exemple d'URL rewriting (des efforts restent à faire)
|
||||
- Amélioration : (Mode chacun son vote) Possiblité d'éditer son vote directement après un vote
|
||||
- Amélioration : Message plus parlant lors de la création d'une colonne
|
||||
## Version 0.9.2 (Olivier)
|
||||
- Fix : Completion d'un manque de contrôle sur les ID
|
||||
## Version 0.9.1 (JosephK - Olivier - Antonin - Michael - Paul)
|
||||
- Fix : Correction des lenteurs de défilement
|
||||
- Fix : Arrêt du défilement auto à gauche qu'on clique sur un choix
|
||||
- Fix : Traductions Français/Anglais/Allemand
|
||||
- Fix : Fin du tri automatique des colonnes (ex: 10h < 9h)
|
||||
- Fix : Option à la création masquée dans certains cas
|
||||
- Fix : Le nom peut maintenant contenir des caractères spéciaux (ex: , - ')
|
||||
- Fix : Correction mineure de la doc d'installation
|
||||
- Fix : Interdiction du caractère "," dans choix d'un vote
|
||||
- Fix : Correction de la suppression de choix contenant des caractères spéciaux
|
||||
- Fix : Correction du contrôle pour empêcher de supprimer le dernier choix d'un sondage
|
||||
- Fix : Taille du champs "Votre nom" agrandie
|
||||
- Technique : Ajout de logs
|
||||
## Version 0.9 (JosephK - Olivier - Antonin - ...)
|
||||
- Technique : Réorganisation des classes
|
||||
- Technique : Découpage en MVC + Installation de Smarty
|
||||
- Technique : Refonte de l'accès aux données + Remplacement de Adodb par PDO
|
||||
- Technique : Définition claire des couches Service et Repository
|
||||
- Technique : Utilisation de la librairie open source o80-i18n pour la gestion des langue
|
||||
- Amélioration : Refonte de l'administration
|
||||
- Amélioration : Formulaire de recherche pour trouver des sondages
|
||||
- Amélioration : Notification de l'utilisation si JAvascript ou les cookies sont désactivés
|
||||
- Amélioration : Découpage en 2 options pour recevoir ou non les nouveaux vote et commentaires
|
||||
- Amélioration : Utilisation de jetons CSRF sur certaines actions
|
||||
- Amélioration : Nouvelle option de sondage "Chaque participant peut modifier son propre vote"
|
||||
- Amélioration : Nouvelle option de sondage "Vote caché, seul le créateur du sondage peu voir les résultats"
|
||||
- Amélioration : Auto-cmoplétion des champs de dates (15/5 peut devenir 15/05/2015 ou 15/05/2016 en fonction de la date actuelle)
|
||||
- Amélioration : Nouvelle page pour retrouver ses sondages par mail
|
||||
- Amélioration : Mise à jour des fichiers .md pour faciliter la collaboration
|
||||
- Amélioration : Le nom de l'auteur et la date d'expiration sont modifiables
|
||||
- Amélioration : Le nom de vote est modifiable
|
||||
- Amélioration : Affichage du comptage des "Si nécessaire" entre parenthèses
|
||||
- Amélioration : Page d'installation
|
||||
- Fix : Purge en 2 étapes → 1. Verrouillage du sondage → 2. 60 jours plus tard suppression du sondage
|
||||
- Fix : Date d'expiration qui devient nulle quand on ajoute une colonne
|
||||
- Fix : clic/focus sur oui/non/si nécessaire → retour à gauche de la barre de scroll sur Chromium
|
||||
- Fix : Perte de ses votes quand le nom entré n'est pas valide
|
||||
- Fix : Certains sondages sont créé avec un ID déjà existant
|
||||
- Fix : 2 choix minimum sont nécessaires pour créer un sondage
|
||||
- Fix : Ajout de la police d'écriture Déjà Vu
|
||||
- Fix : Mémorisation de la langue entre l'application et l'administration
|
||||
- Fix : Bug à la création d'un sondage sans Javascript ou sans Cookies
|
||||
- Fix : Erreur d'url avec les noms de domaine contenant "admin"
|
||||
- Fix : Mise à jour de la doc d'installation
|
||||
|
||||
## Version 0.8 (juillet 2014 Pascal Chevrel - Armony Altinier - JosephK)
|
||||
- Améliorations sur l'accessibilité
|
||||
- Améliorations sur l'ergonomie
|
||||
- Améliorations sur l'internationalisation (nombreuses phrases en français dans le code)
|
||||
- Découpage chaines de langue pour virer le code html
|
||||
- Remise en place de l'export CSV
|
||||
- Remise en place de get_server_name() pour permettre l'installation dans un sous dossier, en https ou sur un port différent
|
||||
- Ajout Authors.md + en-têtes refaits
|
||||
- Fix bug changement de langues en mode URL rewriting (requête GET passée en formulaire POST)
|
||||
- Fix bug 2 boutons valider lorsqu'on édite un vote
|
||||
- Fix focus javascript sur "Votre nom"
|
||||
- Nettoyage + Bootstrap
|
||||
- Ajout vote Oui/Non/Si nécessaire
|
||||
- Formulaire simplifié pour l'ajout de colonne date (horaire libre)
|
||||
- Restructuration
|
||||
- Fix (partiel) bug modification du premier vote en tapant Entrée
|
||||
|
||||
## Version 0.7 (mars 2013)
|
||||
- Fix : le sondage supprimé n'était pas forcément le sondage sélectionné (cfévrier)
|
||||
- Fix : suppression de STUDS_DIR pour éviter toute confusion
|
||||
- Fix : corrections l'en-tete et de l'encodage des e-mails (cfévrier)
|
||||
- Fix : rend Optionnelle l'utilisation de la variable "REMOTE_USER" (cfévrier)
|
||||
- Amélioration : ne faire apparaitre dans l'admin que les sondage actifs ou expirés depuis x mois (pyg)
|
||||
- Amélioration : ajout d'un champs date_creation dans la table "sondage" (pyg)
|
||||
- Amélioration : permet de faire fonctionner gettext avec le serveur de dev de PHP5.4 + enlève code commenté depuis des années (pascalchevrel)
|
||||
- Fix : enlève les appels à get_server_name() partout sauf dans un appel à sendMail(), réécriture de la fonction pour cet usage (pascalchevrel)
|
||||
- Amélioration : remplacement des define() par des const plus concis (pascalchevrel)
|
||||
- Amélioration : possibilité de faire des liens directs vers les types de sondages à créer (pascalchevrel)
|
||||
- Amélioration : meilleure intégration de la framanav (pyg)
|
||||
- Amélioration : nombreuses modifications CSS pour un meilleur affichage (pyg)
|
||||
|
||||
## Changelog des 22 et 23 juin (pyg@framasoft.net)
|
||||
- très nombreuses modifications CSS
|
||||
- ajout de buttons.css pour des boutons plus propres
|
||||
- ajout de print.css pour une impression sans la classe "corps"
|
||||
- refonte de la page d'accueil
|
||||
- ajout de la framanav
|
||||
- qq retouches dans les fichiers .po
|
||||
- date de destruction passée de 2j à 30j
|
||||
- ajout de l'adresse à transmettre
|
||||
- ajout d'un bouton imprimer
|
||||
- généralisation des stripslashes
|
||||
- fix d'un bug sur une requete (suppression). Reste la seconde partie : https://github.com/leblanc-simon/OpenSondage/issues/8
|
||||
- modification du titre en image
|
||||
- ajout de htmlspecialchars_decode avec param ENT_QUOTES pour l'envoi des emails
|
||||
|
||||
## Changelog du 21 juin 2011 (pyg@framasoft.net)
|
||||
- très nombreuses modifications CSS
|
||||
- modification adminstuds.php : ajout de classes aux formulaires et ajout de stripslashes à l'affichage
|
||||
- modification infos_sondages.php : simplification du tableau de choix, ajouts de CSS, ajouts de labels pour faciliter la selection
|
||||
|
||||
## Changelog version 0.6.7 (mai 2011)
|
||||
- fork du projet STUdS (https://sourcesup.cru.fr/projects/studs/) de la version trunk du 15 mai 2011)
|
||||
- reprise par Simon Leblanc
|
||||
- nettoyage du code (indentation, cohérence de la convention de codage)
|
||||
- suppression des warning php
|
||||
- résolution d'une faille de sécurité par injection SQL
|
||||
- résolution d'une faille de sécurité par injection mail
|
||||
- correction dans le fichier de langue (merci à Julien Reitzel)
|
||||
- possibilité de mettre un texte libre pour les horaires
|
||||
- version Framasoft
|
||||
|
||||
# Les dernières améliorations de STUdS
|
||||
Changelog version 0.6.6 (XXX 2011) :
|
||||
- internationalisation avec gettext
|
||||
- abstraction de la base de données avec ADOdb
|
||||
- support de mysql (fichier d'initialisation disponible)
|
||||
- meilleure compatibilité avec le mode strict de PHP
|
||||
- factorisation de code et de CSS
|
||||
- moins de boutons de formulaire, plus de liens <a href>
|
||||
|
||||
Changelog version 0.6.5 (juin 2010) :
|
||||
- Changement de deux icones dans la creation d'un sondage.
|
||||
|
||||
Changelog version 0.6.4 (mars 2010) :
|
||||
- Corrections de bug
|
||||
|
||||
Changelog version 0.6.3 (janvier 2010) :
|
||||
- Corrections de bug
|
||||
|
||||
Changelog version 0.6.2 (novembre 2009) :
|
||||
- Correction dans l'affichage des bandeaux,
|
||||
- Modification de la partie "A propos",
|
||||
- Préparation à l'authentification,
|
||||
- De UdSification de l'application dans certains fichiers.
|
||||
|
||||
Changelog version 0.6.1 (octobre 2009) :
|
||||
- Corrections d'erreurs dans les traductions et d'oublis de traduction dans certaines pages.
|
||||
|
||||
Changelog version 0.6 (août 2009) :
|
||||
- Mise sous la licence CeCILL-B du code source de STUdS,
|
||||
- Passage de STUdS en encodage UTF8,
|
||||
- Ajout des icones des menus dans toutes les pages et non pas seulement sur la page d'accueil,
|
||||
- Correction d'un bug lors du rajout d'une colonne dans l'interface d'administration des sondages.
|
||||
|
||||
Changelog version 0.5 (février 2009) :
|
||||
- Traduction de STUdS en anglais, allemand et espagnol,
|
||||
- Changement de la CSS avec ajout du logo de l'Université de Strasbourg,
|
||||
- Possibilité d'ajouter un commentaire pour les sondés.
|
||||
|
||||
Changelog version 0.4 (janvier 2009) :
|
||||
- Possibilité de faire un export PDF pour envoyer la lettre de convocation à la date de réunion,
|
||||
- Possibilité de rajouter des colonnes dans la partie administration de sondage,
|
||||
- Correction de bugs d'affichage avec les caractères ' et " .
|
||||
|
||||
Changelog version 0.3 (novembre 2008) :
|
||||
- Possibilité de faire un export CSV pour exploiter le sondage dans un tableur,
|
||||
- Mise en place d'un repository Subversion pour partager les nouvelles versions de STUdS,
|
||||
- Amélioration de la CSS pour un meilleur affichage,
|
||||
- Modification du code source pour le rendre portable vers une autre machine.
|
||||
|
||||
Changelog version 0.2 (novembre 2008) :
|
||||
- Lors de la création d'un sondage DATE, classement des dates par ordre croissant,
|
||||
- Lors de la création d'un sondage DATE, accepter les horaires au format "8h" ou "8H",
|
||||
- Lors de la création d'un sondage DATE, possibilité de copier des horaires entre les dates,
|
||||
- Lors d'une modification de ligne, cocher les cases initialement choisies et non pas des cases vides,
|
||||
- Changement du format d'affichage des dates pour un formatage type : "Mardi 13/06",
|
||||
- Meilleure visualisation des choix les plus votés,
|
||||
- Possibilité pour l'administrateur du sondage de choisir de recevoir un mail d'alerte à chaque participation d'un sondé,
|
||||
- Remplacement des boutons de formulaire par des images moins austères,
|
||||
- Correction de quelques petits bugs d'affichage,
|
||||
- Possibilité de rajouter des cases supplémentaires lors de la création d'un sondage AUTRE,
|
||||
- Possibilité de rajouter des cases supplémentaires lors de la création d'un sondage DATE.
|
|
@ -0,0 +1,7 @@
|
|||
# Contributing
|
||||
|
||||
Please report issues on <https://framagit.org/framasoft/framadate/issues>
|
||||
|
||||
If you made a change and want it to be available in official repository, merge requests are welcome!
|
||||
|
||||
Read the [guidelines](https://framagit.org/framasoft/framadate/wikis/coding) to submit your changes.
|
|
@ -0,0 +1 @@
|
|||
# [Now available here](https://framagit.org/framasoft/framadate/wikis/home)
|
|
@ -0,0 +1,519 @@
|
|||
CONTRAT DE LICENCE DE LOGICIEL LIBRE CeCILL-B
|
||||
|
||||
|
||||
Avertissement
|
||||
|
||||
Ce contrat est une licence de logiciel libre issue d'une concertation
|
||||
entre ses auteurs afin que le respect de deux grands principes préside à
|
||||
sa rédaction:
|
||||
|
||||
* d'une part, le respect des principes de diffusion des logiciels
|
||||
libres: accès au code source, droits étendus conférés aux
|
||||
utilisateurs,
|
||||
* d'autre part, la désignation d'un droit applicable, le droit
|
||||
français, auquel elle est conforme, tant au regard du droit de la
|
||||
responsabilité civile que du droit de la propriété intellectuelle
|
||||
et de la protection qu'il offre aux auteurs et titulaires des
|
||||
droits patrimoniaux sur un logiciel.
|
||||
|
||||
Les auteurs de la licence CeCILL-B (pour Ce[a] C[nrs] I[nria] L[ogiciel]
|
||||
L[ibre]) sont:
|
||||
|
||||
Commissariat à l'Energie Atomique - CEA, établissement public de
|
||||
recherche à caractère scientifique, technique et industriel, dont le
|
||||
siège est situé 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris.
|
||||
|
||||
Centre National de la Recherche Scientifique - CNRS, établissement
|
||||
public à caractère scientifique et technologique, dont le siège est
|
||||
situé 3 rue Michel-Ange, 75794 Paris cedex 16.
|
||||
|
||||
Institut National de Recherche en Informatique et en Automatique -
|
||||
INRIA, établissement public à caractère scientifique et technologique,
|
||||
dont le siège est situé Domaine de Voluceau, Rocquencourt, BP 105, 78153
|
||||
Le Chesnay cedex.
|
||||
|
||||
|
||||
Préambule
|
||||
|
||||
Ce contrat est une licence de logiciel libre dont l'objectif est de
|
||||
conférer aux utilisateurs une très large liberté de modification et de
|
||||
redistribution du logiciel régi par cette licence.
|
||||
|
||||
L'exercice de cette liberté est assorti d'une obligation forte de
|
||||
citation à la charge de ceux qui distribueraient un logiciel incorporant
|
||||
un logiciel régi par la présente licence afin d'assurer que les
|
||||
contributions de tous soient correctement identifiées et reconnues.
|
||||
|
||||
L'accessibilité au code source et les droits de copie, de modification
|
||||
et de redistribution qui découlent de ce contrat ont pour contrepartie
|
||||
de n'offrir aux utilisateurs qu'une garantie limitée et de ne faire
|
||||
peser sur l'auteur du logiciel, le titulaire des droits patrimoniaux et
|
||||
les concédants successifs qu'une responsabilité restreinte.
|
||||
|
||||
A cet égard l'attention de l'utilisateur est attirée sur les risques
|
||||
associés au chargement, à l'utilisation, à la modification et/ou au
|
||||
développement et à la reproduction du logiciel par l'utilisateur étant
|
||||
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
|
||||
manipuler et qui le réserve donc à des développeurs ou des
|
||||
professionnels avertis possédant des connaissances informatiques
|
||||
approfondies. Les utilisateurs sont donc invités à charger et tester
|
||||
l'adéquation du logiciel à leurs besoins dans des conditions permettant
|
||||
d'assurer la sécurité de leurs systèmes et/ou de leurs données et, plus
|
||||
généralement, à l'utiliser et l'exploiter dans les mêmes conditions de
|
||||
sécurité. Ce contrat peut être reproduit et diffusé librement, sous
|
||||
réserve de le conserver en l'état, sans ajout ni suppression de clauses.
|
||||
|
||||
Ce contrat est susceptible de s'appliquer à tout logiciel dont le
|
||||
titulaire des droits patrimoniaux décide de soumettre l'exploitation aux
|
||||
dispositions qu'il contient.
|
||||
|
||||
|
||||
Article 1 - DEFINITIONS
|
||||
|
||||
Dans ce contrat, les termes suivants, lorsqu'ils seront écrits avec une
|
||||
lettre capitale, auront la signification suivante:
|
||||
|
||||
Contrat: désigne le présent contrat de licence, ses éventuelles versions
|
||||
postérieures et annexes.
|
||||
|
||||
Logiciel: désigne le logiciel sous sa forme de Code Objet et/ou de Code
|
||||
Source et le cas échéant sa documentation, dans leur état au moment de
|
||||
l'acceptation du Contrat par le Licencié.
|
||||
|
||||
Logiciel Initial: désigne le Logiciel sous sa forme de Code Source et
|
||||
éventuellement de Code Objet et le cas échéant sa documentation, dans
|
||||
leur état au moment de leur première diffusion sous les termes du Contrat.
|
||||
|
||||
Logiciel Modifié: désigne le Logiciel modifié par au moins une
|
||||
Contribution.
|
||||
|
||||
Code Source: désigne l'ensemble des instructions et des lignes de
|
||||
programme du Logiciel et auquel l'accès est nécessaire en vue de
|
||||
modifier le Logiciel.
|
||||
|
||||
Code Objet: désigne les fichiers binaires issus de la compilation du
|
||||
Code Source.
|
||||
|
||||
Titulaire: désigne le ou les détenteurs des droits patrimoniaux d'auteur
|
||||
sur le Logiciel Initial.
|
||||
|
||||
Licencié: désigne le ou les utilisateurs du Logiciel ayant accepté le
|
||||
Contrat.
|
||||
|
||||
Contributeur: désigne le Licencié auteur d'au moins une Contribution.
|
||||
|
||||
Concédant: désigne le Titulaire ou toute personne physique ou morale
|
||||
distribuant le Logiciel sous le Contrat.
|
||||
|
||||
Contribution: désigne l'ensemble des modifications, corrections,
|
||||
traductions, adaptations et/ou nouvelles fonctionnalités intégrées dans
|
||||
le Logiciel par tout Contributeur, ainsi que tout Module Interne.
|
||||
|
||||
Module: désigne un ensemble de fichiers sources y compris leur
|
||||
documentation qui permet de réaliser des fonctionnalités ou services
|
||||
supplémentaires à ceux fournis par le Logiciel.
|
||||
|
||||
Module Externe: désigne tout Module, non dérivé du Logiciel, tel que ce
|
||||
Module et le Logiciel s'exécutent dans des espaces d'adressage
|
||||
différents, l'un appelant l'autre au moment de leur exécution.
|
||||
|
||||
Module Interne: désigne tout Module lié au Logiciel de telle sorte
|
||||
qu'ils s'exécutent dans le même espace d'adressage.
|
||||
|
||||
Parties: désigne collectivement le Licencié et le Concédant.
|
||||
|
||||
Ces termes s'entendent au singulier comme au pluriel.
|
||||
|
||||
|
||||
Article 2 - OBJET
|
||||
|
||||
Le Contrat a pour objet la concession par le Concédant au Licencié d'une
|
||||
licence non exclusive, cessible et mondiale du Logiciel telle que
|
||||
définie ci-après à l'article 5 pour toute la durée de protection des droits
|
||||
portant sur ce Logiciel.
|
||||
|
||||
|
||||
Article 3 - ACCEPTATION
|
||||
|
||||
3.1 L'acceptation par le Licencié des termes du Contrat est réputée
|
||||
acquise du fait du premier des faits suivants:
|
||||
|
||||
* (i) le chargement du Logiciel par tout moyen notamment par
|
||||
téléchargement à partir d'un serveur distant ou par chargement à
|
||||
partir d'un support physique;
|
||||
* (ii) le premier exercice par le Licencié de l'un quelconque des
|
||||
droits concédés par le Contrat.
|
||||
|
||||
3.2 Un exemplaire du Contrat, contenant notamment un avertissement
|
||||
relatif aux spécificités du Logiciel, à la restriction de garantie et à
|
||||
la limitation à un usage par des utilisateurs expérimentés a été mis à
|
||||
disposition du Licencié préalablement à son acceptation telle que
|
||||
définie à l'article 3.1 ci dessus et le Licencié reconnaît en avoir pris
|
||||
connaissance.
|
||||
|
||||
|
||||
Article 4 - ENTREE EN VIGUEUR ET DUREE
|
||||
|
||||
|
||||
4.1 ENTREE EN VIGUEUR
|
||||
|
||||
Le Contrat entre en vigueur à la date de son acceptation par le Licencié
|
||||
telle que définie en 3.1.
|
||||
|
||||
|
||||
4.2 DUREE
|
||||
|
||||
Le Contrat produira ses effets pendant toute la durée légale de
|
||||
protection des droits patrimoniaux portant sur le Logiciel.
|
||||
|
||||
|
||||
Article 5 - ETENDUE DES DROITS CONCEDES
|
||||
|
||||
Le Concédant concède au Licencié, qui accepte, les droits suivants sur
|
||||
le Logiciel pour toutes destinations et pour la durée du Contrat dans
|
||||
les conditions ci-après détaillées.
|
||||
|
||||
Par ailleurs, si le Concédant détient ou venait à détenir un ou
|
||||
plusieurs brevets d'invention protégeant tout ou partie des
|
||||
fonctionnalités du Logiciel ou de ses composants, il s'engage à ne pas
|
||||
opposer les éventuels droits conférés par ces brevets aux Licenciés
|
||||
successifs qui utiliseraient, exploiteraient ou modifieraient le
|
||||
Logiciel. En cas de cession de ces brevets, le Concédant s'engage à
|
||||
faire reprendre les obligations du présent alinéa aux cessionnaires.
|
||||
|
||||
|
||||
5.1 DROIT D'UTILISATION
|
||||
|
||||
Le Licencié est autorisé à utiliser le Logiciel, sans restriction quant
|
||||
aux domaines d'application, étant ci-après précisé que cela comporte:
|
||||
|
||||
1. la reproduction permanente ou provisoire du Logiciel en tout ou
|
||||
partie par tout moyen et sous toute forme.
|
||||
|
||||
2. le chargement, l'affichage, l'exécution, ou le stockage du
|
||||
Logiciel sur tout support.
|
||||
|
||||
3. la possibilité d'en observer, d'en étudier, ou d'en tester le
|
||||
fonctionnement afin de déterminer les idées et principes qui sont
|
||||
à la base de n'importe quel élément de ce Logiciel; et ceci,
|
||||
lorsque le Licencié effectue toute opération de chargement,
|
||||
d'affichage, d'exécution, de transmission ou de stockage du
|
||||
Logiciel qu'il est en droit d'effectuer en vertu du Contrat.
|
||||
|
||||
|
||||
5.2 DROIT D'APPORTER DES CONTRIBUTIONS
|
||||
|
||||
Le droit d'apporter des Contributions comporte le droit de traduire,
|
||||
d'adapter, d'arranger ou d'apporter toute autre modification au Logiciel
|
||||
et le droit de reproduire le logiciel en résultant.
|
||||
|
||||
Le Licencié est autorisé à apporter toute Contribution au Logiciel sous
|
||||
réserve de mentionner, de façon explicite, son nom en tant qu'auteur de
|
||||
cette Contribution et la date de création de celle-ci.
|
||||
|
||||
|
||||
5.3 DROIT DE DISTRIBUTION
|
||||
|
||||
Le droit de distribution comporte notamment le droit de diffuser, de
|
||||
transmettre et de communiquer le Logiciel au public sur tout support et
|
||||
par tout moyen ainsi que le droit de mettre sur le marché à titre
|
||||
onéreux ou gratuit, un ou des exemplaires du Logiciel par tout procédé.
|
||||
|
||||
Le Licencié est autorisé à distribuer des copies du Logiciel, modifié ou
|
||||
non, à des tiers dans les conditions ci-après détaillées.
|
||||
|
||||
|
||||
5.3.1 DISTRIBUTION DU LOGICIEL SANS MODIFICATION
|
||||
|
||||
Le Licencié est autorisé à distribuer des copies conformes du Logiciel,
|
||||
sous forme de Code Source ou de Code Objet, à condition que cette
|
||||
distribution respecte les dispositions du Contrat dans leur totalité et
|
||||
soit accompagnée:
|
||||
|
||||
1. d'un exemplaire du Contrat,
|
||||
|
||||
2. d'un avertissement relatif à la restriction de garantie et de
|
||||
responsabilité du Concédant telle que prévue aux articles 8
|
||||
et 9,
|
||||
|
||||
et que, dans le cas où seul le Code Objet du Logiciel est redistribué,
|
||||
le Licencié permette un accès effectif au Code Source complet du
|
||||
Logiciel pendant au moins toute la durée de sa distribution du Logiciel,
|
||||
étant entendu que le coût additionnel d'acquisition du Code Source ne
|
||||
devra pas excéder le simple coût de transfert des données.
|
||||
|
||||
|
||||
5.3.2 DISTRIBUTION DU LOGICIEL MODIFIE
|
||||
|
||||
Lorsque le Licencié apporte une Contribution au Logiciel, le Logiciel
|
||||
Modifié peut être distribué sous un contrat de licence autre que le
|
||||
présent Contrat sous réserve du respect des dispositions de l'article
|
||||
5.3.4.
|
||||
|
||||
|
||||
5.3.3 DISTRIBUTION DES MODULES EXTERNES
|
||||
|
||||
Lorsque le Licencié a développé un Module Externe les conditions du
|
||||
Contrat ne s'appliquent pas à ce Module Externe, qui peut être distribué
|
||||
sous un contrat de licence différent.
|
||||
|
||||
|
||||
5.3.4 CITATIONS
|
||||
|
||||
Le Licencié qui distribue un Logiciel Modifié s'engage expressément:
|
||||
|
||||
1. à indiquer dans sa documentation qu'il a été réalisé à partir du
|
||||
Logiciel régi par le Contrat, en reproduisant les mentions de
|
||||
propriété intellectuelle du Logiciel,
|
||||
|
||||
2. à faire en sorte que l'utilisation du Logiciel, ses mentions de
|
||||
propriété intellectuelle et le fait qu'il est régi par le Contrat
|
||||
soient indiqués dans un texte facilement accessible depuis
|
||||
l'interface du Logiciel Modifié,
|
||||
|
||||
3. à mentionner, sur un site Web librement accessible décrivant le
|
||||
Logiciel Modifié, et pendant au moins toute la durée de sa
|
||||
distribution, qu'il a été réalisé à partir du Logiciel régi par le
|
||||
Contrat, en reproduisant les mentions de propriété intellectuelle
|
||||
du Logiciel,
|
||||
|
||||
4. lorsqu'il le distribue à un tiers susceptible de distribuer
|
||||
lui-même un Logiciel Modifié, sans avoir à en distribuer le code
|
||||
source, à faire ses meilleurs efforts pour que les obligations du
|
||||
présent article 5.3.4 soient reprises par le dit tiers.
|
||||
|
||||
Lorsque le Logiciel modifié ou non est distribué avec un Module Externe
|
||||
qui a été conçu pour l'utiliser, le Licencié doit soumettre le dit
|
||||
Module Externe aux obligations précédentes.
|
||||
|
||||
|
||||
5.3.5 COMPATIBILITE AVEC LES LICENCES CeCILL et CeCILL-C
|
||||
|
||||
Lorsqu'un Logiciel Modifié contient une Contribution soumise au contrat
|
||||
de licence CeCILL, les stipulations prévues à l'article 5.3.4 sont
|
||||
facultatives.
|
||||
|
||||
Un Logiciel Modifié peut être distribué sous le contrat de licence
|
||||
CeCILL-C. Les stipulations prévues à l'article 5.3.4 sont alors
|
||||
facultatives.
|
||||
|
||||
|
||||
Article 6 - PROPRIETE INTELLECTUELLE
|
||||
|
||||
|
||||
6.1 SUR LE LOGICIEL INITIAL
|
||||
|
||||
Le Titulaire est détenteur des droits patrimoniaux sur le Logiciel
|
||||
Initial. Toute utilisation du Logiciel Initial est soumise au respect
|
||||
des conditions dans lesquelles le Titulaire a choisi de diffuser son
|
||||
oeuvre et nul autre n'a la faculté de modifier les conditions de
|
||||
diffusion de ce Logiciel Initial.
|
||||
|
||||
Le Titulaire s'engage à ce que le Logiciel Initial reste au moins régi
|
||||
par le Contrat et ce, pour la durée visée à l'article 4.2.
|
||||
|
||||
|
||||
6.2 SUR LES CONTRIBUTIONS
|
||||
|
||||
Le Licencié qui a développé une Contribution est titulaire sur celle-ci
|
||||
des droits de propriété intellectuelle dans les conditions définies par
|
||||
la législation applicable.
|
||||
|
||||
|
||||
6.3 SUR LES MODULES EXTERNES
|
||||
|
||||
Le Licencié qui a développé un Module Externe est titulaire sur celui-ci
|
||||
des droits de propriété intellectuelle dans les conditions définies par
|
||||
la législation applicable et reste libre du choix du contrat régissant
|
||||
sa diffusion.
|
||||
|
||||
|
||||
6.4 DISPOSITIONS COMMUNES
|
||||
|
||||
Le Licencié s'engage expressément:
|
||||
|
||||
1. à ne pas supprimer ou modifier de quelque manière que ce soit les
|
||||
mentions de propriété intellectuelle apposées sur le Logiciel;
|
||||
|
||||
2. à reproduire à l'identique lesdites mentions de propriété
|
||||
intellectuelle sur les copies du Logiciel modifié ou non.
|
||||
|
||||
Le Licencié s'engage à ne pas porter atteinte, directement ou
|
||||
indirectement, aux droits de propriété intellectuelle du Titulaire et/ou
|
||||
des Contributeurs sur le Logiciel et à prendre, le cas échéant, à
|
||||
l'égard de son personnel toutes les mesures nécessaires pour assurer le
|
||||
respect des dits droits de propriété intellectuelle du Titulaire et/ou
|
||||
des Contributeurs.
|
||||
|
||||
|
||||
Article 7 - SERVICES ASSOCIES
|
||||
|
||||
7.1 Le Contrat n'oblige en aucun cas le Concédant à la réalisation de
|
||||
prestations d'assistance technique ou de maintenance du Logiciel.
|
||||
|
||||
Cependant le Concédant reste libre de proposer ce type de services. Les
|
||||
termes et conditions d'une telle assistance technique et/ou d'une telle
|
||||
maintenance seront alors déterminés dans un acte séparé. Ces actes de
|
||||
maintenance et/ou assistance technique n'engageront que la seule
|
||||
responsabilité du Concédant qui les propose.
|
||||
|
||||
7.2 De même, tout Concédant est libre de proposer, sous sa seule
|
||||
responsabilité, à ses licenciés une garantie, qui n'engagera que lui,
|
||||
lors de la redistribution du Logiciel et/ou du Logiciel Modifié et ce,
|
||||
dans les conditions qu'il souhaite. Cette garantie et les modalités
|
||||
financières de son application feront l'objet d'un acte séparé entre le
|
||||
Concédant et le Licencié.
|
||||
|
||||
|
||||
Article 8 - RESPONSABILITE
|
||||
|
||||
8.1 Sous réserve des dispositions de l'article 8.2, le Licencié a la
|
||||
faculté, sous réserve de prouver la faute du Concédant concerné, de
|
||||
solliciter la réparation du préjudice direct qu'il subirait du fait du
|
||||
Logiciel et dont il apportera la preuve.
|
||||
|
||||
8.2 La responsabilité du Concédant est limitée aux engagements pris en
|
||||
application du Contrat et ne saurait être engagée en raison notamment:
|
||||
(i) des dommages dus à l'inexécution, totale ou partielle, de ses
|
||||
obligations par le Licencié, (ii) des dommages directs ou indirects
|
||||
découlant de l'utilisation ou des performances du Logiciel subis par le
|
||||
Licencié et (iii) plus généralement d'un quelconque dommage indirect. En
|
||||
particulier, les Parties conviennent expressément que tout préjudice
|
||||
financier ou commercial (par exemple perte de données, perte de
|
||||
bénéfices, perte d'exploitation, perte de clientèle ou de commandes,
|
||||
manque à gagner, trouble commercial quelconque) ou toute action dirigée
|
||||
contre le Licencié par un tiers, constitue un dommage indirect et
|
||||
n'ouvre pas droit à réparation par le Concédant.
|
||||
|
||||
|
||||
Article 9 - GARANTIE
|
||||
|
||||
9.1 Le Licencié reconnaît que l'état actuel des connaissances
|
||||
scientifiques et techniques au moment de la mise en circulation du
|
||||
Logiciel ne permet pas d'en tester et d'en vérifier toutes les
|
||||
utilisations ni de détecter l'existence d'éventuels défauts. L'attention
|
||||
du Licencié a été attirée sur ce point sur les risques associés au
|
||||
chargement, à l'utilisation, la modification et/ou au développement et à
|
||||
la reproduction du Logiciel qui sont réservés à des utilisateurs avertis.
|
||||
|
||||
Il relève de la responsabilité du Licencié de contrôler, par tous
|
||||
moyens, l'adéquation du produit à ses besoins, son bon fonctionnement et
|
||||
de s'assurer qu'il ne causera pas de dommages aux personnes et aux biens.
|
||||
|
||||
9.2 Le Concédant déclare de bonne foi être en droit de concéder
|
||||
l'ensemble des droits attachés au Logiciel (comprenant notamment les
|
||||
droits visés à l'article 5).
|
||||
|
||||
9.3 Le Licencié reconnaît que le Logiciel est fourni "en l'état" par le
|
||||
Concédant sans autre garantie, expresse ou tacite, que celle prévue à
|
||||
l'article 9.2 et notamment sans aucune garantie sur sa valeur commerciale,
|
||||
son caractère sécurisé, innovant ou pertinent.
|
||||
|
||||
En particulier, le Concédant ne garantit pas que le Logiciel est exempt
|
||||
d'erreur, qu'il fonctionnera sans interruption, qu'il sera compatible
|
||||
avec l'équipement du Licencié et sa configuration logicielle ni qu'il
|
||||
remplira les besoins du Licencié.
|
||||
|
||||
9.4 Le Concédant ne garantit pas, de manière expresse ou tacite, que le
|
||||
Logiciel ne porte pas atteinte à un quelconque droit de propriété
|
||||
intellectuelle d'un tiers portant sur un brevet, un logiciel ou sur tout
|
||||
autre droit de propriété. Ainsi, le Concédant exclut toute garantie au
|
||||
profit du Licencié contre les actions en contrefaçon qui pourraient être
|
||||
diligentées au titre de l'utilisation, de la modification, et de la
|
||||
redistribution du Logiciel. Néanmoins, si de telles actions sont
|
||||
exercées contre le Licencié, le Concédant lui apportera son aide
|
||||
technique et juridique pour sa défense. Cette aide technique et
|
||||
juridique est déterminée au cas par cas entre le Concédant concerné et
|
||||
le Licencié dans le cadre d'un protocole d'accord. Le Concédant dégage
|
||||
toute responsabilité quant à l'utilisation de la dénomination du
|
||||
Logiciel par le Licencié. Aucune garantie n'est apportée quant à
|
||||
l'existence de droits antérieurs sur le nom du Logiciel et sur
|
||||
l'existence d'une marque.
|
||||
|
||||
|
||||
Article 10 - RESILIATION
|
||||
|
||||
10.1 En cas de manquement par le Licencié aux obligations mises à sa
|
||||
charge par le Contrat, le Concédant pourra résilier de plein droit le
|
||||
Contrat trente (30) jours après notification adressée au Licencié et
|
||||
restée sans effet.
|
||||
|
||||
10.2 Le Licencié dont le Contrat est résilié n'est plus autorisé à
|
||||
utiliser, modifier ou distribuer le Logiciel. Cependant, toutes les
|
||||
licences qu'il aura concédées antérieurement à la résiliation du Contrat
|
||||
resteront valides sous réserve qu'elles aient été effectuées en
|
||||
conformité avec le Contrat.
|
||||
|
||||
|
||||
Article 11 - DISPOSITIONS DIVERSES
|
||||
|
||||
|
||||
11.1 CAUSE EXTERIEURE
|
||||
|
||||
Aucune des Parties ne sera responsable d'un retard ou d'une défaillance
|
||||
d'exécution du Contrat qui serait dû à un cas de force majeure, un cas
|
||||
fortuit ou une cause extérieure, telle que, notamment, le mauvais
|
||||
fonctionnement ou les interruptions du réseau électrique ou de
|
||||
télécommunication, la paralysie du réseau liée à une attaque
|
||||
informatique, l'intervention des autorités gouvernementales, les
|
||||
catastrophes naturelles, les dégâts des eaux, les tremblements de terre,
|
||||
le feu, les explosions, les grèves et les conflits sociaux, l'état de
|
||||
guerre...
|
||||
|
||||
11.2 Le fait, par l'une ou l'autre des Parties, d'omettre en une ou
|
||||
plusieurs occasions de se prévaloir d'une ou plusieurs dispositions du
|
||||
Contrat, ne pourra en aucun cas impliquer renonciation par la Partie
|
||||
intéressée à s'en prévaloir ultérieurement.
|
||||
|
||||
11.3 Le Contrat annule et remplace toute convention antérieure, écrite
|
||||
ou orale, entre les Parties sur le même objet et constitue l'accord
|
||||
entier entre les Parties sur cet objet. Aucune addition ou modification
|
||||
aux termes du Contrat n'aura d'effet à l'égard des Parties à moins
|
||||
d'être faite par écrit et signée par leurs représentants dûment habilités.
|
||||
|
||||
11.4 Dans l'hypothèse où une ou plusieurs des dispositions du Contrat
|
||||
s'avèrerait contraire à une loi ou à un texte applicable, existants ou
|
||||
futurs, cette loi ou ce texte prévaudrait, et les Parties feraient les
|
||||
amendements nécessaires pour se conformer à cette loi ou à ce texte.
|
||||
Toutes les autres dispositions resteront en vigueur. De même, la
|
||||
nullité, pour quelque raison que ce soit, d'une des dispositions du
|
||||
Contrat ne saurait entraîner la nullité de l'ensemble du Contrat.
|
||||
|
||||
|
||||
11.5 LANGUE
|
||||
|
||||
Le Contrat est rédigé en langue française et en langue anglaise, ces
|
||||
deux versions faisant également foi.
|
||||
|
||||
|
||||
Article 12 - NOUVELLES VERSIONS DU CONTRAT
|
||||
|
||||
12.1 Toute personne est autorisée à copier et distribuer des copies de
|
||||
ce Contrat.
|
||||
|
||||
12.2 Afin d'en préserver la cohérence, le texte du Contrat est protégé
|
||||
et ne peut être modifié que par les auteurs de la licence, lesquels se
|
||||
réservent le droit de publier périodiquement des mises à jour ou de
|
||||
nouvelles versions du Contrat, qui posséderont chacune un numéro
|
||||
distinct. Ces versions ultérieures seront susceptibles de prendre en
|
||||
compte de nouvelles problématiques rencontrées par les logiciels libres.
|
||||
|
||||
12.3 Tout Logiciel diffusé sous une version donnée du Contrat ne pourra
|
||||
faire l'objet d'une diffusion ultérieure que sous la même version du
|
||||
Contrat ou une version postérieure.
|
||||
|
||||
|
||||
Article 13 - LOI APPLICABLE ET COMPETENCE TERRITORIALE
|
||||
|
||||
13.1 Le Contrat est régi par la loi française. Les Parties conviennent
|
||||
de tenter de régler à l'amiable les différends ou litiges qui
|
||||
viendraient à se produire par suite ou à l'occasion du Contrat.
|
||||
|
||||
13.2 A défaut d'accord amiable dans un délai de deux (2) mois à compter
|
||||
de leur survenance et sauf situation relevant d'une procédure d'urgence,
|
||||
les différends ou litiges seront portés par la Partie la plus diligente
|
||||
devant les Tribunaux compétents de Paris.
|
||||
|
||||
|
||||
Version 1.0 du 2006-09-05.
|
||||
|
|
@ -0,0 +1,515 @@
|
|||
CeCILL-B FREE SOFTWARE LICENSE AGREEMENT
|
||||
|
||||
|
||||
Notice
|
||||
|
||||
This Agreement is a Free Software license agreement that is the result
|
||||
of discussions between its authors in order to ensure compliance with
|
||||
the two main principles guiding its drafting:
|
||||
|
||||
* firstly, compliance with the principles governing the distribution
|
||||
of Free Software: access to source code, broad rights granted to
|
||||
users,
|
||||
* secondly, the election of a governing law, French law, with which
|
||||
it is conformant, both as regards the law of torts and
|
||||
intellectual property law, and the protection that it offers to
|
||||
both authors and holders of the economic rights over software.
|
||||
|
||||
The authors of the CeCILL-B (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
|
||||
license are:
|
||||
|
||||
Commissariat à l'Energie Atomique - CEA, a public scientific, technical
|
||||
and industrial research establishment, having its principal place of
|
||||
business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
|
||||
|
||||
Centre National de la Recherche Scientifique - CNRS, a public scientific
|
||||
and technological establishment, having its principal place of business
|
||||
at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
|
||||
|
||||
Institut National de Recherche en Informatique et en Automatique -
|
||||
INRIA, a public scientific and technological establishment, having its
|
||||
principal place of business at Domaine de Voluceau, Rocquencourt, BP
|
||||
105, 78153 Le Chesnay cedex, France.
|
||||
|
||||
|
||||
Preamble
|
||||
|
||||
This Agreement is an open source software license intended to give users
|
||||
significant freedom to modify and redistribute the software licensed
|
||||
hereunder.
|
||||
|
||||
The exercising of this freedom is conditional upon a strong obligation
|
||||
of giving credits for everybody that distributes a software
|
||||
incorporating a software ruled by the current license so as all
|
||||
contributions to be properly identified and acknowledged.
|
||||
|
||||
In consideration of access to the source code and the rights to copy,
|
||||
modify and redistribute granted by the license, users are provided only
|
||||
with a limited warranty and the software's author, the holder of the
|
||||
economic rights, and the successive licensors only have limited liability.
|
||||
|
||||
In this respect, the risks associated with loading, using, modifying
|
||||
and/or developing or reproducing the software by the user are brought to
|
||||
the user's attention, given its Free Software status, which may make it
|
||||
complicated to use, with the result that its use is reserved for
|
||||
developers and experienced professionals having in-depth computer
|
||||
knowledge. Users are therefore encouraged to load and test the
|
||||
suitability of the software as regards their requirements in conditions
|
||||
enabling the security of their systems and/or data to be ensured and,
|
||||
more generally, to use and operate it in the same conditions of
|
||||
security. This Agreement may be freely reproduced and published,
|
||||
provided it is not altered, and that no provisions are either added or
|
||||
removed herefrom.
|
||||
|
||||
This Agreement may apply to any or all software for which the holder of
|
||||
the economic rights decides to submit the use thereof to its provisions.
|
||||
|
||||
|
||||
Article 1 - DEFINITIONS
|
||||
|
||||
For the purpose of this Agreement, when the following expressions
|
||||
commence with a capital letter, they shall have the following meaning:
|
||||
|
||||
Agreement: means this license agreement, and its possible subsequent
|
||||
versions and annexes.
|
||||
|
||||
Software: means the software in its Object Code and/or Source Code form
|
||||
and, where applicable, its documentation, "as is" when the Licensee
|
||||
accepts the Agreement.
|
||||
|
||||
Initial Software: means the Software in its Source Code and possibly its
|
||||
Object Code form and, where applicable, its documentation, "as is" when
|
||||
it is first distributed under the terms and conditions of the Agreement.
|
||||
|
||||
Modified Software: means the Software modified by at least one
|
||||
Contribution.
|
||||
|
||||
Source Code: means all the Software's instructions and program lines to
|
||||
which access is required so as to modify the Software.
|
||||
|
||||
Object Code: means the binary files originating from the compilation of
|
||||
the Source Code.
|
||||
|
||||
Holder: means the holder(s) of the economic rights over the Initial
|
||||
Software.
|
||||
|
||||
Licensee: means the Software user(s) having accepted the Agreement.
|
||||
|
||||
Contributor: means a Licensee having made at least one Contribution.
|
||||
|
||||
Licensor: means the Holder, or any other individual or legal entity, who
|
||||
distributes the Software under the Agreement.
|
||||
|
||||
Contribution: means any or all modifications, corrections, translations,
|
||||
adaptations and/or new functions integrated into the Software by any or
|
||||
all Contributors, as well as any or all Internal Modules.
|
||||
|
||||
Module: means a set of sources files including their documentation that
|
||||
enables supplementary functions or services in addition to those offered
|
||||
by the Software.
|
||||
|
||||
External Module: means any or all Modules, not derived from the
|
||||
Software, so that this Module and the Software run in separate address
|
||||
spaces, with one calling the other when they are run.
|
||||
|
||||
Internal Module: means any or all Module, connected to the Software so
|
||||
that they both execute in the same address space.
|
||||
|
||||
Parties: mean both the Licensee and the Licensor.
|
||||
|
||||
These expressions may be used both in singular and plural form.
|
||||
|
||||
|
||||
Article 2 - PURPOSE
|
||||
|
||||
The purpose of the Agreement is the grant by the Licensor to the
|
||||
Licensee of a non-exclusive, transferable and worldwide license for the
|
||||
Software as set forth in Article 5 hereinafter for the whole term of the
|
||||
protection granted by the rights over said Software.
|
||||
|
||||
|
||||
Article 3 - ACCEPTANCE
|
||||
|
||||
3.1 The Licensee shall be deemed as having accepted the terms and
|
||||
conditions of this Agreement upon the occurrence of the first of the
|
||||
following events:
|
||||
|
||||
* (i) loading the Software by any or all means, notably, by
|
||||
downloading from a remote server, or by loading from a physical
|
||||
medium;
|
||||
* (ii) the first time the Licensee exercises any of the rights
|
||||
granted hereunder.
|
||||
|
||||
3.2 One copy of the Agreement, containing a notice relating to the
|
||||
characteristics of the Software, to the limited warranty, and to the
|
||||
fact that its use is restricted to experienced users has been provided
|
||||
to the Licensee prior to its acceptance as set forth in Article 3.1
|
||||
hereinabove, and the Licensee hereby acknowledges that it has read and
|
||||
understood it.
|
||||
|
||||
|
||||
Article 4 - EFFECTIVE DATE AND TERM
|
||||
|
||||
|
||||
4.1 EFFECTIVE DATE
|
||||
|
||||
The Agreement shall become effective on the date when it is accepted by
|
||||
the Licensee as set forth in Article 3.1.
|
||||
|
||||
|
||||
4.2 TERM
|
||||
|
||||
The Agreement shall remain in force for the entire legal term of
|
||||
protection of the economic rights over the Software.
|
||||
|
||||
|
||||
Article 5 - SCOPE OF RIGHTS GRANTED
|
||||
|
||||
The Licensor hereby grants to the Licensee, who accepts, the following
|
||||
rights over the Software for any or all use, and for the term of the
|
||||
Agreement, on the basis of the terms and conditions set forth hereinafter.
|
||||
|
||||
Besides, if the Licensor owns or comes to own one or more patents
|
||||
protecting all or part of the functions of the Software or of its
|
||||
components, the Licensor undertakes not to enforce the rights granted by
|
||||
these patents against successive Licensees using, exploiting or
|
||||
modifying the Software. If these patents are transferred, the Licensor
|
||||
undertakes to have the transferees subscribe to the obligations set
|
||||
forth in this paragraph.
|
||||
|
||||
|
||||
5.1 RIGHT OF USE
|
||||
|
||||
The Licensee is authorized to use the Software, without any limitation
|
||||
as to its fields of application, with it being hereinafter specified
|
||||
that this comprises:
|
||||
|
||||
1. permanent or temporary reproduction of all or part of the Software
|
||||
by any or all means and in any or all form.
|
||||
|
||||
2. loading, displaying, running, or storing the Software on any or
|
||||
all medium.
|
||||
|
||||
3. entitlement to observe, study or test its operation so as to
|
||||
determine the ideas and principles behind any or all constituent
|
||||
elements of said Software. This shall apply when the Licensee
|
||||
carries out any or all loading, displaying, running, transmission
|
||||
or storage operation as regards the Software, that it is entitled
|
||||
to carry out hereunder.
|
||||
|
||||
|
||||
5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
|
||||
|
||||
The right to make Contributions includes the right to translate, adapt,
|
||||
arrange, or make any or all modifications to the Software, and the right
|
||||
to reproduce the resulting software.
|
||||
|
||||
The Licensee is authorized to make any or all Contributions to the
|
||||
Software provided that it includes an explicit notice that it is the
|
||||
author of said Contribution and indicates the date of the creation thereof.
|
||||
|
||||
|
||||
5.3 RIGHT OF DISTRIBUTION
|
||||
|
||||
In particular, the right of distribution includes the right to publish,
|
||||
transmit and communicate the Software to the general public on any or
|
||||
all medium, and by any or all means, and the right to market, either in
|
||||
consideration of a fee, or free of charge, one or more copies of the
|
||||
Software by any means.
|
||||
|
||||
The Licensee is further authorized to distribute copies of the modified
|
||||
or unmodified Software to third parties according to the terms and
|
||||
conditions set forth hereinafter.
|
||||
|
||||
|
||||
5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
|
||||
|
||||
The Licensee is authorized to distribute true copies of the Software in
|
||||
Source Code or Object Code form, provided that said distribution
|
||||
complies with all the provisions of the Agreement and is accompanied by:
|
||||
|
||||
1. a copy of the Agreement,
|
||||
|
||||
2. a notice relating to the limitation of both the Licensor's
|
||||
warranty and liability as set forth in Articles 8 and 9,
|
||||
|
||||
and that, in the event that only the Object Code of the Software is
|
||||
redistributed, the Licensee allows effective access to the full Source
|
||||
Code of the Software at a minimum during the entire period of its
|
||||
distribution of the Software, it being understood that the additional
|
||||
cost of acquiring the Source Code shall not exceed the cost of
|
||||
transferring the data.
|
||||
|
||||
|
||||
5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
|
||||
|
||||
If the Licensee makes any Contribution to the Software, the resulting
|
||||
Modified Software may be distributed under a license agreement other
|
||||
than this Agreement subject to compliance with the provisions of Article
|
||||
5.3.4.
|
||||
|
||||
|
||||
5.3.3 DISTRIBUTION OF EXTERNAL MODULES
|
||||
|
||||
When the Licensee has developed an External Module, the terms and
|
||||
conditions of this Agreement do not apply to said External Module, that
|
||||
may be distributed under a separate license agreement.
|
||||
|
||||
|
||||
5.3.4 CREDITS
|
||||
|
||||
Any Licensee who may distribute a Modified Software hereby expressly
|
||||
agrees to:
|
||||
|
||||
1. indicate in the related documentation that it is based on the
|
||||
Software licensed hereunder, and reproduce the intellectual
|
||||
property notice for the Software,
|
||||
|
||||
2. ensure that written indications of the Software intended use,
|
||||
intellectual property notice and license hereunder are included in
|
||||
easily accessible format from the Modified Software interface,
|
||||
|
||||
3. mention, on a freely accessible website describing the Modified
|
||||
Software, at least throughout the distribution term thereof, that
|
||||
it is based on the Software licensed hereunder, and reproduce the
|
||||
Software intellectual property notice,
|
||||
|
||||
4. where it is distributed to a third party that may distribute a
|
||||
Modified Software without having to make its source code
|
||||
available, make its best efforts to ensure that said third party
|
||||
agrees to comply with the obligations set forth in this Article .
|
||||
|
||||
If the Software, whether or not modified, is distributed with an
|
||||
External Module designed for use in connection with the Software, the
|
||||
Licensee shall submit said External Module to the foregoing obligations.
|
||||
|
||||
|
||||
5.3.5 COMPATIBILITY WITH THE CeCILL AND CeCILL-C LICENSES
|
||||
|
||||
Where a Modified Software contains a Contribution subject to the CeCILL
|
||||
license, the provisions set forth in Article 5.3.4 shall be optional.
|
||||
|
||||
A Modified Software may be distributed under the CeCILL-C license. In
|
||||
such a case the provisions set forth in Article 5.3.4 shall be optional.
|
||||
|
||||
|
||||
Article 6 - INTELLECTUAL PROPERTY
|
||||
|
||||
|
||||
6.1 OVER THE INITIAL SOFTWARE
|
||||
|
||||
The Holder owns the economic rights over the Initial Software. Any or
|
||||
all use of the Initial Software is subject to compliance with the terms
|
||||
and conditions under which the Holder has elected to distribute its work
|
||||
and no one shall be entitled to modify the terms and conditions for the
|
||||
distribution of said Initial Software.
|
||||
|
||||
The Holder undertakes that the Initial Software will remain ruled at
|
||||
least by this Agreement, for the duration set forth in Article 4.2.
|
||||
|
||||
|
||||
6.2 OVER THE CONTRIBUTIONS
|
||||
|
||||
The Licensee who develops a Contribution is the owner of the
|
||||
intellectual property rights over this Contribution as defined by
|
||||
applicable law.
|
||||
|
||||
|
||||
6.3 OVER THE EXTERNAL MODULES
|
||||
|
||||
The Licensee who develops an External Module is the owner of the
|
||||
intellectual property rights over this External Module as defined by
|
||||
applicable law and is free to choose the type of agreement that shall
|
||||
govern its distribution.
|
||||
|
||||
|
||||
6.4 JOINT PROVISIONS
|
||||
|
||||
The Licensee expressly undertakes:
|
||||
|
||||
1. not to remove, or modify, in any manner, the intellectual property
|
||||
notices attached to the Software;
|
||||
|
||||
2. to reproduce said notices, in an identical manner, in the copies
|
||||
of the Software modified or not.
|
||||
|
||||
The Licensee undertakes not to directly or indirectly infringe the
|
||||
intellectual property rights of the Holder and/or Contributors on the
|
||||
Software and to take, where applicable, vis-à-vis its staff, any and all
|
||||
measures required to ensure respect of said intellectual property rights
|
||||
of the Holder and/or Contributors.
|
||||
|
||||
|
||||
Article 7 - RELATED SERVICES
|
||||
|
||||
7.1 Under no circumstances shall the Agreement oblige the Licensor to
|
||||
provide technical assistance or maintenance services for the Software.
|
||||
|
||||
However, the Licensor is entitled to offer this type of services. The
|
||||
terms and conditions of such technical assistance, and/or such
|
||||
maintenance, shall be set forth in a separate instrument. Only the
|
||||
Licensor offering said maintenance and/or technical assistance services
|
||||
shall incur liability therefor.
|
||||
|
||||
7.2 Similarly, any Licensor is entitled to offer to its licensees, under
|
||||
its sole responsibility, a warranty, that shall only be binding upon
|
||||
itself, for the redistribution of the Software and/or the Modified
|
||||
Software, under terms and conditions that it is free to decide. Said
|
||||
warranty, and the financial terms and conditions of its application,
|
||||
shall be subject of a separate instrument executed between the Licensor
|
||||
and the Licensee.
|
||||
|
||||
|
||||
Article 8 - LIABILITY
|
||||
|
||||
8.1 Subject to the provisions of Article 8.2, the Licensee shall be
|
||||
entitled to claim compensation for any direct loss it may have suffered
|
||||
from the Software as a result of a fault on the part of the relevant
|
||||
Licensor, subject to providing evidence thereof.
|
||||
|
||||
8.2 The Licensor's liability is limited to the commitments made under
|
||||
this Agreement and shall not be incurred as a result of in particular:
|
||||
(i) loss due the Licensee's total or partial failure to fulfill its
|
||||
obligations, (ii) direct or consequential loss that is suffered by the
|
||||
Licensee due to the use or performance of the Software, and (iii) more
|
||||
generally, any consequential loss. In particular the Parties expressly
|
||||
agree that any or all pecuniary or business loss (i.e. loss of data,
|
||||
loss of profits, operating loss, loss of customers or orders,
|
||||
opportunity cost, any disturbance to business activities) or any or all
|
||||
legal proceedings instituted against the Licensee by a third party,
|
||||
shall constitute consequential loss and shall not provide entitlement to
|
||||
any or all compensation from the Licensor.
|
||||
|
||||
|
||||
Article 9 - WARRANTY
|
||||
|
||||
9.1 The Licensee acknowledges that the scientific and technical
|
||||
state-of-the-art when the Software was distributed did not enable all
|
||||
possible uses to be tested and verified, nor for the presence of
|
||||
possible defects to be detected. In this respect, the Licensee's
|
||||
attention has been drawn to the risks associated with loading, using,
|
||||
modifying and/or developing and reproducing the Software which are
|
||||
reserved for experienced users.
|
||||
|
||||
The Licensee shall be responsible for verifying, by any or all means,
|
||||
the suitability of the product for its requirements, its good working
|
||||
order, and for ensuring that it shall not cause damage to either persons
|
||||
or properties.
|
||||
|
||||
9.2 The Licensor hereby represents, in good faith, that it is entitled
|
||||
to grant all the rights over the Software (including in particular the
|
||||
rights set forth in Article 5).
|
||||
|
||||
9.3 The Licensee acknowledges that the Software is supplied "as is" by
|
||||
the Licensor without any other express or tacit warranty, other than
|
||||
that provided for in Article 9.2 and, in particular, without any warranty
|
||||
as to its commercial value, its secured, safe, innovative or relevant
|
||||
nature.
|
||||
|
||||
Specifically, the Licensor does not warrant that the Software is free
|
||||
from any error, that it will operate without interruption, that it will
|
||||
be compatible with the Licensee's own equipment and software
|
||||
configuration, nor that it will meet the Licensee's requirements.
|
||||
|
||||
9.4 The Licensor does not either expressly or tacitly warrant that the
|
||||
Software does not infringe any third party intellectual property right
|
||||
relating to a patent, software or any other property right. Therefore,
|
||||
the Licensor disclaims any and all liability towards the Licensee
|
||||
arising out of any or all proceedings for infringement that may be
|
||||
instituted in respect of the use, modification and redistribution of the
|
||||
Software. Nevertheless, should such proceedings be instituted against
|
||||
the Licensee, the Licensor shall provide it with technical and legal
|
||||
assistance for its defense. Such technical and legal assistance shall be
|
||||
decided on a case-by-case basis between the relevant Licensor and the
|
||||
Licensee pursuant to a memorandum of understanding. The Licensor
|
||||
disclaims any and all liability as regards the Licensee's use of the
|
||||
name of the Software. No warranty is given as regards the existence of
|
||||
prior rights over the name of the Software or as regards the existence
|
||||
of a trademark.
|
||||
|
||||
|
||||
Article 10 - TERMINATION
|
||||
|
||||
10.1 In the event of a breach by the Licensee of its obligations
|
||||
hereunder, the Licensor may automatically terminate this Agreement
|
||||
thirty (30) days after notice has been sent to the Licensee and has
|
||||
remained ineffective.
|
||||
|
||||
10.2 A Licensee whose Agreement is terminated shall no longer be
|
||||
authorized to use, modify or distribute the Software. However, any
|
||||
licenses that it may have granted prior to termination of the Agreement
|
||||
shall remain valid subject to their having been granted in compliance
|
||||
with the terms and conditions hereof.
|
||||
|
||||
|
||||
Article 11 - MISCELLANEOUS
|
||||
|
||||
|
||||
11.1 EXCUSABLE EVENTS
|
||||
|
||||
Neither Party shall be liable for any or all delay, or failure to
|
||||
perform the Agreement, that may be attributable to an event of force
|
||||
majeure, an act of God or an outside cause, such as defective
|
||||
functioning or interruptions of the electricity or telecommunications
|
||||
networks, network paralysis following a virus attack, intervention by
|
||||
government authorities, natural disasters, water damage, earthquakes,
|
||||
fire, explosions, strikes and labor unrest, war, etc.
|
||||
|
||||
11.2 Any failure by either Party, on one or more occasions, to invoke
|
||||
one or more of the provisions hereof, shall under no circumstances be
|
||||
interpreted as being a waiver by the interested Party of its right to
|
||||
invoke said provision(s) subsequently.
|
||||
|
||||
11.3 The Agreement cancels and replaces any or all previous agreements,
|
||||
whether written or oral, between the Parties and having the same
|
||||
purpose, and constitutes the entirety of the agreement between said
|
||||
Parties concerning said purpose. No supplement or modification to the
|
||||
terms and conditions hereof shall be effective as between the Parties
|
||||
unless it is made in writing and signed by their duly authorized
|
||||
representatives.
|
||||
|
||||
11.4 In the event that one or more of the provisions hereof were to
|
||||
conflict with a current or future applicable act or legislative text,
|
||||
said act or legislative text shall prevail, and the Parties shall make
|
||||
the necessary amendments so as to comply with said act or legislative
|
||||
text. All other provisions shall remain effective. Similarly, invalidity
|
||||
of a provision of the Agreement, for any reason whatsoever, shall not
|
||||
cause the Agreement as a whole to be invalid.
|
||||
|
||||
|
||||
11.5 LANGUAGE
|
||||
|
||||
The Agreement is drafted in both French and English and both versions
|
||||
are deemed authentic.
|
||||
|
||||
|
||||
Article 12 - NEW VERSIONS OF THE AGREEMENT
|
||||
|
||||
12.1 Any person is authorized to duplicate and distribute copies of this
|
||||
Agreement.
|
||||
|
||||
12.2 So as to ensure coherence, the wording of this Agreement is
|
||||
protected and may only be modified by the authors of the License, who
|
||||
reserve the right to periodically publish updates or new versions of the
|
||||
Agreement, each with a separate number. These subsequent versions may
|
||||
address new issues encountered by Free Software.
|
||||
|
||||
12.3 Any Software distributed under a given version of the Agreement may
|
||||
only be subsequently distributed under the same version of the Agreement
|
||||
or a subsequent version.
|
||||
|
||||
|
||||
Article 13 - GOVERNING LAW AND JURISDICTION
|
||||
|
||||
13.1 The Agreement is governed by French law. The Parties agree to
|
||||
endeavor to seek an amicable solution to any disagreements or disputes
|
||||
that may arise during the performance of the Agreement.
|
||||
|
||||
13.2 Failing an amicable solution within two (2) months as from their
|
||||
occurrence, and unless emergency proceedings are necessary, the
|
||||
disagreements or disputes shall be referred to the Paris Courts having
|
||||
jurisdiction, by the more diligent Party.
|
||||
|
||||
|
||||
Version 1.0 dated 2006-09-05.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
locales:
|
||||
scripts/locales.sh
|
||||
|
||||
push-locales: locales
|
||||
zanata-cli -q -B push --errors --project-version `git branch | grep \* | cut -d ' ' -f2-`
|
||||
|
||||
pull-locales:
|
||||
zanata-cli -q -B pull --min-doc-percent 50 --project-version `git branch | grep \* | cut -d ' ' -f2-`
|
||||
scripts/po2json.sh
|
||||
|
||||
stats-locales:
|
||||
zanata-cli -q stats --project-version `git branch | grep \* | cut -d ' ' -f2-`
|
||||
|
||||
push-trad-to-zanata:
|
||||
scripts/push-trad-to-zanata.sh $(filter-out $@,$(MAKECMDGOALS))
|
||||
|
||||
add-key-locales:
|
||||
scripts/locale-add-key.pl "$(subst ",\",$(filter-out $@,$(MAKECMDGOALS)))"
|
||||
|
||||
# empty targets to be able to use MAKECMDGOALS as arguments to scripts
|
||||
%:
|
||||
@:
|
83
README.md
83
README.md
|
@ -1,32 +1,83 @@
|
|||
![Build Status](https://gitlab.com/pages/plain-html/badges/master/build.svg)
|
||||
# Framadate
|
||||
|
||||
![English](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) Framadate is an online service for planning an appointment or making a decision quickly and easily. No registration is required.
|
||||
|
||||
![Français](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png) Framadate est un service en ligne permettant de planifier un rendez-vous ou prendre des décisions rapidement et simplement. Aucune inscription préalable n’est nécessaire.
|
||||
|
||||
---
|
||||
# Installation
|
||||
|
||||
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/wikis/home>
|
||||
|
||||
# Contribute
|
||||
|
||||
## Code
|
||||
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/coding>
|
||||
|
||||
# Traductions
|
||||
|
||||
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/translating>
|
||||
|
||||
# Used libraries
|
||||
|
||||
* PHP [PHP 5.6](http://php.net)
|
||||
* Templating [Smarty](http://www.smarty.net/),
|
||||
* I18N [o80-i18n](https://github.com/olivierperez/o80-i18n)
|
||||
* Database: PostgreSQL ou [MySQL 5.5](https://dev.mysql.com/downloads/mysql/5.5.html)
|
||||
|
||||
---
|
||||
|
||||
Basé sur un l'exemple de publication de pages sur Gitlab, voici quelques pages permettant d'explorer une nouvelle version du formulaire de création de [Framadate](https://framadate.org).
|
||||
Framadate is a fork of the [STUdS](https://sourcesup.cru.fr/projects/studs/) project, that works at framadate.org for framasoft.org
|
||||
|
||||
Pour voir là où nous en somme :
|
||||
Previous main authors of Framadate were :
|
||||
* Simon LEBLANC
|
||||
* Pierre-Yves GOSSET
|
||||
|
||||
https://funky.framadate.org/
|
||||
Main authors of project STUdS are :
|
||||
* Guilhem BORGHESI
|
||||
* Raphaël DROZ
|
||||
|
||||
## Développement
|
||||
---
|
||||
|
||||
Nous travaillons sur la base des [maquettes](https://sketch.cloud/s/bavo4/all/cration-de-sondage/1-accueil/play) réalisé par Maïwann.
|
||||
Le principe se base sur un formulaire « textuel », aussi appelé [Natural Language Form](https://tympanus.net/Tutorials/NaturalLanguageForm/)
|
||||
Université de Strasbourg - Direction Informatique
|
||||
Auteur : Guilhem BORGHESI
|
||||
Création : Février 2008
|
||||
|
||||
Pour lancer executer le projet sur sa propre machine :
|
||||
borghesi@unistra.fr
|
||||
|
||||
Cloner le projet
|
||||
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".
|
||||
|
||||
```sh
|
||||
git clone https://framagit.org/yaf/funky-framadate-front
|
||||
```
|
||||
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.
|
||||
|
||||
```sh
|
||||
./dev
|
||||
```
|
||||
---
|
||||
|
||||
Le formulaire devrait être visible sur l'url http://locahost:8080
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
Janvier 2008
|
||||
Guilhem BORGHESI
|
||||
Université de Strasbourg
|
||||
|
||||
Mai 2010
|
||||
Raphaël DROZ, raphael.droz@gmail.com
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?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/OpenSondage: 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\Message;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\NotificationService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SecurityService;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
/* Variables */
|
||||
/* --------- */
|
||||
|
||||
$poll_id = null;
|
||||
$poll = null;
|
||||
$message = null;
|
||||
$result = false;
|
||||
$comments = [];
|
||||
$is_admin = false;
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$securityService = new SecurityService();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
||||
if (!empty($_POST['poll'])) {
|
||||
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
|
||||
if (!empty($_POST['poll_admin'])) {
|
||||
$admin_poll_id = filter_input(INPUT_POST, 'poll_admin', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (strlen($admin_poll_id) === 24) {
|
||||
$is_admin = ($pollService->findByAdminId($admin_poll_id) !== null);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$poll) {
|
||||
$message = new Message('error', __('Error', "This poll doesn't exist!"));
|
||||
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) {
|
||||
$message = new Message('error', __('Password', 'Wrong password'));
|
||||
} else {
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$comment = $inputService->filterComment($_POST['comment']);
|
||||
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Add comment
|
||||
$result = $pollService->addComment($poll_id, $name, $comment);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('Comments', 'Comment saved'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Comment failed'));
|
||||
}
|
||||
}
|
||||
$comments = $pollService->allCommentsByPollId($poll_id);
|
||||
}
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
$smarty->assign('comments', $comments);
|
||||
$comments_html = $smarty->fetch('part/comments_list.tpl');
|
||||
|
||||
$response = ['result' => $result, 'message' => $message, 'comments' => $comments_html];
|
||||
|
||||
echo json_encode($response);
|
|
@ -0,0 +1,94 @@
|
|||
<?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/OpenSondage: 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\Message;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
$logService = new LogService();
|
||||
$sessionService = new SessionService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
|
||||
$pollService = new PollService($connect, $logService);
|
||||
|
||||
$result = false;
|
||||
$message = null;
|
||||
$poll = null;
|
||||
$poll_id = null;
|
||||
$email = null;
|
||||
|
||||
if (!empty($_POST['poll'])) {
|
||||
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
|
||||
$token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$token_form_value = empty($_POST['token']) ? null : $_POST['token'];
|
||||
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (is_null($poll) || $config['use_smtp'] === false || is_null($token) || is_null($token_form_value)
|
||||
|| !$token->check($token_form_value) || is_null($editedVoteUniqueId)) {
|
||||
$message = new Message('error', __('Error', 'Something has gone wrong...'));
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$email = $mailService->isValidEmail($_POST['email']);
|
||||
if (is_null($email)) {
|
||||
$message = new Message('error', __('EditLink', 'The email address is not correct.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$time = $sessionService->get("Common", SESSION_EDIT_LINK_TIME);
|
||||
|
||||
if (!empty($time)) {
|
||||
$remainingTime = TIME_EDIT_LINK_EMAIL - (time() - $time);
|
||||
|
||||
if ($remainingTime > 0) {
|
||||
$message = new Message('error', __f('EditLink', 'Please wait %d seconds before we can send an email to you then try again.', $remainingTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($message)) {
|
||||
$url = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
|
||||
|
||||
$smarty->assign('poll', $poll);
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('editedVoteUniqueId', $editedVoteUniqueId);
|
||||
$body = $smarty->fetch('mail/remember_edit_link.tpl');
|
||||
|
||||
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
|
||||
|
||||
$mailService->send($email, $subject, $body);
|
||||
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());
|
||||
|
||||
$message = new Message('success', __('EditLink', 'Your reminder has been successfully sent!'));
|
||||
$result = true;
|
||||
}
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
|
||||
$response = ['result' => $result, 'message' => $message];
|
||||
|
||||
echo json_encode($response);
|
|
@ -0,0 +1 @@
|
|||
admin:$apr1$BskOE5OK$zOMq.fKFNCHkKhBdA12mR/
|
|
@ -0,0 +1,230 @@
|
|||
<?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/OpenSondage: 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\Message;
|
||||
use Framadate\Utils;
|
||||
|
||||
define('ROOT_DIR', __DIR__ . '/../');
|
||||
|
||||
/**
|
||||
* Checking for missing vendors.
|
||||
*/
|
||||
if (!file_exists(ROOT_DIR . 'vendor/autoload.php') || !file_exists(ROOT_DIR . 'vendor/o80/i18n/src/shortcuts.php')) {
|
||||
die ("ERROR: You should use <code>composer install</code> to fetch dependant libraries.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stripped ini sequence
|
||||
*/
|
||||
require_once ROOT_DIR . 'vendor/autoload.php';
|
||||
require_once ROOT_DIR . 'vendor/o80/i18n/src/shortcuts.php';
|
||||
require_once ROOT_DIR . 'app/inc/constants.php';
|
||||
if (session_id() === '') {
|
||||
session_start();
|
||||
}
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'oc' => 'Occitan',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
];
|
||||
const DEFAULT_LANGUAGE = 'en';
|
||||
require_once ROOT_DIR . 'app/inc/i18n.php';
|
||||
|
||||
/**
|
||||
* Function to sort messages by type (priorise errors on warning, warning on info, etc.)
|
||||
*
|
||||
* @param Message $a
|
||||
* @param Message $b
|
||||
* @return int
|
||||
*/
|
||||
function compareCheckMessage(Message $a, Message $b)
|
||||
{
|
||||
$values = [
|
||||
'danger' => 0,
|
||||
'warning' => 1,
|
||||
'info' => 2,
|
||||
'success' => 3
|
||||
];
|
||||
$vA = $values[$a->type];
|
||||
$vB = $values[$b->type];
|
||||
|
||||
if ($vA === $vB) {
|
||||
return 0;
|
||||
}
|
||||
return ($vA < $vB) ? -1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vars
|
||||
*/
|
||||
$messages = [];
|
||||
$inc_directory = ROOT_DIR . 'app/inc/';
|
||||
$conf_filename = $inc_directory . 'config.php';
|
||||
|
||||
/**
|
||||
* Messages
|
||||
*/
|
||||
|
||||
// PHP Version
|
||||
if (version_compare(PHP_VERSION, PHP_NEEDED_VERSION) >= 0) {
|
||||
$messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).', PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_NEEDED_VERSION));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.', phpversion(), PHP_NEEDED_VERSION));
|
||||
}
|
||||
|
||||
// INTL extension
|
||||
if (extension_loaded('intl')) {
|
||||
$messages[] = new Message('info', __('Check','PHP Intl extension is enabled.'));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __('Check','You need to enable the PHP Intl extension.'));
|
||||
}
|
||||
|
||||
// Is template compile dir exists and writable ?
|
||||
if (!file_exists(ROOT_DIR . COMPILE_DIR)) {
|
||||
$messages[] = new Message('danger', __f('Check','The template compile directory (%s) doesn\'t exist in "%s". Retry the installation process.', COMPILE_DIR, realpath(ROOT_DIR)));
|
||||
} elseif (is_writable(ROOT_DIR . COMPILE_DIR)) {
|
||||
$messages[] = new Message('info', __f('Check','The template compile directory (%s) is writable.', realpath(ROOT_DIR . COMPILE_DIR)));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __f('Check','The template compile directory (%s) is not writable.', realpath(ROOT_DIR . COMPILE_DIR)));
|
||||
}
|
||||
|
||||
// Does config.php exists or is writable ?
|
||||
if (file_exists($conf_filename)) {
|
||||
$messages[] = new Message('info', __('Check','The config file exists.'));
|
||||
} elseif (is_writable($inc_directory)) {
|
||||
$messages[] = new Message('info', __f('Check','The config file directory (%s) is writable.', $inc_directory));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __f('Check','The config file directory (%s) is not writable and the config file (%s) does not exists.', $inc_directory, $conf_filename));
|
||||
}
|
||||
|
||||
// Security
|
||||
if (extension_loaded('openssl')) {
|
||||
$messages[] = new Message('info', __('Check','OpenSSL extension loaded.'));
|
||||
} else {
|
||||
$messages[] = new Message('warning', __('Check','Consider enabling the PHP extension OpenSSL for increased security.'));
|
||||
}
|
||||
|
||||
if (ini_get('session.cookie_httponly') === '1') {
|
||||
$messages[] = new Message('info', __('Check', 'Cookies are served from HTTP only.'));
|
||||
} else {
|
||||
$messages[] = new Message('warning', __('Check', "Consider setting « session.cookie_httponly = 1 » inside your php.ini or add « php_value session.cookie_httponly 1 » to your .htaccess so that cookies can't be accessed through Javascript."));
|
||||
}
|
||||
|
||||
// Datetime
|
||||
$timezone = ini_get('date.timezone');
|
||||
if (!empty($timezone)) {
|
||||
$messages[] = new Message('info', __('Check','date.timezone is set.'));
|
||||
} else {
|
||||
$messages[] = new Message('warning', __('Check','Consider setting the date.timezone in php.ini.'));
|
||||
}
|
||||
|
||||
// The percentage of steps needed to be ready to launch the application
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
foreach ($messages as $message) {
|
||||
if ($message->type === 'danger') {
|
||||
$errors++;
|
||||
} else if ($message->type === 'warning') {
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
$readyPercentage = round((count($messages)-$errors)*100/count($messages));
|
||||
|
||||
if ($errors > 0) {
|
||||
$readyClass = 'danger';
|
||||
} else if ($warnings > 0) {
|
||||
$readyClass = 'warning';
|
||||
} else {
|
||||
$readyClass = 'success';
|
||||
}
|
||||
|
||||
usort($messages, 'compareCheckMessage');
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?=$locale?>">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<title><?=__('Check', 'Installation checking') ?></title>
|
||||
|
||||
<link rel="stylesheet" href="../css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="../css/style.css">
|
||||
<link rel="stylesheet" href="../css/frama.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container ombre">
|
||||
<div class="row">
|
||||
<form method="get" action="" class="hidden-print">
|
||||
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2">
|
||||
<select name="lang" class="form-control" title="<?=__('Language selector', 'Select language')?>" >
|
||||
<?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?>
|
||||
<option lang="fr" <?php if (substr($lang_key, 0, 2)===$locale) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-default btn-sm" title="<?=__('Language selector', 'Select language')?>">OK</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h1><?=__('Check', 'Installation checking') ?></h1>
|
||||
<div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-<?= $readyClass ?>" role="progressbar" aria-valuenow="<?= $readyPercentage ?>" aria-valuemin="0" aria-valuemax="100" style="width: <?= $readyPercentage ?>%;">
|
||||
<?= $readyPercentage ?>%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<?php
|
||||
foreach ($messages as $message) {
|
||||
echo '<div class="alert alert-' . $message->type . '" role="alert">';
|
||||
echo Utils::htmlEscape($message->message);
|
||||
echo '<span class="sr-only">' . $message->type . '</span>';
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<a class="btn btn-info" role="button" href=""><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?= __('Check', 'Check again') ?></a>
|
||||
<?php
|
||||
if (!is_file($conf_filename)) {
|
||||
if ($errors === 0) {
|
||||
?>
|
||||
<a class="btn btn-primary" role="button" href="<?= Utils::get_server_name() . 'admin/install.php' ?>"><span class=" glyphicon glyphicon-arrow-right" aria-hidden="true"></span> <?= __('Check', 'Continue the installation') ?></a>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
?>
|
||||
<a class="btn btn-primary" role="button" href="<?= Utils::get_server_name() . 'admin/'?>"><span class=" glyphicon glyphicon-arrow-left" aria-hidden="true"></span> <?= __('Admin', 'Back to administration') ?></a>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
|
@ -0,0 +1,29 @@
|
|||
<?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/OpenSondage: 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;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
$logService = new LogService();
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
|
||||
$purgeService->cleanDemoPoll();
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?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/OpenSondage: 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';
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Administration'));
|
||||
$smarty->assign('logsAreReadable', is_readable('../' . LOG_FILE));
|
||||
$smarty->display('admin/index.tpl');
|
|
@ -0,0 +1,50 @@
|
|||
<?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/OpenSondage: 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\InstallService;
|
||||
use Framadate\Utils;
|
||||
|
||||
require_once '../app/inc/init.php';
|
||||
|
||||
if (is_file(CONF_FILENAME)) {
|
||||
header(('Location: ' . Utils::get_server_name()));
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = null;
|
||||
$result['details'] = null;
|
||||
$installService = new InstallService();
|
||||
|
||||
if (!empty($_POST)) {
|
||||
$installService->updateFields($_POST);
|
||||
$result = $installService->install($smarty);
|
||||
|
||||
if ($result['status'] === 'OK') {
|
||||
header(('Location: ' . Utils::get_server_name() . 'admin/migration.php'));
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = __('Error', $result['code']);
|
||||
}
|
||||
|
||||
$smarty->assign('error', $error);
|
||||
$smarty->assign('error_details', $result['details']);
|
||||
$smarty->assign('title', __('Admin', 'Installation'));
|
||||
$smarty->assign('fields', $installService->getFields());
|
||||
$smarty->display('admin/install.tpl');
|
|
@ -0,0 +1,29 @@
|
|||
<?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/OpenSondage: 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('logs', $content);
|
||||
$smarty->assign('title', __('Admin', 'Logs'));
|
||||
|
||||
$smarty->display('admin/logs.tpl');
|
|
@ -0,0 +1,80 @@
|
|||
<?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/OpenSondage: 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 Doctrine\DBAL\Migrations\Configuration\Configuration;
|
||||
use Doctrine\DBAL\Migrations\Migration;
|
||||
use Doctrine\DBAL\Migrations\OutputWriter;
|
||||
use Doctrine\DBAL\Migrations\Tools\Console\Helper\MigrationStatusInfosHelper;
|
||||
use Framadate\Utils;
|
||||
|
||||
require_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
class MigrationLogger {
|
||||
private $log;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->log = '';
|
||||
}
|
||||
|
||||
public function addLine($message)
|
||||
{
|
||||
$this->log .= $message . "\n";
|
||||
}
|
||||
|
||||
public function getLog()
|
||||
{
|
||||
return $this->log;
|
||||
}
|
||||
}
|
||||
|
||||
$executing = false;
|
||||
$migration = null;
|
||||
$output = '';
|
||||
|
||||
if (isset($_POST['execute'])) {
|
||||
$executing = true;
|
||||
}
|
||||
|
||||
$migrationsDirectory = __DIR__ . '/../app/classes/Framadate/Migrations';
|
||||
$log = new MigrationLogger();
|
||||
|
||||
$configuration = new Configuration($connect, new OutputWriter(function ($message) use ($log) {
|
||||
$log->addLine($message);
|
||||
}));
|
||||
$configuration->setMigrationsTableName(Utils::table(MIGRATION_TABLE) . '_new');
|
||||
$configuration->setMigrationsDirectory($migrationsDirectory);
|
||||
$configuration->setMigrationsNamespace('DoctrineMigrations');
|
||||
$configuration->registerMigrationsFromDirectory($migrationsDirectory);
|
||||
|
||||
if ($executing) {
|
||||
$migration = new Migration($configuration);
|
||||
$migration->migrate();
|
||||
$output = trim(strip_tags($log->getLog()));
|
||||
}
|
||||
$infos = (new MigrationStatusInfosHelper($configuration))->getMigrationsInfos();
|
||||
|
||||
$smarty->assign('countTotal', $infos['Available Migrations']);
|
||||
$smarty->assign('countExecuted', $infos['Executed Migrations']);
|
||||
$smarty->assign('countWaiting', $infos['New Migrations']);
|
||||
$smarty->assign('executing', $executing);
|
||||
$smarty->assign('title', __('Admin', 'Migration'));
|
||||
$smarty->assign('output', $output);
|
||||
$smarty->assign('time', round((microtime(true)-$_SERVER['REQUEST_TIME_FLOAT']), 4));
|
||||
$smarty->display('admin/migration.tpl');
|
|
@ -0,0 +1,101 @@
|
|||
<?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/OpenSondage: 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;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
include_once __DIR__ . '/../bandeaux.php';
|
||||
|
||||
const POLLS_PER_PAGE = 30;
|
||||
|
||||
/* Functions */
|
||||
|
||||
function buildSearchQuery($search) {
|
||||
$query = '';
|
||||
foreach ($search as $key => $value) {
|
||||
$query .= $key . '=' . urlencode($value) . '&';
|
||||
}
|
||||
return substr($query, 0, -1);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
/* 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();
|
||||
$securityService = new SecurityService();
|
||||
|
||||
/* GET */
|
||||
/*-----*/
|
||||
$page = (int)filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);
|
||||
$page = ($page >= 1) ? $page : 1;
|
||||
|
||||
// Search
|
||||
$search['poll'] = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$search['title'] = filter_input(INPUT_GET, 'title', FILTER_SANITIZE_STRING);
|
||||
$search['name'] = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING);
|
||||
$search['mail'] = filter_input(INPUT_GET, 'mail', FILTER_SANITIZE_STRING);
|
||||
|
||||
/* 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($search, $page - 1, POLLS_PER_PAGE);
|
||||
$polls = $found['polls'];
|
||||
$count = $found['count'];
|
||||
$total = $found['total'];
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('polls', $polls);
|
||||
$smarty->assign('count', $count);
|
||||
$smarty->assign('total', $total);
|
||||
$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->assign('search', $search);
|
||||
$smarty->assign('search_query', buildSearchQuery($search));
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Polls'));
|
||||
|
||||
$smarty->display('admin/polls.tpl');
|
|
@ -0,0 +1,60 @@
|
|||
<?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/OpenSondage: 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\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SecurityService;
|
||||
|
||||
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();
|
||||
$inputService = new InputService();
|
||||
|
||||
/* POST */
|
||||
/*-----*/
|
||||
|
||||
$action = $inputService->filterName(isset($_POST['action']) ? $_POST['action'] : null);
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
||||
if ($action === 'purge' && $securityService->checkCsrf('admin', $_POST['csrf'])) {
|
||||
$count = $purgeService->purgeOldPolls();
|
||||
$message = __('Admin', 'Purged:') . ' ' . $count;
|
||||
}
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->assign('crsf', $securityService->getToken('admin'));
|
||||
|
||||
$smarty->assign('title', __('Admin', 'Purge'));
|
||||
|
||||
$smarty->display('admin/purge.tpl');
|
|
@ -0,0 +1,510 @@
|
|||
<?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/OpenSondage: 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\Editable;
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Exception\MomentAlreadyExistsException;
|
||||
use Framadate\Message;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Services\AdminPollService;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\NotificationService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
/* Variables */
|
||||
/* --------- */
|
||||
|
||||
$admin_poll_id = null;
|
||||
$poll_id = null;
|
||||
$poll = null;
|
||||
$message = null;
|
||||
$editingVoteId = 0;
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$adminPollService = new AdminPollService($connect, $pollService, $logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$sessionService = new SessionService();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
||||
if (!empty($_GET['poll'])) {
|
||||
$admin_poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (strlen($admin_poll_id) === 24) {
|
||||
$poll = $pollService->findByAdminId($admin_poll_id);
|
||||
}
|
||||
}
|
||||
|
||||
if ($poll) {
|
||||
$poll_id = $poll->id;
|
||||
} else {
|
||||
$smarty->assign('error', __('Error', "This poll doesn't exist!"));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// creation message
|
||||
// -------------------------------
|
||||
|
||||
$messagePollCreated = $sessionService->get("Framadate", "messagePollCreated", FALSE);
|
||||
|
||||
if ($messagePollCreated) {
|
||||
$sessionService->remove("Framadate", "messagePollCreated");
|
||||
|
||||
$message = new Message('success', __('adminstuds', 'The poll was created.'));
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Update poll info
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['update_poll_info'])) {
|
||||
$updated = false;
|
||||
$field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description',
|
||||
'rules', 'expiration_date', 'name', 'hidden', 'removePassword', 'password']);
|
||||
|
||||
// Update the right poll field
|
||||
if ($field === 'title') {
|
||||
$title = $inputService->filterTitle($_POST['title']);
|
||||
if ($title) {
|
||||
$poll->title = $title;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'admin_mail') {
|
||||
$admin_mail = $inputService->filterMail($_POST['admin_mail']);
|
||||
if ($admin_mail) {
|
||||
$poll->admin_mail = $admin_mail;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'description') {
|
||||
$description = $inputService->filterDescription($_POST['description']);
|
||||
if ($description) {
|
||||
$poll->description = $description;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'rules') {
|
||||
$rules = strip_tags($_POST['rules']);
|
||||
switch ($rules) {
|
||||
case 0:
|
||||
$poll->active = false;
|
||||
$poll->editable = Editable::NOT_EDITABLE;
|
||||
$updated = true;
|
||||
break;
|
||||
case 1:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::NOT_EDITABLE;
|
||||
$updated = true;
|
||||
break;
|
||||
case 2:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::EDITABLE_BY_ALL;
|
||||
$updated = true;
|
||||
break;
|
||||
case 3:
|
||||
$poll->active = true;
|
||||
$poll->editable = Editable::EDITABLE_BY_OWN;
|
||||
$updated = true;
|
||||
break;
|
||||
}
|
||||
} elseif ($field === 'expiration_date') {
|
||||
$expiration_date = $inputService->filterDate($_POST['expiration_date']);
|
||||
if ($expiration_date) {
|
||||
$poll->end_date = $expiration_date;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'name') {
|
||||
$admin_name = $inputService->filterName($_POST['name']);
|
||||
if ($admin_name) {
|
||||
$poll->admin_name = $admin_name;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'hidden') {
|
||||
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
|
||||
if ($hidden !== $poll->hidden) {
|
||||
$poll->hidden = $hidden;
|
||||
$poll->results_publicly_visible = false;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'removePassword') {
|
||||
$removePassword = isset($_POST['removePassword']) ? $inputService->filterBoolean($_POST['removePassword']) : false;
|
||||
if ($removePassword) {
|
||||
$poll->results_publicly_visible = false;
|
||||
$poll->password_hash = null;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field === 'password') {
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
|
||||
/**
|
||||
* Did the user choose results to be publicly visible ?
|
||||
*/
|
||||
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false;
|
||||
/**
|
||||
* If there's one, save the password
|
||||
*/
|
||||
if (!empty($password)) {
|
||||
$poll->password_hash = PasswordHasher::hash($password);
|
||||
$updated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If not pasword was set and the poll should be hidden, hide the results
|
||||
*/
|
||||
if ($poll->password_hash === null || $poll->hidden === true) {
|
||||
$poll->results_publicly_visible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't have a password, the poll is hidden and we change the results public visibility
|
||||
*/
|
||||
if ($resultsPubliclyVisible !== $poll->results_publicly_visible && $poll->password_hash !== null && $poll->hidden === false) {
|
||||
$poll->results_publicly_visible = $resultsPubliclyVisible;
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update poll in database
|
||||
if ($updated && $adminPollService->updatePoll($poll)) {
|
||||
$message = new Message('success', __('adminstuds', 'Poll saved'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_POLL);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to save poll'));
|
||||
$poll = $pollService->findById($poll_id);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// A vote is going to be edited
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_GET['vote'])) {
|
||||
$editingVoteId = filter_input(INPUT_GET, 'vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Something to save (edit or add)
|
||||
// -------------------------------
|
||||
|
||||
$selectedNewVotes = [];
|
||||
|
||||
if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
if(empty($_POST['mail']) || $inputService->filterMail($_POST['mail'])===false) {
|
||||
$mail = null;
|
||||
} else {
|
||||
$mail = $inputService->filterMail($_POST['mail']);
|
||||
}
|
||||
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
|
||||
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
|
||||
$slots_hash = $inputService->filterMD5($_POST['control']);
|
||||
|
||||
if (empty($editedVote)) {
|
||||
$message = new Message('danger', __('Error', 'Something has gone wrong...'));
|
||||
}
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Update vote
|
||||
try {
|
||||
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash, $mail);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote updated'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Update vote failed'));
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', "The name you've chosen already exists in this poll!"));
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['save'])) { // Add a new vote
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
if(empty($_POST['mail']) || $inputService->filterMail($_POST['mail'])===false) {
|
||||
$mail = null;
|
||||
} else {
|
||||
$mail = $inputService->filterMail($_POST['mail']);
|
||||
}
|
||||
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
|
||||
$slots_hash = $inputService->filterMD5($_POST['control']);
|
||||
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
// Add vote
|
||||
try {
|
||||
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash, $mail);
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote added'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Adding vote failed'));
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', 'You already voted'));
|
||||
$selectedNewVotes = $choices;
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a votes
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_GET['delete_vote'])) {
|
||||
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BASE64_REGEX]]);
|
||||
$vote_id = Utils::base64url_decode($vote_id);
|
||||
if ($vote_id && $adminPollService->deleteVote($poll_id, $vote_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Vote deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the vote!'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Remove all votes
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['remove_all_votes'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_votes.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_remove_all_votes'])) {
|
||||
if ($adminPollService->cleanVotes($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'All votes deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete all votes'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a comment
|
||||
// -------------------------------
|
||||
|
||||
if (!empty($_POST['delete_comment'])) {
|
||||
$comment_id = filter_input(INPUT_POST, 'delete_comment', FILTER_VALIDATE_INT);
|
||||
|
||||
if ($adminPollService->deleteComment($poll_id, $comment_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Comment deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the comment'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Remove all comments
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['remove_all_comments'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_comments.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_remove_all_comments'])) {
|
||||
if ($adminPollService->cleanComments($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'All comments deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete all comments'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete the entire poll
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_POST['delete_poll'])) {
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->display('confirm/delete_poll.tpl');
|
||||
exit;
|
||||
}
|
||||
if (isset($_POST['confirm_delete_poll'])) {
|
||||
if ($adminPollService->deleteEntirePoll($poll_id)) {
|
||||
$message = new Message('success', __('adminstuds', 'Poll fully deleted'));
|
||||
$notificationService->sendUpdateNotification($poll, NotificationService::DELETED_POLL);
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete the poll'));
|
||||
}
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->display('poll_deleted.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Delete a slot
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_GET['delete_column'])) {
|
||||
$column = filter_input(INPUT_GET, 'delete_column', FILTER_DEFAULT);
|
||||
$column = Utils::base64url_decode($column);
|
||||
|
||||
if ($poll->format === 'D') {
|
||||
$ex = explode('@', $column);
|
||||
|
||||
$slot = new stdClass();
|
||||
$slot->title = $ex[0];
|
||||
$slot->moment = $ex[1];
|
||||
|
||||
$result = $adminPollService->deleteDateSlot($poll, $slot);
|
||||
} else {
|
||||
$result = $adminPollService->deleteClassicSlot($poll, $column);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$message = new Message('success', __('adminstuds', 'Column deleted'));
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Failed to delete column'));
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Collect the mails of a column
|
||||
// -------------------------------
|
||||
|
||||
if (isset($_GET['collect_mail'])) {
|
||||
$column_str = strval(filter_input(INPUT_GET, 'collect_mail', FILTER_DEFAULT));
|
||||
$column_str = strval(Utils::base64url_decode($column_str));
|
||||
$column = intval($column_str);
|
||||
$votes = $pollService->splitVotes($pollService->allVotesByPollId($poll_id));
|
||||
$mails_yes = $mails_ifneedbe = $mails_no = [];
|
||||
foreach ($votes as $vote) {
|
||||
if (intval($vote->choices[$column]) === 2 && $vote->mail !== NULL) {
|
||||
$mails_yes[] = $vote->mail;
|
||||
} elseif (intval($vote->choices[$column]) === 1 && $vote->mail !== NULL) {
|
||||
$mails_ifneedbe[] = $vote->mail;
|
||||
} elseif($vote->mail !== NULL) {
|
||||
$mails_no[] = $vote->mail;
|
||||
}
|
||||
}
|
||||
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('admin', true);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title . ' - ' . __('adminstuds', 'Collect the emails of the polled users for the choice'));
|
||||
$smarty->assign('mails_yes', $mails_yes);
|
||||
$smarty->assign('mails_ifneedbe', $mails_ifneedbe);
|
||||
$smarty->assign('mails_no', $mails_no);
|
||||
$smarty->display('display_mails.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Add a slot
|
||||
// -------------------------------
|
||||
|
||||
function exit_displaying_add_column($message = null) {
|
||||
global $smarty, $poll_id, $admin_poll_id, $poll;
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('format', $poll->format);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->display('add_column.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_GET['add_column'])) {
|
||||
exit_displaying_add_column();
|
||||
}
|
||||
|
||||
if (isset($_POST['confirm_add_column'])) {
|
||||
try {
|
||||
if (($poll->format === 'D' && empty($_POST['newdate']))
|
||||
|| ($poll->format === 'A' && empty($_POST['choice']))) {
|
||||
exit_displaying_add_column(new Message('danger', __('Error', "Can't create an empty column.")));
|
||||
}
|
||||
if ($poll->format === 'D') {
|
||||
$date = DateTime::createFromFormat(__('Date', 'Y-m-d'), $_POST['newdate'])->setTime(0, 0, 0);
|
||||
$time = $date->getTimestamp();
|
||||
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
|
||||
$adminPollService->addDateSlot($poll_id, $time, $newmoment);
|
||||
} else {
|
||||
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
|
||||
$adminPollService->addClassicSlot($poll_id, $newslot);
|
||||
}
|
||||
|
||||
$message = new Message('success', __('adminstuds', 'Choice added'));
|
||||
} catch (MomentAlreadyExistsException $e) {
|
||||
exit_displaying_add_column(new Message('danger', __('Error', 'The column already exists')));
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve data
|
||||
$slots = $pollService->allSlotsByPoll($poll);
|
||||
$votes = $pollService->allVotesByPollId($poll_id);
|
||||
$comments = $pollService->allCommentsByPollId($poll_id);
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$smarty->assign('poll', $poll);
|
||||
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
|
||||
$smarty->assign('expired', strtotime($poll->end_date) < time());
|
||||
$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
|
||||
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
|
||||
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
|
||||
$smarty->assign('votes', $pollService->splitVotes($votes));
|
||||
$smarty->assign('best_choices', $pollService->computeBestChoices($votes, $poll));
|
||||
$smarty->assign('comments', $comments);
|
||||
$smarty->assign('editingVoteId', $editingVoteId);
|
||||
$smarty->assign('message', $message);
|
||||
$smarty->assign('admin', true);
|
||||
$smarty->assign('hidden', false);
|
||||
$smarty->assign('accessGranted', true);
|
||||
$smarty->assign('resultPubliclyVisible', true);
|
||||
$smarty->assign('editedVoteUniqueId', '');
|
||||
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
|
||||
$smarty->assign('selectedNewVotes', $selectedNewVotes);
|
||||
|
||||
$smarty->display('studs.tpl');
|
|
@ -0,0 +1,54 @@
|
|||
<?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/OpenSondage: 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 Doctrine\DBAL\Migrations\AbstractMigration as DoctrineAbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
abstract class AbstractMigration extends DoctrineAbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @param $class
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @return bool
|
||||
*/
|
||||
public function legacyCheck(Schema $schema, $class)
|
||||
{
|
||||
/**
|
||||
* If there's no legacy table, we can go on
|
||||
*/
|
||||
if (!$schema->hasTable(Utils::table(MIGRATION_TABLE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$migration_table = $schema->getTable(Utils::table(MIGRATION_TABLE));
|
||||
/**
|
||||
* We check the migration table
|
||||
*/
|
||||
if ($migration_table->hasColumn('name')) {
|
||||
/** @var $stmt \Doctrine\DBAL\Driver\Statement */
|
||||
$stmt = $this->connection->prepare('SELECT * FROM ' . Utils::table(MIGRATION_TABLE) . ' WHERE name = ?');
|
||||
$stmt->execute([$class]);
|
||||
return $stmt->rowCount() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?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/OpenSondage: 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 = [];
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?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/OpenSondage: 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 CollectMail
|
||||
*
|
||||
* Is used to specify the poll's edition permissions.
|
||||
* @TODO : wait to use the SplEnum
|
||||
*
|
||||
* @package Framadate
|
||||
*/
|
||||
class CollectMail { // extends SplEnum
|
||||
const NO_COLLECT = 0;
|
||||
const COLLECT = 1;
|
||||
const COLLECT_REQUIRED = 2;
|
||||
const COLLECT_REQUIRED_VERIFIED = 3;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?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/OpenSondage: 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 Editable
|
||||
*
|
||||
* Is used to specify the poll's edition permissions.
|
||||
* @TODO : wait to use the SplEnum
|
||||
*
|
||||
* @package Framadate
|
||||
*/
|
||||
class Editable { // extends SplEnum
|
||||
const __default = self::EDITABLE_BY_ALL;
|
||||
|
||||
const NOT_EDITABLE = 0;
|
||||
const EDITABLE_BY_ALL = 1;
|
||||
const EDITABLE_BY_OWN = 2;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class AlreadyExistsException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class ConcurrentEditionException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
/**
|
||||
* Class ConcurrentVoteException
|
||||
*
|
||||
* Thrown when a poll has a maximum votes constraint for options, and a vote happened since the poll was rendered
|
||||
*/
|
||||
class ConcurrentVoteException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class MomentAlreadyExistsException extends \Exception {
|
||||
function __construct() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
<?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/OpenSondage: 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 $id;
|
||||
public $description;
|
||||
public $admin_name;
|
||||
public $admin_mail;
|
||||
public $format;
|
||||
public $end_date;
|
||||
public $choix_sondage;
|
||||
public $ValueMax;
|
||||
|
||||
/**
|
||||
* Tells if users can modify their choices.
|
||||
* @var \Framadate\Editable
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* If true, only the poll maker can see the poll's results
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_ValueMax;
|
||||
|
||||
/**
|
||||
* if true, there will be a limit of voters per option
|
||||
* @var boolean
|
||||
*/
|
||||
public $hidden;
|
||||
|
||||
/**
|
||||
* If true, the author want to customize the URL
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_customized_url;
|
||||
|
||||
/**
|
||||
* If true, a password will be needed to access the poll
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_password;
|
||||
|
||||
/**
|
||||
* The password needed to access the poll, hashed. Only used if $use_password is set to true
|
||||
* @var string
|
||||
*/
|
||||
public $password_hash;
|
||||
|
||||
/**
|
||||
* If true, the polls results will be also visible for those without password
|
||||
* @var boolean
|
||||
*/
|
||||
public $results_publicly_visible;
|
||||
|
||||
/**
|
||||
* Tells if voters email addresses are collected or not.
|
||||
* @var \Framadate\CollectMail
|
||||
*/
|
||||
public $collect_users_mail;
|
||||
|
||||
/**
|
||||
* List of available choices
|
||||
*/
|
||||
private $choices;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->editable = Editable::EDITABLE_BY_ALL;
|
||||
$this->collect_users_mail = CollectMail::NO_COLLECT;
|
||||
$this->clearChoices();
|
||||
}
|
||||
|
||||
public function clearChoices() {
|
||||
$this->choices = [];
|
||||
}
|
||||
|
||||
public function addChoice(Choice $choice)
|
||||
{
|
||||
$this->choices[] = $choice;
|
||||
}
|
||||
|
||||
public function getChoices()
|
||||
{
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
public function sortChoices()
|
||||
{
|
||||
usort($this->choices, ['Framadate\Choice', 'compare']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?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/OpenSondage: 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;
|
||||
var $link;
|
||||
var $linkTitle;
|
||||
var $linkIcon;
|
||||
var $includeTemplate;
|
||||
|
||||
function __construct($type, $message, $link=null, $linkTitle=null, $linkIcon=null, $includeTemplate=null) {
|
||||
$this->type = $type;
|
||||
$this->message = $message;
|
||||
$this->link = $link;
|
||||
$this->linkTitle = $linkTitle;
|
||||
$this->linkIcon = $linkIcon;
|
||||
$this->includeTemplate = $includeTemplate;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Class From_0_0_to_0_8_Migration
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.8
|
||||
*/
|
||||
class Version20150101000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'First installation of the Framadate application (v0.8)';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @return void true is the execution succeeded
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_0_to_0_8_Migration'), 'Migration has been executed in an earlier database migration system');
|
||||
$sondage = $schema->createTable('sondage');
|
||||
$sondage->addColumn('id_sondage', 'string');
|
||||
$sondage->addColumn('commentaires', 'text');
|
||||
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('titre', 'text');
|
||||
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
|
||||
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
|
||||
$sondage->addColumn('format', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
|
||||
$sondage->addColumn('statut', 'integer', ['default' => '1']);
|
||||
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
|
||||
|
||||
$sujetStuds = $schema->createTable('sujet_studs');
|
||||
$sujetStuds->addColumn('id_sondage', 'string');
|
||||
$sujetStuds->addColumn('sujet', 'text');
|
||||
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
|
||||
|
||||
$comments = $schema->createTable('comments');
|
||||
$schema->createSequence('comments_seq');
|
||||
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
|
||||
$comments->addColumn('id_sondage', 'string');
|
||||
$comments->addColumn('comment', 'text');
|
||||
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
|
||||
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
|
||||
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
|
||||
|
||||
$userStuds = $schema->createTable('user_studs');
|
||||
$schema->createSequence('user_studs_seq');
|
||||
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
|
||||
$userStuds->addColumn('nom', 'string');
|
||||
$userStuds->addColumn('id_sondage', 'string');
|
||||
$userStuds->addColumn('reponses', 'text');
|
||||
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
|
||||
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$this->addSql('DROP TABLE sondage');
|
||||
$this->addSql('DROP TABLE sujet_studs');
|
||||
$this->addSql('DROP TABLE comments');
|
||||
$this->addSql('DROP TABLE user_studs');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150102000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'From 0.8 to 0.9 first part';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_8_to_0_9_Migration'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach (['sondage', 'sujet_studs', 'comments', 'user_studs'] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
|
||||
$this->createPollTable($schema);
|
||||
$this->createCommentTable($schema);
|
||||
$this->createSlotTable($schema);
|
||||
$this->createVoteTable($schema);
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$sondage = $schema->createTable('sondage');
|
||||
$sondage->addColumn('id_sondage', 'string');
|
||||
$sondage->addColumn('commentaires', 'text');
|
||||
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('titre', 'text');
|
||||
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
|
||||
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
|
||||
$sondage->addColumn('format', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
|
||||
$sondage->addColumn('statut', 'integer', ['default' => '1']);
|
||||
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
|
||||
|
||||
$sujetStuds = $schema->createTable('sujet_studs');
|
||||
$sujetStuds->addColumn('id_sondage', 'string');
|
||||
$sujetStuds->addColumn('sujet', 'text');
|
||||
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
|
||||
|
||||
$comments = $schema->createTable('comments');
|
||||
$schema->createSequence('comments_seq');
|
||||
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
|
||||
$comments->addColumn('id_sondage', 'string');
|
||||
$comments->addColumn('comment', 'text');
|
||||
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
|
||||
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
|
||||
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
|
||||
|
||||
$userStuds = $schema->createTable('user_studs');
|
||||
$schema->createSequence('user_studs_seq');
|
||||
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
|
||||
$userStuds->addColumn('nom', 'string');
|
||||
$userStuds->addColumn('id_sondage', 'string');
|
||||
$userStuds->addColumn('reponses', 'text');
|
||||
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
|
||||
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
|
||||
|
||||
$schema->dropTable(Utils::table('poll'));
|
||||
$schema->dropTable(Utils::table('comment'));
|
||||
$schema->dropTable(Utils::table('vote'));
|
||||
$schema->dropTable(Utils::table('slot'));
|
||||
}
|
||||
|
||||
private function createPollTable(Schema $schema)
|
||||
{
|
||||
$poll = $schema->createTable(Utils::table('poll'));
|
||||
$poll->addColumn('id', 'string');
|
||||
$poll->addColumn('admin_id', 'string');
|
||||
$poll->addColumn('title', 'text');
|
||||
$poll->addColumn('description', 'text', ['notnull' => false]);
|
||||
$poll->addColumn('admin_name', 'string');
|
||||
$poll->addColumn('admin_mail', 'string', ['notnull' => false]);
|
||||
$poll->addColumn('creation_date', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
|
||||
$poll->addColumn('end_date', 'datetime', ['notnull' => false]);
|
||||
$poll->addColumn('format', 'string', ['default' => null, 'notnull' => false]);
|
||||
$poll->addColumn('editable', 'integer', ['default' => 0]);
|
||||
$poll->addColumn('receiveNewVotes', 'boolean', ['default' => false]);
|
||||
$poll->addColumn('active', 'boolean', ['default' => true]);
|
||||
$poll->addUniqueIndex(['id'], 'poll_index_id');
|
||||
}
|
||||
|
||||
private function createSlotTable(Schema $schema)
|
||||
{
|
||||
$slot = $schema->createTable(Utils::table('slot'));
|
||||
$schema->createSequence('slot_seq');
|
||||
$slot->addColumn('id', 'integer', ['autoincrement' => true]);
|
||||
$slot->addColumn('poll_id', 'string');
|
||||
$slot->addColumn('title', 'text');
|
||||
$slot->addColumn('moments', 'text', ['notnull' => false]);
|
||||
$slot->addUniqueIndex(['id'], 'slot_index_id');
|
||||
$slot->addIndex(['poll_id'], 'slot_index_poll_id');
|
||||
}
|
||||
|
||||
private function createCommentTable(Schema $schema)
|
||||
{
|
||||
$comment = $schema->createTable(Utils::table('comment'));
|
||||
$schema->createSequence('comment_seq');
|
||||
$comment->addColumn('id', 'integer', ['autoincrement' => true]);
|
||||
$comment->addColumn('poll_id', 'string');
|
||||
$comment->addColumn('name', 'text', ['notnull' => false]);
|
||||
$comment->addColumn('comment', 'text');
|
||||
$comment->addUniqueIndex(['id'], 'comment_index_id');
|
||||
$comment->addIndex(['poll_id'], 'comment_index_poll_id');
|
||||
}
|
||||
|
||||
private function createVoteTable(Schema $schema)
|
||||
{
|
||||
$vote = $schema->createTable(Utils::table('vote'));
|
||||
$schema->createSequence('vote_seq');
|
||||
$vote->addColumn('id', 'integer', ['autoincrement' => true]);
|
||||
$vote->addColumn('poll_id', 'string');
|
||||
$vote->addColumn('name', 'string');
|
||||
$vote->addColumn('choices', 'string');
|
||||
$vote->addUniqueIndex(['id'], 'vote_index_id');
|
||||
$vote->addIndex(['poll_id'], 'vote_index_poll_id');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150102100000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'From 0.8 to 0.9 second part';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_8_to_0_9_Migration'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach ([Utils::table('poll'), Utils::table('comment'), Utils::table('slot'), Utils::table('vote')] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
|
||||
$this->migrateFromSondageToPoll();
|
||||
$this->migrateFromCommentsToComment();
|
||||
$this->migrateFromSujetStudsToSlot();
|
||||
//$this->migrateFromUserStudsToVote();
|
||||
|
||||
$this->dropOldTables($schema);
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$sondage = $schema->createTable('sondage');
|
||||
$sondage->addColumn('id_sondage', 'string');
|
||||
$sondage->addColumn('commentaires', 'text');
|
||||
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('titre', 'text');
|
||||
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
|
||||
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
|
||||
$sondage->addColumn('format', 'string', ['notnull' => false]);
|
||||
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
|
||||
$sondage->addColumn('statut', 'integer', ['default' => '1']);
|
||||
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
|
||||
|
||||
$sujetStuds = $schema->createTable('sujet_studs');
|
||||
$sujetStuds->addColumn('id_sondage', 'string');
|
||||
$sujetStuds->addColumn('sujet', 'text');
|
||||
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
|
||||
|
||||
$comments = $schema->createTable('comments');
|
||||
$schema->createSequence('comments_seq');
|
||||
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
|
||||
$comments->addColumn('id_sondage', 'string');
|
||||
$comments->addColumn('comment', 'text');
|
||||
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
|
||||
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
|
||||
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
|
||||
|
||||
$userStuds = $schema->createTable('user_studs');
|
||||
$schema->createSequence('user_studs_seq');
|
||||
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
|
||||
$userStuds->addColumn('nom', 'string');
|
||||
$userStuds->addColumn('id_sondage', 'string');
|
||||
$userStuds->addColumn('reponses', 'text');
|
||||
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
|
||||
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
|
||||
|
||||
$schema->dropTable(Utils::table('poll'));
|
||||
$schema->dropTable(Utils::table('comment'));
|
||||
$schema->dropTable(Utils::table('vote'));
|
||||
$schema->dropTable(Utils::table('slot'));
|
||||
}
|
||||
|
||||
private function migrateFromSondageToPoll()
|
||||
{
|
||||
$select = $this->connection->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 = $this->connection->prepare('
|
||||
INSERT INTO ' . Utils::table('poll') . '
|
||||
(id, admin_id, title, description, admin_name, admin_mail, creation_date, end_date, format, editable, receiveNewVotes, active)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
|
||||
|
||||
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 migrateFromSujetStudsToSlot()
|
||||
{
|
||||
$stmt = $this->connection->query('SELECT * FROM sujet_studs');
|
||||
$sujets = $stmt->fetchAll();
|
||||
$slots = [];
|
||||
|
||||
foreach ($sujets as $sujet) {
|
||||
$newSlots = $this->transformSujetToSlot($sujet);
|
||||
foreach ($newSlots as $newSlot) {
|
||||
$slots[] = $newSlot;
|
||||
}
|
||||
}
|
||||
|
||||
$prepared = $this->connection->prepare('INSERT INTO ' . Utils::table('slot') . ' (poll_id, title, moments) VALUES (?,?,?)');
|
||||
foreach ($slots as $slot) {
|
||||
$prepared->execute([
|
||||
$slot->poll_id,
|
||||
$this->unescape($slot->title),
|
||||
!empty($slot->moments) ? $this->unescape($slot->moments) : null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function migrateFromCommentsToComment()
|
||||
{
|
||||
$select = $this->connection->query('
|
||||
SELECT
|
||||
id_sondage,
|
||||
usercomment,
|
||||
comment
|
||||
FROM comments');
|
||||
|
||||
$insert = $this->connection->prepare('
|
||||
INSERT INTO ' . Utils::table('comment') . ' (poll_id, name, comment)
|
||||
VALUES (?,?,?)');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
$insert->execute([
|
||||
$row->id_sondage,
|
||||
$this->unescape($row->usercomment),
|
||||
$this->unescape($row->comment)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function migrateFromUserStudsToVote()
|
||||
{
|
||||
$select = $this->connection->query('
|
||||
SELECT
|
||||
id_sondage,
|
||||
nom,
|
||||
REPLACE(REPLACE(REPLACE(reponses, 1, \'X\'), 2, 1), \'X\', 2) reponses
|
||||
FROM user_studs');
|
||||
|
||||
$insert = $this->connection->prepare('
|
||||
INSERT INTO ' . Utils::table('vote') . ' (poll_id, name, choices)
|
||||
VALUES (?,?,?)');
|
||||
|
||||
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(Schema $schema)
|
||||
{
|
||||
$schema->dropTable('comments');
|
||||
$schema->dropTable('sujet_studs');
|
||||
$schema->dropTable('user_studs');
|
||||
$schema->dropTable('sondage');
|
||||
}
|
||||
|
||||
private function unescape($value)
|
||||
{
|
||||
return stripslashes(html_entity_decode($value, ENT_QUOTES));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the field receiveNewComments on the poll table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150117000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column "receiveNewComments" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\AddColumn_receiveNewComments_For_0_9'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
$this->skipIf($pollTable->hasColumn('receiveNewComments'), 'Column receiveNewComments already exists');
|
||||
|
||||
$pollTable->addColumn('receiveNewComments', 'boolean', ['default' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
$pollTable->dropColumn('receiveNewComments');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the field uniqId on the vote table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150402000000 extends AbstractMigration
|
||||
{
|
||||
private $indexUniqIdName = 'IDX_vote_uniqId';
|
||||
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column "uniqId" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_uniqId_In_vote_For_0_9'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
$voteTable = $schema->getTable(Utils::table('vote'));
|
||||
|
||||
$this->skipIf($voteTable->hasColumn('uniqId'), 'Column uniqId already existing');
|
||||
|
||||
$voteTable->addColumn('uniqId', 'string', ['length' => 16]);
|
||||
$voteTable->addIndex(['uniqId'], $this->indexUniqIdName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$voteTable = $schema->getTable(Utils::table('vote'));
|
||||
|
||||
$voteTable->dropIndex($this->indexUniqIdName);
|
||||
$voteTable->dropColumn('uniqId');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the field hidden on the poll table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150405000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column "hidden" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_hidden_In_poll_For_0_9'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
|
||||
$this->skipIf($pollTable->hasColumn('hidden'), 'Column hidden already existing in table poll');
|
||||
|
||||
$pollTable->addColumn('hidden', 'boolean', ['default' => false, 'notnull' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
|
||||
$pollTable->dropColumn('hidden');
|
||||
}
|
||||
}
|
|
@ -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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Security\Token;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration generate uniqId for all legacy votes.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150624000000 extends AbstractMigration
|
||||
{
|
||||
public function description()
|
||||
{
|
||||
return 'Generate "uniqId" in "vote" table for all legacy votes';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Generate_uniqId_for_old_votes'), 'Migration has been executed in an earlier database migration system');
|
||||
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
|
||||
$this->connection->beginTransaction();
|
||||
|
||||
$select = $this->connection->query('
|
||||
SELECT id
|
||||
FROM ' . Utils::table('vote') . '
|
||||
WHERE uniqid = \'\'');
|
||||
|
||||
$update = $this->connection->prepare('
|
||||
UPDATE ' . Utils::table('vote') . '
|
||||
SET uniqid = :uniqid
|
||||
WHERE id = :id');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
$token = Token::getToken(16);
|
||||
$update->execute([
|
||||
'uniqid' => $token,
|
||||
'id' => $row->id
|
||||
]);
|
||||
}
|
||||
|
||||
$this->connection->commit();
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// TODO: Implement down() method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
<?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<EFBFBD>l DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est r<EFBFBD>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<EFBFBD>l DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration RPad votes from version 0.8.
|
||||
* Because some votes does not have enough values for their poll.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20150918000000 extends AbstractMigration
|
||||
{
|
||||
public function description()
|
||||
{
|
||||
return 'RPad votes from version 0.8.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf(
|
||||
$this->legacyCheck($schema, 'Framadate\Migration\RPadVotes_from_0_8'),
|
||||
'Migration has been executed in an earlier database migration system'
|
||||
);
|
||||
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table(
|
||||
'comment'
|
||||
)] as $table) {
|
||||
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
|
||||
}
|
||||
|
||||
$driver_name = $this->connection->getDatabasePlatform()->getName();
|
||||
switch ($driver_name) {
|
||||
case 'mysql':
|
||||
$this->addSql(
|
||||
'UPDATE ' . Utils::table('vote') . ' fv
|
||||
INNER JOIN (
|
||||
SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices
|
||||
FROM ' . Utils::table('vote') . ' v
|
||||
INNER JOIN
|
||||
(SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count
|
||||
FROM ' . Utils::table('slot') . ' s
|
||||
GROUP BY s.poll_id
|
||||
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
|
||||
WHERE LENGTH(v.choices) != inn.slots_count
|
||||
) computed ON fv.id = computed.id
|
||||
SET fv.choices = computed.new_choices'
|
||||
);
|
||||
break;
|
||||
case 'postgresql':
|
||||
$this->addSql(
|
||||
"UPDATE " . Utils::table('vote') . " fv
|
||||
SET choices = computed.new_choices
|
||||
FROM (
|
||||
SELECT v.id, RPAD(v.choices::text, inn.slots_count::int, '0') new_choices
|
||||
FROM " . Utils::table('vote') . " v
|
||||
INNER JOIN
|
||||
(SELECT s.poll_id, SUM(coalesce(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, ',', '')) + 1, 1)) slots_count
|
||||
FROM " . Utils::table('slot') . " s
|
||||
GROUP BY s.poll_id
|
||||
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
|
||||
WHERE LENGTH(v.choices) != inn.slots_count
|
||||
) computed WHERE fv.id = computed.id"
|
||||
);
|
||||
break;
|
||||
default:
|
||||
$this->skipIf(true, "Not on MySQL or PostgreSQL");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// TODO: Implement down() method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration alter the comment table to set a length to the name column.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.0
|
||||
*/
|
||||
class Version20151012075900 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Alter the comment table to set a length to the name column.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Alter_Comment_table_for_name_length'), 'Migration has been executed in an earlier database migration system');
|
||||
$commentTable = $schema->getTable(Utils::table('comment'));
|
||||
|
||||
$commentTable->changeColumn('name', ['default' => null, 'notnull' => false]);
|
||||
|
||||
$commentTable->changeColumn('name', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$commentTable = $schema->getTable(Utils::table('comment'));
|
||||
|
||||
$commentTable->changeColumn('name', ['type' => Type::getType('string')]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration alter the comment table to add a date column.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.0
|
||||
*/
|
||||
class Version20151012082600 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Alter the comment table to add a date column.';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Alter_Comment_table_adding_date'), 'Migration has been executed in an earlier database migration system');
|
||||
$commentTable = $schema->getTable(Utils::table('comment'));
|
||||
|
||||
$this->skipIf($commentTable->hasColumn('date'), 'Column date in comment table already exists');
|
||||
|
||||
$commentTable->addColumn('date', 'datetime', ['default' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$commentTable = $schema->getTable(Utils::table('comment'));
|
||||
|
||||
$commentTable->dropColumn('comment');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the fields password_hash and results_publicly_visible on the poll table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20151028000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9'), 'Migration has been executed in an earlier database migration system');
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
|
||||
$this->skipIf($pollTable->hasColumn('password_hash'), 'Column password_hash in table poll already exists');
|
||||
$this->skipIf($pollTable->hasColumn('results_publicly_visible'), 'Column results_publicly_visible in table poll already exists');
|
||||
|
||||
$pollTable->addColumn('password_hash', 'string', ['notnull' => false]);
|
||||
$pollTable->addColumn('results_publicly_visible', 'boolean', ['notnull' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
|
||||
$pollTable->dropColumn('password_hash');
|
||||
$pollTable->dropColumn('results_publicly_visible');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
class Version20151205000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Increase the size of id column in poll table';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Increase_pollId_size'), 'Migration has been executed in an earlier database migration system');
|
||||
$commentTable = $schema->getTable(Utils::table('comment'));
|
||||
|
||||
$commentTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
|
||||
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
|
||||
$pollTable->changeColumn('id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
|
||||
|
||||
$slotTable = $schema->getTable(Utils::table('slot'));
|
||||
|
||||
$slotTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
|
||||
|
||||
$voteTable = $schema->getTable(Utils::table('vote'));
|
||||
|
||||
$voteTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// TODO: Implement down() method.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the field Value_Max on the poll table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class Version20180220000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column "ValueMax" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_ValueMax_In_poll_For_1_1'), 'Migration has been executed in an earlier database migration system');
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
$pollTable->addColumn('ValueMax', 'smallint', ['default' => null, 'notnull' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$pollTable = $schema->getTable(Utils::table('poll'));
|
||||
$pollTable->dropColumn('ValueMax');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration sets Poll.end_date to NULL by default
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.1
|
||||
*/
|
||||
class Version20180411000000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Sets Poll end_date to NULL by default (work around MySQL NO_ZERO_DATE)';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param Connection|\PDO $connection The connection to database
|
||||
* @return bool true if the Migration should be executed.
|
||||
*/
|
||||
public function preCondition(Connection $connection)
|
||||
{
|
||||
$driver_name = $connection->getWrappedConnection()->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
|
||||
if ($driver_name === 'mysql') {
|
||||
$stmt = $connection->prepare(
|
||||
"SELECT Column_Default from Information_Schema.Columns where Table_Name = ? AND Column_Name = ?;"
|
||||
);
|
||||
$stmt->bindValue(1, Utils::table('poll'));
|
||||
$stmt->bindValue(2, 'end_date');
|
||||
$stmt->execute();
|
||||
$default = $stmt->fetch(\PDO::FETCH_COLUMN);
|
||||
|
||||
return $default === null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
// We don't disable this migration even if legacy because it wasn't working correctly before
|
||||
// $this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Fix_MySQL_No_Zero_Date'), 'Migration has been executed in an earlier database migration system');
|
||||
$this->skipIf($this->preCondition($this->connection), "Database server isn't MySQL or poll end_date default value was already NULL");
|
||||
$poll = $schema->getTable(Utils::table('poll'));
|
||||
$poll->changeColumn('end_date', ['default' => null, 'notnull' => false]);
|
||||
}
|
||||
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the column collect_users_mail in the poll table
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.2
|
||||
*/
|
||||
class Version20180419170000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column collect_users_mail in table poll';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_collect_mail_In_poll'), 'Migration has been executed in an earlier database migration system');
|
||||
$poll = $schema->getTable(Utils::table('poll'));
|
||||
$poll->addColumn('collect_users_mail', 'boolean', ['default' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$poll = $schema->getTable(Utils::table('poll'));
|
||||
$poll->dropColumn('collect_users_mail');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the column mail in the vote table
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.2
|
||||
*/
|
||||
class Version20180419180000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Add column mail in table vote';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_collect_mail_In_poll'), 'Migration has been executed in an earlier database migration system');
|
||||
$vote = $schema->getTable(Utils::table('vote'));
|
||||
$vote->addColumn('mail', 'string', ['default' => null, 'notnull' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$vote = $schema->getTable(Utils::table('vote'));
|
||||
$vote->dropColumn('mail');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the column mail in the vote table
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.2
|
||||
*/
|
||||
class Version20180419190000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Remove the old migration table';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$this->skipIf(!$schema->hasTable(Utils::table(MIGRATION_TABLE)), "The old migration table wasn't created, no need to delete it.");
|
||||
$schema->dropTable(Utils::table(MIGRATION_TABLE));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
// No need to recreate legacy migration table
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?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/OpenSondage: 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 DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Framadate\AbstractMigration;
|
||||
use Framadate\Utils;
|
||||
|
||||
/**
|
||||
* This migration adds the column collect_users_mail in the poll table
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.2
|
||||
*/
|
||||
class Version20180525110000 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
return 'Change column collect_users_mail in table poll from boolean to smallint';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
$poll = $schema->getTable(Utils::table('poll'));
|
||||
$poll->addColumn('collect_users_mail_integer', 'smallint', ['default' => 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function postUp(Schema $schema)
|
||||
{
|
||||
$this->addSql('UPDATE ' . Utils::table('poll') . ' SET collect_users_mail_integer = collect_users_mail');
|
||||
$this->addSql('ALTER TABLE ' . Utils::table('poll') . ' DROP COLUMN collect_users_mail');
|
||||
if ($this->connection->getDatabasePlatform()->getName() === 'mysql') {
|
||||
$this->addSql(
|
||||
'ALTER TABLE ' . Utils::table('poll') . ' CHANGE collect_users_mail_integer collect_users_mail SMALLINT'
|
||||
);
|
||||
} else {
|
||||
$this->addSql(
|
||||
'ALTER TABLE ' . Utils::table('poll') . ' RENAME COLUMN collect_users_mail_integer to collect_users_mail'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
{
|
||||
$poll = $schema->getTable(Utils::table('poll'));
|
||||
$poll->addColumn('collect_users_mail_boolean', 'boolean', ['default' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function postDown(Schema $schema)
|
||||
{
|
||||
$this->addSql('UPDATE ' . Utils::table('poll') . ' SET collect_users_mail_boolean = collect_users_mail > 0');
|
||||
$this->addSql('ALTER TABLE ' . Utils::table('poll') . ' DROP COLUMN collect_users_mail');
|
||||
|
||||
if ($this->connection->getDatabasePlatform()->getName() === 'mysql') {
|
||||
$this->addSql(
|
||||
'ALTER TABLE ' . Utils::table('poll') . ' CHANGE collect_users_mail_boolean collect_users_mail SMALLINT'
|
||||
);
|
||||
} else {
|
||||
$this->addSql(
|
||||
'ALTER TABLE ' . Utils::table('poll') . ' RENAME COLUMN collect_users_mail_boolean to collect_users_mail'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
abstract class AbstractRepository {
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected $connect;
|
||||
|
||||
/**
|
||||
* PollRepository constructor.
|
||||
* @param Connection $connect
|
||||
*/
|
||||
public function __construct(Connection $connect) {
|
||||
$this->connect = $connect;
|
||||
$this->connect->setFetchMode(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function beginTransaction()
|
||||
{
|
||||
$this->connect->beginTransaction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\ConnectionException
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\ConnectionException
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->connect->rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool|\Doctrine\DBAL\Driver\Statement|\PDOStatement
|
||||
*/
|
||||
public function prepare($sql)
|
||||
{
|
||||
return $this->connect->prepare($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sql
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool|\Doctrine\DBAL\Driver\Statement|\PDOStatement
|
||||
*/
|
||||
public function query($sql)
|
||||
{
|
||||
return $this->connect->query($sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function lastInsertId()
|
||||
{
|
||||
return $this->connect->lastInsertId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\Utils;
|
||||
|
||||
class CommentRepository extends AbstractRepository {
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array
|
||||
*/
|
||||
public function findAllByPollId($poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('comment') . ' WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new comment.
|
||||
*
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $comment
|
||||
* @return bool
|
||||
*/
|
||||
function insert($poll_id, $name, $comment)
|
||||
{
|
||||
return $this->connect->insert(Utils::table('comment'), ['poll_id' => $poll_id, 'name' => $name, 'comment' => $comment]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $comment_id
|
||||
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
function deleteById($poll_id, $comment_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('comment'), ['poll_id' => $poll_id, 'id' => $comment_id]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all comments of a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the given poll.
|
||||
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
|
||||
* @return bool true if action succeeded.
|
||||
*/
|
||||
function deleteByPollId($poll_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('comment'), ['poll_id' => $poll_id]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $comment
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($poll_id, $name, $comment)
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('comment') . ' WHERE poll_id = ? AND name = ? AND comment = ?');
|
||||
$prepared->execute([$poll_id, $name, $comment]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
class PollRepository extends AbstractRepository {
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $admin_poll_id
|
||||
* @param $form
|
||||
*/
|
||||
public function insertPoll($poll_id, $admin_poll_id, $form)
|
||||
{
|
||||
$this->connect->insert(Utils::table('poll'), [
|
||||
'id' => $poll_id,
|
||||
'admin_id' => $admin_poll_id,
|
||||
'title' => $form->title,
|
||||
'description' => $form->description,
|
||||
'admin_name' => $form->admin_name,
|
||||
'admin_mail' => $form->admin_mail,
|
||||
'end_date' => (new \DateTime)->setTimestamp($form->end_date)->format('Y-m-d H:i:s'),
|
||||
'format' => $form->format,
|
||||
'editable' => ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0,
|
||||
'receiveNewVotes' => $form->receiveNewVotes ? 1 : 0,
|
||||
'receiveNewComments' => $form->receiveNewComments ? 1 : 0,
|
||||
'hidden' => $form->hidden ? 1 : 0,
|
||||
'password_hash' => $form->password_hash,
|
||||
'results_publicly_visible' => $form->results_publicly_visible ? 1 : 0,
|
||||
'ValueMax' => $form->ValueMax,
|
||||
'collect_users_mail' => ($form->collect_users_mail >= 0 && $form->collect_users_mail <= 3) ? $form->collect_users_mail : 0,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return mixed
|
||||
*/
|
||||
public function findById($poll_id)
|
||||
{
|
||||
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE id = ?', [$poll_id]);
|
||||
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $admin_poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return mixed
|
||||
*/
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE admin_id = ?', [$admin_poll_id]);
|
||||
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function existsById($poll_id) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('poll') . ' WHERE id = ?');
|
||||
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $admin_poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function existsByAdminId($admin_poll_id) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('poll') . ' WHERE admin_id = ?');
|
||||
|
||||
$prepared->execute([$admin_poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll
|
||||
* @return bool
|
||||
*/
|
||||
public function update($poll)
|
||||
{
|
||||
return $this->connect->update(Utils::table('poll'), [
|
||||
'title' => $poll->title,
|
||||
'admin_name' => $poll->admin_name,
|
||||
'admin_mail' => $poll->admin_mail,
|
||||
'description' => $poll->description,
|
||||
'end_date' => $poll->end_date, # TODO : Harmonize dates between here and insert
|
||||
'active' => $poll->active,
|
||||
'editable' => $poll->editable >= 0 && $poll->editable <= 2 ? $poll->editable : 0,
|
||||
'hidden' => $poll->hidden ? 1 : 0,
|
||||
'password_hash' => $poll->password_hash,
|
||||
'results_publicly_visible' => $poll->results_publicly_visible ? 1 : 0
|
||||
], [
|
||||
'id' => $poll->id
|
||||
]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteById($poll_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('poll'), ['id' => $poll_id]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find old polls. Limit: 20.
|
||||
*
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array Array of old polls
|
||||
*/
|
||||
public function findOldPolls()
|
||||
{
|
||||
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE DATE_ADD(end_date, INTERVAL ? DAY) < NOW() AND end_date != 0 LIMIT 20', [PURGE_DELAY]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search polls in database.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
|
||||
* @param int $start The number of first entry to select
|
||||
* @param int $limit The number of entries to find
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array The found polls
|
||||
*/
|
||||
public function findAll($search, $start, $limit) {
|
||||
// Polls
|
||||
|
||||
$request = "";
|
||||
$request .= "SELECT p.*,";
|
||||
$request .= " (SELECT count(1) FROM " . Utils::table('vote') . " v WHERE p.id=v.poll_id) votes";
|
||||
$request .= " FROM " . Utils::table('poll') . " p";
|
||||
$request .= " WHERE 1";
|
||||
|
||||
$values = [];
|
||||
|
||||
if (!empty($search["poll"])) {
|
||||
$request .= " AND p.id LIKE :poll";
|
||||
$values["poll"] = "{$search["poll"]}%";
|
||||
}
|
||||
|
||||
$fields = [
|
||||
// key of $search => column name
|
||||
"title" => "title",
|
||||
"name" => "admin_name",
|
||||
"mail" => "admin_mail",
|
||||
];
|
||||
|
||||
foreach ($fields as $searchKey => $columnName) {
|
||||
if (empty($search[$searchKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$request .= " AND p.$columnName LIKE :$searchKey";
|
||||
$values[$searchKey] = "%{$search[$searchKey]}%";
|
||||
}
|
||||
|
||||
$request .= " ORDER BY p.title ASC";
|
||||
$request .= " LIMIT :start, :limit";
|
||||
|
||||
$prepared = $this->prepare($request);
|
||||
|
||||
foreach ($values as $searchKey => $value) {
|
||||
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
|
||||
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||
|
||||
$prepared->execute();
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all polls that are created with the given admin mail.
|
||||
*
|
||||
* @param string $mail Email address of the poll admin
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array The list of matching polls
|
||||
*/
|
||||
public function findAllByAdminMail($mail) {
|
||||
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('poll') . ' WHERE admin_mail = :admin_mail');
|
||||
$prepared->execute(['admin_mail' => $mail]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of polls in database.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return int The number of polls
|
||||
*/
|
||||
public function count($search = null) {
|
||||
// Total count
|
||||
$prepared = $this->prepare('
|
||||
SELECT count(1) nb
|
||||
FROM ' . Utils::table('poll') . ' p
|
||||
WHERE (:id = "" OR p.id LIKE :id)
|
||||
AND (:title = "" OR p.title LIKE :title)
|
||||
AND (:name = "" OR p.admin_name LIKE :name)
|
||||
ORDER BY p.title ASC');
|
||||
|
||||
$poll = $search === null ? '' : $search['poll'] . '%';
|
||||
$title = $search === null ? '' : '%' . $search['title'] . '%';
|
||||
$name = $search === null ? '' : '%' . $search['name'] . '%';
|
||||
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
|
||||
|
||||
$prepared->execute();
|
||||
$count = $prepared->fetch();
|
||||
|
||||
/*echo '---';
|
||||
print_r($count);
|
||||
echo '---';
|
||||
exit;*/
|
||||
|
||||
return $count->nb;
|
||||
}
|
||||
}
|
|
@ -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/OpenSondage: 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\Repositories;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
class RepositoryFactory {
|
||||
private static $connect;
|
||||
|
||||
private static $pollRepository;
|
||||
private static $slotRepository;
|
||||
private static $voteRepository;
|
||||
private static $commentRepository;
|
||||
|
||||
/**
|
||||
* @param Connection $connect
|
||||
*/
|
||||
static function init(Connection $connect) {
|
||||
self::$connect = $connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PollRepository The singleton of PollRepository
|
||||
*/
|
||||
static function pollRepository() {
|
||||
if (self::$pollRepository === null) {
|
||||
self::$pollRepository = new PollRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$pollRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SlotRepository The singleton of SlotRepository
|
||||
*/
|
||||
static function slotRepository() {
|
||||
if (self::$slotRepository === null) {
|
||||
self::$slotRepository = new SlotRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$slotRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return VoteRepository The singleton of VoteRepository
|
||||
*/
|
||||
static function voteRepository() {
|
||||
if (self::$voteRepository === null) {
|
||||
self::$voteRepository = new VoteRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$voteRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CommentRepository The singleton of CommentRepository
|
||||
*/
|
||||
static function commentRepository() {
|
||||
if (self::$commentRepository === null) {
|
||||
self::$commentRepository = new CommentRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$commentRepository;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
<?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<EFBFBD>l DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est r<EFBFBD>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<EFBFBD>l DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\Choice;
|
||||
use Framadate\Utils;
|
||||
|
||||
class SlotRepository extends AbstractRepository {
|
||||
/**
|
||||
* Insert a bulk of slots.
|
||||
*
|
||||
* @param int $poll_id
|
||||
* @param array $choices
|
||||
*/
|
||||
public function insertSlots($poll_id, $choices) {
|
||||
foreach ($choices as $choice) {
|
||||
/** @var Choice $choice */
|
||||
// We prepared the slots (joined by comas)
|
||||
$joinedSlots = null;
|
||||
$first = true;
|
||||
foreach ($choice->getSlots() as $slot) {
|
||||
if ($first) {
|
||||
$joinedSlots = $slot;
|
||||
$first = false;
|
||||
} else {
|
||||
$joinedSlots .= ',' . $slot;
|
||||
}
|
||||
}
|
||||
|
||||
// We execute the insertion
|
||||
$this->connect->insert(Utils::table('slot'), [
|
||||
'poll_id' => $poll_id,
|
||||
'title' => $choice->getName(),
|
||||
'moments' => $joinedSlots
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array
|
||||
*/
|
||||
public function listByPollId($poll_id)
|
||||
{
|
||||
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('slot') . ' WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return mixed Object The slot found, or null
|
||||
*/
|
||||
function findByPollIdAndDatetime($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 insert($poll_id, $title, $moments)
|
||||
{
|
||||
return $this->connect->insert(Utils::table('slot'), ['poll_id' => $poll_id, 'title' => $title, 'moments' => $moments]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 update($poll_id, $datetime, $newMoments)
|
||||
{
|
||||
return $this->connect->update(Utils::table('slot'), ['moments' => $newMoments], ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a entire slot from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $datetime mixed The datetime of the slot
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteByDateTime($poll_id, $datetime)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteByPollId($poll_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id]) > 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\Utils;
|
||||
|
||||
class VoteRepository extends AbstractRepository {
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return array
|
||||
*/
|
||||
public function allUserVotesByPollId($poll_id)
|
||||
{
|
||||
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('vote') . ' WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $insert_position
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function insertDefault($poll_id, $insert_position)
|
||||
{
|
||||
# TODO : Handle this on PHP's side
|
||||
$prepared = $this->prepare('UPDATE ' . Utils::table('vote') . ' SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
|
||||
|
||||
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
|
||||
}
|
||||
|
||||
function insert($poll_id, $name, $choices, $token, $mail) {
|
||||
$this->connect->insert(Utils::table('vote'), ['poll_id' => $poll_id, 'name' => $name, 'choices' => $choices, 'uniqId' => $token, 'mail' => $mail]);
|
||||
|
||||
$newVote = new \stdClass();
|
||||
$newVote->poll_id = $poll_id;
|
||||
$newVote->id = $this->lastInsertId();
|
||||
$newVote->name = $name;
|
||||
$newVote->choices = $choices;
|
||||
$newVote->uniqId = $token;
|
||||
$newVote->mail=$mail;
|
||||
|
||||
return $newVote;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $vote_id
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteById($poll_id, $vote_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('vote'), ['poll_id' => $poll_id, 'id' => $vote_id]) > 0;
|
||||
}
|
||||
|
||||
public function deleteOldVotesByPollId($poll_id, $votesToDelete) {
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY `poll_id` ASC LIMIT ' . $votesToDelete);
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all votes of a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the given poll.
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
public function deleteByPollId($poll_id)
|
||||
{
|
||||
return $this->connect->delete(Utils::table('vote'), ['poll_id' => $poll_id]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
public function deleteByIndex($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]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $vote_id
|
||||
* @param $name
|
||||
* @param $choices
|
||||
* @return bool
|
||||
*/
|
||||
public function update($poll_id, $vote_id, $name, $choices, $mail)
|
||||
{
|
||||
return $this->connect->update(Utils::table('vote'), [
|
||||
'choices' => $choices,
|
||||
'name' => $name,
|
||||
'mail' => $mail,
|
||||
], [
|
||||
'poll_id' => $poll_id,
|
||||
'id' => $vote_id,
|
||||
]) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if name is already used for the given poll.
|
||||
*
|
||||
* @param int $poll_id ID of the poll
|
||||
* @param string $name Name of the vote
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool true if vote already exists
|
||||
*/
|
||||
public function existsByPollIdAndName($poll_id, $name) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('vote') . ' WHERE poll_id = ? AND name = ?');
|
||||
$prepared->execute([$poll_id, $name]);
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if name is already used for the given poll and another vote.
|
||||
*
|
||||
* @param int $poll_id ID of the poll
|
||||
* @param string $name Name of the vote
|
||||
* @param int $vote_id ID of the current vote
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
* @return bool true if vote already exists
|
||||
*/
|
||||
public function existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id) {
|
||||
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('vote') . ' WHERE poll_id = ? AND name = ? AND id != ?');
|
||||
$prepared->execute([$poll_id, $name, $vote_id]);
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Security;
|
||||
|
||||
/**
|
||||
* Class PasswordHasher
|
||||
*
|
||||
* Used to abstract the password hash logic
|
||||
*
|
||||
* @package Framadate\Security
|
||||
*/
|
||||
class PasswordHasher {
|
||||
/**
|
||||
* Hash a password
|
||||
*
|
||||
* @param string $password the password to hash.
|
||||
* @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash.
|
||||
*/
|
||||
public static function hash($password) {
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a password with a hash
|
||||
*
|
||||
* @param string $password the password to verify
|
||||
* @param string $hash the hash to compare.
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify($password, $hash) {
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
namespace Framadate\Security;
|
||||
|
||||
class Token {
|
||||
const DEFAULT_LENGTH = 64;
|
||||
private $time;
|
||||
private $value;
|
||||
private $length;
|
||||
private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
|
||||
|
||||
function __construct($length = self::DEFAULT_LENGTH) {
|
||||
$this->length = $length;
|
||||
$this->time = time() + TOKEN_TIME;
|
||||
$this->value = $this->generate();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a secure token if possible, or a less secure one if not.
|
||||
*
|
||||
* @param int $length The token length
|
||||
* @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not.
|
||||
* @return string
|
||||
*/
|
||||
public static function getToken($length = self::DEFAULT_LENGTH, &$crypto_strong = false) {
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
openssl_random_pseudo_bytes(1, $crypto_strong); // Fake use to see if the algorithm used was "cryptographically strong"
|
||||
return self::getSecureToken($length);
|
||||
}
|
||||
return self::getUnsecureToken($length);
|
||||
}
|
||||
|
||||
public static function getUnsecureToken($length) {
|
||||
$string = '';
|
||||
mt_srand();
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$string .= self::$codeAlphabet[mt_rand() % strlen(self::$codeAlphabet)];
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author http://stackoverflow.com/a/13733588
|
||||
*/
|
||||
public static function getSecureToken($length){
|
||||
$token = "";
|
||||
for($i=0;$i<$length;$i++){
|
||||
$token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))];
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
private function generate() {
|
||||
return self::getToken($this->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
|
||||
*/
|
||||
private static function crypto_rand_secure($min, $max) {
|
||||
$range = $max - $min;
|
||||
if ($range < 0) return $min; // not so random...
|
||||
$log = log($range, 2);
|
||||
$bytes = (int) ($log / 8) + 1; // length in bytes
|
||||
$bits = (int) $log + 1; // length in bits
|
||||
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
|
||||
do {
|
||||
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
|
||||
$rnd = $rnd & $filter; // discard irrelevant bits
|
||||
} while ($rnd >= $range);
|
||||
return $min + $rnd;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Framadate\Exception\MomentAlreadyExistsException;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
/**
|
||||
* Class AdminPollService
|
||||
*
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class AdminPollService {
|
||||
private $connect;
|
||||
private $pollService;
|
||||
private $logService;
|
||||
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(Connection $connect, PollService $pollService, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
$this->pollService = $pollService;
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
function updatePoll($poll) {
|
||||
global $config;
|
||||
if ($poll->end_date > $poll->creation_date) {
|
||||
return $this->pollRepository->update($poll);
|
||||
}
|
||||
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->commentRepository->deleteById($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->commentRepository->deleteByPollId($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->voteRepository->deleteById($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->voteRepository->deleteByPollId($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->pollRepository->findById($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->voteRepository->deleteByPollId($poll_id);
|
||||
$this->commentRepository->deleteByPollId($poll_id);
|
||||
$this->slotRepository->deleteByPollId($poll_id);
|
||||
$this->pollRepository->deleteById($poll_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a slot from a poll.
|
||||
*
|
||||
* @param object $poll The ID of the poll
|
||||
* @param object $slot The slot informations (datetime + moment)
|
||||
* @return bool true if action succeeded
|
||||
*/
|
||||
public function deleteDateSlot($poll, $slot) {
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
|
||||
|
||||
$datetime = $slot->title;
|
||||
$moment = $slot->moment;
|
||||
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
|
||||
// We can't delete the last slot
|
||||
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
|
||||
return false;
|
||||
} elseif ($poll->format === 'A' && count($slots) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$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->voteRepository->deleteByIndex($poll->id, $indexToDelete);
|
||||
if (count($newMoments) > 0) {
|
||||
$this->slotRepository->update($poll->id, $datetime, implode(',', $newMoments));
|
||||
} else {
|
||||
$this->slotRepository->deleteByDateTime($poll->id, $datetime);
|
||||
}
|
||||
$this->connect->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteClassicSlot($poll, $slot_title) {
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
|
||||
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
|
||||
if (count($slots) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$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->voteRepository->deleteByIndex($poll->id, $indexToDelete);
|
||||
$this->slotRepository->deleteByDateTime($poll->id, $slot_title);
|
||||
$this->connect->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new slot to a date 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
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
* @throws \Doctrine\DBAL\ConnectionException
|
||||
*/
|
||||
public function addDateSlot($poll_id, $datetime, $new_moment) {
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
|
||||
|
||||
try {
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
$result = $this->findInsertPosition($slots, $datetime);
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('ERROR', "Database error, couldn't find slot insert position" . $e->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
if ($result->slot !== null) {
|
||||
$slot = $result->slot;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
// Check if moment already exists (maybe not necessary)
|
||||
if (in_array($new_moment, $moments, true)) {
|
||||
throw new MomentAlreadyExistsException();
|
||||
}
|
||||
|
||||
// Update found slot
|
||||
$moments[] = $new_moment;
|
||||
$this->slotRepository->update($poll_id, $datetime, implode(',', $moments));
|
||||
} else {
|
||||
$this->slotRepository->insert($poll_id, $datetime, $new_moment);
|
||||
}
|
||||
|
||||
$this->voteRepository->insertDefault($poll_id, $result->insert);
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
} catch (DBALException $e) {
|
||||
$this->connect->rollBack();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new slot to a classic poll. And insert default values for user's votes.
|
||||
* <ul>
|
||||
* <li>Create a new slot if no one exists for the given title</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $title int The title
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
* @throws \Doctrine\DBAL\ConnectionException
|
||||
* @throws \Doctrine\DBAL\DBALException
|
||||
*/
|
||||
public function addClassicSlot($poll_id, $title) {
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
|
||||
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
|
||||
// Check if slot already exists
|
||||
$titles = array_map(function ($slot) {
|
||||
return $slot->title;
|
||||
}, $slots);
|
||||
if (in_array($title, $titles, true)) {
|
||||
// The moment already exists
|
||||
throw new MomentAlreadyExistsException();
|
||||
}
|
||||
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
// New slot
|
||||
$this->slotRepository->insert($poll_id, $title, null);
|
||||
// Set default votes
|
||||
$this->voteRepository->insertDefault($poll_id, count($slots));
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
|
||||
*/
|
||||
private function findInsertPosition($slots, $datetime) {
|
||||
$result = new \stdClass();
|
||||
$result->slot = null;
|
||||
$result->insert = 0;
|
||||
|
||||
// Sort slots before searching where to insert
|
||||
$this->pollService->sortSlorts($slots);
|
||||
|
||||
// Search where to insert new column
|
||||
foreach ($slots as $k=>$slot) {
|
||||
$rowDatetime = $slot->title;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
if ($datetime === $rowDatetime) {
|
||||
// Here we have to insert at the end of a slot
|
||||
$result->insert += count($moments);
|
||||
$result->slot = $slot;
|
||||
break;
|
||||
} elseif ($datetime < $rowDatetime) {
|
||||
// We have to insert before this slot
|
||||
break;
|
||||
}
|
||||
$result->insert += count($moments);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
<?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/OpenSondage: 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 DateTime;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param array $arr The array to filter
|
||||
* @param int $type The type of filter to apply
|
||||
* @param array|null $options The associative array of options
|
||||
* @return array The filtered array
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
public function filterTitle($title) {
|
||||
return $this->returnIfNotBlank($title);
|
||||
}
|
||||
|
||||
public function filterId($id) {
|
||||
$filtered = filter_var($id, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
return $filtered ? substr($filtered, 0, 64) : false;
|
||||
}
|
||||
|
||||
public function filterName($name) {
|
||||
$filtered = trim($name);
|
||||
return $this->returnIfNotBlank($filtered);
|
||||
}
|
||||
|
||||
public function filterMail($mail) {
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// formatting
|
||||
|
||||
$mail = trim($mail);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// e-mail validation
|
||||
|
||||
$resultat = FALSE;
|
||||
|
||||
$validator = new EmailValidator();
|
||||
|
||||
if ($validator->isValid($mail, new RFCValidation())) {
|
||||
$resultat = $mail;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// return
|
||||
|
||||
return $resultat;
|
||||
}
|
||||
|
||||
public function filterDescription($description) {
|
||||
$description = str_replace("\r\n", "\n", $description);
|
||||
return $description;
|
||||
}
|
||||
|
||||
public function filterMD5($control) {
|
||||
return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterInteger($int) {
|
||||
return filter_var($int, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
public function filterValueMax($int)
|
||||
{
|
||||
return $this->filterInteger($int) >= 1 ? $this->filterInteger($int) : false;
|
||||
}
|
||||
|
||||
public function filterBoolean($boolean) {
|
||||
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterEditable($editable) {
|
||||
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterCollectMail($collectMail) {
|
||||
return filter_var($collectMail, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => COLLECT_MAIL_CHOICE_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterComment($comment) {
|
||||
$comment = str_replace("\r\n", "\n", $comment);
|
||||
return $this->returnIfNotBlank($comment);
|
||||
}
|
||||
|
||||
public function filterDate($date) {
|
||||
$dDate = DateTime::createFromFormat(__('Date', 'Y-m-d'), $date)->setTime(0, 0, 0);
|
||||
return $dDate->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value if it's not blank.
|
||||
*
|
||||
* @param string $filtered The value
|
||||
* @return string|null
|
||||
*/
|
||||
private function returnIfNotBlank($filtered) {
|
||||
if ($filtered) {
|
||||
$withoutSpaces = str_replace(' ', '', $filtered);
|
||||
if (!empty($withoutSpaces)) {
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
<?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<EFBFBD>l DROZ
|
||||
* Authors of Framadate/OpenSondage: 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<EFBFBD>l DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
use Doctrine\DBAL\Configuration;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Framadate\Utils;
|
||||
use Smarty;
|
||||
|
||||
/**
|
||||
* This class helps to clean all inputs from the users or external services.
|
||||
*/
|
||||
class InstallService {
|
||||
private $fields = [
|
||||
// General
|
||||
'appName' => 'Framadate',
|
||||
'appMail' => '',
|
||||
'responseMail' => '',
|
||||
'defaultLanguage' => 'fr',
|
||||
'cleanUrl' => true,
|
||||
|
||||
// Database configuration
|
||||
'dbName' => 'framadate',
|
||||
'dbPort' => 3306,
|
||||
'dbHost' => 'localhost',
|
||||
'dbUser' => 'root',
|
||||
'dbPassword' => '',
|
||||
'dbPrefix' => 'fd_',
|
||||
'migrationTable' => 'framadate_migration'
|
||||
];
|
||||
|
||||
function __construct() {}
|
||||
|
||||
public function updateFields($data) {
|
||||
foreach ($data as $field => $value) {
|
||||
$this->fields[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function install(Smarty &$smarty) {
|
||||
// Check values are present
|
||||
if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbName']) || empty($this->fields['dbHost']) || empty($this->fields['dbPort']) || empty($this->fields['dbUser'])) {
|
||||
return $this->error('Missing values');
|
||||
}
|
||||
|
||||
// Connect to database
|
||||
try {
|
||||
$connect = $this->connectTo($this->fields);
|
||||
} catch(\Doctrine\DBAL\DBALException $e) {
|
||||
return $this->error('Unable to connect to database', $e->getMessage());
|
||||
}
|
||||
|
||||
// Write configuration to conf.php file
|
||||
if ($this->writeConfiguration($smarty) === false) {
|
||||
return $this->error(__f('Error', "Can't create the config.php file in '%s'.", CONF_FILENAME));
|
||||
}
|
||||
|
||||
return $this->ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $fields
|
||||
* @return \Doctrine\DBAL\Connection|null
|
||||
*/
|
||||
function connectTo($fields) {
|
||||
$doctrineConfig = new Configuration();
|
||||
$connectionParams = [
|
||||
'dbname' => $fields['dbName'],
|
||||
'user' => $fields['dbUser'],
|
||||
'password' => $fields['dbPassword'],
|
||||
'host' => $fields['dbHost'],
|
||||
'driver' => $fields['dbDriver'],
|
||||
'charset' => $fields['dbDriver'] === 'pdo_mysql' ? 'utf8mb4' : 'utf8',
|
||||
];
|
||||
try {
|
||||
return DriverManager::getConnection($connectionParams, $doctrineConfig);
|
||||
} catch (DBALException $e) {
|
||||
$logger = new LogService();
|
||||
$logger->log('ERROR', $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeConfiguration(Smarty &$smarty) {
|
||||
foreach($this->fields as $field=>$value) {
|
||||
$smarty->assign($field, $value);
|
||||
}
|
||||
|
||||
$content = $smarty->fetch('admin/config.tpl');
|
||||
|
||||
return $this->writeToFile($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return bool|int
|
||||
*/
|
||||
function writeToFile($content) {
|
||||
return @file_put_contents(CONF_FILENAME, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function ok() {
|
||||
return [
|
||||
'status' => 'OK',
|
||||
'msg' => __f('Installation', 'Ended', Utils::get_server_name())
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @return array
|
||||
*/
|
||||
function error($msg, $details = '') {
|
||||
return [
|
||||
'status' => 'ERROR',
|
||||
'code' => $msg,
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
public function getFields() {
|
||||
return $this->fields;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
class MailService {
|
||||
const DELAY_BEFORE_RESEND = 300;
|
||||
|
||||
const MAILSERVICE_KEY = 'mailservice';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $smtp_allowed;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $smtp_options = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $use_sendmail;
|
||||
|
||||
/**
|
||||
* @var LogService
|
||||
*/
|
||||
private $logService;
|
||||
|
||||
/**
|
||||
* MailService constructor.
|
||||
* @param $smtp_allowed
|
||||
* @param array $smtp_options
|
||||
* @param bool $use_sendmail
|
||||
*/
|
||||
public function __construct($smtp_allowed, $smtp_options = [], $use_sendmail = false) {
|
||||
$this->logService = new LogService();
|
||||
$this->smtp_allowed = $smtp_allowed;
|
||||
if (true === is_array($smtp_options)) {
|
||||
$this->smtp_options = $smtp_options;
|
||||
}
|
||||
$this->use_sendmail = $use_sendmail;
|
||||
}
|
||||
|
||||
public function isValidEmail($email) {
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
public function send($to, $subject, $body, $msgKey = null) {
|
||||
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
|
||||
$mail = new PHPMailer(true);
|
||||
$this->configureMailer($mail);
|
||||
|
||||
// From
|
||||
$mail->FromName = NOMAPPLICATION;
|
||||
$mail->From = ADRESSEMAILADMIN;
|
||||
if ($this->isValidEmail(ADRESSEMAILREPONSEAUTO)) {
|
||||
$mail->addReplyTo(ADRESSEMAILREPONSEAUTO);
|
||||
}
|
||||
|
||||
// To
|
||||
$mail->addAddress($to);
|
||||
|
||||
// Subject
|
||||
$mail->Subject = $subject;
|
||||
|
||||
// Bodies
|
||||
$body = $body . ' <br/><br/>' . __('Mail', 'Thank you for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', "\"The road is long, but the way is clear…\"<br/>Framasoft lives only by your donations.<br/>Thank you in advance for your support https://soutenir.framasoft.org");
|
||||
$mail->isHTML(true);
|
||||
$mail->msgHTML($body, ROOT_DIR, true);
|
||||
|
||||
// Build headers
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
|
||||
$mail->addCustomHeader('Return-Path', '<>');
|
||||
|
||||
// Send mail
|
||||
$mail->send();
|
||||
|
||||
// Log
|
||||
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
|
||||
|
||||
// Store the mail sending date
|
||||
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
|
||||
}
|
||||
}
|
||||
|
||||
public function canSendMsg($msgKey) {
|
||||
if ($msgKey === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
|
||||
$_SESSION[self::MAILSERVICE_KEY] = [];
|
||||
}
|
||||
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the mailer with the options
|
||||
*
|
||||
* @param PHPMailer $mailer
|
||||
*/
|
||||
private function configureMailer(PHPMailer $mailer) {
|
||||
if ($this->use_sendmail) {
|
||||
$mailer->isSendmail();
|
||||
} else {
|
||||
$mailer->isSMTP();
|
||||
}
|
||||
|
||||
$available_options = [
|
||||
'host' => 'Host',
|
||||
'auth' => 'SMTPAuth',
|
||||
'username' => 'Username',
|
||||
'password' => 'Password',
|
||||
'secure' => 'SMTPSecure',
|
||||
'port' => 'Port',
|
||||
];
|
||||
|
||||
foreach ($available_options as $config_option => $mailer_option) {
|
||||
if (true === isset($this->smtp_options[$config_option]) && false === empty($this->smtp_options[$config_option])) {
|
||||
$mailer->{$mailer_option} = $this->smtp_options[$config_option];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
use \stdClass;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Utils;
|
||||
|
||||
class NotificationService {
|
||||
const UPDATE_VOTE = 1;
|
||||
const ADD_VOTE = 2;
|
||||
const ADD_COMMENT = 3;
|
||||
const UPDATE_POLL = 10;
|
||||
const DELETED_POLL = 11;
|
||||
|
||||
private $mailService;
|
||||
|
||||
function __construct(MailService $mailService) {
|
||||
$this->mailService = $mailService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the poll admin to notify him about an update.
|
||||
*
|
||||
* @param $poll stdClass The poll
|
||||
* @param $name string The name user who triggered the notification
|
||||
* @param $type int cf: Constants on the top of this page
|
||||
*/
|
||||
function sendUpdateNotification(stdClass $poll, $type, $name='') {
|
||||
if (!isset($_SESSION['mail_sent'])) {
|
||||
$_SESSION['mail_sent'] = [];
|
||||
}
|
||||
|
||||
$isVoteAndCanSendIt = ($type === self::UPDATE_VOTE || $type === self::ADD_VOTE) && $poll->receiveNewVotes;
|
||||
$isCommentAndCanSendIt = $type === self::ADD_COMMENT && $poll->receiveNewComments;
|
||||
$isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT;
|
||||
|
||||
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
|
||||
if (self::isParticipation($type)) {
|
||||
$translationString = 'Poll participation: %s';
|
||||
} else {
|
||||
$translationString = 'Notification of poll: %s';
|
||||
}
|
||||
|
||||
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', $translationString, $poll->title);
|
||||
|
||||
$message = '';
|
||||
|
||||
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
|
||||
$link = '<a href="' . $urlSondage . '">' . $urlSondage . '</a>' . "\n\n";
|
||||
|
||||
switch ($type) {
|
||||
case self::UPDATE_VOTE:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "updated a vote.<br/>You can visit your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::ADD_VOTE:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "added a vote.<br/>You can visit your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::ADD_COMMENT:
|
||||
$message .= $name . ' ';
|
||||
$message .= __('Mail', "wrote a comment.<br/>You can visit your poll at the link") . " :\n\n";
|
||||
$message .= $link;
|
||||
break;
|
||||
case self::UPDATE_POLL:
|
||||
$message = __f('Mail', 'Someone just changed your poll at the following link <a href=\"%1$s\">%1$s</a>.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
|
||||
break;
|
||||
case self::DELETED_POLL:
|
||||
$message = __f('Mail', 'Someone just deleted your poll "%s".', Utils::htmlEscape($poll->title)) . "\n\n";
|
||||
break;
|
||||
}
|
||||
|
||||
$messageTypeKey = $type . '-' . $poll->id;
|
||||
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
|
||||
}
|
||||
}
|
||||
|
||||
function isParticipation($type)
|
||||
{
|
||||
return $type >= self::UPDATE_POLL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,398 @@
|
|||
<?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/OpenSondage: 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 Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\ConnectionException;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Form;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Security\Token;
|
||||
|
||||
class PollService {
|
||||
private $connect;
|
||||
private $logService;
|
||||
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(Connection $connect, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
try {
|
||||
if (preg_match(POLL_REGEX, $poll_id)) {
|
||||
return $this->pollRepository->findById($poll_id);
|
||||
}
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('ERROR', 'Database error : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $admin_poll_id
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
try {
|
||||
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
|
||||
return $this->pollRepository->findByAdminId($admin_poll_id);
|
||||
}
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('ERROR', 'Database error : ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @return array
|
||||
*/
|
||||
public function allCommentsByPollId($poll_id)
|
||||
{
|
||||
try {
|
||||
return $this->commentRepository->findAllByPollId($poll_id);
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('error', $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function allVotesByPollId($poll_id) {
|
||||
return $this->voteRepository->allUserVotesByPollId($poll_id);
|
||||
}
|
||||
|
||||
function allSlotsByPoll($poll) {
|
||||
$slots = $this->slotRepository->listByPollId($poll->id);
|
||||
if ($poll->format === 'D') {
|
||||
$this->sortSlorts($slots);
|
||||
}
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $vote_id
|
||||
* @param $name
|
||||
* @param $choices
|
||||
* @param $slots_hash
|
||||
* @param string $mail
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @return bool
|
||||
*/
|
||||
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash, $mail) {
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
|
||||
|
||||
// Update vote
|
||||
$choices = implode($choices);
|
||||
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices, $mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $choices
|
||||
* @param $slots_hash
|
||||
* @param string $mail
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @return \stdClass
|
||||
*/
|
||||
function addVote($poll_id, $name, $choices, $slots_hash, $mail) {
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
|
||||
|
||||
// Insert new vote
|
||||
$choices = implode($choices);
|
||||
$token = $this->random(16);
|
||||
return $this->voteRepository->insert($poll_id, $name, $choices, $token, $mail);
|
||||
}
|
||||
|
||||
function addComment($poll_id, $name, $comment) {
|
||||
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->commentRepository->insert($poll_id, $name, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Form $form
|
||||
* @return array
|
||||
*/
|
||||
function createPoll(Form $form) {
|
||||
// Generate poll IDs, loop while poll ID already exists
|
||||
|
||||
$this->pollRepository->beginTransaction();
|
||||
try {
|
||||
if (empty($form->id)) { // User want us to generate an id for him
|
||||
do {
|
||||
$poll_id = $this->random(16);
|
||||
} while ($this->pollRepository->existsById($poll_id));
|
||||
$admin_poll_id = $poll_id . $this->random(8);
|
||||
} else { // User have choosen the poll id
|
||||
$poll_id = $form->id;
|
||||
do {
|
||||
$admin_poll_id = $this->random(24);
|
||||
} while ($this->pollRepository->existsByAdminId($admin_poll_id));
|
||||
}
|
||||
|
||||
// Insert poll + slots
|
||||
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
|
||||
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
|
||||
$this->pollRepository->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];
|
||||
} catch (DBALException $e) {
|
||||
$this->pollRepository->rollback();
|
||||
$this->logService->log('ERROR', "Poll couldn't be saved : " . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function findAllByAdminMail($mail) {
|
||||
return $this->pollRepository->findAllByAdminMail($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $votes
|
||||
* @param \stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
public function computeBestChoices($votes, $poll) {
|
||||
if (0 === count($votes)) {
|
||||
return $this->computeEmptyBestChoices($poll);
|
||||
}
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
|
||||
// if there are votes
|
||||
foreach ($votes as $vote) {
|
||||
$choices = str_split($vote->choices);
|
||||
foreach ($choices as $i => $choice) {
|
||||
if (!isset($result['y'][$i])) {
|
||||
$result['inb'][$i] = 0;
|
||||
$result['y'][$i] = 0;
|
||||
}
|
||||
if ($choice === "1") {
|
||||
$result['inb'][$i]++;
|
||||
}
|
||||
if ($choice === "2") {
|
||||
$result['y'][$i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function splitSlots($slots) {
|
||||
$splitted = [];
|
||||
foreach ($slots as $slot) {
|
||||
$obj = new \stdClass();
|
||||
$obj->day = $slot->title;
|
||||
$obj->moments = explode(',', $slot->moments);
|
||||
|
||||
$splitted[] = $obj;
|
||||
}
|
||||
|
||||
return $splitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $slots array The slots to hash
|
||||
* @return string The hash
|
||||
*/
|
||||
public function hashSlots($slots) {
|
||||
return md5(array_reduce($slots, function($carry, $item) {
|
||||
return $carry . $item->id . '@' . $item->moments . ';';
|
||||
}));
|
||||
}
|
||||
|
||||
function splitVotes($votes) {
|
||||
$splitted = [];
|
||||
foreach ($votes as $vote) {
|
||||
$obj = new \stdClass();
|
||||
$obj->id = $vote->id;
|
||||
$obj->name = $vote->name;
|
||||
$obj->uniqId = $vote->uniqId;
|
||||
$obj->choices = str_split($vote->choices);
|
||||
$obj->mail = $vote->mail;
|
||||
|
||||
$splitted[] = $obj;
|
||||
}
|
||||
|
||||
return $splitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The max timestamp allowed for expiry date
|
||||
*/
|
||||
public function maxExpiryDate() {
|
||||
global $config;
|
||||
return time() + (86400 * $config['default_poll_duration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The min timestamp allowed for expiry date
|
||||
*/
|
||||
public function minExpiryDate() {
|
||||
return time() + 86400;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function sortSlorts(&$slots) {
|
||||
uasort($slots, function ($a, $b) {
|
||||
return $a->title > $b->title;
|
||||
});
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
private function computeEmptyBestChoices($poll)
|
||||
{
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
// if there is no votes, calculates the number of slot
|
||||
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
|
||||
if ($poll->format === 'A') {
|
||||
// poll format classic
|
||||
|
||||
for ($i = 0; $i < count($slots); $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
} else {
|
||||
// poll format date
|
||||
|
||||
$slots = $this->splitSlots($slots);
|
||||
|
||||
foreach ($slots as $slot) {
|
||||
for ($i = 0; $i < count($slot->moments); $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function random($length) {
|
||||
return Token::getToken($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $choices
|
||||
* @param $poll_id
|
||||
* @param $slots_hash
|
||||
* @param $name
|
||||
* @param string $vote_id
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentVoteException
|
||||
* @throws ConcurrentEditionException
|
||||
*/
|
||||
private function checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id = FALSE) {
|
||||
// Check if vote already exists with the same name
|
||||
if (FALSE === $vote_id) {
|
||||
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
|
||||
} else {
|
||||
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
throw new AlreadyExistsException();
|
||||
}
|
||||
|
||||
$poll = $this->findById($poll_id);
|
||||
|
||||
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint
|
||||
$this->checkMaxVotes($choices, $poll, $poll_id);
|
||||
|
||||
// Check if slots are still the same
|
||||
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the hash send by the user is the same as the computed hash.
|
||||
*
|
||||
* @param $poll /stdClass The poll
|
||||
* @param $slots_hash string The hash sent by the user
|
||||
* @throws ConcurrentEditionException Thrown when hashes are differents
|
||||
*/
|
||||
private function checkThatSlotsDidntChanged($poll, $slots_hash) {
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
if ($slots_hash !== $this->hashSlots($slots)) {
|
||||
throw new ConcurrentEditionException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the votes doesn't conflicts the maximum votes constraint
|
||||
*
|
||||
* @param $user_choice
|
||||
* @param \stdClass $poll
|
||||
* @param string $poll_id
|
||||
* @throws ConcurrentVoteException
|
||||
*/
|
||||
private function checkMaxVotes($user_choice, $poll, $poll_id) {
|
||||
$votes = $this->allVotesByPollId($poll_id);
|
||||
if (count($votes) <= 0) {
|
||||
return;
|
||||
}
|
||||
$best_choices = $this->computeBestChoices($votes, $poll);
|
||||
foreach ($best_choices['y'] as $i => $nb_choice) {
|
||||
// if for this option we have reached maximum value and user wants to add itself too
|
||||
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
|
||||
throw new ConcurrentVoteException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
/**
|
||||
* This service helps to purge data.
|
||||
*
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class PurgeService {
|
||||
private $logService;
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(Connection $connect, LogService $logService) {
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
$this->voteRepository = RepositoryFactory::voteRepository();
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
public function repeatedCleanings() {
|
||||
$this->purgeOldPolls();
|
||||
|
||||
if (0 === time() % 10) {
|
||||
$this->cleanDemoPoll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode purges all old polls (the ones with end_date in past).
|
||||
*
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
public function purgeOldPolls() {
|
||||
try {
|
||||
$oldPolls = $this->pollRepository->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;
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('ERROR', $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function cleanDemoPoll() {
|
||||
if (!defined("DEMO_POLL_ID") || !defined("DEMO_POLL_NUMBER_VOTES")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->voteRepository->beginTransaction();
|
||||
|
||||
$demoVotes = $this->voteRepository->allUserVotesByPollId(DEMO_POLL_ID);
|
||||
$votesToDelete = count($demoVotes) - DEMO_POLL_NUMBER_VOTES;
|
||||
|
||||
if ($votesToDelete > 0) {
|
||||
$this->voteRepository->deleteOldVotesByPollId(DEMO_POLL_ID, $votesToDelete);
|
||||
}
|
||||
|
||||
$this->voteRepository->commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode delete all data about a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
private function purgePollById($poll_id) {
|
||||
$done = true;
|
||||
|
||||
try {
|
||||
$this->pollRepository->beginTransaction();
|
||||
$done &= $this->commentRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->voteRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->slotRepository->deleteByPollId($poll_id);
|
||||
$done &= $this->pollRepository->deleteById($poll_id);
|
||||
|
||||
if ($done) {
|
||||
$this->pollRepository->commit();
|
||||
} else {
|
||||
$this->pollRepository->rollback();
|
||||
}
|
||||
} catch (DBALException $e) {
|
||||
$this->logService->log('ERROR', $e->getMessage());
|
||||
}
|
||||
|
||||
return $done;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Security\PasswordHasher;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the current session allows to access given poll.
|
||||
*
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @return bool true if the current session can access this poll
|
||||
*/
|
||||
public function canAccessPoll($poll) {
|
||||
if (is_null($poll->password_hash)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
|
||||
$currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null;
|
||||
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
|
||||
return true;
|
||||
}
|
||||
unset($_SESSION['poll_security'][$poll->id]);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit to the session a poll password
|
||||
*
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @param $password string the password to compare
|
||||
*/
|
||||
public function submitPollAccess($poll, $password) {
|
||||
if (!empty($password)) {
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
$_SESSION['poll_security'][$poll->id] = $password;
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureSessionPollSecurityIsCreated() {
|
||||
if (!isset($_SESSION['poll_security'])) {
|
||||
$_SESSION['poll_security'] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
class SessionService {
|
||||
/**
|
||||
* Get value of $key in $section, or $defaultValue
|
||||
*
|
||||
* @param $section
|
||||
* @param $key
|
||||
* @param null $defaultValue
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($section, $key, $defaultValue=null) {
|
||||
assert(!empty($key));
|
||||
assert(!empty($section));
|
||||
|
||||
$this->initSectionIfNeeded($section);
|
||||
|
||||
$returnValue = $defaultValue;
|
||||
if (isset($_SESSION[$section][$key])) {
|
||||
$returnValue = $_SESSION[$section][$key];
|
||||
}
|
||||
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a $value for $key in $section
|
||||
*
|
||||
* @param $section
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function set($section, $key, $value) {
|
||||
assert(!empty($key));
|
||||
assert(!empty($section));
|
||||
|
||||
$this->initSectionIfNeeded($section);
|
||||
|
||||
$_SESSION[$section][$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session value
|
||||
*
|
||||
* @param $section
|
||||
* @param $key
|
||||
*/
|
||||
public function remove($section, $key) {
|
||||
assert(!empty($key));
|
||||
assert(!empty($section));
|
||||
|
||||
unset($_SESSION[$section][$key]);
|
||||
}
|
||||
|
||||
private function initSectionIfNeeded($section) {
|
||||
if (!isset($_SESSION[$section])) {
|
||||
$_SESSION[$section] = [];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
/**
|
||||
* The class provides action for application administrators.
|
||||
*
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class SuperAdminService {
|
||||
private $pollRepository;
|
||||
|
||||
function __construct() {
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all polls.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
|
||||
* @param int $page The page index (O = first page)
|
||||
* @param int $limit The limit size
|
||||
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
|
||||
*/
|
||||
public function findAllPolls($search, $page, $limit) {
|
||||
$start = $page * $limit;
|
||||
$polls = $this->pollRepository->findAll($search, $start, $limit);
|
||||
$count = $this->pollRepository->count($search);
|
||||
$total = $this->pollRepository->count();
|
||||
|
||||
return ['polls' => $polls, 'count' => $count, 'total' => $total];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
<?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/OpenSondage: 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 Parsedown;
|
||||
|
||||
class Utils {
|
||||
/**
|
||||
* @return string Server name
|
||||
*/
|
||||
public static function get_server_name() {
|
||||
$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
$serverPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '';
|
||||
|
||||
$scheme = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) ? 'https' : 'http';
|
||||
$port = in_array($serverPort, ['80', '443'], true) ? '' : ':' . $serverPort;
|
||||
$dirname = dirname($_SERVER['SCRIPT_NAME']);
|
||||
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
|
||||
$dirname = str_replace('/admin', '', $dirname);
|
||||
$dirname = str_replace('/action', '', $dirname);
|
||||
$server_name = (defined('APP_URL') ? APP_URL : $serverName) . $port . $dirname;
|
||||
|
||||
return $scheme . '://' . preg_replace('#//+#', '/', $server_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
* @deprecated
|
||||
*/
|
||||
public static function print_header($title = '') {
|
||||
global $locale;
|
||||
|
||||
echo '<!DOCTYPE html>
|
||||
<html lang="' . $locale . '">
|
||||
<head>
|
||||
<meta charset="utf-8" />';
|
||||
|
||||
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" />
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/jquery-1.12.4.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>';
|
||||
if ('en' !== $locale) {
|
||||
echo '
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
|
||||
}
|
||||
echo '
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/core.js"></script>';
|
||||
if (is_file($_SERVER['DOCUMENT_ROOT'] . "/nav/nav.js")) {
|
||||
echo '<script src="/nav/nav.js" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->';
|
||||
}
|
||||
|
||||
echo '
|
||||
</head>
|
||||
<body>
|
||||
<div class="container ombre">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Function allowing to generate poll's url
|
||||
* @param string $id The poll's id
|
||||
* @param bool $admin True to generate an admin URL, false for a public one
|
||||
* @param string $vote_id (optional) The vote's unique id
|
||||
* @param null $action
|
||||
* @param null $action_value
|
||||
* @return string The poll's URL.
|
||||
*/
|
||||
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) {
|
||||
// URL-Encode $action_value
|
||||
$action_value = $action_value ? Utils::base64url_encode($action_value) : null;
|
||||
|
||||
if (URL_PROPRE) {
|
||||
if ($admin === true) {
|
||||
$url = self::get_server_name() . $id . '/admin';
|
||||
} else {
|
||||
$url = self::get_server_name() . $id;
|
||||
}
|
||||
if ($vote_id !== '') {
|
||||
$url .= '/vote/' . $vote_id . "#edit";
|
||||
} elseif ($action) {
|
||||
if ($action_value) {
|
||||
$url .= '/action/' . $action . '/' . $action_value;
|
||||
} else {
|
||||
$url .= '/action/' . $action;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($admin === true) {
|
||||
$url = self::get_server_name() . 'adminstuds.php?poll=' . $id;
|
||||
} else {
|
||||
$url = self::get_server_name() . 'studs.php?poll=' . $id;
|
||||
}
|
||||
if ($vote_id !== '') {
|
||||
$url .= '&vote=' . $vote_id . "#edit";
|
||||
} elseif ($action) {
|
||||
if ($action_value) {
|
||||
$url .= '&' . $action . "=" . $action_value;
|
||||
} else {
|
||||
$url .= '&' . $action . "=";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 table($tableName) {
|
||||
return TABLENAME_PREFIX . $tableName;
|
||||
}
|
||||
|
||||
public static function markdown($md, $clear=false, $line=true) {
|
||||
$parseDown = new Parsedown();
|
||||
|
||||
$parseDown
|
||||
->setBreaksEnabled(true)
|
||||
->setSafeMode(true)
|
||||
;
|
||||
|
||||
if ($line) {
|
||||
$html = $parseDown->line($md);
|
||||
} else {
|
||||
$md = preg_replace_callback(
|
||||
'#( ){2,}#',
|
||||
function ($m) {
|
||||
return str_repeat(' ', strlen($m[0]));
|
||||
},
|
||||
$md
|
||||
);
|
||||
$html = $parseDown->text($md);
|
||||
}
|
||||
|
||||
$text = strip_tags($html);
|
||||
|
||||
return $clear ? $text : $html;
|
||||
}
|
||||
|
||||
public static function htmlEscape($html) {
|
||||
return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
|
||||
}
|
||||
|
||||
public static function htmlMailEscape($html) {
|
||||
return htmlspecialchars($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);
|
||||
$escaped = preg_replace("/^(=|\+|\-|\@)/", "'$1", $escaped);
|
||||
|
||||
return '"' . $escaped . '"';
|
||||
}
|
||||
|
||||
public static function cleanFilename($title) {
|
||||
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
|
||||
$cleaned = preg_replace(' {2,}', ' ', $cleaned);
|
||||
|
||||
return $cleaned;
|
||||
}
|
||||
|
||||
public static function fromPostOrDefault($postKey, $default = '') {
|
||||
return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default;
|
||||
}
|
||||
|
||||
public static function base64url_encode($input) {
|
||||
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
public static function base64url_decode($input) {
|
||||
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
<?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/OpenSondage: 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)
|
||||
*/
|
||||
|
||||
// Fully qualified domain name of your webserver.
|
||||
// If this is unset or empty, the servername is determined automatically.
|
||||
// You *have to set this* if you are running Framadate behind a reverse proxy.
|
||||
const APP_URL = 'localhost';
|
||||
|
||||
// Application name
|
||||
const NOMAPPLICATION = 'Framadate';
|
||||
|
||||
// Database administrator email
|
||||
const ADRESSEMAILADMIN = 'mon@email.fr';
|
||||
|
||||
// Email for automatic responses (you should set it to "no-reply")
|
||||
const ADRESSEMAILREPONSEAUTO = '';
|
||||
|
||||
// Database driver
|
||||
const DB_DRIVER = 'pdo_mysql';
|
||||
|
||||
// Database name
|
||||
const DB_NAME = 'framadate';
|
||||
|
||||
// Database host
|
||||
const DB_HOST = 'db';
|
||||
|
||||
// Database port
|
||||
const DB_PORT = '3307';
|
||||
|
||||
// Database user
|
||||
const DB_USER = 'framadate';
|
||||
|
||||
// Database password
|
||||
const DB_PASSWORD = 'framadatedbpassword';
|
||||
|
||||
// Table name prefix
|
||||
const TABLENAME_PREFIX = 'fd_';
|
||||
|
||||
// Name of the table that stores migration script already executed
|
||||
const MIGRATION_TABLE = 'framadate_migration';
|
||||
|
||||
// Default Language
|
||||
const DEFAULT_LANGUAGE = 'fr';
|
||||
|
||||
// List of supported languages, fake constant as arrays can be used as constants only in PHP >=5.6
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'oc' => 'Occitan',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'nl' => 'Dutch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
'ca' => 'Catalan',
|
||||
];
|
||||
|
||||
// Path to image file with the title
|
||||
const IMAGE_TITRE = 'images/logo-framadate.png';
|
||||
|
||||
// Clean URLs, boolean
|
||||
const URL_PROPRE = true;
|
||||
|
||||
// Use REMOTE_USER data provided by web server
|
||||
const USE_REMOTE_USER = true;
|
||||
|
||||
// Path to the log file
|
||||
const LOG_FILE = 'admin/stdout.log';
|
||||
|
||||
// Days (after expiration date) before purging a poll
|
||||
const PURGE_DELAY = 60;
|
||||
|
||||
// Max slots per poll
|
||||
const MAX_SLOTS_PER_POLL = 366;
|
||||
|
||||
// Number of seconds before we allow to resend an "Remember Edit Link" email.
|
||||
const TIME_EDIT_LINK_EMAIL = 60;
|
||||
|
||||
// uncomment to display a link to the demo poll at the home page
|
||||
//const DEMO_POLL_ID = "aqg259dth55iuhwm";
|
||||
|
||||
// number of recent votes that are not deleted
|
||||
const DEMO_POLL_NUMBER_VOTES = 10;
|
||||
|
||||
|
||||
// Config
|
||||
$config = [
|
||||
/* general config */
|
||||
'use_smtp' => false, // use email for polls creation/modification/responses notification (uses smtp only if `use_sendmail` is disabled)
|
||||
'use_sendmail' => false, // use sendmail instead of smtp
|
||||
'smtp_options' => [
|
||||
'host' => 'localhost', // SMTP server (you could add many servers (main and backup for example) : use ";" like separator
|
||||
'auth' => false, // Enable SMTP authentication
|
||||
'username' => '', // SMTP username
|
||||
'password' => '', // SMTP password
|
||||
'secure' => '', // Enable encryption (false, tls or ssl)
|
||||
'port' => 25, // TCP port to connect to
|
||||
],
|
||||
/* home */
|
||||
'show_what_is_that' => true, // display "how to use" section
|
||||
'show_the_software' => true, // display technical information about the software
|
||||
'show_cultivate_your_garden' => true, // display "development and administration" information
|
||||
/* create_classic_poll.php / create_date_poll.php */
|
||||
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
|
||||
/* create_classic_poll.php */
|
||||
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
|
||||
'markdown_editor_by_default' => true, // The markdown editor for the description is enabled by default
|
||||
'provide_fork_awesome' => true, // Whether the build-in fork-awesome should be provided
|
||||
];
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
<?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/OpenSondage: 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)
|
||||
*/
|
||||
|
||||
// Fully qualified domain name of your webserver.
|
||||
// If this is unset or empty, the servername is determined automatically.
|
||||
// You *have to set this* if you are running Framadate behind a reverse proxy.
|
||||
// const APP_URL = '<www.mydomain.fr>';
|
||||
|
||||
// Application name
|
||||
const NOMAPPLICATION = 'Framadate';
|
||||
|
||||
// Database administrator email
|
||||
const ADRESSEMAILADMIN = 'admin@app.tld';
|
||||
|
||||
// Email for automatic responses (you should set it to "no-reply")
|
||||
const ADRESSEMAILREPONSEAUTO = 'no@reply';
|
||||
|
||||
// Database driver
|
||||
const DB_DRIVER = 'pdo_sqlite';
|
||||
|
||||
// Database name
|
||||
const DB_NAME = 'framadate';
|
||||
|
||||
// Database host
|
||||
const DB_HOST = '';
|
||||
|
||||
// Database port
|
||||
const DB_PORT = '';
|
||||
|
||||
// Database user
|
||||
const DB_USER = '';
|
||||
|
||||
// Database password
|
||||
const DB_PASSWORD = '';
|
||||
|
||||
// Table name prefix
|
||||
const TABLENAME_PREFIX = 'fd_';
|
||||
|
||||
// Name of the table that stores migration script already executed
|
||||
const MIGRATION_TABLE = 'framadate_migration';
|
||||
|
||||
// Default Language
|
||||
const DEFAULT_LANGUAGE = 'fr';
|
||||
|
||||
// List of supported languages, fake constant as arrays can be used as constants only in PHP >=5.6
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'oc' => 'Occitan',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'nl' => 'Dutch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
];
|
||||
|
||||
// Path to image file with the title
|
||||
const IMAGE_TITRE = 'images/logo-framadate.png';
|
||||
|
||||
// Clean URLs, boolean
|
||||
const URL_PROPRE = false;
|
||||
|
||||
// Use REMOTE_USER data provided by web server
|
||||
const USE_REMOTE_USER = true;
|
||||
|
||||
// Path to the log file
|
||||
const LOG_FILE = 'admin/stdout.log';
|
||||
|
||||
// Days (after expiration date) before purging a poll
|
||||
const PURGE_DELAY = 60;
|
||||
|
||||
// Max slots per poll
|
||||
const MAX_SLOTS_PER_POLL = 366;
|
||||
|
||||
// Number of seconds before we allow to resend an "Remember Edit Link" email.
|
||||
const TIME_EDIT_LINK_EMAIL = 60;
|
||||
|
||||
// Config
|
||||
$config = [
|
||||
/* general config */
|
||||
'use_smtp' => false, // use email for polls creation/modification/responses notification
|
||||
'smtp_options' => [
|
||||
'host' => 'localhost', // SMTP server (you could add many servers (main and backup for example) : use ";" like separator
|
||||
'auth' => false, // Enable SMTP authentication
|
||||
'username' => '', // SMTP username
|
||||
'password' => '', // SMTP password
|
||||
'secure' => '', // Enable encryption (false, tls or ssl)
|
||||
'port' => 25, // TCP port to connect to
|
||||
],
|
||||
/* home */
|
||||
'show_what_is_that' => true, // display "how to use" section
|
||||
'show_the_software' => true, // display technical information about the software
|
||||
'show_cultivate_your_garden' => true, // display "development and administration" information
|
||||
/* create_classic_poll.php / create_date_poll.php */
|
||||
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
|
||||
/* create_classic_poll.php */
|
||||
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
|
||||
'markdown_editor_by_default' => true, // The markdown editor for the description is enabled by default
|
||||
'provide_fork_awesome' => true, // Whether the build-in fork-awesome should be provided
|
||||
];
|
|
@ -0,0 +1,45 @@
|
|||
<?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/OpenSondage: 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 = '1.2.0';
|
||||
|
||||
// PHP Needed version
|
||||
const PHP_NEEDED_VERSION = '5.6';
|
||||
|
||||
// Config constants
|
||||
const COMPILE_DIR = '/tpl_c/';
|
||||
|
||||
// Regex
|
||||
const POLL_REGEX = '/^[a-z0-9-]*$/i';
|
||||
const ADMIN_POLL_REGEX = '/^[a-z0-9]{24}$/i';
|
||||
const CHOICE_REGEX = '/^[ 012]$/';
|
||||
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i';
|
||||
const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i';
|
||||
const EDITABLE_CHOICE_REGEX = '/^[0-2]$/';
|
||||
const COLLECT_MAIL_CHOICE_REGEX = '/^[0-3]$/';
|
||||
const BASE64_REGEX = '/^[A-Za-z0-9]+$/';
|
||||
const MD5_REGEX = '/^[A-Fa-f0-9]{32}$/';
|
||||
|
||||
// Session constants
|
||||
const SESSION_EDIT_LINK_TOKEN = 'EditLinkToken';
|
||||
const SESSION_EDIT_LINK_TIME = "EditLinkMail";
|
||||
|
||||
// CSRF (300s = 5min)
|
||||
const TOKEN_TIME = 300;
|
|
@ -0,0 +1,45 @@
|
|||
<?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/OpenSondage: 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)
|
||||
*/
|
||||
|
||||
// Prepare I18N instance
|
||||
$i18n = \o80\i18n\I18N::instance();
|
||||
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
|
||||
$i18n->setPath(__DIR__ . '/../../locale');
|
||||
|
||||
// Change langauge when user asked for it
|
||||
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES), true)) {
|
||||
$_SESSION['lang'] = $_POST['lang'];
|
||||
}
|
||||
|
||||
/* <html lang="$locale"> */
|
||||
$i18n->get('', 'Something, just to load the dictionary');
|
||||
$locale = str_replace('_', '-', $i18n->getLoadedLang());
|
||||
|
||||
/* Date Format */
|
||||
$date_format['txt_full'] = __('Date', '%A, %B %e, %Y'); //summary in create_date_poll.php and removal date in choix_(date|autre).php
|
||||
$date_format['txt_short'] = __('Date', '%A %e %B %Y'); // radio title
|
||||
$date_format['txt_day'] = __('Date', '%a %e');
|
||||
$date_format['txt_date'] = __('Date', '%Y-%m-%d');
|
||||
$date_format['txt_month_year'] = __('Date', '%B %Y');
|
||||
$date_format['txt_datetime_short'] = __('Date', '%m/%d/%Y %H:%M');
|
||||
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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
<?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/OpenSondage: 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 Doctrine\DBAL\Configuration;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Services\LogService;
|
||||
|
||||
// Autoloading of dependencies with Composer
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../vendor/o80/i18n/src/shortcuts.php';
|
||||
|
||||
if (session_id() === '') {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (ini_get('date.timezone') === '') {
|
||||
date_default_timezone_set('Europe/Paris');
|
||||
}
|
||||
|
||||
define('ROOT_DIR', __DIR__ . '/../../');
|
||||
|
||||
$path = '/app/inc/config.php';
|
||||
if (getenv('APP_ENV') === 'test') {
|
||||
$path = '/app/inc/config.test.php';
|
||||
}
|
||||
define('CONF_FILENAME', ROOT_DIR . $path);
|
||||
|
||||
require_once __DIR__ . '/constants.php';
|
||||
|
||||
if (is_file(CONF_FILENAME)) {
|
||||
@include_once CONF_FILENAME;
|
||||
|
||||
// Connection to database
|
||||
$doctrineConfig = new Configuration();
|
||||
$connectionParams = [
|
||||
'dbname' => DB_NAME,
|
||||
'user' => DB_USER,
|
||||
'password' => DB_PASSWORD,
|
||||
'host' => DB_HOST,
|
||||
'driver' => DB_DRIVER,
|
||||
'charset' => DB_DRIVER === 'pdo_mysql' ? 'utf8mb4' : 'utf8',
|
||||
];
|
||||
|
||||
if (DB_DRIVER === 'pdo_sqlite') {
|
||||
$connectionParams['path'] = 'test_database.sqlite';
|
||||
}
|
||||
|
||||
try {
|
||||
$connect = DriverManager::getConnection($connectionParams, $doctrineConfig);
|
||||
RepositoryFactory::init($connect);
|
||||
$err = 0;
|
||||
} catch (DBALException $e) {
|
||||
$logger = new LogService();
|
||||
$logger->log('ERROR', $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
define('NOMAPPLICATION', 'Framadate');
|
||||
define('DEFAULT_LANGUAGE', 'fr');
|
||||
define('IMAGE_TITRE', 'images/logo-framadate.png');
|
||||
define('LOG_FILE', 'admin/stdout.log');
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
];
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/i18n.php';
|
||||
// Smarty
|
||||
require_once __DIR__ . '/smarty.php';
|
|
@ -0,0 +1,122 @@
|
|||
<?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/OpenSondage: 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 . COMPILE_DIR);
|
||||
$smarty->setCacheDir(ROOT_DIR . '/cache/');
|
||||
$smarty->caching = false;
|
||||
|
||||
$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
|
||||
$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', strstr($serverName, 'framadate.org'));
|
||||
$smarty->assign('provide_fork_awesome', !isset($config['provide_fork_awesome']) || $config['provide_fork_awesome']);
|
||||
$smarty->assign('locale', $locale);
|
||||
$smarty->assign('langs', $ALLOWED_LANGUAGES);
|
||||
$smarty->assign('date_format', $date_format);
|
||||
if (isset($config['tracking_code'])) {
|
||||
$smarty->assign('tracking_code', $config['tracking_code']);
|
||||
}
|
||||
if (defined('FAVICON')) {
|
||||
$smarty->assign('favicon', FAVICON);
|
||||
}
|
||||
|
||||
// Dev Mode
|
||||
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE'] || php_sapi_name() === 'cli-server') {
|
||||
$smarty->force_compile = true;
|
||||
$smarty->compile_check = true;
|
||||
} else {
|
||||
$smarty->force_compile = false;
|
||||
$smarty->compile_check = false;
|
||||
}
|
||||
|
||||
function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
|
||||
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$admin = (isset($params['admin']) && $params['admin']) ? true : false;
|
||||
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : false;
|
||||
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? $params['action_value'] : false;
|
||||
$vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
|
||||
|
||||
// If filter_var fails (i.e.: hack tentative), it will return false. At least no leak is possible from this.
|
||||
|
||||
return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value);
|
||||
}
|
||||
|
||||
function smarty_modifier_markdown($md, $clear = false, $inline=true) {
|
||||
return Utils::markdown($md, $clear, $inline);
|
||||
}
|
||||
|
||||
function smarty_modifier_resource($link) {
|
||||
return Utils::get_server_name() . $link;
|
||||
}
|
||||
function smarty_modifier_addslashes_single_quote($string) {
|
||||
return addcslashes($string, '\\\'');
|
||||
}
|
||||
|
||||
function smarty_modifier_html($html) {
|
||||
return Utils::htmlEscape($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* markdown_to_text
|
||||
* Retrieves a markdown string and tries to make a plain text value
|
||||
*
|
||||
* @param array $options
|
||||
* @return string
|
||||
*/
|
||||
function smarty_function_markdown_to_text($options, Smarty_Internal_Template $template)
|
||||
{
|
||||
$locale = \o80\i18n\I18N::instance()->getLoadedLang();
|
||||
$text = strip_tags(Parsedown::instance()->text($options['markdown']));
|
||||
$number_letters = (new NumberFormatter($locale, NumberFormatter::ORDINAL))->format($options['id'] + 1);
|
||||
return $text !== '' ? $text : __f('Poll results', '%s option', $number_letters);
|
||||
}
|
||||
|
||||
function smarty_modifier_datepicker_path($lang) {
|
||||
$i = 0;
|
||||
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
|
||||
$lang_arr = explode('-', $lang);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
$lang = $lang_arr[0];
|
||||
} else {
|
||||
$lang = 'en';
|
||||
}
|
||||
$i += 1;
|
||||
}
|
||||
return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
||||
|
||||
function smarty_modifier_locale_2_lang($locale) {
|
||||
$lang_arr = explode('-', $locale);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
return $lang_arr[0];
|
||||
}
|
||||
return $locale;
|
||||
}
|
||||
|
||||
function path_for_datepicker_locale($lang) {
|
||||
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
namespace Framadate;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
abstract class FramaTestCase extends TestCase {
|
||||
protected function getTestResourcePath($resourcepath) {
|
||||
return __DIR__ . '/../resources/' . $resourcepath;
|
||||
}
|
||||
|
||||
protected function readTestResource($resourcepath) {
|
||||
return file_get_contents($this->getTestResourcePath($resourcepath));
|
||||
}
|
||||
|
||||
protected function invoke(&$object, $methodName) {
|
||||
$reflectionClass = new \ReflectionClass($object);
|
||||
$reflectionMethod = $reflectionClass->getMethod($methodName);
|
||||
$reflectionMethod->setAccessible(true);
|
||||
|
||||
$params = array_slice(func_get_args(), 2); // get all the parameters after $methodName
|
||||
return $reflectionMethod->invokeArgs($object, $params);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\FramaTestCase;
|
||||
|
||||
class InputServiceUnitTest extends FramaTestCase
|
||||
{
|
||||
public function liste_emails()
|
||||
{
|
||||
return [
|
||||
// valids addresses
|
||||
"valid address" => ["example@example.com", "example@example.com"],
|
||||
"local address" => ["test@localhost", "test@localhost"],
|
||||
"IP address" => ["ip.email@127.0.0.1", "ip.email@127.0.0.1"],
|
||||
"with spaces arround" => [" with@spaces ", "with@spaces"],
|
||||
"unicode caracters" => ["unicode.éà@idn-œ.com", "unicode.éà@idn-œ.com"],
|
||||
// invalids addresses
|
||||
"without domain" => ["without-domain", FALSE],
|
||||
"space inside" => ["example example@example.com", FALSE],
|
||||
"forbidden chars" => ["special_chars.@example.com", FALSE],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider liste_emails
|
||||
*/
|
||||
public function test_filterMail($email, $expected)
|
||||
{
|
||||
$inputService = new InputService();
|
||||
$filtered = $inputService->filterMail($email);
|
||||
|
||||
$this->assertSame($expected, $filtered);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\FramaTestCase;
|
||||
|
||||
class MailServiceUnitTest extends FramaTestCase {
|
||||
const MSG_KEY = '666';
|
||||
|
||||
public function test_should_send_a_2nd_mail_after_a_good_interval() {
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
|
||||
|
||||
// When
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertSame(true, $canSendMsg);
|
||||
}
|
||||
|
||||
public function test_should_not_send_2_mails_in_a_short_interval() {
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
|
||||
|
||||
// When
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertSame(false, $canSendMsg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
$loader = require __DIR__ . '/../../vendor/autoload.php';
|
||||
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');
|
|
@ -0,0 +1,72 @@
|
|||
<?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/OpenSondage: 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;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
// bandeaux de titre
|
||||
function bandeau_titre($titre)
|
||||
{
|
||||
global $ALLOWED_LANGUAGES;
|
||||
$img = ( IMAGE_TITRE ) ? '<img src="' . Utils::get_server_name() . IMAGE_TITRE . '" alt="' . NOMAPPLICATION . '" class="img-responsive">' : '';
|
||||
echo '
|
||||
<header role="banner">';
|
||||
if(count($ALLOWED_LANGUAGES) > 1){
|
||||
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="' . __('Language selector', 'Select language') . '" >' . liste_lang() . '</select>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-default btn-sm" title="' . __('Language selector', 'Change language') . '">OK</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>';
|
||||
}
|
||||
echo '
|
||||
<h1><a href="' . Utils::get_server_name() . '" title="' . __('Generic', 'Home') . ' - ' . NOMAPPLICATION . '">' . $img . '</a></h1>
|
||||
<h2 class="lead"><i>' . $titre . '</i></h2>
|
||||
<hr class="trait" role="presentation" />
|
||||
</header>
|
||||
<main role="main">';
|
||||
}
|
||||
|
||||
function liste_lang()
|
||||
{
|
||||
global $ALLOWED_LANGUAGES; global $locale;
|
||||
|
||||
$str = '';
|
||||
|
||||
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
|
||||
if (substr($k,0,2)===$locale) {
|
||||
$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" ;
|
||||
}
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function bandeau_pied()
|
||||
{
|
||||
echo '
|
||||
</main>
|
||||
</div> <!-- .container -->
|
||||
</body>
|
||||
</html>' . "\n";
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\Migrations\Configuration\Configuration;
|
||||
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
|
||||
use Framadate\Utils;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\ConsoleOutput;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
try {
|
||||
require_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
$input = new ArgvInput();
|
||||
$output = new ConsoleOutput();
|
||||
$style = new SymfonyStyle($input, $output);
|
||||
|
||||
if ($connect === null) {
|
||||
throw new \Exception("Undefined database connection\n");
|
||||
}
|
||||
|
||||
// replace the ConsoleRunner::run() statement with:
|
||||
$cli = new Application('Doctrine Command Line Interface', VERSION);
|
||||
$cli->setCatchExceptions(true);
|
||||
|
||||
$helperSet = new HelperSet(
|
||||
[
|
||||
'db' => new ConnectionHelper($connect),
|
||||
'question' => new QuestionHelper(),
|
||||
]
|
||||
);
|
||||
|
||||
$cli->setHelperSet($helperSet);
|
||||
|
||||
$migrateCommand = new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand();
|
||||
$statusCommand = new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand();
|
||||
|
||||
$migrationsDirectory = __DIR__ . '/../app/classes/Framadate/Migrations';
|
||||
|
||||
$configuration = new Configuration($connect);
|
||||
$configuration->setMigrationsTableName(Utils::table(MIGRATION_TABLE) . '_new');
|
||||
$configuration->setMigrationsDirectory($migrationsDirectory);
|
||||
$configuration->setMigrationsNamespace('DoctrineMigrations');
|
||||
$configuration->registerMigrationsFromDirectory($migrationsDirectory);
|
||||
$migrateCommand->setMigrationConfiguration($configuration);
|
||||
$statusCommand->setMigrationConfiguration($configuration);
|
||||
|
||||
// Register All Doctrine Commands
|
||||
$cli->addCommands([$migrateCommand, $statusCommand]);
|
||||
|
||||
// Runs console application
|
||||
$cli->run($input, $output);
|
||||
} catch (\Exception $e) {
|
||||
$style->error($e->getMessage());
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body><pre><?php
|
||||
|
||||
$goodLang = $_GET['good'];
|
||||
$otherLang = $_GET['other'];
|
||||
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
|
||||
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true);
|
||||
|
||||
foreach ($good as $sectionName => $section) {
|
||||
foreach ($section as $key => $value) {
|
||||
$good[$sectionName][$key] = getFromOther($other, $key, $value, $otherLang);
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($good, JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
|
||||
|
||||
function getFromOther($other, $goodKey, $default, $otherLang) {
|
||||
foreach ($other as $sectionName => $section) {
|
||||
foreach ($section as $key => $value) {
|
||||
if (
|
||||
strtolower($key) === strtolower($goodKey) ||
|
||||
strtolower(trim($key)) === strtolower($goodKey) ||
|
||||
strtolower(substr($key, 0, strlen($key) - 1)) === strtolower($goodKey) ||
|
||||
strtolower(trim(substr(trim($key), 0, strlen($key) - 1))) === strtolower($goodKey)
|
||||
) {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo '[-]' . $goodKey . "\n";
|
||||
|
||||
return strtoupper($otherLang) . '_' . $default;
|
||||
}
|
||||
|
||||
?>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
?>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body><pre><?php
|
||||
|
||||
$goodLang = $_GET['good'];
|
||||
$testLang = $_GET['test'];
|
||||
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
|
||||
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true);
|
||||
|
||||
$diffSection = false;
|
||||
|
||||
foreach ($good as $sectionName => $section) {
|
||||
if (!isset($test[$sectionName])) {
|
||||
echo '- section: ' . $sectionName . "\n";
|
||||
$diffSection = true;
|
||||
}
|
||||
}
|
||||
foreach ($test as $sectionName => $section) {
|
||||
if (!isset($good[$sectionName])) {
|
||||
echo '+ section: ' . $sectionName . "\n";
|
||||
$diffSection = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$diffSection and array_keys($good)!==array_keys($test)) {
|
||||
var_dump(array_keys($good));
|
||||
var_dump(array_keys($test));
|
||||
} else {
|
||||
echo 'All sections are in two langs.' . "\n";
|
||||
}
|
||||
|
||||
$diff = [];
|
||||
|
||||
foreach ($good as $sectionName => $section) {
|
||||
$diffSection = false;
|
||||
foreach($section as $key=>$value) {
|
||||
if (!isset($test[$sectionName][$key])) {
|
||||
$diff[$sectionName]['-'][] = $key;
|
||||
$diffSection = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$diffSection and array_keys($good[$sectionName]) !== array_keys($test[$sectionName])) {
|
||||
$diff[$sectionName]['order_good'] = array_keys($good[$sectionName]);
|
||||
$diff[$sectionName]['order_test'] = array_keys($test[$sectionName]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($test as $sectionName => $section) {
|
||||
foreach($section as $key=>$value) {
|
||||
if (!isset($good[$sectionName][$key])) {
|
||||
$diff[$sectionName]['+'][] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($diff) > 0) {
|
||||
var_dump($diff);
|
||||
}
|
||||
?>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"name": "framasoft/framadate",
|
||||
"description": "Application to facilitate the schedule of events or classic polls",
|
||||
"homepage": "https://framadate.org/",
|
||||
"keywords": ["poll", "framadate"],
|
||||
"version": "0.9.0",
|
||||
"license": "CECILL-B",
|
||||
|
||||
"type": "project",
|
||||
"support": {
|
||||
"issues": "https://framagit.org/framasoft/framadate/issues"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas CITHAREL",
|
||||
"email": "tcit@tcit.fr",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "JosephK",
|
||||
"email": "joseph@framasoft.org",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Olivier PEREZ",
|
||||
"email": "olivier@olivierperez.fr",
|
||||
"role": "Former maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Antonin MURTIN",
|
||||
"email": "antonin.murtin@gmail.com",
|
||||
"role": "Former developper"
|
||||
},
|
||||
{
|
||||
"name": "Simon LEBLANC",
|
||||
"role": "Former developper",
|
||||
"email": "contact@leblanc-simon.eu"
|
||||
},
|
||||
{
|
||||
"name": "Pierre-Yves GOSSET",
|
||||
"role": "Former developper",
|
||||
"email": "pyg@framasoft.org"
|
||||
},
|
||||
{
|
||||
"name": "Guilhem BORGHESI",
|
||||
"role": "Studs developper",
|
||||
"email": "borghesi@unistra.fr"
|
||||
},
|
||||
{
|
||||
"name": "Raphaël DROZ",
|
||||
"role": "Studs developper",
|
||||
"email": "raphael.droz@gmail.com"
|
||||
}
|
||||
],
|
||||
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"ext-pdo": "*",
|
||||
"smarty/smarty": "^3.1",
|
||||
"o80/i18n": "dev-develop",
|
||||
"phpmailer/phpmailer": "~6.0",
|
||||
"ircmaxell/password-compat": "dev-master",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"egulias/email-validator": "~2.1",
|
||||
"doctrine/dbal": "^2.5",
|
||||
"doctrine/migrations": "^1.5",
|
||||
"sensiolabs/ansi-to-html": "^1.1"
|
||||
},
|
||||
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"friendsofphp/php-cs-fixer": "~2.0"
|
||||
},
|
||||
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Framadate\\": "app/classes/Framadate/"
|
||||
}
|
||||
},
|
||||
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,220 @@
|
|||
<?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/OpenSondage: 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\Choice;
|
||||
use Framadate\Form;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
/* Service */
|
||||
/*---------*/
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$sessionService = new SessionService();
|
||||
|
||||
if (is_file('bandeaux_local.php')) {
|
||||
include_once('bandeaux_local.php');
|
||||
} else {
|
||||
include_once('bandeaux.php');
|
||||
}
|
||||
|
||||
// Min/Max archive date
|
||||
$min_expiry_time = $pollService->minExpiryDate();
|
||||
$max_expiry_time = $pollService->maxExpiryDate();
|
||||
|
||||
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
|
||||
|
||||
if ($form === null || !($form instanceof Form)) {
|
||||
$smarty->assign('title', __('Error', 'Error!'));
|
||||
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// The poll format is AUTRE (other) if we are in this file
|
||||
if (!isset($form->format)) {
|
||||
$form->format = 'A';
|
||||
}
|
||||
|
||||
// The poll format is AUTRE (other)
|
||||
if ($form->format !== 'A') {
|
||||
$form->format = 'A';
|
||||
$form->clearChoices();
|
||||
}
|
||||
|
||||
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
|
||||
$step = 1;
|
||||
} elseif (isset($_POST['confirmation'])) {
|
||||
$step = 4;
|
||||
} elseif (empty($_POST['fin_sondage_autre']) ) {
|
||||
$step = 2;
|
||||
} else {
|
||||
$step = 3;
|
||||
}
|
||||
|
||||
switch ($step) {
|
||||
case 2: // Step 2/4 : Select choices of the poll
|
||||
$choices = $form->getChoices();
|
||||
$nb_choices = max( 5- count($choices), 0);
|
||||
while ($nb_choices-- > 0) {
|
||||
$c = new Choice('');
|
||||
$form->addChoice($c);
|
||||
}
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
// Display step 2
|
||||
$smarty->assign('title', __('Step 2 classic', 'Poll options (2 of 3)'));
|
||||
$smarty->assign('choices', $form->getChoices());
|
||||
$smarty->assign('allowMarkdown', $config['user_can_add_img_or_link']);
|
||||
$smarty->assign('error', null);
|
||||
|
||||
$smarty->display('create_classic_poll_step_2.tpl');
|
||||
exit;
|
||||
|
||||
case 3: // Step 3/4 : Confirm poll creation and choose a removal date
|
||||
// Handle Step2 submission
|
||||
if (!empty($_POST['choices'])) {
|
||||
// remove empty choices
|
||||
$_POST['choices'] = array_filter($_POST['choices'], function ($c) {
|
||||
return !empty($c);
|
||||
});
|
||||
|
||||
$form->clearChoices();
|
||||
|
||||
// store choices in $_SESSION
|
||||
foreach ($_POST['choices'] as $c) {
|
||||
$c = strip_tags($c);
|
||||
$choice = new Choice($c);
|
||||
$form->addChoice($choice);
|
||||
}
|
||||
}
|
||||
|
||||
// Expiry date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
|
||||
$form->end_date = $max_expiry_time;
|
||||
|
||||
// Summary
|
||||
$summary = '<ol>';
|
||||
foreach ($form->getChoices() as $i => $choice) {
|
||||
/** @var Choice $choice */
|
||||
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]) : __('Generic', '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)
|
||||
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] !== '') ? stripslashes($md_img[1][0]) : __('Generic', '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)
|
||||
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] !== '') ? stripslashes($md_a[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
$li_subject_html = '<a href="' . $md_a[2][0] . '">' . $li_subject_text . '</a>';
|
||||
} else { // text only
|
||||
$li_subject_text = stripslashes($choice->getName());
|
||||
$li_subject_html = $li_subject_text;
|
||||
}
|
||||
|
||||
$summary .= '<li>' . $li_subject_html . '</li>' . "\n";
|
||||
}
|
||||
$summary .= '</ol>';
|
||||
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); //textual date
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 of 3)'));
|
||||
$smarty->assign('summary', $summary);
|
||||
$smarty->assign('end_date_str', $end_date_str);
|
||||
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
|
||||
$smarty->assign('use_smtp', $config['use_smtp']);
|
||||
|
||||
$smarty->display('create_poll_step_3.tpl');
|
||||
exit;
|
||||
case 4: // Step 4 : Data prepare before insert in DB
|
||||
$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_expiry_time) {
|
||||
$form->end_date = $min_expiry_time;
|
||||
} elseif ($max_expiry_time < $time) {
|
||||
$form->end_date = $max_expiry_time;
|
||||
} else {
|
||||
$form->end_date = $time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($form->end_date)) {
|
||||
// By default, expiration date is 6 months after last day
|
||||
$form->end_date = $max_expiry_time;
|
||||
}
|
||||
|
||||
// Insert poll in database
|
||||
$ids = $pollService->createPoll($form);
|
||||
$poll_id = $ids[0];
|
||||
$admin_poll_id = $ids[1];
|
||||
|
||||
// Send confirmation by mail if enabled
|
||||
if ($config['use_smtp'] === true) {
|
||||
$message = __('Mail', "This is the message to forward to the poll participants.");
|
||||
$message .= '<br/><br/>';
|
||||
$message .= Utils::htmlMailEscape($form->admin_name) . ' ' . __('Mail', 'has just created a poll called') . ' : "' . Utils::htmlMailEscape($form->title) . '".<br/>';
|
||||
$message .= sprintf(__('Mail', 'Thank you for participating in the poll at the following link') . ' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($poll_id));
|
||||
|
||||
$message_admin = __('Mail', "This message should NOT be sent to the poll participants. You should keep it private. <br/><br/>You can modify your poll at the following link");
|
||||
$message_admin .= sprintf(' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($admin_poll_id, true));
|
||||
|
||||
if ($mailService->isValidEmail($form->admin_mail)) {
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Message for the author') . '] ' . __('Generic', 'Poll') . ': ' . $form->title, $message_admin);
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Participant link') . '] ' . __('Generic', 'Poll') . ': ' . $form->title, $message);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean Form data in $_SESSION
|
||||
unset($_SESSION['form']);
|
||||
|
||||
// Delete old polls
|
||||
$purgeService->purgeOldPolls();
|
||||
|
||||
// creation message
|
||||
$sessionService->set("Framadate", "messagePollCreated", TRUE);
|
||||
// Redirect to poll administration
|
||||
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
|
||||
exit;
|
||||
|
||||
case 1: // Step 1/4 : error if $_SESSION from info_sondage are not valid
|
||||
default:
|
||||
$smarty->assign('title', __('Error', 'Error!'));
|
||||
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
<?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/OpenSondage: Framasoft (https://github.com/framasoft https://framagit.org/framasoft/framadate/)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* 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 https://framagit.org/framasoft/framadate/)
|
||||
*/
|
||||
use Framadate\Choice;
|
||||
use Framadate\Form;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
/* Service */
|
||||
/*---------*/
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$inputService = new InputService();
|
||||
$sessionService = new SessionService();
|
||||
|
||||
if (is_readable('bandeaux_local.php')) {
|
||||
include_once('bandeaux_local.php');
|
||||
}
|
||||
|
||||
// Min/Max archive date
|
||||
$min_expiry_time = $pollService->minExpiryDate();
|
||||
$max_expiry_time = $pollService->maxExpiryDate();
|
||||
|
||||
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
|
||||
|
||||
if ($form === null || !($form instanceof Form)) {
|
||||
$smarty->assign('title', __('Error', 'Error!'));
|
||||
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// The poll format is DATE if we are in this file
|
||||
if (!isset($form->format)) {
|
||||
$form->format = 'D';
|
||||
}
|
||||
// If we come from another format, we need to clear choices
|
||||
if ($form->format !== 'D') {
|
||||
$form->format = 'D';
|
||||
$form->clearChoices();
|
||||
}
|
||||
|
||||
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
|
||||
$step = 1;
|
||||
} else if (!empty($_POST['confirmation'])) {
|
||||
$step = 4;
|
||||
} else if (empty($_POST['choixheures']) || isset($form->totalchoixjour)) {
|
||||
$step = 2;
|
||||
} else {
|
||||
$step = 3;
|
||||
}
|
||||
|
||||
switch ($step) {
|
||||
case 2:
|
||||
// Step 2/4 : Select dates of the poll
|
||||
|
||||
// Prefill form->choices
|
||||
foreach ($form->getChoices() as $c) {
|
||||
/** @var Choice $c */
|
||||
$count = 3 - count($c->getSlots());
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$c->addSlot('');
|
||||
}
|
||||
}
|
||||
|
||||
$count = 3 - count($form->getChoices());
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$c = new Choice('');
|
||||
$c->addSlot('');
|
||||
$c->addSlot('');
|
||||
$c->addSlot('');
|
||||
$form->addChoice($c);
|
||||
}
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
// Display step 2
|
||||
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 of 3)'));
|
||||
$smarty->assign('choices', $form->getChoices());
|
||||
$smarty->assign('error', null);
|
||||
|
||||
$smarty->display('create_date_poll_step_2.tpl');
|
||||
exit;
|
||||
|
||||
case 3:
|
||||
// Step 3/4 : Confirm poll creation
|
||||
|
||||
// Handle Step2 submission
|
||||
if (!empty($_POST['days'])) {
|
||||
// Remove empty dates
|
||||
$_POST['days'] = array_filter($_POST['days'], function ($d) {
|
||||
return !empty($d);
|
||||
});
|
||||
|
||||
// Check if there are at most MAX_SLOTS_PER_POLL slots
|
||||
if (count($_POST['days']) > MAX_SLOTS_PER_POLL) {
|
||||
// Display step 2
|
||||
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 of 3)'));
|
||||
$smarty->assign('choices', $form->getChoices());
|
||||
$smarty->assign('error', __f('Error', 'You can\'t select more than %d dates', MAX_SLOTS_PER_POLL));
|
||||
|
||||
$smarty->display('create_date_poll_step_2.tpl');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Clear previous choices
|
||||
$form->clearChoices();
|
||||
|
||||
// Reorder moments to deal with suppressed dates
|
||||
$moments = [];
|
||||
$i = 0;
|
||||
while(count($moments) < count($_POST['days'])) {
|
||||
if (!empty($_POST['horaires' . $i])) {
|
||||
$moments[] = $_POST['horaires' . $i];
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < count($_POST['days']); $i++) {
|
||||
$day = $_POST['days'][$i];
|
||||
|
||||
if (!empty($day)) {
|
||||
// Add choice to Form data
|
||||
$date = DateTime::createFromFormat(__('Date', 'Y-m-d'), $_POST['days'][$i])->setTime(0, 0, 0);
|
||||
$time = (string) $date->getTimestamp();
|
||||
$choice = new Choice($time);
|
||||
$form->addChoice($choice);
|
||||
|
||||
$schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT);
|
||||
for ($j = 0; $j < count($schedules); $j++) {
|
||||
if (!empty($schedules[$j])) {
|
||||
$choice->addSlot(strip_tags($schedules[$j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$form->sortChoices();
|
||||
}
|
||||
|
||||
// Display step 3
|
||||
$summary = '<ul>';
|
||||
$choices = $form->getChoices();
|
||||
foreach ($choices as $choice) {
|
||||
/** @var Choice $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>';
|
||||
}
|
||||
$summary .= '</ul>';
|
||||
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); // textual date
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 of 3)'));
|
||||
$smarty->assign('summary', $summary);
|
||||
$smarty->assign('end_date_str', $end_date_str);
|
||||
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
|
||||
$smarty->assign('use_smtp', $config['use_smtp']);
|
||||
|
||||
$smarty->display('create_poll_step_3.tpl');
|
||||
exit;
|
||||
|
||||
case 4:
|
||||
// Step 4 : Data prepare before insert in DB
|
||||
|
||||
// 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_expiry_time) {
|
||||
$form->end_date = $min_expiry_time;
|
||||
} elseif ($max_expiry_time < $time) {
|
||||
$form->end_date = $max_expiry_time;
|
||||
} else {
|
||||
$form->end_date = $time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($form->end_date)) {
|
||||
// By default, expiration date is 6 months after last day
|
||||
$form->end_date = $max_expiry_time;
|
||||
}
|
||||
|
||||
// Insert poll in database
|
||||
$ids = $pollService->createPoll($form);
|
||||
$poll_id = $ids[0];
|
||||
$admin_poll_id = $ids[1];
|
||||
|
||||
// Send confirmation by mail if enabled
|
||||
if ($config['use_smtp'] === true) {
|
||||
$message = __('Mail', "This is the message to forward to the poll participants.");
|
||||
$message .= '<br/><br/>';
|
||||
$message .= Utils::htmlEscape($form->admin_name) . ' ' . __('Mail', 'has just created a poll called') . ' : "' . Utils::htmlEscape($form->title) . '".<br/>';
|
||||
$message .= __('Mail', 'Thank you for participating in the poll at the following link') . ' :<br/><br/><a href="%1$s">%1$s</a>';
|
||||
|
||||
$message_admin = __('Mail', "This message should NOT be sent to the poll participants. You should keep it private. <br/><br/>You can modify your poll at the following link");
|
||||
$message_admin .= ' :<br/><br/><a href="%1$s">%1$s</a>';
|
||||
|
||||
$message = sprintf($message, Utils::getUrlSondage($poll_id));
|
||||
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
|
||||
|
||||
if ($mailService->isValidEmail($form->admin_mail)) {
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Message for the author') . '] ' . __('Generic', 'Poll') . ': ' . $form->title, $message_admin);
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Participant link') . '] ' . __('Generic', 'Poll') . ': ' . $form->title, $message);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean Form data in $_SESSION
|
||||
unset($_SESSION['form']);
|
||||
|
||||
$purgeService->repeatedCleanings();
|
||||
|
||||
// creation message
|
||||
$sessionService->set("Framadate", "messagePollCreated", TRUE);
|
||||
|
||||
// Redirect to poll administration
|
||||
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
|
||||
exit;
|
||||
|
||||
case 1:
|
||||
default:
|
||||
// Step 1/4 : error if $_SESSION from info_sondage are not valid
|
||||
$smarty->assign('title', __('Error', 'Error!'));
|
||||
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
|
||||
$smarty->display('error.tpl');
|
||||
exit;
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
<?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<EFBFBD>l DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est r<EFBFBD>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<EFBFBD>l DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
use Framadate\Form;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
const GO_TO_STEP_2 = 'gotostep2';
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$inputService = new InputService();
|
||||
$pollRepository = RepositoryFactory::pollRepository();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
|
||||
|
||||
if ($form === null || !($form instanceof Form)) {
|
||||
$form = new Form();
|
||||
}
|
||||
|
||||
// Type de sondage
|
||||
if (isset($_GET['type']) && $_GET['type'] === 'date') {
|
||||
$poll_type = 'date';
|
||||
$form->choix_sondage = $poll_type;
|
||||
} else {
|
||||
$poll_type = 'classic';
|
||||
$form->choix_sondage = $poll_type;
|
||||
}
|
||||
|
||||
// We clean the data
|
||||
$goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(date|classic)$/']]);
|
||||
if ($goToStep2) {
|
||||
$title = $inputService->filterTitle($_POST['title']);
|
||||
|
||||
$use_ValueMax = isset($_POST['use_ValueMax']) ? $inputService->filterBoolean($_POST['use_ValueMax']) : false;
|
||||
$ValueMax = $use_ValueMax === true ? $inputService->filterValueMax($_POST['ValueMax']) : null;
|
||||
|
||||
$use_customized_url = isset($_POST['use_customized_url']) ? $inputService->filterBoolean($_POST['use_customized_url']) : false;
|
||||
$customized_url = $use_customized_url === true ? $inputService->filterId($_POST['customized_url']) : null;
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$mail = $config['use_smtp'] === true ? $inputService->filterMail($_POST['mail']) : null;
|
||||
$description = $inputService->filterDescription($_POST['description']);
|
||||
$editable = $inputService->filterEditable($_POST['editable']);
|
||||
$receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false;
|
||||
$receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false;
|
||||
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
|
||||
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
|
||||
$collect_users_mail = $inputService->filterCollectMail($_POST['collect_users_mail']);
|
||||
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
$password_repeat = isset($_POST['password_repeat']) ? $_POST['password_repeat'] : null;
|
||||
$results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
|
||||
|
||||
// On initialise également les autres variables
|
||||
$error_on_mail = false;
|
||||
$error_on_title = false;
|
||||
$error_on_name = false;
|
||||
$error_on_description = false;
|
||||
$error_on_password = false;
|
||||
$error_on_password_repeat = false;
|
||||
$error_on_customized_url = false;
|
||||
$error_on_ValueMax = false;
|
||||
|
||||
$form->title = $title;
|
||||
$form->id = $customized_url;
|
||||
$form->use_customized_url = $use_customized_url;
|
||||
$form->use_ValueMax = $use_ValueMax;
|
||||
$form->ValueMax = $ValueMax;
|
||||
$form->admin_name = $name;
|
||||
$form->admin_mail = $mail;
|
||||
$form->description = $description;
|
||||
$form->editable = $editable;
|
||||
$form->receiveNewVotes = $receiveNewVotes;
|
||||
$form->receiveNewComments = $receiveNewComments;
|
||||
$form->hidden = $hidden;
|
||||
$form->collect_users_mail = $collect_users_mail;
|
||||
$form->use_password = ($use_password !== null);
|
||||
$form->results_publicly_visible = ($results_publicly_visible !== null);
|
||||
|
||||
if ($config['use_smtp'] === true && empty($mail)) {
|
||||
$error_on_mail = true;
|
||||
}
|
||||
|
||||
if ($title !== $_POST['title']) {
|
||||
$error_on_title = true;
|
||||
}
|
||||
|
||||
if ($use_customized_url) {
|
||||
if ($customized_url === false) {
|
||||
$error_on_customized_url = true;
|
||||
} else if ($pollRepository->existsById($customized_url)) {
|
||||
$error_on_customized_url = true;
|
||||
$error_on_customized_url_msg = __('Error', 'Identifier is already used');
|
||||
} else if (in_array($customized_url, ['admin', 'vote', 'action'], true)) {
|
||||
$error_on_customized_url = true;
|
||||
$error_on_customized_url_msg = __('Error', 'This identifier is not allowed');
|
||||
}
|
||||
}
|
||||
|
||||
if ($use_ValueMax && $ValueMax === false) {
|
||||
$error_on_ValueMax = true;
|
||||
}
|
||||
|
||||
if ($name !== $_POST['name']) {
|
||||
$error_on_name = true;
|
||||
}
|
||||
|
||||
if ($description === false) {
|
||||
$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 = $mail && !$error_on_mail;
|
||||
} else {
|
||||
$email_OK = true;
|
||||
}
|
||||
|
||||
if ($use_password) {
|
||||
if (empty($password)) {
|
||||
$error_on_password = true;
|
||||
} else if ($password !== $password_repeat) {
|
||||
$error_on_password_repeat = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($title && $name && $email_OK && !$error_on_title && !$error_on_customized_url && !$error_on_description && !$error_on_name
|
||||
&& !$error_on_password && !$error_on_password_repeat &&!$error_on_ValueMax
|
||||
) {
|
||||
// If no errors, we hash the password if needed
|
||||
if ($form->use_password) {
|
||||
$form->password_hash = PasswordHasher::hash($password);
|
||||
} else {
|
||||
$form->password_hash = null;
|
||||
$form->results_publicly_visible = null;
|
||||
}
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
if ($goToStep2 === 'date') {
|
||||
header('Location:create_date_poll.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($goToStep2 === 'classic') {
|
||||
header('Location:create_classic_poll.php');
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
// Title Erreur !
|
||||
$title = __('Error', 'Error!') . ' - ' . __('Step 1', 'Poll creation (1 of 3)');
|
||||
}
|
||||
} else {
|
||||
// Title OK (formulaire pas encore rempli)
|
||||
$title = __('Step 1', 'Poll creation (1 of 3)');
|
||||
}
|
||||
|
||||
// Prepare error messages
|
||||
$errors = [
|
||||
'title' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'customized_url' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'description' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'name' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'email' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'password' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'ValueMax' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
'password_repeat' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
];
|
||||
|
||||
if (!empty($_POST[GO_TO_STEP_2])) {
|
||||
if (empty($_POST['title'])) {
|
||||
$errors['title']['aria'] = 'aria-describeby="poll_title_error" ';
|
||||
$errors['title']['class'] = ' has-error';
|
||||
$errors['title']['msg'] = __('Error', 'Enter a title');
|
||||
} elseif ($error_on_title) {
|
||||
$errors['title']['aria'] = 'aria-describeby="poll_title_error" ';
|
||||
$errors['title']['class'] = ' has-error';
|
||||
$errors['title']['msg'] = __('Error', 'Something is wrong with the format');
|
||||
}
|
||||
|
||||
if ($error_on_customized_url) {
|
||||
$errors['customized_url']['aria'] = 'aria-describeby="customized_url" ';
|
||||
$errors['customized_url']['class'] = ' has-error';
|
||||
$errors['customized_url']['msg'] = isset($error_on_customized_url_msg) ? $error_on_customized_url_msg : __('Error', "Something is wrong with the format: Customized URLs should only consist of alphanumeric characters and hyphens.");
|
||||
}
|
||||
|
||||
if ($error_on_description) {
|
||||
$errors['description']['aria'] = 'aria-describeby="poll_comment_error" ';
|
||||
$errors['description']['class'] = ' has-error';
|
||||
$errors['description']['msg'] = __('Error', 'Something is wrong with the format');
|
||||
}
|
||||
|
||||
if (empty($_POST['name'])) {
|
||||
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['name']['class'] = ' has-error';
|
||||
$errors['name']['msg'] = __('Error', 'Enter a name');
|
||||
} elseif ($error_on_name) {
|
||||
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['name']['class'] = ' has-error';
|
||||
$errors['name']['msg'] = __('Error', "Something is wrong with the format: name shouldn't have any spaces before or after");
|
||||
}
|
||||
|
||||
if (empty($_POST['mail'])) {
|
||||
$errors['email']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['email']['class'] = ' has-error';
|
||||
$errors['email']['msg'] = __('Error', 'Enter an email address');
|
||||
} elseif ($error_on_mail) {
|
||||
$errors['email']['aria'] = 'aria-describeby="poll_email_error" ';
|
||||
$errors['email']['class'] = ' has-error';
|
||||
$errors['email']['msg'] = __('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.');
|
||||
}
|
||||
|
||||
if ($error_on_password) {
|
||||
$errors['password']['aria'] = 'aria-describeby="poll_password_error" ';
|
||||
$errors['password']['class'] = ' has-error';
|
||||
$errors['password']['msg'] = __('Error', 'Password is empty.');
|
||||
}
|
||||
if ($error_on_password_repeat) {
|
||||
$errors['password_repeat']['aria'] = 'aria-describeby="poll_password_repeat_error" ';
|
||||
$errors['password_repeat']['class'] = ' has-error';
|
||||
$errors['password_repeat']['msg'] = __('Error', 'Passwords do not match.');
|
||||
}
|
||||
if ($error_on_ValueMax) {
|
||||
$errors['ValueMax']['aria'] = 'aria-describeby="poll_ValueMax" ';
|
||||
$errors['ValueMax']['class'] = ' has-error';
|
||||
$errors['ValueMax']['msg'] = __('Error', 'Error on amount of votes limitation: Value must be an integer greater than 0');
|
||||
}
|
||||
}
|
||||
|
||||
$useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']);
|
||||
|
||||
$smarty->assign('title', $title);
|
||||
$smarty->assign('useRemoteUser', $useRemoteUser);
|
||||
$smarty->assign('errors', $errors);
|
||||
$smarty->assign('advanced_errors', $goToStep2 && ($error_on_ValueMax || $error_on_customized_url || $error_on_password || $error_on_password_repeat));
|
||||
$smarty->assign('use_smtp', $config['use_smtp']);
|
||||
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
|
||||
$smarty->assign('goToStep2', GO_TO_STEP_2);
|
||||
|
||||
$smarty->assign('poll_type', $poll_type);
|
||||
$smarty->assign('poll_title', Utils::fromPostOrDefault('title', $form->title));
|
||||
$smarty->assign('customized_url', Utils::fromPostOrDefault('customized_url', $form->id));
|
||||
$smarty->assign('use_customized_url', Utils::fromPostOrDefault('use_customized_url', $form->use_customized_url));
|
||||
$smarty->assign('ValueMax', Utils::fromPostOrDefault('ValueMax', $form->ValueMax));
|
||||
$smarty->assign('use_ValueMax', Utils::fromPostOrDefault('use_ValueMax', $form->use_ValueMax));
|
||||
$smarty->assign('collect_users_mail', Utils::fromPostOrDefault('collect_users_mail', $form->collect_users_mail));
|
||||
$smarty->assign('poll_description', !empty($_POST['description']) ? $_POST['description'] : $form->description);
|
||||
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $form->admin_name));
|
||||
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $form->admin_mail));
|
||||
$smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $form->editable));
|
||||
$smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $form->receiveNewVotes));
|
||||
$smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $form->receiveNewComments));
|
||||
$smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $form->hidden));
|
||||
$smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $form->use_password));
|
||||
$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $form->results_publicly_visible));
|
||||
$smarty->assign('form', $form);
|
||||
|
||||
$smarty->display('create_poll.tpl');
|
|
@ -0,0 +1,25 @@
|
|||
.form-horizontal .radio label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.optionnal-parameters:hover, .optionnal-parameters:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.optionnal-parameters:active, .optionnal-parameters:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.optionnal-parameters .caret, .optionnal-parameters.collapsed .caret.caret-up {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.optionnal-parameters .caret.caret-up, .optionnal-parameters.collapsed .caret {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.caret.caret-up {
|
||||
border-bottom: 4px dashed;
|
||||
border-top: 0 none;
|
||||
content: "";
|
||||
}
|
|
@ -0,0 +1,587 @@
|
|||
/*!
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #2e6da4;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-striped {
|
||||
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,512 @@
|
|||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-top: 0;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #ffffff;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:before {
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:after {
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:before {
|
||||
right: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:after {
|
||||
right: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:before {
|
||||
top: -7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:after {
|
||||
top: -6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:before {
|
||||
bottom: -7px;
|
||||
border-bottom: 0;
|
||||
border-top: 7px solid #999;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:after {
|
||||
bottom: -6px;
|
||||
border-bottom: 0;
|
||||
border-top: 6px solid #ffffff;
|
||||
}
|
||||
.datepicker > div {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.datepicker td,
|
||||
.datepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover,
|
||||
.datepicker table tr td.day.focused {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
background-color: #fde19a;
|
||||
background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a));
|
||||
background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: -o-linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-image: linear-gradient(top, #fdd49a, #fdf59a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0);
|
||||
border-color: #fdf59a #fdf59a #fbed50;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled] {
|
||||
background-color: #fdf59a;
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active {
|
||||
background-color: #fbf069 \9;
|
||||
}
|
||||
.datepicker table tr td.today:hover:hover {
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today.active:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eeeeee;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
background-color: #f3d17a;
|
||||
background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a));
|
||||
background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: -o-linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-image: linear-gradient(top, #f3c17a, #f3e97a);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0);
|
||||
border-color: #f3e97a #f3e97a #edde34;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled] {
|
||||
background-color: #f3e97a;
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active {
|
||||
background-color: #efe24b \9;
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
background-color: #9e9e9e;
|
||||
background-image: -moz-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -ms-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080));
|
||||
background-image: -webkit-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: -o-linear-gradient(top, #b3b3b3, #808080);
|
||||
background-image: linear-gradient(top, #b3b3b3, #808080);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0);
|
||||
border-color: #808080 #808080 #595959;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled] {
|
||||
background-color: #808080;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #666666 \9;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
background-color: #006dcc;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -ms-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0044cc);
|
||||
background-image: linear-gradient(top, #0088cc, #0044cc);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0);
|
||||
border-color: #0044cc #0044cc #002a80;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
||||
color: #fff;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled] {
|
||||
background-color: #0044cc;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #003399 \9;
|
||||
}
|
||||
.datepicker table tr td span.old,
|
||||
.datepicker table tr td span.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker thead tr:first-child th,
|
||||
.datepicker tfoot tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker thead tr:first-child th:hover,
|
||||
.datepicker tfoot tr th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.datepicker thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
.input-append.date .add-on i,
|
||||
.input-prepend.date .add-on i {
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
-webkit-border-radius: 3px 0 0 3px;
|
||||
-moz-border-radius: 3px 0 0 3px;
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
-webkit-border-radius: 0 3px 3px 0;
|
||||
-moz-border-radius: 0 3px 3px 0;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .add-on {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
height: 20px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
vertical-align: middle;
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #ccc;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
.datepicker.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
float: left;
|
||||
display: none;
|
||||
min-width: 160px;
|
||||
list-style: none;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
*border-right-width: 2px;
|
||||
*border-bottom-width: 2px;
|
||||
color: #333333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.datepicker.dropdown-menu th,
|
||||
.datepicker.dropdown-menu td {
|
||||
padding: 4px 5px;
|
||||
}
|
|
@ -0,0 +1,791 @@
|
|||
/*!
|
||||
* Datepicker for Bootstrap
|
||||
*
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*/
|
||||
.datepicker {
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
direction: ltr;
|
||||
/*.dow {
|
||||
border-top: 1px solid #ddd !important;
|
||||
}*/
|
||||
}
|
||||
.datepicker-inline {
|
||||
width: 220px;
|
||||
}
|
||||
.datepicker.datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.datepicker.datepicker-rtl table tr td span {
|
||||
float: right;
|
||||
}
|
||||
.datepicker-dropdown {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.datepicker-dropdown:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
border-top: 0;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:before {
|
||||
left: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-left:after {
|
||||
left: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:before {
|
||||
right: 6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-right:after {
|
||||
right: 7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:before {
|
||||
top: -7px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-top:after {
|
||||
top: -6px;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:before {
|
||||
bottom: -7px;
|
||||
border-bottom: 0;
|
||||
border-top: 7px solid #999;
|
||||
}
|
||||
.datepicker-dropdown.datepicker-orient-bottom:after {
|
||||
bottom: -6px;
|
||||
border-bottom: 0;
|
||||
border-top: 6px solid #fff;
|
||||
}
|
||||
.datepicker > div {
|
||||
display: none;
|
||||
}
|
||||
.datepicker.days div.datepicker-days {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.months div.datepicker-months {
|
||||
display: block;
|
||||
}
|
||||
.datepicker.years div.datepicker-years {
|
||||
display: block;
|
||||
}
|
||||
.datepicker table {
|
||||
margin: 0;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.datepicker table tr td,
|
||||
.datepicker table tr th {
|
||||
text-align: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
.table-striped .datepicker table tr td,
|
||||
.table-striped .datepicker table tr th {
|
||||
background-color: transparent;
|
||||
}
|
||||
.datepicker table tr td.day:hover,
|
||||
.datepicker table tr td.day.focused {
|
||||
background: #eeeeee;
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker table tr td.old,
|
||||
.datepicker table tr td.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker table tr td.disabled,
|
||||
.datepicker table tr td.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td.today,
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today.disabled:hover {
|
||||
font-weight:bold;
|
||||
/*color: #000000;
|
||||
/*background-color: #ffdb99;
|
||||
border-color: #ffb733;*/
|
||||
}
|
||||
.datepicker table tr td.today:hover,
|
||||
.datepicker table tr td.today:hover:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today:focus,
|
||||
.datepicker table tr td.today:hover:focus,
|
||||
.datepicker table tr td.today.disabled:focus,
|
||||
.datepicker table tr td.today.disabled:hover:focus,
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.today,
|
||||
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
||||
/*color: #000000;
|
||||
background-color: #ffcd70;
|
||||
border-color: #f59e00;*/
|
||||
}
|
||||
.datepicker table tr td.today:active,
|
||||
.datepicker table tr td.today:hover:active,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.active,
|
||||
.datepicker table tr td.today:hover.active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.today,
|
||||
.open .dropdown-toggle.datepicker table tr td.today:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.today.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.today.disabled:hover {
|
||||
background-image: none;
|
||||
}
|
||||
.datepicker table tr td.today.disabled,
|
||||
.datepicker table tr td.today:hover.disabled,
|
||||
.datepicker table tr td.today.disabled.disabled,
|
||||
.datepicker table tr td.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.today[disabled],
|
||||
.datepicker table tr td.today:hover[disabled],
|
||||
.datepicker table tr td.today.disabled[disabled],
|
||||
.datepicker table tr td.today.disabled:hover[disabled],
|
||||
fieldset[disabled] .datepicker table tr td.today,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover,
|
||||
.datepicker table tr td.today:hover.disabled:hover,
|
||||
.datepicker table tr td.today.disabled.disabled:hover,
|
||||
.datepicker table tr td.today.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td.today[disabled]:hover,
|
||||
.datepicker table tr td.today:hover[disabled]:hover,
|
||||
.datepicker table tr td.today.disabled[disabled]:hover,
|
||||
.datepicker table tr td.today.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:hover,
|
||||
.datepicker table tr td.today.disabled:focus,
|
||||
.datepicker table tr td.today:hover.disabled:focus,
|
||||
.datepicker table tr td.today.disabled.disabled:focus,
|
||||
.datepicker table tr td.today.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td.today[disabled]:focus,
|
||||
.datepicker table tr td.today:hover[disabled]:focus,
|
||||
.datepicker table tr td.today.disabled[disabled]:focus,
|
||||
.datepicker table tr td.today.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.today:focus,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:focus,
|
||||
.datepicker table tr td.today.disabled:active,
|
||||
.datepicker table tr td.today:hover.disabled:active,
|
||||
.datepicker table tr td.today.disabled.disabled:active,
|
||||
.datepicker table tr td.today.disabled:hover.disabled:active,
|
||||
.datepicker table tr td.today[disabled]:active,
|
||||
.datepicker table tr td.today:hover[disabled]:active,
|
||||
.datepicker table tr td.today.disabled[disabled]:active,
|
||||
.datepicker table tr td.today.disabled:hover[disabled]:active,
|
||||
fieldset[disabled] .datepicker table tr td.today:active,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover:active,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:active,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover:active,
|
||||
.datepicker table tr td.today.disabled.active,
|
||||
.datepicker table tr td.today:hover.disabled.active,
|
||||
.datepicker table tr td.today.disabled.disabled.active,
|
||||
.datepicker table tr td.today.disabled:hover.disabled.active,
|
||||
.datepicker table tr td.today[disabled].active,
|
||||
.datepicker table tr td.today:hover[disabled].active,
|
||||
.datepicker table tr td.today.disabled[disabled].active,
|
||||
.datepicker table tr td.today.disabled:hover[disabled].active,
|
||||
fieldset[disabled] .datepicker table tr td.today.active,
|
||||
fieldset[disabled] .datepicker table tr td.today:hover.active,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled.active,
|
||||
fieldset[disabled] .datepicker table tr td.today.disabled:hover.active {
|
||||
/*background-color: #ffdb99;
|
||||
border-color: #ffb733;*/
|
||||
}
|
||||
.datepicker table tr td.today:hover:hover {
|
||||
color: #000;
|
||||
}
|
||||
.datepicker table tr td.today.active:hover {
|
||||
color: #fff;
|
||||
}
|
||||
.datepicker table tr td.range,
|
||||
.datepicker table tr td.range:hover,
|
||||
.datepicker table tr td.range.disabled,
|
||||
.datepicker table tr td.range.disabled:hover {
|
||||
background: #eeeeee;
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today,
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover {
|
||||
color: #000000;
|
||||
/*background-color: #f7ca77;
|
||||
border-color: #f1a417;*/
|
||||
border-radius: 0;
|
||||
}
|
||||
.datepicker table tr td.range.today:hover,
|
||||
.datepicker table tr td.range.today:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today:focus,
|
||||
.datepicker table tr td.range.today:hover:focus,
|
||||
.datepicker table tr td.range.today.disabled:focus,
|
||||
.datepicker table tr td.range.today.disabled:hover:focus,
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
||||
/*color: #000000;
|
||||
background-color: #f4bb51;
|
||||
border-color: #bf800c;*/
|
||||
}
|
||||
.datepicker table tr td.range.today:active,
|
||||
.datepicker table tr td.range.today:hover:active,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.active,
|
||||
.datepicker table tr td.range.today:hover.active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.range.today.disabled:hover {
|
||||
background-image: none;
|
||||
}
|
||||
.datepicker table tr td.range.today.disabled,
|
||||
.datepicker table tr td.range.today:hover.disabled,
|
||||
.datepicker table tr td.range.today.disabled.disabled,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled,
|
||||
.datepicker table tr td.range.today[disabled],
|
||||
.datepicker table tr td.range.today:hover[disabled],
|
||||
.datepicker table tr td.range.today.disabled[disabled],
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled],
|
||||
fieldset[disabled] .datepicker table tr td.range.today,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover,
|
||||
.datepicker table tr td.range.today:hover.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled.disabled:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td.range.today[disabled]:hover,
|
||||
.datepicker table tr td.range.today:hover[disabled]:hover,
|
||||
.datepicker table tr td.range.today.disabled[disabled]:hover,
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:hover,
|
||||
.datepicker table tr td.range.today.disabled:focus,
|
||||
.datepicker table tr td.range.today:hover.disabled:focus,
|
||||
.datepicker table tr td.range.today.disabled.disabled:focus,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td.range.today[disabled]:focus,
|
||||
.datepicker table tr td.range.today:hover[disabled]:focus,
|
||||
.datepicker table tr td.range.today.disabled[disabled]:focus,
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:focus,
|
||||
.datepicker table tr td.range.today.disabled:active,
|
||||
.datepicker table tr td.range.today:hover.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled.disabled:active,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled:active,
|
||||
.datepicker table tr td.range.today[disabled]:active,
|
||||
.datepicker table tr td.range.today:hover[disabled]:active,
|
||||
.datepicker table tr td.range.today.disabled[disabled]:active,
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled]:active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover:active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover:active,
|
||||
.datepicker table tr td.range.today.disabled.active,
|
||||
.datepicker table tr td.range.today:hover.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled.disabled.active,
|
||||
.datepicker table tr td.range.today.disabled:hover.disabled.active,
|
||||
.datepicker table tr td.range.today[disabled].active,
|
||||
.datepicker table tr td.range.today:hover[disabled].active,
|
||||
.datepicker table tr td.range.today.disabled[disabled].active,
|
||||
.datepicker table tr td.range.today.disabled:hover[disabled].active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today:hover.active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled.active,
|
||||
fieldset[disabled] .datepicker table tr td.range.today.disabled:hover.active {
|
||||
/*background-color: #f7ca77;
|
||||
border-color: #f1a417;*/
|
||||
}
|
||||
.datepicker table tr td.selected,
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #9D74B5;
|
||||
border-color: #8E65A6;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.selected:hover,
|
||||
.datepicker table tr td.selected:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected:focus,
|
||||
.datepicker table tr td.selected:hover:focus,
|
||||
.datepicker table tr td.selected.disabled:focus,
|
||||
.datepicker table tr td.selected.disabled:hover:focus,
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #9D74B5;
|
||||
border-color: #8E65A6;
|
||||
}
|
||||
.datepicker table tr td.selected:active,
|
||||
.datepicker table tr td.selected:hover:active,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.active,
|
||||
.datepicker table tr td.selected:hover.active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.selected.disabled:hover {
|
||||
background-image: none;
|
||||
}
|
||||
.datepicker table tr td.selected.disabled,
|
||||
.datepicker table tr td.selected:hover.disabled,
|
||||
.datepicker table tr td.selected.disabled.disabled,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled,
|
||||
.datepicker table tr td.selected[disabled],
|
||||
.datepicker table tr td.selected:hover[disabled],
|
||||
.datepicker table tr td.selected.disabled[disabled],
|
||||
.datepicker table tr td.selected.disabled:hover[disabled],
|
||||
fieldset[disabled] .datepicker table tr td.selected,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover,
|
||||
.datepicker table tr td.selected:hover.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled.disabled:hover,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td.selected[disabled]:hover,
|
||||
.datepicker table tr td.selected:hover[disabled]:hover,
|
||||
.datepicker table tr td.selected.disabled[disabled]:hover,
|
||||
.datepicker table tr td.selected.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:hover,
|
||||
.datepicker table tr td.selected.disabled:focus,
|
||||
.datepicker table tr td.selected:hover.disabled:focus,
|
||||
.datepicker table tr td.selected.disabled.disabled:focus,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td.selected[disabled]:focus,
|
||||
.datepicker table tr td.selected:hover[disabled]:focus,
|
||||
.datepicker table tr td.selected.disabled[disabled]:focus,
|
||||
.datepicker table tr td.selected.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:focus,
|
||||
.datepicker table tr td.selected.disabled:active,
|
||||
.datepicker table tr td.selected:hover.disabled:active,
|
||||
.datepicker table tr td.selected.disabled.disabled:active,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled:active,
|
||||
.datepicker table tr td.selected[disabled]:active,
|
||||
.datepicker table tr td.selected:hover[disabled]:active,
|
||||
.datepicker table tr td.selected.disabled[disabled]:active,
|
||||
.datepicker table tr td.selected.disabled:hover[disabled]:active,
|
||||
fieldset[disabled] .datepicker table tr td.selected:active,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover:active,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:active,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover:active,
|
||||
.datepicker table tr td.selected.disabled.active,
|
||||
.datepicker table tr td.selected:hover.disabled.active,
|
||||
.datepicker table tr td.selected.disabled.disabled.active,
|
||||
.datepicker table tr td.selected.disabled:hover.disabled.active,
|
||||
.datepicker table tr td.selected[disabled].active,
|
||||
.datepicker table tr td.selected:hover[disabled].active,
|
||||
.datepicker table tr td.selected.disabled[disabled].active,
|
||||
.datepicker table tr td.selected.disabled:hover[disabled].active,
|
||||
fieldset[disabled] .datepicker table tr td.selected.active,
|
||||
fieldset[disabled] .datepicker table tr td.selected:hover.active,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled.active,
|
||||
fieldset[disabled] .datepicker table tr td.selected.disabled:hover.active {
|
||||
background-color: #9D74B5;
|
||||
border-color: #8E65A6;
|
||||
}
|
||||
.datepicker table tr td.active,
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td.active:hover,
|
||||
.datepicker table tr td.active:hover:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active:focus,
|
||||
.datepicker table tr td.active:hover:focus,
|
||||
.datepicker table tr td.active.disabled:focus,
|
||||
.datepicker table tr td.active.disabled:hover:focus,
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
}
|
||||
.datepicker table tr td.active:active,
|
||||
.datepicker table tr td.active:hover:active,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.active,
|
||||
.datepicker table tr td.active:hover.active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.active,
|
||||
.open .dropdown-toggle.datepicker table tr td.active:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td.active.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td.active.disabled:hover {
|
||||
background-image: none;
|
||||
}
|
||||
.datepicker table tr td.active.disabled,
|
||||
.datepicker table tr td.active:hover.disabled,
|
||||
.datepicker table tr td.active.disabled.disabled,
|
||||
.datepicker table tr td.active.disabled:hover.disabled,
|
||||
.datepicker table tr td.active[disabled],
|
||||
.datepicker table tr td.active:hover[disabled],
|
||||
.datepicker table tr td.active.disabled[disabled],
|
||||
.datepicker table tr td.active.disabled:hover[disabled],
|
||||
fieldset[disabled] .datepicker table tr td.active,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover,
|
||||
.datepicker table tr td.active:hover.disabled:hover,
|
||||
.datepicker table tr td.active.disabled.disabled:hover,
|
||||
.datepicker table tr td.active.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td.active[disabled]:hover,
|
||||
.datepicker table tr td.active:hover[disabled]:hover,
|
||||
.datepicker table tr td.active.disabled[disabled]:hover,
|
||||
.datepicker table tr td.active.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:hover,
|
||||
.datepicker table tr td.active.disabled:focus,
|
||||
.datepicker table tr td.active:hover.disabled:focus,
|
||||
.datepicker table tr td.active.disabled.disabled:focus,
|
||||
.datepicker table tr td.active.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td.active[disabled]:focus,
|
||||
.datepicker table tr td.active:hover[disabled]:focus,
|
||||
.datepicker table tr td.active.disabled[disabled]:focus,
|
||||
.datepicker table tr td.active.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:focus,
|
||||
.datepicker table tr td.active.disabled:active,
|
||||
.datepicker table tr td.active:hover.disabled:active,
|
||||
.datepicker table tr td.active.disabled.disabled:active,
|
||||
.datepicker table tr td.active.disabled:hover.disabled:active,
|
||||
.datepicker table tr td.active[disabled]:active,
|
||||
.datepicker table tr td.active:hover[disabled]:active,
|
||||
.datepicker table tr td.active.disabled[disabled]:active,
|
||||
.datepicker table tr td.active.disabled:hover[disabled]:active,
|
||||
fieldset[disabled] .datepicker table tr td.active:active,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover:active,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:active,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover:active,
|
||||
.datepicker table tr td.active.disabled.active,
|
||||
.datepicker table tr td.active:hover.disabled.active,
|
||||
.datepicker table tr td.active.disabled.disabled.active,
|
||||
.datepicker table tr td.active.disabled:hover.disabled.active,
|
||||
.datepicker table tr td.active[disabled].active,
|
||||
.datepicker table tr td.active:hover[disabled].active,
|
||||
.datepicker table tr td.active.disabled[disabled].active,
|
||||
.datepicker table tr td.active.disabled:hover[disabled].active,
|
||||
fieldset[disabled] .datepicker table tr td.active.active,
|
||||
fieldset[disabled] .datepicker table tr td.active:hover.active,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled.active,
|
||||
fieldset[disabled] .datepicker table tr td.active.disabled:hover.active {
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
}
|
||||
.datepicker table tr td span {
|
||||
display: block;
|
||||
width: 23%;
|
||||
height: 54px;
|
||||
line-height: 54px;
|
||||
float: left;
|
||||
margin: 1%;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.datepicker table tr td span:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker table tr td span.disabled,
|
||||
.datepicker table tr td span.disabled:hover {
|
||||
background: none;
|
||||
color: #999999;
|
||||
cursor: default;
|
||||
}
|
||||
.datepicker table tr td span.active,
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
.datepicker table tr td span.active:hover,
|
||||
.datepicker table tr td span.active:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active:focus,
|
||||
.datepicker table tr td span.active:hover:focus,
|
||||
.datepicker table tr td span.active.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled:hover:focus,
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
||||
color: #ffffff;
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
}
|
||||
.datepicker table tr td span.active:active,
|
||||
.datepicker table tr td span.active:hover:active,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.active,
|
||||
.datepicker table tr td span.active:hover.active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.active,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active:hover,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled,
|
||||
.open .dropdown-toggle.datepicker table tr td span.active.disabled:hover {
|
||||
background-image: none;
|
||||
}
|
||||
.datepicker table tr td span.active.disabled,
|
||||
.datepicker table tr td span.active:hover.disabled,
|
||||
.datepicker table tr td span.active.disabled.disabled,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled,
|
||||
.datepicker table tr td span.active[disabled],
|
||||
.datepicker table tr td span.active:hover[disabled],
|
||||
.datepicker table tr td span.active.disabled[disabled],
|
||||
.datepicker table tr td span.active.disabled:hover[disabled],
|
||||
fieldset[disabled] .datepicker table tr td span.active,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover,
|
||||
.datepicker table tr td span.active:hover.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled.disabled:hover,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled:hover,
|
||||
.datepicker table tr td span.active[disabled]:hover,
|
||||
.datepicker table tr td span.active:hover[disabled]:hover,
|
||||
.datepicker table tr td span.active.disabled[disabled]:hover,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled]:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:hover,
|
||||
.datepicker table tr td span.active.disabled:focus,
|
||||
.datepicker table tr td span.active:hover.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled.disabled:focus,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled:focus,
|
||||
.datepicker table tr td span.active[disabled]:focus,
|
||||
.datepicker table tr td span.active:hover[disabled]:focus,
|
||||
.datepicker table tr td span.active.disabled[disabled]:focus,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled]:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:focus,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:focus,
|
||||
.datepicker table tr td span.active.disabled:active,
|
||||
.datepicker table tr td span.active:hover.disabled:active,
|
||||
.datepicker table tr td span.active.disabled.disabled:active,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled:active,
|
||||
.datepicker table tr td span.active[disabled]:active,
|
||||
.datepicker table tr td span.active:hover[disabled]:active,
|
||||
.datepicker table tr td span.active.disabled[disabled]:active,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled]:active,
|
||||
fieldset[disabled] .datepicker table tr td span.active:active,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover:active,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:active,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover:active,
|
||||
.datepicker table tr td span.active.disabled.active,
|
||||
.datepicker table tr td span.active:hover.disabled.active,
|
||||
.datepicker table tr td span.active.disabled.disabled.active,
|
||||
.datepicker table tr td span.active.disabled:hover.disabled.active,
|
||||
.datepicker table tr td span.active[disabled].active,
|
||||
.datepicker table tr td span.active:hover[disabled].active,
|
||||
.datepicker table tr td span.active.disabled[disabled].active,
|
||||
.datepicker table tr td span.active.disabled:hover[disabled].active,
|
||||
fieldset[disabled] .datepicker table tr td span.active.active,
|
||||
fieldset[disabled] .datepicker table tr td span.active:hover.active,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled.active,
|
||||
fieldset[disabled] .datepicker table tr td span.active.disabled:hover.active {
|
||||
background-color: #6A5687;
|
||||
border-color: #5C4978;
|
||||
}
|
||||
.datepicker table tr td span.old,
|
||||
.datepicker table tr td span.new {
|
||||
color: #999999;
|
||||
}
|
||||
.datepicker th.datepicker-switch {
|
||||
width: 145px;
|
||||
}
|
||||
.datepicker thead tr:first-child th,
|
||||
.datepicker tfoot tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
.datepicker thead tr:first-child th:hover,
|
||||
.datepicker tfoot tr th:hover {
|
||||
background: #eeeeee;
|
||||
}
|
||||
.datepicker .cw {
|
||||
font-size: 10px;
|
||||
width: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.datepicker thead tr:first-child th.cw {
|
||||
cursor: default;
|
||||
background-color: transparent;
|
||||
}
|
||||
.input-group.date .input-group-addon i {
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.input-daterange input {
|
||||
text-align: center;
|
||||
}
|
||||
.input-daterange input:first-child {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.input-daterange input:last-child {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.input-daterange .input-group-addon {
|
||||
width: auto;
|
||||
min-width: 16px;
|
||||
padding: 4px 5px;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
text-align: center;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
vertical-align: middle;
|
||||
background-color: #eeeeee;
|
||||
border: solid #cccccc;
|
||||
border-width: 1px 0;
|
||||
margin-left: -5px;
|
||||
margin-right: -5px;
|
||||
}
|
||||
.datepicker.dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
float: left;
|
||||
display: none;
|
||||
min-width: 160px;
|
||||
list-style: none;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
*border-right-width: 2px;
|
||||
*border-bottom-width: 2px;
|
||||
color: #333333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
.datepicker.dropdown-menu th,
|
||||
.datepicker.dropdown-menu td {
|
||||
padding: 4px 5px;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue