Compare commits

..

97 Commits

Author SHA1 Message Date
Thomas Citharel
19bec88184 Merge branch 'docker' into 'develop'
Introduce Docker Image

See merge request framasoft/framadate!314
2018-07-12 15:57:37 +02:00
Thomas Citharel
8f115461e1 Introduce dev Docker Compose
Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Run migrations on entrypoint

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Finalize Docker configuration

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

turn on pretty urls

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Make framadate await database container

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Add missing rewrite rule to apache config

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Finalize

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-12 15:31:57 +02:00
Thomas Citharel
d8123634fa Disable zanata also on beta
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-11 14:07:34 +02:00
Thomas Citharel
8dcfb0d494 Disable everything Zanata related for now
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-11 14:05:25 +02:00
Thomas Citharel
e7d5b1762e Merge tag '1.1.7' into develop 2018-07-09 15:53:59 +02:00
Thomas Citharel
f861b77326 Merge tag '1.1.6' into develop 2018-07-09 12:26:38 +02:00
Thomas Citharel
c4b562d016 Merge tag '1.1.5' into develop 2018-07-09 11:25:41 +02:00
Thomas Citharel
a5373818fb Fix Gitlab push-trads
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-06-22 12:08:43 +02:00
Thomas Citharel
f1678c1744 Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-06-22 11:43:07 +02:00
Thomas Citharel
3438635cda Add contribution guide 2018-06-11 13:17:31 +02:00
Thomas Citharel
f8f4cc6076 Merge branch 'secure-push-trad-to-zanata' into 'develop'
Secure push-trad-to-zanata

Closes #331

See merge request framasoft/framadate!326
2018-05-28 10:04:27 +02:00
Thomas Citharel
4b8fe75f95
Secure push-trad-to-zanata
Test json files with python module to make sure they're valid before converting them to po

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Remove &&
2018-05-28 10:03:05 +02:00
Thomas Citharel
149b64e3b0 Merge branch 'fix-markdown-in-previews' into 'develop'
Also handle the graph

See merge request framasoft/framadate!324
2018-05-25 19:41:13 +02:00
Thomas Citharel
ce11049630
Also handle the graph
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 19:40:05 +02:00
Thomas Citharel
f656c790d0 Merge branch 'fix-markdown-in-previews' into 'develop'
Properly handle markdown choices inside titles, if no text as fallback, use the…

Closes #326

See merge request framasoft/framadate!323
2018-05-25 19:35:20 +02:00
Thomas Citharel
14053d20c9
Handle markdown properly in the best choices section
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 19:34:36 +02:00
Thomas Citharel
92f699337f
Properly handle markdown choices inside titles, if no text as fallback, use the number of the choice column
Also, unrelated, but finally disabled smarty caching on php's build-in server

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 19:24:54 +02:00
Thomas Citharel
af760cf273
Add checks on session
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 18:16:22 +02:00
Thomas Citharel
f4c901d6cd
Typo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 18:05:19 +02:00
Thomas Citharel
b04f95f0e1 Merge branch 'split-create-poll-form' into 'develop'
Split create poll form

See merge request framasoft/framadate!322
2018-05-25 17:39:26 +02:00
Thomas Citharel
bd748e5da7
Split the create poll (step 1) template into parts
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 17:37:48 +02:00
Thomas Citharel
e793a77b2a
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 16:25:35 +02:00
Thomas Citharel
e078e2d22b
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 16:18:38 +02:00
Thomas Citharel
52bc066b05
Fix json translation file that deleted the whole zanata document 🙈
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 16:02:56 +02:00
Thomas Citharel
4b394e9546 Merge branch 'add-sendmail-option' into 'develop'
Introduce an use_sendmail option

See merge request framasoft/framadate!321
2018-05-25 15:51:31 +02:00
Thomas Citharel
d6c2f01457
Introduce an use_sendmail option
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 15:50:35 +02:00
Thomas Citharel
7305c0f89c Merge branch 'add-missing-migration' into 'develop'
Add a missing migration back

See merge request framasoft/framadate!320
2018-05-25 15:20:42 +02:00
Thomas Citharel
2cd54d9c4d
Add a missing migration back
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 15:18:56 +02:00
Thomas Citharel
964952433c Merge branch 'JMarlow/framadate-Justine/Issue2' into 'develop'
J marlow/framadate justine/issue2

Closes #324

