Compare commits

...

818 Commits

Author SHA1 Message Date
ff6abe4001 Mise à jour lien vers Studs 2023-08-17 17:54:55 +02:00
6d8c60c88b Fix ability to update an existing poll (6122) 2023-03-26 11:15:46 +02:00
obitanz
cd9b00d0e9 Correction erreur de syntaxe 2023-02-25 15:56:31 +01:00
obitanz
36681dcdea 23-12-2021
- Remove the X-Mailer header in e-mails, as this causes some email servers to see emails sent by Framadate as spam
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEExMITpfxOHHCvHn8FoGG53eDKB3MFAmHElvwACgkQoGG53eDK
 B3PO3w/+LyyC4Y7fDtl4hm9ClIswp62ozhxlIzRDJbza2S2S37A0ssXJMYCf2VOR
 ak+vWFUs9xj3o6G3oGlhLF1KMpWJ/nFVhyhCGgV1mQmUHSX2gnn6S9PBFenOGc9w
 zD2onJhVhJ/tbCJbF0Yl6zPitkfiucJI3FFIauByhkZ8wI3MPIYo/2m+H7KeVwLp
 YgJU0VqUQNMYl1clC+9Vu6gpSk/f6RklEVzJfEgV4MWMfoM6hLFqo0F6IDyWmgKp
 eNi1KHmFBGKFIexQeI+AvDyJyYsZH7uJrB0F7bV4wJGS7MTPOJlOJG5MXlLDcJgK
 EKNFUqrb9pQT5TWkO5FbJjfHwbP5bhO7TAEEvdIzhFqziR6gqdr2uXi+HiwG7yHO
 21YzDMnRZPqiYTBJuP0XIhsJrgpIBQDuQ3u9XJTs4pfyxQDSR8m9sHdiPSEZySRN
 p7XJSsDyLhcjjAobdodLuZLSWqOvZfaHzXEEKZO3HdQmccbwRHvniXDEy0FHEt2A
 L7LVvd4Qpa4wi6T2b3UIK4ubuC4xYdtYBfpzOklcti5TwDT2jbSTJRbv5oGAPX1b
 HfMzMoi/os3ZDSnPPRUaBxxrbjFQ9bX4bdxfQZeC2XIBWMBOcF3SeSbC1vVlcu+3
 bgwo3mehLvBCY0uMCGrjdymcAQq2IwX5Df8dGS7jFWUS5rAUi7s=
 =OVGk
 -----END PGP SIGNATURE-----

Merge tag '1.1.19' into chapril-1.1.19
2023-02-25 12:11:22 +01:00
Thomas Citharel
125045bb74
Release 1.1.19
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-23 16:34:08 +01:00
Thomas Citharel
c2f20b1ab2
Add CHANGELOG entry for 1.1.19
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-23 16:33:55 +01:00
Thomas Citharel
cffbeaf51d Merge branch 'remove-php-mailer-x-mailer-header' into 'v1.1.x'
Remove PHPMailer X-Mailer header

See merge request framasoft/framadate/framadate!500
2021-12-23 15:31:16 +00:00
Thomas Citharel
7343463c74
Remove PHPMailer X-Mailer header
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-23 16:29:31 +01:00
Thomas Citharel
7465852eb4
Merge pull request #71 from damufo/master
Galician translation
2021-12-21 11:23:09 +01:00
Thomas Citharel
f30fe54c4c
Update README.md
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 11:18:43 +01:00
Thomas Citharel
ac932e55f6
Add release file
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 10:53:40 +01:00
Thomas Citharel
fb04860c78 Merge branch 'fixes' into 'v1.1.x'
Release 1.1.18

See merge request framasoft/framadate/framadate!499
2021-12-21 09:51:25 +00:00
Thomas Citharel
9322a41d0c
Release 1.1.18
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 10:50:02 +01:00
Thomas Citharel
65cbc8b4b4
Cleanup CI
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 10:50:02 +01:00
Thomas Citharel
fc353ce708
Fix another missing translation in the french file
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 10:31:07 +01:00
Thomas Citharel
7603bed6d9
Modernize project
- Use PHP typings
- Update some front-end libraries

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-21 10:31:07 +01:00
Thomas Citharel
6144f33e9f
Update CHANGELOG.md with missing entries
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-20 11:08:39 +01:00
Thomas Citharel
2995851e2a Merge branch 'fixes' into 'v1.1.x'
Various fixes

Closes #566

See merge request framasoft/framadate/framadate!498
2021-12-17 14:23:42 +00:00
Thomas Citharel
0d4bbe8f2d
Allow /abc/ as well, for Framasoft
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 15:21:05 +01:00
Thomas Citharel
dffd7edf42
Improve metadata on poll pages
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 15:14:29 +01:00
Thomas Citharel
e7f7e26141
Fix a missing translation in the french file
Closes #566

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 15:00:43 +01:00
Thomas Citharel
3de07eb565
Activate Catalan language
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:58:46 +01:00
Thomas Citharel
9239954123
Upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:54:07 +01:00
Thomas Citharel
76e59902e4
Fix depreciated return boolean value from uasort
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:54:06 +01:00
Thomas Citharel
2c3148fe9e
Fix some HTML structure and JS formatting
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:54:06 +01:00
Thomas Citharel
5a5c233a5e
Refactor calculating max/min expiration date and enforce on poll edition
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:54:06 +01:00
Thomas Citharel
9c969f8896
Cleanup some invalid HTML
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:09:52 +01:00
Thomas Citharel
3b9dcd8085
Replace SimpleMDE with EasyMDE fork
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-12-17 14:09:52 +01:00
Thomas Citharel
bd12f33e12 Update README.md
Closes #565
2021-11-30 10:14:30 +00:00
Thomas Citharel
f877e4a27f Merge branch 'add-version-to-latest-zip' into 'v1.1.x'
Add version to latest zip

See merge request framasoft/framadate/framadate!497
2021-10-21 07:52:59 +00:00
Luc Didry
86934e9753
👷 — Automatically add VERSION file to release zip file 2021-10-21 09:42:38 +02:00
Luc Didry
12ce504421
🌐 — Remove Zanata stuff (we use weblate now) 2021-10-21 09:42:09 +02:00
Thomas Citharel
0aa11211f3
Add french translation for ICS feature
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-10-18 16:51:09 +02:00
Thomas Citharel
61a63c55bd
CSS and a11y improvements for export to ics feature
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-10-18 16:50:51 +02:00
Thomas Citharel
03dbceea5a
Use Content-Type: text/calendar for ics files
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-10-18 16:50:34 +02:00
Thomas Citharel
d971e015a6 Merge branch 'update-deps' into 'v1.1.x'
Update deps and bump to 1.1.17

See merge request framasoft/framadate/framadate!496
2021-10-18 14:30:59 +00:00
Thomas Citharel
bfd2fe5351
Bump to 1.1.17
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-10-18 16:29:55 +02:00
Thomas Citharel
d87243873f
Update deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-10-18 16:29:55 +02:00
Thomas Citharel
6e40f1cf02 Merge branch 'fix-graph-xss' into 'v1.1.x'
Fix an XSS in the result graph

