mirror of
https://framagit.org/framasoft/framadate/funky-framadate-front.git
synced 2023-08-25 13:53:14 +02:00
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
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
tpl_c/*.php
|
||||
vendor/
|
||||
|
||||
po/*.po
|
||||
po/*.json
|
17
.htaccess
Normal file
17
.htaccess
Normal file
@ -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>
|
14
AUTHORS.md
Normal file
14
AUTHORS.md
Normal file
@ -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
|
||||
|
235
CHANGELOG.md
Normal file
235
CHANGELOG.md
Normal file
@ -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.
|
7
CONTRIBUTING.md
Normal file
7
CONTRIBUTING.md
Normal file
@ -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.
|
1
INSTALL.md
Normal file
1
INSTALL.md
Normal file
@ -0,0 +1 @@
|
||||
# [Now available here](https://framagit.org/framasoft/framadate/wikis/home)
|
519
LICENCE.fr.txt
Normal file
519
LICENCE.fr.txt
Normal file
@ -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.
|
||||
|
515
LICENSE.en.txt
Normal file
515
LICENSE.en.txt
Normal file
@ -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.
|
||||
|
22
Makefile
Normal file
22
Makefile
Normal file
@ -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
|
||||
|
95
action/add_comment.php
Normal file
95
action/add_comment.php
Normal file
@ -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);
|
94
action/send_edit_link_by_email_action.php
Normal file
94
action/send_edit_link_by_email_action.php
Normal file
@ -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);
|
1
admin/.htpasswd
Normal file
1
admin/.htpasswd
Normal file
@ -0,0 +1 @@
|
||||
admin:$apr1$BskOE5OK$zOMq.fKFNCHkKhBdA12mR/
|
230
admin/check.php
Normal file
230
admin/check.php
Normal file
@ -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>
|
29
admin/cleanDemo.php
Normal file
29
admin/cleanDemo.php
Normal file
@ -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();
|
||||
|
24
admin/index.php
Normal file
24
admin/index.php
Normal file
@ -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');
|
50
admin/install.php
Normal file
50
admin/install.php
Normal file
@ -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');
|
29
admin/logs.php
Normal file
29
admin/logs.php
Normal file
@ -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');
|
80
admin/migration.php
Normal file
80
admin/migration.php
Normal file
@ -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');
|
101
admin/polls.php
Normal file
101
admin/polls.php
Normal file
@ -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');
|
60
admin/purge.php
Normal file
60
admin/purge.php
Normal file
@ -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');
|
510
adminstuds.php
Normal file
510
adminstuds.php
Normal file
@ -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');
|
54
app/classes/Framadate/AbstractMigration.php
Normal file
54
app/classes/Framadate/AbstractMigration.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
58
app/classes/Framadate/Choice.php
Normal file
58
app/classes/Framadate/Choice.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
35
app/classes/Framadate/CollectMail.php
Normal file
35
app/classes/Framadate/CollectMail.php
Normal file
@ -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;
|
||||
}
|
36
app/classes/Framadate/Editable.php
Normal file
36
app/classes/Framadate/Editable.php
Normal file
@ -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() {
|
||||
}
|
||||
}
|
12
app/classes/Framadate/Exception/ConcurrentVoteException.php
Normal file
12
app/classes/Framadate/Exception/ConcurrentVoteException.php
Normal file
@ -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() {
|
||||
}
|
||||
}
|
121
app/classes/Framadate/Form.php
Normal file
121
app/classes/Framadate/Form.php
Normal file
@ -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']);
|
||||
}
|
||||
}
|
38
app/classes/Framadate/Message.php
Normal file
38
app/classes/Framadate/Message.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
99
app/classes/Framadate/Migrations/Version20150101000000.php
Normal file
99
app/classes/Framadate/Migrations/Version20150101000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
160
app/classes/Framadate/Migrations/Version20150102000000.php
Normal file
160
app/classes/Framadate/Migrations/Version20150102000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
263
app/classes/Framadate/Migrations/Version20150102100000.php
Normal file
263
app/classes/Framadate/Migrations/Version20150102100000.php
Normal file
@ -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));
|
||||
}
|
||||
}
|
70
app/classes/Framadate/Migrations/Version20150117000000.php
Normal file
70
app/classes/Framadate/Migrations/Version20150117000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
76
app/classes/Framadate/Migrations/Version20150402000000.php
Normal file
76
app/classes/Framadate/Migrations/Version20150402000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
72
app/classes/Framadate/Migrations/Version20150405000000.php
Normal file
72
app/classes/Framadate/Migrations/Version20150405000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
78
app/classes/Framadate/Migrations/Version20150624000000.php
Normal file
78
app/classes/Framadate/Migrations/Version20150624000000.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/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.
|
||||
}
|
||||
}
|
102
app/classes/Framadate/Migrations/Version20150918000000.php
Normal file
102
app/classes/Framadate/Migrations/Version20150918000000.php
Normal file
@ -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.
|
||||
}
|
||||
}
|
71
app/classes/Framadate/Migrations/Version20151012075900.php
Normal file
71
app/classes/Framadate/Migrations/Version20151012075900.php
Normal file
@ -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')]);
|
||||
}
|
||||
}
|
69
app/classes/Framadate/Migrations/Version20151012082600.php
Normal file
69
app/classes/Framadate/Migrations/Version20151012082600.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
72
app/classes/Framadate/Migrations/Version20151028000000.php
Normal file
72
app/classes/Framadate/Migrations/Version20151028000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
51
app/classes/Framadate/Migrations/Version20151205000000.php
Normal file
51
app/classes/Framadate/Migrations/Version20151205000000.php
Normal file
@ -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.
|
||||
}
|
||||
}
|
65
app/classes/Framadate/Migrations/Version20180220000000.php
Normal file
65
app/classes/Framadate/Migrations/Version20180220000000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
88
app/classes/Framadate/Migrations/Version20180411000000.php
Normal file
88
app/classes/Framadate/Migrations/Version20180411000000.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/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
|
||||
}
|
||||
}
|
65
app/classes/Framadate/Migrations/Version20180419170000.php
Normal file
65
app/classes/Framadate/Migrations/Version20180419170000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
65
app/classes/Framadate/Migrations/Version20180419180000.php
Normal file
65
app/classes/Framadate/Migrations/Version20180419180000.php
Normal file
@ -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');
|
||||
}
|
||||
}
|
60
app/classes/Framadate/Migrations/Version20180419190000.php
Normal file
60
app/classes/Framadate/Migrations/Version20180419190000.php
Normal file
@ -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
|
||||
}
|
||||
}
|
100
app/classes/Framadate/Migrations/Version20180525110000.php
Normal file
100
app/classes/Framadate/Migrations/Version20180525110000.php
Normal file
@ -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'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
69
app/classes/Framadate/Repositories/AbstractRepository.php
Normal file
69
app/classes/Framadate/Repositories/AbstractRepository.php
Normal file
@ -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();
|
||||
}
|
||||
}
|
69
app/classes/Framadate/Repositories/CommentRepository.php
Normal file
69
app/classes/Framadate/Repositories/CommentRepository.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
241
app/classes/Framadate/Repositories/PollRepository.php
Normal file
241
app/classes/Framadate/Repositories/PollRepository.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
81
app/classes/Framadate/Repositories/RepositoryFactory.php
Normal file
81
app/classes/Framadate/Repositories/RepositoryFactory.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/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;
|
||||
}
|
||||
}
|
134
app/classes/Framadate/Repositories/SlotRepository.php
Normal file
134
app/classes/Framadate/Repositories/SlotRepository.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
140
app/classes/Framadate/Repositories/VoteRepository.php
Normal file
140
app/classes/Framadate/Repositories/VoteRepository.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
34
app/classes/Framadate/Security/PasswordHasher.php
Normal file
34
app/classes/Framadate/Security/PasswordHasher.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
90
app/classes/Framadate/Security/Token.php
Normal file
90
app/classes/Framadate/Security/Token.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
316
app/classes/Framadate/Services/AdminPollService.php
Normal file
316
app/classes/Framadate/Services/AdminPollService.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
148
app/classes/Framadate/Services/InputService.php
Normal file
148
app/classes/Framadate/Services/InputService.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
143
app/classes/Framadate/Services/InstallService.php
Normal file
143
app/classes/Framadate/Services/InstallService.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
23
app/classes/Framadate/Services/LogService.php
Normal file
23
app/classes/Framadate/Services/LogService.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
127
app/classes/Framadate/Services/MailService.php
Normal file
127
app/classes/Framadate/Services/MailService.php
Normal 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
app/classes/Framadate/Services/NotificationService.php
Normal file
86
app/classes/Framadate/Services/NotificationService.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
398
app/classes/Framadate/Services/PollService.php
Normal file
398
app/classes/Framadate/Services/PollService.php
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
app/classes/Framadate/Services/PurgeService.php
Normal file
114
app/classes/Framadate/Services/PurgeService.php
Normal file
@ -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;
|
||||
}
|
||||
}
|
91
app/classes/Framadate/Services/SecurityService.php
Normal file
91
app/classes/Framadate/Services/SecurityService.php
Normal file
@ -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'] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
63
app/classes/Framadate/Services/SessionService.php
Normal file
63
app/classes/Framadate/Services/SessionService.php
Normal file
@ -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] = [];
|
||||
}
|
||||
}
|
||||
}
|
35
app/classes/Framadate/Services/SuperAdminService.php
Normal file
35
app/classes/Framadate/Services/SuperAdminService.php
Normal file
@ -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];
|
||||
}
|
||||
}
|
||||
|
209
app/classes/Framadate/Utils.php
Normal file
209
app/classes/Framadate/Utils.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
126
app/inc/config.php
Normal file
126
app/inc/config.php
Normal file
@ -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
|
||||
];
|
||||
|
116
app/inc/config.test.php
Normal file
116
app/inc/config.test.php
Normal file
@ -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
|
||||
];
|
45
app/inc/constants.php
Normal file
45
app/inc/constants.php
Normal file
@ -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;
|
45
app/inc/i18n.php
Normal file
45
app/inc/i18n.php
Normal file
@ -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
|
||||
}
|
||||
}
|
91
app/inc/init.php
Normal file
91
app/inc/init.php
Normal file
@ -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';
|
122
app/inc/smarty.php
Normal file
122
app/inc/smarty.php
Normal file
@ -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';
|
||||
}
|
23
app/tests/Framadate/FramaTestCase.php
Normal file
23
app/tests/Framadate/FramaTestCase.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
35
app/tests/Framadate/Services/InputServiceUnitTest.php
Normal file
35
app/tests/Framadate/Services/InputServiceUnitTest.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
32
app/tests/Framadate/Services/MailServiceUnitTest.php
Normal file
32
app/tests/Framadate/Services/MailServiceUnitTest.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
3
app/tests/bootstrap.php
Normal file
3
app/tests/bootstrap.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
$loader = require __DIR__ . '/../../vendor/autoload.php';
|
||||
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');
|
72
bandeaux.php
Normal file
72
bandeaux.php
Normal file
@ -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";
|
||||
}
|
58
bin/doctrine
Executable file
58
bin/doctrine
Executable file
@ -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());
|
||||
}
|
46
buildlang.php
Normal file
46
buildlang.php
Normal file
@ -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>
|
68
compare.php
Normal file
68
compare.php
Normal file
@ -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>
|
87
composer.json
Normal file
87
composer.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
3566
composer.lock
generated
Normal file
3566
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
220
create_classic_poll.php
Normal file
220
create_classic_poll.php
Normal file
@ -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;
|
||||
}
|
263
create_date_poll.php
Normal file
263
create_date_poll.php
Normal file
@ -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;
|
||||
}
|
313
create_poll.php
Normal file
313
create_poll.php
Normal file
@ -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');
|
25
css/app/create_poll.css
Normal file
25
css/app/create_poll.css
Normal file
@ -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: "";
|
||||
}
|
587
css/bootstrap-theme.css
vendored
Normal file
587
css/bootstrap-theme.css
vendored
Normal file
@ -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 */
|
1
css/bootstrap-theme.css.map
Normal file
1
css/bootstrap-theme.css.map
Normal file
File diff suppressed because one or more lines are too long
6
css/bootstrap-theme.min.css
vendored
Normal file
6
css/bootstrap-theme.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
css/bootstrap-theme.min.css.map
Normal file
1
css/bootstrap-theme.min.css.map
Normal file
File diff suppressed because one or more lines are too long
6757
css/bootstrap.css
vendored
Normal file
6757
css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
css/bootstrap.css.map
Normal file
1
css/bootstrap.css.map
Normal file
File diff suppressed because one or more lines are too long
6
css/bootstrap.min.css
vendored
Normal file
6
css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
css/bootstrap.min.css.map
Normal file
1
css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
512
css/datepicker.css
Normal file
512
css/datepicker.css
Normal file
@ -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;
|
||||
}
|
791
css/datepicker3.css
Normal file
791
css/datepicker3.css
Normal file
@ -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
Block a user