See merge request framasoft/framadate!319
2018-05-25 12:39:18 +02:00
Thomas Citharel
4c0c2a16f9
Small fixes
Change CollectMail class name, add a title on voters name in admin with email value

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 12:36:18 +02:00
Meteor-Furet
e6716bc745
MAJ table_vote_classic et table_vote_date + passage option 3 de collect_users_mail en disabled car non implémenté 2018-05-25 11:40:05 +02:00
Meteor-Furet
b5ce4baf60
MAJ poll_info.tpl 2018-05-25 11:39:30 +02:00
Meteor-Furet
feb0c1e6ce
Modification collect_users_mail de bool à int (MAJ de la création d'un sondage) 2018-05-25 11:39:28 +02:00
Thomas Citharel
bcd5acdea5 Merge branch 'chrosey/framadate-develop' into 'develop'
Chrosey/framadate develop

See merge request framasoft/framadate!318
2018-05-25 10:59:18 +02:00
Thomas Citharel
2e3fe1dcd3
Small fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 10:57:46 +02:00
chrosey
ad450e2798
fixed classic_poll.js to work again
fix typo

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Fixed typo

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-25 10:40:40 +02:00
chrosey
141e9105be
refactored create_classic_poll and put step_2 view into own tpl file 2018-05-25 10:35:46 +02:00
chrosey
fad662e09a
renamed poll_step_3.tpl 2018-05-25 10:29:48 +02:00
Thomas Citharel
586e59e23f Merge branch 'fix-session' into 'develop'
Fix session

Closes #255

See merge request framasoft/framadate!317
2018-05-25 10:28:25 +02:00
Thomas Citharel
d5f0f5a289 Merge branch 'develop' into 'develop'
Fix missing beginTransaction()

See merge request framasoft/framadate!315
2018-05-24 17:57:45 +02:00
Thomas Citharel
442fd174e9
Fix session by serializing the form object
Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Fixup !

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Fixup

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-24 17:55:40 +02:00
Thomas Citharel
081a32b1bb
Merge branch 'mosterdt/framadate-bug/restrict-custom-url' into develop 2018-05-19 08:59:56 +02:00
Thomas De Backer
5951519ad8
restrict custom urls
fixed coding standard issues

added error message to locale

Remove unecessary variable

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-19 08:59:30 +02:00
PICHOU Kyâne
a64e182076 Fix missing beginTransaction() 2018-05-16 21:55:54 +02:00
Thomas Citharel
823c41d2e4
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-05-15 08:55:34 +02:00
Thomas Citharel
f28198e497 Merge branch 'feature/warn-wrong-customized-url' into 'develop'
Warn when url has a wrong format

See merge request framasoft/framadate!311
2018-05-14 19:02:51 +02:00
Thomas Citharel
fe93733872
fix typo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-23 10:40:18 +02:00
Thomas Citharel
5631167ca7 Merge branch 'dbal-fixes' into 'develop'
Couple of fixes following DBAL migration

See merge request framasoft/framadate!312
2018-04-23 10:30:10 +02:00
Thomas Citharel
87a61ebea3
Couple of fixes following DBAL migration
* Make sure we save timestamp as a string (🙈) inside slot table
* Make sure to set poll format properly at creation
* More Repository methods use DBAL specifics
* Clear legacy check all tables call

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-23 10:27:15 +02:00
Thomas Citharel
b008bfea9b
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-23 09:07:18 +02:00
Thomas Citharel
3ba045791f Merge branch 'dbal' into 'develop'
Make Framadate compatible with PostgreSQL and great again !

Closes #90 et #179

See merge request framasoft/framadate!305
2018-04-22 18:55:55 +02:00
Thomas Citharel
7f41eb831c
Remove class FramaDB and unused call
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-22 18:45:30 +02:00
Thomas Citharel
006a191544
Make Framadate compatible with PostgreSQL and great again !
* Move the database handling to Doctrine DBAL
* Move Migrations to Doctrine Migrations
* Rename migrations for Doctrine Migrations Uses
* Fix Migrations
* Change config parameters, introduce db name, host and port parameters and get rid of database url
* Change install form for this
* Add a CLI command to make migrations
* Add config.test.php to be used with APP_ENV=test for testing

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

CS

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Add sqlite to CI and execute migration in test env

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Typo

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

SQLite is already inside the image...

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Rebase two new migrations

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Move from trait to abstract class and remove legacy migration table after checks

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

CS

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Move doctrine command path inside CI

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Move abstract migration class to correct namespace and remove unused command

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

CS

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Check for legacy migration table existence

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Check if legacy migration table exists before deleting it

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Add messages for skipped migrations and fix an issue with MySQL ERR_NO_DATE Migration

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-22 18:37:53 +02:00
Quentin Dupont
235002cd24 Warn when url has a wrong format 2018-04-22 12:54:12 +02:00
Thomas Citharel
c4e9cb59d3 Merge branch 'bug/button-create-poll-step-2' into 'develop'
bug/button-create-poll-step-2

See merge request framasoft/framadate!308
2018-04-21 11:56:56 +02:00
m
8e8ffedc4a bug/button-create-poll-step-2 2018-04-20 20:11:10 +02:00
Thomas Citharel
2b351bc3d1
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-20 14:31:18 +02:00
Thomas Citharel
17c264721e Merge branch 'bug/send-comment-button' into 'develop'
Bug/send comment button

See merge request framasoft/framadate!307
2018-04-20 09:24:37 +02:00
m
e379b58d58 bug/send-comment-button 2018-04-20 09:17:37 +02:00
m
b2a550d09b Merge branch 'develop' of https://framagit.org/framasoft/framadate into develop 2018-04-19 21:53:28 +02:00
Thomas Citharel
0409256588 Merge branch 'Justine/Issue1' into 'develop'
Collecting polled users emails

Closes #38

See merge request framasoft/framadate!266
2018-04-19 16:55:21 +02:00
Thomas Citharel
abec2cb6a3
Refactor and fix indentation a bit, add a <pre> section for email collection and string changes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-19 16:53:54 +02:00
Thomas Citharel
b24cc43c85 Merge branch 'bug/better-handle-undefined-config-values' into 'develop'
Bug/better handle undefined config values

See merge request framasoft/framadate!304
2018-04-19 15:33:56 +02:00
Thomas Citharel
4c4fb1557f Merge branch 'feature/ask-name-and-comment-before-sending' into 'develop'
feature/ask name and comment before sending

See merge request framasoft/framadate!285
2018-04-19 15:21:49 +02:00
m
0841675577
ask name and comment before sending comment
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-19 15:12:13 +02:00
Thomas Citharel
aa690bb6d8
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-19 14:52:41 +02:00
Thomas Citharel
06c3a8a078 Merge branch 'bug/create-date-poll-step-2' into 'develop'
Bug/create date poll step 2

See merge request framasoft/framadate!302
2018-04-19 14:20:54 +02:00
m
41578b19c8
error on date poll creation
https://framagit.org/framasoft/framadate/issues/318

code style

modify error message on session expiration

new local text

remove empty string
2018-04-19 14:19:24 +02:00
m
81092482af Merge branch 'develop' of https://framagit.org/framasoft/framadate into develop 2018-04-19 14:14:42 +02:00
Thomas Citharel
290ec002d5 Merge branch 'feature/files-cleaning' into 'develop'
Feature/files cleaning

See merge request framasoft/framadate!306
2018-04-19 14:06:16 +02:00
m
3330d28f29
nettoyage de printemps
clean type test
2018-04-19 14:05:22 +02:00
m
fb7c5e2f12 fork-awesome enabled by default 2018-04-18 17:05:20 +02:00
m
cc0f572335 Better handle undefined config values
https://framagit.org/framasoft/framadate/issues/322
2018-04-18 16:49:02 +02:00
m
0b598b8e80 Merge branch 'develop' of https://framagit.org/framasoft/framadate into develop 2018-04-18 16:14:33 +02:00
Thomas Citharel
2627dc2ae6
Fixup for find_polls with empty localstorage
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-18 13:33:38 +02:00
Thomas Citharel
1a3081b58d Merge branch 'save-poll-localstorage' into 'develop'
Implement saving polls in localstorage and finding them

Closes #235

See merge request framasoft/framadate!283
2018-04-18 13:12:49 +02:00
Thomas Citharel
685d8b5e3d
Move VueJS template to x-template DOM and update strings
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-18 13:11:17 +02:00
Thomas Citharel
7a12d98943
Handle localstorage empty or failure properly
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-18 12:46:02 +02:00
Thomas Citharel
c1ea6ae2a8
Implement saving polls in localstorage and finding them
Also, this brings VueJS and MomentJS in Framadate, 🍾 🎉

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-18 12:46:01 +02:00
JMarlow
68d5b64180
Collecting Polled Users Emails
Modification de la BDD : ajout de la colonne mail dans la table vote

Modification de la BDD : ajout de la colonne mail dans la table vote (bis)

MAJ de VoteRepository : méthode insert et update

MAJ de PollService : méthodes updateVote, addVote et splitVotes

Modification studs.php, adminstuds.php et vote_table_date.tpl : OK pour l'ajout d'un vote avec nom + mail

Modification de vote_table_classic.tpl : OK pour l'ajout d'un vote avec nom + mail

Ajout d'un bouton enveloppe pour chaque colonne avec méthode de traitement pour test

Le numéro de la colonne est enfin récupéré correctement

Implémentation récupération des adresses mails des sondés intéressants dans adminstuds.php et appel d'un fichier display_mails.tpl qui affichent ces adresses mails.

Extension du traitement pour les sondages classic

Ajout récupération des adresses des non aussi, et ajouts de tests pour l'affichage

Changement des input type=text en type=email

Corrections automatiques pour passage pipelines

Corrections suite aux remarques sur la merge request

Corrections 2 suite aux remarques sur la merge request

Corrections 3 suite aux remarques sur la merge request

Modif BDD : ajout colonne collect_mail dans poll

Modif classes Form, PollRepository

Passage de la collecte des mails des sondés en fonctionalité optionnelle

Si la collecte de mail est activée, la saisie du mail est obligatoire

Ajout avertissements collect_mail + editableByAll dans création de sondage et tableaux de vote

Update create poll string and put a danger background on warning

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Translation strings updated and better position for the email public warning message

Also, a CSS tweak and cleanup

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

CS

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-18 12:34:08 +02:00
Thomas Citharel
6dd8fb1723 Merge branch 'feature/clean-demo-poll' into 'develop'
Feature/clean demo poll

See merge request framasoft/framadate!297
2018-04-18 11:13:27 +02:00
Luc Didry
b89622695e [i18n] Update translations 2018-04-17 11:56:52 +02:00
Luc Didry
7d9f5becec Add script to add a key/value in locale/en.json + put all scripts in scripts/ 2018-04-17 11:46:15 +02:00
m
5b2a40ac58 Merge remote-tracking branch 'framadate/develop' into develop 2018-04-16 07:32:46 +02:00
m
0f4587adf8 less often demo cleaning 2018-04-15 23:30:40 +02:00
Thomas Citharel
3878c3ce7c Merge branch 'fork-awesome' into 'develop'
Fork awesome

See merge request framasoft/framadate!300
2018-04-15 12:19:31 +02:00
Thomas Citharel
a004576592
Provide Fork-Awesome and add an option to disable it (should be useful on installations with Framanav)
Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Use fork-awesome minified css

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-15 12:17:35 +02:00
m
081fdae5f5 code style 2018-04-15 11:59:40 +02:00
Thomas Citharel
8528a7fe65
Update version to 1.2.0 on develop
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-15 11:41:03 +02:00
Thomas Citharel
c723c81697 Merge branch 'bug/error-on-empty-choices' into 'develop'
Bug/error on empty choices - 1.1.0

See merge request framasoft/framadate!298
2018-04-15 11:29:27 +02:00
m
afd83efd79
bug/error-on-empty-choices 2018-04-15 11:28:27 +02:00
m
00b871df5c nettoyage du sondage de démonstration
https://framagit.org/framasoft/framadate/issues/316
2018-04-14 08:48:24 +02:00
m
3b54590bde IC 2018-04-13 21:26:00 +02:00
m
6455f4be87 nettoyage du sondage de démonstration
https://framagit.org/framasoft/framadate/issues/316
2018-04-13 21:24:43 +02:00
Thomas Citharel
83ba53dffa Merge branch 'doc/deprecate-changelog' into 'develop'
Deprecate CHANGELOG.md

See merge request framasoft/framadate!296
2018-04-12 23:53:57 +02:00
framartin
94f7288446 deprecate CHANGELOG.md 2018-04-12 18:37:46 -03:00
m
f9c3cde701 Merge remote-tracking branch 'framadate/develop' into develop 2018-04-09 22:13:15 +02:00
187 changed files with 19246 additions and 9121 deletions

9
.gitignore vendored
View File

@ -8,9 +8,8 @@ nav
app/inc/config.php
vendor
cache/
tpl_c/*
!tpl_c/.gitkeep
.php-cs-fixer.cache
tpl_c/
.php_cs.cache
.zanata-cache/
# Temp files
@ -25,6 +24,4 @@ Thumbs.db
.project
.idea/
*.iml
#ics temp file
out.ics
test_database.sqlite

View File

@ -1,27 +1,37 @@
image: framasoft/framadate-ci:7.3-pdo_mysql
image: framasoft/framadate-ci
stages:
- test
- deploy
- beta
- funky
# Run php-cs-fixer and phpunit on all branches
test:
stage: test
script:
- composer install -o --no-interaction --no-progress --prefer-dist
- mkdir tpl_c
- php vendor/bin/php-cs-fixer fix --verbose --dry-run
- APP_ENV=test bin/doctrine migrations:migrate --no-interaction -vvv
- vendor/bin/phpunit --bootstrap app/tests/bootstrap.php --debug app/tests
image: framasoft/framadate-ci:${PHP_VERSION}-pdo_mysql
parallel:
matrix:
- PHP_VERSION:
- "7.3"
- "7.4"
- "8.0"
- "8.1"
cache:
paths:
- vendor/
# check-trad:
# stage: test
# allow_failure: true
# script:
# - if [ -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then echo "*** Unable to check if translations need to be pulled, exiting ***"; exit 1; fi
# - export ORIG=$(git diff-files --shortstat)
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make push-locales; fi
# - git status > /dev/null 2>&1
# - export CHANGES=$(git diff-files --shortstat)
# - if [[ $CHANGES != $ORIG ]]; then echo "*** There is changes in locales ***"; echo "*** You need to do `make pull-locales` in your repo ***"; exit 1; fi
# only:
# - develop
# Create artifacts on master
pages:
stage: deploy
@ -30,29 +40,14 @@ pages:
- git checkout ${latesttag}
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
- composer dump-autoload --optimize --no-dev --classmap-authoritative
- mkdir tpl_c
- mkdir framadate
- mv `ls -A | grep -v framadate` ./framadate
- echo $latesttag > framadate/VERSION
- find framadate/ -type d -exec chmod 750 {} \;
- find framadate/ -type f -exec chmod 640 {} \;
- rm -rf framadate/.git
- export RELEASE_ZIP="framadate-${CI_COMMIT_TAG}.zip"
- zip -r $RELEASE_ZIP framadate
- chmod -R 644 framadate/ && chmod -R 770 framadate/tpl_c/ && chmod -R 770 framadate/app/inc/
- zip -r latest.zip framadate
- mkdir .public
- cp $RELEASE_ZIP .public/latest.zip
- cp latest.zip .public
- mv .public public
- if [[ -z $GITLAB_API_TOKEN ]]; then exit; fi
- export PROJECT_API_URL="https://framagit.org/api/v4/projects/${CI_PROJECT_ID}"
- export DESCRIPTION_URL="${PROJECT_API_URL}/repository/tags/${CI_COMMIT_TAG}"
- export RELEASE_URL="${DESCRIPTION_URL}/release"
- 'export HEADER="Private-Token: ${GITLAB_API_TOKEN}"'
- export artifactUrl=$(curl -s --request POST --header "${HEADER}" --form "file=@${RELEASE_ZIP}" "${PROJECT_API_URL}/uploads" | jq .url)
- export artifactAbsoluteUrl="${CI_PROJECT_URL}${artifactUrl}"
- export description=$(curl -s --header "${HEADER}" "${DESCRIPTION_URL}" | jq .release.description | sed -e 's@"@@g')
- if [[ $description == 'null' ]]; then export METHOD="POST"; echo -e 'You can download the release zip here:'" [${RELEASE_ZIP}](${artifactAbsoluteUrl})" > /tmp/text; fi
- if [[ $description != 'null' ]]; then export METHOD="PUT"; echo -e "${description}\n\n"'You can download the release zip here:'" [${RELEASE_ZIP}](${artifactAbsoluteUrl})" > /tmp/text; fi
- curl -s --request $METHOD --data-urlencode "description@/tmp/text" --header "${HEADER}" "${RELEASE_URL}"
- curl -s --request POST --header "${HEADER}" --data name="${RELEASE_ZIP}" --data url="${artifactAbsoluteUrl}" "${PROJECT_API_URL}/releases/${CI_COMMIT_TAG}/assets/links"
artifacts:
paths:
- public
@ -60,3 +55,55 @@ pages:
- tags
except:
- (beta|alpha)
# Deploy on develop
beta:
stage: beta
script:
- git checkout develop
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
- composer dump-autoload --optimize --no-dev --classmap-authoritative
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make pull-locales; fi
- mkdir tpl_c
- mkdir .public
- cp -r * .public
- cp -r .git .public
- mv .public public
- mkdir "${HOME}/.ssh"
- chmod 700 "${HOME}/.ssh"
- if [ ! -z ${DEPLOYEMENT_KNOWN_HOSTS+x} ]; then echo -e "${DEPLOYEMENT_KNOWN_HOSTS}" > ${HOME}/.ssh/known_hosts; fi
- eval `ssh-agent -s`
- if [ ! -z ${BETA_KEY+x} ]; then ssh-add <(echo "${BETA_KEY}" | base64 --decode -i); fi
- if [ ! -z ${BETA_KEY+x} ]; then rsync -a --delete --exclude admin/.stdout.log --exclude admin/.htpasswd --exclude app/inc/config.php --exclude stats/ --exclude error/ public/ ${BETA_USER}@${DEPLOYEMENT_HOST}:../../web/; fi
only:
- develop
# Deploy on funky
funky:
stage: funky
script:
- git checkout funky
- composer install
- mkdir tpl_c
- mkdir .public
- cp -r * .public
- mv .public public
- mkdir "${HOME}/.ssh"
- chmod 700 "${HOME}/.ssh"
- if [ ! -z ${DEPLOYEMENT_KNOWN_HOSTS+x} ]; then echo -e "${DEPLOYEMENT_KNOWN_HOSTS}" > ${HOME}/.ssh/known_hosts; fi
- eval `ssh-agent -s`
- if [ ! -z ${DEPLOYEMENT_KEY+x} ]; then ssh-add <(echo "${DEPLOYEMENT_KEY}" | base64 --decode -i); fi
- if [ ! -z ${DEPLOYEMENT_KEY+x} ]; then rsync -a --delete --exclude admin/.stdout.log --exclude admin/.htpasswd --exclude app/inc/config.php --exclude stats/ --exclude error/ public/ ${DEPLOYEMENT_USER}@${DEPLOYEMENT_HOST}:../../web/; fi
only:
- funky
# Push new translations strings to https://trad.framasoft.org
# trads:
# stage: deploy
# image: framasoft/push-trad:latest
# script:
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
# - if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make push-locales; fi
# only:
# - develop

View File

@ -1,6 +1,6 @@
<?php
return (new PhpCsFixer\Config())
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([
'array_syntax' => [
@ -8,18 +8,16 @@ return (new PhpCsFixer\Config())
],
'combine_consecutive_unsets' => true,
'heredoc_to_nowdoc' => true,
'no_extra_blank_lines' => [
'tokens' => [
'break',
'continue',
'extra',
'return',
'throw',
'use',
'parenthesis_brace_block',
'square_brace_block',
'curly_brace_block'
]
'no_extra_consecutive_blank_lines' => [
'break',
'continue',
'extra',
'return',
'throw',
'use',
'parenthesis_brace_block',
'square_brace_block',
'curly_brace_block'
],
'no_unreachable_default_argument_value' => true,
'no_useless_else' => true,
@ -41,7 +39,7 @@ return (new PhpCsFixer\Config())
'vendor',
'var',
'web',
'tpl_c'
'tpl_c',
])
->in(__DIR__)
)

View File

@ -1,215 +1,9 @@
This changelog file is **deprecated**. For an up-to-date changelog, please check [the tags](https://framagit.org/framasoft/framadate/tags).
---------------------
# Changelog de framadate
## 1.1.19
23-12-2021
### Fixed
- Remove the X-Mailer header in e-mails, as this causes some email servers to see emails sent by Framadate as spam
## 1.1.18
20-12-2021
### Changed
- Dependency updates
- Replace abandonned SimpleMDE with EasyMDE fork
### Fixed
- Enforce the instance expiration limits when editing the poll expiration date once created, from poll admin
- Fixed some HTML markup validity
### Translations
- Fixed a missing french language key
- Enable Catalan language
## 1.1.17
18-10-2021
### Added
- Allow to export to ICS the best choices
### Changed
- Allow configuring AuthType for MailService
### Security
- Fix an XSS possibility in the result graph
## 1.1.16
22-03-2021
### Changed
- **Framadate now requires the `mbstring` PHP extension.** Make sure it's installed and activated before updating.
### Fixed
- Handle poll creator names being too long properly
## 1.1.15
22-03-2021
### Security
- Fixed cross-site scripting (XSS) attacks in poll description markdown preview. All administrators are encouraged to upgrade, especially if you have sensitive services and data on the same domain name.
This was reported by @martgil
https://framagit.org/framasoft/framadate/framadate/-/issues/546
## 1.1.14
08-03-2021
### Fixed
- Avoid error with a name too long https://framagit.org/framasoft/framadate/framadate/-/issues/530
## 1.1.13
08-03-2021
### Fixed
- Fixed error when closing a poll https://framagit.org/framasoft/framadate/framadate/-/issues/532
## 1.1.12
18-12-2020
### Changed
* Framadate now requires PHP 7.3
## 1.1.11
18-12-2020
### Fixed
- Fixed translations keys missing into emails https://framagit.org/framasoft/framadate/framadate/-/issues/463
### Translations
- Added Catalan translation
## 1.1.10
### Fixed
* Remove .git folder inside releases.
* Create releases through CI
## 1.1.9
### Fixed
- Fixes session issue https://framagit.org/framasoft/framadate/framadate/issues/255
- Fixes bug when editing column https://framagit.org/framasoft/framadate/framadate/issues/379
- Fix mail subject escaping https://framagit.org/framasoft/framadate/framadate/issues/375
## 1.1.8
### Fixed
- Stop creating `tpl_c` directory in releases and add a `.gitkeep`
- Show database connection issue details on installation panel
- Set the proper file rights on release packages
- Added `session.cookie_httponly = 1` to local php.ini file
## 1.1.7
### Fixed
- Fix issue with maximum number of participants https://framagit.org/framasoft/framadate/issues/353 (thanks to @lohmeyer for reporting it)
## 1.1.6
### Fixed
- Bump dependencies, including PHPMailer to version 6.x
- Fix an small issue with Smarty template
## 1.1.5
### Fixed
- Restrict custom poll URLs against app urls (thanks @mosterdt)
- Add a parameter to disable build-in font-awesome (thanks @mm)
- Fix an XSS security issue with time slots (thanks https://bitsoffreedom.nl for responsibly disclosing it).
## 1.1.4
### Fixed
* Add Fork-awesome, remove dependency to Font-Awesome Bootstrap CDN, add an option to disable it (https://framagit.org/framasoft/framadate/merge_requests/300 - @tcit)
## 1.1.3
### Fixed
* Fixing issue when no choice is selected introducted in https://framagit.org/framasoft/framadate/merge_requests/284 (https://framagit.org/framasoft/framadate/merge_requests/298 - @mm)
## 1.1.2
### Fixed
- Use Parsedown's Safe Mode
## 1.1.1
### Bug fixes
- Send email with correct vote address (thanks to @lohmeyer for finding it)
## 1.1.0
### Warning
**Framadate now requires PHP 5.6** to be used (it should still work under 5.4 but will not be supported anymore).
### Features
- Markdown editor for descriptions ! (@Antonin)
- Adding a maximum participants number (@SuperNach0)
- Allow setting SMTP config (Simon LEBLANC)
- Allow admins to give the vote link back to the voters (@mm, @tcit)
- Sending voters emails to remind themselves their voting url now works (@mm)
### Enhancements
- UI improvements for responsive design (@marjolaine-v)
- Better coherence for visible results and passwords (@TDavid)
- Added an edit button on the right when too many options (@SuperNach0)
- Emails with international characters are now allowed (added an unit test) (@mm)
### Translations
**New strings are available, don't hesitate to head to <https://trad.framasoft.org/zanata/project/view/framadate> to translate them into your language !**
### Fixed
- Reschedule function (https://framagit.org/framasoft/framadate/issues/203) (@TDavid)
- lang attribute must be a valid IETF language tag (@Rudloff)
- Fix datepicker js locale file path
- Fix everyone can always vote #267
- Fix MySQL error with `NO_ZERO_DATE` #224
- SimpleMDE Markdown Editor has been updated the latest version to remove console.log calls
- Fix width of `if need be` vote option and missing parenthesis
- Remove autocomplete on date fields
- Various fixes for value max error handling
- New error strings for bad formatted inputs (admin name, wrong value max option)
- Email is now a email field (better for virtual keyboards) and is html required as well as title
- Advanced settings for poll are now opened if there's error within them
- css fixes for pictures inside columns, and little space between editor and description text area (@marjolaine-v)
- released zip files now have proper chmod rights (@tcit)
- Best choices now work properly when there's no votes (@mm)
- Don't allow an existing name when updating a vote (@mm)
- Keep vote selections when there's an error on the name (@mm)
- Add a message « Your poll has been created » at the end of the poll form process (@mm)
### Documentation
- Move everything to wiki, translate everything to English
### Technical
- Continuous Integration handles the release process
- Translations with Zanata : https://trad.framasoft.org/zanata/project/view/framadate (@luc)
- Style fixes with PHP-CS
- Libraries updated
- Improved a few docs
- Use own Framadate Docker Image for CI
- https://beta.framadate.org now gets the latest translations for each deployment (@luc)
- A CI job tells if translations strings are up-to-date (@luc)
## 1.0.3
- Corrections de wording (fr / en)
## 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
@ -315,7 +109,7 @@
- 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
@ -349,7 +143,7 @@
## 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 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
@ -415,7 +209,7 @@
- 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,
@ -426,7 +220,7 @@
- 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",

7
CONTRIBUTING.md Normal file
View 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.

View File

@ -1,12 +1,22 @@
locales:
json2po -P -i locale/en.json -t locale/en.json -o po/framadate.pot
scripts/locales.sh
push-locales: locales
zanata-cli -q -B push
zanata-cli -q -B push --errors
pull-locales:
zanata-cli -q -B pull --min-doc-percent 50
./.po2json.sh
scripts/po2json.sh
stats-locales:
zanata-cli -q stats
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
%:
@:

View File

@ -4,28 +4,26 @@
![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 nest nécessaire.
**Framadate is now in maintenance mode.** [Read more](https://framagit.org/framasoft/framadate/framadate/-/issues/545#note_920869)
---
# Installation
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/framadate/-/wikis/home>
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/wikis/home>
# Contribute
## Code
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/coding>
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/coding>
# Traductions
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/translating>
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/translating>
# Used libraries
* PHP [PHP 7.3](http://php.net)
* PHP [PHP 5.6](http://php.net)
* Templating [Smarty](http://www.smarty.net/),
* I18N [o80-i18n](https://framagit.org/framasoft/framadate/o80-i18nn)
* Database: MySQL or MariaDB.
* I18N [o80-i18n](https://github.com/olivierperez/o80-i18n)
* Database: PostgreSQL ou [MySQL 5.5](https://dev.mysql.com/downloads/mysql/5.5.html)
---

View File

@ -40,9 +40,9 @@ $is_admin = false;
/*----------*/
$logService = new LogService();
$pollService = new PollService($logService);
$pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
$notificationService = new NotificationService($mailService);
$securityService = new SecurityService();
@ -63,7 +63,7 @@ if (!empty($_POST['poll_admin'])) {
if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
} else if (!$is_admin && !$securityService->canAccessPoll($poll)) {
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) {
$message = new Message('error', __('Password', 'Wrong password'));
} else {
$name = $inputService->filterName($_POST['name']);
@ -88,10 +88,8 @@ if (!$poll) {
$smarty->error_reporting = E_ALL & ~E_NOTICE;
$smarty->assign('comments', $comments);
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$comments_html = $smarty->fetch('part/comments_list.tpl');
$response = ['result' => $result, 'message' => $message, 'comments' => $comments_html];
echo json_encode($response, JSON_THROW_ON_ERROR);
echo json_encode($response);

View File

@ -28,8 +28,8 @@ include_once __DIR__ . '/../app/inc/init.php';
$logService = new LogService();
$sessionService = new SessionService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
$pollService = new PollService($connect, $logService);
$result = false;
$message = null;
@ -45,7 +45,7 @@ if (!empty($_POST['poll'])) {
$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 ($config['use_smtp'] === false || is_null($poll) || is_null($token) || is_null($token_form_value)
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 is going wrong...'));
}
@ -91,4 +91,4 @@ $smarty->error_reporting = E_ALL & ~E_NOTICE;
$response = ['result' => $result, 'message' => $message];
echo json_encode($response, JSON_THROW_ON_ERROR);
echo json_encode($response);

View File

@ -20,7 +20,7 @@
use Framadate\Message;
use Framadate\Utils;
const ROOT_DIR = __DIR__ . '/../';
define('ROOT_DIR', __DIR__ . '/../');
/**
* Checking for missing vendors.
@ -46,7 +46,6 @@ $ALLOWED_LANGUAGES = [
'de' => 'Deutsch',
'it' => 'Italiano',
'br' => 'Brezhoneg',
'ca' => 'Català',
];
const DEFAULT_LANGUAGE = 'en';
require_once ROOT_DIR . 'app/inc/i18n.php';
@ -58,7 +57,7 @@ require_once ROOT_DIR . 'app/inc/i18n.php';
* @param Message $b
* @return int
*/
function compareCheckMessage(Message $a, Message $b): int
function compareCheckMessage(Message $a, Message $b)
{
$values = [
'danger' => 0,
@ -90,7 +89,7 @@ $conf_filename = $inc_directory . 'config.php';
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.', PHP_VERSION, PHP_NEEDED_VERSION));
$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
@ -100,13 +99,6 @@ if (extension_loaded('intl')) {
$messages[] = new Message('danger', __('Check','You need to enable the PHP Intl extension.'));
}
// mbstring extension
if (extension_loaded('mbstring')) {
$messages[] = new Message('info', __('Check','PHP mbstring extension is enabled.'));
} else {
$messages[] = new Message('danger', __('Check','You need to enable the PHP mbstring 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)));
@ -120,7 +112,7 @@ if (!file_exists(ROOT_DIR . COMPILE_DIR)) {
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));
$messages[] = new Message('info', __('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));
}
@ -183,11 +175,11 @@ usort($messages, 'compareCheckMessage');
<body>
<div class="container ombre">
<div class="row">
<form method="get" class="hidden-print">
<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 the language')?>" >
<?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?>
<option lang="fr" <?php if (strpos($lang_key, $locale) === 0) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
<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">

29
admin/cleanDemo.php Normal file
View 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();

View File

@ -1,91 +0,0 @@
<?php
/**
* Copyright 2018 Christian P. MOMON <cmomon@april.org>
*
* 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
*
*/
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\PurgeService;
use Framadate\Services\SecurityService;
// /////////////////////////////////////////////////
// ////////// include_once __DIR__ . '/../app/inc/init.php';
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
define('ROOT_DIR', __DIR__ . '/../');
// Autoloading of dependencies with Composer
require_once ROOT_DIR . '/vendor/autoload.php';
require_once ROOT_DIR . '/vendor/o80/i18n/src/shortcuts.php';
if (ini_get('date.timezone') === '') {
date_default_timezone_set('Europe/Paris');
}
require_once ROOT_DIR . '/app/inc/constants.php';
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
if (is_file(CONF_FILENAME)) {
@include_once CONF_FILENAME;
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
RepositoryFactory::init($connect);
$err = 0;
} else {
define('NOMAPPLICATION', 'Framadate');
define('DEFAULT_LANGUAGE', 'fr');
define('IMAGE_TITRE', 'images/logo-framadate.png');
define('LOG_FILE', 'admin/stdout.log');
}
require_once ROOT_DIR . '/app/inc/i18n.php';
// /////////////////////////////////////////////////
/* Variables */
/* --------- */
/* Services */
/*----------*/
$logService = new LogService();
$purgeService = new PurgeService($connect, $logService);
$securityService = new SecurityService();
$inputService = new InputService();
/* Action */
/* ------ */
$logService->log('CRON PURGE', 'Cron purge starting…');
$ended = false;
$iterationCount = 0;
$totalCount = 0;
while (!$ended)
{
$count = $purgeService->purgeOldPolls();
$logService->log('CRON PURGE', 'count='.$count);
if ($count == 0)
{
$ended = true;
}
else
{
$iterationCount += 1;
$totalCount += $count;
}
}
$logService->log('CRON PURGE', 'Purged '.$totalCount.' poll(s) in '.$iterationCount.' iterations.');
$logService->log('CRON PURGE', 'Cron purge done.');
/* PAGE */
/* ---- */
echo date("Y-m-d H:i:s").": cron purge done.\n"
?>

View File

@ -29,7 +29,6 @@ if (is_file(CONF_FILENAME)) {
$error = null;
$installService = new InstallService();
$result['details'] = null;
if (!empty($_POST)) {
$installService->updateFields($_POST);
@ -38,13 +37,11 @@ if (!empty($_POST)) {
if ($result['status'] === 'OK') {
header(('Location: ' . Utils::get_server_name() . 'admin/migration.php'));
exit;
}
$error = __('Error', $result['code']);
}
$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');
$smarty->display('admin/install.tpl');

View File

@ -17,113 +17,64 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\FramaDB;
use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9;
use Framadate\Migration\AddColumn_receiveNewComments_For_0_9;
use Framadate\Migration\AddColumn_uniqId_In_vote_For_0_9;
use Framadate\Migration\AddColumn_ValueMax_In_poll_For_1_1;
use Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9;
use Framadate\Migration\Alter_Comment_table_adding_date;
use Framadate\Migration\Alter_Comment_table_for_name_length;
use Framadate\Migration\Fix_MySQL_No_Zero_Date;
use Framadate\Migration\From_0_0_to_0_8_Migration;
use Framadate\Migration\From_0_8_to_0_9_Migration;
use Framadate\Migration\Generate_uniqId_for_old_votes;
use Framadate\Migration\Increase_pollId_size;
use Framadate\Migration\Migration;
use Framadate\Migration\RPadVotes_from_0_8;
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;
include_once __DIR__ . '/../app/inc/init.php';
require_once __DIR__ . '/../app/inc/init.php';
set_time_limit(300);
class MigrationLogger {
private $log;
// List a Migration sub classes to execute
$migrations = [
new From_0_0_to_0_8_Migration(),
new From_0_8_to_0_9_Migration(),
new AddColumn_receiveNewComments_For_0_9(),
new AddColumn_uniqId_In_vote_For_0_9(),
new AddColumn_hidden_In_poll_For_0_9(),
new AddColumn_ValueMax_In_poll_For_1_1(),
new Generate_uniqId_for_old_votes(),
new RPadVotes_from_0_8(),
new Alter_Comment_table_for_name_length(),
new Alter_Comment_table_adding_date(),
new AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9(),
new Increase_pollId_size(),
new AddColumn_ValueMax_In_poll_For_1_1(),
new Fix_MySQL_No_Zero_Date(),
];
// ---------------------------------------
// Check if MIGRATION_TABLE already exists
/** @var FramaDB $connect */
$tables = $connect->allTables();
$pdo = $connect->getPDO();
$prefixedMigrationTable = Utils::table(MIGRATION_TABLE);
if (!in_array($prefixedMigrationTable, $tables, true)) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . $prefixedMigrationTable . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` TEXT NOT NULL,
`execute_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8;');
}
$selectStmt = $pdo->prepare('SELECT id FROM `' . $prefixedMigrationTable . '` WHERE name=?');
$insertStmt = $pdo->prepare('INSERT INTO `' . $prefixedMigrationTable . '` (name) VALUES (?)');
$countSucceeded = 0;
$countFailed = 0;
$countSkipped = 0;
// Loop on every Migration sub classes
$success = [];
$fail = [];
foreach ($migrations as $migration) {
$className = get_class($migration);
// Check if $className is a Migration sub class
if (!$migration instanceof Migration) {
$smarty->assign('error', 'The class ' . $className . ' is not a sub class of Framadate\\Migration\\Migration.');
$smarty->display('error.tpl');
exit;
public function __construct()
{
$this->log = '';
}
// Check if the Migration is already executed
$selectStmt->execute([$className]);
$executed = $selectStmt->rowCount();
$selectStmt->closeCursor();
public function addLine($message)
{
$this->log .= $message . "\n";
}
if (!$executed && $migration->preCondition($pdo)) {
$migration->execute($pdo);
if ($insertStmt->execute([$className])) {
$countSucceeded++;
$success[] = $migration->description();
} else {
$countFailed++;
$fail[] = $migration->description();
}
} else {
$countSkipped++;
public function getLog()
{
return $this->log;
}
}
$countTotal = $countSucceeded + $countFailed + $countSkipped;
$executing = false;
$migration = null;
$output = '';
$smarty->assign('success', $success);
$smarty->assign('fail', $fail);
if (isset($_POST['execute'])) {
$executing = true;
}
$smarty->assign('countSucceeded', $countSucceeded);
$smarty->assign('countFailed', $countFailed);
$smarty->assign('countSkipped', $countSkipped);
$smarty->assign('countTotal', $countTotal);
$smarty->assign('time', $total_time = round((microtime(true)-$_SERVER['REQUEST_TIME_FLOAT']), 4));
$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');

View File

@ -50,7 +50,7 @@ $poll_to_delete = null;
/*----------*/
$logService = new LogService();
$pollService = new PollService($logService);
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$superAdminService = new SuperAdminService();
$securityService = new SecurityService();

View File

@ -34,14 +34,14 @@ $message = null;
/*----------*/
$logService = new LogService();
$purgeService = new PurgeService($logService);
$purgeService = new PurgeService($connect, $logService);
$securityService = new SecurityService();
$inputService = new InputService();
/* POST */
/*-----*/
$action = $inputService->filterName($_POST['action'] ?? null);
$action = $inputService->filterName(isset($_POST['action']) ? $_POST['action'] : null);
/* PAGE */
/* ---- */
@ -57,4 +57,4 @@ $smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->assign('title', __('Admin', 'Purge'));
$smarty->display('admin/purge.tpl');
$smarty->display('admin/purge.tpl');

View File

@ -47,10 +47,10 @@ $editingVoteId = 0;
/*----------*/
$logService = new LogService();
$pollService = new PollService($logService);
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
$notificationService = new NotificationService($mailService);
$sessionService = new SessionService();
@ -113,7 +113,7 @@ if (isset($_POST['update_poll_info'])) {
$updated = true;
}
} elseif ($field === 'rules') {
$rules = (int) strip_tags($_POST['rules']);
$rules = strip_tags($_POST['rules']);
switch ($rules) {
case 0:
$poll->active = false;
@ -137,41 +137,38 @@ if (isset($_POST['update_poll_info'])) {
break;
}
} elseif ($field === 'expiration_date') {
$givenExpirationDate = $inputService->parseDate($_POST['expiration_date']);
$expiration_date = $inputService->validateDate($givenExpirationDate, $pollService->minExpiryDate(), $pollService->maxExpiryDate());
if ($poll->end_date !== $expiration_date->format('Y-m-d H:i:s')) {
$poll->end_date = $expiration_date->format('Y-m-d H:i:s');
$expiration_date = $inputService->filterDate($_POST['expiration_date']);
if ($expiration_date) {
$poll->end_date = $expiration_date;
$updated = true;
}
} elseif ($field === 'name') {
$admin_name = $_POST['name'];
$admin_name = mb_substr($admin_name, 0, 32);
$admin_name = $inputService->filterName($admin_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']);
$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']);
$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 = $_POST['password'] ?? null;
$password = isset($_POST['password']) ? $_POST['password'] : null;
/**
* Did the user choose results to be publicly visible ?
*/
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) && $inputService->filterBoolean($_POST['resultsPubliclyVisible']);
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false;
/**
* If there's one, save the password
*/
@ -222,6 +219,11 @@ $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']);
@ -236,7 +238,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message === null) {
// Update vote
try {
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash, $mail);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote updated'));
} else {
@ -252,6 +254,11 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
}
} 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']);
@ -265,7 +272,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message === null) {
// Add vote
try {
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash, $mail);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote added'));
} else {
@ -401,6 +408,37 @@ if (isset($_GET['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
// -------------------------------

View 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;
}
}

View File

@ -24,34 +24,34 @@ 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): void
public function addSlot($slot)
{
$this->slots[] = $slot;
}
public function getName(): string
public function getName()
{
return $this->name;
}
public function getSlots(): array
public function getSlots()
{
return $this->slots;
}
public static function compare(Choice $a, Choice $b): int
static function compare(Choice $a, Choice $b)
{
return strcmp($a->name, $b->name);
}

View 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;
}

View File

@ -23,13 +23,14 @@ 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;
public const NOT_EDITABLE = 0;
public const EDITABLE_BY_ALL = 1;
public const EDITABLE_BY_OWN = 2;
}
const NOT_EDITABLE = 0;
const EDITABLE_BY_ALL = 1;
const EDITABLE_BY_OWN = 2;
}

View File

@ -2,4 +2,6 @@
namespace Framadate\Exception;
class AlreadyExistsException extends \Exception {
function __construct() {
}
}

View File

@ -2,4 +2,6 @@
namespace Framadate\Exception;
class ConcurrentEditionException extends \Exception {
function __construct() {
}
}

View File

@ -7,4 +7,6 @@ namespace Framadate\Exception;
* 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() {
}
}

View File

@ -2,4 +2,6 @@
namespace Framadate\Exception;
class MomentAlreadyExistsException extends \Exception {
function __construct() {
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Framadate\Exception;
/**
* Class PollNotFoundException
*
* Thrown when a poll isn't found in a critical process
*/
class PollNotFoundException extends \Exception {
}

View File

@ -32,7 +32,7 @@ class Form
/**
* Tells if users can modify their choices.
* @var int
* @var \Framadate\Editable
*/
public $editable;
@ -82,22 +82,29 @@ class Form
*/
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(){
public function __construct()
{
$this->editable = Editable::EDITABLE_BY_ALL;
$this->collect_users_mail = CollectMail::NO_COLLECT;
$this->clearChoices();
}
public function clearChoices(): void
{
public function clearChoices() {
$this->choices = [];
}
public function addChoice(Choice $choice): void
public function addChoice(Choice $choice)
{
$this->choices[] = $choice;
}
@ -107,8 +114,8 @@ class Form
return $this->choices;
}
public function sortChoices(): void
public function sortChoices()
{
usort($this->choices, [Choice::class, 'compare']);
usort($this->choices, ['Framadate\Choice', 'compare']);
}
}

View File

@ -1,95 +0,0 @@
<?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 PDO;
class FramaDB {
/**
* PDO Object, connection to database.
* @var PDO
*/
private $pdo;
public function __construct(string $connection_string, string $user, string $password) {
$this->pdo = new PDO($connection_string, $user, $password);
$this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
/**
* @return PDO Connection to database
*/
public function getPDO(): PDO
{
return $this->pdo;
}
/**
* Find all tables in database.
*
* @return array The array of table names
*/
public function allTables(): array
{
return $this->pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
}
/**
* @return \PDOStatement|false
*/
public function prepare(string $sql) {
return $this->pdo->prepare($sql);
}
public function beginTransaction(): void
{
$this->pdo->beginTransaction();
}
public function commit(): void
{
$this->pdo->commit();
}
public function rollback(): void
{
$this->pdo->rollback();
}
public function errorCode(): ?string {
return $this->pdo->errorCode();
}
public function errorInfo(): array
{
return $this->pdo->errorInfo();
}
/**
* @return \PDOStatement|false
*/
public function query($sql) {
return $this->pdo->query($sql);
}
public function lastInsertId(): string {
return $this->pdo->lastInsertId();
}
}

View File

@ -1,37 +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;
<?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;
public 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;
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;
}
}
}

View File

@ -1,80 +0,0 @@
<?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\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field hidden on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_hidden_In_poll_For_0_9 implements Migration {
public function __construct() {
}
/**
* 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(): string
{
return 'Add column "hidden" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"');
}
}

View File

@ -1,81 +0,0 @@
<?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\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field receiveNewComments on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_receiveNewComments_For_0_9 implements Migration {
public function __construct() {
}
/**
* 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(): string
{
return 'Add column "receiveNewComments" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'
AFTER `receiveNewVotes`');
}
}

View File

@ -1,79 +0,0 @@
<?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\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field uniqId on the vote table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
public function __construct() {
}
/**
* 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(): string {
return 'Add column "uniqId" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '`
ADD `uniqId` CHAR(16) NOT NULL
AFTER `id`,
ADD INDEX (`uniqId`) ;');
}
}

View File

@ -1,78 +0,0 @@
<?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\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the fields password_hash and results_publicly_visible on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 implements Migration {
public function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description(): string {
return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL ,
ADD `results_publicly_visible` TINYINT(1) NULL DEFAULT NULL');
}
}

View File

@ -1,72 +0,0 @@
<?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\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration sets Poll.end_date to NULL by default
*
* @package Framadate\Migration
* @version 1.1
*/
class Fix_MySQL_No_Zero_Date implements Migration {
public function __construct() {
}
/**
* 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(): string {
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 PDO $pdo The connection to database
* @return bool true if the Migration should be executed.
*/
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->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);
$driver_name = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
return $default !== null && $driver_name === 'mysql';
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool if the execution succeeded
*/
public function execute(PDO $pdo): bool {
$pdo->exec('ALTER TABLE ' . Utils::table('poll') . ' MODIFY end_date TIMESTAMP NULL DEFAULT NULL;');
return true;
}
}

View File

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

View File

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

View File

@ -1,82 +0,0 @@
<?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\Migration;
use Framadate\Security\Token;
use Framadate\Utils;
use PDO;
/**
* This migration generate uniqId for all legacy votes.
*
* @package Framadate\Migration
* @version 0.9
*/
class Generate_uniqId_for_old_votes implements Migration {
public function __construct() {
}
public function description(): string {
return 'Generate "uniqId" in "vote" table for all legacy votes';
}
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This methode is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
public function execute(PDO $pdo): bool {
$pdo->beginTransaction();
$this->generateUniqIdsForEmptyOnes($pdo);
$pdo->commit();
return true;
}
private function generateUniqIdsForEmptyOnes(PDO $pdo): void
{
$select = $pdo->query('
SELECT `id`
FROM `' . Utils::table('vote') . '`
WHERE `uniqid` = \'\'');
$update = $pdo->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
]);
}
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
use PDO;
class Increase_pollId_size implements Migration {
public function __construct() {
}
/**
* 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(): string {
return 'Increase the size of id column in poll table';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed
*/
public function preCondition(PDO $pdo): bool {
return true;
}
/**
* This methode is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true if the execution succeeded
*/
public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo);
$this->alterPollTable($pdo);
$this->alterSlotTable($pdo);
$this->alterVoteTable($pdo);
return true;
}
private function alterCommentTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
private function alterSlotTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('slot') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
private function alterVoteTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
}

View File

@ -1,67 +0,0 @@
<?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 Raphael 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 Raphael DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
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 RPadVotes_from_0_8 implements Migration {
public function description(): string {
return 'RPad votes from version 0.8.';
}
public function preCondition(\PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
public function execute(\PDO $pdo): bool {
$pdo->beginTransaction();
$this->rpadVotes($pdo);
$pdo->commit();
return true;
}
private function rpadVotes(\PDO $pdo): void
{
$pdo->exec('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');
}
}

View 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');
}
}

View 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');
}
}

View 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));
}
}