See merge request framasoft/framadate/framadate!493
2021-10-18 14:12:34 +00:00
ff62406853 Added version number (#4871). 2021-08-28 18:44:20 +02:00
8d9770c584 Fixed typography. 2021-08-28 16:25:02 +02:00
eed7da32bd Merge tag '1.1.16' into chapril-1.1.16 2021-08-28 15:59:04 +02:00
Thomas Citharel
02229c671b
Fix an XSS in the result graph
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-07-19 11:17:00 +02:00
Thomas Citharel
e0028dc813 Merge branch 'ical-1.1.x-backport' into 'v1.1.x'
[1.1.x backport] Allow downloading ics/ical files for best choices

See merge request framasoft/framadate/framadate!482
2021-04-22 07:23:40 +00:00
Thomas Citharel
ecee1b5025 Merge branch 'smtp-authtype-1.1.x-backport' into 'v1.1.x'
[1.1.x backport] MailService: Allow configuring AuthType.

See merge request framasoft/framadate/framadate!484
2021-04-21 18:09:22 +00:00
Kevin Kofler
ab211a93e8 MailService: Allow configuring AuthType.
This works around a broken mail server that claims to support CRAM-MD5,
but then actually does not.
2021-04-21 19:49:50 +02:00
Kevin Kofler
6ec188da23 ICalService: Support start_time-end_time time specs
E.g., "13:00-14:00".
2021-04-21 19:44:06 +02:00
Michael Schär
709b48f293 Allow downloading ics/ical files for best choices
Backported to 1.1.x by Kevin Kofler.
2021-04-21 19:43:57 +02:00
Thomas Citharel
b46430ebe2
Bump version to 1.1.16
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-22 19:06:57 +01:00
Thomas Citharel
36cef8cc39
Actually enforce the length limitation on the poll creator name
Show a nice error message if it's too long (not on the edit page because
there's no support for this right now)

mbstring is now a required extension

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-22 19:04:44 +01:00
Thomas Citharel
a5c7df64b2
Bump version to 1.1.15
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-22 18:10:56 +01:00
Thomas Citharel
c266373344
Add DOMPurify to sanitize markdown
Closes #546

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-22 15:56:09 +01:00
Thomas Citharel
76f936b0cf
Bump version to 1.1.14
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-08 19:04:54 +01:00
Thomas Citharel
fa6bd17365 Merge branch 'add-maxlength-for-author-name' into 'v1.1.x'
Add a maxlength attribute for the author name

See merge request framasoft/framadate/framadate!475
2021-03-08 18:04:11 +00:00
Thomas Citharel
2ce6e56f8a
Add a maxlength attribute for the author name
Closes #530

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-08 18:52:14 +01:00
Thomas Citharel
9dd52be963
Bump version to 1.1.13
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-08 16:31:18 +01:00
Thomas Citharel
299b87a2b6 Merge branch 'fix-poll-closed' into 'v1.1.x'
Fix poll not possible to be closed

See merge request framasoft/framadate/framadate!474
2021-03-08 15:30:44 +00:00
Thomas Citharel
00ad08d037
Fix poll not possible to be closed
Closes #532

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2021-03-08 16:27:42 +01:00
Thomas Citharel
12d4bdb857 Merge branch 'master' into 'v1.1.x'
Add datapicker for Occitan

See merge request framasoft/framadate/framadate!469
2021-01-13 19:02:35 +01:00
Quentin
15e72d6957 Upload New File 2021-01-13 18:37:30 +01:00
Thomas Citharel
dda9c851ab
Bump version to 1.1.12
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-21 11:28:28 +01:00
Thomas Citharel
cb7823574e
Bump php version to 7.3
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-21 11:28:13 +01:00
Thomas Citharel
eab3150ba6
Bump version to 1.1.11
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-21 11:28:13 +01:00
Thomas Citharel
69e69efe32
Fix leftovers
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-18 17:37:21 +01:00
Thomas Citharel
dcd30e0974
Fix translation keys missing into emails
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-18 17:37:21 +01:00
Thomas Citharel
d0e01434b7
Fix nested ca translations
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2020-12-18 17:37:20 +01:00
f1587f7b36 Ajout d'un test pour éliminer les colonnes choices vides dans la table fd_vote (#4617) 2020-08-26 12:00:37 +02:00
9068919c47 Added 3 keys in ../locale/*.json (#4620,#4619,#4618) 2020-08-11 21:53:13 +02:00
c269048008 Modified message in fr_FR.json (#4542) 2020-06-10 15:59:20 +02:00
4ec047218a Added keys for date poll (#3921) 2020-03-11 17:21:37 +01:00
d0ffcd0dba Suppressed comment in index.tpl 2020-03-10 16:52:08 +01:00
fe0558955b Modified again key for messages (#4299) 2020-03-10 14:54:25 +01:00
a48b48abc8 Modified key for messages (#4299) 2020-03-10 14:43:10 +01:00
75ee2ef419 Changed meta description to DateChaprilOrg (#4294) 2020-03-01 16:49:02 +01:00
Antoni Serrano i Cortès
8357c0e757 Translated using Weblate (Catalan)
Currently translated at 100.0% (401 of 401 strings)

Translation: Framadate/Framadate v1.1.x
Translate-URL: https://weblate.framasoft.org/projects/framadate/framadate-v11x/ca/
2020-02-07 12:09:10 +01:00
Thomas Citharel
6d2101a3cb Merge branch 'weblate-framadate-framadate-v11x' into 'v1.1.x'
Update from Weblate

See merge request framasoft/framadate/framadate!424
2020-02-06 15:59:01 +01:00
Thomas Citharel
1b87fac9fa Translated using Weblate (Catalan)
Currently translated at 93.3% (374 of 401 strings)

Translation: Framadate/Framadate v1.1.x
Translate-URL: https://weblate.framasoft.org/projects/framadate/framadate-v11x/ca/
2020-02-06 15:58:13 +01:00
Thomas Citharel
de56c42d64 Added translation using Weblate (Catalan) 2020-02-06 15:44:39 +01:00
f6855dce8f Directory tools moved to srv (#4127) 2020-01-31 16:57:12 +01:00
6030c3da3a Corrected bug in rapport_activite.sh (#3528) 2020-01-01 23:07:28 +01:00
163630bc66 renamed date.chapril.org.cron (#4089) 2019-12-27 22:17:52 +01:00
a540df01ef Added check_datechaprilorg_update (#3591) 2019-12-07 19:13:11 +01:00
2db36d63bd Added date.chapril.org.cron (#4087) 2019-12-07 13:03:10 +01:00
415637f4f8 Improved statistics on ip (#3332) 2019-10-01 16:56:10 +02:00
88c62dc13d Added user count with ipv4 or ipv6 2019-09-18 23:03:33 +02:00
51b27b6b73 Normalized Apache access log file. 2019-08-14 17:02:23 +02:00
bbe505ed41 Correction d'un bug dans le script ligne 86 base decimale forcée (#3528) 2019-08-01 22:23:13 +02:00
0bab6f939f Added statistics on users (#3332) 2019-07-12 19:18:23 +02:00
c6fda43581 Merge tag '1.1.10' into chapril-1.1.9 to create chapril-1.1.10. 2019-05-10 18:50:09 +02:00
fd91b558a3 Merge tag '1.1.9' into chapril-1.1.8 to create chapril-1.1.9. 2019-05-10 18:31:03 +02:00
Thomas Citharel
e8747ffef6 Merge branch 'release' into 'master'
Version 1.1.10

See merge request framasoft/framadate/framadate!402
2019-05-06 12:06:06 +02:00
Thomas Citharel
0900c92fd6 Merge branch 'add-link-to-release' into 'master'
[CI] Add link of artifact to release’s assets

See merge request framasoft/framadate/framadate!401
2019-05-06 11:59:21 +02:00
Thomas Citharel
b30d7f2076
Version 1.1.10
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-05-06 11:58:01 +02:00
Luc Didry
ee8a423e00
[CI] 🏗️ Add link of artifact to release’s assets 2019-05-02 10:36:25 +02:00
23f933595b Removed list of web browers (#3626) 2019-03-25 17:09:06 +01:00
a75ce8aab5 Improved english translation of FOOTER (#3250) 2019-03-10 17:42:11 +01:00
b108cbbd15 Added directory tools and script rapport_activite.sh (#3592) 2019-02-20 15:29:17 +01:00
4d58bc9371 Updated locale/fr.json and fr_FR.json (#3538,#3493,#3491) 2019-01-13 22:49:04 +01:00
Thomas Citharel
bf3313118a Merge branch 'push-artifacts-to-tags-notes' into 'master'
On tag, upload artifacts to project and update tag note

See merge request framasoft/framadate/framadate!356
2019-01-07 11:16:06 +01:00
Luc Didry
ec53d9c504
On tag, upload artifacts to project and update tag note 2019-01-07 10:11:03 +01:00
JosephK
5b29497859 Merge branch 'patch-1' into 'master'
.git folder deleted before creating latest.zip

See merge request framasoft/framadate/framadate!355
2019-01-07 08:45:00 +01:00
JosephK
30045fd56d .git folder deleted before creating latest.zip 2019-01-07 08:44:36 +01:00
add73d3189 Customization of another text for DateChaprilOrg in locale/en.json (#3538) 2019-01-04 23:22:53 +01:00
80f99c9c68 Customization of text for DateChaprilOrg in locale/en.json (#3538) 2019-01-04 23:17:45 +01:00
f3aab00199 Customization of text for DateChaprilOrg in locale/fr.json (#3538) 2019-01-04 23:06:54 +01:00
b9715d7142 Improved message 'où sont mes sondages?' (#3493) 2019-01-01 22:08:44 +01:00
2c7d79dca2 Modified third section in locale/fr.json (#3491) 2018-12-31 12:42:19 +01:00
4cf50cb7bf modified the mail text of 'where are my polls' (#3493) 2018-12-28 21:51:05 +01:00
1bf1656d87 personalized third section of comment (#3491) 2018-12-28 21:34:53 +01:00
8d76fae8a1 renamed logo-DateChaprilOrg.png (#3478) 2018-12-28 17:52:10 +01:00
Thomas Citharel
0e72dafbf6 Merge branch 'release-1.1.9' into 'master'
Release 1.1.9

See merge request framasoft/framadate/framadate!353
2018-12-04 18:23:24 +01:00
Thomas Citharel
e35589e1a8
Bump version
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-12-04 18:22:32 +01:00
Yoann
4fa7ae12d4
Fix wrong display of email subject with a date poll
fix parenthesis

Fix

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-12-04 18:18:56 +01:00
Thomas Citharel
15e1d1cc18 Merge branch 'backport-session-fix' into 'master'
backport session fix

See merge request framasoft/framadate/framadate!352
2018-12-04 18:08:23 +01:00
Lazare Olivry
ca347b024d backport session fix 2018-12-04 18:02:54 +01:00
4106e2ca96 Changed new logo DateChaprilOrg 2018-12-02 15:34:16 +01:00
Thomas Citharel
faa2ee1289 Merge branch 'patch-1' into 'master'
Set $rowDatetime to int for comparison

See merge request framasoft/framadate/framadate!350
2018-11-05 16:25:04 +01:00
Thomas Citharel
46338cc078 Set $rowDatetime to int for comparison
Closes #379
2018-11-05 16:22:04 +01:00
cb10c87d07 Added script for automatic poll purge (#3150). 2018-08-15 11:28:41 +02:00
ebe9add057 Fixed mistaken message about expiry date (#3205). 2018-08-13 16:02:06 +02:00
13c2543a7a Updated AdminPollService to correct bug on end_date 2018-08-13 12:19:06 +02:00
c2eb45666a Updated fr.json to suppress <br/> 2018-08-12 16:03:58 +02:00
30c94c7aa1 Merge remote-tracking branch 'origin/master' into chapril-1.1.8
mise à jour vers 1.1.8
2018-08-12 15:00:08 +02:00
4fe0b2097f Updated AdminPollService to limit end_date (#3206). 2018-08-08 22:56:34 +02:00
f4debb7da8 Updated one little mistake. Suppressed one blank character 2018-08-07 23:18:58 +02:00
506c56aa28 Updated one word forgetten 2018-08-07 22:36:08 +02:00
782a42d571 Updated according with agir issue 3205 2018-08-07 22:32:25 +02:00
205c9230ad Updated one word for test 2018-08-07 21:55:21 +02:00
46356b9744 Changed pushline APRIL instead of Framasoft. 2018-08-03 20:33:52 +02:00
25def10867 Improved English translation 2018-08-03 17:26:20 +02:00
Thomas Citharel
e3060e18d2 Merge branch 'master-merge' into 'master'
Master merge

See merge request framasoft/framadate!329
2018-08-03 14:37:51 +02:00
Thomas Citharel
84ac58c430 Release 1.1.8
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-08-03 14:30:09 +02:00
Thomas Citharel
79586319fc Add session.cookie_httponly = 1 to local php.ini
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-08-03 14:29:45 +02:00
Thomas Citharel
70db1e91e0 Release v1.1.8-beta.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-08-03 14:07:02 +02:00
Thomas Citharel
5ffd4361e4 Fix #358, #355 and #342
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-08-03 14:06:23 +02:00
fcfc8786fa Updated English punch line 2018-08-03 12:53:57 +02:00
7ba226af4a Removed <br>, custom the email. 2018-08-01 17:39:18 +02:00
07368eb2d5 updated fr_FR.json 2018-08-01 17:29:57 +02:00
9a6784c6a6 Merge remote-tracking branch 'origin/develop' tag1.1.7 into chapril
Conflicts:
	locale/fr.json
2018-07-28 17:57:58 +02:00
damufo
2badf15074 Galician translation 2018-07-10 19:04:34 +02:00
Thomas Citharel
b40957f484 Bump version
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 15:51:05 +02:00
Thomas Citharel
a6e337e046 Fix issue with limit of participants
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 15:50:46 +02:00
Thomas Citharel
0da24788d0 Bump version
(yeah, forgot last one)

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 12:18:22 +02:00
Thomas Citharel
cad9dac189 Fix an issue with smarty filters and priority
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 12:17:59 +02:00
Thomas Citharel
bc28281477 Update deps and bump PHPMailer to version 6
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 12:17:33 +02:00
Thomas Citharel
be128e8158 Handle XSS issue on date poll slots
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 10:56:09 +02:00
m
a1a7d180f8 fork-awesome enabled by default 2018-07-09 10:26:12 +02:00
m
15e78ba070 Better handle undefined config values
https://framagit.org/framasoft/framadate/issues/322
2018-07-09 10:26:06 +02:00
Thomas De Backer
88fb61660a restrict custom urls
fixed coding standard issues

added error message to locale

Remove unecessary variable

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-07-09 10:25:14 +02:00
a31dcd60d6 Removed initial htaccess.txt file (previously renamed .htaccess). 2018-05-27 12:37:07 +02:00
7088684b2e Add phpinfo.php file for easy check. 2018-05-27 12:19:28 +02:00
f62e0408de Personnalisation du pied de page de courriel. 2018-05-06 11:50:15 +02:00
Thomas Citharel
59569071e8 Merge branch '1.1.4-bugfix' into 'master'
1.1.4 bugfix

See merge request framasoft/framadate!301
2018-04-15 12:23:57 +02:00
Thomas Citharel
30e77a77c4
Version 1.1.4
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-15 12:21:56 +02:00
Thomas Citharel
367b239549
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:21:27 +02:00
Thomas Citharel
8ce671e4d9 Merge branch '1.1.3-bugfix' into 'master'
1.1.3 bugfix

See merge request framasoft/framadate!299
2018-04-15 11:35:10 +02:00
Thomas Citharel
901f907dd4
Release Version 1.1.3
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-15 11:33:39 +02:00
m
a80602e3d5
bug/error-on-empty-choices 2018-04-15 11:30:28 +02:00
Thomas Citharel
53572dbc1f Merge branch 'develop' into 'master'
Version 1.1.2

See merge request framasoft/framadate!295
2018-04-11 18:08:01 +02:00
Thomas Citharel
b2fc3a9d53
Remove unneeded Parsedown option
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 18:06:45 +02:00
Thomas Citharel
baadea8e74 Merge branch 'security/parsedown-safe-mode' into 'develop'
Enable Parsedown Safe-Mode

Closes #226

See merge request framasoft/framadate!293
2018-04-11 17:52:57 +02:00
Thomas Citharel
075ba1739c
Enable Parsedown Safe-Mode
Also remove the setUrlsLinked(false) so that we can haz urls

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 17:50:49 +02:00
Thomas Citharel
9829c826ef Merge branch 'develop' into 'master'
Version 1.1.1

See merge request framasoft/framadate!292
2018-04-11 15:55:16 +02:00
Thomas Citharel
32492cf119
Update version to 1.1.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 15:52:50 +02:00
Thomas Citharel
c5a2f299c5 Merge branch 'bug/send-voters-email' into 'develop'
Fix link sent by email to voters

Closes #312

See merge request framasoft/framadate!291
2018-04-11 15:51:54 +02:00
Thomas Citharel
d72cf4ce24
Fix link sent by email to voters
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 15:50:07 +02:00
Thomas Citharel
f931516f24 Merge branch 'develop' into 'master'
Release 1.1.0

See merge request framasoft/framadate!290
2018-04-11 10:14:24 +02:00
Thomas Citharel
4035193377
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 10:08:14 +02:00
Thomas Citharel
4838577d9d
Release 1.1.0
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-11 09:59:05 +02:00
Luc Didry
8e8b8a63cb [zanata] fix zanata --min-doc-percent option 2018-04-10 22:36:20 +02:00
Luc Didry
3db84de4aa [zanata] pull locales only if translated at 50% or more 2018-04-10 22:14:10 +02:00
Thomas Citharel
2a33310d4d Merge branch 'build-only-on-stable-tags' into 'develop'
Build artifacts only on stable tags

Closes #306

See merge request framasoft/framadate!289
2018-04-10 21:57:20 +02:00
Thomas Citharel
ff7bf2b2a2
Build artifacts only on stable tags
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-10 21:56:01 +02:00
Thomas Citharel
45b646b178 Merge branch 'develop' into 'master'
Version 1.1.0-beta.4

See merge request framasoft/framadate!287
2018-04-10 12:49:52 +02:00
Thomas Citharel
8e6373c964
Version 1.1.0-beta.4
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-10 12:43:47 +02:00
Thomas Citharel
a092777657 Merge branch 'feature/message-on-poll-creation' into 'develop'
ajout du message "Le sondage est bien créé."

See merge request framasoft/framadate!286
2018-04-08 21:56:22 +02:00
Thomas Citharel
412b409397 Merge branch 'bug/vote-rempli-si-erreur-nom' into 'develop'
Bug/vote rempli si erreur nom

See merge request framasoft/framadate!284
2018-04-08 21:54:24 +02:00
Thomas Citharel
be1f29773b Merge branch 'bug/test-name-on-vote-edit' into 'develop'
bug/test name on vote edit

See merge request framasoft/framadate!282
2018-04-08 21:53:53 +02:00
m
46df3b6fce change test 2018-04-08 11:29:03 +02:00
m
addaa95ce7 using SessionService 2018-04-08 11:21:11 +02:00
m
852c2c0d2a HTML 5 and fixes 2018-04-08 10:46:46 +02:00
m
402f2abf64 ajout du message "Le sondage est bien créé."
https://framagit.org/framasoft/framadate/issues/62
2018-04-07 22:32:52 +02:00
m
179235eaf9 refactoring vote checks 2018-04-06 21:16:28 +02:00
m
c4f27dc6e0 code style 2018-04-06 14:04:07 +02:00
m
f921d383a9 bug/test-name-on-vote-edit 2018-04-06 14:00:20 +02:00
m
c180090a59 rebase
Merge branch 'bug/vote-rempli-si-erreur-nom' of https://framagit.org/mm/framadate into bug/vote-rempli-si-erreur-nom
2018-04-06 12:20:53 +02:00
m
659cb1ec75 correction CI 2018-04-06 12:19:15 +02:00
m
445dac2e51 Ne pas effacer les sélections après l'erreur "Vous avez déjà voté"
https://framagit.org/framasoft/framadate/issues/194
2018-04-06 12:19:15 +02:00
Thomas Citharel
20e5f5c130 Merge branch 'develop' into 'master'
Version 1.1.0-beta.3

See merge request framasoft/framadate!281
2018-04-06 12:04:45 +02:00
Thomas Citharel
cb03c5c054
Check locales only on develop
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 11:51:08 +02:00
Thomas Citharel
f470d63ab0 Merge branch 'improve-vote-link' into 'develop'
Improve the vote link reminder function

Closes #307

See merge request framasoft/framadate!280
2018-04-06 11:48:05 +02:00
Thomas Citharel
2cf458960e
Improve the vote link reminder function
Move the link to a clickable button that copies the link inside the clipboard, and triggers a 2 sec popover to tell it to the user
If the clipboard API fails, the user should be redirected directly to the link

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 11:44:11 +02:00
Thomas Citharel
f2078ce2f5 Merge branch 'debug-ci-artifacts' into 'develop'
Debug CI Artifacts

Closes #304

See merge request framasoft/framadate!279
2018-04-06 10:07:09 +02:00
Thomas Citharel
a4ad96c78f
Put correct rights on produced zip
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 10:06:34 +02:00
Thomas Citharel
e6f6df5eb6
Version 1.1.0-beta.3
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 09:53:50 +02:00
Thomas Citharel
c27c747726 Merge branch 'improve-tests' into 'develop'
Improve tests a bit

See merge request framasoft/framadate!278
2018-04-06 09:48:20 +02:00
Thomas Citharel
f726adf2ee Merge branch 'bug/undefined-offset-on-best-choices' into 'develop'
bug/undefined-offset-on-best-choices

See merge request framasoft/framadate!277
2018-04-06 09:46:50 +02:00
Thomas Citharel
2c3dfa8f59
Improve tests a bit
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 09:46:30 +02:00
Thomas Citharel
08f7ca141d
CS
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 09:43:32 +02:00
Thomas Citharel
61ac8fcf76
Refactor a bit
Also fix utf8 issue on licence file

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-06 09:40:53 +02:00
Thomas Citharel
37df499a37 Merge branch 'bug/send_edit_link_by_email_action' into 'develop'
correction : Les mails aux sondés contenant le lien d'édition du vote ne partent pas

See merge request framasoft/framadate!276
2018-04-06 09:13:39 +02:00
m
c79b32984d bug/undefined-offset-on-best-choices 2018-04-05 17:34:43 +02:00
Luc Didry
0151361cc9 [i18n] Add check-trad job + pull locales when deploying to beta 2018-04-05 10:46:55 +02:00
Luc Didry
9ce586347b [i18n] Update translation 2018-04-05 09:12:09 +02:00
m
4e4e21cef2 correction CI 2018-04-04 23:01:57 +02:00
m
54fd8464d9 Ne pas effacer les sélections après l'erreur "Vous avez déjà voté"
https://framagit.org/framasoft/framadate/issues/194
2018-04-04 22:18:53 +02:00
m
a003254c7d Les mails aux sondés contenant le lien d'édition du vote ne partent pas
https://framagit.org/framasoft/framadate/issues/221
2018-04-04 20:03:55 +02:00
Thomas Citharel
5b4b8a8606 Merge branch 'feature/adresse-email-Nom-de-domaine-internationalisé-2' into 'develop'
Feature/adresse email nom de domaine internationalisé

Closes #233

See merge request framasoft/framadate!274
2018-04-03 20:30:57 +02:00
Thomas Citharel
a367a4abac Merge branch 'feature/lien-edition-vote' into 'develop'
Donner le lien d'édition d'un vote lorsqu'on est administrateur

See merge request framasoft/framadate!267
2018-04-03 20:20:01 +02:00
m
25e62c238d Merge branch 'mm/framadate-feature/adresse-email-Nom-de-domaine-internationalisé' into develop 2018-04-03 20:07:49 +02:00
m
6f257f416a fix code style 2018-04-03 14:09:18 +02:00
m
599f3104f6 new test for InputService -> filterMail 2018-04-03 13:21:47 +02:00
Mathieu
1d8244fd99 correction : filtre adresse e-mail avec nom de domaine internationalisé
https://framagit.org/framasoft/framadate/merge_requests/268
2018-04-03 12:30:37 +02:00
Thomas Citharel
3c9b8d3708 Merge branch 'trim-mail' into 'develop'
add trim function in order to delete the blank characters in mail input

See merge request framasoft/framadate!272
2018-04-03 09:36:25 +02:00
Anael
9da92fd775
issues 237, add trim function in order to delete the blank characters in mail input 2018-04-03 09:32:29 +02:00
Thomas Citharel
72de7bff1c Merge branch 'bug/sondage-avec-smtp-désactivé' into 'develop'
correction : Adresse e-mail d'un sondage avec SMTP désactivé

See merge request framasoft/framadate!270
2018-04-03 09:27:37 +02:00
m
d1870e516e
Adresse e-mail d'un sondage avec SMTP désactivé
https://framagit.org/framasoft/framadate/issues/301

retour pour recherche d'une autre solution

Adresse e-mail d'un sondage avec SMTP désactivé
https://framagit.org/framasoft/framadate/issues/301

CS

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-03 09:26:31 +02:00
Thomas Citharel
56d6e015c1 Merge branch 'add-git-to-beta-deploy' into 'develop'
Add git to beta.framadate.org deploy so that we can be sure on which revision we're on

See merge request framasoft/framadate!271
2018-04-02 11:34:12 +02:00
Thomas Citharel
37c2df781a
Add git to beta.framadate.org deploy so that we can be sure on which revision we're on
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-04-02 11:32:08 +02:00
m
cdd1840217 correction : filtre adresse e-mail avec nom de domaine internationalisé
https://framagit.org/framasoft/framadate/issues/233
2018-04-01 19:29:32 +02:00
m
d2268e8543 Donner le lien d'édition d'un vote lorsqu'on est administrateur
https://framagit.org/framasoft/framadate/issues/209
2018-04-01 17:40:45 +02:00
Thomas Citharel
6855cf4a47 Merge branch 'develop' into 'master'
Version 1.1.0-beta.2

See merge request framasoft/framadate!265
2018-03-29 13:50:18 +02:00
Thomas Citharel
48c3551e61 Merge branch 'feature/traduction-fr' into 'develop'
Traductions

See merge request framasoft/framadate!257
2018-03-29 13:32:41 +02:00
Thomas Citharel
7823a23e72 Merge branch 'feature/fix-css' into 'develop'
Fix css

See merge request framasoft/framadate!258
2018-03-29 13:21:50 +02:00
Marjolaine Vé
2fd590fad6
Add margin between textarea and buttons for the description when poll creation 2018-03-29 13:20:24 +02:00
Marjolaine Vé
b553305a0e
Images in poll maxwidth: 100% 2018-03-29 13:20:24 +02:00
Marjolaine Vé
3aaa7ee629
Change "Ifneedbe" into "If need be" 2018-03-29 13:13:29 +02:00
Marjolaine Vé
48134d70f7
Add french ecriture inclusive for 2 terms 2018-03-29 13:13:29 +02:00
Thomas Citharel
05223bfd7b Merge branch 'fix-ifneedbe-checkbox-size' into 'develop'
Increase ifneedbe choice width

Closes #292

See merge request framasoft/framadate!264
2018-03-29 13:07:55 +02:00
Thomas Citharel
3df48b25f7
Increase ifneedbe choice width
Closes #292 for good
Also a few syntax typos

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 13:06:50 +02:00
Thomas Citharel
004fda4f96 Merge branch 'fix-value-max-validation' into 'develop'
Fix ValueMax validation and improve error messages for username format

Closes #295 et #290

See merge request framasoft/framadate!263
2018-03-29 12:56:07 +02:00
Thomas Citharel
7134581535
Fix ValueMax validation and improve error messages for username format
Closes #295
New translation strings
Check that ValueMax is >= 1
Show details if error in advanced settings
Correctly show error message for ValueMax
Make email field an email one
Make username, email (if smtp of course) and title required html inputs

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 12:54:48 +02:00
Thomas Citharel
2d0699184c
Debug routing
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 11:29:04 +02:00
Thomas Citharel
21c6c7684e Merge branch 'add-maxlength-on-comment-name' into 'develop'
Add MaxLength attribute on comment name

See merge request framasoft/framadate!262
2018-03-29 11:03:45 +02:00
Thomas Citharel
7d7f39190f
Add MaxLength attribute on comment name
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 11:03:17 +02:00
Thomas Citharel
4d88760ce1 Merge branch 'remove-autocomplete-on-date' into 'develop'
Remove autocomplete on date fields

See merge request framasoft/framadate!261
2018-03-29 11:00:49 +02:00
Thomas Citharel
ebbee26a59
Remove autocomplete on date fields
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 11:00:08 +02:00
Thomas Citharel
0cd41782d1 Merge branch 'fix-ifneedbe-checkbox' into 'develop'
Add back parenthesis for ifneedbe vote

Closes #292

See merge request framasoft/framadate!260
2018-03-29 10:50:18 +02:00
Thomas Citharel
ce16034c84
Add back parenthesis for ifneedbe vote
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 10:49:08 +02:00
Thomas Citharel
2935fdf644 Merge branch 'remove-console-log' into 'develop'
Upgrading SimpleMDE's version

Closes #288

See merge request framasoft/framadate!259
2018-03-29 10:37:29 +02:00
Thomas Citharel
d35848ed5a
Upgrading SimpleMDE's version
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-29 10:35:34 +02:00
Thomas Citharel
5d0a388285
typo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-28 16:55:04 +02:00
Thomas Citharel
3e09125719 Merge branch 'push-develop' into 'develop'
add beta stage

See merge request framasoft/framadate!255
2018-03-28 16:51:07 +02:00
Thomas Citharel
5a171277cb
add beta stage
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-28 16:50:00 +02:00
Thomas Citharel
07a311239a Merge branch 'develop' into 'master'
Version 1.1.0-beta.1

See merge request framasoft/framadate!253
2018-03-25 18:51:13 +02:00
Thomas Citharel
6ed3a0c3cf Merge branch 'fix-#283' into 'develop'
Fix URL Routing for actions

Closes #283

See merge request framasoft/framadate!249
2018-03-23 09:21:37 +01:00
Thomas Citharel
71c3498475 Fix URL Routing for actions
Closes #283

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-23 09:19:16 +01:00
Thomas Citharel
d079935714 Merge branch 'develop' into 'master'
Version 1.1.0-alpha.3

See merge request framasoft/framadate!248
2018-03-22 14:44:02 +01:00
Thomas Citharel
4631b8d4c4 Merge branch 'fix-#281' into 'develop'
Fix MySQL NO_ZERO_DATE issue previous fix. Correct SQL and execute only on MySQL.

Closes #281

See merge request framasoft/framadate!247
2018-03-22 14:41:28 +01:00
Thomas Citharel
1673ed8c71 Fix MySQL NO_ZERO_DATE issue previous fix. Correct SQL and execute only on MySQL.
Closes #281

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-22 14:40:16 +01:00
Thomas Citharel
17f16cd0f8 Merge branch 'develop' into 'master'
Release 1.1.0-alpha.2

See merge request framasoft/framadate!246
2018-03-22 11:04:11 +01:00
Thomas Citharel
edb5a84cf9 Update README.md 2018-03-22 10:55:52 +01:00
Thomas Citharel
77afe10e44 Update INSTALL.md 2018-03-22 10:43:01 +01:00
Thomas Citharel
955465f5f9 Merge branch 'fix-MySQL-zero-no-date' into 'develop'
Fix MySQL NO_ZERO_DATE

Closes #224

See merge request framasoft/framadate!245
2018-03-21 16:21:50 +01:00
Thomas Citharel
d3eedbcbf6 Fix MySQL NO_ZERO_DATE
Closes #224

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-21 16:19:50 +01:00
Thomas Citharel
0e9075a985 Merge branch 'fix-#267' into 'develop'
Fix everybody can vote

Closes #267

See merge request framasoft/framadate!244
2018-03-21 15:31:05 +01:00
Thomas Citharel
991f51aac0 Fix left editing button always shown
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-21 15:29:14 +01:00
Thomas Citharel
bfc986a66e Fix everyone can vote
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-21 15:28:58 +01:00
Thomas Citharel
4e021c2bc3 Remove the check on Valuemax
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-21 15:27:57 +01:00
Thomas Citharel
129dcba693 Merge branch 'allow-smtp-config' into 'develop'
Allow setting SMTP config

Closes #192 and #150

See merge request framasoft/framadate!224
2018-03-21 14:30:50 +01:00
Thomas Citharel
2991b9e1d3 Merge branch 'fix-datepicker-locale-path' into 'develop'
Fix datepicker locale path

Closes #279

See merge request framasoft/framadate!242
2018-03-21 14:22:30 +01:00
Luc Didry
5b5330ceb9 [i18n] Update translations 2018-03-21 09:52:00 +01:00
Luc Didry
18cbfb7b75 [zanata] Add locales target in Makefile to generate framadate.pot 2018-03-21 09:50:14 +01:00
Luc Didry
cf48c9c2d7 [i18n] update oc translation 2018-03-20 18:06:07 +01:00
Luc Didry
eccae40718 Update translations 2018-03-20 17:10:04 +01:00
Luc Didry
65d8fd9639 [zanata] Use en translation string if not translated 2018-03-20 17:09:02 +01:00
Thomas Citharel
3705e6c493 CS
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 18:06:13 +01:00
Thomas Citharel
ea3e391b87 Fix datepicker locale path
Closes #279

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 18:01:19 +01:00
Luc Didry
05e9ea1589 Update locales 2018-03-19 16:12:53 +01:00
Thomas Citharel
0e022f51c1 Merge branch 'feature/fr' into 'develop'
Missing string in French and English locales

See merge request framasoft/framadate!241
2018-03-19 13:55:24 +01:00
Pierre Rudloff
f344169157 Missing string in French and English locales 2018-03-19 13:46:46 +01:00
Thomas Citharel
6c2e44e400 Merge branch 'change-ci-image' into 'develop'
Use own framasoft/framadate-ci image for CI

See merge request framasoft/framadate!240
2018-03-19 13:36:40 +01:00
Thomas Citharel
3bd1999e15 Merge branch 'feature/lang' into 'develop'
lang attribute must be a valid IETF language tag

See merge request framasoft/framadate!239
2018-03-19 13:22:29 +01:00
Pierre Rudloff
2711b3a019 lang attribute must be a valid IETF language tag 2018-03-19 13:13:15 +01:00
Thomas Citharel
e9458c1839 Move php composer.phar to composer
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 13:06:31 +01:00
Thomas Citharel
edee5626f4 Use own framasoft/framadate-ci image for CI
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 13:04:09 +01:00
Thomas Citharel
89c5ff3637 Merge branch 'develop' into 'master'
Update composer.lock

See merge request framasoft/framadate!238
2018-03-19 11:16:06 +01:00
Thomas Citharel
a1fbe3946a Merge branch 'update-composer-lock' into 'develop'
Update composer.lock

See merge request framasoft/framadate!237
2018-03-19 11:14:24 +01:00
Thomas Citharel
7a8549cfd7 Update composer.lock
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 11:12:17 +01:00
Thomas Citharel
4f402f60e7 Merge branch 'develop' 2018-03-19 11:09:47 +01:00
Thomas Citharel
a97bcf4d6a fixup for copying app files to framadate folder
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 11:09:12 +01:00
Thomas Citharel
0bd4f46535 Merge branch 'develop' into 'master'
Release 1.1.0-alpha.1

See merge request framasoft/framadate!236
2018-03-19 11:03:39 +01:00
Thomas Citharel
72e0791e23 Merge branch 'release-1.1.0-alpha.1' into 'develop'
Release version 1.1.0-alpha.1

See merge request framasoft/framadate!235
2018-03-19 11:01:59 +01:00
Thomas Citharel
05ee7bf256 Release version 1.1.0-alpha.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 11:01:09 +01:00
Thomas Citharel
a78d3b3e01 Merge branch 'fix-zanata' into 'develop'
Fix Zanata (https://framagit.org/framasoft/framadate/merge_requests/228#note_194515)

Closes #271 and #272

See merge request framasoft/framadate!234
2018-03-19 10:50:04 +01:00
Thomas Citharel
ff3dcc9249 Fix Zanata (https://framagit.org/framasoft/framadate/merge_requests/228#note_194515)
Fix #271
Fix #272

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 10:45:35 +01:00
Thomas Citharel
8931255033 Merge branch 'upgrade-parsedown' into 'develop'
Upgrade parsedown

Closes #226

See merge request framasoft/framadate!233
2018-03-19 10:36:37 +01:00
Thomas Citharel
9305a82cb2 Upgrade parsedown
Closes #226

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 10:34:58 +01:00
Thomas Citharel
b9f4577fbb Merge branch 'master' into develop 2018-03-19 10:33:00 +01:00
Thomas Citharel
915fe17bfc Merge branch 'fix-develop' into 'develop'
Fix develop

See merge request framasoft/framadate!232
2018-03-19 10:29:18 +01:00
Thomas Citharel
26761c97a6 CS
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 10:26:00 +01:00
Thomas Citharel
eadf775bb3 Fix generating URL issue with type check
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 10:24:31 +01:00
Thomas Citharel
7ffd7f0b21 Fix unable to vote with ConcurrentVoteException when ValueMax is null
Closes #276

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-03-19 10:23:35 +01:00
Thomas Citharel
bac91c9182 Merge branch 'zanata-backed-locale-files' into 'develop'
Fix Zanata integration + upgrade locales

See merge request framasoft/framadate!228
2018-03-02 12:04:40 +01:00
Thomas Citharel
d8abafc78e Merge branch 'fix-SERVER_PORT-handling' into 'develop'
Fix $_SERVER['SERVER_PORT'] handling

See merge request framasoft/framadate!229
2018-03-02 12:03:43 +01:00
Thomas Citharel
0951a89c33 Merge branch 'patch-1' into 'master'
Update INSTALL.md: typo

See merge request framasoft/framadate!230
2018-03-02 12:03:22 +01:00
Thomas Citharel
678070af8f Merge branch 'patch-2' into 'master'
fix fork url, update urls from git.framasoft.org to framagit.org (avoid the redirection)

See merge request framasoft/framadate!231
2018-03-02 12:03:01 +01:00
Fla
fdbded4310 Update README.md: fix fork url, update urls from git.framasoft.org to framagit.org (avoid the redirection) 2018-03-01 22:52:39 +01:00
Fla
847dcf2b92 Update INSTALL.md: typo 2018-03-01 22:48:59 +01:00
Luc Didry
c4ec2bbc41 Fix $_SERVER['SERVER_PORT'] handling
While installing Framadate on Apache, but proxyfied by an Nginx,
$_SERVER['SERVER_PORT'] failed to be correctly handled.

$_SERVER['SERVER_PORT'] is then a string and not an integer, so it was
added in the response of get_server_name() function, breaking all the
css/js links.

I don't know if there is other situations where the bug appears.

Schema of the installation:

Nginx (443) -> Apache2 (80) + Framadate

Please note that using port 80 on Nginx worked.
2018-02-28 17:09:16 +01:00
Luc Didry
ee807db380 [Zanata] Add push-locales to CI for branch develop 2018-02-28 09:43:53 +01:00
Luc Didry
3637e052d5 [Zanata] Add script to update zanata for one locale
ex: ./push-trad-to-zanata.sh fr_FR
2018-02-27 17:39:46 +01:00
Luc Didry
f340fbe046 Update json locale files (fetched from Zanata) 2018-02-27 17:12:25 +01:00
Luc Didry
6839065ece [Zanata] Fix bugs in po to nested json toolchain 2018-02-27 17:12:25 +01:00
Luc Didry
1c8e0061b5 [Zanata] Prettyfying locales json files
In order to compare actual json files to zanata-backed files, we need to
sort the keys and have a 3-spaces indent.

This is done with jq:

cd locale/
mkdir tmp/
for i in *.json; do jq --indent 3 -S -M '.' < $i > tmp/$i ; done
mv tmp/* .
rmdir tmp
2018-02-27 17:12:15 +01:00
Thomas Citharel
c5b50050c9 Merge branch 'zanata-integration' into 'develop'
Zanata integration (https://trad.framasoft.org)

See merge request framasoft/framadate!227
2018-02-26 17:29:00 +01:00
Luc Didry
2907a20c9f Update README for zanata integration 2018-02-26 14:36:31 +01:00
Luc Didry
15b173569d Zanata integration (https://trad.framasoft.org)
1. Install zanata-cli on your computer
(https://zanata.readthedocs.io/en/latest/client/installation/linux-installation/)
2. Configure zanata-cli
(https://zanata.readthedocs.io/en/latest/client/configuration/)
3. Do your stuff, add your new strings to locale/en.json only
4. Push the new locales: make push-locales
5. Translate on https://trad.framasoft.org
6. Pull the new locales: make pull-locales (requires the Perl module
JSON, provided by libjson-perl on Debian)
7. Commit and enjoy
2018-02-26 14:32:17 +01:00
Thomas Citharel
87b89bc609 Merge branch 'trad-de' into 'develop'
Translate missing italian strings

See merge request framasoft/framadate!226
2018-02-21 12:10:41 +01:00
Tubia
1d89074965 Translate missing italian strings
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-21 12:08:57 +01:00
Thomas Citharel
32ffa5f3e3 Merge branch 'trad-de' into 'develop'
German translation

See merge request framasoft/framadate!225
2018-02-21 11:53:06 +01:00
Lukas Müller
710304c183 German translation
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-21 11:44:44 +01:00
Thomas Citharel
991088119f typo on CI
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-21 11:36:27 +01:00
Thomas Citharel
872e30939c Merge branch 'fix-version-check' into 'develop'
Fix php version check

See merge request framasoft/framadate!223
2018-02-21 11:24:10 +01:00
Thomas Citharel
755cd18d1a Fix php version check
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-21 11:17:55 +01:00
Simon LEBLANC
8720595874 Allow setting SMTP config
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-21 11:07:11 +01:00
Thomas Citharel
7e12443bc2 Merge branch 'develop' into funky 2018-02-20 19:40:51 +01:00
Thomas Citharel
01dc23125a Merge branch 'merge-develop' into 'master'
Merge develop

Closes #205 and #190

See merge request framasoft/framadate!222
2018-02-20 19:31:26 +01:00
Thomas Citharel
77f05b1c21 fix issue with check for maxValue
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 19:29:56 +01:00
Thomas Citharel
bf753480dd fix ui stuff
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 19:29:40 +01:00
Thomas Citharel
801bc08a81 Merge branch 'develop' 2018-02-20 19:05:24 +01:00
Thomas Citharel
e6ed712123 Merge branch 'update-jquery' into 'master'
Update jquery

Closes #254

See merge request framasoft/framadate!221
2018-02-20 18:06:47 +01:00
Thomas Citharel
bd931676ae update bootstrap
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 18:00:43 +01:00
Thomas Citharel
d106d3ba41 update jquery
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 18:00:27 +01:00
Thomas Citharel
d9f50e8706 Merge branch 'cookies-http-only' into 'master'
Add a check at install to suggest setting session.cookie_httponly inside php.ini

See merge request framasoft/framadate!220
2018-02-20 17:46:36 +01:00
Thomas Citharel
1de98223db Add a check at install to suggest setting session.cookie_httponly inside php.ini
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 17:44:08 +01:00
Thomas Citharel
2328b2c871 Merge branch 'css-results-poll-responsive' into 'master'
Css results poll responsive

See merge request framasoft/framadate!219
2018-02-20 17:14:13 +01:00
Marjolaine Vé
c350a7a012 Changement de le·a en le ou la 2018-02-20 17:04:11 +01:00
Marjolaine Vé
f11fd8c541 [FR] Ecriture inclusive 2018-02-20 17:04:11 +01:00
Marjolaine Vé
4bbbaf19ed [Responsive] Buttons not too long 2018-02-20 17:04:11 +01:00
Marjolaine Vé
efda4e97b0 [Responsive] List of polls for admins with scrollbar 2018-02-20 17:04:11 +01:00
Marjolaine Vé
b29bbb5c9a Create a button for "Back to administration" link 2018-02-20 17:04:11 +01:00
Marjolaine Vé
02fdf05786 Change place of arrows on poll results 2018-02-20 17:04:11 +01:00
Marjolaine Vé
4e2a48685a Add space between language select and h1 2018-02-20 17:04:11 +01:00
Marjolaine Vé
27f4c1045a Allow buttons to be on many lines 2018-02-20 17:04:11 +01:00
Marjolaine Vé
ea575522a9 Add viewport to test responsive online 2018-02-20 17:04:11 +01:00
Thomas Citharel
6e1386fa4d Merge branch 'limit-number-of-participants' into 'master'
Limit number of participants

Closes #238

See merge request framasoft/framadate!218
2018-02-20 16:52:32 +01:00
Thomas Citharel
a442cc5ee7 CS
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 16:50:14 +01:00
Thomas Citharel
ff47a21ac2 Check that conditions on valueMax are acceptable before adding the vote
Handles people voting when the answers valueMax has been reached by someone else in the background

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 16:47:10 +01:00
Thomas Citharel
76b19ee9ba fix votes wrongly calculated because of type check
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 15:17:14 +01:00
Thomas Citharel
2d39a83a7f rebase and run php-cs-fixer
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 14:15:18 +01:00
Erwan TERTRAIS
64ff414900 ValueMax : limit the number of voters per option
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 14:15:18 +01:00
Thomas Citharel
2a01afe4b8 Merge branch 'fix-edit-vote-button-pos' into 'master'
Fix edit vote button pos

Closes #69

See merge request framasoft/framadate!217
2018-02-20 14:11:19 +01:00
Thomas Citharel
70f296377f Move some style to css files
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 12:42:27 +01:00
Erwan TERTRAIS
95c4737953 edit button classic poll 2018-02-20 12:09:56 +01:00
Erwan TERTRAIS
2b25d8c301 edit button if slot >7 2018-02-20 12:09:56 +01:00
Erwan TERTRAIS
c95c13e069 bouton edit à droite du nom 2018-02-20 12:09:56 +01:00
Thomas Citharel
05e4b820fb Merge branch 'fix-password-function' into 'master'
issue 240 desactivation checkbox resultat public

Closes #240

See merge request framasoft/framadate!216
2018-02-20 11:56:46 +01:00
Thomas Citharel
c2cbc6eb0e Merge branch 'fix-copy-hours-function' into 'master'
Fix copy hours function

Closes #203

See merge request framasoft/framadate!215
2018-02-20 11:56:31 +01:00
Thomas Citharel
4dc62b7da2 Merge branch 'run-tests-on-ci' into 'master'
Run tests on CI, refactor .gitlab-ci.yml

See merge request framasoft/framadate!214
2018-02-20 11:56:19 +01:00
Thomas Citharel
2310333f8d Adds some comments to understand the logic
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 11:55:14 +01:00
Thomas Citharel
e9deb8d22e run CS
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 10:58:50 +01:00
DAVID Titouan
1740a1d949 issue 240 desactivation checkbox resultat public
changement du nom de l'option "resultat visible" dans password en "seul les votes sont proteges"

correction de l'ajout du js pour issue240

correction test pour desactivation dynamique du champ
2018-02-20 10:58:30 +01:00
Thomas Citharel
78ba609c5c Clean commented out code, fix a console warning and idents and typos
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 09:31:36 +01:00
DAVID Titouan
013c62eb9c Update date_poll.js 2018-02-20 09:11:14 +01:00
Thomas Citharel
0a9c5095e5 cache vendor
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 08:58:49 +01:00
Thomas Citharel
83d3565472 Refactor to put tests outside of deployment and optimize autoloader on builds
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 08:54:11 +01:00
Thomas Citharel
89dd2fbc35 Run tests on CI, refactor .gitlab-ci.yml
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-20 08:43:46 +01:00
Thomas Citharel
819220997c Merge branch 'php-cs-fixer' into 'master'
fix typo

See merge request framasoft/framadate!213
2018-02-19 19:51:05 +01:00
Thomas Citharel
deb1da2635 fix typo
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:50:50 +01:00
Thomas Citharel
1e8d3a1bf0 Merge branch 'php-cs-fixer' into 'master'
Run php-cs-fixer with a custom config. This may break a lot of things

See merge request framasoft/framadate!209
2018-02-19 19:48:20 +01:00
Thomas Citharel
b8907fb1b6 Rebase
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:47:06 +01:00
Thomas Citharel
006e5c8c10 Add php-cs-fixer and phpunit to build
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:44:40 +01:00
Thomas Citharel
3157d6a590 Run php-cs-fixer with a custom config. This may break a lot of things
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:43:30 +01:00
Thomas Citharel
9827070b3d Merge branch 'require-php-56' into 'master'
Require at least PHP 5.6

See merge request framasoft/framadate!212
2018-02-19 19:39:39 +01:00
Thomas Citharel
c2e00ef4d6 remove nginx for now
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:38:43 +01:00
Thomas Citharel
1e423e880e rebase
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:38:23 +01:00
Thomas Citharel
6b04d3542e Check for PHP 5.6
Better detection, get rid of dedicated file and use version_compare() instead

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:37:47 +01:00
Thomas Citharel
54772d42f7 Update composer.json with authors and build version and links to issues
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:37:29 +01:00
Thomas Citharel
e8f37a33bd Require at least PHP 5.6
Even Debian old stable (jessie) has it.

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 19:37:29 +01:00
Thomas Citharel
2ebe57a828 Merge branch 'fix-phpunit' into 'master'
Update and fix phpunit

See merge request framasoft/framadate!210
2018-02-19 19:36:55 +01:00
Thomas Citharel
21568fc6ef Merge branch 'funky' into 'funky'
Corrige une faute d'anglais et ajoute une précision dans le README

See merge request framasoft/framadate!203
2018-02-19 10:55:11 +01:00
Thomas Citharel
af8c00f2b5 Merge branch 'feature/description' into 'develop'
Add description meta tag

See merge request framasoft/framadate!205
2018-02-19 10:54:20 +01:00
Thomas Citharel
ba04557ac9 Merge branch 'remove-browser-compat' into 'master'
remove browser compat (it's been outdated for 4 years, everyone should be fine by now)

See merge request framasoft/framadate!211
2018-02-19 00:56:37 +01:00
Thomas Citharel
c4ccecd91e remove browser compat (it's been outdated for 4 years, everyone should be fine by now)
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 00:23:24 +01:00
Thomas Citharel
00e4d205e6 Update and fix phpunit
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-02-19 00:11:00 +01:00
Thomas Citharel
3f4cf0f721 Merge branch 'fix-breton-l10n' into 'develop'
[l10n] corrections for breton translation

See merge request framasoft/framadate!183
2018-02-18 23:58:04 +01:00
Thomas Citharel
678db4a115 Merge branch 'features/security' into 'master'
Update dependencies and add a security component

See merge request framasoft/framadate!199
2018-02-18 23:46:44 +01:00
Thomas Citharel
f982aa54d5 Merge branch 'feature/doc-update' into 'master'
Install doc updated

See merge request framasoft/framadate!198
2018-02-18 23:42:07 +01:00
Thomas Citharel
68a47dba73 Merge branch 'master' into 'master'
Ajout de traductions Allemandes

See merge request framasoft/framadate!207
2018-02-18 23:40:18 +01:00
Thomas Citharel
1b105ba1d8 Merge branch 'dutch-locales' into 'master'
nl translations

See merge request framasoft/framadate!197
2018-02-18 23:40:00 +01:00
Thomas Citharel
3183b54dbf Merge branch 'master' into 'master'
Update it.json - some italian translation

See merge request framasoft/framadate!190
2018-02-18 23:39:24 +01:00
root
10dca1c2c5 update German translations 2018-02-05 13:02:43 +01:00
goofy-bz
f18ce7845d Update fr.json 2018-01-21 11:03:52 +01:00
goofy-bz
4e8d77f9af Update fr.json 2018-01-21 11:01:42 +01:00
Pierre Rudloff
2f2378a80f Add description meta tag 2018-01-16 14:04:02 +01:00
Yannick Francois
60e4e9f9b1 Ajout d'une précision dans le README 2018-01-04 22:57:15 +01:00
Yannick Francois
b93ff95a54 Corrige une faute d'anglais dans la clé d'un message 2018-01-04 22:53:18 +01:00
Simon Leblanc
0f2313e273 Update dependencies and add a security component 2017-12-28 13:33:58 +01:00
Simounet
7cfd2117e8 Install doc updated 2017-12-22 14:00:28 +01:00
SpF
2bc9eb62d0 nl translations 2017-12-20 11:35:50 +01:00
JosephK
8b79c7f43a Création en CI de l’archive zip de la dernière release (tag) 2017-12-14 15:30:21 +01:00
JosephK
9897a024fc Déploiement en CI sur funky.framasoft.org 2017-12-14 15:23:02 +01:00
Olivier Perez
9a69f64132 Merge branch 'patch-1' into 'master'
Update oc.json

See merge request framasoft/framadate!187
2017-09-29 15:02:49 +02:00
Olivier Perez
1d75ef0ea2 Merge branch 'patch-1' into 'master'
Fix typo in INSTALL.md

See merge request framasoft/framadate!189
2017-09-29 15:02:01 +02:00
Nikos
6a2ee5604e Update it.json - some italian translation 2017-09-13 13:31:15 +02:00
Kyâne
a2df40b924 Fix typo in INSTALL.md 2017-09-06 19:19:29 +02:00
Quentin
d33cc58cf7 Update oc.json 2017-09-02 11:06:26 +02:00
Quentin
c03bd0216f Update oc.json with thin non-breakable spaces and + apostrophes + translations 2017-09-02 09:22:28 +02:00
Gwenn Meynier
71d8ef5134 [l10n] another breton fix 2017-06-21 23:29:55 +02:00
Gwenn Meynier
f8e00084be [l10n] corrections for breton translation 2017-06-14 13:24:02 +02:00
Antonin
724133f6ae Merge branch 'fix/NotScrollableOnMobile' into 'develop'
Let the table be scrollable even if there is no 'over' event

See merge request !170
2017-03-19 18:38:54 +01:00
Antonin
e9199d8396 Merge branch 'master' into 'master'
More NL translations

See merge request !172
2017-03-19 18:26:53 +01:00
Antonin
9229b5cb13 Merge branch 'fix/NotScrollableOnMobile' into 'develop'
Let the table be scrollable even if there is no 'over' event

See merge request !170
2017-03-19 18:25:46 +01:00
Antonin
53cf6498ed Merge branch 'bugfix/install' into 'master'
Bugfix/install

See merge request !168
2017-03-19 18:25:04 +01:00
Olivier Perez
d69de89f2f Merge branch 'feature/favicon' into 'develop'
Add favicon path to config

See merge request !164
2017-02-23 00:04:28 +01:00
Pierre Rudloff
3642bf87d4 Add favicon path to config 2017-02-22 21:16:09 +01:00
JosephK
ca653cf568 Merge branch 'spelling' into 'master'
Fix typos

See merge request !177
2017-02-20 07:04:36 +01:00
JosephK
de6de7d6ee Merge branch 'feature/fix-fork-link' into 'master'
README.md: Fix the fork link.

See merge request !175
2017-02-20 07:03:39 +01:00
JosephK
9bb4cab2d8 Merge branch 'gitignore' into 'master'
Add .htdigest to .gitignore

See merge request !176
2017-02-20 07:02:20 +01:00
JosephK
495db1b748 Merge branch 'patch-1' into 'master'
Update robots.txt

See merge request !178
2017-02-20 07:00:35 +01:00
JosephK
adcea7d04b Merge branch 'iss220' into 'master'
Sanitize names to avoid Formula Injections on CSV export (fix issue #220)

See merge request !171
2017-02-20 06:59:36 +01:00
Lucas D
254130559f Update robots.txt
For delete 
Allow: /apropos.php
Allow: /contacts.php

page does not exist anymore
2017-02-13 10:47:57 +01:00
François B
fd3526c9c7 Fix typos 2017-02-05 04:19:04 +01:00
François B
e8c3984a07 Add .htdigest to .gitignore 2017-02-05 03:25:20 +01:00
Cyril Roelandt
2205156a46 README.md: Fix the fork link. 2017-01-21 20:24:43 +01:00
Jeroen Demeyer
be44e53eda More NL translations 2017-01-06 14:19:49 +01:00
framartin
b0e6b82877 Sanitize names to avoid Formula Injections on CSV export 2017-01-05 18:48:46 +01:00
Olivier Perez
61e3d0735f Merge branch 'feature/piwik' into 'develop'
New tracking_code setting that inserts HTML code in the footer of every page

See merge request !163
2016-12-30 15:59:40 +01:00
Olivier PEREZ
00a755fb10 Let the table be scrollable even if there is no 'over' event 2016-12-29 16:23:29 +01:00
Olivier Perez
df2576445d Merge branch 'feature/admin_add_slot_on_enter' into 'develop'
Submit slot when hitting enter key

See merge request !162
2016-12-28 02:17:54 +01:00
Olivier Perez
8f6c665c3d Merge branch 'bugfix/nl-locale-date' into 'develop'
Get back the dutch locale

See merge request !165
2016-12-28 02:14:23 +01:00
Olivier Perez
b1d996f7d0 Merge branch 'bugfix/nl-date' into 'master'
Correcting the NL date locale

Closes #212

See merge request !166
2016-12-28 02:13:47 +01:00
Olivier Perez
2a9c663c8c Merge branch 'bugfix/phpmailler-update' into 'master'
PHPMailer update

Closes #217

See merge request !169
2016-12-28 02:11:46 +01:00
Damien Finck
7e05d8aef8 Improve a warning message 2016-12-27 18:11:52 +01:00
Antonin
61f57ee41c While we're at it, updating other components.
All those updates affect only the dev environment.
2016-12-27 17:10:10 +01:00
Antonin
e6c2d035fe Update phpmailer/phpmailer to fix security issue. 2016-12-27 16:21:46 +01:00
Damien Finck
5bb3d394a1 Add new key in all locales 2016-12-27 16:07:09 +01:00
Damien Finck
5012ed32db Fix typo + add key in all locales 2016-12-27 16:03:06 +01:00
Damien Finck
03727577e8 Add missing key 2016-12-27 15:29:46 +01:00
Damien Finck
57d6e1c0a4 Resources standardization 2016-12-27 15:29:25 +01:00
Damien Finck
545f167a7b Fix the check of template dir 2016-12-27 15:15:26 +01:00
Antonin
0a0b3b233b Correcting the NL date locale 2016-12-26 16:41:33 +01:00
Antonin
44aca0a7e7 Correcting the NL date locale 2016-12-26 16:38:26 +01:00
Antonin
e17abed619 Get back the NL locale from master. 2016-12-26 16:32:07 +01:00
Pierre Rudloff
c5a50a7cad New tracking_code setting that inserts HTML code in the footer of every page 2016-12-22 16:14:22 +01:00
Antonin
48de916f81 Submit slot when hitting enter key 2016-12-06 15:58:05 +01:00
Antonin
8f6a31737d Merge branch 'fix/Some_fixes_on_comments' into 'master'
Some fixes on comments

Fix #205 

See merge request !161
2016-12-06 15:34:30 +01:00
Antonin
14451ade7a Merge branch 'fix/Some_fixes_on_comments' into 'master'
Some fixes on comments

Fix #205 

See merge request !161
2016-12-06 15:34:10 +01:00
Olivier PEREZ
56620e4a43 Fix #200 - git.framassoft.org => framagit.org 2016-11-15 00:00:45 +01:00
Olivier PEREZ
716b171a36 n2.framasoft.org => framasoft.org 2016-11-14 23:53:05 +01:00
Olivier PEREZ
cf21a10314 Remove '/action' when calculating server URL 2016-11-14 23:41:57 +01:00
Olivier PEREZ
79ae5bb77d Fix the code that checks if we should send notification regarding type of notification 2016-11-14 23:35:04 +01:00
Olivier Perez
c6a98e16e8 Translate missing wordings 2016-10-24 19:31:51 +02:00
goofy-bz
250a6bba65 Update fr.json - my bad 2016-10-14 19:32:13 +02:00
goofy-bz
c7992be6bb Update fr.json - minor fix s/optionnel/facultatif 2016-10-14 19:29:30 +02:00
Antonin
f5e579437a Problèmes d'envois de commentaire de l'administrateur sur un sondage avec mot de passe. 2016-10-14 09:02:47 +02:00
Antonin
df8bbf3f65 Correction de l'editabilité du sondage : derniers noublis ! 2016-10-14 08:49:47 +02:00
Antonin
daf7fcd30a Bug sur l'éditabilité des sondage. 2016-10-14 08:22:15 +02:00
Antonin
7a1033b216 Merge branch 'master' into release 2016-10-14 07:28:03 +02:00
Antonin
1714434eb9 Merge branch 'task/merge-dutch-language-from-master' into 'release'
Task - merge dutch language from master

I also normalized the indentation from the current javascript "norm" of the project.

All `locale/` files of the b2d8723043 commit are just from the change of indentation: Gitlab doesn't seem to have a "ignore whitespace" functionality yet, so that's better to be said...

See merge request !160
2016-10-14 07:12:09 +02:00
Antonin
b2d8723043 Unification of locale indentation. 2016-10-13 17:40:48 +02:00
Antonin
ac3a8ca25c Get back the NL locale from master. 2016-10-13 17:36:39 +02:00
Antonin
76c2b1ae1c Merge branch 'patch-1' into 'release'
Update oc.json

Translated the remaining French bits

See merge request !159
2016-10-12 20:22:13 +02:00
Quentin
4eb2d8b381 Update oc.json 2016-10-12 20:01:23 +02:00
Antonin
a414f96a88 Merge branch 'patch-1' into 'release'
Update br.json

translate missing strings
(made online with gitlab, sorry for the ugly branch name)

See merge request !158
2016-10-12 17:30:51 +02:00
Gwenn
72221fd0a3 Update br.json 2016-10-12 17:19:48 +02:00
Olivier PEREZ
134cd23f5f Mise à jour de la release note 2016-10-06 23:52:57 +02:00
Olivier Perez
1dfb276b52 Merge branch 'task/update-composer-lock' into 'release'
Update composer.lock



See merge request !157
2016-10-06 23:41:28 +02:00
Antonin
22956d4c2c Update composer.lock 2016-10-06 23:39:57 +02:00
Olivier PEREZ
2377013a6f Fix Breton dates management 2016-10-06 23:36:07 +02:00
Olivier Perez
7c1b3b163d Merge branch 'feature/allow-to-remove-date-in-wizard' into 'develop'
Refactoring on button state handling



See merge request !155
2016-10-06 23:08:47 +02:00
Antonin
c9c4e945b8 attr('value', '') != val('') pour les inputs 2016-10-06 23:04:39 +02:00
Antonin
671f37c05c Merge branch 'feature/Disable_when_results_are_hidden' into 'release'
Disable when results are hidden

Fix #196

See merge request !154
2016-10-06 22:46:24 +02:00
Olivier Perez
1d43293276 Merge branch 'bugfix/translate-optionnal-parameters' into 'release'
Added missing translation for "Optional parameters"



See merge request !156
2016-10-06 22:43:28 +02:00
Olivier PEREZ
73de6199fa Password protected polls cannot be exported 2016-10-06 22:42:57 +02:00
Antonin
8bc9b05329 Added missing translation for "Optional parameters" 2016-10-06 22:39:13 +02:00
Antonin
1206975d30 Bad selector 2016-10-06 22:16:53 +02:00
Antonin
1cfba5efee Updated buttons state and refactored a little 2016-10-06 22:16:53 +02:00
Antonin
4881b3c8b2 Revert "Manage button state in the new feature"
This reverts commit c2cfdf4ff6.
2016-10-06 22:10:51 +02:00
Olivier PEREZ
d980571a74 Disable export when results are invisibles 2016-10-06 21:13:01 +02:00
Olivier PEREZ
e8aac339f0 Booleanize some fields 2016-10-06 20:56:23 +02:00
Olivier PEREZ
357bc89d16 Remove useless default SQL columns 2016-10-06 20:37:46 +02:00
Antonin
785d1fe55f Merge branch 'translation' into 'develop'
Update French Translation



See merge request !151
2016-09-28 15:55:27 +02:00
Antonin
0fd5348ff5 Merge branch 'translation' into 'develop'
Update French Translation



See merge request !151
2016-09-28 15:53:00 +02:00
Antonin
5be042a014 Merge branch 'feature/update-br' into 'develop'
Restore and update br translation file

Le fichier de traduction br.json avait été tronqué lors du commit e79a0bf6 (probablement une erreur de manipulation pendant le merge). J'en ai profité pour mettre à jour avec les nouvelles trads.

See merge request !152
2016-09-28 15:44:47 +02:00
Gwenn Meynier
205dd08013 restore and update br translation file 2016-09-21 00:50:54 +02:00
Gautier Pelloux-Prayer
65872d1c7b i18n: update french translation 2016-09-05 22:03:39 +02:00
Antonin
05faa85074 Merge branch 'patch-1' into 'develop'
Update add_comment.php: update sendUpdateNotification parameters order



See merge request !150
2016-09-05 16:41:20 +02:00
Antonin
1872204d1b Merge branch 'patch-1' into 'develop'
Update add_comment.php: update sendUpdateNotification parameters order



See merge request !150
2016-09-05 16:41:05 +02:00
Gautier Pelloux-Prayer
451b299bc3 Update add_comment.php: update sendUpdateNotification parameters order 2016-08-28 22:03:02 +02:00
Olivier Perez
52d7e88b00 Merge branch 'feature/hide-optionnal-parameters' into 'develop'
Collapse by default optional parameters

To be cherry-picked on `release`.

See merge request !147
2016-08-09 01:23:38 +02:00
Olivier Perez
c5b9c4152d Merge branch 'feature/hide-optionnal-parameters' into 'develop'
Collapse by default optional parameters

To be cherry-picked on `release`.

See merge request !147
2016-08-09 01:23:30 +02:00
Antonin
18dd7f58b0 Collapse by default optionnal parameters 2016-08-08 18:07:36 +02:00
Olivier Perez
f3ce74d997 Merge branch 'feature/allow-to-remove-date-in-wizard' into 'develop'
Allow to remove date in the poll creation wizard

CF MR !140 

To be cherry-picked in `release`.

See merge request !146
2016-08-08 17:15:45 +02:00
Olivier Perez
469d288136 Merge branch 'feature/allow-to-remove-date-in-wizard' into 'develop'
Allow to remove date in the poll creation wizard

CF MR !140 

To be cherry-picked in `release`.

See merge request !146
2016-08-08 17:15:18 +02:00
Antonin
9cb2ce6fe6 Total of bootstrap columns should be 12. 2016-08-08 17:13:30 +02:00
Antonin
e79a0bf683 Merge branch 'release' into develop 2016-08-08 15:23:42 +02:00
Olivier Perez
08d623a1e1 Merge branch 'bugfix/add-tooltip-on-table-headers' into 'master'
Added the missing title attribute

Fix #189

See merge request !145
2016-08-08 15:10:47 +02:00
Antonin
c2cfdf4ff6 Manage button state in the new feature 2016-08-08 11:51:20 +02:00
Antonin
65c8bb4d77 Restaured the mistakely supressed function 2016-08-08 07:09:51 +02:00
Antonin
ec43f91df5 Sorting the date choices 2016-08-07 12:34:42 +02:00
Antonin
fcf8f5d621 Using last added day number as base for new day 2016-08-07 12:24:41 +02:00
Antonin
4d14dd1b70 Dealing in PHP with the suppressed dates 2016-08-07 12:23:57 +02:00
Antonin
d762c38e81 Add a 'remove this day' button 2016-08-07 12:22:58 +02:00
Olivier Perez
1d5af5de3b
Add missing translations 2016-08-05 12:02:41 +02:00
Antonin
9eb55db522 Added the missing title attribute 2016-08-04 22:28:05 +02:00
Olivier Perez
651289ec2d Merge branch 'bugfix/typo-in-comment-header' into 'develop'
Correcting comment typo in file header

Fix #190 

See merge request !144
2016-08-04 22:26:50 +02:00
Olivier Perez
5c11e5fe4d Merge branch 'bugfix/typo-in-comment-header' into 'develop'
Correcting comment typo in file header

Fix #190 

See merge request !144
2016-08-04 22:26:37 +02:00
Antonin
075083bab8
Updated doc to include check.php 2016-08-04 22:24:21 +02:00
Antonin
c50ca48efd Merge branch 'feature/rework_poll_creation_1st_page' into 'release'
Rework on 1st page of poll creation



See merge request !142
2016-08-04 22:07:52 +02:00
Olivier Perez
4b433bc124 Merge branch 'feature/disable_auto_complete' into 'develop'
Disabled autocompletion on the datepicker input

Avoid autocompletion of the datepicker input field to occur
(Step to reproduce : Long click on a datepicker input field)

See merge request !141
2016-08-04 21:55:09 +02:00
Olivier Perez
a86d39c49d Merge branch 'feature/better-markdown' into 'develop'
Feature - better markdown for description

The description of the poll now has full markdown support:
- Use of ParseDown PHP library to parse markdown
- Use of SimpleMDE JS library to have an more confortable editor
- Possibility to switch from rich editor to simple textarea editor
- Possibility to configure default editor in application
- Small refactoring of adminstuds javascript

See merge request !132
2016-08-04 21:54:09 +02:00
Olivier Perez
6a06c71c50 Merge branch 'feature/disable_auto_complete' into 'develop'
Disabled autocompletion on the datepicker input

Avoid autocompletion of the datepicker input field to occur
(Step to reproduce : Long click on a datepicker input field)

See merge request !141
2016-08-04 21:52:37 +02:00
Antonin
4059956b02 Correcting comment typo in file header 2016-08-04 21:51:37 +02:00
Olivier Perez
9b39dd2fa1
New style of URL customization 2016-08-04 21:44:31 +02:00
Olivier Perez
64fe5e0589
Add 'Permissions' section 2016-08-04 20:19:17 +02:00
Antonin
dc9babd2ed Merge branch 'develop' into feature/better-markdown 2016-08-04 17:49:40 +02:00
Antonin
fe3e1bc8b7 Merge branch 'feature/update-breton-translation' into 'develop'
Update, fix and add breton translation to correct branch



See merge request !143
2016-08-04 17:29:29 +02:00
Antonin
c01238b635 Merge branch 'feature/update-breton-translation' into 'develop'
Update, fix and add breton translation to correct branch



See merge request !143
2016-08-04 17:29:10 +02:00
Antonin
ccddddaf2d Merge branch 'Issue187' into 'develop'
Install framadate in non empty database

Modifications pour l'issue #187.

See merge request !139
2016-08-04 16:57:38 +02:00
Antonin
525a5ab4b1 Merge branch 'Issue187' into 'develop'
Install framadate in non empty database

Modifications pour l'issue #187.

See merge request !139
2016-08-04 16:57:20 +02:00
Antonin
2b946dda4d Merge branch 'documentation/composer_instruction' into 'develop'
Documentation/composer instruction

Just adding composer instruction for next dev (2 minutes lost ;) 

See merge request !135
2016-08-04 16:50:42 +02:00
François-Xavier Lyonnet du Moutier
fc19a02cde coding style rework 2016-08-01 18:41:06 +02:00
gwenn
f99c402e6d Update, fix and add breton translation to correct branch 2016-07-31 00:31:43 +02:00
Olivier Perez
7b5f42b174 New style for password fields 2016-07-29 17:50:37 +02:00
Olivier Perez
8066e18ae7 Reorder parameters at poll creation 2016-07-29 17:49:52 +02:00
Olivier Perez
d7675831e1
New style of poll id 2016-07-29 14:47:56 +02:00
JosephK
2144febf95 Bandeau README + URL Framanav 2016-07-19 08:41:14 +02:00
JosephK
a34b767f8e Bandeau README + URL Framanav 2016-07-19 08:38:01 +02:00
François-Xavier Lyonnet du Moutier
8672153a7e Disabled autocompletion on the datepicker input 2016-07-19 02:59:00 +02:00
ecmu
f2562ce5fb Issue 187 : Install framadate in non empty database 2016-07-18 14:27:12 +02:00
Olivier Perez
e642d1e35c Merge branch 'bugfix/php54-empty-compatibility' into 'develop'
PHP5.4 compatibility for empty() function.



See merge request !137
2016-07-07 15:11:35 +02:00
Olivier Perez
dc61be5de5 Merge branch 'bugfix/php54-empty-compatibility' into 'develop'
PHP5.4 compatibility for empty() function.



See merge request !137
2016-07-07 15:11:14 +02:00
Olivier Perez
0cf52c8461 Merge branch 'bugfix/install-config-escape' into 'develop'
Added escaping for install in config.tpl

In the installation process, string like the one below should be a valid application name (or DB password).
> Framadate c'est vraiment super ! "Datez" comme vous voulez \o/

It missed some escaping for single quote (') and backslash (\\), resulting in PHP parse errors.

It should be cherry-picked to the `release` branch too.

See merge request !136
2016-07-07 14:16:09 +02:00
Olivier Perez
91e74dc835 Merge branch 'bugfix/install-config-escape' into 'develop'
Added escaping for install in config.tpl

In the installation process, string like the one below should be a valid application name (or DB password).
> Framadate c'est vraiment super ! "Datez" comme vous voulez \o/

It missed some escaping for single quote (') and backslash (\\), resulting in PHP parse errors.

It should be cherry-picked to the `release` branch too.

See merge request !136
2016-07-07 14:15:34 +02:00
Antonin
c88d4daf3c PHP5.4 compatibility for empty() function. 2016-07-07 14:11:38 +02:00
Antonin
cd581a9d29 Added escaping for install in config.tpl 2016-07-07 11:31:51 +02:00
Denis Chenu
5be1c07d8b Add composer instruction for GIT version + usage of example.org 2016-07-03 18:55:42 +02:00
Antonin
b3ecf1262f Merge branch 'Fix_edition_of_expired_poll' into 'release'
Fix edition limits for expired polls

Fix #129 

See merge request !117
2016-07-01 00:34:42 +02:00
Olivier Perez
7adb14f3ef Merge branch 'bugfix/double-escaped-field-in-create-poll' into 'release'
Correcting escaping in create_poll.php

In create_poll.php, removing of the php escaping and completion of the template ones.

See merge request !134
2016-06-30 23:42:15 +02:00
Olivier Perez
0a8d955f77
Update CHANGELOG.md 2016-06-30 23:36:50 +02:00
Antonin
2af04c3b1a Removing php escape and completing template ones. 2016-06-30 23:10:47 +02:00
Olivier Perez
3181204228 Merge branch 'doc/contributing-git-branch' into 'master'
Readme: added the contributing git branch

Added in the `README.md` from which branch the contributing users can work.

See merge request !133
2016-06-30 22:36:21 +02:00
Olivier Perez
90963f0fc4 Merge branch 'doc/contributing-git-branch' into 'master'
Readme: added the contributing git branch

Added in the `README.md` from which branch the contributing users can work.

See merge request !133
2016-06-30 22:35:48 +02:00
Antonin
19182e98e3 Readme: added the contributing git branch 2016-06-30 22:17:19 +02:00
Antonin
9fe4fa159e Merge branch 'feature/breton-translation' into 'master'
Add breton translation



See merge request !120
2016-06-30 20:53:16 +02:00
Olivier Perez
fa5014de32
Fixed expiry date edition 2016-06-30 20:48:30 +02:00
Olivier Perez
d3db76ccd7
Allow admin to delete an expired poll 2016-06-30 20:09:22 +02:00
Olivier Perez
5862e647d3
Fix edition limits for expired polls 2016-06-30 20:09:22 +02:00
Antonin
9d465487da Merge branch 'editorconfig' into 'master'
Add editorconfig file



See merge request !123
2016-06-30 19:27:37 +02:00
Antonin
318d409464 Merge branch 'editorconfig' into 'master'
Add editorconfig file



See merge request !123
2016-06-30 19:27:06 +02:00
Antonin
acbe0a828a Merge branch 'editorconfig' into 'master'
Add editorconfig file



See merge request !123
2016-06-30 19:26:45 +02:00
Antonin
62603a71fd Merge branch 'fix-datepicker-en' into 'release'
Avoid warnings due to non-existant 'en' datepicker locale

There is no 'en' Translation of datepicker. Hence, do no try to load it.

See merge request !127
2016-06-27 12:31:18 +02:00
Antonin
2e794ea7da Some js logs cleaning. 2016-06-27 12:25:37 +02:00
Antonin
d37ec75550 Added configuration option for enabling rich editor by default. 2016-06-27 12:24:12 +02:00
Pierre Rudloff
d605a1455c Valid SPDX license 2016-06-27 12:00:56 +02:00
Antonin
d80b54349a Merge branch 'feature/spdx' into 'develop'
License "CeCILL-B" is not a valid SPDX license identifier

Bonjour,

CeCILL-B n'est pas un identifiant SPDX valide, l'identifiant correct est CECILL-B.
C'est un détail mais ça provoque un avertissement de `composer validate`.

See merge request !121
2016-06-27 12:00:18 +02:00
Olivier Perez
a31c14e1ae Merge branch 'patch-2' into 'release'
Fix typo in locale/en.json

Hello @olivierperez  
Following your comment in !122 I've just cherry-picked my commit and rebased it onto the release branch.  
Best regards

See merge request !131
2016-06-21 22:52:54 +02:00
Erik Martin-Dorel
b69c171d79 Fix typo in locale/en.json 2016-06-21 22:31:23 +02:00
Olivier Perez
9f564b958e Merge branch 'patch-1' into 'develop'
Fix typo in locale/en.json

Hi,  
Just a minor typofix.  
Kind regards.

See merge request !122
2016-06-21 22:16:28 +02:00
Olivier Perez
e119ff7e38 Merge branch 'fix/typo' into 'master'
Fix two typos



See merge request !124
2016-06-21 22:14:50 +02:00
Olivier Perez
523fa48c6d Merge branch 'fix-plain-index' into 'release'
Fix divBy0 if all info columns are disabled

Hiding all information columns results in a division by zero.

See merge request !128
2016-06-21 22:12:01 +02:00
Olivier Perez
abe50986e0 Merge branch 'update-german-translation' into 'release'
Update german translation

Add missing translation of strings for german locale.

See merge request !130
2016-06-21 22:09:10 +02:00
Markus Jung
a1ca38e269 Update german translation 2016-06-18 22:46:02 +02:00
Markus Jung
72335e1ec5 Fix divBy0 if all info columns are disabled 2016-06-18 22:42:50 +02:00
Markus Jung
e49c19ab00 Avoid warnings due to non-existant 'en' datepicker locale 2016-06-18 22:14:15 +02:00
Rodolphe PELLOUX-PRAYER
a40efa3d56 Fix two typos 2016-06-16 20:39:06 +02:00
Rodolphe PELLOUX-PRAYER
e7afbcd9bf Add editorconfig file 2016-06-16 18:12:13 +02:00
Erik Martin-Dorel
ecb79e3dc1 Fix typo in locale/en.json 2016-06-13 18:48:05 +02:00
Pierre Rudloff
1ddc451829 Valid SPDX license 2016-05-24 16:31:54 +02:00
Gwenn Meynier
21ad6a0381 Add breton translation 2016-05-22 20:44:49 +02:00
Antonin
bcb289406e Missing translation ; prompt URL option enabled 2016-05-12 17:57:53 +02:00
Antonin
a37d621fab Factorized description button and added modal hint. 2016-05-12 17:41:27 +02:00
Olivier Perez
c972e6f274 Merge branch 'feature/confirmation-on-column-deletion' into 'develop'
Demande de confirmation lors de la supression d'une colonne.



See merge request !119
2016-05-12 16:31:30 +02:00
Antonin
f681a6f0e8 Enabling/Disabling markdown editor 2016-05-12 16:20:47 +02:00
Antonin
83e0cae47a Markdown pour la description
- Ajout d'un editeur de markdown
    - On garde le formattage des espaces
    - On empeche l'utilisateur de mettre du html
2016-05-11 17:38:34 +02:00
Antonin
beee7874dc Correcting link without text (and some small refactoring) 2016-05-10 17:53:47 +02:00
Antonin
1c633d74d9 Using the library 2016-05-10 17:53:06 +02:00
Antonin
67ec67d50f Added the parsedown library 2016-05-10 16:57:31 +02:00
Antonin
f92d0009bb Confirmation on column removal 2016-05-10 16:11:47 +02:00
Olivier Perez
f61955a80b Merge branch 'bugfix/deny-empty-column-creation' into 'develop'
Empêcher l'ajout de colonne vide

Fix #123 

- Empêche l'ajout de colonne vide
- Permet la suppression des colonnes vides existantes
- Si l'on a une erreur lors de l'ajout de la colonne , on reste sur le formulaire d'ajout
- Correction de la date pour prendre le bon format par rapport a la langue

See merge request !118
2016-05-09 16:00:23 +02:00
Antonin
b949e3e2e4 Using I18N for DatePicker in addColumn 2016-05-09 15:52:03 +02:00
Antonin
905208c211 Deny the possibility to add an empty slot. 2016-05-09 15:07:23 +02:00
Antonin
d05a1e5182 Allowing to remove empty slot. 2016-05-09 14:49:09 +02:00
Antonin
b359139bae Merge branch 'FixDatePicker' into 'develop'
Fix date picker

Fix #154

And more Smarty on poll creation

See merge request !115
2016-05-04 22:15:57 +02:00
Olivier Perez
feba9ebc96 Merge branch 'develop' into FixDatePicker 2016-05-04 19:41:34 +02:00
Olivier Perez
de9ca55feb Merge branch 'improvement/install' into 'develop'
Improvement - install

A check.php page before install
Updating config.tpl
Verification of config.php creation at install

See merge request !116
2016-05-04 19:28:33 +02:00
Olivier Perez
0f75055517 Fix langs + date formats 2016-05-04 19:21:40 +02:00
Antonin
5dead9e9e4 Corrections following Olivier's comments 2016-05-04 11:12:59 +02:00
Antonin
b739683d96 Install : check that the config.php file is written. 2016-05-04 01:27:51 +02:00
Olivier Perez
5ae9d8fc1b Reword FR "Export en CSV" to "Export Tableur (CSV)" 2016-05-04 01:07:24 +02:00
Antonin
59f437d80e Updating config.tpl from config.template.php 2016-05-04 01:01:11 +02:00
Olivier Perez
bc4b92a7bb More smarty! 2016-05-04 00:58:45 +02:00
Antonin
77de8f38e7 Check.php is used first at install and available in admin 2016-05-04 00:58:31 +02:00
Antonin
f046cbdf95 Check.php 2016-05-04 00:57:45 +02:00
Olivier Perez
cb63031f15 Refactor create_date_poll.php 2016-05-04 00:56:53 +02:00
Olivier Perez
48cd77a5fc Use Smarty to display step 3/4 of poll creation 2016-05-04 00:28:39 +02:00
Olivier Perez
498a6a740f Fix datepicker when adding column 2016-05-03 23:55:30 +02:00
Olivier Perez
20d222dcfe Classical polls - Remove loops on moments 2016-05-03 23:31:44 +02:00
Olivier Perez
1e0a8b25bc Fix warnings on poll creation 2016-05-03 22:02:09 +02:00
Olivier Perez
ab0748597a Merge branch 'feature/improve-self-editing-vote' into 'develop'
Feature : improve self editing vote

Adding a possibility to the user to enter his email address and to receive the personalized url by email.

See merge request !110
2016-05-03 21:16:05 +02:00
Antonin
8981ca9cee Removing specification of error reporting in actions 2016-05-03 21:12:51 +02:00
Antonin
a376e18a90 Translation : CLose => Close 2016-05-03 21:12:51 +02:00
Antonin
00dcd4a006 Better use of the translation system 2016-05-03 21:12:51 +02:00
Olivier Perez
2297b628dc Fix comments form URL 2016-05-03 21:11:19 +02:00
Olivier Perez
0a9855848a Update composer.lock 2016-05-03 21:11:19 +02:00
Olivier Perez
02b94b44d3 Merge branch 'bugfix/config_encoding_on_install' into 'develop'
Config.php has bad encoding on install

Fix #142 

See merge request !114
2016-05-03 21:01:37 +02:00
Olivier Perez
a6db9e6da1 Fix string 2016-05-03 20:59:45 +02:00
Olivier Perez
e570ed2c87 Merge branch 'bugfix/limited-result-from-wizard' into 'master'
Hidden results work from wizard creation

Fix #153

See merge request !113
2016-05-03 20:24:44 +02:00
Antonin
07b60b22f6 Config.php bad encoding on install 2016-05-03 17:24:05 +02:00
Antonin
fffe5dab82 Removing some unused functions 2016-05-03 17:17:48 +02:00
Olivier Perez
6a277eeda3 Merge branch 'improvement/find_poll_email_template' into 'develop'
Improvement of the email template to find polls

Fix #78 

See merge request !111
2016-05-03 14:21:19 +02:00
Olivier Perez
dc71de8ce6 Merge branch 'bugfix/escaping-email-chars' into 'develop'
Escaping email chars

Fix #126 

See merge request !112
2016-05-03 14:19:11 +02:00
Antonin
cd130e6443 Hidden results work from wizard creation 2016-05-03 12:00:22 +02:00
Antonin
69d8b7aa96 Removing useless UTF-8 specification 2016-05-02 17:51:01 +02:00
Antonin
d26dae1298 Escaping some forgotten vars in create_poll 2016-05-02 17:44:43 +02:00
Antonin
f4aa091b80 Adapting html escape for emails 2016-05-02 17:44:14 +02:00
Antonin
73eb1d07da Removing unused translations. 2016-05-02 15:16:05 +02:00
Antonin
2f7b48d142 Improved the email template for finding polls 2016-05-02 15:13:38 +02:00
Antonin
fc235cefeb Using o80::i18n shortcut and cleaning 2016-05-02 14:10:14 +02:00
Antonin
69e9f656ce Some refactoring 2016-05-01 14:21:31 +02:00
Antonin
97a9d49c20 Added personalized link translations 2016-05-01 14:21:31 +02:00
Antonin
391a8f751b Added the edit link reminder functionnality 2016-05-01 14:21:31 +02:00
Antonin
8085dd3c3e Adding remove key functionnality to SessionService 2016-04-29 19:38:00 +02:00
Antonin
42283dade2 Token default lenght 2016-04-29 18:46:22 +02:00
Antonin
4faf1be986 Message : allow showing a template 2016-04-29 18:46:02 +02:00
Antonin
62cc6f6f09 Added some security in add_comment.php 2016-04-29 18:44:33 +02:00
Olivier Perez
c5c1dee6a7 Fix Increase size of poll id in all tables 2016-04-27 13:22:36 +02:00
Olivier Perez
f0c36ec05f Merge branch 'Ecmu/framadate-Issue51' into develop 2016-04-27 01:15:01 +02:00
Olivier Perez
4fc2d1022e Use bg-info background instead of '?' for unanswered slots 2016-04-27 01:13:32 +02:00
Olivier Perez
11ff60a170 Fix CHOICE_REGEX to allow space 2016-04-27 01:01:23 +02:00
Olivier Perez
8a3e74cadb Clean code 2016-04-27 00:59:20 +02:00
Olivier PEREZ
63ed3b110f Merge branch 'bkummel/framadate-feature/dutch-translation' 2016-04-27 00:39:03 +02:00
Olivier PEREZ
3e4ee299ec Reorder langs by name 2016-04-27 00:33:53 +02:00
Olivier PEREZ
0579580a4f Complete NL translation 2016-04-27 00:33:29 +02:00
Antonin
6d6334aa6d Improving edit link visibility 2016-04-27 00:12:21 +02:00
Antonin
ada0471b27 Removing unused imports. 2016-04-27 00:10:31 +02:00
Antonin
64454f0f79 Merge branch 'feature/create_interval_date' into 'develop'
Définition des dates par interval

Voilou, voilou

See merge request !106
2016-04-26 23:44:33 +02:00
Olivier PEREZ
c102661590 LeftPad day/month/year 2016-04-26 23:29:22 +02:00
Olivier PEREZ
96c28738c4 PHP: Check if there are at most MAX_SLOTS_PER_POLL dates before create a poll 2016-04-26 23:16:08 +02:00
Olivier PEREZ
51fbee139d Javascript : Don't let user insert more than 123 dates in a row 2016-04-26 22:45:59 +02:00
Olivier PEREZ
1ddc11af58 Remove console.log 2016-04-26 21:54:54 +02:00
Olivier PEREZ
c493de7cbe Merge branch 'develop' into feature/create_interval_date 2016-04-26 21:53:47 +02:00
Bart Kummel
1f48e984d3 Added Dutch translation 2016-04-16 14:25:11 +02:00
Olivier PEREZ
b35aa2420e Fix comments deletion 2016-04-12 00:19:15 +02:00
Olivier PEREZ
a055c6d739 Merge branch 'master' into develop 2016-03-23 22:57:04 +01:00
Olivier Perez
448b5e05b5 Merge branch 'feature/ES_translate' into 'master'
Translation to spanish

Merci pour le service framadate !

See merge request !103
2016-03-23 22:43:39 +01:00
Olivier Perez
28381f18a6 Merge branch 'Fix/119-Translations-change' into 'develop'
119 - Amélioration des traductions pour les permissions d'édition d'un sondage.

Changement des libellés visibles par le créateur d'un sondage concernant les permissions d'édition d'un vote.

Fix #119 

See merge request !107
2016-03-23 22:40:14 +01:00
Olivier PEREZ
c141644c21 Merge branch 'Quent--y/framadate-feature/Occitan' 2016-03-23 20:59:23 +01:00
ecmu
c857eac91c #51 : make empty vote different from 'No' 2016-03-19 10:30:49 +01:00
Olivier PEREZ
3f2245bd8c Completion du Changelog 2016-03-15 00:11:24 +01:00
Olivier PEREZ
f6751d8daf Merge branch 'master' into develop 2016-03-14 23:55:39 +01:00
Olivier PEREZ
03c480e32e Fix error when customized ID is not selected 2016-03-14 23:55:18 +01:00
Olivier PEREZ
783affb73a Remove trailling dots 2016-03-14 23:55:12 +01:00
ecmu
8216bcbaa6 #51 : make empty vote different from 'No' 2016-03-13 16:20:38 +01:00
Quentin
39b4673bab Update, best coherence in vocabulary 2016-03-11 15:22:07 +01:00
Ecmu
84414aeb97 #51 : make empty vote different from 'No' 2016-03-06 20:50:59 +01:00
Ecmu
af19e08b02 #51 : make empty vote different from 'No' 2016-03-06 20:36:22 +01:00
Antonin
8909333f09 Encore un point en trop... Yurk.~ 2016-03-05 18:35:46 +01:00
Antonin
7da17c4f28 Suppression d'un point inutile dans une traduction. 2016-03-05 18:24:07 +01:00
Antonin
ebfdf68d15 Libellés changés pour la modification des votes du sondage.
Fixes #119
2016-03-05 18:02:22 +01:00
Olivier Perez
ee80682223 Merge branch 'fix/Merge_develop_master_comments' into 'develop'
Fix : L'ajout de commentaire ne fonctionne pas avec un sondage nommé.

Deux problèmes corrigés : 
 - En cas d'erreur d'ajout d'un commentaire, le message n'était pas montré
 - On ne prenait pas en compte la possibilité que le sondage puisse être appelé par son nom et pas par son ID

See merge request !105
2016-03-05 17:45:13 +01:00
Antonin
caa1072712 Merge branch 'develop' of git.framasoft.org:framasoft/framadate into develop 2016-03-05 17:37:29 +01:00
Antonin
f07b4c3d10 Merge branch 'fix/hide_vote_form' into 'develop'
Fix/hide vote form

Lorsque le mot de passe est requis pour voter mais pas pour voir le sondage, on n'affiche plus le formulaire de vote (la protection était déjà présente pour les sondages classiques).

See merge request !104
2016-03-05 16:44:57 +01:00
Pierre Goifon
aec2f04965 Fixes #140 - Auto-select url fields when user clicks in it
(cherry picked from commit e7064ed70c)
2016-03-05 16:42:34 +01:00
Olivier PEREZ
e7064ed70c Fixes #140 - Auto-select url fields when user clicks in it 2016-03-05 16:35:13 +01:00
Antonin
a42f232196 Ajout de commentaire sur un sondage nommé. 2016-03-05 16:33:42 +01:00
Julien Lepiller
c675c3f026 Fix #143 - Apply the patch of Julien Lepiller
(cherry picked from commit 6481de4347)
2016-03-05 16:28:04 +01:00
Julien Lepiller
6481de4347 Fix #143 - Apply the patch of Julien Lepiller 2016-03-05 16:27:26 +01:00
Olivier PEREZ
67ed7977d6 Classical poll - Add missing check of $expired 2016-03-05 16:24:54 +01:00
Olivier PEREZ
449f6bfc9d Date poll - Don't display vote form when not granted 2016-03-05 16:17:18 +01:00
Antonin
7523294508 Merge branch 'develop' of git.framasoft.org:framasoft/framadate into develop 2016-03-05 16:05:49 +01:00
Olivier PEREZ
284dc4e5a5 Fix after merge 2016-03-05 16:05:37 +01:00
Antonin
fdbb3d73c0 Merge branch 'develop' of git.framasoft.org:framasoft/framadate into develop 2016-03-05 16:03:57 +01:00
Olivier PEREZ
0ffdbe8573 Merge branch 'master' into develop 2016-03-05 15:59:10 +01:00
Sylvain Lesage
da933b7668 Date format. See www.cyberciti.biz/faq/linux-unix-formatting-dates-for-display/ 2016-03-04 17:04:58 -04:00
Sylvain Lesage
b635fed880 Translation to spanish 2016-03-04 16:36:22 -04:00
Quentin
4dc13587eb Update OC 2016-03-04 20:16:53 +01:00
Quentin
d0e00de893 Alx => als 2016-03-04 19:32:30 +01:00
Quentin
7f9ea895ae Update of some mistakes 2016-03-04 19:18:23 +01:00
Olivier PEREZ
276f732def Fix #145 and #146 - Sort slots before check slots hash 2016-03-02 23:55:12 +01:00
Olivier PEREZ
1ef4551779 Fix Occitan language 2016-03-02 23:26:31 +01:00
Olivier Perez
a095c763fd Merge branch 'feature/Occitan' into 'master'
Feature/occitan

Request in order to integrate the Occitan language, in its lengadocian version, to Framadate. The ISO code is OC.

See merge request !101
2016-02-25 14:51:35 +01:00
Quentin
2caa5117d6 Update to add the Occitan language 2016-02-23 09:25:05 +01:00
Olivier PEREZ
bde59fe974 Fix the position computation to insert default vote when adding column 2016-02-22 23:51:50 +01:00
goofy-bz
31c3b9f974 minor lang fix 2016-02-20 18:12:35 +01:00
goofy-bz
5decfb4488 minor lang fix 2016-02-20 17:58:39 +01:00
Quentin
a6373c6e5f Final update for the Occitan language 2016-02-13 18:50:51 +01:00
Quentin
b24d59478f Update, almost finished 2016-02-13 17:40:43 +01:00
Quentin
c0875e6f58 Update 2016-02-12 18:06:49 +01:00
Quentin
2816bd7362 Update 2016-02-12 18:06:21 +01:00
Quentin
070ea74fa9 Update 2016-02-12 17:54:09 +01:00
Quentin
d537d5028c Added the Occitan version 2016-02-12 17:27:15 +01:00
Olivier Perez
55ef95d923 Merge branch 'fix/127_add_missing_translation' into 'master'
add missing translation : ref #127

Hi there,
As suggested by Olivier, I added the missing strings for the other languages (with locale prefix and english text).

Regards

Vincent

See merge request !100
2016-02-04 16:23:04 +01:00
Vincent Gatignol-Jamon
7d9a1140a4 add missing translation : ref #127 2016-02-04 15:20:35 +01:00
Olivier PEREZ
510b797f85 Check if current user is admin BEFORE check editedVoteUniqueId 2016-01-19 22:28:34 +01:00
Antonin
a19cc5334c Merge branch 'fix/ConcurrentEdition' into 'master'
Edition concurrente à un vote

Correction du problème quand un admin modifie les colonnes d'un vote en même temps que quelqu'un vote ou met à jour un vote.

La solution gardée est de garder un hash des colonnes côté client, de le soumettre avec le vote, et de le comparer au hash des colonnes actuelles.

See merge request !98
2016-01-07 12:06:28 +01:00
Olivier PEREZ
2109ef3469 More precision on MD5 regex 2016-01-06 19:38:19 +01:00
Olivier PEREZ
3fc25c0c4a Can't vote if admin made some changes on slots 2015-12-09 00:23:32 +01:00
Olivier PEREZ
310aa596cc Fix missing key 2015-12-08 00:20:36 +01:00
Olivier PEREZ
c6f3b9b527 Fill "action" attribute in comments form 2015-12-08 00:20:34 +01:00
Olivier PEREZ
cb0e74fba8 Fixes #124 - vote deletion 2015-12-08 00:20:23 +01:00
Olivier Perez
128b5f5dd3 Fix slots/votes/comments selection for granted users 2015-12-07 13:57:29 +01:00
Antonin
ab9c0660cf Merge branch 'feature/UrlNaming' into 'develop'
Choix de l'URL du sondage

Traitement de l'issue #39 

- Agrandissement du champ ID
- Possilité de choisir son propre ID dans un champ texte (64 caractères, lettres/nombres/-)
 - Si ID choisi => L'ID de l'admin est généré sur 24 caractères
 - Si ID non choisi => L'ID de l'admin est généré sur 8 caractères + les caractères de l'ID
- Changement du htaccess

See merge request !97
2015-12-07 11:24:26 +01:00
Olivier Perez
a4ff2aaaa6 Standardization of regex format 2015-12-07 10:35:06 +01:00
Olivier PEREZ
0c5f623ce0 Fixes #102 - Increase the number of moments available to 99 per date 2015-12-06 03:21:04 +01:00
Olivier PEREZ
6823133322 Update Framadate version number 2015-12-05 21:20:19 +01:00
Olivier PEREZ
a10220642e UrlNaming - Update htaccess example 2015-12-05 19:30:10 +01:00
Olivier PEREZ
604be480c4 UrlNaming - "Translate" 2015-12-05 19:26:32 +01:00
Olivier Perez
5e8fbe2e48 Merge branch 'feature/display-title-on-table-header' into 'develop'
Affichage d'un titre sur l'entête des colonnes

Attribut title ajouté à l'entête des colonnes pour voir tout le contenu lorsqu'elle sont tronquées.

See merge request !96
2015-12-05 18:53:10 +01:00
Olivier Perez
5c895cca22 Merge branch 'feature/own_vote_edition_in_session' into 'develop'
Feature / Own vote edition in session

Dans le cas ou chaque utilisateur ne peut modifier que son propre vote, le dernier vote d'un utilisateur est gardé en session pour qu'il n'ait pas à utiliser l'URL personnelle tout de suite.

Fix #118 

See merge request !95
2015-12-05 18:52:42 +01:00
Olivier PEREZ
013aeaecd1 UrlNaming - Check if poll id is already used 2015-12-05 17:28:42 +01:00
Olivier PEREZ
17d050507b UrlNaming - Allow new id format when loading a poll 2015-12-05 17:28:40 +01:00
Olivier PEREZ
1efd7b9ab0 UrlNaming - Generate id unless if user has already choosen one 2015-12-05 17:28:38 +01:00
Olivier PEREZ
fac20a5908 UrlNaming - Add ID field to creation form 2015-12-05 17:28:35 +01:00
Olivier PEREZ
86fb49d51e Fix tpl binding 2015-12-05 14:38:24 +01:00
Olivier PEREZ
421f86d8a3 UrlNaming - Increase size of poll's id column 2015-12-05 14:24:38 +01:00
Antonin
374a284393 Oublis sur le merge 2015-12-05 14:00:55 +01:00
Antonin
a9bddc94b3 Affichage d'un titre sur l'entête des colonnes 2015-12-02 17:29:42 +01:00
Olivier PEREZ
2b9f68780e Fixes #122 use the right method to use table names 2015-12-01 22:20:24 +01:00
Antonin
65a753129a Remembering last user vote in session 2015-11-30 22:40:26 +01:00
Olivier PEREZ
44c24493b6 Check date format and display warning on the failing fields 2015-11-30 22:23:57 +01:00
Olivier PEREZ
4cf28bf8f6 Nice effect for adding CSS class has-error 2015-11-30 22:12:09 +01:00
Olivier PEREZ
1c8a41dc02 Add has-error CSS class when dates are not ordered + stop checking if first date is not passed 2015-11-30 22:06:07 +01:00
Antonin
a219ccdee3 Added SessionService 2015-11-30 21:58:26 +01:00
Antonin
c78613481e Merge branch 'master' into develop
Conflicts:
	CHANGELOG.md
	htaccess.txt
	locale/it.json
	studs.php
	tpl/part/vote_table_classic.tpl
	tpl/part/vote_table_date.tpl
2015-11-30 21:55:56 +01:00
Olivier PEREZ
c2f5c4731a Fix the date parsing in step 3 of date poll creation 2015-11-30 21:04:19 +01:00
Olivier PEREZ
9accf7dab8 Add modal to insert an interval of dates 2015-11-30 21:04:17 +01:00
Antonin
2fd5b3e13d Merge branch 'fix/Encode_actions_base64' into 'master'
Encode action values in base64 instead of url_encode (because of UrlRewrite)

L'URL rewriting gère mal les caractères encodés, ils les décodent à la volé.
Ce qui fait que `%26` devient `&` et fait bugger quand l'url est de la forme `x.php?name=Titi%26Toto`, il la traduit en `x.php?name=Titi&Toto`. `x` est alors égal à `Titi`, et un paramètre Toto fait son apparition.

Avec apache, il faut utiliser le flag `[B]`. N'ayant pas trouvé de solution pour nginx on a choisi d'encoder tout en base64.

See merge request !94
2015-11-30 20:43:47 +01:00
Olivier PEREZ
5750a36172 Rename add_slot by add_column 2015-11-30 20:38:53 +01:00
Olivier PEREZ
5d4958e1f4 Encode action values in base64 instead of url_encode (because of UrlRewrite) 2015-11-30 20:26:23 +01:00
Olivier PEREZ
9b4b72e3d4 Fix title of button to add column 2015-11-25 01:35:10 +01:00
Olivier PEREZ
f88131f512 Merge branch 'Filopoulos/framadate-master'
Conflicts:
	locale/it.json
2015-11-23 16:49:17 +01:00
Olivier PEREZ
f588a3945e Fixes #109 - Keep url encoding while URLRewriting 2015-11-23 16:30:29 +01:00
Nikos
4f90895882 qualche correzione 2015-11-21 18:22:20 +01:00
JosephK
50febffdf2 Merge branch 'master' into 'master'
Only one day needed to submit

Issue #108 

See merge request !92
2015-11-20 09:14:01 +01:00
JosephK
18f488c4f6 Only one day needed to submit 2015-11-20 09:12:10 +01:00
Olivier Perez
e647bd6e33 Merge branch 'develop' into 'develop'
Only one day needed to submit

Issue #108 

See merge request !91
2015-11-19 18:34:08 +01:00
JosephK
801dc150e2 Only one day needed to submit 2015-11-19 15:38:27 +01:00
Olivier PEREZ
36b79e5390 URL rewriting: Add _- to regex for admin action names 2015-11-19 08:19:10 +01:00
Olivier Perez
4544dfc5de URL rewriting: Add "_-" to regex for action names 2015-11-18 17:45:39 +01:00
Olivier Perez
f3ef1f9647 Merge branch 'develop' into 'develop'
Add rewrites for the actions with underscores

See https://git.framasoft.org/framasoft/framadate/issues/117

I couldn't find a "hotfix" branch, so I guess this commit should be merged into develop?

See merge request !90
2015-11-18 17:38:27 +01:00
Imre Jonk
2115509d95 Adapt new rewrites to develop branch 2015-11-18 17:25:25 +01:00
Imre Jonk
e7ec470723 Add rewrites for the actions with underscores
See https://git.framasoft.org/framasoft/framadate/issues/117
2015-11-18 16:43:38 +01:00
Olivier Perez
2e42fe6dcd Merge branch 'develop' into 'develop'
Traduction Italienne 08/11/15



See merge request !88
2015-11-15 11:22:29 +01:00
Nikos
3aac2dfdd6 footer correction 2015-11-15 10:32:26 +01:00
Nikos
64d3157d77 error correction 2015-11-14 17:03:18 +01:00
Nikos
cc95961c85 Correction message link pour modifier la votation 2015-11-08 18:35:10 +01:00
Nikos
7b8138608a traduzione italiana 07/11/2015 2015-11-07 11:59:33 +01:00
Olivier Perez
c019cd08b4 Mise à jour du changelog pour 0.9.2 et 0.9.3 2015-11-06 14:16:05 +01:00
Antonin
163901bec4 Merge branch 'fix/Issue117_Fix_url_rewriting' into 'master'
Fixes #117 - Fix url rewriting example

Tout est dans le titre

See merge request !87
2015-11-05 22:49:49 +01:00
Olivier PEREZ
109e72bdc1 Tiens ta correction 2015-11-05 22:48:00 +01:00
Antonin
2dd68146a2 Merge branch 'fix/Issue109_delete_column' into 'master'
Fixes issue #109 - Urlencode title of column to delete for classical polls too

Tout est dans le titre 😃 

See merge request !85
2015-11-05 22:45:01 +01:00
Olivier PEREZ
171d45938c Move URL encore to Utils::getUrlSondage 2015-11-05 22:43:54 +01:00
Antonin
4cb189299f Merge branch 'fix/Issue114_delete_last_column' into 'master'
Fixes #114 - Can't delete last column anymore

Correction de la règle qui vérifie que la colonne à supprimer n'est pas la dernière.

`strpos` ne retourne jamais `-1` mais `false`

See merge request !82
2015-11-05 22:32:02 +01:00
Antonin
af175e40d7 Merge branch 'olivierperez/framadate-fix/Error_message_when_delete_column'
Conflicts:
	locale/it.json
2015-11-05 22:30:10 +01:00
Olivier PEREZ
79b3cc2b43 Fixes #117 - Fix url rewriting example 2015-11-05 22:20:33 +01:00
Olivier Perez
76161240c2 Merge branch 'fix/Issue116_allow_user_to_edit_his_own_private_vote_on_the_fly' into 'master'
On permet d'éditer son vote à la volée.

Dans le cas d'un vote ou l'utilisateur ne peut modifier que son propre vote, après qu'il ait ajouté (ou édité) son vote, on affiche le crayon qui permet d'éditer.

Fix #116

See merge request !86
2015-11-05 21:38:59 +01:00
Antonin
e1221b5a90 It's better to also correct the date poll... 2015-11-05 21:37:51 +01:00
Nikos
3a4b6b79c8 Traduzione italiana completata 2015-11-05 21:29:48 +01:00
Nikos
964310e8c0 traduzione italiana 2015-11-05 21:28:22 +01:00
Antonin
3f774d5445 On permet d'éditer son vote à la volée.
Dans le cas d'un vote ou l'utilisateur ne peut modifier que son propre vote, après qu'il ait ajouté (ou édité) son vote, on affiche le crayon qui permet d'éditer son vote.

Fix #116
2015-11-05 21:26:59 +01:00
Olivier Perez
7fd1afb12a Merge branch 'issue#106' into 'develop'
Fixe issue #106 - Syntax errors in CSS files

Syntax errors in CSS files;
Erreurs de syntaxe dans des fichiers CSS

See merge request !84
2015-11-05 21:07:54 +01:00
Olivier PEREZ
afe8eac89b Fixes issue #109 - Urlencode title of column to delete for classical polls too 2015-11-05 21:02:49 +01:00
blr21560
cf9cda99bd Fixe issue #106
Syntax errors in CSS files
Erreurs de syntaxe dans des fichiers CSS
2015-11-04 18:03:09 +01:00
Olivier Perez
acbf7db8b5 Fixes #114 - Can't delete last column anymore 2015-11-04 12:37:53 +01:00
Olivier PEREZ
c3b48b885d Refactor something :-) 2015-11-03 21:17:00 +01:00
Olivier PEREZ
40f2a1729c Better message when fail to create column 2015-11-03 21:15:47 +01:00
Nikos
c385b80475 Traduzione italiana completata 2015-10-31 20:53:11 +01:00
Nikos
43194e4ece traduzione italiana 2015-10-31 20:40:44 +01:00
Nikos
b590768a14 Traduzione italiana completata 2015-10-31 20:16:56 +01:00
Nikos
b8210a2eb7 traduzione italiana 2015-10-31 20:02:41 +01:00
Olivier Perez
f0a45bd203 Merge branch 'feature/private-polls' into 'develop'
Feature : private polls

Allowing to protect a poll by password .

An option can allow the user which doesn't know the password to see (and only see) the poll and its votes.

(Sorry for the double commits...)

See merge request !79
2015-10-30 22:04:56 +01:00
Antonin
d72c5295ce useless spaces 2015-10-30 22:02:24 +01:00
Antonin
91de1661bb Inlining a change event 2015-10-30 22:02:24 +01:00
Antonin
7b05eeec44 Remove dev tests 2015-10-30 22:02:24 +01:00
Antonin
1376426f27 Useless else if removed 2015-10-30 22:02:24 +01:00
Antonin
3eae4d97d4 Local improvement (remove useless dots)
- French errors messages were missing due to bad merge.
2015-10-30 22:00:43 +01:00
Antonin
15c57eeb37 Allows the admin to modify password related stuff. 2015-10-30 20:45:18 +01:00
Antonin
3192098ff5 Merge branch 'feature/private-polls' of git.framasoft.org:Antonin/framadate into feature/private-polls 2015-10-29 15:00:53 +01:00
Antonin
c6d6ad2393 Applying the password restriction in studs. 2015-10-29 11:52:13 +01:00
Antonin
bb37d16dff Sugar for comment sending.
- Do not send if comment empty
    - Disable submit button while submit is in progress
2015-10-29 11:52:13 +01:00
Antonin
22253b4e68 Cleaning useless code. 2015-10-29 11:52:13 +01:00
Antonin
6cc46b5bae Composer.lock update 2015-10-29 11:52:13 +01:00
Antonin
b462a6bbdb Hashing the password early 2015-10-29 11:52:13 +01:00
Antonin
161b6b14c0 Password options are used with poll creation.
- Just found that the "hidden" parameter wasn't...
2015-10-29 11:52:13 +01:00
Antonin
cddbf3cb43 Safe way to hash password. 2015-10-29 11:52:13 +01:00
Antonin
f888a643e0 Added column password in poll table. 2015-10-29 11:52:13 +01:00
Olivier Perez
1ba245258a Merge branch 'master' into develop 2015-10-29 09:42:30 +01:00
Olivier PEREZ
8f2c9f07d4 Don't split admin_poll_id as poll_id 2015-10-28 22:53:56 +01:00
Olivier PEREZ
15640a6788 Fix stupid break in adminstuds 2015-10-28 22:11:00 +01:00
Antonin
c194d9a43f Applying the password restriction in studs. 2015-10-28 17:30:42 +01:00
Antonin
1ec9ea2dd5 Sugar for comment sending.
- Do not send if comment empty
    - Disable submit button while submit is in progress
2015-10-28 16:36:54 +01:00
Antonin
f6f04c69e0 Cleaning useless code. 2015-10-28 15:08:54 +01:00
Antonin
6255a55128 Merge branch 'feature/admin_search_by_mail' into 'develop'
Admin : Recherche de sondages par adresse mail de l'auteur

- Ajout d'un champ pour chercher par adresse mail (type=text par laisser la possibilité de chercher une partie de l'adresse).
- Ajout des critères de recherche dans la requête SQL

See merge request !78
2015-10-28 14:25:45 +01:00
Antonin
5955c0ece0 Composer.lock update 2015-10-28 13:43:58 +01:00
Antonin
79133ef70e Hashing the password early 2015-10-28 13:40:46 +01:00
Antonin
e20ac74f0b Password options are used with poll creation.
- Just found that the "hidden" parameter wasn't...
2015-10-28 13:40:46 +01:00
Antonin
05f840ceb0 Safe way to hash password. 2015-10-28 13:31:16 +01:00
Antonin
4e01d07da0 Added column password in poll table. 2015-10-28 13:30:31 +01:00
Olivier Perez
2cfeb385bb Admin: Search in polls by author's mail address 2015-10-28 13:06:02 +01:00
Olivier Perez
16c3c70f5a Update CHANGELOG for v1.0 2015-10-28 12:56:30 +01:00
Olivier Perez
61605e5e5a Merge branch 'master' into develop 2015-10-28 12:28:20 +01:00
Olivier Perez
3f1f957c5e Merge branch 'feature/ajax_submission_for_comments' into 'develop'
Feature - ajax submission for comments

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

The comment list is also refreshed while commenting.

See merge request !72
2015-10-26 17:00:40 +01:00
Antonin
5552cc4d9d studs.js refactoring.
- Replacing "" by ''
    - Use chaining when possible
    - Correct the scrolling on comment correctly set.
2015-10-26 16:49:32 +01:00
Antonin
974148550b Refactoring for better reading 2015-10-26 16:28:59 +01:00
Antonin
0e0becb5c4 Replacing :: by NotificationService:: 2015-10-26 16:26:22 +01:00
Olivier Perez
fd5cfe3ca6 Merge branch 'reverse-proxy-https' into 'develop'
Set URLs to https if HTTP X-Forwarded-Proto=https

This line changes all URLs to https URLs if the Framadate installation
is behind a reverse proxy, and the reverse proxy adds the
X-Forwarded-Proto header with the 'https' value. Configuration for
nginx:

> proxy_set_header X-Forwarded-Proto https;

This setup is currently used by the Framadate installation of
Bits of Freedom (https://kies.bof.nl).

See merge request !77
2015-10-26 15:55:08 +01:00
Olivier Perez
d652407252 Merge branch 'develop' into 'develop'
Fix issue #98

Placement des radios à côté des labels pour éviter le retour au début du tableau de la barre de scroll (les navigateurs basés sur webkit font le focus sur les radios quand on clique sur les labels) #98 

See merge request !75
2015-10-26 15:21:21 +01:00
Imre Jonk
7ba352f0e5 Set URLs to https if HTTP X-Forwarded-Proto=https
This line changes all URLs to https URLs if the Framadate installation
is behind a reverse proxy, and the reverse proxy adds the
X-Forwarded-Proto header with the 'https' value. Configuration for
nginx:

proxy_set_header X-Forwarded-Proto https;

This setup is currently used by the Framadate installation of
Bits of Freedom (https://kies.bof.nl).
2015-10-26 15:02:02 +01:00
JosephK
761b485d9b Fix issue #98 2015-10-26 13:44:23 +01:00
Antonin
166927f8af Ajaxifisation des commentaires.
- Permet de ne pas perdre ses votes à l'ajout d'un commentaire.
	- On rafraichit aussi à l'envois du commentaire la liste des commentaires.

Fix #3
2015-10-22 22:50:53 +02:00
Olivier Perez
5ffc332b48 Merge branch 'Add_nonHTML_version_to_mails' into 'develop'
Fix #87 Use PHPMailer to add non-HTML body to mails

Utilisation de PHPMailer

See merge request !70
2015-10-22 21:58:46 +02:00
Olivier PEREZ
882203fad8 Fix #87 Use PHPMailer to add non-HTML body to mails 2015-10-22 21:34:15 +02:00
Olivier PEREZ
242b2ca09b Optimise scrolling -> Delete heavy useless JS
(cherry picked from commit 029fcbf470)
2015-10-22 20:33:25 +02:00
Olivier Perez
c3183d564f Merge branch 'feature/hide_hint_panel' into 'develop'
Cacher le message d'aide

J'ai caché le message d'aide dans un modal qu'on ouvre via le bouton (?).

See merge request !68
2015-10-13 01:08:32 +02:00
Antonin
2b38a7ddd6 Refactoring NotificationService 2015-10-13 01:03:41 +02:00
Olivier PEREZ
69a5bc2e1c Hide poll hint into a modal 2015-10-13 00:42:33 +02:00
Olivier PEREZ
fe8f779217 Merge branch 'master' into develop 2015-10-12 23:58:57 +02:00
Olivier PEREZ
4fa4ee0acc Merge branch 'master' into develop
Conflicts:
	admin/migration.php
2015-10-12 23:46:02 +02:00
Olivier Perez
76c2077100 Merge branch 'develop' into 'develop'
Timestamp on comments

The comments are now timestamped

See merge request !66
2015-10-12 23:39:30 +02:00
Antonin
6196a53a45 Display the comment timestamp 2015-10-12 23:14:21 +02:00
Antonin
af95ceb06e French admin translation typo 2015-10-12 22:28:27 +02:00
Antonin
ba9a444f33 Adding a date column in the comment table 2015-10-12 22:26:22 +02:00
Antonin
82876c573e Set a length to comment table's name column
The name column was of text type. Passed to VARCHAR(64), like the poll name column.
2015-10-12 21:59:45 +02:00
Olivier Perez
8c394d9857 Merge branch 'develop' into 'develop'
Make poll description wrapping

As you can see on http://openbar.framadate.org/Ojtrj1Cw2mJjP6AG, the poll description has a scroll bar. This MR wraps the poll description to make it more readable.

See merge request !58
2015-09-15 09:39:39 +02:00
Luc Didry
27fe46640d Make poll description wrapping 2015-09-14 23:33:14 +02:00
189 changed files with 24438 additions and 15753 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

9
.gitignore vendored
View File

@ -1,4 +1,5 @@
.htaccess
.htdigest
.htpasswd
admin/stdout.log
composer.phar
@ -7,7 +8,10 @@ nav
app/inc/config.php
vendor
cache/
tpl_c/
tpl_c/*
!tpl_c/.gitkeep
.php-cs-fixer.cache
.zanata-cache/
# Temp files
*~
@ -21,3 +25,6 @@ Thumbs.db
.project
.idea/
*.iml
#ics temp file
out.ics

62
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,62 @@
image: framasoft/framadate-ci:7.3-pdo_mysql
stages:
- test
- deploy
# Run php-cs-fixer and phpunit on all branches
test:
stage: test
script:
- composer install -o --no-interaction --no-progress --prefer-dist
- php vendor/bin/php-cs-fixer fix --verbose --dry-run
- 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/
# Create artifacts on master
pages:
stage: deploy
script:
- latesttag=$(git describe --tags)
- git checkout ${latesttag}
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
- composer dump-autoload --optimize --no-dev --classmap-authoritative
- 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
- mkdir .public
- cp $RELEASE_ZIP .public/latest.zip
- 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
only:
- tags
except:
- (beta|alpha)

48
.php-cs-fixer.dist.php Normal file
View File

@ -0,0 +1,48 @@
<?php
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setRules([
'array_syntax' => [
'syntax' => 'short'
],
'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_unreachable_default_argument_value' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_strict' => true,
'phpdoc_order' => true,
// 'psr4' => true,
'strict_comparison' => true,
'strict_param' => true,
'concat_space' => [
'spacing' => 'one'
],
])
->setFinder(
PhpCsFixer\Finder::create()
->exclude([
'vendor',
'var',
'web',
'tpl_c'
])
->in(__DIR__)
)
;

9
.po2json.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
po2json -i po/en.po -t locale/en.json --progress none -o po/default.json
for i in po/*.po
do
j=$(echo $i | cut -d '.' -f 1 | cut -d '/' -f 2)
po2json -i $i -t locale/en.json --progress none | ./.renest_json.pl > po/$j.json
mv po/$j.json locale/
done

40
.renest_json.pl Executable file
View File

@ -0,0 +1,40 @@
#!/usr/bin/perl
use strict;
use warnings;
use JSON;
my $json = JSON->new->utf8->space_before(0)->space_after(1)->indent(4)->canonical(1);
my $en_file = 'po/default.json';
my $en;
{
open my $fh, '<', $en_file or die;
local $/ = undef;
$en = <$fh>;
close $fh;
}
$en = $json->decode($en);
my $new_json = {};
my $old_json = '';
while (defined(my $line = <STDIN>)) {
$old_json .= $line;
}
$old_json = $json->decode($old_json);
for my $key (keys %{$old_json}) {
my $index = index($key, '.');
my $real_key = substr($key, 0, $index++);
my $trad_key = substr($key, $index);
if ($old_json->{$key}) {
$new_json->{$real_key}->{$trad_key} = $old_json->{$key};
} else {
$new_json->{$real_key}->{$trad_key} = $en->{$key};
}
}
print $json->encode($new_json);

View File

@ -1,5 +1,276 @@
# 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
- Amélioration : Masquer l'encart au dessus du tableau des votes, maintenant visible grâce à un bouton
- Amélioration : Les commentaires sont horodatés
- Amélioration : Auto wrapping de la description du sondage
- Amélioration : Protection de sondages par mots de passe
- Amélioration : Un click dans les champs URL sélectionne le contenu
- Amélioration : Choix du lien du sondage
- Amélioration : Possibilité de modifier un sondage après expiration
- Amélioration : Confirmation demandée pour supprimer une colonne
- Amélioration : Création d'une sondage par intervale de dates
- Amélioration : Possibilité de ne pas faire de choix sur une colonne
- Amélioration : Amélioration du format des mails
- Amélioration : Amélioration du mode où chaque votant ne peut modifier que son vote
- Amélioration : Fichier check.php pour vérifier la possibilité d'installation
- Amélioration : Changements de libellés
- Amélioration : Admin - Rechercher un sondage grâce à l'adresse mail
- Amélioration : Fluidification du défilement de la page
- Amélioration : Simplification de l'écran de création de sondage
- Fix : Correction de traductions
- Fix : Corrections diverses sur les dates et leurs formats
- Fix : Impossible d'ajouter une colonne vide
- Fix : Possibilité de supprimer des colonnes vides
- Fix : Correction du formulaire de commentaires
- Fix : Correction d'échappements de caractères
- Fix : Rectification des contraintes sur les sondage expirés
- Fix : Interdiction d'exporter les résultats lorsque l'utilisateur ne peut pas les voir
- Technique : Travail sur le service des notifications
- Technique : Prise en compte de l'entête X-Forwarded-Proto
- Technique : Utilisation de PHPMailer pour l'envoi de mails
- Technique : Encore de la Smartization
- Technique : Pas mal de nettoyage de code
## Version 0.9.6 (goofy-bz - Olivier - Quentin - Vincent)
- Fix : Corrections mineures de langues
- Amélioration : Nouvelle langue - Occitan
- Amélioration : Blocage d'un vote si l'admin a changé les possibilités entre temps
## Version 0.9.5 (Olivier)
- Fix : Corrections mineures de langues
- Fix : Correction de la suppresion de votes
- Amélioration : Possibilité d'ajouter plus de "moments" à une date
## Version 0.9.4 (JosephK - Olivier - Nikos)
- Fix : Correction de l'échappement des tables Vote et Slot
- Fix : Encodage des "actions" en base64 pour fonctionner avec l'UrlRewriting
- Fix : Correction d'attributs "title"
- Fix : Un seul jour est requis pour faire un sondage
- Fix : Correction de l'UrlRewriting
- Amélioration : Traduction en Italien
## Version 0.9.3 (Antonin - Olivier - Nikos)
- Fix : Traduction de textes en Italien
- Fix : Empêchement de la suppression de la dernière colonne
- Fix : Possiblité de supprimer des colonnes contenant des caractères spéciaux (par exemple "&")
- Fix : Correction de l'exemple d'URL rewriting (des efforts restent à faire)
- Amélioration : (Mode chacun son vote) Possiblité d'éditer son vote directement après un vote
- Amélioration : Message plus parlant lors de la création d'une colonne
## Version 0.9.2 (Olivier)
- Fix : Completion d'un manque de contrôle sur les ID
## Version 0.9.1 (JosephK - Olivier - Antonin - Michael - Paul)
- Fix : Correction des lenteurs de défilement
- Fix : Arrêt du défilement auto à gauche qu'on clique sur un choix
@ -44,7 +315,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
@ -78,7 +349,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
@ -93,7 +364,7 @@
## Changelog du 21 juin 2011 (pyg@framasoft.net)
- très nombreuses modifications CSS
- modification adminstuds.php : ajout de classes aux formulaires et ajout de stripslashes à l'affichage (TODO: à généraliser)
- modification adminstuds.php : ajout de classes aux formulaires et ajout de stripslashes à l'affichage
- modification infos_sondages.php : simplification du tableau de choix, ajouts de CSS, ajouts de labels pour faciliter la selection
## Changelog version 0.6.7 (mai 2011)
@ -144,7 +415,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,
@ -155,7 +426,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",

View File

@ -1,82 +1 @@
# Pré-installation
## Base de données
Framadate fonctionne indépendemment de la base SQL utilisée.
Cependant la base de donnée doit être créée au préalable,
après avoir renseigné les paramètres de la base de données, créez la.
### PostgreSQL
```bash
su - pgsql
createdb framadate
```
Attention : Si vous créez la base de données avec l'utilisateur "pgsql",
il vous faudra faire un "grant all on <chaque table> to `framadate`" pour donner les droits à l'utilisateur `framadate` de lire et modifier la base.
Les tables de l'applications sont décrites plus loin dans ce fichier dans la partie "Tables de la base de données".
### MySQL
```sql
-- Créer une base de données
CREATE DATABASE IF NOT EXISTS `framadate_db` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
-- Créer un utilisateur
CREATE USER 'framadate_user'@'localhost' IDENTIFIED BY '<password>';
GRANT ALL PRIVILEGES ON `framadate_db`.* TO 'framadate_user'@'localhost';
```
# Installation
Pour installer l'application Framadate, rendez-vous sur la page http://monsite/admin/install.php et remplisez le formulaire.
Une fois le formulaire rempli et envoyé, un script va générer le fichier `app/inc/config.php` puis vous rediriger vers la page de migration.
La page de migration s'occupe :
- D'installer toute la base de données (tables + données d'exemple)
- De mettre à jour la base de données lors des mises à jour de l'applciation.
! Attention, le chargement de la page de migration peu prendre du temps étant donné qu'elle applique toutes les mises à jours requises !
# Accès à la page administrateur
Le répertoire `admin/` fournit l'accès à certainnes actions et informations à protéger.
Il convient de mettre en place un couple de fichiers `.htaccess`/`.htpasswd`, pour restreindre l'accès à la page d'administration de l'application.
Il existe une multitude de tutoriels sur internet à ce sujet.
# Journal de l'application
Un fichier `admin/stdout.log` doit être créé et accessible en écriture
par votre serveur Web. Quelque chose comme devrait convenir:
```bash
touch admin/stdout.log
chmod 700 admin/stdout.log
chown www-data admin/stdout.log
```
# Maintenance
Framadate dispose d'une possibilité de mise en maintenance par le biais d'un fichier `.htaccess`.
La section `<Directory>` relative à Framadate, dans la configuration d'Apache doit au moins contenir :
`AllowOverride AuthConfig Options`
Le fichier `.htaccess` correspondant doit être modifier pour y configurer
l'adresse IP depuis laquelle s'effectue la maintenance.
N'oubliez pas de le recommenter en intégralité une fois la maintenance effectuée.
# Tables de la base de données
Voici la structure des tables de l'application, le nom des tables est donné sans préfixe.
La base se compose de quatre tables :
- `poll` : Le paramètrage des sondages;
- `slot` : les choix disponibles pour chaque sondage;
- `vote` : les votes effectués par les utilisateurs pour chaque sondage;
- `comment` : les commentaires apportés à chaque sondage.
# [Now available here](https://framagit.org/framasoft/framadate/wikis/home)

12
Makefile Normal file
View File

@ -0,0 +1,12 @@
locales:
json2po -P -i locale/en.json -t locale/en.json -o po/framadate.pot
push-locales: locales
zanata-cli -q -B push
pull-locales:
zanata-cli -q -B pull --min-doc-percent 50
./.po2json.sh
stats-locales:
zanata-cli -q stats

View File

@ -1,85 +1,41 @@
# Présentation du projet
# Framadate
![Gitlab](https://git.framasoft.org/assets/logo-black-f52905a40830b30aa287f784b537c823.png)[https://git.framasoft.org](https://git.framasoft.org)
![English](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) Framadate is an online service for planning an appointment or making a decision quickly and easily. No registration is required.
![English](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) **Framasoft uses GitLab** for the development of its free softwares. Our Github repositories are only mirrors.
If you want to work with us, **fork us on [git.framasoft.org](https://git.framasoft.org)**. (no registration needed, you can sign in with your Github account)
![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.
![Français](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png) **Framasoft utilise GitLab** pour le développement de ses logiciels libres. Nos dépôts Github ne sont que des mirroirs.
Si vous souhaitez travailler avec nous, **forkez-nous sur [git.framasoft.org](https://git.framasoft.org)**. (l'inscription n'est pas nécessaire, vous pouvez vous connecter avec votre compte Github)
**Framadate is now in maintenance mode.** [Read more](https://framagit.org/framasoft/framadate/framadate/-/issues/545#note_920869)
---
# Compatibilités des navigateurs
_Dernière mise à jour le 21 avril 2014_
| Navigateur | Version testée |
|------------|-------------------------|
| Firefox | Ubuntu 13.10/FF28 |
| Chrome | Ubuntu 13.10/Chromium33 |
| Opera | (non testé) |
| Konqueror | (non testé) |
| Links | (non testé, inutile) |
| Safari | (non testé) |
| IE | Win7/IE9 |
# Installation
Un fichier est dédié à l'installation de framadate : [INSTALL.md](INSTALL.md).
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/framadate/-/wikis/home>
# Comment contribuer
# Contribute
## De votre côté
1. Créer un compte sur [https://git.framasoft.org](https://git.framasoft.org)
1. Créer un fork du projet principal : [Créer le fork](https://git.framasoft.org/framasoft/framadate/fork/new)
1. Créer une branche nommée feature/[Description]
* Où [Description] est une description en anglais très courte de ce qui va être fait
1. Faire des commits dans votre branche
1. Pusher la branche sur votre fork
1. Demander une merge request
## La suite se passe de notre côté
1. Quelqu'un relira votre travail
* Essayez de rendre ce travail plus facile en organisant vos commits
1. S'il y a des remarques sur le travail, le relecteur fera des commentaires sur la merge request
1. Si la merge request lui semble correcte il peut merger votre travail avec la branche **develop**
## Corrections suite à une relecture
La relecture de la merge request peut vous amener à faire des corrections.
Vous pouvez faire ces corrections dans votre branche, ce qui aura pour effet de les ajouter à la merge request.
## Comprendre le code
Un fichier est dédié à l'appréhension du code de framadate : [Document technique](doc/TECHNICAL.md).
## Code
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/coding>
# Traductions
Les traductions se trouvent dans le dossier `locale`. Chaque langue est dans un fichier JSON différent organisé par section.
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/translating>
# Synthèses des librairies utilisées
# Used libraries
[Smarty](http://www.smarty.net/),
gestion des templates pour PHP
[o80-i18n](https://github.com/olivierperez/o80-i18n),
système d'internationalisation
[PHP 5.4.4](http://php.net)
PostgreSQL ou MySQL
* PHP [PHP 7.3](http://php.net)
* Templating [Smarty](http://www.smarty.net/),
* I18N [o80-i18n](https://framagit.org/framasoft/framadate/o80-i18nn)
* Database: MySQL or MariaDB.
---
Framadate est un fork du projet [STUdS](https://sourcesup.cru.fr/projects/studs/), il motorise framadate.org pour framasoft.org
Framadate is a fork of the [STUdS](https://sourcesup.cru.fr/projects/studs/) project, that works at framadate.org for framasoft.org
Les auteurs principaux de Framadate sont :
Previous main authors of Framadate were :
* Simon LEBLANC
* Pierre-Yves GOSSET
Les auteurs principaux du projet STUdS sont :
Main authors of project STUdS are :
* Guilhem BORGHESI
* Raphaël DROZ

97
action/add_comment.php Normal file
View File

@ -0,0 +1,97 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Message;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
use Framadate\Services\PollService;
use Framadate\Services\SecurityService;
include_once __DIR__ . '/../app/inc/init.php';
/* Variables */
/* --------- */
$poll_id = null;
$poll = null;
$message = null;
$result = false;
$comments = [];
$is_admin = false;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService);
$securityService = new SecurityService();
/* PAGE */
/* ---- */
if (!empty($_POST['poll'])) {
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$poll = $pollService->findById($poll_id);
}
if (!empty($_POST['poll_admin'])) {
$admin_poll_id = filter_input(INPUT_POST, 'poll_admin', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if (strlen($admin_poll_id) === 24) {
$is_admin = ($pollService->findByAdminId($admin_poll_id) !== null);
}
}
if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
} else if (!$is_admin && !$securityService->canAccessPoll($poll)) {
$message = new Message('error', __('Password', 'Wrong password'));
} else {
$name = $inputService->filterName($_POST['name']);
$comment = $inputService->filterComment($_POST['comment']);
if ($name === null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if ($message === null) {
// Add comment
$result = $pollService->addComment($poll_id, $name, $comment);
if ($result) {
$message = new Message('success', __('Comments', 'Comment added'));
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name);
} else {
$message = new Message('danger', __('Error', 'Comment failed'));
}
}
$comments = $pollService->allCommentsByPollId($poll_id);
}
$smarty->error_reporting = E_ALL & ~E_NOTICE;
$smarty->assign('comments', $comments);
$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);

View File

@ -0,0 +1,94 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Message;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\PollService;
use Framadate\Services\SessionService;
use Framadate\Utils;
include_once __DIR__ . '/../app/inc/init.php';
$logService = new LogService();
$sessionService = new SessionService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$pollService = new PollService($logService);
$result = false;
$message = null;
$poll = null;
$poll_id = null;
$email = null;
if (!empty($_POST['poll'])) {
$poll_id = filter_input(INPUT_POST, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$poll = $pollService->findById($poll_id);
}
$token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN);
$token_form_value = empty($_POST['token']) ? null : $_POST['token'];
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if ($config['use_smtp'] === false || is_null($poll) || 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...'));
}
if (is_null($message)) {
$email = $mailService->isValidEmail($_POST['email']);
if (is_null($email)) {
$message = new Message('error', __('EditLink', 'The email address is not correct.'));
}
}
if (is_null($message)) {
$time = $sessionService->get("Common", SESSION_EDIT_LINK_TIME);
if (!empty($time)) {
$remainingTime = TIME_EDIT_LINK_EMAIL - (time() - $time);
if ($remainingTime > 0) {
$message = new Message('error', __f('EditLink', 'Please wait %d seconds before we can send an email to you then try again.', $remainingTime));
}
}
}
if (is_null($message)) {
$url = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
$smarty->assign('poll', $poll);
$smarty->assign('poll_id', $poll_id);
$smarty->assign('editedVoteUniqueId', $editedVoteUniqueId);
$body = $smarty->fetch('mail/remember_edit_link.tpl');
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
$mailService->send($email, $subject, $body);
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());
$message = new Message('success', __('EditLink', 'Your reminder has been successfully sent!'));
$result = true;
}
$smarty->error_reporting = E_ALL & ~E_NOTICE;
$response = ['result' => $result, 'message' => $message];
echo json_encode($response, JSON_THROW_ON_ERROR);