View 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');
}
}

View 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');
}
}

View 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');
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/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.
}
}

View 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.
}
}

View File

@ -16,10 +16,12 @@
* 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\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Framadate\AbstractMigration;
use Framadate\Utils;
use PDO;
/**
* This migration alter the comment table to set a length to the name column.
@ -27,46 +29,43 @@ use PDO;
* @package Framadate\Migration
* @version 1.0
*/
class Alter_Comment_table_for_name_length implements Migration {
public function __construct() {
}
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(): string {
public function description()
{
return 'Alter the comment table to set a length to the name column.';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
public function preCondition(PDO $pdo): bool {
return true;
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]);
}
/**
* This methode is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo);
return true;
}
private function alterCommentTable(PDO $pdo): void
public function down(Schema $schema)
{
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;');
$commentTable = $schema->getTable(Utils::table('comment'));
$commentTable->changeColumn('name', ['type' => Type::getType('string')]);
}
}

View File

@ -16,10 +16,11 @@
* 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\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
use PDO;
/**
* This migration alter the comment table to add a date column.
@ -27,46 +28,42 @@ use PDO;
* @package Framadate\Migration
* @version 1.0
*/
class Alter_Comment_table_adding_date implements Migration {
public function __construct() {
}
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():string {
public function description()
{
return 'Alter the comment table to add a date column.';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function preCondition(PDO $pdo): bool {
return true;
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]);
}
/**
* This methode is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo);
return true;
}
private function alterCommentTable(PDO $pdo): void
public function down(Schema $schema)
{
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;');
$commentTable = $schema->getTable(Utils::table('comment'));
$commentTable->dropColumn('comment');
}
}

View 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');
}
}

View 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.
}
}

View File

@ -16,10 +16,11 @@
* 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\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field Value_Max on the poll table.
@ -27,46 +28,38 @@ use PDO;
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
public function __construct() {
}
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():string {
public function description()
{
return 'Add column "ValueMax" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\DBALException
*/
public function preCondition(PDO $pdo): bool {
return true;
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]);
}
/**
* This method is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(PDO $pdo): void
public function down(Schema $schema)
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `ValueMax` TINYINT NULL;');
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->dropColumn('ValueMax');
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/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
}
}

View 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');
}
}

View 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');
}
}

View File

@ -16,32 +16,45 @@
* 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\Migration;
namespace DoctrineMigrations;
use PDO;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
interface Migration {
/**
* 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(): string;
public function description()
{
return 'Remove the old migration table';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
public function preCondition(PDO $pdo): bool;
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));
}
/**
* This methode is called only one time in the migration page.
*
* @param PDO $pdo The connection to database
* @return bool true if the execution succeeded
* @param Schema $schema
*/
public function execute(PDO $pdo): bool;
public function down(Schema $schema)
{
// No need to recreate legacy migration table
}
}