238
admin/check.php Normal file
View File

@ -0,0 +1,238 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Message;
use Framadate\Utils;
const ROOT_DIR = __DIR__ . '/../';
/**
* Checking for missing vendors.
*/
if (!file_exists(ROOT_DIR . 'vendor/autoload.php') || !file_exists(ROOT_DIR . 'vendor/o80/i18n/src/shortcuts.php')) {
die ("ERROR: You should use <code>composer install</code> to fetch dependant libraries.");
}
/**
* Stripped ini sequence
*/
require_once ROOT_DIR . 'vendor/autoload.php';
require_once ROOT_DIR . 'vendor/o80/i18n/src/shortcuts.php';
require_once ROOT_DIR . 'app/inc/constants.php';
if (session_id() === '') {
session_start();
}
$ALLOWED_LANGUAGES = [
'fr' => 'Français',
'en' => 'English',
'oc' => 'Occitan',
'es' => 'Español',
'de' => 'Deutsch',
'it' => 'Italiano',
'br' => 'Brezhoneg',
'ca' => 'Català',
];
const DEFAULT_LANGUAGE = 'en';
require_once ROOT_DIR . 'app/inc/i18n.php';
/**
* Function to sort messages by type (priorise errors on warning, warning on info, etc.)
*
* @param Message $a
* @param Message $b
* @return int
*/
function compareCheckMessage(Message $a, Message $b): int
{
$values = [
'danger' => 0,
'warning' => 1,
'info' => 2,
'success' => 3
];
$vA = $values[$a->type];
$vB = $values[$b->type];
if ($vA === $vB) {
return 0;
}
return ($vA < $vB) ? -1 : 1;
}
/**
* Vars
*/
$messages = [];
$inc_directory = ROOT_DIR . 'app/inc/';
$conf_filename = $inc_directory . 'config.php';
/**
* Messages
*/
// PHP Version
if (version_compare(PHP_VERSION, PHP_NEEDED_VERSION) >= 0) {
$messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).', PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_NEEDED_VERSION));
} else {
$messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.', PHP_VERSION, PHP_NEEDED_VERSION));
}
// INTL extension
if (extension_loaded('intl')) {
$messages[] = new Message('info', __('Check','PHP Intl extension is enabled.'));
} else {
$messages[] = new Message('danger', __('Check','You need to enable the PHP Intl extension.'));
}
// 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)));
} elseif (is_writable(ROOT_DIR . COMPILE_DIR)) {
$messages[] = new Message('info', __f('Check','The template compile directory (%s) is writable.', realpath(ROOT_DIR . COMPILE_DIR)));
} else {
$messages[] = new Message('danger', __f('Check','The template compile directory (%s) is not writable.', realpath(ROOT_DIR . COMPILE_DIR)));
}
// Does config.php exists or is writable ?
if (file_exists($conf_filename)) {
$messages[] = new Message('info', __('Check','The config file exists.'));
} elseif (is_writable($inc_directory)) {
$messages[] = new Message('info', __f('Check','The config file directory (%s) is writable.', $inc_directory));
} else {
$messages[] = new Message('danger', __f('Check','The config file directory (%s) is not writable and the config file (%s) does not exists.', $inc_directory, $conf_filename));
}
// Security
if (extension_loaded('openssl')) {
$messages[] = new Message('info', __('Check','OpenSSL extension loaded.'));
} else {
$messages[] = new Message('warning', __('Check','Consider enabling the PHP extension OpenSSL for increased security.'));
}
if (ini_get('session.cookie_httponly') === '1') {
$messages[] = new Message('info', __('Check', 'Cookies are served from HTTP only.'));
} else {
$messages[] = new Message('warning', __('Check', "Consider setting « session.cookie_httponly = 1 » inside your php.ini or add « php_value session.cookie_httponly 1 » to your .htaccess so that cookies can't be accessed through Javascript."));
}
// Datetime
$timezone = ini_get('date.timezone');
if (!empty($timezone)) {
$messages[] = new Message('info', __('Check','date.timezone is set.'));
} else {
$messages[] = new Message('warning', __('Check','Consider setting the date.timezone in php.ini.'));
}
// The percentage of steps needed to be ready to launch the application
$errors = 0;
$warnings = 0;
foreach ($messages as $message) {
if ($message->type === 'danger') {
$errors++;
} else if ($message->type === 'warning') {
$warnings++;
}
}
$readyPercentage = round((count($messages)-$errors)*100/count($messages));
if ($errors > 0) {
$readyClass = 'danger';
} else if ($warnings > 0) {
$readyClass = 'warning';
} else {
$readyClass = 'success';
}
usort($messages, 'compareCheckMessage');
?>
<!DOCTYPE html>
<html lang="<?=$locale?>">
<head>
<meta charset="utf-8">
<title><?=__('Check', 'Installation checking') ?></title>
<link rel="stylesheet" href="../css/bootstrap.min.css">
<link rel="stylesheet" href="../css/style.css">
<link rel="stylesheet" href="../css/frama.css">
</head>
<body>
<div class="container ombre">
<div class="row">
<form method="get" 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>
<?php } ?>
</select>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="<?=__('Language selector', 'Select the language')?>">OK</button>
</span>
</div>
</form>
</div>
<div class="row">
<div class="col-md-12">
<h1><?=__('Check', 'Installation checking') ?></h1>
<div>
<div class="progress">
<div class="progress-bar progress-bar-<?= $readyClass ?>" role="progressbar" aria-valuenow="<?= $readyPercentage ?>" aria-valuemin="0" aria-valuemax="100" style="width: <?= $readyPercentage ?>%;">
<?= $readyPercentage ?>%
</div>
</div>
</div>
<div>
<?php
foreach ($messages as $message) {
echo '<div class="alert alert-' . $message->type . '" role="alert">';
echo Utils::htmlEscape($message->message);
echo '<span class="sr-only">' . $message->type . '</span>';
echo '</div>';
}
?>
</div>
</div>
<div class="text-center">
<a class="btn btn-info" role="button" href=""><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?= __('Check', 'Check again') ?></a>
<?php
if (!is_file($conf_filename)) {
if ($errors === 0) {
?>
<a class="btn btn-primary" role="button" href="<?= Utils::get_server_name() . 'admin/install.php' ?>"><span class=" glyphicon glyphicon-arrow-right" aria-hidden="true"></span> <?= __('Check', 'Continue the installation') ?></a>
<?php
}
} else {
?>
<a class="btn btn-primary" role="button" href="<?= Utils::get_server_name() . 'admin/'?>"><span class=" glyphicon glyphicon-arrow-left" aria-hidden="true"></span> <?= __('Admin', 'Back to administration') ?></a>
<?php
}
?>
</div>
</div>
</div>
</body>