View 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'
);
}
}
}

View File

@ -1,52 +1,69 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\Connection;
abstract class AbstractRepository {
/**
* @var FramaDB
* @var Connection
*/
private $connect;
protected $connect;
/**
* PollRepository constructor.
* @param FramaDB $connect
* @param Connection $connect
*/
public function __construct(FramaDB $connect) {
public function __construct(Connection $connect) {
$this->connect = $connect;
$this->connect->setFetchMode(\PDO::FETCH_OBJ);
}
public function beginTransaction(): void
public function beginTransaction()
{
$this->connect->beginTransaction();
}
public function commit(): void
/**
* @throws \Doctrine\DBAL\ConnectionException
*/
public function commit()
{
$this->connect->commit();
}
public function rollback(): void
/**
* @throws \Doctrine\DBAL\ConnectionException
*/
public function rollback()
{
$this->connect->rollback();
}
/**
* @return \PDOStatement|false
* @param string $sql
* @throws \Doctrine\DBAL\DBALException
* @return bool|\Doctrine\DBAL\Driver\Statement|\PDOStatement
*/
public function prepare(string $sql) {
public function prepare($sql)
{
return $this->connect->prepare($sql);
}
/**
* @return \PDOStatement|false
* @param string $sql
* @throws \Doctrine\DBAL\DBALException
* @return bool|\Doctrine\DBAL\Driver\Statement|\PDOStatement
*/
public function query($sql) {
public function query($sql)
{
return $this->connect->query($sql);
}
public function lastInsertId(): string {
/**
* @return string
*/
public function lastInsertId()
{
return $this->connect->lastInsertId();
}
}

View File

@ -5,10 +5,12 @@ use Framadate\Utils;
class CommentRepository extends AbstractRepository {
/**
* @return array|false
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return array
*/
public function findAllByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
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();
@ -17,41 +19,49 @@ class CommentRepository extends AbstractRepository {
/**
* Insert a new comment.
*
* @param string $poll_id
* @param string $name
* @param string $comment
* @param $poll_id
* @param $name
* @param $comment
* @return bool
*/
public function insert(string $poll_id, string $name, string $comment): bool
function insert($poll_id, $name, $comment)
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $name, $comment]);
return $this->connect->insert(Utils::table('comment'), ['poll_id' => $poll_id, 'name' => $name, 'comment' => $comment]) > 0;
}
public function deleteById(string $poll_id, int $comment_id): bool
/**
* @param $poll_id
* @param $comment_id
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
* @return bool
*/
function deleteById($poll_id, $comment_id)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$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 string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded.
* @param $poll_id int The ID of the given poll.
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
* @return bool true if action succeeded.
*/
public function deleteByPollId(string $poll_id): ?bool
function deleteByPollId($poll_id)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
return $this->connect->delete(Utils::table('comment'), ['poll_id' => $poll_id]) > 0;
}
public function exists(string $poll_id, string $name, string $comment): bool
/**
* @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 = $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;

View File

@ -1,81 +1,133 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Framadate\Utils;
use PDO;
class PollRepository extends AbstractRepository {
public function insertPoll(string $poll_id, string $admin_poll_id, $form): void
/**
* @param $poll_id
* @param $admin_poll_id
* @param $form
*/
public function insertPoll($poll_id, $admin_poll_id, $form)
{
$sql = 'INSERT INTO `' . Utils::table('poll') . '`
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible,ValueMax)
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?)';
$prepared = $this->prepare($sql);
$prepared->execute([$poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0, $form->receiveNewVotes ? 1 : 0, $form->receiveNewComments ? 1 : 0, $form->hidden ? 1 : 0, $form->password_hash, $form->results_publicly_visible ? 1 : 0,$form->ValueMax]);
$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,
]);
}
public function findById(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
/**
* @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]);
$prepared->execute([$poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
return $poll;
}
public function findByAdminId(string $admin_poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
/**
* @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]);
$prepared->execute([$admin_poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
return $poll;
}
public function existsById(string $poll_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?');
/**
* @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;
}
public function existsByAdminId(string $admin_poll_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
/**
* @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;
}
public function update($poll): bool
/**
* @param $poll
* @return bool
*/
public function update($poll)
{
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=?, password_hash=?, results_publicly_visible=? WHERE id = ?');
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active ? 1 : 0, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]);
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;
}
public function deleteById($poll_id): bool
/**
* @param $poll_id
* @throws \Doctrine\DBAL\Exception\InvalidArgumentException
* @return bool
*/
public function deleteById($poll_id)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
return $prepared->execute([$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(): array
public function findOldPolls()
{
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE DATE_ADD(`end_date`, INTERVAL ' . PURGE_DELAY . ' DAY) < NOW() AND `end_date` != 0 LIMIT 20');
$prepared->execute([]);
$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();
}
@ -86,15 +138,16 @@ class PollRepository extends AbstractRepository {
* @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(array $search, int $start, int $limit): array
{
public function findAll($search, $start, $limit) {
// Polls
$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 = "";
$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 = [];
@ -117,7 +170,7 @@ class PollRepository extends AbstractRepository {
}
$request .= " AND p.$columnName LIKE :$searchKey";
$values[$searchKey] = "%$search[$searchKey]%";
$values[$searchKey] = "%{$search[$searchKey]}%";
}
$request .= " ORDER BY p.title ASC";
@ -141,11 +194,11 @@ class PollRepository extends AbstractRepository {
* 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(string $mail): array
{
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
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();
@ -154,15 +207,15 @@ class PollRepository extends AbstractRepository {
/**
* Get the total number of polls in database.
*
* @param array|null $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @throws \Doctrine\DBAL\DBALException
* @return int The number of polls
*/
public function count(array $search = null): int
{
public function count($search = null) {
// Total count
$prepared = $this->prepare('
SELECT count(1) nb
FROM `' . Utils::table('poll') . '` p
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)
@ -176,7 +229,13 @@ SELECT count(1) nb
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
$prepared->execute();
$count = $prepared->fetch();
return $prepared->fetch()->nb;
/*echo '---';
print_r($count);
echo '---';
exit;*/
return $count->nb;
}
}

View File

@ -18,7 +18,7 @@
*/
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\Connection;
class RepositoryFactory {
private static $connect;
@ -29,17 +29,16 @@ class RepositoryFactory {
private static $commentRepository;
/**
* @param FramaDB $connect
* @param Connection $connect
*/
public static function init(FramaDB $connect): void {
static function init(Connection $connect) {
self::$connect = $connect;
}
/**
* @return PollRepository The singleton of PollRepository
*/
public static function pollRepository(): PollRepository
{
static function pollRepository() {
if (self::$pollRepository === null) {
self::$pollRepository = new PollRepository(self::$connect);
}
@ -50,8 +49,7 @@ class RepositoryFactory {
/**
* @return SlotRepository The singleton of SlotRepository
*/
public static function slotRepository(): SlotRepository
{
static function slotRepository() {
if (self::$slotRepository === null) {
self::$slotRepository = new SlotRepository(self::$connect);
}
@ -62,8 +60,7 @@ class RepositoryFactory {
/**
* @return VoteRepository The singleton of VoteRepository
*/
public static function voteRepository(): VoteRepository
{
static function voteRepository() {
if (self::$voteRepository === null) {
self::$voteRepository = new VoteRepository(self::$connect);
}
@ -74,8 +71,7 @@ class RepositoryFactory {
/**
* @return CommentRepository The singleton of CommentRepository
*/
public static function commentRepository(): CommentRepository
{
static function commentRepository() {
if (self::$commentRepository === null) {
self::$commentRepository = new CommentRepository(self::$connect);
}

View File

@ -4,37 +4,35 @@
* 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 Raphael DROZ
* 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
* 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 Raphael DROZ
* 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\FramaDB;
use Framadate\Choice;
use Framadate\Utils;
class SlotRepository extends AbstractRepository {
/**
* Insert a bulk of slots.
*
* @param string $poll_id
* @param int $poll_id
* @param array $choices
*/
public function insertSlots(string $poll_id, array $choices): void
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
public function insertSlots($poll_id, $choices) {
foreach ($choices as $choice) {
/** @var Choice $choice */
// We prepared the slots (joined by comas)
$joinedSlots = '';
$joinedSlots = null;
$first = true;
foreach ($choice->getSlots() as $slot) {
if ($first) {
@ -46,19 +44,22 @@ class SlotRepository extends AbstractRepository {
}
// We execute the insertion
if (empty($joinedSlots)) {
$prepared->execute([$poll_id, $choice->getName(), null]);
} else {
$prepared->execute([$poll_id, $choice->getName(), $joinedSlots]);
}
$this->connect->insert(Utils::table('slot'), [
'poll_id' => $poll_id,
'title' => $choice->getName(),
'moments' => $joinedSlots
]);
}
}
/**
* @return array|false
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return array
*/
public function listByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
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();
@ -67,12 +68,13 @@ class SlotRepository extends AbstractRepository {
/**
* Find the slot into poll for a given datetime.
*
* @param string $poll_id The ID of the poll
* @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
*/
public function findByPollIdAndDatetime(string $poll_id, $datetime) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
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();
@ -84,49 +86,49 @@ class SlotRepository extends AbstractRepository {
/**
* Insert a new slot into a given poll.
*
* @param string $poll_id The ID of the 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
*/
public function insert(string $poll_id, string $title, ?string $moments): bool
function insert($poll_id, $title, $moments)
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
return $prepared->execute([$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 string $poll_id The ID of the 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.
*/
public function update(string $poll_id, $datetime, $newMoments): ?bool
function update($poll_id, $datetime, $newMoments)
{
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
return $prepared->execute([$newMoments, $poll_id, $datetime]);
return $this->connect->update(Utils::table('slot'), ['moments' => $newMoments], ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
}
/**
* Delete a entire slot from a poll.
*
* @param string $poll_id int The ID of the 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(string $poll_id, $datetime): void
public function deleteByDateTime($poll_id, $datetime)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
$prepared->execute([$poll_id, $datetime]);
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
}
public function deleteByPollId(string $poll_id): bool
/**
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return bool
*/
public function deleteByPollId($poll_id)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id]) > 0;
}
}

View File

@ -1,30 +1,38 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Framadate\Utils;
class VoteRepository extends AbstractRepository {
/**
* @return array|false
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return array
*/
public function allUserVotesByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
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();
}
public function insertDefault(string $poll_id, int $insert_position): bool
/**
* @param $poll_id
* @param $insert_position
* @throws \Doctrine\DBAL\DBALException
* @return bool
*/
public function insertDefault($poll_id, $insert_position)
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
# 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]);
}
public function insert(string $poll_id, string $name, string $choices, string $token): \stdClass {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices, uniqId) VALUES (?,?,?,?)');
$prepared->execute([$poll_id, $name, $choices, $token]);
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;
@ -32,61 +40,84 @@ class VoteRepository extends AbstractRepository {
$newVote->name = $name;
$newVote->choices = $choices;
$newVote->uniqId = $token;
$newVote->mail=$mail;
return $newVote;
}
public function deleteById(string $poll_id, int $vote_id): bool
/**
* @param $poll_id
* @param $vote_id
* @throws \Doctrine\DBAL\DBALException
* @return bool
*/
public function deleteById($poll_id, $vote_id)
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $vote_id]);
return $this->connect->delete(Utils::table('vote'), ['poll_id' => $poll_id, 'id' => $vote_id]) > 0;
}
/**
* Delete all votes of a given poll.
*
* @param string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded.
*/
public function deleteByPollId(string $poll_id): ?bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
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 made on given moment index.
* Delete all votes of a given poll.
*
* @param string $poll_id The ID of the poll
* @param $index int The index of the vote into the poll
* @param $poll_id int The ID of the given poll.
* @throws \Doctrine\DBAL\DBALException
* @return bool|null true if action succeeded.
*/
public function deleteByIndex(string $poll_id, int $index): ?bool
public function deleteByPollId($poll_id)
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE 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]);
}
public function update(string $poll_id, string $vote_id, string $name, $choices): bool
/**
* @param $poll_id
* @param $vote_id
* @param $name
* @param $choices
* @return bool
*/
public function update($poll_id, $vote_id, $name, $choices, $mail)
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
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 string $poll_id ID of the 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(string $poll_id, string $name): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?');
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;
}
@ -94,14 +125,14 @@ class VoteRepository extends AbstractRepository {
/**
* Check if name is already used for the given poll and another vote.
*
* @param string $poll_id ID of the poll
* @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(string $poll_id, string $name, int $vote_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ? AND id != ?');
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;
}

View File

@ -1,5 +1,6 @@
<?php
namespace Framadate\Security;
/**
@ -16,7 +17,7 @@ class PasswordHasher {
* @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(string $password) {
public static function hash($password) {
return password_hash($password, PASSWORD_DEFAULT);
}
@ -27,8 +28,7 @@ class PasswordHasher {
* @param string $hash the hash to compare.
* @return bool
*/
public static function verify(string $password, string $hash): bool
{
public static function verify($password, $hash) {
return password_verify($password, $hash);
}
}
}

View File

@ -2,35 +2,31 @@
namespace Framadate\Security;
class Token {
public const DEFAULT_LENGTH = 64;
const DEFAULT_LENGTH = 64;
private $time;
private $value;
private $length;
private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
public function __construct($length = self::DEFAULT_LENGTH) {
function __construct($length = self::DEFAULT_LENGTH) {
$this->length = $length;
$this->time = time() + TOKEN_TIME;
$this->value = $this->generate();
}
public function getTime(): int
{
public function getTime() {
return $this->time;
}
public function getValue(): string
{
public function getValue() {
return $this->value;
}
public function isGone(): bool
{
public function isGone() {
return $this->time < time();
}
public function check($value): bool
{
public function check($value) {
return $value === $this->value;
}
@ -41,8 +37,7 @@ class Token {
* @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not.
* @return string
*/
public static function getToken(int $length = self::DEFAULT_LENGTH, bool &$crypto_strong = false): 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);
@ -50,8 +45,7 @@ class Token {
return self::getUnsecureToken($length);
}
public static function getUnsecureToken(int $length): string
{
public static function getUnsecureToken($length) {
$string = '';
mt_srand();
for ($i = 0; $i < $length; $i++) {
@ -64,8 +58,7 @@ class Token {
/**
* @author http://stackoverflow.com/a/13733588
*/
public static function getSecureToken(int $length): string
{
public static function getSecureToken($length){
$token = "";
for($i=0;$i<$length;$i++){
$token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))];
@ -73,33 +66,25 @@ class Token {
return $token;
}
private function generate(): string
{
private function generate() {
return self::getToken($this->length);
}
/**
* @author http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
*
* @param int $max
*
* @psalm-param 0 $min
* @psalm-param 0|positive-int $max
*/
private static function crypto_rand_secure(int $min, $max): int {
private static function crypto_rand_secure($min, $max) {
$range = $max - $min;
// not so random...
if ($range < 0) {
return $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 &= $filter; // discard irrelevant bits
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd >= $range);
return $min + $rnd;
}
}

View File

@ -1,10 +1,10 @@
<?php
namespace Framadate\Services;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
/**
* Class AdminPollService
@ -21,7 +21,7 @@ class AdminPollService {
private $voteRepository;
private $commentRepository;
public function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
function __construct(Connection $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect;
$this->pollService = $pollService;
$this->logService = $logService;
@ -31,38 +31,32 @@ class AdminPollService {
$this->commentRepository = RepositoryFactory::commentRepository();
}
public function updatePoll($poll): bool
{
function updatePoll($poll) {
global $config;
if ($poll->end_date < $poll->creation_date) {
$poll->end_date = $poll->creation_date;
} elseif ($poll->end_date > $this->pollService->maxExpiryDate()) {
$poll->end_date = $this->pollService->maxExpiryDate();
if ($poll->end_date > $poll->creation_date) {
return $this->pollRepository->update($poll);
}
return $this->pollRepository->update($poll);
return false;
}
/**
* Delete a comment from a poll.
*
* @param string $poll_id The ID of the 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
*/
public function deleteComment(string $poll_id, int $comment_id) {
function deleteComment($poll_id, $comment_id) {
return $this->commentRepository->deleteById($poll_id, $comment_id);
}
/**
* Remove all comments of a poll.
*
* @param string $poll_id The ID a the poll
* @param $poll_id int The ID a the poll
* @return bool|null true is action succeeded
*/
public function cleanComments(string $poll_id): ?bool
{
function cleanComments($poll_id) {
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
return $this->commentRepository->deleteByPollId($poll_id);
}
@ -70,23 +64,21 @@ class AdminPollService {
/**
* Delete a vote from a poll.
*
* @param string $poll_id The ID of the poll
* @param $poll_id int The ID of the poll
* @param $vote_id int The ID of the vote
* @return bool true is action succeeded
* @return mixed true is action succeeded
*/
public function deleteVote(string $poll_id, int $vote_id): bool
{
function deleteVote($poll_id, $vote_id) {
return $this->voteRepository->deleteById($poll_id, $vote_id);
}
/**
* Remove all votes of a poll.
*
* @param string $poll_id The ID of the poll
* @param $poll_id int The ID of the poll
* @return bool|null true is action succeeded
*/
public function cleanVotes(string $poll_id): ?bool
{
function cleanVotes($poll_id) {
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
return $this->voteRepository->deleteByPollId($poll_id);
}
@ -94,11 +86,10 @@ class AdminPollService {
/**
* Delete the entire given poll.
*
* @param $poll_id string The ID of the poll
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
public function deleteEntirePoll(string $poll_id): bool
{
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");
@ -118,8 +109,7 @@ class AdminPollService {
* @param object $slot The slot informations (datetime + moment)
* @return bool true if action succeeded
*/
public function deleteDateSlot(object $poll, object $slot): bool
{
public function deleteDateSlot($poll, $slot) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$datetime = $slot->title;
@ -130,9 +120,7 @@ class AdminPollService {
// We can't delete the last slot
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
return false;
}
if ($poll->format === 'A' && count($slots) === 1) {
} elseif ($poll->format === 'A' && count($slots) === 1) {
return false;
}
@ -169,8 +157,7 @@ class AdminPollService {
return true;
}
public function deleteClassicSlot($poll, string $slot_title): bool
{
public function deleteClassicSlot($poll, $slot_title) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
$slots = $this->pollService->allSlotsByPoll($poll);
@ -206,41 +193,50 @@ class AdminPollService {
* <li>Create a new moment if a slot already exists for the given date</li>
* </ul>
*
* @param string $poll_id The ID of the poll
* @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(string $poll_id, int $datetime, string $new_moment): void
{
public function addDateSlot($poll_id, $datetime, $new_moment) {
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
// 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);
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;
}
$this->voteRepository->insertDefault($poll_id, $result->insert);
try {
// Begin transaction
$this->connect->beginTransaction();
// Commit transaction
$this->connect->commit();
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();
}
}
/**
@ -249,18 +245,19 @@ class AdminPollService {
* <li>Create a new slot if no one exists for the given title</li>
* </ul>
*
* @param string $poll_id The ID of the poll
* @param string $title The title
* @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(string $poll_id, string $title): void
{
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(static function ($slot) {
$titles = array_map(function ($slot) {
return $slot->title;
}, $slots);
if (in_array($title, $titles, true)) {
@ -289,7 +286,7 @@ class AdminPollService {
* @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(array $slots, int $datetime) {
private function findInsertPosition($slots, $datetime) {
$result = new \stdClass();
$result->slot = null;
$result->insert = 0;
@ -299,7 +296,7 @@ class AdminPollService {
// Search where to insert new column
foreach ($slots as $k=>$slot) {
$rowDatetime = (int) $slot->title;
$rowDatetime = $slot->title;
$moments = explode(',', $slot->moments);
if ($datetime === $rowDatetime) {
@ -307,13 +304,11 @@ class AdminPollService {
$result->insert += count($moments);
$result->slot = $slot;
break;
}
if ($datetime < $rowDatetime) {
} elseif ($datetime < $rowDatetime) {
// We have to insert before this slot
break;
}
$result->insert += count($moments);
$result->insert += count($moments);
}
return $result;

View File

@ -1,182 +0,0 @@
<?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 Framadate\Utils;
use Sabre\VObject;
class ICalService {
/**
* Creates an ical-File and initiates the download. If possible, the provided time is used, else an all day event is created.
*/
public function getEvent($poll, string $start_day, string $start_time): void
{
if(!$this->dayIsReadable($start_day)) {
return;
}
$ical_text = "";
$elements = explode("-", $start_time);
$end_time = null;
if(count($elements) === 2) {
$start_time = trim($elements[0]);
$end_time = trim($elements[1]);
}
$start_time = $this->reviseTimeString($start_time);
if($end_time !== null) {
$end_time = $this->reviseTimeString($end_time);
}
if($start_time !== null) {
if($end_time !== null) {
$ical_text = $this->getTimedEvent($poll, $start_day . " " . $start_time, $start_day . " " . $end_time);
} else {
$ical_text = $this->getTimedEvent1Hour($poll, $start_day . " " . $start_time);
}
}
else {
$date = DateTime::createFromFormat('d-m-Y', $start_day);
$day = $date->format('Ymd');
$ical_text = $this->getAllDayEvent($poll, $day);
}
$this->provideFile($poll->title, $ical_text);
}
/**
* Calls getTimedEvent with one hour as a time slot, starting at $start_daytime
*/
public function getTimedEvent1Hour($poll, string $start_daytime): string
{
$end_daytime = date(DATE_ATOM, strtotime('+1 hours', strtotime($start_daytime)));
return $this->getTimedEvent($poll, $start_daytime, $end_daytime);
}
/**
* Generates the text for an ical event including the time
*/
public function getTimedEvent($poll, string $start_daytime, string $end_daytime): string
{
$vcalendar = new VObject\Component\VCalendar([
'VEVENT' => [
'SUMMARY' => $poll->title,
'DESCRIPTION' => $this->stripMD($poll->description),
'DTSTART' => new DateTime($start_daytime),
'DTEND' => new DateTime($end_daytime)
],
'PRODID' => ICAL_PRODID
]);
return $vcalendar->serialize();
}
/**
* Generates the text for an ical event if the time is not known
*/
public function getAllDayEvent($poll, string $day): string
{
$vcalendar = new VObject\Component\VCalendar();
$vevent = $vcalendar->add('VEVENT');
$vevent->add('SUMMARY', $poll->title);
$vevent->add('DESCRIPTION', $this->stripMD($poll->description));
$dtstart = $vevent->add('DTSTART', $day);
$dtstart['VALUE'] = 'DATE';
unset($vcalendar->PRODID);
$vcalendar->add('PRODID', ICAL_PRODID);
return $vcalendar->serialize();
}
/**
* Creates a file and initiates the download
* @param string $title
* @param string $ical_text
*/
public function provideFile(string $title, string $ical_text): void
{
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename=' . $this->stripTitle($title) . ICAL_ENDING);
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header("Content-Type: text/calendar");
echo $ical_text;
exit;
}
/**
* Reformats a string value into a time readable by DateTime
* @param string $time
* @return string the corrected value, null if the format is unknown
*/
public function reviseTimeString(string $time): ?string
{
// 24-hour clock / international format
if (preg_match('/^\d\d(:)\d\d$/', $time)) {
return $time;
}
// 12-hour clock / using am and pm
if (preg_match('/^\d[0-2]?:?\d{0,2}\s?[aApP][mM]$/', $time)) {
return $this->formatTime($time);
}
// french format HHhMM or HHh
if (preg_match('/^\d\d?[hH]\d?\d?$/', $time)) {
return $this->formatTime(str_pad(str_ireplace("H", ":", $time), 5, "0"));
}
// Number only
if (preg_match('/^\d{1,4}$/', $time)) {
return $this->formatTime(str_pad(str_pad($time, 2, "0", STR_PAD_LEFT), 4, "0"));
}
return null;
}
/**
* @param string $day
* @return false|int 1 if the day string can be parsed, 0 if not and false if an error occured
*/
public function dayIsReadable(string $day) {
return preg_match('/^\d{2}-\d{2}-\d{4}$/', $day);
}
/**
* @param string $time
* @return string date string in format H:i (e.g. 19:00)
*/
public function formatTime(string $time): string
{
return date("H:i", strtotime($time));
}
/**
* Converts MD Code to HTML, then strips HTML away
*/
public function stripMD(string $string): string
{
return strip_tags(Utils::markdown($string));
}
/**
* Strips a string so it's usable as a file name (only digits, letters and underline allowed)
*
* @return null|string
*/
public function stripTitle(string $string): ?string {
return preg_replace('/[^a-z0-9_]+/', '-', strtolower($string));
}
}

View File

@ -17,28 +17,25 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Services;
use function __;
use DateTime;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use o80\i18n\CantLoadDictionaryException;
/**
* This class helps to clean all inputs from the users or external services.
*/
class InputService {
public function __construct() {}
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|int $options The associative array of options
* @param array|null $options The associative array of options
* @return array The filtered array
*/
public function filterArray(array $arr, int $type, $options = 0): array
{
function filterArray(array $arr, $type, $options = null) {
$newArr = [];
foreach($arr as $id=>$item) {
@ -51,32 +48,24 @@ class InputService {
return $newArr;
}
public function filterAllowedValues($value, array $allowedValues) {
function filterAllowedValues($value, array $allowedValues) {
return in_array($value, $allowedValues, true) ? $value : null;
}
public function filterTitle($title): ?string
{
public function filterTitle($title) {
return $this->returnIfNotBlank($title);
}
/**
* @return false|string
*/
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): ?string
{
public function filterName($name) {
$filtered = trim($name);
return $this->returnIfNotBlank($filtered);
}
/**
* @return false|string
*/
public function filterMail($mail) {
///////////////////////////////////////////////////////////////////////////////////////
// formatting
@ -100,67 +89,44 @@ class InputService {
return $resultat;
}
public function filterDescription($description): string {
return str_replace("\r\n", "\n", $description);
public function filterDescription($description) {
$description = str_replace("\r\n", "\n", $description);
return $description;
}
/**
* @return false|string
*/
public function filterMD5($control) {
return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]);
}
/**
* @return false|int
*/
public function filterInteger($int) {
return filter_var($int, FILTER_VALIDATE_INT);
}
/**
* @return false|int
*/
public function filterValueMax($int)
{
return $this->filterInteger($int) >= 1 ? $this->filterInteger($int) : false;
}
public function filterBoolean($boolean): bool
{
return (bool)filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
public function filterBoolean($boolean) {
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
}
/**
* @return false|string
*/
public function filterEditable($editable) {
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
}
public function filterComment($comment): ?string
{
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 validateDate(DateTime $date, DateTime $minDate, DateTime $maxDate): DateTime {
if ($date < $minDate) {
return $minDate;
}
if ($maxDate < $date) {
return $maxDate;
}
return $date;
}
/**
* @throws CantLoadDictionaryException
* @return DateTime|false
*/
public function parseDate(string $date) {
return DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0);
public function filterDate($date) {
$dDate = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0, 0);
return $dDate->format('Y-m-d H:i:s');
}
/**
@ -169,8 +135,7 @@ class InputService {
* @param string $filtered The value
* @return string|null
*/
private function returnIfNotBlank(string $filtered): ?string
{
private function returnIfNotBlank($filtered) {
if ($filtered) {
$withoutSpaces = str_replace(' ', '', $filtered);
if (!empty($withoutSpaces)) {

View File

@ -17,10 +17,10 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Services;
use function __f;
use Exception;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Framadate\Utils;
use PDO;
use Smarty;
/**
@ -36,34 +36,33 @@ class InstallService {
'cleanUrl' => true,
// Database configuration
'dbConnectionString' => 'mysql:host=<HOST>;dbname=<SCHEMA>;port=3306',
'dbName' => 'framadate',
'dbPort' => 3306,
'dbHost' => 'localhost',
'dbUser' => 'root',
'dbPassword' => '',
'dbPrefix' => 'fd_',
'migrationTable' => 'framadate_migration'
];
public function __construct() {}
function __construct() {}
public function updateFields($data): void
{
public function updateFields($data) {
foreach ($data as $field => $value) {
$this->fields[$field] = $value;
}
}
public function install(Smarty &$smarty): array
{
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['dbConnectionString']) || empty($this->fields['dbUser'])) {
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['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']);
} catch(Exception $e) {
return $this->error('CANT_CONNECT_TO_DATABASE', $e->getMessage());
$connect = $this->connectTo($this->fields);
if (!$connect) {
return $this->error('CANT_CONNECT_TO_DATABASE');
}
// Write configuration to conf.php file
@ -75,25 +74,29 @@ class InstallService {
}
/**
* Connect to PDO compatible source
*
* @param string $connectionString
* @param string $user
* @param string $password
* @return PDO
* @param $fields
* @return \Doctrine\DBAL\Connection|null
*/
public function connectTo(string $connectionString, string $user, string $password): PDO
{
$pdo = @new PDO($connectionString, $user, $password);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
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;
}
}
/**
* @return false|int
*/
public function writeConfiguration(Smarty &$smarty) {
function writeConfiguration(Smarty &$smarty) {
foreach($this->fields as $field=>$value) {
$smarty->assign($field, $value);
}
@ -105,17 +108,16 @@ class InstallService {
/**
* @param $content
* @return false|int
* @return bool|int
*/
public function writeToFile(string $content) {
function writeToFile($content) {
return @file_put_contents(CONF_FILENAME, $content);
}
/**
* @return array
*/
public function ok(): array
{
function ok() {
return [
'status' => 'OK',
'msg' => __f('Installation', 'Ended', Utils::get_server_name())
@ -123,21 +125,17 @@ class InstallService {
}
/**
* @param string $msg
* @param string $details
* @param $msg
* @return array
*/
public function error(string $msg, string $details = ''): array
{
function error($msg) {
return [
'status' => 'ERROR',
'code' => $msg,
'details' => $details,
'code' => $msg
];
}
public function getFields(): array
{
public function getFields() {
return $this->fields;
}
}

View File

@ -7,7 +7,7 @@ namespace Framadate\Services;
* @package Framadate\Services
*/
class LogService {
public function __construct() {
function __construct() {
}
/**
@ -16,8 +16,8 @@ class LogService {
* @param $tag string A tag is used to quickly found a message when reading log file
* @param $message string some message
*/
public function log(string $tag, string $message): void
{
function log($tag, $message) {
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
}
}

View File

@ -1,40 +1,53 @@
<?php
namespace Framadate\Services;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
class MailService {
public const DELAY_BEFORE_RESEND = 300;
const DELAY_BEFORE_RESEND = 300;
public const MAILSERVICE_KEY = 'mailservice';
const MAILSERVICE_KEY = 'mailservice';
/**
* @var bool
*/
private $smtp_allowed;
/**
* @var array
*/
private $smtp_options = [];
/**
* @var bool
*/
private $use_sendmail;
/**
* @var LogService
*/
private $logService;
public function __construct($smtp_allowed, $smtp_options = []) {
/**
* 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;
}
/**
* @return false|string
*/
public function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
/**
* @throws Exception
*/
public function send(string $to, string $subject, string $body, ?string $msgKey = null): void
{
public function send($to, $subject, $body, $msgKey = null) {
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
$mail = new PHPMailer(true);
$this->configureMailer($mail);
@ -53,7 +66,7 @@ class MailService {
$mail->Subject = $subject;
// Bodies
$body .= ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
$body = $body . ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
$mail->isHTML(true);
$mail->msgHTML($body, ROOT_DIR, true);
@ -61,7 +74,6 @@ class MailService {
$mail->CharSet = 'UTF-8';
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
$mail->addCustomHeader('Return-Path', '<>');
$mail->XMailer = ' ';
// Send mail
$mail->send();
@ -70,25 +82,19 @@ class MailService {
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
// Store the mail sending date
$this->initializeSession();
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
}
}
public function canSendMsg(?string $msgKey): bool
{
public function canSendMsg($msgKey) {
if ($msgKey === null) {
return true;
}
$this->initializeSession();
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
}
private function initializeSession(): void {
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;
}
/**
@ -96,14 +102,16 @@ class MailService {
*
* @param PHPMailer $mailer
*/
private function configureMailer(PHPMailer $mailer): void
{
$mailer->isSMTP();
private function configureMailer(PHPMailer $mailer) {
if ($this->use_sendmail) {
$mailer->isSendmail();
} else {
$mailer->isSMTP();
}
$available_options = [
'host' => 'Host',
'auth' => 'SMTPAuth',
'authtype' => 'AuthType',
'username' => 'Username',
'password' => 'Password',
'secure' => 'SMTPSecure',

View File

@ -1,24 +1,22 @@
<?php
namespace Framadate\Services;
use \stdClass;
use function __;
use function __f;
use Framadate\Services\MailService;
use Framadate\Utils;
use o80\i18n\CantLoadDictionaryException;
use PHPMailer\PHPMailer\Exception;
class NotificationService {
public const UPDATE_VOTE = 1;
public const ADD_VOTE = 2;
public const ADD_COMMENT = 3;
public const UPDATE_POLL = 10;
public const DELETED_POLL = 11;
const UPDATE_VOTE = 1;
const ADD_VOTE = 2;
const ADD_COMMENT = 3;
const UPDATE_POLL = 10;
const DELETED_POLL = 11;
private $mailService;
public function __construct(MailService $mailService) {
function __construct(MailService $mailService) {
$this->mailService = $mailService;
}
@ -28,10 +26,8 @@ class NotificationService {
* @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
* @throws Exception|CantLoadDictionaryException
*/
public function sendUpdateNotification($poll, int $type, string $name=''): void
{
function sendUpdateNotification(stdClass $poll, $type, $name='') {
if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = [];
}
@ -41,7 +37,7 @@ class NotificationService {
$isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT;
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
if ($this->isParticipation($type)) {
if (self::isParticipation($type)) {
$translationString = 'Poll\'s participation: %s';
} else {
$translationString = 'Notification of poll: %s';
@ -79,13 +75,11 @@ class NotificationService {
}
$messageTypeKey = $type . '-' . $poll->id;
if ($poll->admin_mail) {
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
public function isParticipation(int $type): bool
function isParticipation($type)
{
return $type >= self::UPDATE_POLL;
}

View File

@ -18,19 +18,18 @@
*/
namespace Framadate\Services;
use DateInterval;
use DateTime;
use Exception;
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\Exception\PollNotFoundException;
use Framadate\Form;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token;
use stdClass;
class PollService {
private $connect;
private $logService;
private $pollRepository;
@ -38,7 +37,8 @@ class PollService {
private $voteRepository;
private $commentRepository;
public function __construct(LogService $logService) {
function __construct(Connection $connect, LogService $logService) {
$this->connect = $connect;
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
@ -49,34 +49,56 @@ class PollService {
/**
* Find a poll from its ID.
*
* @param string $poll_id The ID of the poll
* @return stdClass|null The found poll, or null
* @param $poll_id int The ID of the poll
* @return \stdClass|null The found poll, or null
*/
public function findById(string $poll_id) {
if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id);
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;
}
public function findByAdminId(string $admin_poll_id) {
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
return $this->pollRepository->findByAdminId($admin_poll_id);
/**
* @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;
}
public function allCommentsByPollId(string $poll_id) {
return $this->commentRepository->findAllByPollId($poll_id);
/**
* @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;
}
}
public function allVotesByPollId(string $poll_id) {
function allVotesByPollId($poll_id) {
return $this->voteRepository->allUserVotesByPollId($poll_id);
}
public function allSlotsByPoll(stdClass $poll) {
function allSlotsByPoll($poll) {
$slots = $this->slotRepository->listByPollId($poll->id);
if ($poll->format === 'D') {
$this->sortSlorts($slots);
@ -85,45 +107,46 @@ class PollService {
}
/**
* @param string $poll_id
* @param int $vote_id
* @param string $name
* @param array $choices
* @param string $slots_hash
* @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(string $poll_id, int $vote_id, string $name, array $choices, string $slots_hash): 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
return $this->voteRepository->update($poll_id, $vote_id, $name, implode($choices));
$choices = implode($choices);
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices, $mail);
}
/**
* @param string $poll_id
* @param string $name
* @param array $choices
* @param string $slots_hash
* @param $poll_id
* @param $name
* @param $choices
* @param $slots_hash
* @param string $mail
* @throws AlreadyExistsException
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @throws PollNotFoundException
* @throws AlreadyExistsException
* @return stdClass
* @return \stdClass
*/
public function addVote(string $poll_id, string $name, array $choices, string $slots_hash): stdClass
{
function addVote($poll_id, $name, $choices, $slots_hash, $mail) {
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
// Insert new vote
return $this->voteRepository->insert($poll_id, $name, implode($choices), $this->random(16));
$choices = implode($choices);
$token = $this->random(16);
return $this->voteRepository->insert($poll_id, $name, $choices, $token, $mail);
}
public function addComment($poll_id, $name, $comment): bool
{
function addComment($poll_id, $name, $comment) {
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
return true;
}
@ -135,45 +158,51 @@ class PollService {
* @param Form $form
* @return array
*/
public function createPoll(Form $form): array
{
function createPoll(Form $form) {
// Generate poll IDs, loop while poll ID already exists
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->beginTransaction();
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
$this->pollRepository->commit();
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));
}
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
// Insert poll + slots
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
$this->pollRepository->commit();
return [$poll_id, $admin_poll_id];
$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): array
{
public function findAllByAdminMail($mail) {
return $this->pollRepository->findAllByAdminMail($mail);
}
/**
* @param array $votes
* @param stdClass $poll
* @param \stdClass $poll
* @return array
*/
public function computeBestChoices(array $votes, $poll): array
{
public function computeBestChoices($votes, $poll) {
if (0 === count($votes)) {
return $this->computeEmptyBestChoices($poll);
}
@ -199,11 +228,10 @@ class PollService {
return $result;
}
public function splitSlots($slots): array
{
function splitSlots($slots) {
$splitted = [];
foreach ($slots as $slot) {
$obj = new stdClass();
$obj = new \stdClass();
$obj->day = $slot->title;
$obj->moments = explode(',', $slot->moments);
@ -217,22 +245,21 @@ class PollService {
* @param $slots array The slots to hash
* @return string The hash
*/
public function hashSlots(array $slots): string
{
return md5(array_reduce($slots, static function($carry, $item) {
public function hashSlots($slots) {
return md5(array_reduce($slots, function($carry, $item) {
return $carry . $item->id . '@' . $item->moments . ';';
}));
}
public function splitVotes(array $votes): array
{
function splitVotes($votes) {
$splitted = [];
foreach ($votes as $vote) {
$obj = new stdClass();
$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;
}
@ -241,40 +268,35 @@ class PollService {
}
/**
* @throws Exception
* @return DateTime The max date allowed for expiry date
* @return int The max timestamp allowed for expiry date
*/
public function maxExpiryDate(): DateTime {
public function maxExpiryDate() {
global $config;
return (new DateTime())->add(new DateInterval('P' . $config['default_poll_duration'] . 'D'));
return time() + (86400 * $config['default_poll_duration']);
}
/**
* @return DateTime The min date allowed for expiry date
* @return int The min timestamp allowed for expiry date
*/
public function minExpiryDate(): DateTime
{
return (new DateTime())->add(new DateInterval('P1D'));
public function minExpiryDate() {
return time() + 86400;
}
/**
* @return mixed
*/
public function sortSlorts(array &$slots): array {
uasort($slots, static function ($a, $b) {
if ($a->title === $b->title) {
return 0;
}
return $a->title > $b->title ? 1 : -1;
public function sortSlorts(&$slots) {
uasort($slots, function ($a, $b) {
return $a->title > $b->title;
});
return $slots;
}
/**
* @param stdClass $poll
* @param \stdClass $poll
* @return array
*/
private function computeEmptyBestChoices($poll): array
private function computeEmptyBestChoices($poll)
{
$result = ['y' => [], 'inb' => []];
// if there is no votes, calculates the number of slot
@ -284,7 +306,7 @@ class PollService {
if ($poll->format === 'A') {
// poll format classic
for ($i = 0, $iMax = count($slots); $i < $iMax; $i++) {
for ($i = 0; $i < count($slots); $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
@ -294,7 +316,7 @@ class PollService {
$slots = $this->splitSlots($slots);
foreach ($slots as $slot) {
for ($i = 0, $iMax = count($slot->moments); $i < $iMax; $i++) {
for ($i = 0; $i < count($slot->moments); $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
@ -303,26 +325,23 @@ class PollService {
return $result;
}
private function random(int $length): string
{
private function random($length) {
return Token::getToken($length);
}
/**
* @param array $choices
* @param string $poll_id
* @param string $slots_hash
* @param string $name
* @param bool|int $vote_id
* @param $choices
* @param $poll_id
* @param $slots_hash
* @param $name
* @param string $vote_id
* @throws AlreadyExistsException
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @throws PollNotFoundException
* @throws ConcurrentEditionException
*/
private function checkVoteConstraints(array $choices, string $poll_id, string $slots_hash, string $name, $vote_id = false): void
{
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) {
if (FALSE === $vote_id) {
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
} else {
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
@ -334,10 +353,6 @@ class PollService {
$poll = $this->findById($poll_id);
if (!$poll) {
throw new PollNotFoundException();
}
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint
$this->checkMaxVotes($choices, $poll, $poll_id);
@ -352,8 +367,7 @@ class PollService {
* @param $slots_hash string The hash sent by the user
* @throws ConcurrentEditionException Thrown when hashes are differents
*/
private function checkThatSlotsDidntChanged(stdClass $poll, string $slots_hash): void
{
private function checkThatSlotsDidntChanged($poll, $slots_hash) {
$slots = $this->allSlotsByPoll($poll);
if ($slots_hash !== $this->hashSlots($slots)) {
throw new ConcurrentEditionException();
@ -364,12 +378,11 @@ class PollService {
* This method checks if the votes doesn't conflicts the maximum votes constraint
*
* @param $user_choice
* @param stdClass $poll
* @param \stdClass $poll
* @param string $poll_id
* @throws ConcurrentVoteException
*/
private function checkMaxVotes(array $user_choice, $poll, string $poll_id): void
{
private function checkMaxVotes($user_choice, $poll, $poll_id) {
$votes = $this->allVotesByPollId($poll_id);
if (count($votes) <= 0) {
return;

View File

@ -1,6 +1,7 @@
<?php
namespace Framadate\Services;
use Framadate\FramaDB;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Framadate\Repositories\RepositoryFactory;
/**
@ -15,7 +16,7 @@ class PurgeService {
private $voteRepository;
private $commentRepository;
public function __construct(LogService $logService) {
function __construct(Connection $connect, LogService $logService) {
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
@ -23,51 +24,89 @@ class PurgeService {
$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 int number of purged polls
* @return bool true is action succeeded
*/
public function purgeOldPolls(): int
{
$oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls);
public function purgeOldPolls() {
try {
$oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls);
if ($count > 0) {
$this->logService->log('EXPIRATION', 'Going to purge ' . $count . ' poll(s)...');
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);
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;
}
return $count;
}
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 string $poll_id The ID of the poll
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
public function purgePollById(string $poll_id): bool
{
private function purgePollById($poll_id) {
$done = true;
$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);
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();
if ($done) {
$this->pollRepository->commit();
} else {
$this->pollRepository->rollback();
}
} catch (DBALException $e) {
$this->logService->log('ERROR', $e->getMessage());
}
return $done;

View File

@ -5,7 +5,7 @@ use Framadate\Security\PasswordHasher;
use Framadate\Security\Token;
class SecurityService {
public function __construct() {
function __construct() {
}
/**
@ -18,10 +18,9 @@ class SecurityService {
* </ul>
*
* @param $tokan_name string The name of the CSRF token
* @return string The token
* @return Token The token
*/
function getToken(string $tokan_name): string
{
function getToken($tokan_name) {
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens'] = [];
}
@ -39,8 +38,7 @@ class SecurityService {
* @param $csrf string Value to check
* @return bool true if the token is well checked
*/
public function checkCsrf(string $tokan_name, string $csrf): bool
{
public function checkCsrf($tokan_name, $csrf) {
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
if($checked) {
@ -56,18 +54,17 @@ class SecurityService {
* @param $poll \stdClass The poll which we seek access
* @return bool true if the current session can access this poll
*/
public function canAccessPoll($poll): bool
{
public function canAccessPoll($poll) {
if (is_null($poll->password_hash)) {
return true;
}
$this->ensureSessionPollSecurityIsCreated();
$currentPassword = $_SESSION['poll_security'][$poll->id] ?? null;
$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;
}
@ -78,18 +75,17 @@ class SecurityService {
* @param $poll \stdClass The poll which we seek access
* @param $password string the password to compare
*/
public function submitPollAccess($poll, string $password): void
{
public function submitPollAccess($poll, $password) {
if (!empty($password)) {
$this->ensureSessionPollSecurityIsCreated();
$_SESSION['poll_security'][$poll->id] = $password;
}
}
private function ensureSessionPollSecurityIsCreated(): void
{
private function ensureSessionPollSecurityIsCreated() {
if (!isset($_SESSION['poll_security'])) {
$_SESSION['poll_security'] = [];
}
}
}

View File

@ -1,5 +1,6 @@
<?php
namespace Framadate\Services;
class SessionService {
@ -17,7 +18,12 @@ class SessionService {
$this->initSectionIfNeeded($section);
return $_SESSION[$section][$key] ?? $defaultValue;
$returnValue = $defaultValue;
if (isset($_SESSION[$section][$key])) {
$returnValue = $_SESSION[$section][$key];
}
return $returnValue;
}
/**
@ -27,8 +33,7 @@ class SessionService {
* @param $key
* @param $value
*/
public function set($section, $key, $value): void
{
public function set($section, $key, $value) {
assert(!empty($key));
assert(!empty($section));
@ -43,18 +48,16 @@ class SessionService {
* @param $section
* @param $key
*/
public function remove($section, $key): void
{
public function remove($section, $key) {
assert(!empty($key));
assert(!empty($section));
unset($_SESSION[$section][$key]);
}
private function initSectionIfNeeded($section): void
{
private function initSectionIfNeeded($section) {
if (!isset($_SESSION[$section])) {
$_SESSION[$section] = [];
}
}
}
}

View File

@ -11,7 +11,7 @@ use Framadate\Repositories\RepositoryFactory;
class SuperAdminService {
private $pollRepository;
public function __construct() {
function __construct() {
$this->pollRepository = RepositoryFactory::pollRepository();
}
@ -23,8 +23,7 @@ class SuperAdminService {
* @param int $limit The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
*/
public function findAllPolls(array $search, int $page, int $limit): array
{
public function findAllPolls($search, $page, $limit) {
$start = $page * $limit;
$polls = $this->pollRepository->findAll($search, $start, $limit);
$count = $this->pollRepository->count($search);
@ -33,3 +32,4 @@ class SuperAdminService {
return ['polls' => $polls, 'count' => $count, 'total' => $total];
}
}

View File

@ -24,24 +24,26 @@ class Utils {
/**
* @return string Server name
*/
public static function get_server_name(): string
{
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($_SERVER['SERVER_PORT'], ['80', '443'], true) ? '' : ':' . $_SERVER['SERVER_PORT'];
$port = in_array($serverPort, ['80', '443'], true) ? '' : ':' . $serverPort;
$dirname = dirname($_SERVER['SCRIPT_NAME']);
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
$dirname = str_replace(['/admin', '/action'], '', $dirname);
$server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $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 = ''): void {
public static function print_header($title = '') {
global $locale;
echo '<!DOCTYPE html>
@ -61,17 +63,17 @@ class Utils {
<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 src="' . self::get_server_name() . 'js/jquery-3.6.0.min.js"></script>
<script src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
<script src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>';
<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 src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
}
echo '
<script src="' . self::get_server_name() . 'js/core.js"></script>';
<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" charset="utf-8"></script><!-- /Framanav -->';
echo '<script src="/nav/nav.js" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->';
}
echo '
@ -82,17 +84,16 @@ class Utils {
/**
* 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 string|null $action
* @param string|null $action_value
* @return string The 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(string $id, bool $admin = false, string $vote_id = '', string $action = null, string $action_value = null): string
{
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) {
// URL-Encode $action_value
$action_value = $action_value ? self::base64url_encode($action_value) : null;
$action_value = $action_value ? Utils::base64url_encode($action_value) : null;
if (URL_PROPRE) {
if ($admin === true) {
@ -134,20 +135,17 @@ class Utils {
*
* @param mixed $object The object to print.
*/
public static function debug($object): void
{
public static function debug($object) {
echo '<pre>';
print_r($object);
echo '</pre>';
}
public static function table(string $tableName): string
{
public static function table($tableName) {
return TABLENAME_PREFIX . $tableName;
}
public static function markdown(string $md, bool $clear=false, bool $line=true): string
{
public static function markdown($md, $clear=false, $line=true) {
$parseDown = new Parsedown();
$parseDown
@ -160,7 +158,7 @@ class Utils {
} else {
$md = preg_replace_callback(
'#( ){2,}#',
static function ($m) {
function ($m) {
return str_repeat('&nbsp;', strlen($m[0]));
},
$md
@ -173,38 +171,39 @@ class Utils {
return $clear ? $text : $html;
}
public static function htmlEscape(string $html): string {
public static function htmlEscape($html) {
return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
}
public static function htmlMailEscape(string $html): string
{
public static function htmlMailEscape($html) {
return htmlspecialchars($html, ENT_HTML5 | ENT_QUOTES);
}
public static function csvEscape(string $text): string
{
$escaped = str_replace(['"', "\r\n", "\n"], ['""', '', ''], $text);
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(string $title): string {
public static function cleanFilename($title) {
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
return preg_replace(' {2,}', ' ', $cleaned);
$cleaned = preg_replace(' {2,}', ' ', $cleaned);
return $cleaned;
}
public static function fromPostOrDefault(string $postKey, ?string $default = '') {
public static function fromPostOrDefault($postKey, $default = '') {
return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default;
}
public static function base64url_encode(string $input): string
{
public static function base64url_encode($input) {
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
}
public static function base64url_decode(string $input): string {
public static function base64url_decode($input) {
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true);
}
}

116
app/inc/config.test.php Normal file
View 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
];

View File

@ -18,10 +18,10 @@
*/
// FRAMADATE version
const VERSION = '1.1.19';
const VERSION = '1.2.0';
// PHP Needed version
const PHP_NEEDED_VERSION = '7.3';
const PHP_NEEDED_VERSION = '5.6';
// Config constants
const COMPILE_DIR = '/tpl_c/';
@ -33,6 +33,7 @@ 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}$/';
@ -42,6 +43,3 @@ const SESSION_EDIT_LINK_TIME = "EditLinkMail";
// CSRF (300s = 5min)
const TOKEN_TIME = 300;
const ICAL_ENDING = ".ics";
const ICAL_PRODID = "-//Framasoft//Framadate//EN";

View File

@ -18,14 +18,12 @@
*/
// Prepare I18N instance
use o80\i18n\I18N;
$i18n = I18N::instance();
$i18n = \o80\i18n\I18N::instance();
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
$i18n->setPath(__DIR__ . '/../../locale');
// Change language when user asked for it
if (isset($_POST['lang']) && is_string($_POST['lang']) && array_key_exists($_POST['lang'], $ALLOWED_LANGUAGES)) {
// 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'];
}
@ -40,7 +38,7 @@ $date_format['txt_day'] = __('Date', 'DAY');
$date_format['txt_date'] = __('Date', 'DATE');
$date_format['txt_month_year'] = __('Date', 'MONTH_YEAR');
$date_format['txt_datetime_short'] = __('Date', 'DATETIME');
if (PHP_OS_FAMILY === 'Windows') { //%e can't be used on Windows platform, use %#d instead
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { //%e can't be used on Windows platform, use %#d instead
foreach ($date_format as $k => $v) {
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
}

View File

@ -16,9 +16,12 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\FramaDB;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
use Framadate\Services\LogService;
// Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php';
@ -33,23 +36,40 @@ if (ini_get('date.timezone') === '') {
}
define('ROOT_DIR', __DIR__ . '/../../');
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
$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 __DIR__ . '/config.php';
@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 {
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
$connect = DriverManager::getConnection($connectionParams, $doctrineConfig);
RepositoryFactory::init($connect);
} catch (PDOException $e) {
if ($_SERVER['SCRIPT_NAME'] !== '/maintenance.php') {
header(('Location: ' . Utils::get_server_name() . 'maintenance.php'));
exit;
}
$error = $e->getMessage();
$err = 0;
} catch (DBALException $e) {
$logger = new LogService();
$logger->log('ERROR', $e->getMessage());
}
} else {
define('NOMAPPLICATION', 'Framadate');
@ -63,7 +83,6 @@ if (is_file(CONF_FILENAME)) {
'de' => 'Deutsch',
'it' => 'Italiano',
'br' => 'Brezhoneg',
'ca' => 'Català'
];
}

View File

@ -25,11 +25,14 @@ $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($_SERVER['SERVER_NAME'], 'framadate.org'));
$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);
@ -42,7 +45,7 @@ if (defined('FAVICON')) {
}
// Dev Mode
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE'] || php_sapi_name() === 'cli-server') {
$smarty->force_compile = true;
$smarty->compile_check = true;
} else {
@ -50,10 +53,9 @@ if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
$smarty->compile_check = false;
}
function smarty_function_poll_url($params, Smarty_Internal_Template $template): string
{
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'];
$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]]) : '';
@ -63,40 +65,41 @@ function smarty_function_poll_url($params, Smarty_Internal_Template $template):
return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value);
}
function smarty_modifier_markdown(string $md, bool $clear = false, bool $inline=true): string
{
function smarty_modifier_markdown($md, $clear = false, $inline=true) {
return Utils::markdown($md, $clear, $inline);
}
function smarty_modifier_resource(string $link): string
{
function smarty_modifier_resource($link) {
return Utils::get_server_name() . $link;
}
function smarty_modifier_addslashes_single_quote(string $string): string
{
function smarty_modifier_addslashes_single_quote($string) {
return addcslashes($string, '\\\'');
}
function smarty_modifier_addslashes(string $string): string
{
return addslashes($string);
}
function smarty_modifier_html(?string $html): string
{
if (!$html) {
return '';
}
function smarty_modifier_html($html) {
return Utils::htmlEscape($html);
}
function smarty_modifier_html_special_chars(string $html): string
/**
* 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_html_special_chars($html) {
return Utils::htmlMailEscape($html);
}
function smarty_modifier_datepicker_path(string $lang): string
{
function smarty_modifier_datepicker_path($lang) {
$i = 0;
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
$lang_arr = explode('-', $lang);
@ -105,13 +108,12 @@ function smarty_modifier_datepicker_path(string $lang): string
} else {
$lang = 'en';
}
++$i;
$i += 1;
}
return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
}
function smarty_modifier_locale_2_lang(string $locale): string
{
function smarty_modifier_locale_2_lang($locale) {
$lang_arr = explode('-', $locale);
if ($lang_arr && count($lang_arr) > 1) {
return $lang_arr[0];
@ -119,10 +121,6 @@ function smarty_modifier_locale_2_lang(string $locale): string
return $locale;
}
function path_for_datepicker_locale(string $lang): string
{
function path_for_datepicker_locale($lang) {
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
}
# Customization #4871 par Didier le 28/08/2021.
$smarty->assign('VERSION',VERSION);

View File

@ -4,12 +4,11 @@ namespace Framadate;
use PHPUnit\Framework\TestCase;
abstract class FramaTestCase extends TestCase {
protected function getTestResourcePath(string $resourcepath): string
{
protected function getTestResourcePath($resourcepath) {
return __DIR__ . '/../resources/' . $resourcepath;
}
protected function readTestResource(string $resourcepath) {
protected function readTestResource($resourcepath) {
return file_get_contents($this->getTestResourcePath($resourcepath));
}

View File

@ -1,34 +1,35 @@
<?php
namespace Framadate\Services;
use Framadate\FramaTestCase;
class InputServiceUnitTest extends FramaTestCase
{
public function liste_emails(): array
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"],
"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],
"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): void
/**
* @dataProvider liste_emails
*/
public function test_filterMail($email, $expected)
{
$inputService = new InputService();
$filtered = $inputService->filterMail($email);
$inputService = new InputService();
$filtered = $inputService->filterMail($email);
$this->assertSame($expected, $filtered);
}
$this->assertSame($expected, $filtered);
}
}

View File

@ -4,10 +4,9 @@ namespace Framadate\Services;
use Framadate\FramaTestCase;
class MailServiceUnitTest extends FramaTestCase {
public const MSG_KEY = '666';
const MSG_KEY = '666';
public function test_should_send_a_2nd_mail_after_a_good_interval(): void
{
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];
@ -16,11 +15,10 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then
$this->assertTrue($canSendMsg);
$this->assertSame(true, $canSendMsg);
}
public function test_should_not_send_2_mails_in_a_short_interval(): void
{
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()];
@ -29,6 +27,6 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then
$this->assertFalse($canSendMsg);
$this->assertSame(false, $canSendMsg);
}
}

View File

@ -28,7 +28,7 @@ function bandeau_titre($titre)
echo '
<header role="banner">';
if(count($ALLOWED_LANGUAGES) > 1){
echo '<form method="post" class="hidden-print">
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 the language') . '" >' . liste_lang() . '</select>
<span class="input-group-btn">
@ -43,25 +43,16 @@ function bandeau_titre($titre)
<hr class="trait" role="presentation" />
</header>
<main role="main">';
global $connect;
$tables = $connect->allTables();
$diff = array_diff([Utils::table('comment'), Utils::table('poll'), Utils::table('slot'), Utils::table('vote')], $tables);
if (0 !== count($diff)) {
echo '<div class="alert alert-danger">' . __('Error', 'Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') . '</div>';
bandeau_pied();
die();
}
}
function liste_lang(): string
function liste_lang()
{
global $ALLOWED_LANGUAGES; global $locale;
$str = '';
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
if (strpos($k, $locale) === 0) {
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" ;

58
bin/doctrine Executable file
View 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());
}

View File

@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
$goodLang = $_GET['good'];
$otherLang = $_GET['other'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$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) {
@ -19,15 +19,15 @@ include_once __DIR__ . '/app/inc/init.php';
}
}
echo json_encode($good, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
echo json_encode($good, JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
function getFromOther($other, $goodKey, $default, $otherLang): string {
function getFromOther($other, $goodKey, $default, $otherLang) {
foreach ($other as $sectionName => $section) {
foreach ($section as $key => $value) {
if (
strtolower($key) === strtolower($goodKey) ||
stripos($key, strtolower($goodKey)) === 0 ||
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;

View File

@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
$goodLang = $_GET['good'];
$testLang = $_GET['test'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true);
$diffSection = false;
@ -46,8 +46,8 @@ include_once __DIR__ . '/app/inc/init.php';
}
}
if (!$diffSection and array_keys($section) !== array_keys($test[$sectionName])) {
$diff[$sectionName]['order_good'] = array_keys($section);
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]);
}
}

View File

@ -2,12 +2,10 @@
"name": "framasoft/framadate",
"description": "Application to facilitate the schedule of events or classic polls",
"homepage": "https://framadate.org/",
"keywords": [
"poll",
"framadate"
],
"keywords": ["poll", "framadate"],
"version": "0.9.0",
"license": "CECILL-B",
"type": "project",
"support": {
"issues": "https://framagit.org/framasoft/framadate/issues"
@ -54,43 +52,36 @@
"email": "raphael.droz@gmail.com"
}
],
"scripts": {
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -not -path './tests/integration/vendor/*' -print0 | xargs -0 -n1 php -l"
},
"require": {
"php": ">=7.3.0",
"php": ">=5.6.0",
"ext-pdo": "*",
"ext-json": "*",
"smarty/smarty": "^4.0",
"smarty/smarty": "^3.1",
"o80/i18n": "dev-develop",
"phpmailer/phpmailer": "~6.2",
"phpmailer/phpmailer": "~6.0",
"ircmaxell/password-compat": "dev-master",
"roave/security-advisories": "dev-master",
"erusev/parsedown": "^1.7",
"egulias/email-validator": "^3.1",
"sabre/vobject": "~4.1"
"egulias/email-validator": "~2.1",
"doctrine/dbal": "^2.5",
"doctrine/migrations": "^1.5",
"sensiolabs/ansi-to-html": "^1.1"
},
"require-dev": {
"phpunit/phpunit": "^9",
"friendsofphp/php-cs-fixer": "^3.2",
"vimeo/psalm": "^4.15"
"phpunit/phpunit": "^5.7",
"friendsofphp/php-cs-fixer": "~2.0"
},
"repositories": [
{
"type": "git",
"url": "https://framagit.org/framasoft/framadate/o80-i18n"
}
],
"autoload": {
"psr-4": {
"Framadate\\": "app/classes/Framadate/"
}
},
"config": {
"platform": {
"php": "7.3.0"
"php": "5.6.0"
}
}
}

5564
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
* 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;
@ -30,11 +31,10 @@ include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($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();
$inputService = new InputService();
if (is_file('bandeaux_local.php')) {
include_once('bandeaux_local.php');
@ -42,88 +42,89 @@ if (is_file('bandeaux_local.php')) {
include_once('bandeaux.php');
}
$form = unserialize($_SESSION['form']);
// Min/Max archive date
$min_expiry_time = $pollService->minExpiryDate();
$max_expiry_time = $pollService->maxExpiryDate();
// Step 1/4 : error if $_SESSION from info_sondage are not valid
if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && empty($form->admin_mail))) {
$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.'));
$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 other (A) if we are in this file
if (!isset($form->format)) {
$form->format = 'A';
}
// If we come from another format, we need to clear choices
if (isset($form->format) && $form->format !== 'A') {
$form->format = 'A';
$form->clearChoices();
}
// Step 4 : Data prepare before insert in DB
if (isset($_POST['confirmation'])) {
// Define expiration date
$expiration_date = $inputService->parseDate($_POST['enddate']);
$form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
// The poll format is AUTRE (other) if we are in this file
if (!isset($form->format)) {
$form->format = 'A';
}
// Insert poll in database
$ids = $pollService->createPoll($form);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// The poll format is AUTRE (other)
if ($form->format !== 'A') {
$form->format = 'A';
$form->clearChoices();
}
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('Mail', "This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.");
$message .= '<br/><br/>';
$message .= Utils::htmlMailEscape($form->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlMailEscape($form->title) . '".<br/>';
$message .= sprintf(__('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($poll_id));
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;
}
$message_admin = __('Mail', "This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above");
$message_admin .= 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', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message_admin);
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message);
}
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);
}
// Clean Form data in $_SESSION
unset($_SESSION['form']);
$_SESSION['form'] = serialize($form);
// Delete old polls
$purgeService->purgeOldPolls();
// Display step 2
$smarty->assign('title', __('Step 2 classic', 'Poll subjects (2 on 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('allowMarkdown', $config['user_can_add_img_or_link']);
$smarty->assign('error', null);
// creation message
$sessionService->set("Framadate", "messagePollCreated", TRUE);
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
$smarty->display('create_classic_poll_step_2.tpl');
exit;
} // Step 3/4 : Confirm poll creation and choose a removal date
else if (isset($_POST['fin_sondage_autre'])) {
// Store choices in $_SESSION
if (isset($_POST['choices'])) {
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) {
if (!empty($c)) {
$c = strip_tags($c);
$choice = new Choice($c);
$form->addChoice($choice);
}
$c = strip_tags($c);
$choice = new Choice($c);
$form->addChoice($choice);
}
}
// Expiration date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
$form->end_date = $pollService->maxExpiryDate()->format('Y-m-d H:i:s');
$form->end_date = $max_expiry_time;
// Summary
$summary = '<ol>';
foreach ($form->getChoices() as $i=>$choice) {
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[3][0]) && $md_a_img[2][0] !== '' && $md_a_img[3][0] !== '') { // [![alt](src)](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)
@ -141,7 +142,7 @@ if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && e
}
$summary .= '</ol>';
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); //textual date
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); //textual date
$_SESSION['form'] = serialize($form);
@ -151,91 +152,69 @@ if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && e
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->display('create_classic_poll_step3.tpl');
$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}$#']]);
// Step 2/4 : Select choices of the poll
} else {
Utils::print_header(__('Step 2 classic', 'Poll subjects (2 on 3)'));
bandeau_titre(__('Step 2 classic', 'Poll subjects (2 on 3)'));
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'create_classic_poll.php" method="POST" class="form-horizontal">
<div class="row">
<div class="col-md-8 col-md-offset-2">';
echo '
<div class="alert alert-info">
<p>' . __('Step 2 classic', 'To make a generic poll you need to propose at least two choices between differents subjects.') . '</p>
<p>' . __('Step 2 classic', 'You can add or remove additional choices with the buttons') . ' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . __('Generic', 'Remove') . '</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . __('Generic', 'Add') . '</span></p>';
if ($config['user_can_add_img_or_link']) {
echo ' <p>' . __('Step 2 classic', 'It\'s possible to propose links or images by using') . ' <a href="http://' . $locale . '.wikipedia.org/wiki/Markdown">' . __('Step 2 classic', 'the Markdown syntax') . '</a>.</p>';
}
echo ' </div>' . "\n";
if (is_array($registredate) && count($registredate) === 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
// Fields choices : 5 by default
$choices = $form->getChoices();
$nb_choices = max(count($choices), 5);
for ($i = 0; $i < $nb_choices; $i++) {
$choice = $choices[$i] ?? new Choice();
echo '
<div class="form-group choice-field">
<label for="choice' . $i . '" class="col-sm-2 control-label">' . __('Generic', 'Choice') . ' ' . ($i + 1) . '</label>
<div class="col-sm-10 input-group">
<input type="text" class="form-control" name="choices[]" size="40" value="' . $choice->getName() . '" id="choice' . $i . '" />';
if ($config['user_can_add_img_or_link']) {
echo '<span class="input-group-addon btn-link md-a-img" title="' . __('Step 2 classic', 'Add a link or an image') . ' - ' . __('Generic', 'Choice') . ' ' . ($i + 1) . '" ><span class="glyphicon glyphicon-picture"></span> <span class="glyphicon glyphicon-link"></span></span>';
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;
}
}
echo '
</div>
</div>' . "\n";
}
echo '
<div class="col-md-4">
<div class="btn-group btn-group">
<button type="button" id="remove-a-choice" class="btn btn-default" title="' . __('Step 2 classic', 'Remove a choice') . '"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . __('Generic', 'Remove') . '</span></button>
<button type="button" id="add-a-choice" class="btn btn-default" title="' . __('Step 2 classic', 'Add a choice') . '"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . __('Generic', 'Add') . '</span></button>
</div>
</div>
<div class="col-md-8 text-right">
<a class="btn btn-default" href="' . Utils::get_server_name() . 'create_poll.php?type=classic" title="' . __('Step 2', 'Back to step 1') . '">' . __('Generic', 'Back') . '</a>
<button name="fin_sondage_autre" value="' . __('Generic', 'Next') . '" type="submit" class="btn btn-success disabled" title="' . __('Step 2', 'Go to step 3') . '">' . __('Generic', 'Next') . '</button>
</div>
</div>
</div>
<div class="modal fade" id="md-a-imgModal" tabindex="-1" role="dialog" aria-labelledby="md-a-imgModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">' . __('Generic', 'Close') . '</span></button>
<p class="modal-title" id="md-a-imgModalLabel">' . __('Step 2 classic', 'Add a link or an image') . '</p>
</div>
<div class="modal-body">
<p class="alert alert-info">' . __('Step 2 classic', 'These fields are optional. You can add a link, an image or both.') . '</p>
<div class="form-group">
<label for="md-img"><span class="glyphicon glyphicon-picture"></span> ' . __('Step 2 classic', 'URL of the image') . '</label>
<input id="md-img" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-a"><span class="glyphicon glyphicon-link"></span> ' . __('Generic', 'Link') . '</label>
<input id="md-a" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-text">' . __('Step 2 classic', 'Alternative text') . '</label>
<input id="md-text" type="text" class="form-control" size="40" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">' . __('Generic', 'Cancel') . '</button>
<button type="button" class="btn btn-primary">' . __('Generic', 'Add') . '</button>
</div>
</div>
</div>
</div>
</form>
if (empty($form->end_date)) {
// By default, expiration date is 6 months after last day
$form->end_date = $max_expiry_time;
}
<script src="js/app/framadatepicker.js"></script>
<script src="js/app/classic_poll.js"></script>
' . "\n";
// Insert poll in database
$ids = $pollService->createPoll($form);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
bandeau_pied();
}
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('Mail', "This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.");
$message .= '<br/><br/>';
$message .= Utils::htmlMailEscape($form->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlMailEscape($form->title) . '".<br/>';
$message .= sprintf(__('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($poll_id));
$message_admin = __('Mail', "This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above");
$message_admin .= 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', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . $form->title, $message_admin);
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('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;
}

View File

@ -17,6 +17,7 @@
* 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;
@ -30,9 +31,9 @@ include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($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();
@ -40,19 +41,30 @@ if (is_readable('bandeaux_local.php')) {
include_once('bandeaux_local.php');
}
$form = unserialize($_SESSION['form']);
// 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 (isset($form->format) && $form->format !== 'D') {
if ($form->format !== 'D') {
$form->format = 'D';
$form->clearChoices();
}
if (!isset($form->title, $form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
$step = 1;
} else if (!empty($_POST['confirmation'])) {
$step = 4;
@ -63,13 +75,6 @@ if (!isset($form->title, $form->admin_name) || ($config['use_smtp'] && !isset($f
}
switch ($step) {
case 1:
// 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.'));
$smarty->display('error.tpl');
exit;
case 2:
// Step 2/4 : Select dates of the poll
@ -107,7 +112,7 @@ switch ($step) {
// Handle Step2 submission
if (!empty($_POST['days'])) {
// Remove empty dates
$_POST['days'] = array_filter($_POST['days'], static function ($d) {
$_POST['days'] = array_filter($_POST['days'], function ($d) {
return !empty($d);
});
@ -135,18 +140,18 @@ switch ($step) {
$i++;
}
for ($i = 0, $iMax = count($_POST['days']); $i < $iMax; $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', 'datetime_parseformat'), $_POST['days'][$i])->setTime(0, 0, 0);
$time = $date->getTimestamp();
$time = (string) $date->getTimestamp();
$choice = new Choice($time);
$form->addChoice($choice);
$schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT);
for ($j = 0, $jMax = count($schedules); $j < $jMax; $j++) {
for ($j = 0; $j < count($schedules); $j++) {
if (!empty($schedules[$j])) {
$choice->addSlot(strip_tags($schedules[$j]));
}
@ -160,6 +165,7 @@ switch ($step) {
$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) {
@ -171,7 +177,7 @@ switch ($step) {
}
$summary .= '</ul>';
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); // textual date
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); // textual date
$_SESSION['form'] = serialize($form);
@ -181,15 +187,35 @@ switch ($step) {
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->display('create_classic_poll_step3.tpl');
$smarty->display('create_poll_step_3.tpl');
exit;
case 4:
// Step 4 : Data prepare before insert in DB
// Define expiration date
$expiration_date = $inputService->parseDate($_POST['enddate']);
$form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
$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);
@ -218,8 +244,7 @@ switch ($step) {
// Clean Form data in $_SESSION
unset($_SESSION['form']);
// Delete old polls
$purgeService->purgeOldPolls();
$purgeService->repeatedCleanings();
// creation message
$sessionService->set("Framadate", "messagePollCreated", TRUE);
@ -227,4 +252,12 @@ switch ($step) {
// 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;
}

View File

@ -37,14 +37,12 @@ $pollRepository = RepositoryFactory::pollRepository();
/* ---- */
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
if ($form === null && !($form instanceof Form)) {
if ($form === null || !($form instanceof Form)) {
$form = new Form();
}
// Type de sondage
if ((isset($_GET['type']) && $_GET['type'] === 'date') ||
(isset($_POST['type']) && $_POST['type'] === 'date')
) {
if (isset($_GET['type']) && $_GET['type'] === 'date') {
$poll_type = 'date';
$form->choix_sondage = $poll_type;
} else {
@ -57,21 +55,23 @@ $goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['op
if ($goToStep2) {
$title = $inputService->filterTitle($_POST['title']);
$use_ValueMax = isset($_POST['use_ValueMax']) && $inputService->filterBoolean($_POST['use_ValueMax']);
$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']);
$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 = mb_substr($inputService->filterName($_POST['name']), 0, 32);
$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']);
$receiveNewComments = isset($_POST['receiveNewComments']) && $inputService->filterBoolean($_POST['receiveNewComments']);
$hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
$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]]);
$password = $_POST['password'] ?? null;
$password_repeat = $_POST['password_repeat'] ?? null;
$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
@ -96,6 +96,7 @@ if ($goToStep2) {
$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);
@ -235,7 +236,7 @@ if (!empty($_POST[GO_TO_STEP_2])) {
if ($error_on_customized_url) {
$errors['customized_url']['aria'] = 'aria-describeby="customized_url" ';
$errors['customized_url']['class'] = ' has-error';
$errors['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.");
$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) {
@ -248,10 +249,6 @@ if (!empty($_POST[GO_TO_STEP_2])) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['name']['class'] = ' has-error';
$errors['name']['msg'] = __('Error', 'Enter a name');
} elseif (mb_strlen($inputService->filterName($_POST['name'])) > 32) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['name']['class'] = ' has-error';
$errors['name']['msg'] = __('Error', "Name is limited to 32 characters");
} elseif ($error_on_name) {
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
$errors['name']['class'] = ' has-error';
@ -301,6 +298,7 @@ $smarty->assign('customized_url', Utils::fromPostOrDefault('customized_url', $fo
$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));

7
css/easymde.min.css vendored

File diff suppressed because one or more lines are too long

7
css/simplemde.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -64,6 +64,8 @@ a:focus { /* a11y */
outline:#000 dotted 1px;
}
header, footer {
}
main {
margin-top: 20px;
}
@ -183,7 +185,7 @@ caption {
font-weight:bold;
}
.results a.btn-default.btn-sm, .best-choice .list-unstyled a.btn-default.btn-sm {
.results a.btn-default.btn-sm {
padding: 3px 7px;
font-size: 0.7em;
}
@ -447,19 +449,10 @@ span.edit-username-left {
border-color: #949494 !important;
}
table.results .bg-danger .glyphicon {
/* TODO : Refactor me ! */
table.results .bg-danger .glyphicon:not(.glyphicon-alert) {
opacity:0;
-moz-animation-name: hideNoIcon;
-moz-animation-iteration-count: 1;
-moz-animation-timing-function: ease-in;
-moz-animation-duration: 2s;
-webkit-animation-name: hideNoIcon;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in;
-webkit-animation-duration: 2s;
animation-name: hideNoIcon;
animation-iteration-count: 1;
animation-timing-function: ease-in;

40
docker-compose.yml Normal file
View File

@ -0,0 +1,40 @@
version: '3'
services:
db:
container_name: framadate-db
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=rootpassword
- MYSQL_USER=framadate
- MYSQL_PASSWORD=framadatedbpassword
- MYSQL_DATABASE=framadate
restart: always
ports:
- "3307:3306"
framadate:
container_name: framadate
build:
dockerfile: ./docker/stretch/Dockerfile
context: .
depends_on:
- db
ports:
- 80:80
environment:
- ENV=dev
- DOMAIN=localhost
- APP_NAME=Framadate
- ADMIN_MAIL=mon@email.fr
- MYSQL_USER=framadate
- MYSQL_PASSWORD=framadatedbpassword
- MYSQL_DB=framadate
- MYSQL_HOST=db
- MYSQL_PORT=3307
- ADMIN_USER=admin
- ADMIN_PASSWORD=adminpassword
- DISABLE_SMTP=true
- APACHE_RUN_USER=#1000
restart: always
volumes:
- '.:/var/www/framadate'

30
docker/stretch/Dockerfile Normal file
View File

@ -0,0 +1,30 @@
FROM php:apache
MAINTAINER kyane@kyane.fr
RUN apt-get -y update && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq zip unzip git zlib1g-dev libicu-dev g++ mysql-client
RUN docker-php-ext-install intl && docker-php-ext-install pdo_mysql
RUN a2enmod rewrite
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
COPY docker/stretch/php.ini /usr/local/etc/php/php.ini
COPY docker/stretch/apache-framadate.conf /etc/apache2/sites-enabled/framadate.conf
COPY docker/stretch/entrypoint.sh /usr/local/bin/entrypoint
ENV COMPOSER_ALLOW_SUPERUSER=1
RUN set -eux; \
composer global require "hirak/prestissimo:^0.3" --prefer-dist --no-progress --no-suggest --classmap-authoritative; \
composer clear-cache
ENV PATH="${PATH}:/root/.composer/vendor/bin"
ENV COMPOSER_ALLOW_SUPERUSER 0
WORKDIR /var/www/framadate
# Some Apache and PHP configuration
RUN if [ "$ENV" = "dev" ] ; then echo Using PHP production mode ; else echo Using PHP development mode && echo "error_reporting = E_ERROR | E_WARNING | E_PARSE\ndisplay_errors = On" > /usr/local/etc/php/conf.d/php.ini ; fi
RUN rm /etc/apache2/sites-enabled/000-default.conf
EXPOSE 80
ENTRYPOINT ["entrypoint"]

Some files were not shown because too many files have changed in this diff Show More