91
admin/cron_purge.php Normal file
View File

@ -0,0 +1,91 @@
<?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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -29,6 +29,7 @@ if (is_file(CONF_FILENAME)) {
$error = null;
$installService = new InstallService();
$result['details'] = null;
if (!empty($_POST)) {
$installService->updateFields($_POST);
@ -37,12 +38,13 @@ if (!empty($_POST)) {
if ($result['status'] === 'OK') {
header(('Location: ' . Utils::get_server_name() . 'admin/migration.php'));
exit;
} else {
$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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -17,12 +17,19 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Migration\From_0_0_to_0_8_Migration;
use Framadate\Migration\From_0_8_to_0_9_Migration;
use Framadate\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_hidden_In_poll_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 Framadate\Utils;
@ -38,17 +45,25 @@ $migrations = [
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 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)) {
if (!in_array($prefixedMigrationTable, $tables, true)) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . $prefixedMigrationTable . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -96,7 +111,6 @@ foreach ($migrations as $migration) {
} else {
$countSkipped++;
}
}
$countTotal = $countSucceeded + $countFailed + $countSkipped;

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -50,7 +50,7 @@ $poll_to_delete = null;
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$pollService = new PollService($logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$superAdminService = new SuperAdminService();
$securityService = new SecurityService();
@ -64,6 +64,7 @@ $page = ($page >= 1) ? $page : 1;
$search['poll'] = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$search['title'] = filter_input(INPUT_GET, 'title', FILTER_SANITIZE_STRING);
$search['name'] = filter_input(INPUT_GET, 'name', FILTER_SANITIZE_STRING);
$search['mail'] = filter_input(INPUT_GET, 'mail', FILTER_SANITIZE_STRING);
/* PAGE */
/* ---- */

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -34,14 +34,14 @@ $message = null;
/*----------*/
$logService = new LogService();
$purgeService = new PurgeService($connect, $logService);
$purgeService = new PurgeService($logService);
$securityService = new SecurityService();
$inputService = new InputService();
/* POST */
/*-----*/
$action = $inputService->filterName(isset($_POST['action']) ? $_POST['action'] : null);
$action = $inputService->filterName($_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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -17,21 +17,23 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Editable;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\ConcurrentVoteException;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\Message;
use Framadate\Security\PasswordHasher;
use Framadate\Services\AdminPollService;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\NotificationService;
use Framadate\Services\PollService;
use Framadate\Services\SessionService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
/* Constants */
/* --------- */
const UPDATE_POLL = 1;
const DELETED_POLL = 2;
/* Variables */
/* --------- */
@ -45,44 +47,12 @@ $editingVoteId = 0;
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$pollService = new PollService($logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp']);
/* Functions */
/*-----------*/
/**
* Send a notification to the poll admin to notify him about an update.
*
* @param stdClass $poll The poll
* @param MailService $mailService The mail service
* @param int $type cf: Constants on the top of this page
*/
function sendUpdateNotification($poll, $mailService, $type) {
if (!isset($_SESSION['mail_sent'])) {
$_SESSION['mail_sent'] = [];
}
if ($poll->receiveNewVotes) {
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', 'Notification of poll: %s', $poll->title);
$message = '';
switch ($type) {
case UPDATE_POLL:
$message = __f('Mail', 'Someone just change your poll available at the following link %s.', Utils::getUrlSondage($poll->admin_id, true)) . "\n\n";
break;
case DELETED_POLL:
$message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
break;
}
$messageTypeKey = $type . '-' . $poll->id;
$mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
}
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService);
$sessionService = new SessionService();
/* PAGE */
/* ---- */
@ -90,46 +60,60 @@ function sendUpdateNotification($poll, $mailService, $type) {
if (!empty($_GET['poll'])) {
$admin_poll_id = filter_input(INPUT_GET, 'poll', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if (strlen($admin_poll_id) === 24) {
$poll_id = substr($admin_poll_id, 0, 16);
$poll = $pollService->findById($poll_id);
$poll = $pollService->findByAdminId($admin_poll_id);
}
}
if (!$poll) {
if ($poll) {
$poll_id = $poll->id;
} else {
$smarty->assign('error', __('Error', 'This poll doesn\'t exist !'));
$smarty->display('error.tpl');
exit;
}
// -------------------------------
// creation message
// -------------------------------
$messagePollCreated = $sessionService->get("Framadate", "messagePollCreated", FALSE);
if ($messagePollCreated) {
$sessionService->remove("Framadate", "messagePollCreated");
$message = new Message('success', __('adminstuds', 'The poll is created.'));
}
// -------------------------------
// Update poll info
// -------------------------------
if (isset($_POST['update_poll_info'])) {
$updated = false;
$field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description', 'rules', 'expiration_date', 'name', 'hidden']);
$field = $inputService->filterAllowedValues($_POST['update_poll_info'], ['title', 'admin_mail', 'description',
'rules', 'expiration_date', 'name', 'hidden', 'removePassword', 'password']);
// Update the right poll field
if ($field == 'title') {
if ($field === 'title') {
$title = $inputService->filterTitle($_POST['title']);
if ($title) {
$poll->title = $title;
$updated = true;
}
} elseif ($field == 'admin_mail') {
} elseif ($field === 'admin_mail') {
$admin_mail = $inputService->filterMail($_POST['admin_mail']);
if ($admin_mail) {
$poll->admin_mail = $admin_mail;
$updated = true;
}
} elseif ($field == 'description') {
} elseif ($field === 'description') {
$description = $inputService->filterDescription($_POST['description']);
if ($description) {
$poll->description = $description;
$updated = true;
}
} elseif ($field == 'rules') {
$rules = strip_tags($_POST['rules']);
} elseif ($field === 'rules') {
$rules = (int) strip_tags($_POST['rules']);
switch ($rules) {
case 0:
$poll->active = false;
@ -152,23 +136,62 @@ if (isset($_POST['update_poll_info'])) {
$updated = true;
break;
}
} elseif ($field == 'expiration_date') {
$expiration_date = filter_input(INPUT_POST, 'expiration_date', FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '#^[0-9]{4}-[0-9]{2}-[0-9]{2}$#']]);
if ($expiration_date) {
$poll->end_date = $expiration_date;
} 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');
$updated = true;
}
} elseif ($field == 'name') {
$admin_name = $inputService->filterName($_POST['name']);
} elseif ($field === 'name') {
$admin_name = $_POST['name'];
$admin_name = mb_substr($admin_name, 0, 32);
$admin_name = $inputService->filterName($admin_name);
if ($admin_name) {
$poll->admin_name = $admin_name;
$updated = true;
}
} elseif ($field == 'hidden') {
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
if ($hidden != $poll->hidden) {
} elseif ($field === 'hidden') {
$hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
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']);
if ($removePassword) {
$poll->results_publicly_visible = false;
$poll->password_hash = null;
$updated = true;
}
} elseif ($field === 'password') {
$password = $_POST['password'] ?? null;
/**
* Did the user choose results to be publicly visible ?
*/
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) && $inputService->filterBoolean($_POST['resultsPubliclyVisible']);
/**
* If there's one, save the password
*/
if (!empty($password)) {
$poll->password_hash = PasswordHasher::hash($password);
$updated = true;
}
/**
* If not pasword was set and the poll should be hidden, hide the results
*/
if ($poll->password_hash === null || $poll->hidden === true) {
$poll->results_publicly_visible = false;
}
/**
* We don't have a password, the poll is hidden and we change the results public visibility
*/
if ($resultsPubliclyVisible !== $poll->results_publicly_visible && $poll->password_hash !== null && $poll->hidden === false) {
$poll->results_publicly_visible = $resultsPubliclyVisible;
$updated = true;
}
}
@ -176,7 +199,7 @@ if (isset($_POST['update_poll_info'])) {
// Update poll in database
if ($updated && $adminPollService->updatePoll($poll)) {
$message = new Message('success', __('adminstuds', 'Poll saved'));
sendUpdateNotification($poll, $mailService, UPDATE_POLL);
$notificationService->sendUpdateNotification($poll, NotificationService::UPDATE_POLL);
} else {
$message = new Message('danger', __('Error', 'Failed to save poll'));
$poll = $pollService->findById($poll_id);
@ -195,45 +218,66 @@ if (!empty($_GET['vote'])) {
// Something to save (edit or add)
// -------------------------------
$selectedNewVotes = [];
if (!empty($_POST['save'])) { // Save edition of an old vote
$name = $inputService->filterName($_POST['name']);
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
if (empty($editedVote)) {
$message = new Message('danger', __('Error', 'Something is going wrong...'));
}
if (count($choices) != count($_POST['choices'])) {
if (count($choices) !== count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
if ($message === null) {
// Update vote
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote updated'));
} else {
$message = new Message('danger', __('Error', 'Update vote failed'));
try {
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote updated'));
} else {
$message = new Message('danger', __('Error', 'Update vote failed'));
}
} catch (AlreadyExistsException $aee) {
$message = new Message('danger', __('Error', 'The name you\'ve chosen already exist in this poll!'));
} catch (ConcurrentEditionException $cee) {
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
} catch (ConcurrentVoteException $cve) {
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
}
}
} elseif (isset($_POST['save'])) { // Add a new vote
$name = $inputService->filterName($_POST['name']);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
if ($name == null) {
if ($name === null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if (count($choices) != count($_POST['choices'])) {
if (count($choices) !== count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
}
if ($message == null) {
if ($message === null) {
// Add vote
$result = $pollService->addVote($poll_id, $name, $choices);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote added'));
} else {
$message = new Message('danger', __('Error', 'Adding vote failed'));
try {
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote added'));
} else {
$message = new Message('danger', __('Error', 'Adding vote failed'));
}
} catch (AlreadyExistsException $aee) {
$message = new Message('danger', __('Error', 'You already voted'));
$selectedNewVotes = $choices;
} catch (ConcurrentEditionException $cee) {
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
} catch (ConcurrentVoteException $cve) {
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
}
}
}
@ -243,11 +287,12 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
// -------------------------------
if (!empty($_GET['delete_vote'])) {
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_INT);
if ($adminPollService->deleteVote($poll_id, $vote_id)) {
$vote_id = filter_input(INPUT_GET, 'delete_vote', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BASE64_REGEX]]);
$vote_id = Utils::base64url_decode($vote_id);
if ($vote_id && $adminPollService->deleteVote($poll_id, $vote_id)) {
$message = new Message('success', __('adminstuds', 'Vote deleted'));
} else {
$message = new Message('danger', __('Error', 'Failed to delete the vote'));
$message = new Message('danger', __('Error', 'Failed to delete the vote!'));
}
}
@ -270,30 +315,6 @@ if (isset($_POST['confirm_remove_all_votes'])) {
}
}
// -------------------------------
// Add a comment
// -------------------------------
if (isset($_POST['add_comment'])) {
$name = $inputService->filterName($_POST['name']);
$comment = $inputService->filterComment($_POST['comment']);
if ($name == null) {
$message = new Message('danger', __('Error', 'The name is invalid.'));
}
if ($message == null) {
// Add comment
$result = $pollService->addComment($poll_id, $name, $comment);
if ($result) {
$message = new Message('success', __('Comments', 'Comment added'));
} else {
$message = new Message('danger', __('Error', 'Comment failed'));
}
}
}
// -------------------------------
// Delete a comment
// -------------------------------
@ -341,7 +362,7 @@ if (isset($_POST['delete_poll'])) {
if (isset($_POST['confirm_delete_poll'])) {
if ($adminPollService->deleteEntirePoll($poll_id)) {
$message = new Message('success', __('adminstuds', 'Poll fully deleted'));
sendUpdateNotification($poll, $mailService, DELETED_POLL);
$notificationService->sendUpdateNotification($poll, NotificationService::DELETED_POLL);
} else {
$message = new Message('danger', __('Error', 'Failed to delete the poll'));
}
@ -357,8 +378,9 @@ if (isset($_POST['confirm_delete_poll'])) {
// Delete a slot
// -------------------------------
if (!empty($_GET['delete_column'])) {
if (isset($_GET['delete_column'])) {
$column = filter_input(INPUT_GET, 'delete_column', FILTER_DEFAULT);
$column = Utils::base64url_decode($column);
if ($poll->format === 'D') {
$ex = explode('@', $column);
@ -383,30 +405,40 @@ if (!empty($_GET['delete_column'])) {
// Add a slot
// -------------------------------
if (isset($_GET['add_slot'])) {
function exit_displaying_add_column($message = null) {
global $smarty, $poll_id, $admin_poll_id, $poll;
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$smarty->assign('format', $poll->format);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->display('add_slot.tpl');
$smarty->assign('message', $message);
$smarty->display('add_column.tpl');
exit;
}
if (isset($_POST['confirm_add_slot'])) {
if ($poll->format === 'D') {
$newdate = strip_tags($_POST['newdate']);
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
$ex = explode('/', $newdate);
$result = $adminPollService->addDateSlot($poll_id, mktime(0, 0, 0, $ex[1], $ex[0], $ex[2]), $newmoment);
} else {
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
$result = $adminPollService->addClassicSlot($poll_id, $newslot);
}
if (isset($_GET['add_column'])) {
exit_displaying_add_column();
}
if (isset($_POST['confirm_add_column'])) {
try {
if (($poll->format === 'D' && empty($_POST['newdate']))
|| ($poll->format === 'A' && empty($_POST['choice']))) {
exit_displaying_add_column(new Message('danger', __('Error', "Can't create an empty column.")));
}
if ($poll->format === 'D') {
$date = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $_POST['newdate'])->setTime(0, 0, 0);
$time = $date->getTimestamp();
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
$adminPollService->addDateSlot($poll_id, $time, $newmoment);
} else {
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
$adminPollService->addClassicSlot($poll_id, $newslot);
}
if ($result) {
$message = new Message('success', __('adminstuds', 'Choice added'));
} else {
$message = new Message('danger', __('Error', 'Failed to add the column'));
} catch (MomentAlreadyExistsException $e) {
exit_displaying_add_column(new Message('danger', __('Error', 'The column already exists')));
}
}
@ -415,7 +447,6 @@ $slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
// Assign data to template
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
@ -424,12 +455,18 @@ $smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->assign('expired', strtotime($poll->end_date) < time());
$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
$smarty->assign('votes', $pollService->splitVotes($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
$smarty->assign('best_choices', $pollService->computeBestChoices($votes, $poll));
$smarty->assign('comments', $comments);
$smarty->assign('editingVoteId', $editingVoteId);
$smarty->assign('message', $message);
$smarty->assign('admin', true);
$smarty->assign('hidden', false);
$smarty->assign('accessGranted', true);
$smarty->assign('resultPubliclyVisible', true);
$smarty->assign('editedVoteUniqueId', '');
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
$smarty->assign('selectedNewVotes', $selectedNewVotes);
$smarty->display('studs.tpl');

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -24,36 +24,35 @@ class Choice
* Name of the Choice
*/
private $name;
/**
* All availables slots for this Choice.
*/
private $slots;
public function __construct($name='')
{
$this->name = $name;
$this->slots = array();
$this->slots = [];
}
public function addSlot($slot)
public function addSlot($slot): void
{
$this->slots[] = $slot;
}
public function getName()
public function getName(): string
{
return $this->name;
}
public function getSlots()
public function getSlots(): array
{
return $this->slots;
}
static function compare(Choice $a, Choice $b)
public static function compare(Choice $a, Choice $b): int
{
return strcmp($a->name, $b->name);
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,19 +19,17 @@
namespace Framadate;
/**
* Class Editable
*
* Is used to specify the poll's edition permissions.
* @TODO : wait to use the SplEnum
*
* @package Framadate
*/
class Editable { // extends SplEnum
const __default = self::EDITABLE_BY_ALL;
const NOT_EDITABLE = 0;
const EDITABLE_BY_ALL = 1;
const EDITABLE_BY_OWN = 2;
}
public const NOT_EDITABLE = 0;
public const EDITABLE_BY_ALL = 1;
public const EDITABLE_BY_OWN = 2;
}

View File

@ -0,0 +1,5 @@
<?php
namespace Framadate\Exception;
class AlreadyExistsException extends \Exception {
}

View File

@ -0,0 +1,5 @@
<?php
namespace Framadate\Exception;
class ConcurrentEditionException extends \Exception {
}

View File

@ -0,0 +1,10 @@
<?php
namespace Framadate\Exception;
/**
* Class ConcurrentVoteException
*
* Thrown when a poll has a maximum votes constraint for options, and a vote happened since the poll was rendered
*/
class ConcurrentVoteException extends \Exception {
}

View File

@ -0,0 +1,5 @@
<?php
namespace Framadate\Exception;
class MomentAlreadyExistsException extends \Exception {
}

View File

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

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -20,18 +20,19 @@ namespace Framadate;
class Form
{
public $title;
public $id;
public $description;
public $admin_name;
public $admin_mail;
public $format;
public $end_date;
public $choix_sondage;
public $ValueMax;
/**
* Tells if users can modify their choices.
* @var \Framadate\Editable
* @var int
*/
public $editable;
@ -49,8 +50,38 @@ class Form
* If true, only the poll maker can see the poll's results
* @var boolean
*/
public $use_ValueMax;
/**
* if true, there will be a limit of voters per option
* @var boolean
*/
public $hidden;
/**
* If true, the author want to customize the URL
* @var boolean
*/
public $use_customized_url;
/**
* If true, a password will be needed to access the poll
* @var boolean
*/
public $use_password;
/**
* The password needed to access the poll, hashed. Only used if $use_password is set to true
* @var string
*/
public $password_hash;
/**
* If true, the polls results will be also visible for those without password
* @var boolean
*/
public $results_publicly_visible;
/**
* List of available choices
*/
@ -61,11 +92,12 @@ class Form
$this->clearChoices();
}
public function clearChoices() {
$this->choices = array();
public function clearChoices(): void
{
$this->choices = [];
}
public function addChoice(Choice $choice)
public function addChoice(Choice $choice): void
{
$this->choices[] = $choice;
}
@ -75,9 +107,8 @@ class Form
return $this->choices;
}
public function sortChoices()
public function sortChoices(): void
{
usort($this->choices, array('Framadate\Choice', 'compare'));
usort($this->choices, [Choice::class, 'compare']);
}
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -23,19 +23,21 @@ use PDO;
class FramaDB {
/**
* PDO Object, connection to database.
* @var PDO
*/
private $pdo = null;
private $pdo;
function __construct($connection_string, $user, $password) {
$this->pdo = new \PDO($connection_string, $user, $password);
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
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
* @return PDO Connection to database
*/
function getPDO() {
public function getPDO(): PDO
{
return $this->pdo;
}
@ -44,42 +46,50 @@ class FramaDB {
*
* @return array The array of table names
*/
function allTables() {
$result = $this->pdo->query('SHOW TABLES');
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
return $schemas;
public function allTables(): array
{
return $this->pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
}
function prepare($sql) {
/**
* @return \PDOStatement|false
*/
public function prepare(string $sql) {
return $this->pdo->prepare($sql);
}
function beginTransaction() {
public function beginTransaction(): void
{
$this->pdo->beginTransaction();
}
function commit() {
public function commit(): void
{
$this->pdo->commit();
}
function rollback() {
public function rollback(): void
{
$this->pdo->rollback();
}
function errorCode() {
public function errorCode(): ?string {
return $this->pdo->errorCode();
}
function errorInfo() {
public function errorInfo(): array
{
return $this->pdo->errorInfo();
}
function query($sql) {
/**
* @return \PDOStatement|false
*/
public function query($sql) {
return $this->pdo->query($sql);
}
public function lastInsertId() {
public function lastInsertId(): string {
return $this->pdo->lastInsertId();
}
}

View File

@ -1,34 +1,37 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
class Message {
var $type;
var $message;
var $link;
function __construct($type, $message, $link=null) {
$this->type = $type;
$this->message = $message;
$this->link = $link;
}
}
<?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;
$this->includeTemplate = $includeTemplate;
}
}

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 Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field Value_Max on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_ValueMax_In_poll_For_1_1 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 "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.
*/
public function preCondition(PDO $pdo): bool {
return true;
}
/**
* 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 `ValueMax` TINYINT NULL;');
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,6 +19,7 @@
namespace Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field hidden on the poll table.
@ -27,8 +28,7 @@ use Framadate\Utils;
* @version 0.9
*/
class AddColumn_hidden_In_poll_For_0_9 implements Migration {
function __construct() {
public function __construct() {
}
/**
@ -36,7 +36,8 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
*
* @return string The description of the migration class
*/
function description() {
public function description(): string
{
return 'Add column "hidden" in table "vote" for version 0.9';
}
@ -44,12 +45,13 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
* 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
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$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);
@ -57,21 +59,22 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
}
/**
* This methode is called only one time in the migration page.
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"');
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,6 +19,7 @@
namespace Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field receiveNewComments on the poll table.
@ -27,8 +28,7 @@ use Framadate\Utils;
* @version 0.9
*/
class AddColumn_receiveNewComments_For_0_9 implements Migration {
function __construct() {
public function __construct() {
}
/**
@ -36,7 +36,8 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
*
* @return string The description of the migration class
*/
function description() {
public function description(): string
{
return 'Add column "receiveNewComments" for version 0.9';
}
@ -44,12 +45,13 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
* 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
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
public function preCondition(PDO $pdo): bool
{
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$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);
@ -57,22 +59,23 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
}
/**
* This methode is called only one time in the migration page.
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool
{
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
private function alterPollTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'
AFTER `receiveNewVotes`');
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,6 +19,7 @@
namespace Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration adds the field uniqId on the vote table.
@ -27,8 +28,7 @@ use Framadate\Utils;
* @version 0.9
*/
class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
function __construct() {
public function __construct() {
}
/**
@ -36,7 +36,7 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
*
* @return string The description of the migration class
*/
function description() {
public function description(): string {
return 'Add column "uniqId" in table "vote" for version 0.9';
}
@ -44,12 +44,12 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
* 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
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$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);
@ -57,23 +57,23 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
}
/**
* This methode is called only one time in the migration page.
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
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

@ -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 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

@ -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 Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration alter the comment table to add a date column.
*
* @package Framadate\Migration
* @version 1.0
*/
class Alter_Comment_table_adding_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 '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.
*/
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 is the execution succeeded
*/
public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo);
return true;
}
private function alterCommentTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;');
}
}

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 Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* This migration alter the comment table to set a length to the name column.
*
* @package Framadate\Migration
* @version 1.0
*/
class Alter_Comment_table_for_name_length 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 '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.
*/
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 is the execution succeeded
*/
public function execute(PDO $pdo): bool {
$this->alterCommentTable($pdo);
return true;
}
private function alterCommentTable(PDO $pdo): void
{
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;');
}
}

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 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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,6 +19,7 @@
namespace Framadate\Migration;
use Framadate\Utils;
use PDO;
/**
* Class From_0_0_to_0_8_Migration
@ -27,8 +28,7 @@ use Framadate\Utils;
* @version 0.8
*/
class From_0_0_to_0_8_Migration implements Migration {
function __construct() {
public function __construct() {
}
/**
@ -36,7 +36,7 @@ class From_0_0_to_0_8_Migration implements Migration {
*
* @return string The description of the migration class
*/
function description() {
public function description(): string {
return 'First installation of the Framadate application (v0.8)';
}
@ -44,12 +44,12 @@ class From_0_0_to_0_8_Migration implements Migration {
* 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
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
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)]);
@ -57,12 +57,12 @@ class From_0_0_to_0_8_Migration implements Migration {
}
/**
* This methode is called only one time in the migration page.
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `sondage` (
`id_sondage` char(16) NOT NULL,
@ -72,7 +72,7 @@ CREATE TABLE IF NOT EXISTS `sondage` (
`titre` text,
`id_sondage_admin` char(24) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_fin` timestamp NOT NULL DEFAULT \'0000-00-00 00:00:00\',
`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 ; \',
@ -105,5 +105,6 @@ CREATE TABLE IF NOT EXISTS `user_studs` (
PRIMARY KEY (`id_users`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
return true;
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -19,6 +19,7 @@
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.
@ -27,8 +28,7 @@ use Framadate\Utils;
* @version 0.9
*/
class From_0_8_to_0_9_Migration implements Migration {
function __construct() {
public function __construct() {
}
/**
@ -36,7 +36,7 @@ class From_0_8_to_0_9_Migration implements Migration {
*
* @return string The description of the migration class
*/
function description() {
public function description(): string {
return 'From 0.8 to 0.9';
}
@ -44,12 +44,12 @@ class From_0_8_to_0_9_Migration implements Migration {
* 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
* @param PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Check if tables of v0.8 are presents
$diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables);
@ -57,12 +57,12 @@ class From_0_8_to_0_9_Migration implements Migration {
}
/**
* This methode is called only one time in the migration page.
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool {
$this->createPollTable($pdo);
$this->createCommentTable($pdo);
$this->createSlotTable($pdo);
@ -80,7 +80,8 @@ class From_0_8_to_0_9_Migration implements Migration {
return true;
}
private function createPollTable(\PDO $pdo) {
private function createPollTable(PDO $pdo): void
{
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
`id` CHAR(16) NOT NULL,
@ -90,7 +91,7 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
`admin_name` VARCHAR(64) DEFAULT NULL,
`admin_mail` VARCHAR(128) DEFAULT NULL,
`creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`end_date` TIMESTAMP NOT NULL DEFAULT \'0000-00-00 00:00:00\',
`end_date` TIMESTAMP NULL DEFAULT NULL,
`format` VARCHAR(1) DEFAULT NULL,
`editable` TINYINT(1) DEFAULT \'0\',
`receiveNewVotes` TINYINT(1) DEFAULT \'0\',
@ -101,7 +102,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
DEFAULT CHARSET = utf8');
}
private function migrateFromSondageToPoll(\PDO $pdo) {
private function migrateFromSondageToPoll(PDO $pdo): void
{
$select = $pdo->query('
SELECT
`id_sondage`,
@ -127,7 +129,7 @@ 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)) {
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$row->id_sondage_admin,
@ -145,7 +147,8 @@ VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
}
}
private function createSlotTable(\PDO $pdo) {
private function createSlotTable(PDO $pdo): void
{
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -159,7 +162,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
DEFAULT CHARSET = utf8');
}
private function migrateFromSujetStudsToSlot(\PDO $pdo) {
private function migrateFromSujetStudsToSlot(PDO $pdo): void
{
$stmt = $pdo->query('SELECT * FROM sujet_studs');
$sujets = $stmt->fetchAll();
$slots = [];
@ -181,7 +185,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
}
}
private function createCommentTable(\PDO $pdo) {
private function createCommentTable(PDO $pdo): void
{
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -195,7 +200,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
DEFAULT CHARSET = utf8');
}
private function migrateFromCommentsToComment(\PDO $pdo) {
private function migrateFromCommentsToComment(PDO $pdo): void
{
$select = $pdo->query('
SELECT
`id_sondage`,
@ -207,7 +213,7 @@ SELECT
INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->usercomment),
@ -216,7 +222,8 @@ VALUE (?,?,?)');
}
}
private function createVoteTable(\PDO $pdo) {
private function createVoteTable(PDO $pdo): void
{
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
@ -230,7 +237,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
DEFAULT CHARSET = utf8');
}
private function migrateFromUserStudsToVote(\PDO $pdo) {
private function migrateFromUserStudsToVote(PDO $pdo): void
{
$select = $pdo->query('
SELECT
`id_sondage`,
@ -242,7 +250,7 @@ SELECT
INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->nom),
@ -251,7 +259,8 @@ VALUE (?,?,?)');
}
}
private function transformSujetToSlot($sujet) {
private function transformSujetToSlot($sujet): array
{
$slots = [];
$ex = explode(',', $sujet->sujet);
$isDatePoll = strpos($sujet->sujet, '@');
@ -265,14 +274,14 @@ VALUE (?,?,?)');
$slots[] = $slot;
} else { // Date poll
$values = explode('@', $atomicSlot);
if ($lastSlot == null || $lastSlot->title !== $values[0]) {
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] : '-';
$lastSlot->moments = count($values) === 2 ? $values[1] : '-';
$slots[] = $lastSlot;
} else {
$lastSlot->moments .= ',' . (count($values) == 2 ? $values[1] : '-');
$lastSlot->moments .= ',' . (count($values) === 2 ? $values[1] : '-');
}
}
}
@ -280,14 +289,16 @@ VALUE (?,?,?)');
return $slots;
}
private function dropOldTables(\PDO $pdo) {
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($value) {
private function unescape(string $value): string
{
return stripslashes(html_entity_decode($value, ENT_QUOTES));
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -20,6 +20,7 @@ namespace Framadate\Migration;
use Framadate\Security\Token;
use Framadate\Utils;
use PDO;
/**
* This migration generate uniqId for all legacy votes.
@ -28,17 +29,16 @@ use Framadate\Utils;
* @version 0.9
*/
class Generate_uniqId_for_old_votes implements Migration {
function __construct() {
public function __construct() {
}
function description() {
public function description(): string {
return 'Generate "uniqId" in "vote" table for all legacy votes';
}
function preCondition(\PDO $pdo) {
public function preCondition(PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
$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);
@ -48,11 +48,10 @@ class Generate_uniqId_for_old_votes implements Migration {
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
public function execute(PDO $pdo): bool {
$pdo->beginTransaction();
$this->generateUniqIdsForEmptyOnes($pdo);
$pdo->commit();
@ -60,7 +59,8 @@ class Generate_uniqId_for_old_votes implements Migration {
return true;
}
private function generateUniqIdsForEmptyOnes($pdo) {
private function generateUniqIdsForEmptyOnes(PDO $pdo): void
{
$select = $pdo->query('
SELECT `id`
FROM `' . Utils::table('vote') . '`
@ -71,7 +71,7 @@ UPDATE `' . Utils::table('vote') . '`
SET `uniqid` = :uniqid
WHERE `id` = :id');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
$token = Token::getToken(16);
$update->execute([
'uniqid' => $token,
@ -79,5 +79,4 @@ UPDATE `' . Utils::table('vote') . '`
]);
}
}
}

View File

@ -0,0 +1,72 @@
<?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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,31 +18,30 @@
*/
namespace Framadate\Migration;
interface Migration {
use PDO;
interface Migration {
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description();
public function description(): string;
/**
* 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
* @param PDO $pdo The connection to database
* @return bool true if the Migration should be executed
*/
function preCondition(\PDO $pdo);
public function preCondition(PDO $pdo): bool;
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @param PDO $pdo The connection to database
* @return bool true if the execution succeeded
*/
function execute(\PDO $pdo);
public function execute(PDO $pdo): bool;
}

View File

@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* 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
* 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
@ -28,12 +28,11 @@ use Framadate\Utils;
* @version 0.9
*/
class RPadVotes_from_0_8 implements Migration {
function description() {
public function description(): string {
return 'RPad votes from version 0.8.';
}
function preCondition(\PDO $pdo) {
public function preCondition(\PDO $pdo): bool {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
@ -42,8 +41,7 @@ class RPadVotes_from_0_8 implements Migration {
return count($diff) === 0;
}
function execute(\PDO $pdo) {
public function execute(\PDO $pdo): bool {
$pdo->beginTransaction();
$this->rpadVotes($pdo);
$pdo->commit();
@ -51,18 +49,19 @@ class RPadVotes_from_0_8 implements Migration {
return true;
}
private function rpadVotes($pdo) {
$pdo->exec('UPDATE fd_vote fv
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 fd_vote v
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 fd_slot s
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

@ -4,7 +4,6 @@ namespace Framadate\Repositories;
use Framadate\FramaDB;
abstract class AbstractRepository {
/**
* @var FramaDB
*/
@ -14,32 +13,40 @@ abstract class AbstractRepository {
* PollRepository constructor.
* @param FramaDB $connect
*/
function __construct(FramaDB $connect) {
public function __construct(FramaDB $connect) {
$this->connect = $connect;
}
public function beginTransaction() {
public function beginTransaction(): void
{
$this->connect->beginTransaction();
}
public function commit() {
public function commit(): void
{
$this->connect->commit();
}
function rollback() {
public function rollback(): void
{
$this->connect->rollback();
}
public function prepare($sql) {
/**
* @return \PDOStatement|false
*/
public function prepare(string $sql) {
return $this->connect->prepare($sql);
}
function query($sql) {
/**
* @return \PDOStatement|false
*/
public function query($sql) {
return $this->connect->query($sql);
}
function lastInsertId() {
public function lastInsertId(): string {
return $this->connect->lastInsertId();
}
}

View File

@ -1,18 +1,15 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Framadate\Utils;
class CommentRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
function findAllByPollId($poll_id) {
/**
* @return array|false
*/
public function findAllByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
}
@ -20,18 +17,20 @@ class CommentRepository extends AbstractRepository {
/**
* Insert a new comment.
*
* @param $poll_id
* @param $name
* @param $comment
* @param string $poll_id
* @param string $name
* @param string $comment
* @return bool
*/
function insert($poll_id, $name, $comment) {
public function insert(string $poll_id, string $name, string $comment): bool
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $name, $comment]);
}
function deleteById($poll_id, $comment_id) {
public function deleteById(string $poll_id, int $comment_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $comment_id]);
@ -40,20 +39,21 @@ class CommentRepository extends AbstractRepository {
/**
* Delete all comments of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @param string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded.
*/
function deleteByPollId($poll_id) {
public function deleteByPollId(string $poll_id): ?bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
}
public function exists($poll_id, $name, $comment) {
public function exists(string $poll_id, string $name, string $comment): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?');
$prepared->execute(array($poll_id, $name, $comment));
$prepared->execute([$poll_id, $name, $comment]);
return $prepared->rowCount() > 0;
}
}

View File

@ -6,44 +6,62 @@ use Framadate\Utils;
use PDO;
class PollRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
public function insertPoll($poll_id, $admin_poll_id, $form) {
public function insertPoll(string $poll_id, string $admin_poll_id, $form): void
{
$sql = 'INSERT INTO `' . Utils::table('poll') . '`
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments)
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?)';
(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(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, $form->editable, $form->receiveNewVotes, $form->receiveNewComments));
$prepared->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]);
}
function findById($poll_id) {
public function findById(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared->execute(array($poll_id));
$prepared->execute([$poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
return $poll;
}
public function existsById($poll_id) {
public function findByAdminId(string $admin_poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_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 = ?');
$prepared->execute(array($poll_id));
$prepared->execute([$poll_id]);
return $prepared->rowCount() > 0;
}
function update($poll) {
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=? WHERE id = ?');
public function existsByAdminId(string $admin_poll_id): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, $poll->editable, $poll->hidden, $poll->id]);
$prepared->execute([$admin_poll_id]);
return $prepared->rowCount() > 0;
}
function deleteById($poll_id) {
public function update($poll): bool
{
$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]);
}
public function deleteById($poll_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
return $prepared->execute([$poll_id]);
@ -54,7 +72,8 @@ class PollRepository extends AbstractRepository {
*
* @return array Array of old polls
*/
public function findOldPolls() {
public function findOldPolls(): array
{
$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([]);
@ -62,34 +81,57 @@ class PollRepository extends AbstractRepository {
}
/**
* Search polls in databse.
* Search polls in database.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @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
* @return array The found polls
*/
public function findAll($search, $start, $limit) {
public function findAll(array $search, int $start, int $limit): array
{
// Polls
$prepared = $this->prepare('
SELECT p.*,
(SELECT count(1) FROM `' . Utils::table('vote') . '` v WHERE p.id=v.poll_id) votes
FROM `' . Utils::table('poll') . '` p
WHERE (:id = "" OR p.id LIKE :id)
AND (:title = "" OR p.title LIKE :title)
AND (:name = "" OR p.admin_name LIKE :name)
ORDER BY p.title ASC
LIMIT :start, :limit
');
$poll = $search['poll'] . '%';
$title = '%' . $search['title'] . '%';
$name = '%' . $search['name'] . '%';
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
$request = "SELECT p.*,";
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
$request .= " FROM `" . Utils::table('poll') . "` p";
$request .= " WHERE 1";
$values = [];
if (!empty($search["poll"])) {
$request .= " AND p.id LIKE :poll";
$values["poll"] = "{$search["poll"]}%";
}
$fields = [
// key of $search => column name
"title" => "title",
"name" => "admin_name",
"mail" => "admin_mail",
];
foreach ($fields as $searchKey => $columnName) {
if (empty($search[$searchKey])) {
continue;
}
$request .= " AND p.$columnName LIKE :$searchKey";
$values[$searchKey] = "%$search[$searchKey]%";
}
$request .= " ORDER BY p.title ASC";
$request .= " LIMIT :start, :limit";
$prepared = $this->prepare($request);
foreach ($values as $searchKey => $value) {
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
}
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
$prepared->execute();
return $prepared->fetchAll();
@ -101,20 +143,22 @@ SELECT p.*,
* @param string $mail Email address of the poll admin
* @return array The list of matching polls
*/
public function findAllByAdminMail($mail) {
public function findAllByAdminMail(string $mail): array
{
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
$prepared->execute(array('admin_mail' => $mail));
$prepared->execute(['admin_mail' => $mail]);
return $prepared->fetchAll();
}
/**
* Get the total number of polls in databse.
* Get the total number of polls in database.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @param array|null $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @return int The number of polls
*/
public function count($search = null) {
public function count(array $search = null): int
{
// Total count
$prepared = $this->prepare('
SELECT count(1) nb
@ -124,22 +168,15 @@ SELECT count(1) nb
AND (:name = "" OR p.admin_name LIKE :name)
ORDER BY p.title ASC');
$poll = $search == null ? '' : $search['poll'] . '%';
$title = $search == null ? '' : '%' . $search['title'] . '%';
$name = $search == null ? '' : '%' . $search['name'] . '%';
$poll = $search === null ? '' : $search['poll'] . '%';
$title = $search === null ? '' : '%' . $search['title'] . '%';
$name = $search === null ? '' : '%' . $search['name'] . '%';
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
$prepared->execute();
$count = $prepared->fetch();
/*echo '---';
print_r($count);
echo '---';
exit;*/
return $count->nb;
return $prepared->fetch()->nb;
}
}

View File

@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* 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
* 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Repositories;
@ -21,7 +21,6 @@ namespace Framadate\Repositories;
use Framadate\FramaDB;
class RepositoryFactory {
private static $connect;
private static $pollRepository;
@ -32,15 +31,16 @@ class RepositoryFactory {
/**
* @param FramaDB $connect
*/
static function init(FramaDB $connect) {
public static function init(FramaDB $connect): void {
self::$connect = $connect;
}
/**
* @return PollRepository The singleton of PollRepository
*/
static function pollRepository() {
if (self::$pollRepository == null) {
public static function pollRepository(): PollRepository
{
if (self::$pollRepository === null) {
self::$pollRepository = new PollRepository(self::$connect);
}
@ -50,8 +50,9 @@ class RepositoryFactory {
/**
* @return SlotRepository The singleton of SlotRepository
*/
static function slotRepository() {
if (self::$slotRepository == null) {
public static function slotRepository(): SlotRepository
{
if (self::$slotRepository === null) {
self::$slotRepository = new SlotRepository(self::$connect);
}
@ -61,8 +62,9 @@ class RepositoryFactory {
/**
* @return VoteRepository The singleton of VoteRepository
*/
static function voteRepository() {
if (self::$voteRepository == null) {
public static function voteRepository(): VoteRepository
{
if (self::$voteRepository === null) {
self::$voteRepository = new VoteRepository(self::$connect);
}
@ -72,12 +74,12 @@ class RepositoryFactory {
/**
* @return CommentRepository The singleton of CommentRepository
*/
static function commentRepository() {
if (self::$commentRepository == null) {
public static function commentRepository(): CommentRepository
{
if (self::$commentRepository === null) {
self::$commentRepository = new CommentRepository(self::$connect);
}
return self::$commentRepository;
}
}

View File

@ -4,16 +4,16 @@
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* 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
* 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 STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Repositories;
@ -22,22 +22,17 @@ use Framadate\FramaDB;
use Framadate\Utils;
class SlotRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
/**
* Insert a bulk of slots.
*
* @param int $poll_id
* @param string $poll_id
* @param array $choices
*/
public function insertSlots($poll_id, $choices) {
public function insertSlots(string $poll_id, array $choices): void
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
foreach ($choices as $choice) {
// We prepared the slots (joined by comas)
$joinedSlots = '';
$first = true;
@ -52,17 +47,19 @@ class SlotRepository extends AbstractRepository {
// We execute the insertion
if (empty($joinedSlots)) {
$prepared->execute(array($poll_id, $choice->getName(), null));
$prepared->execute([$poll_id, $choice->getName(), null]);
} else {
$prepared->execute(array($poll_id, $choice->getName(), $joinedSlots));
$prepared->execute([$poll_id, $choice->getName(), $joinedSlots]);
}
}
}
function listByPollId($poll_id) {
/**
* @return array|false
*/
public function listByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
}
@ -70,11 +67,11 @@ class SlotRepository extends AbstractRepository {
/**
* Find the slot into poll for a given datetime.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $datetime int The datetime of the slot
* @return mixed Object The slot found, or null
*/
function findByPollIdAndDatetime($poll_id, $datetime) {
public function findByPollIdAndDatetime(string $poll_id, $datetime) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
$prepared->execute([$poll_id, $datetime]);
@ -87,12 +84,13 @@ class SlotRepository extends AbstractRepository {
/**
* Insert a new slot into a given poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $title mixed The title of the slot
* @param $moments mixed|null The moments joined with ","
* @return bool true if action succeeded
*/
function insert($poll_id, $title, $moments) {
public function insert(string $poll_id, string $title, ?string $moments): bool
{
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $title, $moments]);
@ -101,12 +99,13 @@ class SlotRepository extends AbstractRepository {
/**
* Update a slot into a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $datetime int The datetime of the slot to update
* @param $newMoments mixed The new moments
* @return bool|null true if action succeeded.
*/
function update($poll_id, $datetime, $newMoments) {
public function update(string $poll_id, $datetime, $newMoments): ?bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
return $prepared->execute([$newMoments, $poll_id, $datetime]);
@ -115,18 +114,19 @@ class SlotRepository extends AbstractRepository {
/**
* Delete a entire slot from a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id int The ID of the poll
* @param $datetime mixed The datetime of the slot
*/
function deleteByDateTime($poll_id, $datetime) {
public function deleteByDateTime(string $poll_id, $datetime): void
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
$prepared->execute([$poll_id, $datetime]);
}
function deleteByPollId($poll_id) {
public function deleteByPollId(string $poll_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
}
}

View File

@ -5,25 +5,24 @@ use Framadate\FramaDB;
use Framadate\Utils;
class VoteRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
function allUserVotesByPollId($poll_id) {
/**
* @return array|false
*/
public function allUserVotesByPollId(string $poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
$prepared->execute(array($poll_id));
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
}
function insertDefault($poll_id, $insert_position) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), "0", SUBSTRING(choices, ?)) WHERE poll_id = ?');
public function insertDefault(string $poll_id, int $insert_position): bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
}
function insert($poll_id, $name, $choices, $token) {
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]);
@ -37,7 +36,8 @@ class VoteRepository extends AbstractRepository {
return $newVote;
}
function deleteById($poll_id, $vote_id) {
public function deleteById(string $poll_id, int $vote_id): bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $vote_id]);
@ -46,10 +46,11 @@ class VoteRepository extends AbstractRepository {
/**
* Delete all votes of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @param string $poll_id The ID of the given poll.
* @return bool|null true if action succeeded.
*/
function deleteByPollId($poll_id) {
public function deleteByPollId(string $poll_id): ?bool
{
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
@ -58,17 +59,19 @@ class VoteRepository extends AbstractRepository {
/**
* Delete all votes made on given moment index.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $index int The index of the vote into the poll
* @return bool|null true if action succeeded.
*/
function deleteByIndex($poll_id, $index) {
public function deleteByIndex(string $poll_id, int $index): ?bool
{
$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]);
}
function update($poll_id, $vote_id, $name, $choices) {
public function update(string $poll_id, string $vote_id, string $name, $choices): bool
{
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
@ -77,14 +80,30 @@ class VoteRepository extends AbstractRepository {
/**
* Check if name is already used for the given poll.
*
* @param int $poll_id ID of the poll
* @param string $poll_id ID of the poll
* @param string $name Name of the vote
* @return bool true if vote already exists
*/
public function existsByPollIdAndName($poll_id, $name) {
public function existsByPollIdAndName(string $poll_id, string $name): bool
{
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?');
$prepared->execute(array($poll_id, $name));
$prepared->execute([$poll_id, $name]);
return $prepared->rowCount() > 0;
}
/**
* Check if name is already used for the given poll and another vote.
*
* @param string $poll_id ID of the poll
* @param string $name Name of the vote
* @param int $vote_id ID of the current vote
* @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 != ?');
$prepared->execute([$poll_id, $name, $vote_id]);
return $prepared->rowCount() > 0;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Framadate\Security;
/**
* Class PasswordHasher
*
* Used to abstract the password hash logic
*
* @package Framadate\Security
*/
class PasswordHasher {
/**
* Hash a password
*
* @param string $password the password to hash.
* @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash.
*/
public static function hash(string $password) {
return password_hash($password, PASSWORD_DEFAULT);
}
/**
* Verify a password with a hash
*
* @param string $password the password to verify
* @param string $hash the hash to compare.
* @return bool
*/
public static function verify(string $password, string $hash): bool
{
return password_verify($password, $hash);
}
}

View File

@ -2,35 +2,35 @@
namespace Framadate\Security;
class Token {
public const DEFAULT_LENGTH = 64;
private $time;
private $value;
private $length;
private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
function __construct($length = 64) {
public function __construct($length = self::DEFAULT_LENGTH) {
$this->length = $length;
$this->time = time() + TOKEN_TIME;
$this->value = $this->generate();
}
private function generate() {
return self::getToken($this->length);
}
public function getTime() {
public function getTime(): int
{
return $this->time;
}
public function getValue() {
public function getValue(): string
{
return $this->value;
}
public function isGone() {
public function isGone(): bool
{
return $this->time < time();
}
public function check($value) {
public function check($value): bool
{
return $value === $this->value;
}
@ -41,7 +41,8 @@ class Token {
* @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not.
* @return string
*/
public static function getToken($length, &$crypto_strong = false) {
public static function getToken(int $length = self::DEFAULT_LENGTH, bool &$crypto_strong = false): string
{
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);
@ -49,7 +50,8 @@ class Token {
return self::getUnsecureToken($length);
}
public static function getUnsecureToken($length) {
public static function getUnsecureToken(int $length): string
{
$string = '';
mt_srand();
for ($i = 0; $i < $length; $i++) {
@ -62,7 +64,8 @@ class Token {
/**
* @author http://stackoverflow.com/a/13733588
*/
public static function getSecureToken($length){
public static function getSecureToken(int $length): string
{
$token = "";
for($i=0;$i<$length;$i++){
$token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))];
@ -70,22 +73,33 @@ class Token {
return $token;
}
private function generate(): string
{
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($min, $max) {
private static function crypto_rand_secure(int $min, $max): int {
$range = $max - $min;
if ($range < 0) return $min; // not so random...
// not so random...
if ($range < 0) {
return $min;
}
$log = log($range, 2);
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
$rnd &= $filter; // discard irrelevant bits
} while ($rnd >= $range);
return $min + $rnd;
}
}

View File

@ -1,8 +1,10 @@
<?php
namespace Framadate\Services;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
/**
* Class AdminPollService
@ -10,7 +12,6 @@ use Framadate\Repositories\RepositoryFactory;
* @package Framadate\Services
*/
class AdminPollService {
private $connect;
private $pollService;
private $logService;
@ -20,7 +21,7 @@ class AdminPollService {
private $voteRepository;
private $commentRepository;
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
public function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect;
$this->pollService = $pollService;
$this->logService = $logService;
@ -30,33 +31,38 @@ class AdminPollService {
$this->commentRepository = RepositoryFactory::commentRepository();
}
function updatePoll($poll) {
public function updatePoll($poll): bool
{
global $config;
if ($poll->end_date > $poll->creation_date && $poll->end_date <= strtotime($poll->creation_date) + (86400 * $config['default_poll_duration'])) {
return $this->pollRepository->update($poll);
} else {
return false;
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();
}
return $this->pollRepository->update($poll);
}
/**
* Delete a comment from a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $comment_id int The ID of the comment
* @return mixed true is action succeeded
*/
function deleteComment($poll_id, $comment_id) {
public function deleteComment(string $poll_id, int $comment_id) {
return $this->commentRepository->deleteById($poll_id, $comment_id);
}
/**
* Remove all comments of a poll.
*
* @param $poll_id int The ID a the poll
* @param string $poll_id The ID a the poll
* @return bool|null true is action succeeded
*/
function cleanComments($poll_id) {
public function cleanComments(string $poll_id): ?bool
{
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
return $this->commentRepository->deleteByPollId($poll_id);
}
@ -64,21 +70,23 @@ class AdminPollService {
/**
* Delete a vote from a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $vote_id int The ID of the vote
* @return mixed true is action succeeded
* @return bool true is action succeeded
*/
function deleteVote($poll_id, $vote_id) {
public function deleteVote(string $poll_id, int $vote_id): bool
{
return $this->voteRepository->deleteById($poll_id, $vote_id);
}
/**
* Remove all votes of a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @return bool|null true is action succeeded
*/
function cleanVotes($poll_id) {
public function cleanVotes(string $poll_id): ?bool
{
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
return $this->voteRepository->deleteByPollId($poll_id);
}
@ -86,10 +94,11 @@ class AdminPollService {
/**
* Delete the entire given poll.
*
* @param $poll_id int The ID of the poll
* @param $poll_id string The ID of the poll
* @return bool true is action succeeded
*/
function deleteEntirePoll($poll_id) {
public function deleteEntirePoll(string $poll_id): bool
{
$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");
@ -109,7 +118,8 @@ class AdminPollService {
* @param object $slot The slot informations (datetime + moment)
* @return bool true if action succeeded
*/
public function deleteDateSlot($poll, $slot) {
public function deleteDateSlot(object $poll, object $slot): bool
{
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$datetime = $slot->title;
@ -118,9 +128,11 @@ class AdminPollService {
$slots = $this->pollService->allSlotsByPoll($poll);
// We can't delete the last slot
if ($poll->format == 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === -1) {
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
return false;
} elseif ($poll->format == 'A' && count($slots) === 1) {
}
if ($poll->format === 'A' && count($slots) === 1) {
return false;
}
@ -133,8 +145,8 @@ class AdminPollService {
$moments = explode(',', $aSlot->moments);
foreach ($moments as $rowMoment) {
if ($datetime == $aSlot->title) {
if ($moment == $rowMoment) {
if ($datetime === $aSlot->title) {
if ($moment === $rowMoment) {
$indexToDelete = $index;
} else {
$newMoments[] = $rowMoment;
@ -157,7 +169,8 @@ class AdminPollService {
return true;
}
public function deleteClassicSlot($poll, $slot_title) {
public function deleteClassicSlot($poll, string $slot_title): bool
{
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
$slots = $this->pollService->allSlotsByPoll($poll);
@ -171,7 +184,7 @@ class AdminPollService {
// Search the index of the slot to delete
foreach ($slots as $aSlot) {
if ($slot_title == $aSlot->title) {
if ($slot_title === $aSlot->title) {
$indexToDelete = $index;
}
$index++;
@ -193,13 +206,14 @@ class AdminPollService {
* <li>Create a new moment if a slot already exists for the given date</li>
* </ul>
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @param $datetime int The datetime
* @param $new_moment string The moment's name
* @return bool true if added
* @throws MomentAlreadyExistsException When the moment to add already exists in database
*/
public function addDateSlot($poll_id, $datetime, $new_moment) {
$this->logService->log('ADD_SLOT', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
public function addDateSlot(string $poll_id, int $datetime, string $new_moment): void
{
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
@ -207,22 +221,18 @@ class AdminPollService {
// Begin transaction
$this->connect->beginTransaction();
if ($result == null) {
// The moment already exists
return false;
} elseif ($result->slot != null) {
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)) {
return false;
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);
}
@ -231,9 +241,6 @@ class AdminPollService {
// Commit transaction
$this->connect->commit();
return true;
}
/**
@ -242,25 +249,25 @@ class AdminPollService {
* <li>Create a new slot if no one exists for the given title</li>
* </ul>
*
* @param $poll_id int The ID of the poll
* @param $title int The title
* @return bool true if added
* @param string $poll_id The ID of the poll
* @param string $title The title
* @throws MomentAlreadyExistsException When the moment to add already exists in database
*/
public function addClassicSlot($poll_id, $title) {
$this->logService->log('ADD_SLOT', 'id:' . $poll_id . ', title:' . $title);
public function addClassicSlot(string $poll_id, string $title): void
{
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
$slots = $this->slotRepository->listByPollId($poll_id);
// Check if slot already exists
$titles = array_map(function ($slot) {
$titles = array_map(static function ($slot) {
return $slot->title;
}, $slots);
if (in_array($title, $titles)) {
if (in_array($title, $titles, true)) {
// The moment already exists
return false;
throw new MomentAlreadyExistsException();
}
// Begin transaction
$this->connect->beginTransaction();
@ -271,9 +278,6 @@ class AdminPollService {
// Commit transaction
$this->connect->commit();
return true;
}
/**
@ -283,37 +287,35 @@ class AdminPollService {
*
* @param $slots array All the slots of the poll
* @param $datetime int The datetime of the new slot
* @return null|\stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/
private function findInsertPosition($slots, $datetime) {
private function findInsertPosition(array $slots, int $datetime) {
$result = new \stdClass();
$result->slot = null;
$result->insert = -1;
$result->insert = 0;
$i = 0;
// Sort slots before searching where to insert
$this->pollService->sortSlorts($slots);
foreach ($slots as $slot) {
$rowDatetime = $slot->title;
// Search where to insert new column
foreach ($slots as $k=>$slot) {
$rowDatetime = (int) $slot->title;
$moments = explode(',', $slot->moments);
if ($datetime == $rowDatetime) {
$i += count($moments);
if ($datetime === $rowDatetime) {
// Here we have to insert at the end of a slot
$result->insert += count($moments);
$result->slot = $slot;
$result->insert = $i;
break;
} elseif ($datetime < $rowDatetime) {
// Here we have to insert a new slot
break;
} else {
$i += count($moments);
}
if ($datetime < $rowDatetime) {
// We have to insert before this slot
break;
}
$result->insert += count($moments);
}
$result->insert = $i;
return $result;
}
}

View File

@ -0,0 +1,182 @@
<?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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -17,23 +17,28 @@
* 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 {
function __construct() {}
public function __construct() {}
/**
* This method filter an array calling "filter_var" on each items.
* Only items validated are added at their own indexes, the others are not returned.
* @param array $arr The array to filter
* @param int $type The type of filter to apply
* @param array|null $options The associative array of options
* @param array|int $options The associative array of options
* @return array The filtered array
*/
function filterArray(array $arr, $type, $options = null) {
public function filterArray(array $arr, int $type, $options = 0): array
{
$newArr = [];
foreach($arr as $id=>$item) {
@ -46,48 +51,126 @@ class InputService {
return $newArr;
}
function filterAllowedValues($value, array $allowedValues) {
public function filterAllowedValues($value, array $allowedValues) {
return in_array($value, $allowedValues, true) ? $value : null;
}
public function filterTitle($title) {
public function filterTitle($title): ?string
{
return $this->returnIfNotBlank($title);
}
public function filterName($name) {
/**
* @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
{
$filtered = trim($name);
return $this->returnIfNotBlank($filtered);
}
/**
* @return false|string
*/
public function filterMail($mail) {
return filter_var($mail, FILTER_VALIDATE_EMAIL);
///////////////////////////////////////////////////////////////////////////////////////
// formatting
$mail = trim($mail);
///////////////////////////////////////////////////////////////////////////////////////
// e-mail validation
$resultat = FALSE;
$validator = new EmailValidator();
if ($validator->isValid($mail, new RFCValidation())) {
$resultat = $mail;
}
///////////////////////////////////////////////////////////////////////////////////////
// return
return $resultat;
}
public function filterDescription($description) {
$description = str_replace("\r\n", "\n", $description);
return $description;
public function filterDescription($description): string {
return str_replace("\r\n", "\n", $description);
}
public function filterBoolean($boolean) {
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
/**
* @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]]);
}
/**
* @return false|string
*/
public function filterEditable($editable) {
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
}
public function filterComment($comment) {
public function filterComment($comment): ?string
{
$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);
}
/**
* Return the value if it's not blank.
*
* @param string $filtered The value
* @return string|null
*/
private function returnIfNotBlank($filtered) {
private function returnIfNotBlank(string $filtered): ?string
{
if ($filtered) {
$withoutSpaces = str_replace(' ', '', $filtered);
if (!empty($withoutSpaces)) {
@ -97,5 +180,4 @@ class InputService {
return null;
}
}
}

View File

@ -4,28 +4,30 @@
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* 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 Raphaël 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\Services;
use function __f;
use Exception;
use Framadate\Utils;
use PDO;
use Smarty;
/**
* This class helps to clean all inputs from the users or external services.
*/
class InstallService {
private $fields = array(
private $fields = [
// General
'appName' => 'Framadate',
'appMail' => '',
@ -39,85 +41,103 @@ class InstallService {
'dbPassword' => '',
'dbPrefix' => 'fd_',
'migrationTable' => 'framadate_migration'
);
];
function __construct() {}
public function __construct() {}
public function updateFields($data) {
public function updateFields($data): void
{
foreach ($data as $field => $value) {
$this->fields[$field] = $value;
}
}
public function install(Smarty &$smarty) {
public function install(Smarty &$smarty): array
{
// 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'])) {
return $this->error('MISSING_VALUES');
}
// Connect to database
$connect = $this->connectTo($this->fields['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']);
if (!$connect) {
return $this->error('CANT_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());
}
// Write configuration to conf.php file
$this->writeConfiguration($smarty);
if ($this->writeConfiguration($smarty) === false) {
return $this->error(__f('Error', "Can't create the config.php file in '%s'.", CONF_FILENAME));
}
return $this->ok();
}
function connectTo($connectionString, $user, $password) {
try {
$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;
} catch(\Exception $e) {
return null;
}
/**
* Connect to PDO compatible source
*
* @param string $connectionString
* @param string $user
* @param string $password
* @return PDO
*/
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 writeConfiguration(Smarty &$smarty) {
/**
* @return false|int
*/
public function writeConfiguration(Smarty &$smarty) {
foreach($this->fields as $field=>$value) {
$smarty->assign($field, $value);
}
$content = $smarty->fetch('admin/config.tpl');
$this->writeToFile($content);
return $this->writeToFile($content);
}
/**
* @param $content
* @return false|int
*/
function writeToFile($content) {
file_put_contents(CONF_FILENAME, $content);
public function writeToFile(string $content) {
return @file_put_contents(CONF_FILENAME, $content);
}
/**
* @return array
*/
function ok() {
return array(
public function ok(): array
{
return [
'status' => 'OK',
'msg' => __f('Installation', 'Ended', Utils::get_server_name())
);
];
}
/**
* @param $msg
* @param string $msg
* @param string $details
* @return array
*/
function error($msg) {
return array(
public function error(string $msg, string $details = ''): array
{
return [
'status' => 'ERROR',
'code' => $msg
);
'code' => $msg,
'details' => $details,
];
}
public function getFields() {
public function getFields(): array
{
return $this->fields;
}
}

View File

@ -7,8 +7,7 @@ namespace Framadate\Services;
* @package Framadate\Services
*/
class LogService {
function __construct() {
public function __construct() {
}
/**
@ -17,9 +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
*/
function log($tag, $message) {
public function log(string $tag, string $message): void
{
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
}
}

View File

@ -1,85 +1,119 @@
<?php
namespace Framadate\Services;
use PHPMailer\PHPMailer\Exception;
use PHPMailer\PHPMailer\PHPMailer;
class MailService {
public const DELAY_BEFORE_RESEND = 300;
public const MAILSERVICE_KEY = 'mailservice';
private $smtp_allowed;
const DELAY_BEFORE_RESEND = 300;
const MAILSERVICE_KEY = 'mailservice';
private $smtp_options = [];
private $logService;
function __construct($smtp_allowed) {
public function __construct($smtp_allowed, $smtp_options = []) {
$this->logService = new LogService();
$this->smtp_allowed = $smtp_allowed;
if (true === is_array($smtp_options)) {
$this->smtp_options = $smtp_options;
}
}
/**
* @return false|string
*/
public function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
function send($to, $subject, $body, $msgKey = null) {
if ($this->smtp_allowed == true && $this->canSendMsg($msgKey)) {
mb_internal_encoding('UTF-8');
/**
* @throws Exception
*/
public function send(string $to, string $subject, string $body, ?string $msgKey = null): void
{
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
$mail = new PHPMailer(true);
$this->configureMailer($mail);
// From
$mail->FromName = NOMAPPLICATION;
$mail->From = ADRESSEMAILADMIN;
if ($this->isValidEmail(ADRESSEMAILREPONSEAUTO)) {
$mail->addReplyTo(ADRESSEMAILREPONSEAUTO);
}
// To
$mail->addAddress($to);
// Subject
$mail->Subject = $subject;
// Bodies
$body .= ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
$mail->isHTML(true);
$mail->msgHTML($body, ROOT_DIR, true);
// Build headers
$subject = mb_encode_mimeheader(html_entity_decode($subject, ENT_QUOTES, 'UTF-8'), 'UTF-8', 'B', "\n", 9);
$encoded_app = mb_encode_mimeheader(NOMAPPLICATION, 'UTF-8', 'B', "\n", 6);
$size_encoded_app = (6 + strlen($encoded_app)) % 75;
$size_admin_email = strlen(ADRESSEMAILADMIN);
if (($size_encoded_app + $size_admin_email + 9) > 74) {
$folding = "\n";
} else {
$folding = '';
};
$from = sprintf("From: %s%s <%s>\n", $encoded_app, $folding, ADRESSEMAILADMIN);
$headers = $from;
$headers .= 'Reply-To: ' . ADRESSEMAILREPONSEAUTO . "\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: text/html; charset=UTF-8\n";
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Auto-Submitted:auto-generated\n";
$headers .= 'Return-Path: <>';
// Build body
$body = $body . '<br/><br/>' . __('Mail', 'Thanks for your trust.') . '<br/>' . NOMAPPLICATION . '<hr/>' . __('Mail', 'FOOTER');
$mail->CharSet = 'UTF-8';
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
$mail->addCustomHeader('Return-Path', '<>');
$mail->XMailer = ' ';
// Send mail
$mail->send();
$this->sendMail($to, $subject, $body, $msgKey, $headers);
// Log
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
// Store the mail sending date
$this->initializeSession();
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
}
}
function canSendMsg($msgKey) {
if ($msgKey == null) {
public function canSendMsg(?string $msgKey): bool
{
if ($msgKey === null) {
return true;
}
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
$_SESSION[self::MAILSERVICE_KEY] = [];
}
$this->initializeSession();
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
}
private function sendMail($to, $subject, $body, $msgKey, $headers) {
mail($to, $subject, $body, $headers, '');
// Log
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
// Store the mail sending date
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
private function initializeSession(): void {
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
$_SESSION[self::MAILSERVICE_KEY] = [];
}
}
/**
* Configure the mailer with the options
*
* @param PHPMailer $mailer
*/
private function configureMailer(PHPMailer $mailer): void
{
$mailer->isSMTP();
$available_options = [
'host' => 'Host',
'auth' => 'SMTPAuth',
'authtype' => 'AuthType',
'username' => 'Username',
'password' => 'Password',
'secure' => 'SMTPSecure',
'port' => 'Port',
];
foreach ($available_options as $config_option => $mailer_option) {
if (true === isset($this->smtp_options[$config_option]) && false === empty($this->smtp_options[$config_option])) {
$mailer->{$mailer_option} = $this->smtp_options[$config_option];
}
}
}
}

View File

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

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,14 +18,19 @@
*/
namespace Framadate\Services;
use DateInterval;
use DateTime;
use Exception;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\ConcurrentVoteException;
use Framadate\Exception\PollNotFoundException;
use Framadate\Form;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token;
use stdClass;
class PollService {
private $connect;
private $logService;
private $pollRepository;
@ -33,8 +38,7 @@ class PollService {
private $voteRepository;
private $commentRepository;
function __construct(FramaDB $connect, LogService $logService) {
$this->connect = $connect;
public function __construct(LogService $logService) {
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
@ -45,70 +49,107 @@ class PollService {
/**
* Find a poll from its ID.
*
* @param $poll_id int The ID of the poll
* @return \stdClass|null The found poll, or null
* @param string $poll_id The ID of the poll
* @return stdClass|null The found poll, or null
*/
function findById($poll_id) {
if (preg_match('/^[\w\d]{16}$/i', $poll_id)) {
public function findById(string $poll_id) {
if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id);
}
return null;
}
function allCommentsByPollId($poll_id) {
public function findByAdminId(string $admin_poll_id) {
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
return $this->pollRepository->findByAdminId($admin_poll_id);
}
return null;
}
public function allCommentsByPollId(string $poll_id) {
return $this->commentRepository->findAllByPollId($poll_id);
}
function allVotesByPollId($poll_id) {
public function allVotesByPollId(string $poll_id) {
return $this->voteRepository->allUserVotesByPollId($poll_id);
}
function allSlotsByPoll($poll) {
public function allSlotsByPoll(stdClass $poll) {
$slots = $this->slotRepository->listByPollId($poll->id);
if ($poll->format == 'D') {
uasort($slots, function ($a, $b) {
return $a->title > $b->title;
});
if ($poll->format === 'D') {
$this->sortSlorts($slots);
}
return $slots;
}
public function updateVote($poll_id, $vote_id, $name, $choices) {
$choices = implode($choices);
/**
* @param string $poll_id
* @param int $vote_id
* @param string $name
* @param array $choices
* @param string $slots_hash
* @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
{
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
// Update vote
return $this->voteRepository->update($poll_id, $vote_id, $name, implode($choices));
}
function addVote($poll_id, $name, $choices) {
if ($this->voteRepository->existsByPollIdAndName($poll_id, $name)) {
return false;
}
/**
* @param string $poll_id
* @param string $name
* @param array $choices
* @param string $slots_hash
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @throws PollNotFoundException
* @throws AlreadyExistsException
* @return stdClass
*/
public function addVote(string $poll_id, string $name, array $choices, string $slots_hash): stdClass
{
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
$choices = implode($choices);
$token = $this->random(16);
return $this->voteRepository->insert($poll_id, $name, $choices, $token);
// Insert new vote
return $this->voteRepository->insert($poll_id, $name, implode($choices), $this->random(16));
}
function addComment($poll_id, $name, $comment) {
public function addComment($poll_id, $name, $comment): bool
{
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
return true;
} else {
return $this->commentRepository->insert($poll_id, $name, $comment);
}
return $this->commentRepository->insert($poll_id, $name, $comment);
}
/**
* @param Form $form
* @return string
* @return array
*/
function createPoll(Form $form) {
public function createPoll(Form $form): array
{
// Generate poll IDs, loop while poll ID already exists
do {
$poll_id = $this->random(16);
} while ($this->pollRepository->existsById($poll_id));
$admin_poll_id = $poll_id . $this->random(8);
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();
@ -118,15 +159,27 @@ class PollService {
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
return array($poll_id, $admin_poll_id);
return [$poll_id, $admin_poll_id];
}
public function findAllByAdminMail($mail) {
public function findAllByAdminMail($mail): array
{
return $this->pollRepository->findAllByAdminMail($mail);
}
function computeBestChoices($votes) {
$result = ['y' => [0], 'inb' => [0]];
/**
* @param array $votes
* @param stdClass $poll
* @return array
*/
public function computeBestChoices(array $votes, $poll): array
{
if (0 === count($votes)) {
return $this->computeEmptyBestChoices($poll);
}
$result = ['y' => [], 'inb' => []];
// if there are votes
foreach ($votes as $vote) {
$choices = str_split($vote->choices);
foreach ($choices as $i => $choice) {
@ -134,10 +187,10 @@ class PollService {
$result['inb'][$i] = 0;
$result['y'][$i] = 0;
}
if ($choice == 1) {
if ($choice === "1") {
$result['inb'][$i]++;
}
if ($choice == 2) {
if ($choice === "2") {
$result['y'][$i]++;
}
}
@ -146,10 +199,11 @@ class PollService {
return $result;
}
function splitSlots($slots) {
$splitted = array();
public function splitSlots($slots): array
{
$splitted = [];
foreach ($slots as $slot) {
$obj = new \stdClass();
$obj = new stdClass();
$obj->day = $slot->title;
$obj->moments = explode(',', $slot->moments);
@ -159,10 +213,22 @@ class PollService {
return $splitted;
}
function splitVotes($votes) {
$splitted = array();
/**
* @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) {
return $carry . $item->id . '@' . $item->moments . ';';
}));
}
public function splitVotes(array $votes): array
{
$splitted = [];
foreach ($votes as $vote) {
$obj = new \stdClass();
$obj = new stdClass();
$obj->id = $vote->id;
$obj->name = $vote->name;
$obj->uniqId = $vote->uniqId;
@ -174,23 +240,146 @@ class PollService {
return $splitted;
}
private function random($length) {
/**
* @throws Exception
* @return DateTime The max date allowed for expiry date
*/
public function maxExpiryDate(): DateTime {
global $config;
return (new DateTime())->add(new DateInterval('P' . $config['default_poll_duration'] . 'D'));
}
/**
* @return DateTime The min date allowed for expiry date
*/
public function minExpiryDate(): DateTime
{
return (new DateTime())->add(new DateInterval('P1D'));
}
/**
* @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;
});
return $slots;
}
/**
* @param stdClass $poll
* @return array
*/
private function computeEmptyBestChoices($poll): array
{
$result = ['y' => [], 'inb' => []];
// if there is no votes, calculates the number of slot
$slots = $this->allSlotsByPoll($poll);
if ($poll->format === 'A') {
// poll format classic
for ($i = 0, $iMax = count($slots); $i < $iMax; $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
} else {
// poll format date
$slots = $this->splitSlots($slots);
foreach ($slots as $slot) {
for ($i = 0, $iMax = count($slot->moments); $i < $iMax; $i++) {
$result['y'][] = 0;
$result['inb'][] = 0;
}
}
}
return $result;
}
private function random(int $length): string
{
return Token::getToken($length);
}
/**
* @return int The max timestamp allowed for expiry date
* @param array $choices
* @param string $poll_id
* @param string $slots_hash
* @param string $name
* @param bool|int $vote_id
* @throws AlreadyExistsException
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @throws PollNotFoundException
*/
public function maxExpiryDate() {
global $config;
return time() + (86400 * $config['default_poll_duration']);
private function checkVoteConstraints(array $choices, string $poll_id, string $slots_hash, string $name, $vote_id = false): void
{
// Check if vote already exists with the same name
if (false === $vote_id) {
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
} else {
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
}
if ($exists) {
throw new AlreadyExistsException();
}
$poll = $this->findById($poll_id);
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);
// Check if slots are still the same
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
}
/**
* @return int The min timestamp allowed for expiry date
* This method checks if the hash send by the user is the same as the computed hash.
*
* @param $poll /stdClass The poll
* @param $slots_hash string The hash sent by the user
* @throws ConcurrentEditionException Thrown when hashes are differents
*/
public function minExpiryDate() {
return time() + 86400;
private function checkThatSlotsDidntChanged(stdClass $poll, string $slots_hash): void
{
$slots = $this->allSlotsByPoll($poll);
if ($slots_hash !== $this->hashSlots($slots)) {
throw new ConcurrentEditionException();
}
}
/**
* This method checks if the votes doesn't conflicts the maximum votes constraint
*
* @param $user_choice
* @param stdClass $poll
* @param string $poll_id
* @throws ConcurrentVoteException
*/
private function checkMaxVotes(array $user_choice, $poll, string $poll_id): void
{
$votes = $this->allVotesByPollId($poll_id);
if (count($votes) <= 0) {
return;
}
$best_choices = $this->computeBestChoices($votes, $poll);
foreach ($best_choices['y'] as $i => $nb_choice) {
// if for this option we have reached maximum value and user wants to add itself too
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
throw new ConcurrentVoteException();
}
}
}
}

View File

@ -9,14 +9,13 @@ use Framadate\Repositories\RepositoryFactory;
* @package Framadate\Services
*/
class PurgeService {
private $logService;
private $pollRepository;
private $slotRepository;
private $voteRepository;
private $commentRepository;
function __construct(FramaDB $connect, LogService $logService) {
public function __construct(LogService $logService) {
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
@ -27,9 +26,10 @@ class PurgeService {
/**
* This methode purges all old polls (the ones with end_date in past).
*
* @return bool true is action succeeded
* @return int number of purged polls
*/
function purgeOldPolls() {
public function purgeOldPolls(): int
{
$oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls);
@ -38,9 +38,9 @@ class PurgeService {
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);
$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);
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
}
}
}
@ -51,10 +51,11 @@ class PurgeService {
/**
* This methode delete all data about a poll.
*
* @param $poll_id int The ID of the poll
* @param string $poll_id The ID of the poll
* @return bool true is action succeeded
*/
function purgePollById($poll_id) {
public function purgePollById(string $poll_id): bool
{
$done = true;
$this->pollRepository->beginTransaction();
@ -71,6 +72,4 @@ class PurgeService {
return $done;
}
}

View File

@ -1,11 +1,11 @@
<?php
namespace Framadate\Services;
use Framadate\Security\PasswordHasher;
use Framadate\Security\Token;
class SecurityService {
function __construct() {
public function __construct() {
}
/**
@ -18,9 +18,10 @@ class SecurityService {
* </ul>
*
* @param $tokan_name string The name of the CSRF token
* @return Token The token
* @return string The token
*/
function getToken($tokan_name) {
function getToken(string $tokan_name): string
{
if (!isset($_SESSION['tokens'])) {
$_SESSION['tokens'] = [];
}
@ -38,7 +39,8 @@ class SecurityService {
* @param $csrf string Value to check
* @return bool true if the token is well checked
*/
public function checkCsrf($tokan_name, $csrf) {
public function checkCsrf(string $tokan_name, string $csrf): bool
{
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
if($checked) {
@ -48,5 +50,46 @@ class SecurityService {
return $checked;
}
/**
* Verify if the current session allows to access given poll.
*
* @param $poll \stdClass The poll which we seek access
* @return bool true if the current session can access this poll
*/
public function canAccessPoll($poll): bool
{
if (is_null($poll->password_hash)) {
return true;
}
$this->ensureSessionPollSecurityIsCreated();
$currentPassword = $_SESSION['poll_security'][$poll->id] ?? null;
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
return true;
}
unset($_SESSION['poll_security'][$poll->id]);
return false;
}
/**
* Submit to the session a poll password
*
* @param $poll \stdClass The poll which we seek access
* @param $password string the password to compare
*/
public function submitPollAccess($poll, string $password): void
{
if (!empty($password)) {
$this->ensureSessionPollSecurityIsCreated();
$_SESSION['poll_security'][$poll->id] = $password;
}
}
private function ensureSessionPollSecurityIsCreated(): void
{
if (!isset($_SESSION['poll_security'])) {
$_SESSION['poll_security'] = [];
}
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Framadate\Services;
class SessionService {
/**
* Get value of $key in $section, or $defaultValue
*
* @param $section
* @param $key
* @param null $defaultValue
* @return mixed
*/
public function get($section, $key, $defaultValue=null) {
assert(!empty($key));
assert(!empty($section));
$this->initSectionIfNeeded($section);
return $_SESSION[$section][$key] ?? $defaultValue;
}
/**
* Set a $value for $key in $section
*
* @param $section
* @param $key
* @param $value
*/
public function set($section, $key, $value): void
{
assert(!empty($key));
assert(!empty($section));
$this->initSectionIfNeeded($section);
$_SESSION[$section][$key] = $value;
}
/**
* Remove a session value
*
* @param $section
* @param $key
*/
public function remove($section, $key): void
{
assert(!empty($key));
assert(!empty($section));
unset($_SESSION[$section][$key]);
}
private function initSectionIfNeeded($section): void
{
if (!isset($_SESSION[$section])) {
$_SESSION[$section] = [];
}
}
}

View File

@ -9,30 +9,27 @@ use Framadate\Repositories\RepositoryFactory;
* @package Framadate\Services
*/
class SuperAdminService {
private $pollRepository;
function __construct() {
public function __construct() {
$this->pollRepository = RepositoryFactory::pollRepository();
}
/**
* Return the list of all polls.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
* @param int $page The page index (O = first page)
* @param int $limit The limit size
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
*/
public function findAllPolls($search, $page, $limit) {
public function findAllPolls(array $search, int $page, int $limit): array
{
$start = $page * $limit;
$polls = $this->pollRepository->findAll($search, $start, $limit);
$count = $this->pollRepository->count($search);
$total = $this->pollRepository->count();
return ['polls' => $polls, 'count' => $count, 'total' => $total];
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,39 +18,30 @@
*/
namespace Framadate;
use Parsedown;
class Utils {
/**
* @return string Server name
*/
public static function get_server_name() {
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
$port = in_array($_SERVER['SERVER_PORT'], [80, 443]) ? '' : ':' . $_SERVER['SERVER_PORT'];
public static function get_server_name(): string
{
$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'];
$dirname = dirname($_SERVER['SCRIPT_NAME']);
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
$dirname = str_replace('/admin', '', $dirname);
$dirname = str_replace(['/admin', '/action'], '', $dirname);
$server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $dirname;
return $scheme . '://' . preg_replace('#//+#', '/', $server_name);
}
public static function is_error($cerr) {
global $err;
if ($err == 0) {
return false;
}
return ($err & $cerr) != 0;
}
public static function is_user() {
return (USE_REMOTE_USER && isset($_SERVER['REMOTE_USER'])) || isset($_SESSION['nom']);
}
/**
* @param string $title
*
* @deprecated
*/
public static function print_header($title = '') {
public static function print_header($title = ''): void {
global $locale;
echo '<!DOCTYPE html>
@ -70,13 +61,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 type="text/javascript" src="' . self::get_server_name() . 'js/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>
<script type="text/javascript" src="' . self::get_server_name() . 'js/core.js"></script>';
<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>';
if ('en' !== $locale) {
echo '
<script src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
}
echo '
<script src="' . self::get_server_name() . 'js/core.js"></script>';
if (is_file($_SERVER['DOCUMENT_ROOT'] . "/nav/nav.js")) {
echo '<script src="/nav/nav.js" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->';
echo '<script src="/nav/nav.js" id="nav_js" charset="utf-8"></script><!-- /Framanav -->';
}
echo '
@ -85,35 +80,34 @@ class Utils {
<div class="container ombre">';
}
/**
* Check if an email address is valid using PHP filters
*
* @param string $email Email address to check
* @return bool True if valid. False if not valid.
* @deprecated
*/
public static function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
/**
* 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
* @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 string|null $action
* @param string|null $action_value
* @return string The poll's URL.
*/
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) {
public static function getUrlSondage(string $id, bool $admin = false, string $vote_id = '', string $action = null, string $action_value = null): string
{
// URL-Encode $action_value
$action_value = $action_value ? self::base64url_encode($action_value) : null;
if (URL_PROPRE) {
if ($admin === true) {
$url = self::get_server_name() . $id . '/admin';
} else {
$url = self::get_server_name() . $id;
}
if ($vote_id != '') {
if ($vote_id !== '') {
$url .= '/vote/' . $vote_id . "#edit";
} elseif ($action != null && $action_value != null) {
$url .= '/action/' . $action . '/' . $action_value;
} elseif ($action) {
if ($action_value) {
$url .= '/action/' . $action . '/' . $action_value;
} else {
$url .= '/action/' . $action;
}
}
} else {
if ($admin === true) {
@ -121,10 +115,14 @@ class Utils {
} else {
$url = self::get_server_name() . 'studs.php?poll=' . $id;
}
if ($vote_id != '') {
if ($vote_id !== '') {
$url .= '&vote=' . $vote_id . "#edit";
} elseif ($action != null && $action_value != null) {
$url .= '&' . $action . "=" . $action_value;
} elseif ($action) {
if ($action_value) {
$url .= '&' . $action . "=" . $action_value;
} else {
$url .= '&' . $action . "=";
}
}
}
@ -136,65 +134,77 @@ class Utils {
*
* @param mixed $object The object to print.
*/
public static function debug($object) {
public static function debug($object): void
{
echo '<pre>';
print_r($object);
echo '</pre>';
}
public static function table($tableName) {
public static function table(string $tableName): string
{
return TABLENAME_PREFIX . $tableName;
}
public static function markdown($md, $clear) {
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $md, $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $md, $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $md, $md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
public static function markdown(string $md, bool $clear=false, bool $line=true): string
{
$parseDown = new Parsedown();
$text = self::htmlEscape($md_a_img[1][0]);
$html = '<a href="' . self::htmlEscape($md_a_img[3][0]) . '"><img src="' . self::htmlEscape($md_a_img[2][0]) . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
$text = self::htmlEscape($md_img[1][0]);
$html = '<img src="' . self::htmlEscape($md_img[2][0]) . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" />';
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
$text = self::htmlEscape($md_a[1][0]);
$html = '<a href="' . $md_a[2][0] . '">' . $text . '</a>';
} else { // text only
$text = self::htmlEscape($md);
$html = $text;
$parseDown
->setBreaksEnabled(true)
->setSafeMode(true)
;
if ($line) {
$html = $parseDown->line($md);
} else {
$md = preg_replace_callback(
'#( ){2,}#',
static function ($m) {
return str_repeat('&nbsp;', strlen($m[0]));
},
$md
);
$html = $parseDown->text($md);
}
$text = strip_tags($html);
return $clear ? $text : $html;
}
public static function htmlEscape($html) {
public static function htmlEscape(string $html): string {
return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
}
public static function csvEscape($text) {
$escaped = str_replace('"', '""', $text);
$escaped = str_replace("\r\n", '', $escaped);
$escaped = str_replace("\n", '', $escaped);
public static function htmlMailEscape(string $html): string
{
return htmlspecialchars($html, ENT_HTML5 | ENT_QUOTES);
}
public static function csvEscape(string $text): string
{
$escaped = str_replace(['"', "\r\n", "\n"], ['""', '', ''], $text);
$escaped = preg_replace("/^(=|\+|\-|\@)/", "'$1", $escaped);
return '"' . $escaped . '"';
}
public static function cleanFilename($title) {
public static function cleanFilename(string $title): string {
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
$cleaned = preg_replace(' {2,}', ' ', $cleaned);
return $cleaned;
return preg_replace(' {2,}', ' ', $cleaned);
}
public static function fromPostOrDefault($postKey, $default = '') {
return !empty($_POST[$postKey]) ? Utils::htmlEscape($_POST[$postKey]) : $default;
public static function fromPostOrDefault(string $postKey, ?string $default = '') {
return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default;
}
public static function base64url_encode(string $input): string
{
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
}
public static function base64url_decode(string $input): string {
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true);
}
}

View File

@ -1,88 +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/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// 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 Framedate behind a reverse proxy.
// const APP_URL = '<www.mydomain.fr>';
// Application name
const NOMAPPLICATION = 'Développement OPZ';
// Database administrator email
const ADRESSEMAILADMIN = 'framadate-dev@olivierperez.fr';
// Email for automatic responses (you should set it to "no-reply")
const ADRESSEMAILREPONSEAUTO = 'no-reply@olivierperez.fr';
// Database user
const DB_USER= 'dev_framadate';
// Database password
const DB_PASSWORD = 'dev_framadate';
// Database server name, leave empty to use a socket
const DB_CONNECTION_STRING = 'mysql:host=localhost;dbname=framadate_dev;port=3306';
// Name of the table that store migration script already executed
const MIGRATION_TABLE = 'framadate_migration';
// Table name prefix
const TABLENAME_PREFIX = 'fd_';
// Default Language using POSIX variant of BC P47 standard (choose in $ALLOWED_LANGUAGES)
const 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',
'es' => 'Español',
'de' => 'Deutsch',
'it' => 'Italiano',
];
// Nom et emplacement du fichier image contenant le titre
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 purge a poll
const PURGE_DELAY = 60;
// Config
$config = [
/* general config */
'use_smtp' => false, // use email for polls creation/modification/responses notification
/* 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 "developpement 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.
];

View File

@ -1,89 +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/OpenSondate: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// 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 Framedate behind a reverse proxy.
// const APP_URL = '<www.mydomain.fr>';
// Application name
const NOMAPPLICATION = '<Application name>';
// Database administrator email
const ADRESSEMAILADMIN = '<email address>';
// Email for automatic responses (you should set it to "no-reply")
const ADRESSEMAILREPONSEAUTO = '<no-reply@mydomain.com>';
// Database user
const DB_USER= '<database user>';
// Database password
const DB_PASSWORD = '<database password>';
// Database server name, leave empty to use a socket
const DB_CONNECTION_STRING = 'mysql:host=<database host>;dbname=<database name>;port=<database port>';
// Name of the table that store migration script already executed
const MIGRATION_TABLE = 'framadate_migration';
// Table name prefix
const TABLENAME_PREFIX = 'fd_';
// 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',
'es' => 'Español',
'de' => 'Deutsch',
'it' => 'Italiano',
];
// Nom et emplacement du fichier image contenant le titre
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 purge a poll
const PURGE_DELAY = 60;
// Config
$config = [
/* general config */
'use_smtp' => true, // use email for polls creation/modification/responses notification
/* 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 "developpement 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.
];

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,14 +18,30 @@
*/
// FRAMADATE version
const VERSION = '0.9';
const VERSION = '1.1.19';
// PHP Needed version
const PHP_NEEDED_VERSION = '7.3';
// Config constants
const COMPILE_DIR = '/tpl_c/';
// Regex
const POLL_REGEX = '/^[a-z0-9]+$/i';
const CHOICE_REGEX = '/^[012]$/';
const POLL_REGEX = '/^[a-z0-9-]*$/i';
const ADMIN_POLL_REGEX = '/^[a-z0-9]{24}$/i';
const CHOICE_REGEX = '/^[ 012]$/';
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i';
const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i';
const EDITABLE_CHOICE_REGEX = '/^[0-2]$/';
const BASE64_REGEX = '/^[A-Za-z0-9]+$/';
const MD5_REGEX = '/^[A-Fa-f0-9]{32}$/';
// Session constants
const SESSION_EDIT_LINK_TOKEN = 'EditLinkToken';
const SESSION_EDIT_LINK_TIME = "EditLinkMail";
// CSRF (300s = 5min)
const TOKEN_TIME = 300;
const ICAL_ENDING = ".ics";
const ICAL_PRODID = "-//Framasoft//Framadate//EN";

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,18 +18,20 @@
*/
// Prepare I18N instance
$i18n = \o80\i18n\I18N::instance();
use o80\i18n\I18N;
$i18n = I18N::instance();
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
$i18n->setPath(__DIR__ . '/../../locale');
// Change langauge when user asked for it
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES))) {
// Change language when user asked for it
if (isset($_POST['lang']) && is_string($_POST['lang']) && array_key_exists($_POST['lang'], $ALLOWED_LANGUAGES)) {
$_SESSION['lang'] = $_POST['lang'];
}
/* <html lang="$locale"> */
$i18n->get('', 'Something, just to load the dictionary');
$locale = $i18n->getLoadedLang();
$locale = str_replace('_', '-', $i18n->getLoadedLang());
/* Date Format */
$date_format['txt_full'] = __('Date', 'FULL'); //summary in create_date_poll.php and removal date in choix_(date|autre).php
@ -37,7 +39,8 @@ $date_format['txt_short'] = __('Date', 'SHORT'); // radio title
$date_format['txt_day'] = __('Date', 'DAY');
$date_format['txt_date'] = __('Date', 'DATE');
$date_format['txt_month_year'] = __('Date', 'MONTH_YEAR');
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { //%e can't be used on Windows platform, use %#d instead
$date_format['txt_datetime_short'] = __('Date', 'DATETIME');
if (PHP_OS_FAMILY === 'Windows') { //%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

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,31 +18,39 @@
*/
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
// Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/../../vendor/o80/i18n/src/shortcuts.php';
if (session_id() == '') {
if (session_id() === '') {
session_start();
}
if (ini_get('date.timezone') == '') {
if (ini_get('date.timezone') === '') {
date_default_timezone_set('Europe/Paris');
}
define('ROOT_DIR', __DIR__ . '/../../');
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
if (is_file(CONF_FILENAME)) {
require_once __DIR__ . '/constants.php';
require_once __DIR__ . '/constants.php';
if (is_file(CONF_FILENAME)) {
@include_once __DIR__ . '/config.php';
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
RepositoryFactory::init($connect);
$err = 0;
try {
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
RepositoryFactory::init($connect);
} catch (PDOException $e) {
if ($_SERVER['SCRIPT_NAME'] !== '/maintenance.php') {
header(('Location: ' . Utils::get_server_name() . 'maintenance.php'));
exit;
}
$error = $e->getMessage();
}
} else {
define('NOMAPPLICATION', 'Framadate');
define('DEFAULT_LANGUAGE', 'fr');
@ -54,9 +62,11 @@ if (is_file(CONF_FILENAME)) {
'es' => 'Español',
'de' => 'Deutsch',
'it' => 'Italiano',
'br' => 'Brezhoneg',
'ca' => 'Català'
];
}
require_once __DIR__ . '/i18n.php';
// Smarty
require_once __DIR__ . '/smarty.php';
require_once __DIR__ . '/smarty.php';

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -21,7 +21,7 @@ use Framadate\Utils;
require_once __DIR__ . '/../../vendor/smarty/smarty/libs/Smarty.class.php';
$smarty = new \Smarty();
$smarty->setTemplateDir(ROOT_DIR . '/tpl/');
$smarty->setCompileDir(ROOT_DIR . '/tpl_c/');
$smarty->setCompileDir(ROOT_DIR . COMPILE_DIR);
$smarty->setCacheDir(ROOT_DIR . '/cache/');
$smarty->caching = false;
@ -30,26 +30,32 @@ $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('provide_fork_awesome', !isset($config['provide_fork_awesome']) || $config['provide_fork_awesome']);
$smarty->assign('locale', $locale);
$smarty->assign('langs', $ALLOWED_LANGUAGES);
$smarty->assign('date_format', $date_format);
if (isset($config['tracking_code'])) {
$smarty->assign('tracking_code', $config['tracking_code']);
}
if (defined('FAVICON')) {
$smarty->assign('favicon', FAVICON);
}
// Dev Mode
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
$smarty->force_compile = true;
$smarty->compile_check = true;
} else {
$smarty->force_compile = false;
$smarty->compile_check = false;
}
function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
function smarty_function_poll_url($params, Smarty_Internal_Template $template): string
{
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
$admin = (isset($params['admin']) && $params['admin']) ? true : false;
$admin = isset($params['admin']) && $params['admin'];
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : false;
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? Utils::htmlEscape($params['action_value']) : false;
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? $params['action_value'] : false;
$vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
// If filter_var fails (i.e.: hack tentative), it will return false. At least no leak is possible from this.
@ -57,14 +63,66 @@ 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($md, $clear = false) {
return Utils::markdown($md, $clear);
function smarty_modifier_markdown(string $md, bool $clear = false, bool $inline=true): string
{
return Utils::markdown($md, $clear, $inline);
}
function smarty_modifier_resource($link) {
function smarty_modifier_resource(string $link): string
{
return Utils::get_server_name() . $link;
}
function smarty_modifier_addslashes_single_quote(string $string): string
{
return addcslashes($string, '\\\'');
}
function smarty_modifier_html($html) {
function smarty_modifier_addslashes(string $string): string
{
return addslashes($string);
}
function smarty_modifier_html(?string $html): string
{
if (!$html) {
return '';
}
return Utils::htmlEscape($html);
}
}
function smarty_modifier_html_special_chars(string $html): string
{
return Utils::htmlMailEscape($html);
}
function smarty_modifier_datepicker_path(string $lang): string
{
$i = 0;
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
$lang_arr = explode('-', $lang);
if ($lang_arr && count($lang_arr) > 1) {
$lang = $lang_arr[0];
} else {
$lang = 'en';
}
++$i;
}
return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
}
function smarty_modifier_locale_2_lang(string $locale): string
{
$lang_arr = explode('-', $locale);
if ($lang_arr && count($lang_arr) > 1) {
return $lang_arr[0];
}
return $locale;
}
function path_for_datepicker_locale(string $lang): string
{
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
}
# Customization #4871 par Didier le 28/08/2021.
$smarty->assign('VERSION',VERSION);

View File

@ -1,13 +1,15 @@
<?php
namespace Framadate;
abstract class FramaTestCase extends \PHPUnit_Framework_TestCase {
use PHPUnit\Framework\TestCase;
protected function getTestResourcePath($resourcepath) {
return __DIR__ . '/../resources/'.$resourcepath;
abstract class FramaTestCase extends TestCase {
protected function getTestResourcePath(string $resourcepath): string
{
return __DIR__ . '/../resources/' . $resourcepath;
}
protected function readTestResource($resourcepath) {
protected function readTestResource(string $resourcepath) {
return file_get_contents($this->getTestResourcePath($resourcepath));
}
@ -19,5 +21,4 @@ abstract class FramaTestCase extends \PHPUnit_Framework_TestCase {
$params = array_slice(func_get_args(), 2); // get all the parameters after $methodName
return $reflectionMethod->invokeArgs($object, $params);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Framadate\Services;
use Framadate\FramaTestCase;
class InputServiceUnitTest extends FramaTestCase
{
public function liste_emails(): array
{
return [
// valids addresses
"valid address" => ["example@example.com", "example@example.com"],
"local address" => ["test@localhost", "test@localhost"],
"IP address" => ["ip.email@127.0.0.1", "ip.email@127.0.0.1"],
"with spaces arround" => [" with@spaces ", "with@spaces"],
"unicode caracters" => ["unicode.éà@idn-œ.com", "unicode.éà@idn-œ.com"],
// invalids addresses
"without domain" => ["without-domain", FALSE],
"space inside" => ["example example@example.com", FALSE],
"forbidden chars" => ["special_chars.@example.com", FALSE],
];
}
/**
* @dataProvider liste_emails
*/
public function test_filterMail($email, $expected): void
{
$inputService = new InputService();
$filtered = $inputService->filterMail($email);
$this->assertSame($expected, $filtered);
}
}

View File

@ -4,12 +4,10 @@ namespace Framadate\Services;
use Framadate\FramaTestCase;
class MailServiceUnitTest extends FramaTestCase {
const MSG_KEY = '666';
public const MSG_KEY = '666';
/**
* @test
*/
function should_send_a_2nd_mail_after_a_good_interval() {
public function test_should_send_a_2nd_mail_after_a_good_interval(): void
{
// Given
$mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
@ -18,13 +16,11 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then
$this->assertEquals(true, $canSendMsg);
$this->assertTrue($canSendMsg);
}
/**
* @test
*/
function should_not_send_2_mails_in_a_short_interval() {
public function test_should_not_send_2_mails_in_a_short_interval(): void
{
// Given
$mailService = new MailService(true);
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
@ -33,7 +29,6 @@ class MailServiceUnitTest extends FramaTestCase {
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
// Then
$this->assertEquals(false, $canSendMsg);
$this->assertFalse($canSendMsg);
}
}

View File

@ -1,3 +1,3 @@
<?php
$loader = require __DIR__ . '/../../vendor/autoload.php';
$loader->addPsr4('Framadate\\', __DIR__.'/Framadate');
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -24,48 +24,47 @@ include_once __DIR__ . '/app/inc/init.php';
function bandeau_titre($titre)
{
global $ALLOWED_LANGUAGES;
$img = ( IMAGE_TITRE ) ? '<img src="'. Utils::get_server_name(). IMAGE_TITRE. '" alt="'.NOMAPPLICATION.'" class="img-responsive">' : '';
$img = ( IMAGE_TITRE ) ? '<img src="' . Utils::get_server_name() . IMAGE_TITRE . '" alt="' . NOMAPPLICATION . '" class="img-responsive">' : '';
echo '
<header role="banner">';
if(count($ALLOWED_LANGUAGES) > 1){
echo '<form method="post" action="" class="hidden-print">
echo '<form method="post" 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>
<select name="lang" class="form-control" title="' . __('Language selector', 'Select the language') . '" >' . liste_lang() . '</select>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="'. __('Language selector', 'Change the language') .'">OK</button>
<button type="submit" class="btn btn-default btn-sm" title="' . __('Language selector', 'Change the language') . '">OK</button>
</span>
</div>
</form>';
}
echo '
<h1><a href="' . Utils::get_server_name() . '" title="' . __('Generic', 'Home') . ' - ' . NOMAPPLICATION . '">' . $img . '</a></h1>
<h2 class="lead"><i>'. $titre .'</i></h2>
<h2 class="lead"><i>' . $titre . '</i></h2>
<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>';
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()
function liste_lang(): string
{
global $ALLOWED_LANGUAGES; global $locale;
$str = '';
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
if (substr($k,0,2)==$locale) {
$str .= '<option lang="'.substr($k,0,2).'" selected value="' . $k . '">' . $v . '</option>' . "\n" ;
if (strpos($k, $locale) === 0) {
$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" ;
$str .= '<option lang="' . substr($k,0,2) . '" value="' . $k . '">' . $v . '</option>' . "\n" ;
}
}
@ -78,5 +77,5 @@ function bandeau_pied()
</main>
</div> <!-- .container -->
</body>
</html>'."\n";
</html>' . "\n";
}

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);
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true);
$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);
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_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
echo json_encode($good, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
function getFromOther($other, $goodKey, $default, $otherLang) {
function getFromOther($other, $goodKey, $default, $otherLang): string {
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);
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true);
$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);
$diffSection = false;
@ -28,15 +28,14 @@ include_once __DIR__ . '/app/inc/init.php';
}
}
if (!$diffSection and array_keys($good)!=array_keys($test)) {
if (!$diffSection and array_keys($good)!==array_keys($test)) {
var_dump(array_keys($good));
var_dump(array_keys($test));
} else {
echo 'All sections are in two langs.' . "\n";
}
$diff = array();
$diff = [];
foreach ($good as $sectionName => $section) {
$diffSection = false;
@ -47,8 +46,8 @@ include_once __DIR__ . '/app/inc/init.php';
}
}
if (!$diffSection and array_keys($good[$sectionName]) != array_keys($test[$sectionName])) {
$diff[$sectionName]['order_good'] = array_keys($good[$sectionName]);
if (!$diffSection and array_keys($section) !== array_keys($test[$sectionName])) {
$diff[$sectionName]['order_good'] = array_keys($section);
$diff[$sectionName]['order_test'] = array_keys($test[$sectionName]);
}
}

View File

@ -1,26 +1,96 @@
{
"name": "framasoft/framadate",
"description": "Application to facilitate the schedule of events or classic polls",
"homepage": "https://framadate.org/",
"keywords": [
"poll"
"poll",
"framadate"
],
"version": "0.9.0",
"license": "CeCILL-B",
"license": "CECILL-B",
"type": "project",
"support": {
"issues": "https://framagit.org/framasoft/framadate/issues"
},
"authors": [
{
"name": "Thomas CITHAREL",
"email": "tcit@tcit.fr",
"role": "Maintainer"
},
{
"name": "JosephK",
"email": "joseph@framasoft.org",
"role": "Maintainer"
},
{
"name": "Olivier PEREZ",
"email": "olivier@olivierperez.fr",
"role": "Former maintainer"
},
{
"name": "Antonin MURTIN",
"email": "antonin.murtin@gmail.com",
"role": "Former developper"
},
{
"name": "Simon LEBLANC",
"role": "Former developper",
"email": "contact@leblanc-simon.eu"
},
{
"name": "Pierre-Yves GOSSET",
"role": "Former developper",
"email": "pyg@framasoft.org"
},
{
"name": "Guilhem BORGHESI",
"role": "Studs developper",
"email": "borghesi@unistra.fr"
},
{
"name": "Raphaël DROZ",
"role": "Studs developper",
"email": "raphael.droz@gmail.com"
}
],
"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": {
"smarty/smarty": "3.1.21",
"o80/i18n": "dev-develop"
"php": ">=7.3.0",
"ext-pdo": "*",
"ext-json": "*",
"smarty/smarty": "^4.0",
"o80/i18n": "dev-develop",
"phpmailer/phpmailer": "~6.2",
"ircmaxell/password-compat": "dev-master",
"roave/security-advisories": "dev-master",
"erusev/parsedown": "^1.7",
"egulias/email-validator": "^3.1",
"sabre/vobject": "~4.1"
},
"require-dev": {
"phpunit/phpunit": "^4.5"
"phpunit/phpunit": "^9",
"friendsofphp/php-cs-fixer": "^3.2",
"vimeo/psalm": "^4.15"
},
"repositories": [
{
"type": "git",
"url": "https://framagit.org/framasoft/framadate/o80-i18n"
}
],
"autoload": {
"psr-4": {
"Framadate\\": "app/classes/Framadate/"
}
},
"config": {
"platform": {
"php": "7.3.0"
}
}
}

5557
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -16,21 +16,25 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\MailService;
use Framadate\Services\PurgeService;
use Framadate\Utils;
use Framadate\Choice;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\PollService;
use Framadate\Services\PurgeService;
use Framadate\Services\SessionService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp']);
$purgeService = new PurgeService($connect, $logService);
$pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($logService);
$sessionService = new SessionService();
$inputService = new InputService();
if (is_file('bandeaux_local.php')) {
include_once('bandeaux_local.php');
@ -38,80 +42,49 @@ if (is_file('bandeaux_local.php')) {
include_once('bandeaux.php');
}
$form = unserialize($_SESSION['form']);
// Step 1/4 : error if $_SESSION from info_sondage are not valid
if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (($config['use_smtp']) ? empty($_SESSION['form']->admin_mail) : false)) {
Utils::print_header(__('Error', 'Error!'));
bandeau_titre(__('Error', 'Error!'));
echo '
<div class="alert alert-danger">
<h3>' . __('Error', 'You haven\'t filled the first section of the poll creation.') . ' !</h3>
<p>' . __('Generic', 'Back to the homepage of') . ' <a href="' . Utils::get_server_name() . '"> ' . NOMAPPLICATION . '</a></p>
</div>' . "\n";
bandeau_pied();
} else {
// Min/Max archive date
$min_expiry_time = $pollService->minExpiryDate();
$max_expiry_time = $pollService->maxExpiryDate();
// The poll format is AUTRE (other)
if ($_SESSION['form']->format !== 'A') {
$_SESSION['form']->format = 'A';
$_SESSION['form']->clearChoices();
if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && empty($form->admin_mail))) {
$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;
}
// 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['confirmecreation'])) {
if (isset($_POST['confirmation'])) {
// Define expiration date
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
if (is_array($registredate) && count($registredate) == 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
if ($time < $min_expiry_time) {
$_SESSION['form']->end_date = $min_expiry_time;
} elseif ($max_expiry_time < $time) {
$_SESSION['form']->end_date = $max_expiry_time;
} else {
$_SESSION['form']->end_date = $time;
}
}
}
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_expiry_time;
}
$expiration_date = $inputService->parseDate($_POST['enddate']);
$form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$ids = $pollService->createPoll($form);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('Mail', "This is the message 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::htmlEscape($_SESSION['form']->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlEscape($_SESSION['form']->title) . '".<br/>';
$message .= __('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>';
$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 .= ' :<br/><br/><a href="%1$s">%1$s</a>';
$message_admin .= sprintf(' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($admin_poll_id, true));
$message = sprintf($message, Utils::getUrlSondage($poll_id));
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message_admin);
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message);
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);
}
}
@ -121,105 +94,64 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
// 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;
} // Step 3/4 : Confirm poll creation and choose a removal date
else if (isset($_POST['fin_sondage_autre'])) {
Utils::print_header(__('Step 3', 'Removal date and confirmation (3 on 3)'));
bandeau_titre(__('Step 3', 'Removal date and confirmation (3 on 3)'));
// Store choices in $_SESSION
if (isset($_POST['choices'])) {
$_SESSION['form']->clearChoices();
$form->clearChoices();
foreach ($_POST['choices'] as $c) {
if (!empty($c)) {
$c = strip_tags($c);
$choice = new Choice($c);
$_SESSION['form']->addChoice($choice);
$form->addChoice($choice);
}
}
}
// Expiration date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
$_SESSION['form']->end_date = $max_expiry_time;
$form->end_date = $pollService->maxExpiryDate()->format('Y-m-d H:i:s');
// Summary
$summary = '<ol>';
foreach ($_SESSION['form']->getChoices() as $i=>$choice) {
foreach ($form->getChoices() as $i=>$choice) {
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href)
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] != '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
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)
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] !== '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
$li_subject_html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" /></a>';
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] != '') ? stripslashes($md_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
} elseif (isset($md_img[2][0]) && $md_img[2][0] !== '') { // ![alt](src)
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] !== '') ? stripslashes($md_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
$li_subject_html = '<img src="' . $md_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" />';
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] != '') ? stripslashes($md_a[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
} elseif (isset($md_a[2][0]) && $md_a[2][0] !== '') { // [text](href)
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] !== '') ? stripslashes($md_a[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
$li_subject_html = '<a href="' . $md_a[2][0] . '">' . $li_subject_text . '</a>';
} else { // text only
$li_subject_text = stripslashes($choice->getName());
$li_subject_html = $li_subject_text;
}
$summary .= '<li>' . $li_subject_html . '</li>' . "\n";
}
$summary .= '</ol>';
$end_date_str = utf8_encode(strftime('%d/%m/%Y', $max_expiry_time)); //textual date
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); //textual date
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'create_classic_poll.php" method="POST" class="form-horizontal" role="form">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="well summary">
<h4>' . __('Step 3', 'List of your choices') . '</h4>
' . $summary . '
</div>
<div class="alert alert-info">
<p>' . __('Step 3', 'Your poll will automatically be archived') . ' ' . $config['default_poll_duration'] . ' ' . __('Generic', 'days') . ' ' .__('Step 3', 'after the last date of your poll.') . '
<br />' . __('Step 3', 'You can set a closer archiving date for it.') . '</p>
<div class="form-group">
<label for="enddate" class="col-sm-5 control-label">' . __('Step 3', 'Archiving date:') . '</label>
<div class="col-sm-6">
<div class="input-group date">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar text-info"></i></span>
<input type="text" class="form-control" id="enddate" data-date-format="' . __('Date', 'dd/mm/yyyy') . '" aria-describedby="dateformat" name="enddate" value="' . $end_date_str . '" size="10" maxlength="10" placeholder="' . __('Date', 'dd/mm/yyyy') . '" />
</div>
</div>
<span id="dateformat" class="sr-only">' . __('Date', 'dd/mm/yyyy') . '</span>
</div>
</div>
<div class="alert alert-warning">
<p>' . __('Step 3', 'Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.') . '</p>';
if ($config['use_smtp'] == true) {
echo '
<p>' . __('Step 3', 'Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.') . '</p>';
}
echo '
</div>
<p class="text-right">
<button class="btn btn-default" onclick="javascript:window.history.back();" title="' . __('Step 3', 'Back to step 2') . '">' . __('Generic', 'Back') . '</button>
<button name="confirmecreation" value="confirmecreation" type="submit" class="btn btn-success">' . __('Step 3', 'Create the poll') . '</button>
</p>
</div>
</div>
</form>' . "\n";
$_SESSION['form'] = serialize($form);
bandeau_pied();
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
$smarty->assign('summary', $summary);
$smarty->assign('end_date_str', $end_date_str);
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->display('create_classic_poll_step3.tpl');
// Step 2/4 : Select choices of the poll
} else {
@ -227,7 +159,7 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
bandeau_titre(__('Step 2 classic', 'Poll subjects (2 on 3)'));
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'create_classic_poll.php" method="POST" class="form-horizontal" role="form">
<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 '
@ -240,10 +172,10 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
echo ' </div>' . "\n";
// Fields choices : 5 by default
$choices = $_SESSION['form']->getChoices();
$choices = $form->getChoices();
$nb_choices = max(count($choices), 5);
for ($i = 0; $i < $nb_choices; $i++) {
$choice = isset($choices[$i]) ? $choices[$i] : new Choice();
$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>
@ -301,11 +233,9 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
</div>
</form>
<script type="text/javascript" src="js/app/framadatepicker.js"></script>
<script type="text/javascript" src="js/app/classic_poll.js"></script>
<script src="js/app/framadatepicker.js"></script>
<script src="js/app/classic_poll.js"></script>
' . "\n";
bandeau_pied();
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft https://framagit.org/framasoft/framadate/)
*
* =============================
*
@ -14,89 +14,193 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft https://framagit.org/framasoft/framadate/)
*/
use Framadate\Choice;
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\MailService;
use Framadate\Services\PollService;
use Framadate\Services\PurgeService;
use Framadate\Services\SessionService;
use Framadate\Utils;
use Framadate\Choice;
include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp']);
$purgeService = new PurgeService($connect, $logService);
$pollService = new PollService($logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($logService);
$inputService = new InputService();
$sessionService = new SessionService();
if (is_readable('bandeaux_local.php')) {
include_once('bandeaux_local.php');
} else {
include_once('bandeaux.php');
}
// Step 1/4 : error if $_SESSION from info_sondage are not valid
if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) || ($config['use_smtp'] && !isset($_SESSION['form']->admin_mail))) {
$form = unserialize($_SESSION['form']);
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation.'));
$smarty->display('error.tpl');
// 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') {
$form->format = 'D';
$form->clearChoices();
}
if (!isset($form->title, $form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
$step = 1;
} else if (!empty($_POST['confirmation'])) {
$step = 4;
} else if (empty($_POST['choixheures']) || isset($form->totalchoixjour)) {
$step = 2;
} else {
// Min/Max archive date
$min_expiry_time = $pollService->minExpiryDate();
$max_expiry_time = $pollService->maxExpiryDate();
$step = 3;
}
// The poll format is DATE
if ($_SESSION['form']->format !== 'D') {
$_SESSION['form']->format = 'D';
$_SESSION['form']->clearChoices();
}
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;
// Step 4 : Data prepare before insert in DB
if (!empty($_POST['confirmation'])) {
case 2:
// Step 2/4 : Select dates of the poll
// Define expiration date
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
if (is_array($registredate) && count($registredate) == 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
if ($time < $min_expiry_time) {
$_SESSION['form']->end_date = $min_expiry_time;
} elseif ($max_expiry_time < $time) {
$_SESSION['form']->end_date = $max_expiry_time;
} else {
$_SESSION['form']->end_date = $time;
}
// Prefill form->choices
foreach ($form->getChoices() as $c) {
/** @var Choice $c */
$count = 3 - count($c->getSlots());
for ($i = 0; $i < $count; $i++) {
$c->addSlot('');
}
}
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_expiry_time;
$count = 3 - count($form->getChoices());
for ($i = 0; $i < $count; $i++) {
$c = new Choice('');
$c->addSlot('');
$c->addSlot('');
$c->addSlot('');
$form->addChoice($c);
}
$_SESSION['form'] = serialize($form);
// Display step 2
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 on 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('error', null);
$smarty->display('create_date_poll_step_2.tpl');
exit;
case 3:
// Step 3/4 : Confirm poll creation
// Handle Step2 submission
if (!empty($_POST['days'])) {
// Remove empty dates
$_POST['days'] = array_filter($_POST['days'], static function ($d) {
return !empty($d);
});
// Check if there are at most MAX_SLOTS_PER_POLL slots
if (count($_POST['days']) > MAX_SLOTS_PER_POLL) {
// Display step 2
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 on 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('error', __f('Error', 'You can\'t select more than %d dates', MAX_SLOTS_PER_POLL));
$smarty->display('create_date_poll_step_2.tpl');
exit;
}
// Clear previous choices
$form->clearChoices();
// Reorder moments to deal with suppressed dates
$moments = [];
$i = 0;
while(count($moments) < count($_POST['days'])) {
if (!empty($_POST['horaires' . $i])) {
$moments[] = $_POST['horaires' . $i];
}
$i++;
}
for ($i = 0, $iMax = count($_POST['days']); $i < $iMax; $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();
$choice = new Choice($time);
$form->addChoice($choice);
$schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT);
for ($j = 0, $jMax = count($schedules); $j < $jMax; $j++) {
if (!empty($schedules[$j])) {
$choice->addSlot(strip_tags($schedules[$j]));
}
}
}
}
$form->sortChoices();
}
// Display step 3
$summary = '<ul>';
$choices = $form->getChoices();
foreach ($choices as $choice) {
$summary .= '<li>' . strftime($date_format['txt_full'], $choice->getName());
$first = true;
foreach ($choice->getSlots() as $slots) {
$summary .= $first ? ': ' : ', ';
$summary .= $slots;
$first = false;
}
$summary .= '</li>';
}
$summary .= '</ul>';
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); // textual date
$_SESSION['form'] = serialize($form);
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
$smarty->assign('summary', $summary);
$smarty->assign('end_date_str', $end_date_str);
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->display('create_classic_poll_step3.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();
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$ids = $pollService->createPoll($form);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('Mail', "This is the message 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::htmlEscape($_SESSION['form']->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlEscape($_SESSION['form']->title) . '".<br/>';
$message .= Utils::htmlEscape($form->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlEscape($form->title) . '".<br/>';
$message .= __('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>';
$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");
@ -105,9 +209,9 @@ if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) ||
$message = sprintf($message, Utils::getUrlSondage($poll_id));
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message_admin);
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message);
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);
}
}
@ -117,126 +221,10 @@ if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) ||
// 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;
} else {
if (!empty($_POST['days'])) {
// Clear previous choices
$_SESSION['form']->clearChoices();
for ($i = 0; $i < count($_POST['days']); $i++) {
$day = $_POST['days'][$i];
if (!empty($day)) {
// Add choice to Form data
$time = mktime(0, 0, 0, substr($_POST["days"][$i],3,2),substr($_POST["days"][$i],0,2),substr($_POST["days"][$i],6,4));
$choice = new Choice($time);
$_SESSION['form']->addChoice($choice);
$schedules = $inputService->filterArray($_POST['horaires'.$i], FILTER_DEFAULT);
for($j = 0; $j < count($schedules); $j++) {
if (!empty($schedules[$j])) {
$choice->addSlot(strip_tags($schedules[$j]));
}
}
}
}
}
}
// Step 3/4 : Confirm poll creation
if (!empty($_POST['choixheures']) && !isset($_SESSION['form']->totalchoixjour)) {
Utils::print_header ( __('Step 3', 'Removal date and confirmation (3 on 3)') );
bandeau_titre(__('Step 3', 'Removal date and confirmation (3 on 3)'));
$end_date_str = utf8_encode(strftime('%d/%m/%Y', $max_expiry_time)); // textual date
// Summary
$summary = '<ul>';
$choices = $_SESSION['form']->getChoices();
foreach ($choices as $choice) {
$summary .= '<li>'.strftime($date_format['txt_full'], $choice->getName());
$first = true;
foreach ($choice->getSlots() as $slots) {
$summary .= $first ? ': ' : ', ';
$summary .= $slots;
$first = false;
}
$summary .= '</li>';
}
$summary .= '</ul>';
echo '
<form name="formulaire" action="' . Utils::get_server_name() . 'create_date_poll.php" method="POST" class="form-horizontal" role="form">
<div class="row" id="selected-days">
<div class="col-md-8 col-md-offset-2">
<h3>'. __('Step 3', 'Confirm the creation of your poll') .'</h3>
<div class="well summary">
<h4>'. __('Step 3', 'List of your choices').'</h4>
'. $summary .'
</div>
<div class="alert alert-info clearfix">
<p>' . __f('Step 3', 'Your poll will be automatically archived in %d days.', $config['default_poll_duration']) . '
<br />' . __('Step 3', 'You can set a closer archiving date for it.') .'</p>
<div class="form-group">
<label for="enddate" class="col-sm-5 control-label">'. __('Step 3', 'Archiving date:') .'</label>
<div class="col-sm-6">
<div class="input-group date">
<span class="input-group-addon"><i class="glyphicon glyphicon-calendar text-info"></i></span>
<input type="text" class="form-control" id="enddate" data-date-format="'. __('Date', 'dd/mm/yyyy') .'" aria-describedby="dateformat" name="enddate" value="'.$end_date_str.'" size="10" maxlength="10" placeholder="'. __('Date', 'dd/mm/yyyy') .'" />
</div>
</div>
<span id="dateformat" class="sr-only">('. __('Date', 'dd/mm/yyyy') .')</span>
</div>
</div>
<div class="alert alert-warning">
<p>'. __('Step 3', 'Once you have confirmed the creation of your poll, you will be automatically redirected on the administration page of your poll.'). '</p>';
if($config['use_smtp'] == true) {
echo '<p>' . __('Step 3', 'Then, you will receive quickly two emails: one contening the link of your poll for sending it to the voters, the other contening the link to the administration page of your poll.') .'</p>';
}
echo '
</div>
<p class="text-right">
<button class="btn btn-default" onclick="javascript:window.history.back();" title="'. __('Step 3', 'Back to step 2') . '">'. __('Generic', 'Back') . '</button>
<button name="confirmation" value="confirmation" type="submit" class="btn btn-success">'. __('Step 3', 'Create the poll') . '</button>
</p>
</div>
</div>
</form>'."\n";
bandeau_pied();
// Step 2/4 : Select dates of the poll
} else {
// Prefill form->choices
foreach ($_SESSION['form']->getChoices() as $c) {
$count = 3 - count($c->getSlots());
for($i=0; $i< $count; $i++) {
$c->addSlot('');
}
}
$count = 3 - count($_SESSION['form']->getChoices());
for($i=0; $i< $count; $i++) {
$c = new Choice('');
$c->addSlot('');
$c->addSlot('');
$c->addSlot('');
$_SESSION['form']->addChoice($c);
}
// Display step 2
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 on 3)'));
$smarty->assign('choices', $_SESSION['form']->getChoices());
$smarty->display('create_date_poll_step_2.tpl');
}
}

View File

@ -5,7 +5,7 @@
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Rapha<EFBFBD>l DROZ
* Authors of Framadate/OpenSondate: Framasoft (https://github.com/framasoft)
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
@ -18,6 +18,8 @@
*/
use Framadate\Form;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\PasswordHasher;
use Framadate\Services\InputService;
use Framadate\Utils;
@ -25,67 +27,102 @@ include_once __DIR__ . '/app/inc/init.php';
const GO_TO_STEP_2 = 'gotostep2';
/* Services */
/*----------*/
$inputService = new InputService();
$pollRepository = RepositoryFactory::pollRepository();
/* PAGE */
/* ---- */
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
if (!isset($_SESSION['form'])) {
$_SESSION['form'] = new 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') ||
(isset($_POST['type']) && $_POST['type'] === 'date')
) {
$poll_type = 'date';
$_SESSION['form']->choix_sondage = $poll_type;
$form->choix_sondage = $poll_type;
} else {
$poll_type = 'classic';
$_SESSION['form']->choix_sondage = $poll_type;
$form->choix_sondage = $poll_type;
}
// We clean the data
$goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(date|classic)$/']]);
if ($goToStep2) {
$title = $inputService->filterTitle($_POST['title']);
$name = $inputService->filterName($_POST['name']);
$mail = $inputService->filterMail($_POST['mail']);
$use_ValueMax = isset($_POST['use_ValueMax']) && $inputService->filterBoolean($_POST['use_ValueMax']);
$ValueMax = $use_ValueMax === true ? $inputService->filterValueMax($_POST['ValueMax']) : null;
$use_customized_url = isset($_POST['use_customized_url']) && $inputService->filterBoolean($_POST['use_customized_url']);
$customized_url = $use_customized_url === true ? $inputService->filterId($_POST['customized_url']) : null;
$name = mb_substr($inputService->filterName($_POST['name']), 0, 32);
$mail = $config['use_smtp'] === true ? $inputService->filterMail($_POST['mail']) : null;
$description = $inputService->filterDescription($_POST['description']);
$editable = $inputService->filterEditable($_POST['editable']);
$receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false;
$receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false;
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
$receiveNewVotes = isset($_POST['receiveNewVotes']) && $inputService->filterBoolean($_POST['receiveNewVotes']);
$receiveNewComments = isset($_POST['receiveNewComments']) && $inputService->filterBoolean($_POST['receiveNewComments']);
$hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
$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;
$results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
// On initialise également les autres variables
$error_on_mail = false;
$error_on_title = false;
$error_on_name = false;
$error_on_description = false;
$error_on_password = false;
$error_on_password_repeat = false;
$error_on_customized_url = false;
$error_on_ValueMax = false;
$_SESSION['form']->title = $title;
$_SESSION['form']->admin_name = $name;
$_SESSION['form']->admin_mail = $mail;
$_SESSION['form']->description = $description;
$_SESSION['form']->editable = $editable;
$_SESSION['form']->receiveNewVotes = $receiveNewVotes;
$_SESSION['form']->receiveNewComments = $receiveNewComments;
$_SESSION['form']->hidden = $hidden;
$form->title = $title;
$form->id = $customized_url;
$form->use_customized_url = $use_customized_url;
$form->use_ValueMax = $use_ValueMax;
$form->ValueMax = $ValueMax;
$form->admin_name = $name;
$form->admin_mail = $mail;
$form->description = $description;
$form->editable = $editable;
$form->receiveNewVotes = $receiveNewVotes;
$form->receiveNewComments = $receiveNewComments;
$form->hidden = $hidden;
$form->use_password = ($use_password !== null);
$form->results_publicly_visible = ($results_publicly_visible !== null);
if ($config['use_smtp'] == true) {
if (empty($mail)) {
$error_on_mail = true;
}
if ($config['use_smtp'] === true && empty($mail)) {
$error_on_mail = true;
}
if ($title !== $_POST['title']) {
$error_on_title = true;
}
if ($use_customized_url) {
if ($customized_url === false) {
$error_on_customized_url = true;
} else if ($pollRepository->existsById($customized_url)) {
$error_on_customized_url = true;
$error_on_customized_url_msg = __('Error', 'Poll id already used');
} else if (in_array($customized_url, ['admin', 'vote', 'action'], true)) {
$error_on_customized_url = true;
$error_on_customized_url_msg = __('Error', 'This id is not allowed');
}
}
if ($use_ValueMax && $ValueMax === false) {
$error_on_ValueMax = true;
}
if ($name !== $_POST['name']) {
$error_on_name = true;
}
@ -95,24 +132,42 @@ if ($goToStep2) {
}
// Si pas d'erreur dans l'adresse alors on change de page vers date ou autre
if ($config['use_smtp'] == true) {
if ($config['use_smtp'] === true) {
$email_OK = $mail && !$error_on_mail;
} else {
$email_OK = true;
}
if ($title && $name && $email_OK && !$error_on_title && !$error_on_description && !$error_on_name) {
if ($use_password) {
if (empty($password)) {
$error_on_password = true;
} else if ($password !== $password_repeat) {
$error_on_password_repeat = true;
}
}
if ($goToStep2 == 'date') {
if ($title && $name && $email_OK && !$error_on_title && !$error_on_customized_url && !$error_on_description && !$error_on_name
&& !$error_on_password && !$error_on_password_repeat &&!$error_on_ValueMax
) {
// If no errors, we hash the password if needed
if ($form->use_password) {
$form->password_hash = PasswordHasher::hash($password);
} else {
$form->password_hash = null;
$form->results_publicly_visible = null;
}
$_SESSION['form'] = serialize($form);
if ($goToStep2 === 'date') {
header('Location:create_date_poll.php');
exit();
}
if ($goToStep2 == 'classic') {
if ($goToStep2 === 'classic') {
header('Location:create_classic_poll.php');
exit();
}
} else {
// Title Erreur !
$title = __('Error', 'Error!') . ' - ' . __('Step 1', 'Poll creation (1 on 3)');
@ -123,28 +178,48 @@ if ($goToStep2) {
}
// Prepare error messages
$errors = array(
'title' => array(
$errors = [
'title' => [
'msg' => '',
'aria' => '',
'class' => ''
),
'description' => array(
],
'customized_url' => [
'msg' => '',
'aria' => '',
'class' => ''
),
'name' => array(
],
'description' => [
'msg' => '',
'aria' => '',
'class' => ''
),
'email' => array(
],
'name' => [
'msg' => '',
'aria' => '',
'class' => ''
)
);
],
'email' => [
'msg' => '',
'aria' => '',
'class' => ''
],
'password' => [
'msg' => '',
'aria' => '',
'class' => ''
],
'ValueMax' => [
'msg' => '',
'aria' => '',
'class' => ''
],
'password_repeat' => [
'msg' => '',
'aria' => '',
'class' => ''
],
];
if (!empty($_POST[GO_TO_STEP_2])) {
if (empty($_POST['title'])) {
@ -157,6 +232,12 @@ if (!empty($_POST[GO_TO_STEP_2])) {
$errors['title']['msg'] = __('Error', 'Something is wrong with the format');
}
if ($error_on_customized_url) {
$errors['customized_url']['aria'] = 'aria-describeby="customized_url" ';
$errors['customized_url']['class'] = ' has-error';
$errors['customized_url']['msg'] = $error_on_customized_url_msg ?? __('Error', "Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.");
}
if ($error_on_description) {
$errors['description']['aria'] = 'aria-describeby="poll_comment_error" ';
$errors['description']['class'] = ' has-error';
@ -167,10 +248,14 @@ 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';
$errors['name']['msg'] = __('Error', 'Something is wrong with the format');
$errors['name']['msg'] = __('Error', "Something is wrong with the format: name shouldn't have any spaces before or after");
}
if (empty($_POST['mail'])) {
@ -182,6 +267,22 @@ if (!empty($_POST[GO_TO_STEP_2])) {
$errors['email']['class'] = ' has-error';
$errors['email']['msg'] = __('Error', 'The address is not correct! You should enter a valid email address (like r.stallman@outlock.com) in order to receive the link to your poll.');
}
if ($error_on_password) {
$errors['password']['aria'] = 'aria-describeby="poll_password_error" ';
$errors['password']['class'] = ' has-error';
$errors['password']['msg'] = __('Error', 'Password is empty');
}
if ($error_on_password_repeat) {
$errors['password_repeat']['aria'] = 'aria-describeby="poll_password_repeat_error" ';
$errors['password_repeat']['class'] = ' has-error';
$errors['password_repeat']['msg'] = __('Error', 'Passwords do not match');
}
if ($error_on_ValueMax) {
$errors['ValueMax']['aria'] = 'aria-describeby="poll_ValueMax" ';
$errors['ValueMax']['class'] = ' has-error';
$errors['ValueMax']['msg'] = __('Error', 'Error on amount of voters limitation : value must be an integer greater than 0');
}
}
$useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']);
@ -189,18 +290,26 @@ $useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']);
$smarty->assign('title', $title);
$smarty->assign('useRemoteUser', $useRemoteUser);
$smarty->assign('errors', $errors);
$smarty->assign('advanced_errors', $goToStep2 && ($error_on_ValueMax || $error_on_customized_url || $error_on_password || $error_on_password_repeat));
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
$smarty->assign('goToStep2', GO_TO_STEP_2);
$smarty->assign('poll_type', $poll_type);
$smarty->assign('poll_title', Utils::fromPostOrDefault('title', $_SESSION['form']->title));
$smarty->assign('poll_description', Utils::fromPostOrDefault('description', $_SESSION['form']->description));
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $_SESSION['form']->admin_name));
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $_SESSION['form']->admin_mail));
$smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $_SESSION['form']->editable));
$smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $_SESSION['form']->receiveNewVotes));
$smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $_SESSION['form']->receiveNewComments));
$smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden));
$smarty->assign('form', $_SESSION['form']);
$smarty->assign('poll_title', Utils::fromPostOrDefault('title', $form->title));
$smarty->assign('customized_url', Utils::fromPostOrDefault('customized_url', $form->id));
$smarty->assign('use_customized_url', Utils::fromPostOrDefault('use_customized_url', $form->use_customized_url));
$smarty->assign('ValueMax', Utils::fromPostOrDefault('ValueMax', $form->ValueMax));
$smarty->assign('use_ValueMax', Utils::fromPostOrDefault('use_ValueMax', $form->use_ValueMax));
$smarty->assign('poll_description', !empty($_POST['description']) ? $_POST['description'] : $form->description);
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $form->admin_name));
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $form->admin_mail));
$smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $form->editable));
$smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $form->receiveNewVotes));
$smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $form->receiveNewComments));
$smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $form->hidden));
$smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $form->use_password));
$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $form->results_publicly_visible));
$smarty->assign('form', $form);
$smarty->display('create_poll.tpl');

View File

@ -1,3 +1,25 @@
.form-horizontal .radio label {
width: 100%;
}
}
.optionnal-parameters:hover, .optionnal-parameters:focus {
text-decoration: none;
}
.optionnal-parameters:active, .optionnal-parameters:focus {
outline: none;
}
.optionnal-parameters .caret, .optionnal-parameters.collapsed .caret.caret-up {
display: none;
}
.optionnal-parameters .caret.caret-up, .optionnal-parameters.collapsed .caret {
display: inline-block;
}
.caret.caret-up {
border-bottom: 4px dashed;
border-top: 0 none;
content: "";
}

View File

@ -1,9 +1,8 @@
/*!
* Bootstrap v3.2.0 (http://getbootstrap.com)
* Copyright 2011-2014 Twitter, Inc.
* Bootstrap v3.3.7 (http://getbootstrap.com)
* Copyright 2011-2016 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
@ -29,6 +28,35 @@
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
@ -55,34 +83,66 @@
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default:disabled,
.btn-default[disabled] {
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2));
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #2b669a;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #2d6ca2;
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #2d6ca2;
border-color: #2b669a;
background-color: #265a88;
border-color: #245580;
}
.btn-primary:disabled,
.btn-primary[disabled] {
background-color: #2d6ca2;
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
@ -105,8 +165,24 @@
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success:disabled,
.btn-success[disabled] {
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
@ -130,8 +206,24 @@
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info:disabled,
.btn-info[disabled] {
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
@ -155,8 +247,24 @@
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning:disabled,
.btn-warning[disabled] {
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
@ -180,8 +288,24 @@
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger:disabled,
.btn-danger[disabled] {
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
@ -203,12 +327,12 @@
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #357ebd;
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
@ -223,12 +347,13 @@
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
@ -245,13 +370,15 @@
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
background-image: -o-linear-gradient(top, #222 0%, #282828 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828));
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
@ -265,6 +392,19 @@
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
@ -315,11 +455,11 @@
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9));
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
@ -367,14 +507,19 @@
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #3071a9;
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3));
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #3278b3;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
@ -389,11 +534,11 @@
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1238
css/bootstrap.css vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -130,7 +130,7 @@
font-weight:bold;
/*color: #000000;
/*background-color: #ffdb99;
border-color: #ffb733;*/*/
border-color: #ffb733;*/
}
.datepicker table tr td.today:hover,
.datepicker table tr td.today:hover:hover,

7
css/easymde.min.css vendored Normal file

File diff suppressed because one or more lines are too long

2431
css/fork-awesome.css Normal file

File diff suppressed because it is too large Load Diff

4
css/fork-awesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@ body {
}
table {
page-break-after:always}
page-break-after:always
}
table {

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