Compare commits

...

231 Commits

Author SHA1 Message Date
Thomas Citharel 86324bd1f4 Merge branch 'v1.2.0-alpha.1' into 'develop'
V1.2.0 alpha.1

See merge request framasoft/framadate/framadate!400
2019-05-01 20:54:40 +02:00
Thomas Citharel 082644a021
Version 1.2.0-alpha.1
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-05-01 20:51:55 +02:00
Thomas Citharel 82012628d1
Upgrade deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-05-01 20:51:41 +02:00
Thomas Citharel 485fb03405 Merge branch 'locales/update-locales' into 'develop'
Update locales

See merge request framasoft/framadate/framadate!399
2019-04-19 21:13:34 +02:00
Thomas Citharel 6395a3db0b
Update locales
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-19 21:11:07 +02:00
Thomas Citharel 2da258509c Merge branch 'deps/update' into 'develop'
Upgrade composer.lock deps

See merge request framasoft/framadate/framadate!398
2019-04-19 20:49:16 +02:00
Thomas Citharel b93733ab8e
Upgrade composer.lock deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-19 20:46:59 +02:00
Thomas Citharel 1d0b565594 Merge branch 'create-poll-fix' into 'develop'
fix(create_poll): don't send emails in case of DB transaction errors

See merge request framasoft/framadate/framadate!384
2019-04-19 20:44:14 +02:00
Thomas Citharel 9f2cf79171 Merge branch 'mail/templates' into 'develop'
Extract poll update notification mails into smarty templates

See merge request framasoft/framadate/framadate!390
2019-04-19 20:43:00 +02:00
Thomas Citharel be80321545 Merge branch 'cleanup/dependencies' into 'develop'
Cleanup/dependencies

See merge request framasoft/framadate/framadate!397
2019-04-19 20:42:44 +02:00
Thomas Citharel 532118ef06
Rebase and add public modifiers
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-19 20:40:32 +02:00
Liquidsoul aa61f40d51
Use Services factory everywhere to initialize variables 2019-04-19 20:37:35 +02:00
Liquidsoul 1a66bd8bbe
Initialize Services factory 2019-04-19 20:37:35 +02:00
Liquidsoul f641a35f8b
Add Services factory 2019-04-19 20:37:34 +02:00
Liquidsoul 8f23917639
Inject session and purge services in PollService 2019-04-19 20:37:34 +02:00
Thomas Citharel cd55e9a40a Merge branch 'use-php-intl' into 'develop'
[Big] Move displaying dates from libc to PHP-intl / ICU

See merge request framasoft/framadate/framadate!396
2019-04-19 20:33:36 +02:00
Thomas Citharel bc964e87a7
[Big] Move displaying dates from libc to PHP-intl / ICU
* Make sure we always work only with DateTimes
* Support slots again under SQLite
* Small fixes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-19 20:29:32 +02:00
Paul B c93741f475
create_poll: don't send emails in case of DB transaction errors
This commit adds an error message in the STEP 3 of the poll creation
form in case there is a DB transactions error.

It also makes sure to not send emails in case of those DB transaction
errors.
2019-04-19 17:58:59 +02:00
Liquidsoul cefcb834d7
Extract poll update notification mails into smarty templates 2019-04-19 16:56:10 +02:00
Thomas Citharel 9aaf5b3bbd Merge branch 'ci/lint-verbose' into 'develop'
Add diff option to the linter to see errors/fixes

See merge request framasoft/framadate/framadate!394
2019-04-19 16:41:06 +02:00
Thomas Citharel 13d1103698 Merge branch 'refacto-creation-poll-service' into 'develop'
Refactorisation of the creation poll service

See merge request framasoft/framadate/framadate!383
2019-04-19 16:40:47 +02:00
Thomas Citharel cb6c0baa26 Merge branch 'use-symfony-translation' into 'develop'
Use Symfony/Translation and use the .po files directly in the repo and app

See merge request framasoft/framadate/framadate!389
2019-04-19 16:38:33 +02:00
Nicolas Bouilleaud b6723120ab Fix po files following #387
The msgid actually used in yyyy-mm-dd-for-humans, not year-month-day.
2019-04-19 15:56:53 +02:00
Paul B 9dd1a6ecf9
refacto: factorise code between date_poll and classic_poll creation
The last confirmation step during poll creation flow is identical
between date polls and classical polls.

This commit moves the code in the `PollService` to be used in both
creation poll controllers.
2019-04-19 15:52:28 +02:00
Paul B dd55b7444c
MailService: expose a function 'isEnabled'
The `use_smtp` configuration key really means: Are emails enabled?

Indeed the 'smtp' naming is here for legacy reasons. (cf #425)
2019-04-19 15:52:28 +02:00
Paul B f0b38fdb42
sessionService: use a generic removeAll function to unset FORM data
Instead of manually removing entries in the SESSION object, let the
SessionService do the work in creating poll controllers.
2019-04-19 15:52:27 +02:00
Paul B 5d74a5600d
cleanup: backport changes from date_poll into classic_poll
The last action of creatin a poll is the same code between classical
and date polls.

This commit backports the minor refactoring to have the exact same
code.

(done with ediff to make sure the code was similar)
2019-04-19 15:52:26 +02:00
Liquidsoul 95e7977c1b
Add diff option to the linter to see errors/fixes 2019-04-19 15:20:04 +02:00
Nicolas Bouilleaud bc78721381 Fix i18n.php review comments
* Use the actually found locale for $locale in the rest of the app
2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 955faa108c Fix i18n.php review comments
* Use the DEFAULT_LANGUAGE as the default in locale_lookup.
* Initialise the translators explicitly.
2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 94739ba971 Fix i18n.php review comments
Use the DEFAULT_LANGUAGE as the default in locale_lookup.
2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 20240fee15 Fix i18n.php review comments
* remove useless is_string check
* use the DEFAULT_LANGUAGE when Accept-Language is invalid.
2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 780392bb37 Remove i18n related stuff from gitlab-ci 2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud aca1b57dfc Enable Hungarian (Magyar) 2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 0353e6f0d7 Add all po files 2019-04-19 14:58:32 +02:00
Nicolas Bouilleaud 98c43f4685 Remove i18n json files 2019-04-19 14:47:15 +02:00
Nicolas Bouilleaud e7947354ac Fix smarty_function_markdown_to_text() to use $locale 2019-04-19 14:46:26 +02:00
Nicolas Bouilleaud 1f05d88c42 Remove old buildland / compare 2019-04-19 14:46:26 +02:00
Nicolas Bouilleaud 31f0a26baa Remove o80/i18n 2019-04-19 14:46:26 +02:00
Nicolas Bouilleaud ecac9ccc94 Remove all scripts for json-based i18n 2019-04-19 14:46:26 +02:00
Nicolas Bouilleaud 3a6317c25a Rework i18n using Symfony/translation
Reimplement the __() and __f() functions using Symfony.
2019-04-19 14:46:26 +02:00
Thomas Citharel 705abe6cd6 Merge branch 'ci-migration-testing' into 'develop'
ci: some cleanup of the gitlab-ci file and testing migrations on MySQL

See merge request framasoft/framadate/framadate!388
2019-04-19 14:32:56 +02:00
PaulR c26fef6b39 CI: cleanup the gitlab-ci yml file and test MySQL and PostgreSQL migrations
This commit splits the `test` job into three separate jobs:
- test
- lint
- db:migrate

It also factorise some code, and remove useless use of git checkout
2019-04-19 14:32:56 +02:00
Thomas Citharel d87ca39693 Merge branch 'locales/pot-change-after-387' into 'develop'
Update pot after !387

See merge request framasoft/framadate/framadate!392
2019-04-19 13:51:48 +02:00
Thomas Citharel 2c056068de
Update pot after !387
(also make sure it's pushed to Zanata)

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-19 13:50:50 +02:00
Thomas Citharel 36bff4d5cc Merge branch 'localize-date-placeholders' into 'develop'
i18n: localize the date format placeholders

See merge request framasoft/framadate/framadate!387
2019-04-19 13:38:41 +02:00
Thomas Citharel b63e90e53f Merge branch 'docker/smtp' into 'develop'
Add development SMTP server in docker-compose

See merge request framasoft/framadate/framadate!391
2019-04-19 13:35:21 +02:00
Liquidsoul e03ad97382
Add development SMTP server 2019-04-19 12:59:13 +02:00
Pierre de La Morinerie fe5d28e394 i18n: use full nouns in date placeholders 2019-04-18 15:44:06 +02:00
Pierre de La Morinerie 78714069f0 i18n: localize the date format placeholders
For instance, when using French, display the date input placeholder
as "jj/mm/aaaa" (instead of "dd/mm/yyyy").

This doesn't change the way dates are actually parsed – only what is
displayed to the user changes.
2019-04-18 12:18:02 +02:00
Thomas Citharel c9382973ca Merge branch 'locales/add-locales-for-385' into 'develop'
Add locales for !385

See merge request framasoft/framadate/framadate!386
2019-04-18 12:07:56 +02:00
Thomas Citharel a0d29ea0d5
Add locales for !385
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-18 12:06:55 +02:00
Thomas Citharel 4bd8e248e0 Merge branch 'fix-expiry-date' into 'develop'
locales: fix missing "Expiry date" key

See merge request framasoft/framadate/framadate!385
2019-04-18 12:00:34 +02:00
Pierre de La Morinerie ee4053c524 locales: fix missing "Expiry date" key 2019-04-18 11:56:48 +02:00
Thomas Citharel 93d9f44bb1 Merge branch 'mail/templates' into 'develop'
Extract mail content creation and sending into the NotificationService

See merge request framasoft/framadate/framadate!382
2019-04-18 11:24:07 +02:00
Liquidsoul c72dfa17b8
Extract mail creation for FindPollsByMail into NotificationService 2019-04-17 17:41:21 +02:00
Liquidsoul 26d3552276
Move edit link mail notification to NotificationService 2019-04-17 17:41:21 +02:00
Liquidsoul fb05b82e66
Use NotificationService to send poll creation mails 2019-04-17 17:41:20 +02:00
Liquidsoul 8dcea674e8
Add notificationservice->sendPollCreationMails 2019-04-17 17:41:20 +02:00
Liquidsoul ad8f8ab22c
Extract creation mails content into smarty templates 2019-04-17 17:41:20 +02:00
Liquidsoul 29e599983e
Add smarty dependency on NotificationService 2019-04-17 17:41:20 +02:00
Thomas Citharel 4f7e858ed3 Merge branch 'locales/fix-date-format' into 'develop'
Fix date format

See merge request framasoft/framadate/framadate!381
2019-04-17 17:32:01 +02:00
Thomas Citharel 3cebdfa1ea
Fix date format
Parsing and displaying

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-17 17:30:12 +02:00
Thomas Citharel 89d94fb16b Merge branch 'mm/framadate-bug/datepicker-disable-selected-dates' into 'develop'
Fix already selected days not being highlighted

Closes #361

See merge request framasoft/framadate/framadate!380
2019-04-17 16:22:09 +02:00
m f2b57fec9b
correct #361 2019-04-17 16:15:14 +02:00
Thomas Citharel d50305afb3 Merge branch 'workaround-datepicker-below-modal-safari' into 'develop'
Workaround the datepicker appearing below the add_days modal on Safari

See merge request framasoft/framadate/framadate!379
2019-04-17 16:12:30 +02:00
Thomas Citharel 1062f72d56 Merge branch 'fix-trads' into 'develop'
scripts: fix translations

See merge request framasoft/framadate/framadate!377
2019-04-17 16:01:03 +02:00
Nicolas Bouilleaud 61c449205c Workaround the datepicker appearing below the add_days modal on Safari
The correct solution would probably be to upgrade the version of bootstrap and bootstrap-datepicker that we use

My understandng of the bug is:
* bootstrap-datepicker finds the first ancestor whose z-index isn’t auto, and sets its own z-index to the found elemenents z-index + 10.
* On Firefox and Chrome, the modal element is returned (its z-index is 1050)
* On Safari, the modal-dialog element is found. Its z-index is auto, but Safari reports this as '0', which makes the z-index of the datepicker 10.
2019-04-17 15:51:00 +02:00
Pierre de La Morinerie 20c324a018 templates: fix localisation of a string 2019-04-17 15:09:08 +02:00
Pierre de La Morinerie d110ee558f scripts: fix translations
Translations with a comma in the key are rewritten to be on a single
line.

But this failed when the comma was just preceded by a dot (like in
`etc.,`).

Fix the script so that it works even in those cases.
2019-04-17 12:51:37 +02:00
Thomas Citharel 723cf83484 Merge branch 'locale/update' into 'develop'
Update locales

See merge request framasoft/framadate/framadate!376
2019-04-17 10:18:23 +02:00
Thomas Citharel b73ce470b8
Update locales
Following !373

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-17 10:16:53 +02:00
Thomas Citharel fd2d00b2f6 Merge branch 'feature/prevent-leaving' into 'develop'
Prevent leaving in case of vote started or comment written but not submitted

See merge request framasoft/framadate/framadate!373
2019-04-17 10:11:11 +02:00
Thomas Citharel ec939a8a30 Merge branch 'framadate-bug/limit_end_date_when_updating_poll' into 'develop'
Limit end date when updating poll (#336)

See merge request framasoft/framadate/framadate!372
2019-04-16 16:14:15 +02:00
Thomas Citharel 0fb673457b Merge branch 'improvement/better-default-domain-with-docker' into 'develop'
Improvement/better default domain with docker

See merge request framasoft/framadate/framadate!371
2019-04-16 16:13:23 +02:00
Clermonté 2e87e1f761
Fixed the previous fix about end_date bug in case of too far date. 2019-04-16 16:06:00 +02:00
Clermonté 38552da738
Updated AdminPollService to limit end_date (#336). 2019-04-16 16:06:00 +02:00
Paul B 6d9130ef84
refacto(utils): remove a useless intermediate variable
This is the commit of the suggestion I made here:
https://framagit.org/framasoft/framadate/framadate/merge_requests/344/diffs#note_399810
2019-04-16 16:04:27 +02:00
Marc Karassev 3ec6435323
Made docker-compose generate a configuration forcing the domain to be set to $_SERVER['SERVER_NAME'] in order to properly get assets while accessing the website from another device. 2019-04-16 15:58:57 +02:00
Thomas Citharel 4b8de04f60 Merge branch 'locales/fix-again' into 'develop'
Fix missing locales (again…)

See merge request framasoft/framadate/framadate!370
2019-04-16 15:58:12 +02:00
Thomas Citharel 5fb1887497
Fix missing locales (again…)
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-16 15:57:00 +02:00
Marc Karassev b58e61c575
Handled domain determination when constant APP_URL is defined but empty.
Now the bahavior matches the comments in config.php:
// Fully qualified domain name of your webserver.
// If this is unset or empty, the servername is determined automatically.
// You *have to set this* if you are running Framadate behind a reverse proxy.
// const APP_URL = '<www.mydomain.fr>';
2019-04-16 15:56:28 +02:00
Antonin d819327c61 Prevent leaving in case of vote started or comment written but not submitted 2019-04-16 15:36:57 +02:00
Thomas Citharel 324ac9dfcb Merge branch 'fix/mail/text/linebreaks' into 'develop'
Fix line breaks in mails text part

See merge request framasoft/framadate/framadate!368
2019-04-16 15:15:16 +02:00
Thomas Citharel 409c5e3d2b Merge branch 'check-translations' into 'develop'
i18n: adds a check on missing translation keys in locale/*.json file

See merge request framasoft/framadate/framadate!369
2019-04-16 15:11:21 +02:00
Paul B 64f086c04f
i18n: adds a check on missing translation keys in locale/*.json file
This commit adds a script to check all keys from translation files to
make sure no missing keys / no translation files are incorrect.

Even if missing keys should not happen (because we use a script to add
keys automatically) this script made helped to understand that all
three `ar`, `eo` and `fr_FR` translation files were completly off
compared to the `en.json` file.

I believe we can safely delete `ar`, `eo` and `fr_FR` locales from
Zanata (because those languages are not set in the ALLOWED_LANGUAGES
config array). ⚠️ this needs to be check in the production `config.php`
file ⚠️.

Deleting the `fr_FR` locale file should solve both !358 and #395

closes !358 #395
2019-04-16 14:55:15 +02:00
Liquidsoul 46e0edc3dd
Replace html <br/> tags with \n in mail text 2019-04-16 14:11:07 +02:00
Thomas Citharel 80336b431e Merge branch 'locale/update-locales' into 'develop'
Pull locales from Zanata

See merge request framasoft/framadate/framadate!367
2019-04-16 12:33:15 +02:00
Thomas Citharel bd6f2ab1d2
Pull locales from Zanata
Including changes from !366

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2019-04-16 12:31:47 +02:00
Thomas Citharel 6a4b2a44a1 Merge branch 'framadate-feature/add_version' into 'develop'
Add version in the pages footer

See merge request framasoft/framadate/framadate!366
2019-04-16 12:19:57 +02:00
Liquidsoul 7703360d44
Add new localization key for the version in locale/en.json 2019-04-16 12:00:35 +02:00
Liquidsoul 5268a5bd7a
Use placeholder injection for the version string 2019-04-16 12:00:35 +02:00
Mindiell 2624f2da10
#338: Affichage de la version 2019-04-16 12:00:35 +02:00
Thomas Citharel 0493a05526 Merge branch 'csv-export-bom-header' into 'develop'
CSV export: add UTF-8 BOM header

See merge request framasoft/framadate/framadate!365
2019-04-16 11:43:07 +02:00
Thomas Citharel 69d00042e6 Merge branch 'fix-email-encoding-issues' into 'develop'
Fix email issues

Closes #419 et #416

See merge request framasoft/framadate/framadate!364
2019-04-16 11:37:21 +02:00
Cyril Roelandt f626d29843 CSV export: add UTF-8 BOM header.
When exporting a poll to CSV, no BOM character is written. Most applications can
properly detect the required encoding, but some do not, and might find it
helpful to be able to read a BOM character.

--
Comment added 2019-04-16, nico@bou.io:

The issue is specifically with Excel that assumes Latin-1 by default, but switches to utf-8 if a BOM is present. On the other hand, LibreOffice and other apps seem to behave correctly with a BOM.
2019-04-16 11:32:42 +02:00
Paul B a51ee59d1c
mailservice: use a custom html2text callback for plain text emails
The current `html2text` function from the PHPMailer library strips all
html tags and only keeps the inner html.

In the case of <a> tags we need to share the `href` attribute to the
user when he reads his mail in plain text format.

Fixes #419
2019-04-15 18:43:27 +02:00
Nicolas Bouilleaud 7b0e42bec4 Use the new named constant for 'utf-8' 2019-04-15 17:53:53 +02:00
Paul B ed223f2176
deps: bump the PHPMailer library version from 6.0.5 to 6.0.7
This includes a small security fix (in v6.0.6) and keeps us up-to-date
with the vendor PHPMailer library.
2019-04-15 17:24:20 +02:00
Nicolas Bouilleaud 6b91a226ba Set the MailService Charset before setting the body
msgHTML builds the text/plain part by converting the passed html to text in the current CharSet; the default Charset being iso-8859-1, most non-ascii characters are lost.

fixes #416
2019-04-15 17:22:09 +02:00
Thomas Citharel b272a5a7a1 Merge branch 'fix-poll-entry-modification' into 'develop'
refacto(logService): add a new logEntries method to the log service

See merge request framasoft/framadate/framadate!363
2019-04-15 16:48:05 +02:00
Paul B e8620026cf
refacto(logService): add a new logEntries method to the log service
As a followup to #362 this is a small addition to the log service when
we want to log a list of entries into a single message.

The log service can itself make sure to escape commas instead of
expecting callers to do so.
2019-04-15 16:07:32 +02:00
Thomas Citharel 5f7388cddb Merge branch 'fix-poll-entry-modification' into 'develop'
fix(admin/add_column): stop modifying user input in stored data

Closes #384

See merge request framasoft/framadate/framadate!362
2019-04-15 15:16:47 +02:00
Paul B dc164c4d79
fix(admin/add_column): stop modifying user input in stored data
An [earlier
change](892fa11373)
modifed the user's input when creating new columns in an existing
Poll.

Indeed all `,` comma were replaced by `-` dashes and the user inputs
was saved as such.

It seems the rational behind it was to keep the existing "formatting"
of log messages where different elements were separated by commas.

This commit makes sure to not strip the users' data but only the
logged message.

Fixes #384
2019-04-15 15:05:26 +02:00
Thomas Citharel e13f66eb4d Merge branch 'fix-fin_sondage_autre-disabled-style' into 'develop'
Remove the “disabled” class from fin_sondage_autre

See merge request framasoft/framadate/framadate!361
2019-04-15 14:59:49 +02:00
Nicolas Bouilleaud 3071455882 Remove the “disabled” class from fin_sondage_autre
Currently, the button still looks disabled even when more than one choice is filled.

In classic_poll.js, the disabled *property* is set/unset according to isSubmitChoicesAvalaible(). The disabled *class* is not changed after being set in the template.
In the bootstrap css, either the disabled class or the disabled gray out the button. Let’s just use the disabled property.
2019-04-15 12:03:13 +02:00
Thomas Citharel a29c25883a Merge branch 'fix-settime-exception' into 'develop'
Update german translation

Closes #368

See merge request framasoft/framadate/framadate!351
2019-04-15 11:42:02 +02:00
Thomas Citharel 6090568446 Merge branch 'patch-2' into 'develop'
Add new file (breton translation for bootstrap-datepicker)

See merge request framasoft/framadate/framadate!357
2019-02-21 10:33:57 +01:00
Florent Grouin e78ad00d28 Add new file (breton translation for bootstrap-datepicker) 2019-02-20 13:52:07 +01:00
JosephK c3e29e7e82 .git folder deleted before creating latest.zip 2019-01-07 08:42:11 +01:00
Thomas Citharel c4551c2e71 Merge branch 'feature/force-https' into 'develop'
Allow overriding HTTPS

See merge request framasoft/framadate/framadate!346
2018-11-30 11:25:51 +01:00
Tai Kedzierski 179b048f59 Removed redundant truth check. 2018-11-30 11:19:50 +01:00
Maik Hummel b549beabde Update german translation (fixes #368) 2018-11-20 16:51:06 +01:00
Thomas Citharel cbf6fbffc7 Merge branch 'patch-1' into 'develop'
Update INSTALL.md : link actualization

See merge request framasoft/framadate/framadate!348
2018-10-25 15:08:00 +02:00
Thierry Munoz 4e1a0e944a Update INSTALL.md : link actualization 2018-10-25 15:03:43 +02:00
Thomas Citharel 47261c5677 Merge branch 'patch-1' into 'develop'
Update README.md : links actualization

See merge request framasoft/framadate/framadate!347
2018-10-25 14:43:13 +02:00
Tai Kedzierski 537037154e fix erroneous comparator in entrypoint 2018-10-25 13:28:14 +01:00
Thierry Munoz 49c1ec8de7 Update README.md : links actualization 2018-10-25 14:24:10 +02:00
Tai Kedzierski 4242fe2914 Allow overriding HTTPS
In the case where the proxy does not pass `HTTP_X_FORWARDED_PROTO`, we need a way to explicitly request `https://` scheme on callbacks.

This change adds a constant `FORCE_HTTPS` which can be used to forcibly override automatic detection of HTTPS usage, when set.
2018-10-24 18:07:09 +01:00
Thomas Citharel 76db7de73c Merge branch 'bug/fix_incorrect_display_email_subject' into 'develop'
Fix wrong display of email subject with a date poll

Closes #375

See merge request framasoft/framadate!342
2018-10-10 10:37:06 +02:00
Delattre Yoann b5effb8eaa fix parenthesis 2018-10-10 10:27:41 +02:00
Yoann 28a82388cf Fix wrong display of email subject with a date poll 2018-10-10 10:03:53 +02:00
Thomas Citharel 49bd7c9169 Merge branch 'install-fixes' into 'develop'
Install fixes

See merge request framasoft/framadate!339
2018-10-09 11:52:03 +02:00
Thomas Citharel f313699149 Install fixes
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2018-10-09 11:50:25 +02:00
Thomas Citharel d29b703e0e Merge branch 'bug/369-template-syntax-error-creation-step3' into 'develop'
Fixed syntax error by escaping a quote.

Closes #369

See merge request framasoft/framadate!335
2018-09-11 15:43:29 +02:00
Marc Karassev 14b00691c7 Fixed syntax error by escaping a quote. 2018-09-11 15:38:50 +02:00
Thomas Citharel 7baf995f91 Fix escaping apostroph 2018-08-30 14:14:23 +02:00
Thomas Citharel d0207ebb5b Merge branch 'zanata-use-git-branch' into 'develop'
[zanata] Use git branch to specify the version of zanata's project

See merge request framasoft/framadate!333
2018-08-17 14:02:07 +02:00
Luc Didry ff88628acf
[zanata] Use git branch to specify the version of zanata's project 2018-08-17 13:58:43 +02:00
Thomas Citharel 3402571e85 Merge branch 'work-on-locales' into 'develop'
Working on locales

See merge request framasoft/framadate!332
2018-08-17 13:47:35 +02:00
Luc Didry ed95625cbe
[i18n] Update br, de and fr translations 2018-08-14 15:01:23 +02:00
Luc Didry 6b3c5f0610
WIP Working on locales 2018-08-14 14:35:08 +02:00
Thomas Citharel 5e43c1974d Merge branch 'master' into develop 2018-08-14 14:26:15 +02:00
Luc Didry f691694ff1
[zanata] Update Zanata URL 2018-08-14 13:29:37 +02:00
Thomas Citharel 19bec88184 Merge branch 'docker' into 'develop'
Introduce Docker Image

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

Run migrations on entrypoint

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

Finalize Docker configuration

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

turn on pretty urls

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

Make framadate await database container

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

Add missing rewrite rule to apache config

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

Finalize

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

Closes #331

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

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

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

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

Closes #326

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

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

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

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

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

Closes #324

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

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

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

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

Fixed typo

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

Closes #255

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

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

Fixup !

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

Fixup

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

added error message to locale

Remove unecessary variable

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

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

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

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

Closes #90 et #179

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

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

CS

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

Add sqlite to CI and execute migration in test env

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

Typo

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

SQLite is already inside the image...

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

Rebase two new migrations

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

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

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

CS

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

Move doctrine command path inside CI

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

Move abstract migration class to correct namespace and remove unused command

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

CS

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

Check for legacy migration table existence

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

Check if legacy migration table exists before deleting it

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

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

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

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

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

Closes #38

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

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

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

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

code style

modify error message on session expiration

new local text

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

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

Closes #235

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

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

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

MAJ de VoteRepository : méthode insert et update

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

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

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

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

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

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

Extension du traitement pour les sondages classic

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

Changement des input type=text en type=email

Corrections automatiques pour passage pipelines

Corrections suite aux remarques sur la merge request

Corrections 2 suite aux remarques sur la merge request

Corrections 3 suite aux remarques sur la merge request

Modif BDD : ajout colonne collect_mail dans poll

Modif classes Form, PollRepository

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

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

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

Update create poll string and put a danger background on warning

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

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

Also, a CSS tweak and cleanup

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

CS

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

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

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

Use fork-awesome minified css

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

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

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

1
.gitignore vendored
View File

@ -25,3 +25,4 @@ Thumbs.db
.project
.idea/
*.iml
test_database.sqlite

View File

@ -1,34 +1,91 @@
image: framasoft/framadate-ci
variables:
TESTING_IMAGE: framasoft/framadate-ci:php-7.2
image: ${TESTING_IMAGE}
cache:
key: "${TESTING_IMAGE}:${CI_PROJECT_ID}"
paths:
- vendor/
before_script:
- composer install -o --no-interaction --no-progress --prefer-dist
stages:
- test
- deploy
- beta
- funky
# Run php-cs-fixer and phpunit on all branches
# Run 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
cache:
paths:
- vendor/
check-trad:
# Run php-cs-fixer on all branches
lint:
stage: test
allow_failure: true
script:
- if [ -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then echo "*** Unable to check if translations need to be pulled, exiting ***"; exit 1; fi
- export ORIG=$(git diff-files --shortstat)
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make push-locales; fi
- git status > /dev/null 2>&1
- export CHANGES=$(git diff-files --shortstat)
- if [[ $CHANGES != $ORIG ]]; then echo "*** There is changes in locales ***"; echo "*** You need to do `make pull-locales` in your repo ***"; exit 1; fi
only:
- develop
- php vendor/bin/php-cs-fixer fix --verbose --dry-run --diff
# Run doctrine migrations with MySQL
.db:migration:mysql:
stage: test
variables:
APP_ENV: "test"
MYSQL_DATABASE: "fd_test"
MYSQL_ROOT_PASSWORD: "coucou"
FRAMADATE_DB_DRIVER: 'pdo_mysql'
FRAMADATE_DB_HOST: "mysql"
FRAMADATE_DB_NAME: "${MYSQL_DATABASE}"
FRAMADATE_DB_USER: "root"
FRAMADATE_DB_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
script:
- docker-php-ext-install "${FRAMADATE_DB_DRIVER}"
- bin/doctrine migrations:status -vvv
- bin/doctrine migrations:migrate --no-interaction -vvv
- bin/doctrine migrations:status -vvv
db:migration:mysql:5.6:
extends: .db:migration:mysql
services:
- 'mysql:5.6'
db:migration:mysql:5.7:
extends: .db:migration:mysql
services:
- 'mysql:5.7'
db:migration:mysql:8:
extends: .db:migration:mysql
services:
- name: 'mysql:8'
command: ['--default-authentication-plugin=mysql_native_password']
# Run doctrine migrations with PostgreSQL
.db:migration:postgresql:
stage: test
variables:
APP_ENV: "test"
POSTGRES_DB: "fd_test"
POSTGRES_USER: "marmotte"
POSTGRES_PASSWORD: "sleeping"
FRAMADATE_DB_DRIVER: 'pdo_pgsql'
FRAMADATE_DB_HOST: "postgres"
FRAMADATE_DB_NAME: "${POSTGRES_DB}"
FRAMADATE_DB_USER: "${POSTGRES_USER}"
FRAMADATE_DB_PASSWORD: "${POSTGRES_PASSWORD}"
script:
- docker-php-ext-install "${FRAMADATE_DB_DRIVER}"
- bin/doctrine migrations:status -vvv
- bin/doctrine migrations:migrate --no-interaction -vvv
- bin/doctrine migrations:status -vvv
db:migration:postgresql:9.6:
extends: .db:migration:postgresql
services:
- 'postgres:9.6'
# Create artifacts on master
pages:
@ -42,6 +99,7 @@ pages:
- mv `ls -A | grep -v framadate` ./framadate
- find framadate/ -type d -exec chmod 750 {} \;
- find framadate/ -type f -exec chmod 640 {} \;
- rm -rf framadate/.git
- zip -r latest.zip framadate
- mkdir .public
- cp latest.zip .public
@ -58,11 +116,8 @@ pages:
beta:
stage: beta
script:
- git checkout develop
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
- composer dump-autoload --optimize --no-dev --classmap-authoritative
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make pull-locales; fi
- mkdir .public
- cp -r * .public
- cp -r .git .public
@ -80,7 +135,6 @@ beta:
funky:
stage: funky
script:
- git checkout funky
- composer install
- mkdir tpl_c
- mkdir .public
@ -94,13 +148,3 @@ funky:
- if [ ! -z ${DEPLOYEMENT_KEY+x} ]; then rsync -a --delete --exclude admin/.stdout.log --exclude admin/.htpasswd --exclude app/inc/config.php --exclude stats/ --exclude error/ public/ ${DEPLOYEMENT_USER}@${DEPLOYEMENT_HOST}:../../web/; fi
only:
- funky
# Push new translations strings to https://trad.framasoft.org
trads:
stage: deploy
image: framasoft/push-trad:latest
script:
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then mkdir -p ${HOME}/.config; echo -e "${ZANATA_CONFIG_FRAMABOT}" > ${HOME}/.config/zanata.ini; fi
- if [ ! -z ${ZANATA_CONFIG_FRAMABOT+x} ]; then make push-locales; fi
only:
- develop

View File

@ -38,7 +38,8 @@ return PhpCsFixer\Config::create()
->exclude([
'vendor',
'var',
'web'
'web',
'tpl_c',
])
->in(__DIR__)
)

View File

@ -1,9 +0,0 @@
#!/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

View File

@ -1,40 +0,0 @@
#!/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,3 +1,7 @@
This changelog file is **deprecated**. For an up-to-date changelog, please check [the tags](https://framagit.org/framasoft/framadate/tags).
---------------------
# Changelog de framadate
## Version 1.0 (Erik - Markus - Ecmu - Julien - Imre - Luc - Pierre - Antonin - Olivier)

7
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,7 @@
# Contributing
Please report issues on <https://framagit.org/framasoft/framadate/issues>
If you made a change and want it to be available in official repository, merge requests are welcome!
Read the [guidelines](https://framagit.org/framasoft/framadate/wikis/coding) to submit your changes.

View File

@ -1 +1 @@
# [Now available here](https://framagit.org/framasoft/framadate/wikis/home)
# [Now available here](https://framagit.org/framasoft/framadate/framadate/wikis/home)

View File

@ -1,12 +1,8 @@
locales:
json2po -P -i locale/en.json -t locale/en.json -o po/framadate.pot
push-locales: locales
zanata-cli -q -B push
push-locales:
zanata-cli -q -B push --errors --project-version `git branch | grep \* | cut -d ' ' -f2-`
pull-locales:
zanata-cli -q -B pull --min-doc-percent 50
./.po2json.sh
zanata-cli -q -B pull --min-doc-percent 50 --project-version `git branch | grep \* | cut -d ' ' -f2-`
stats-locales:
zanata-cli -q stats
zanata-cli -q stats --project-version `git branch | grep \* | cut -d ' ' -f2-`

View File

@ -7,23 +7,23 @@
---
# Installation
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/wikis/home>
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/framadate/wikis/home>
# Contribute
## Code
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/coding>
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/wikis/coding>
# Traductions
Follow the instructions on <https://framagit.org/framasoft/framadate/wikis/translating>
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/wikis/translating>
# Used libraries
* PHP [PHP 5.6](http://php.net)
* Templating [Smarty](http://www.smarty.net/),
* I18N [o80-i18n](https://github.com/olivierperez/o80-i18n)
* Database: PostgreSQL ou [MySQL 5.5](https://dev.mysql.com/downloads/mysql/5.5.html)
* i18n [Symfony/Translation](https://github.com/symfony/Translation)
* Database: PostgreSQL 9.6 or [MySQL 5.6+](https://dev.mysql.com/downloads/mysql/5.6.html)/[MariaDB 10.1+](https://www.debian.org/releases/stable/mips/release-notes/ch-whats-new.en.html#mariadb-replaces-mysql)
---

View File

@ -17,12 +17,7 @@
* 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';
@ -39,12 +34,10 @@ $is_admin = false;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService);
$securityService = new SecurityService();
$inputService = Services::input();
$notificationService = Services::notification();
$pollService = Services::poll();
$securityService = Services::security();
/* PAGE */
/* ---- */
@ -62,7 +55,7 @@ if (!empty($_POST['poll_admin'])) {
}
if (!$poll) {
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
$message = new Message('error', __('Error', "This poll doesn't exist!"));
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) {
$message = new Message('error', __('Password', 'Wrong password'));
} else {
@ -77,7 +70,7 @@ if (!$poll) {
// Add comment
$result = $pollService->addComment($poll_id, $name, $comment);
if ($result) {
$message = new Message('success', __('Comments', 'Comment added'));
$message = new Message('success', __('Comments', 'Comment saved'));
$notificationService->sendUpdateNotification($poll, NotificationService::ADD_COMMENT, $name);
} else {
$message = new Message('danger', __('Error', 'Comment failed'));

View File

@ -18,18 +18,14 @@
*/
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($connect, $logService);
$mailService = Services::mail();
$notificationService = Services::notification();
$pollService = Services::poll();
$sessionService = Services::session();
$result = false;
$message = null;
@ -47,7 +43,7 @@ $token_form_value = empty($_POST['token']) ? null : $_POST['token'];
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
if (is_null($poll) || $config['use_smtp'] === false || is_null($token) || is_null($token_form_value)
|| !$token->check($token_form_value) || is_null($editedVoteUniqueId)) {
$message = new Message('error', __('Error', 'Something is going wrong...'));
$message = new Message('error', __('Error', 'Something has gone wrong...'));
}
if (is_null($message)) {
@ -70,16 +66,7 @@ if (is_null($message)) {
}
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);
$notificationService->sendEditedVoteNotification($email, $poll, $poll_id, $editedVoteUniqueId);
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());

View File

@ -25,7 +25,7 @@ define('ROOT_DIR', __DIR__ . '/../');
/**
* Checking for missing vendors.
*/
if (!file_exists(ROOT_DIR . 'vendor/autoload.php') || !file_exists(ROOT_DIR . 'vendor/o80/i18n/src/shortcuts.php')) {
if (!file_exists(ROOT_DIR . 'vendor/autoload.php')) {
die ("ERROR: You should use <code>composer install</code> to fetch dependant libraries.");
}
@ -33,7 +33,6 @@ if (!file_exists(ROOT_DIR . 'vendor/autoload.php') || !file_exists(ROOT_DIR . 'v
* 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();
@ -112,7 +111,7 @@ if (!file_exists(ROOT_DIR . COMPILE_DIR)) {
if (file_exists($conf_filename)) {
$messages[] = new Message('info', __('Check','The config file exists.'));
} elseif (is_writable($inc_directory)) {
$messages[] = new Message('info', __('Check','The config file directory (%s) 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));
}
@ -177,14 +176,14 @@ usort($messages, 'compareCheckMessage');
<div class="row">
<form method="get" action="" class="hidden-print">
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2">
<select name="lang" class="form-control" title="<?=__('Language selector', 'Select the language')?>" >
<select name="lang" class="form-control" title="<?=__('Language selector', 'Select language')?>" >
<?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?>
<option lang="fr" <?php if (substr($lang_key, 0, 2)===$locale) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
<?php } ?>
</select>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="<?=__('Language selector', 'Select the language')?>">OK</button>
</span>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="<?=__('Language selector', 'Select language')?>">OK</button>
</span>
</div>
</form>
</div>

22
admin/cleanDemo.php Normal file
View File

@ -0,0 +1,22 @@
<?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)
*/
include_once __DIR__ . '/../app/inc/init.php';
Services::purge()->cleanDemoPoll();

View File

@ -28,6 +28,7 @@ if (is_file(CONF_FILENAME)) {
}
$error = null;
$result['details'] = null;
$installService = new InstallService();
if (!empty($_POST)) {

View File

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

View File

@ -17,10 +17,6 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Services\AdminPollService;
use Framadate\Services\LogService;
use Framadate\Services\PollService;
use Framadate\Services\SecurityService;
use Framadate\Services\SuperAdminService;
include_once __DIR__ . '/../app/inc/init.php';
@ -49,11 +45,10 @@ $poll_to_delete = null;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$adminPollService = Services::adminPoll();
$pollService = Services::poll();
$securityService = Services::security();
$superAdminService = new SuperAdminService();
$securityService = new SecurityService();
/* GET */
/*-----*/

View File

@ -17,11 +17,6 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Services\InputService;
use Framadate\Services\LogService;
use Framadate\Services\PurgeService;
use Framadate\Services\SecurityService;
include_once __DIR__ . '/../app/inc/init.php';
include_once __DIR__ . '/../bandeaux.php';
@ -33,10 +28,9 @@ $message = null;
/* Services */
/*----------*/
$logService = new LogService();
$purgeService = new PurgeService($connect, $logService);
$securityService = new SecurityService();
$inputService = new InputService();
$inputService = Services::input();
$purgeService = Services::purge();
$securityService = Services::security();
/* POST */
/*-----*/
@ -57,4 +51,4 @@ $smarty->assign('crsf', $securityService->getToken('admin'));
$smarty->assign('title', __('Admin', 'Purge'));
$smarty->display('admin/purge.tpl');
$smarty->display('admin/purge.tpl');

View File

@ -16,20 +16,17 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Doctrine\DBAL\DBALException;
use Framadate\Editable;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\ConcurrentVoteException;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\Exception\SlotAlreadyExistsException;
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';
@ -46,13 +43,11 @@ $editingVoteId = 0;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$adminPollService = new AdminPollService($connect, $pollService, $logService);
$inputService = new InputService();
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = new NotificationService($mailService);
$sessionService = new SessionService();
$adminPollService = Services::adminPoll();
$inputService = Services::input();
$notificationService = Services::notification();
$pollService = Services::poll();
$sessionService = Services::session();
/* PAGE */
/* ---- */
@ -67,7 +62,7 @@ if (!empty($_GET['poll'])) {
if ($poll) {
$poll_id = $poll->id;
} else {
$smarty->assign('error', __('Error', 'This poll doesn\'t exist !'));
$smarty->assign('error', __('Error', "This poll doesn't exist!"));
$smarty->display('error.tpl');
exit;
}
@ -80,8 +75,8 @@ $messagePollCreated = $sessionService->get("Framadate", "messagePollCreated", FA
if ($messagePollCreated) {
$sessionService->remove("Framadate", "messagePollCreated");
$message = new Message('success', __('adminstuds', 'The poll is created.'));
$message = new Message('success', __('adminstuds', 'The poll was created.'));
}
// -------------------------------
@ -219,12 +214,17 @@ $selectedNewVotes = [];
if (!empty($_POST['save'])) { // Save edition of an old vote
$name = $inputService->filterName($_POST['name']);
if(empty($_POST['mail']) || $inputService->filterMail($_POST['mail'])===false) {
$mail = null;
} else {
$mail = $inputService->filterMail($_POST['mail']);
}
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
if (empty($editedVote)) {
$message = new Message('danger', __('Error', 'Something is going wrong...'));
$message = new Message('danger', __('Error', 'Something has gone wrong...'));
}
if (count($choices) !== count($_POST['choices'])) {
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
@ -233,14 +233,14 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message === null) {
// Update vote
try {
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash, $mail);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote updated'));
} else {
$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!'));
$message = new Message('danger', __('Error', "The name you've chosen already exists in this poll!"));
} catch (ConcurrentEditionException $cee) {
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
} catch (ConcurrentVoteException $cve) {
@ -249,6 +249,11 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
}
} elseif (isset($_POST['save'])) { // Add a new vote
$name = $inputService->filterName($_POST['name']);
if(empty($_POST['mail']) || $inputService->filterMail($_POST['mail'])===false) {
$mail = null;
} else {
$mail = $inputService->filterMail($_POST['mail']);
}
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
$slots_hash = $inputService->filterMD5($_POST['control']);
@ -262,7 +267,7 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
if ($message === null) {
// Add vote
try {
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash, $mail);
if ($result) {
$message = new Message('success', __('adminstuds', 'Vote added'));
} else {
@ -392,12 +397,43 @@ if (isset($_GET['delete_column'])) {
}
if ($result) {
$message = new Message('success', __('adminstuds', 'Column removed'));
$message = new Message('success', __('adminstuds', 'Column deleted'));
} else {
$message = new Message('danger', __('Error', 'Failed to delete column'));
}
}
// -------------------------------
// Collect the mails of a column
// -------------------------------
if (isset($_GET['collect_mail'])) {
$column_str = strval(filter_input(INPUT_GET, 'collect_mail', FILTER_DEFAULT));
$column_str = strval(Utils::base64url_decode($column_str));
$column = intval($column_str);
$votes = $pollService->splitVotes($pollService->allVotesByPollId($poll_id));
$mails_yes = $mails_ifneedbe = $mails_no = [];
foreach ($votes as $vote) {
if (intval($vote->choices[$column]) === 2 && $vote->mail !== NULL) {
$mails_yes[] = $vote->mail;
} elseif (intval($vote->choices[$column]) === 1 && $vote->mail !== NULL) {
$mails_ifneedbe[] = $vote->mail;
} elseif($vote->mail !== NULL) {
$mails_no[] = $vote->mail;
}
}
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$smarty->assign('admin', true);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title . ' - ' . __('adminstuds', 'Collect the emails of the polled users for the choice'));
$smarty->assign('mails_yes', $mails_yes);
$smarty->assign('mails_ifneedbe', $mails_ifneedbe);
$smarty->assign('mails_no', $mails_no);
$smarty->display('display_mails.tpl');
exit;
}
// -------------------------------
// Add a slot
// -------------------------------
@ -424,18 +460,22 @@ if (isset($_POST['confirm_add_column'])) {
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);
$date = $inputService->filterDate($_POST['newdate']);
$time = $date->getTimestamp();
$newmoment = str_replace(',', '-', strip_tags($_POST['newmoment']));
$newmoment = strip_tags($_POST['newmoment']);
$adminPollService->addDateSlot($poll_id, $time, $newmoment);
} else {
$newslot = str_replace(',', '-', strip_tags($_POST['choice']));
$newslot = strip_tags($_POST['choice']);
$adminPollService->addClassicSlot($poll_id, $newslot);
}
$message = new Message('success', __('adminstuds', 'Choice added'));
} catch (SlotAlreadyExistsException $e) {
exit_displaying_add_column(new Message('danger', __f('Error', 'The column %s already exists', $e->getSlot())));
} catch (MomentAlreadyExistsException $e) {
exit_displaying_add_column(new Message('danger', __('Error', 'The column already exists')));
exit_displaying_add_column(new Message('danger', __f('Error', 'The column %s already exists with %s', $e->getSlot(), $e->getMoment())));
} catch (DBALException $e) {
exit_displaying_add_column(new Message('danger', __('Error', 'Error while adding a column')));
}
}
@ -443,14 +483,16 @@ if (isset($_POST['confirm_add_column'])) {
$slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
$comments = $pollService->allCommentsByPollId($poll_id);
$deletion_date = clone $poll->end_date;
$deletion_date->add(new DateInterval('P' . PURGE_DELAY . 'D'));
// Assign data to template
$smarty->assign('poll_id', $poll_id);
$smarty->assign('admin_poll_id', $admin_poll_id);
$smarty->assign('poll', $poll);
$smarty->assign('title', __('Generic', 'Poll') . ' - ' . $poll->title);
$smarty->assign('expired', strtotime($poll->end_date) < time());
$smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 86400);
$smarty->assign('expired', $poll->end_date < new DateTime());
$smarty->assign('deletion_date', $deletion_date);
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
$smarty->assign('votes', $pollService->splitVotes($votes));

View File

@ -0,0 +1,54 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
use Doctrine\DBAL\Migrations\AbstractMigration as DoctrineAbstractMigration;
use Doctrine\DBAL\Schema\Schema;
abstract class AbstractMigration extends DoctrineAbstractMigration
{
/**
* @param Schema $schema
* @param $class
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @return bool
*/
public function legacyCheck(Schema $schema, $class)
{
/**
* If there's no legacy table, we can go on
*/
if (!$schema->hasTable(Utils::table(MIGRATION_TABLE))) {
return false;
}
$migration_table = $schema->getTable(Utils::table(MIGRATION_TABLE));
/**
* We check the migration table
*/
if ($migration_table->hasColumn('name')) {
/** @var $stmt \Doctrine\DBAL\Driver\Statement */
$stmt = $this->connection->prepare('SELECT * FROM ' . Utils::table(MIGRATION_TABLE) . ' WHERE name = ?');
$stmt->execute([$class]);
return $stmt->rowCount() > 0;
}
return false;
}
}

View File

@ -24,34 +24,34 @@ class Choice
* Name of the Choice
*/
private $name;
/**
* All availables slots for this Choice.
* All available slots for this Choice.
*/
private $slots;
public function __construct($name='')
public function __construct($name = '')
{
$this->name = $name;
$this->slots = [];
}
public function addSlot($slot)
{
$this->slots[] = $slot;
}
public function getName()
{
return $this->name;
}
public function getSlots()
{
return $this->slots;
}
static function compare(Choice $a, Choice $b)
public static function compare(Choice $a, Choice $b)
{
return strcmp($a->name, $b->name);
}

View File

@ -0,0 +1,35 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
/**
* Class CollectMail
*
* Is used to specify the poll's edition permissions.
* @TODO : wait to use the SplEnum
*
* @package Framadate
*/
class CollectMail { // extends SplEnum
const NO_COLLECT = 0;
const COLLECT = 1;
const COLLECT_REQUIRED = 2;
const COLLECT_REQUIRED_VERIFIED = 3;
}

View File

@ -1,7 +1,20 @@
<?php
namespace Framadate\Exception;
class MomentAlreadyExistsException extends \Exception {
function __construct() {
class MomentAlreadyExistsException extends SlotAlreadyExistsException {
public $moment;
public function __construct($slot, $moment, $message = '')
{
parent::__construct($slot, $message);
$this->moment = $moment;
}
/**
* @return mixed
*/
public function getMoment()
{
return $this->moment;
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Framadate\Exception;
class SlotAlreadyExistsException extends \Exception {
public $slot;
public function __construct($slot, $message = '')
{
parent::__construct($message);
$this->slot = $slot;
}
/**
* @return mixed
*/
public function getSlot()
{
return $this->slot;
}
}

View File

@ -18,6 +18,8 @@
*/
namespace Framadate;
use DateTime;
class Form
{
public $title;
@ -26,9 +28,20 @@ class Form
public $admin_name;
public $admin_mail;
public $format;
/**
* @var DateTime
*/
public $end_date;
/**
* @var DateTime
*/
public $creation_date;
public $choix_sondage;
public $ValueMax;
public $errors = [];
/**
* Tells if users can modify their choices.
@ -82,13 +95,21 @@ class Form
*/
public $results_publicly_visible;
/**
* Tells if voters email addresses are collected or not.
* @var \Framadate\CollectMail
*/
public $collect_users_mail;
/**
* List of available choices
*/
private $choices;
public function __construct(){
public function __construct()
{
$this->editable = Editable::EDITABLE_BY_ALL;
$this->collect_users_mail = CollectMail::NO_COLLECT;
$this->clearChoices();
}

View File

@ -1,85 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate;
use PDO;
class FramaDB {
/**
* PDO Object, connection to database.
*/
private $pdo = null;
function __construct($connection_string, $user, $password) {
$this->pdo = new \PDO($connection_string, $user, $password);
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
}
/**
* @return \PDO Connection to database
*/
function getPDO() {
return $this->pdo;
}
/**
* Find all tables in database.
*
* @return array The array of table names
*/
function allTables() {
$result = $this->pdo->query('SHOW TABLES');
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
return $schemas;
}
function prepare($sql) {
return $this->pdo->prepare($sql);
}
function beginTransaction() {
$this->pdo->beginTransaction();
}
function commit() {
$this->pdo->commit();
}
function rollback() {
$this->pdo->rollback();
}
function errorCode() {
return $this->pdo->errorCode();
}
function errorInfo() {
return $this->pdo->errorInfo();
}
function query($sql) {
return $this->pdo->query($sql);
}
public function lastInsertId() {
return $this->pdo->lastInsertId();
}
}

View File

@ -1,75 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This migration adds the field hidden on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_hidden_In_poll_For_0_9 implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'Add column "hidden" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"');
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This migration adds the field receiveNewComments on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_receiveNewComments_For_0_9 implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'Add column "receiveNewComments" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'
AFTER `receiveNewVotes`');
}
}

View File

@ -1,77 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This migration adds the field uniqId on the vote table.
*
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'Add column "uniqId" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '`
ADD `uniqId` CHAR(16) NOT NULL
AFTER `id`,
ADD INDEX (`uniqId`) ;');
}
}

View File

@ -1,76 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* 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 {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return '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.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL ,
ADD `results_publicly_visible` TINYINT(1) NULL DEFAULT NULL');
}
}

View File

@ -1,70 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This migration sets Poll.end_date to NULL by default
*
* @package Framadate\Migration
* @version 1.1
*/
class Fix_MySQL_No_Zero_Date implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return '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.
*/
function preCondition(\PDO $pdo) {
$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|void if the execution succeeded
*/
function execute(\PDO $pdo) {
$pdo->exec('ALTER TABLE ' . Utils::table('poll') . ' MODIFY end_date TIMESTAMP NULL DEFAULT NULL;');
}
}

View File

@ -1,108 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* Class From_0_0_to_0_8_Migration
*
* @package Framadate\Migration
* @version 0.8
*/
class From_0_0_to_0_8_Migration implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'First installation of the Framadate application (v0.8)';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES like \'' . TABLENAME_PREFIX . '%\''); //issue187 : pouvoir installer framadate dans une base contenant d'autres tables.
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if there is no tables but the MIGRATION_TABLE one
$diff = array_diff($tables, [Utils::table(MIGRATION_TABLE)]);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `sondage` (
`id_sondage` char(16) NOT NULL,
`commentaires` text,
`mail_admin` varchar(128) DEFAULT NULL,
`nom_admin` varchar(64) DEFAULT NULL,
`titre` text,
`id_sondage_admin` char(24) DEFAULT NULL,
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`date_fin` timestamp NULL DEFAULT NULL,
`format` varchar(2) DEFAULT NULL,
`mailsonde` tinyint(1) DEFAULT \'0\',
`statut` int(11) NOT NULL DEFAULT \'1\' COMMENT \'1 = actif ; 0 = inactif ; \',
UNIQUE KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `sujet_studs` (
`id_sondage` char(16) NOT NULL,
`sujet` text,
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `comments` (
`id_comment` int(11) unsigned NOT NULL AUTO_INCREMENT,
`id_sondage` char(16) NOT NULL,
`comment` text NOT NULL,
`usercomment` text,
PRIMARY KEY (`id_comment`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
$pdo->exec('
CREATE TABLE IF NOT EXISTS `user_studs` (
`id_users` int(11) unsigned NOT NULL AUTO_INCREMENT,
`nom` varchar(64) NOT NULL,
`id_sondage` char(16) NOT NULL,
`reponses` text NOT NULL,
PRIMARY KEY (`id_users`),
KEY `id_sondage` (`id_sondage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
}
}

View File

@ -1,292 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
*
* @package Framadate\Migration
* @version 0.9
*/
class From_0_8_to_0_9_Migration implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'From 0.8 to 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
*/
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.8 are presents
$diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables);
return count($diff) === 0;
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$this->createPollTable($pdo);
$this->createCommentTable($pdo);
$this->createSlotTable($pdo);
$this->createVoteTable($pdo);
$pdo->beginTransaction();
$this->migrateFromSondageToPoll($pdo);
$this->migrateFromCommentsToComment($pdo);
$this->migrateFromSujetStudsToSlot($pdo);
$this->migrateFromUserStudsToVote($pdo);
$pdo->commit();
$this->dropOldTables($pdo);
return true;
}
private function createPollTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
`id` CHAR(16) NOT NULL,
`admin_id` CHAR(24) NOT NULL,
`title` TEXT NOT NULL,
`description` TEXT,
`admin_name` VARCHAR(64) DEFAULT NULL,
`admin_mail` VARCHAR(128) DEFAULT NULL,
`creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`end_date` TIMESTAMP NULL DEFAULT NULL,
`format` VARCHAR(1) DEFAULT NULL,
`editable` TINYINT(1) DEFAULT \'0\',
`receiveNewVotes` TINYINT(1) DEFAULT \'0\',
`active` TINYINT(1) DEFAULT \'1\',
PRIMARY KEY (`id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromSondageToPoll(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`id_sondage_admin`,
`titre`,
`commentaires`,
`nom_admin`,
`mail_admin`,
`date_creation`,
`date_fin`,
SUBSTR(`format`, 1, 1) AS `format`,
CASE SUBSTR(`format`, 2, 1)
WHEN \'+\' THEN 1
ELSE 0 END AS `editable`,
`mailsonde`,
CASE SUBSTR(`format`, 2, 1)
WHEN \'-\' THEN 0
ELSE 1 END AS `active`
FROM sondage');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('poll') . '`
(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$row->id_sondage_admin,
$this->unescape($row->titre),
$this->unescape($row->commentaires),
$this->unescape($row->nom_admin),
$this->unescape($row->mail_admin),
$row->date_creation,
$row->date_fin,
$row->format,
$row->editable,
$row->mailsonde,
$row->active
]);
}
}
private function createSlotTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`title` TEXT,
`moments` TEXT,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromSujetStudsToSlot(\PDO $pdo) {
$stmt = $pdo->query('SELECT * FROM sujet_studs');
$sujets = $stmt->fetchAll();
$slots = [];
foreach ($sujets as $sujet) {
$newSlots = $this->transformSujetToSlot($sujet);
foreach ($newSlots as $newSlot) {
$slots[] = $newSlot;
}
}
$prepared = $pdo->prepare('INSERT INTO ' . Utils::table('slot') . ' (`poll_id`, `title`, `moments`) VALUE (?,?,?)');
foreach ($slots as $slot) {
$prepared->execute([
$slot->poll_id,
$this->unescape($slot->title),
!empty($slot->moments) ? $this->unescape($slot->moments) : null
]);
}
}
private function createCommentTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` TEXT,
`comment` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromCommentsToComment(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`usercomment`,
`comment`
FROM `comments`');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->usercomment),
$this->unescape($row->comment)
]);
}
}
private function createVoteTable(\PDO $pdo) {
$pdo->exec('
CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`poll_id` CHAR(16) NOT NULL,
`name` VARCHAR(64) NOT NULL,
`choices` TEXT NOT NULL,
PRIMARY KEY (`id`),
KEY `poll_id` (`poll_id`)
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8');
}
private function migrateFromUserStudsToVote(\PDO $pdo) {
$select = $pdo->query('
SELECT
`id_sondage`,
`nom`,
REPLACE(REPLACE(REPLACE(`reponses`, 1, \'X\'), 2, 1), \'X\', 2) reponses
FROM `user_studs`');
$insert = $pdo->prepare('
INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`)
VALUE (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->nom),
$row->reponses
]);
}
}
private function transformSujetToSlot($sujet) {
$slots = [];
$ex = explode(',', $sujet->sujet);
$isDatePoll = strpos($sujet->sujet, '@');
$lastSlot = null;
foreach ($ex as $atomicSlot) {
if ($isDatePoll === false) { // Classic poll
$slot = new \stdClass();
$slot->poll_id = $sujet->id_sondage;
$slot->title = $atomicSlot;
$slots[] = $slot;
} else { // Date poll
$values = explode('@', $atomicSlot);
if ($lastSlot === null || $lastSlot->title !== $values[0]) {
$lastSlot = new \stdClass();
$lastSlot->poll_id = $sujet->id_sondage;
$lastSlot->title = $values[0];
$lastSlot->moments = count($values) === 2 ? $values[1] : '-';
$slots[] = $lastSlot;
} else {
$lastSlot->moments .= ',' . (count($values) === 2 ? $values[1] : '-');
}
}
}
return $slots;
}
private function dropOldTables(\PDO $pdo) {
$pdo->exec('DROP TABLE `comments`');
$pdo->exec('DROP TABLE `sujet_studs`');
$pdo->exec('DROP TABLE `user_studs`');
$pdo->exec('DROP TABLE `sondage`');
}
private function unescape($value) {
return stripslashes(html_entity_decode($value, ENT_QUOTES));
}
}

View File

@ -1,80 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Security\Token;
use Framadate\Utils;
/**
* This migration generate uniqId for all legacy votes.
*
* @package Framadate\Migration
* @version 0.9
*/
class Generate_uniqId_for_old_votes implements Migration {
function __construct() {
}
function description() {
return 'Generate "uniqId" in "vote" table for all legacy votes';
}
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
*/
function execute(\PDO $pdo) {
$pdo->beginTransaction();
$this->generateUniqIdsForEmptyOnes($pdo);
$pdo->commit();
return true;
}
private function generateUniqIdsForEmptyOnes($pdo) {
$select = $pdo->query('
SELECT `id`
FROM `' . Utils::table('vote') . '`
WHERE `uniqid` = \'\'');
$update = $pdo->prepare('
UPDATE `' . Utils::table('vote') . '`
SET `uniqid` = :uniqid
WHERE `id` = :id');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$token = Token::getToken(16);
$update->execute([
'uniqid' => $token,
'id' => $row->id
]);
}
}
}

View File

@ -1,66 +0,0 @@
<?php
namespace Framadate\Migration;
use Framadate\Utils;
class Increase_pollId_size implements Migration {
function __construct() {
}
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return '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
*/
function preCondition(\PDO $pdo) {
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
*/
function execute(\PDO $pdo) {
$this->alterCommentTable($pdo);
$this->alterPollTable($pdo);
$this->alterSlotTable($pdo);
$this->alterVoteTable($pdo);
}
private function alterCommentTable(\PDO $pdo) {
$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) {
$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) {
$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) {
$pdo->exec('
ALTER TABLE `' . Utils::table('vote') . '`
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
}
}

View File

@ -1,66 +0,0 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
use Framadate\Utils;
/**
* This migration RPad votes from version 0.8.
* Because some votes does not have enough values for their poll.
*
* @package Framadate\Migration
* @version 0.9
*/
class RPadVotes_from_0_8 implements Migration {
function description() {
return 'RPad votes from version 0.8.';
}
function preCondition(\PDO $pdo) {
$stmt = $pdo->query('SHOW TABLES');
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
// Check if tables of v0.9 are presents
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
return count($diff) === 0;
}
function execute(\PDO $pdo) {
$pdo->beginTransaction();
$this->rpadVotes($pdo);
$pdo->commit();
return true;
}
private function rpadVotes($pdo) {
$pdo->exec('UPDATE ' . Utils::table('vote') . ' fv
INNER JOIN (
SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices
FROM ' . Utils::table('vote') . ' v
INNER JOIN
(SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count
FROM ' . Utils::table('slot') . ' s
GROUP BY s.poll_id
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
WHERE LENGTH(v.choices) != inn.slots_count
) computed ON fv.id = computed.id
SET fv.choices = computed.new_choices');
}
}

View File

@ -0,0 +1,99 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
/**
* Class From_0_0_to_0_8_Migration
*
* @package Framadate\Migration
* @version 0.8
*/
class Version20150101000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'First installation of the Framadate application (v0.8)';
}
/**
* This method is called only one time in the migration page.
*
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @return void true is the execution succeeded
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_0_to_0_8_Migration'), 'Migration has been executed in an earlier database migration system');
$sondage = $schema->createTable('sondage');
$sondage->addColumn('id_sondage', 'string');
$sondage->addColumn('commentaires', 'text');
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
$sondage->addColumn('titre', 'text');
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
$sondage->addColumn('format', 'string', ['notnull' => false]);
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
$sondage->addColumn('statut', 'integer', ['default' => '1']);
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
$sujetStuds = $schema->createTable('sujet_studs');
$sujetStuds->addColumn('id_sondage', 'string');
$sujetStuds->addColumn('sujet', 'text');
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
$comments = $schema->createTable('comments');
$schema->createSequence('comments_seq');
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
$comments->addColumn('id_sondage', 'string');
$comments->addColumn('comment', 'text');
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
$userStuds = $schema->createTable('user_studs');
$schema->createSequence('user_studs_seq');
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
$userStuds->addColumn('nom', 'string');
$userStuds->addColumn('id_sondage', 'string');
$userStuds->addColumn('reponses', 'text');
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
}
public function down(Schema $schema)
{
$this->addSql('DROP TABLE sondage');
$this->addSql('DROP TABLE sujet_studs');
$this->addSql('DROP TABLE comments');
$this->addSql('DROP TABLE user_studs');
}
}

View File

@ -0,0 +1,160 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150102000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'From 0.8 to 0.9 first part';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_8_to_0_9_Migration'), 'Migration has been executed in an earlier database migration system');
foreach (['sondage', 'sujet_studs', 'comments', 'user_studs'] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$this->createPollTable($schema);
$this->createCommentTable($schema);
$this->createSlotTable($schema);
$this->createVoteTable($schema);
}
public function down(Schema $schema)
{
$sondage = $schema->createTable('sondage');
$sondage->addColumn('id_sondage', 'string');
$sondage->addColumn('commentaires', 'text');
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
$sondage->addColumn('titre', 'text');
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
$sondage->addColumn('format', 'string', ['notnull' => false]);
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
$sondage->addColumn('statut', 'integer', ['default' => '1']);
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
$sujetStuds = $schema->createTable('sujet_studs');
$sujetStuds->addColumn('id_sondage', 'string');
$sujetStuds->addColumn('sujet', 'text');
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
$comments = $schema->createTable('comments');
$schema->createSequence('comments_seq');
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
$comments->addColumn('id_sondage', 'string');
$comments->addColumn('comment', 'text');
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
$userStuds = $schema->createTable('user_studs');
$schema->createSequence('user_studs_seq');
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
$userStuds->addColumn('nom', 'string');
$userStuds->addColumn('id_sondage', 'string');
$userStuds->addColumn('reponses', 'text');
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
$schema->dropTable(Utils::table('poll'));
$schema->dropTable(Utils::table('comment'));
$schema->dropTable(Utils::table('vote'));
$schema->dropTable(Utils::table('slot'));
}
private function createPollTable(Schema $schema)
{
$poll = $schema->createTable(Utils::table('poll'));
$poll->addColumn('id', 'string');
$poll->addColumn('admin_id', 'string');
$poll->addColumn('title', 'text');
$poll->addColumn('description', 'text', ['notnull' => false]);
$poll->addColumn('admin_name', 'string');
$poll->addColumn('admin_mail', 'string', ['notnull' => false]);
$poll->addColumn('creation_date', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
$poll->addColumn('end_date', 'datetime', ['notnull' => false]);
$poll->addColumn('format', 'string', ['default' => null, 'notnull' => false]);
$poll->addColumn('editable', 'integer', ['default' => 0]);
$poll->addColumn('receiveNewVotes', 'boolean', ['default' => false]);
$poll->addColumn('active', 'boolean', ['default' => true]);
$poll->addUniqueIndex(['id'], 'poll_index_id');
}
private function createSlotTable(Schema $schema)
{
$slot = $schema->createTable(Utils::table('slot'));
$schema->createSequence('slot_seq');
$slot->addColumn('id', 'integer', ['autoincrement' => true]);
$slot->addColumn('poll_id', 'string');
$slot->addColumn('title', 'text');
$slot->addColumn('moments', 'text', ['notnull' => false]);
$slot->addUniqueIndex(['id'], 'slot_index_id');
$slot->addIndex(['poll_id'], 'slot_index_poll_id');
}
private function createCommentTable(Schema $schema)
{
$comment = $schema->createTable(Utils::table('comment'));
$schema->createSequence('comment_seq');
$comment->addColumn('id', 'integer', ['autoincrement' => true]);
$comment->addColumn('poll_id', 'string');
$comment->addColumn('name', 'text', ['notnull' => false]);
$comment->addColumn('comment', 'text');
$comment->addUniqueIndex(['id'], 'comment_index_id');
$comment->addIndex(['poll_id'], 'comment_index_poll_id');
}
private function createVoteTable(Schema $schema)
{
$vote = $schema->createTable(Utils::table('vote'));
$schema->createSequence('vote_seq');
$vote->addColumn('id', 'integer', ['autoincrement' => true]);
$vote->addColumn('poll_id', 'string');
$vote->addColumn('name', 'string');
$vote->addColumn('choices', 'string');
$vote->addUniqueIndex(['id'], 'vote_index_id');
$vote->addIndex(['poll_id'], 'vote_index_poll_id');
}
}

View File

@ -0,0 +1,263 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150102100000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'From 0.8 to 0.9 second part';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\From_0_8_to_0_9_Migration'), 'Migration has been executed in an earlier database migration system');
foreach ([Utils::table('poll'), Utils::table('comment'), Utils::table('slot'), Utils::table('vote')] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$this->migrateFromSondageToPoll();
$this->migrateFromCommentsToComment();
$this->migrateFromSujetStudsToSlot();
//$this->migrateFromUserStudsToVote();
$this->dropOldTables($schema);
}
public function down(Schema $schema)
{
$sondage = $schema->createTable('sondage');
$sondage->addColumn('id_sondage', 'string');
$sondage->addColumn('commentaires', 'text');
$sondage->addColumn('mail_admin', 'string', ['notnull' => false]);
$sondage->addColumn('nom_admin', 'string', ['notnull' => false]);
$sondage->addColumn('titre', 'text');
$sondage->addColumn('id_sondage_admin', 'string', ['notnull' => false]);
$sondage->addColumn('date_creation', 'datetime', ['default' => (new \DateTime())->format('Y-m-d H:i:s')]);
$sondage->addColumn('date_fin', 'datetime', ['notnull' => false]);
$sondage->addColumn('format', 'string', ['notnull' => false]);
$sondage->addColumn('mailsonde', 'boolean', ['default' => false]);
$sondage->addColumn('statut', 'integer', ['default' => '1']);
$sondage->addUniqueIndex(['id_sondage'], 'sondage_index_id_sondage');
$sujetStuds = $schema->createTable('sujet_studs');
$sujetStuds->addColumn('id_sondage', 'string');
$sujetStuds->addColumn('sujet', 'text');
$sujetStuds->addIndex(['id_sondage'], 'sujet_studs_index_id_sondage');
$comments = $schema->createTable('comments');
$schema->createSequence('comments_seq');
$comments->addColumn('id_comment', 'integer', ['autoincrement' => true]);
$comments->addColumn('id_sondage', 'string');
$comments->addColumn('comment', 'text');
$comments->addColumn('usercomment', 'text', ['notnull' => false]);
$comments->addUniqueIndex(['id_comment'], 'comments_index_id_comment');
$comments->addIndex(['id_sondage'], 'comments_index_id_sondage');
$userStuds = $schema->createTable('user_studs');
$schema->createSequence('user_studs_seq');
$userStuds->addColumn('id_users', 'integer', ['autoincrement' => true]);
$userStuds->addColumn('nom', 'string');
$userStuds->addColumn('id_sondage', 'string');
$userStuds->addColumn('reponses', 'text');
$userStuds->addUniqueIndex(['id_users'], 'user_studs_index_id_users');
$userStuds->addIndex(['id_sondage'], 'user_studs_index_id_sondage');
$schema->dropTable(Utils::table('poll'));
$schema->dropTable(Utils::table('comment'));
$schema->dropTable(Utils::table('vote'));
$schema->dropTable(Utils::table('slot'));
}
private function migrateFromSondageToPoll()
{
$select = $this->connection->query('
SELECT
id_sondage,
id_sondage_admin,
titre,
commentaires,
nom_admin,
mail_admin,
date_creation,
date_fin,
SUBSTR(format, 1, 1) AS format,
CASE SUBSTR(format, 2, 1)
WHEN \'+\' THEN 1
ELSE 0 END AS editable,
mailsonde,
CASE SUBSTR(format, 2, 1)
WHEN \'-\' THEN 0
ELSE 1 END AS active
FROM sondage');
$insert = $this->connection->prepare('
INSERT INTO ' . Utils::table('poll') . '
(id, admin_id, title, description, admin_name, admin_mail, creation_date, end_date, format, editable, receiveNewVotes, active)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$row->id_sondage_admin,
$this->unescape($row->titre),
$this->unescape($row->commentaires),
$this->unescape($row->nom_admin),
$this->unescape($row->mail_admin),
$row->date_creation,
$row->date_fin,
$row->format,
$row->editable,
$row->mailsonde,
$row->active
]);
}
}
private function migrateFromSujetStudsToSlot()
{
$stmt = $this->connection->query('SELECT * FROM sujet_studs');
$sujets = $stmt->fetchAll();
$slots = [];
foreach ($sujets as $sujet) {
$newSlots = $this->transformSujetToSlot($sujet);
foreach ($newSlots as $newSlot) {
$slots[] = $newSlot;
}
}
$prepared = $this->connection->prepare('INSERT INTO ' . Utils::table('slot') . ' (poll_id, title, moments) VALUES (?,?,?)');
foreach ($slots as $slot) {
$prepared->execute([
$slot->poll_id,
$this->unescape($slot->title),
!empty($slot->moments) ? $this->unescape($slot->moments) : null
]);
}
}
private function migrateFromCommentsToComment()
{
$select = $this->connection->query('
SELECT
id_sondage,
usercomment,
comment
FROM comments');
$insert = $this->connection->prepare('
INSERT INTO ' . Utils::table('comment') . ' (poll_id, name, comment)
VALUES (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->usercomment),
$this->unescape($row->comment)
]);
}
}
private function migrateFromUserStudsToVote()
{
$select = $this->connection->query('
SELECT
id_sondage,
nom,
REPLACE(REPLACE(REPLACE(reponses, 1, \'X\'), 2, 1), \'X\', 2) reponses
FROM user_studs');
$insert = $this->connection->prepare('
INSERT INTO ' . Utils::table('vote') . ' (poll_id, name, choices)
VALUES (?,?,?)');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$insert->execute([
$row->id_sondage,
$this->unescape($row->nom),
$row->reponses
]);
}
}
private function transformSujetToSlot($sujet)
{
$slots = [];
$ex = explode(',', $sujet->sujet);
$isDatePoll = strpos($sujet->sujet, '@');
$lastSlot = null;
foreach ($ex as $atomicSlot) {
if ($isDatePoll === false) { // Classic poll
$slot = new \stdClass();
$slot->poll_id = $sujet->id_sondage;
$slot->title = $atomicSlot;
$slots[] = $slot;
} else { // Date poll
$values = explode('@', $atomicSlot);
if ($lastSlot === null || $lastSlot->title !== $values[0]) {
$lastSlot = new \stdClass();
$lastSlot->poll_id = $sujet->id_sondage;
$lastSlot->title = $values[0];
$lastSlot->moments = count($values) === 2 ? $values[1] : '-';
$slots[] = $lastSlot;
} else {
$lastSlot->moments .= ',' . (count($values) === 2 ? $values[1] : '-');
}
}
}
return $slots;
}
private function dropOldTables(Schema $schema)
{
$schema->dropTable('comments');
$schema->dropTable('sujet_studs');
$schema->dropTable('user_studs');
$schema->dropTable('sondage');
}
private function unescape($value)
{
return stripslashes(html_entity_decode($value, ENT_QUOTES));
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the field receiveNewComments on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150117000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Add column "receiveNewComments" for version 0.9';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema,'Framadate\Migration\AddColumn_receiveNewComments_For_0_9'), 'Migration has been executed in an earlier database migration system');
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$pollTable = $schema->getTable(Utils::table('poll'));
$this->skipIf($pollTable->hasColumn('receiveNewComments'), 'Column receiveNewComments already exists');
$pollTable->addColumn('receiveNewComments', 'boolean', ['default' => false]);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->dropColumn('receiveNewComments');
}
}

View File

@ -0,0 +1,76 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the field uniqId on the vote table.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150402000000 extends AbstractMigration
{
private $indexUniqIdName = 'IDX_vote_uniqId';
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Add column "uniqId" in table "vote" for version 0.9';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_uniqId_In_vote_For_0_9'), 'Migration has been executed in an earlier database migration system');
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$voteTable = $schema->getTable(Utils::table('vote'));
$this->skipIf($voteTable->hasColumn('uniqId'), 'Column uniqId already existing');
$voteTable->addColumn('uniqId', 'string', ['length' => 16]);
$voteTable->addIndex(['uniqId'], $this->indexUniqIdName);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$voteTable = $schema->getTable(Utils::table('vote'));
$voteTable->dropIndex($this->indexUniqIdName);
$voteTable->dropColumn('uniqId');
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the field hidden on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150405000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Add column "hidden" in table "vote" for version 0.9';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_hidden_In_poll_For_0_9'), 'Migration has been executed in an earlier database migration system');
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$pollTable = $schema->getTable(Utils::table('poll'));
$this->skipIf($pollTable->hasColumn('hidden'), 'Column hidden already existing in table poll');
$pollTable->addColumn('hidden', 'boolean', ['default' => false, 'notnull' => true]);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->dropColumn('hidden');
}
}

View File

@ -0,0 +1,78 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Security\Token;
use Framadate\Utils;
/**
* This migration generate uniqId for all legacy votes.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150624000000 extends AbstractMigration
{
public function description()
{
return 'Generate "uniqId" in "vote" table for all legacy votes';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Generate_uniqId_for_old_votes'), 'Migration has been executed in an earlier database migration system');
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$this->connection->beginTransaction();
$select = $this->connection->query('
SELECT id
FROM ' . Utils::table('vote') . '
WHERE uniqid = \'\'');
$update = $this->connection->prepare('
UPDATE ' . Utils::table('vote') . '
SET uniqid = :uniqid
WHERE id = :id');
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
$token = Token::getToken(16);
$update->execute([
'uniqid' => $token,
'id' => $row->id
]);
}
$this->connection->commit();
}
public function down(Schema $schema)
{
// TODO: Implement down() method.
}
}

View File

@ -0,0 +1,102 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Rapha<EFBFBD>l DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est r<EFBFBD>gi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Rapha<EFBFBD>l DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration RPad votes from version 0.8.
* Because some votes does not have enough values for their poll.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20150918000000 extends AbstractMigration
{
public function description()
{
return 'RPad votes from version 0.8.';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function up(Schema $schema)
{
$this->skipIf(
$this->legacyCheck($schema, 'Framadate\Migration\RPadVotes_from_0_8'),
'Migration has been executed in an earlier database migration system'
);
foreach ([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table(
'comment'
)] as $table) {
$this->skipIf(!$schema->hasTable($table), 'Missing table ' . $table);
}
$driver_name = $this->connection->getDatabasePlatform()->getName();
switch ($driver_name) {
case 'mysql':
$this->addSql(
'UPDATE ' . Utils::table('vote') . ' fv
INNER JOIN (
SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices
FROM ' . Utils::table('vote') . ' v
INNER JOIN
(SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count
FROM ' . Utils::table('slot') . ' s
GROUP BY s.poll_id
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
WHERE LENGTH(v.choices) != inn.slots_count
) computed ON fv.id = computed.id
SET fv.choices = computed.new_choices'
);
break;
case 'postgresql':
$this->addSql(
"UPDATE " . Utils::table('vote') . " fv
SET choices = computed.new_choices
FROM (
SELECT v.id, RPAD(v.choices::text, inn.slots_count::int, '0') new_choices
FROM " . Utils::table('vote') . " v
INNER JOIN
(SELECT s.poll_id, SUM(coalesce(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, ',', '')) + 1, 1)) slots_count
FROM " . Utils::table('slot') . " s
GROUP BY s.poll_id
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
WHERE LENGTH(v.choices) != inn.slots_count
) computed WHERE fv.id = computed.id"
);
break;
default:
$this->skipIf(true, "Not on MySQL or PostgreSQL");
break;
}
}
public function down(Schema $schema)
{
// TODO: Implement down() method.
}
}

View File

@ -16,8 +16,11 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
@ -26,45 +29,43 @@ use Framadate\Utils;
* @package Framadate\Migration
* @version 1.0
*/
class Alter_Comment_table_for_name_length implements Migration {
function __construct() {
}
class Version20151012075900 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
public function description()
{
return 'Alter the comment table to set a length to the name column.';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
function preCondition(\PDO $pdo) {
return true;
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Alter_Comment_table_for_name_length'), 'Migration has been executed in an earlier database migration system');
$commentTable = $schema->getTable(Utils::table('comment'));
$commentTable->changeColumn('name', ['default' => null, 'notnull' => false]);
$commentTable->changeColumn('name', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
function execute(\PDO $pdo) {
$this->alterCommentTable($pdo);
public function down(Schema $schema)
{
$commentTable = $schema->getTable(Utils::table('comment'));
return true;
}
private function alterCommentTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;');
$commentTable->changeColumn('name', ['type' => Type::getType('string')]);
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration alter the comment table to add a date column.
*
* @package Framadate\Migration
* @version 1.0
*/
class Version20151012082600 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Alter the comment table to add a date column.';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Alter_Comment_table_adding_date'), 'Migration has been executed in an earlier database migration system');
$commentTable = $schema->getTable(Utils::table('comment'));
$this->skipIf($commentTable->hasColumn('date'), 'Column date in comment table already exists');
// The default for datetime types is only available for MySQL 5.6
// See the "Functionality Added or Changed" section,
// "Important Change: In MySQL, the TIMESTAMP data type" element, on
// https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-6.html
$commentTable->addColumn('date', 'datetime', ['default' => 0]);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$commentTable = $schema->getTable(Utils::table('comment'));
$commentTable->dropColumn('comment');
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the fields password_hash and results_publicly_visible on the poll table.
*
* @package Framadate\Migration
* @version 0.9
*/
class Version20151028000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9'), 'Migration has been executed in an earlier database migration system');
$pollTable = $schema->getTable(Utils::table('poll'));
$this->skipIf($pollTable->hasColumn('password_hash'), 'Column password_hash in table poll already exists');
$this->skipIf($pollTable->hasColumn('results_publicly_visible'), 'Column results_publicly_visible in table poll already exists');
$pollTable->addColumn('password_hash', 'string', ['notnull' => false]);
$pollTable->addColumn('results_publicly_visible', 'boolean', ['notnull' => false]);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->dropColumn('password_hash');
$pollTable->dropColumn('results_publicly_visible');
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Framadate\AbstractMigration;
use Framadate\Utils;
class Version20151205000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Increase the size of id column in poll table';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Increase_pollId_size'), 'Migration has been executed in an earlier database migration system');
$commentTable = $schema->getTable(Utils::table('comment'));
$commentTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->changeColumn('id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
$slotTable = $schema->getTable(Utils::table('slot'));
$slotTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
$voteTable = $schema->getTable(Utils::table('vote'));
$voteTable->changeColumn('poll_id', ['type' => Type::getType('string'), 'length' => 64, 'notnull' => true]);
}
public function down(Schema $schema)
{
// TODO: Implement down() method.
}
}

View File

@ -16,8 +16,10 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
@ -26,45 +28,38 @@ use Framadate\Utils;
* @package Framadate\Migration
* @version 0.9
*/
class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
function __construct() {
}
class Version20180220000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
public function description()
{
return 'Add column "ValueMax" in table "vote" for version 0.9';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\DBALException
*/
function preCondition(\PDO $pdo) {
return true;
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_ValueMax_In_poll_For_1_1'), 'Migration has been executed in an earlier database migration system');
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->addColumn('ValueMax', 'smallint', ['default' => null, 'notnull' => false]);
}
/**
* This method is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
function execute(\PDO $pdo) {
$this->alterPollTable($pdo);
return true;
}
private function alterPollTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('poll') . '`
ADD `ValueMax` TINYINT NULL;');
public function down(Schema $schema)
{
$pollTable = $schema->getTable(Utils::table('poll'));
$pollTable->dropColumn('ValueMax');
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration sets Poll.end_date to NULL by default
*
* @package Framadate\Migration
* @version 1.1
*/
class Version20180411000000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Sets Poll end_date to NULL by default (work around MySQL NO_ZERO_DATE)';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param Connection|\PDO $connection The connection to database
* @return bool true if the Migration should be executed.
*/
public function preCondition(Connection $connection)
{
$driver_name = $connection->getWrappedConnection()->getAttribute(\PDO::ATTR_DRIVER_NAME);
if ($driver_name === 'mysql') {
$stmt = $connection->prepare(
"SELECT Column_Default from Information_Schema.Columns where Table_Name = ? AND Column_Name = ?;"
);
$stmt->bindValue(1, Utils::table('poll'));
$stmt->bindValue(2, 'end_date');
$stmt->execute();
$default = $stmt->fetch(\PDO::FETCH_COLUMN);
return $default === null;
}
return true;
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
// We don't disable this migration even if legacy because it wasn't working correctly before
// $this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\Fix_MySQL_No_Zero_Date'), 'Migration has been executed in an earlier database migration system');
$this->skipIf($this->preCondition($this->connection), "Database server isn't MySQL or poll end_date default value was already NULL");
$poll = $schema->getTable(Utils::table('poll'));
$poll->changeColumn('end_date', ['default' => null, 'notnull' => false]);
}
public function down(Schema $schema)
{
// nothing
}
}

View File

@ -0,0 +1,65 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the column collect_users_mail in the poll table
*
* @package Framadate\Migration
* @version 1.2
*/
class Version20180419170000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Add column collect_users_mail in table poll';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_collect_mail_In_poll'), 'Migration has been executed in an earlier database migration system');
$poll = $schema->getTable(Utils::table('poll'));
$poll->addColumn('collect_users_mail', 'boolean', ['default' => false]);
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$poll = $schema->getTable(Utils::table('poll'));
$poll->dropColumn('collect_users_mail');
}
}

View File

@ -16,55 +16,50 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration alter the comment table to add a date column.
* This migration adds the column mail in the vote table
*
* @package Framadate\Migration
* @version 1.0
* @version 1.2
*/
class Alter_Comment_table_adding_date implements Migration {
function __construct() {
}
class Version20180419180000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description() {
return 'Alter the comment table to add a date column.';
public function description()
{
return 'Add column mail in table vote';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true is the Migration should be executed.
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
* @throws \Doctrine\DBAL\DBALException
*/
function preCondition(\PDO $pdo) {
return true;
public function up(Schema $schema)
{
$this->skipIf($this->legacyCheck($schema, 'Framadate\Migration\AddColumn_collect_mail_In_poll'), 'Migration has been executed in an earlier database migration system');
$vote = $schema->getTable(Utils::table('vote'));
$vote->addColumn('mail', 'string', ['default' => null, 'notnull' => false]);
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true is the execution succeeded
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
function execute(\PDO $pdo) {
$this->alterCommentTable($pdo);
return true;
}
private function alterCommentTable(\PDO $pdo) {
$pdo->exec('
ALTER TABLE `' . Utils::table('comment') . '`
ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;');
public function down(Schema $schema)
{
$vote = $schema->getTable(Utils::table('vote'));
$vote->dropColumn('mail');
}
}

View File

@ -16,31 +16,45 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Migration;
namespace DoctrineMigrations;
interface Migration {
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the column mail in the vote table
*
* @package Framadate\Migration
* @version 1.2
*/
class Version20180419190000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
function description();
public function description()
{
return 'Remove the old migration table';
}
/**
* This method could check if the execute method should be called.
* It is called before the execute method.
*
* @param \PDO $pdo The connection to database
* @return bool true if the Migration should be executed
* @param Schema $schema
* @throws \Doctrine\DBAL\Migrations\SkipMigrationException
*/
function preCondition(\PDO $pdo);
public function up(Schema $schema)
{
$this->skipIf(!$schema->hasTable(Utils::table(MIGRATION_TABLE)), "The old migration table wasn't created, no need to delete it.");
$schema->dropTable(Utils::table(MIGRATION_TABLE));
}
/**
* This methode is called only one time in the migration page.
*
* @param \PDO $pdo The connection to database
* @return bool true if the execution succeeded
* @param Schema $schema
*/
function execute(\PDO $pdo);
public function down(Schema $schema)
{
// No need to recreate legacy migration table
}
}

View File

@ -0,0 +1,100 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Framadate\AbstractMigration;
use Framadate\Utils;
/**
* This migration adds the column collect_users_mail in the poll table
*
* @package Framadate\Migration
* @version 1.2
*/
class Version20180525110000 extends AbstractMigration
{
/**
* This method should describe in english what is the purpose of the migration class.
*
* @return string The description of the migration class
*/
public function description()
{
return 'Change column collect_users_mail in table poll from boolean to smallint';
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
* @throws \Doctrine\DBAL\DBALException
*/
public function up(Schema $schema)
{
$poll = $schema->getTable(Utils::table('poll'));
$poll->addColumn('collect_users_mail_integer', 'smallint', ['default' => 0]);
}
/**
* @param Schema $schema
*/
public function postUp(Schema $schema)
{
$this->addSql('UPDATE ' . Utils::table('poll') . ' SET collect_users_mail_integer = collect_users_mail');
$this->addSql('ALTER TABLE ' . Utils::table('poll') . ' DROP COLUMN collect_users_mail');
if ($this->connection->getDatabasePlatform()->getName() === 'mysql') {
$this->addSql(
'ALTER TABLE ' . Utils::table('poll') . ' CHANGE collect_users_mail_integer collect_users_mail SMALLINT'
);
} else {
$this->addSql(
'ALTER TABLE ' . Utils::table('poll') . ' RENAME COLUMN collect_users_mail_integer to collect_users_mail'
);
}
}
/**
* @param Schema $schema
* @throws \Doctrine\DBAL\Schema\SchemaException
*/
public function down(Schema $schema)
{
$poll = $schema->getTable(Utils::table('poll'));
$poll->addColumn('collect_users_mail_boolean', 'boolean', ['default' => false]);
}
/**
* @param Schema $schema
*/
public function postDown(Schema $schema)
{
$this->addSql('UPDATE ' . Utils::table('poll') . ' SET collect_users_mail_boolean = collect_users_mail > 0');
$this->addSql('ALTER TABLE ' . Utils::table('poll') . ' DROP COLUMN collect_users_mail');
if ($this->connection->getDatabasePlatform()->getName() === 'mysql') {
$this->addSql(
'ALTER TABLE ' . Utils::table('poll') . ' CHANGE collect_users_mail_boolean collect_users_mail SMALLINT'
);
} else {
$this->addSql(
'ALTER TABLE ' . Utils::table('poll') . ' RENAME COLUMN collect_users_mail_boolean to collect_users_mail'
);
}
}
}

View File

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

View File

@ -1,19 +1,30 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Types\Type;
use Framadate\Utils;
class CommentRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
function findAllByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
/**
* @param $poll_id
* @throws DBALException
* @return array
*/
public function findAllByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('comment') . ' WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
$comments = $prepared->fetchAll();
/**
* Hack to make date a proper DateTime
*/
return array_map(function($comment) {
$comment->date = Type::getType(Type::DATETIME)->convertToPhpValue($comment->date, $this->connect->getDatabasePlatform());
return $comment;
}, $comments);
}
/**
@ -24,32 +35,44 @@ class CommentRepository extends AbstractRepository {
* @param $comment
* @return bool
*/
function insert($poll_id, $name, $comment) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $name, $comment]);
public function insert($poll_id, $name, $comment)
{
return $this->connect->insert(Utils::table('comment'), ['poll_id' => $poll_id, 'name' => $name, 'comment' => $comment]) > 0;
}
function deleteById($poll_id, $comment_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
return $prepared->execute([$poll_id, $comment_id]);
/**
* @param $poll_id
* @param $comment_id
* @throws InvalidArgumentException
* @return bool
*/
public function deleteById($poll_id, $comment_id)
{
return $this->connect->delete(Utils::table('comment'), ['poll_id' => $poll_id, 'id' => $comment_id]) > 0;
}
/**
* Delete all comments of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @return bool|null true if action succeeded.
* @throws InvalidArgumentException
* @return bool true if action succeeded.
*/
function deleteByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
public function deleteByPollId($poll_id)
{
return $this->connect->delete(Utils::table('comment'), ['poll_id' => $poll_id]) > 0;
}
public function exists($poll_id, $name, $comment) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?');
/**
* @param $poll_id
* @param $name
* @param $comment
* @throws DBALException
* @return bool
*/
public function exists($poll_id, $name, $comment)
{
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('comment') . ' WHERE poll_id = ? AND name = ? AND comment = ?');
$prepared->execute([$poll_id, $name, $comment]);
return $prepared->rowCount() > 0;

View File

@ -1,79 +1,148 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\Types\Type;
use Exception;
use Framadate\Form;
use Framadate\Utils;
use PDO;
class PollRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
/**
* @param $poll_id
* @param $admin_poll_id
* @param Form $form
* @throws Exception
*/
public function insertPoll($poll_id, $admin_poll_id, Form $form)
{
$this->connect->insert(Utils::table('poll'), [
'id' => $poll_id,
'admin_id' => $admin_poll_id,
'title' => $form->title,
'description' => $form->description,
'admin_name' => $form->admin_name,
'admin_mail' => $form->admin_mail,
'end_date' => $form->end_date->format('Y-m-d H:i:s'),
'format' => $form->format,
'editable' => ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0,
'receiveNewVotes' => $form->receiveNewVotes ? 1 : 0,
'receiveNewComments' => $form->receiveNewComments ? 1 : 0,
'hidden' => $form->hidden ? 1 : 0,
'password_hash' => $form->password_hash,
'results_publicly_visible' => $form->results_publicly_visible ? 1 : 0,
'ValueMax' => $form->ValueMax,
'collect_users_mail' => ($form->collect_users_mail >= 0 && $form->collect_users_mail <= 3) ? $form->collect_users_mail : 0,
]);
}
public function insertPoll($poll_id, $admin_poll_id, $form) {
$sql = 'INSERT INTO `' . Utils::table('poll') . '`
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible,ValueMax)
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?)';
$prepared = $this->prepare($sql);
$prepared->execute([$poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0, $form->receiveNewVotes ? 1 : 0, $form->receiveNewComments ? 1 : 0, $form->hidden ? 1 : 0, $form->password_hash, $form->results_publicly_visible ? 1 : 0,$form->ValueMax]);
}
/**
* @param $poll_id
* @throws DBALException
* @return mixed
*/
public function findById($poll_id)
{
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE id = ?', [$poll_id]);
function findById($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared->execute([$poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
/**
* Hack to make date a proper DateTime
*/
$poll->creation_date = Type::getType(Type::DATETIME)->convertToPhpValue($poll->creation_date, $this->connect->getDatabasePlatform());
$poll->end_date = Type::getType(Type::DATETIME)->convertToPhpValue($poll->end_date, $this->connect->getDatabasePlatform());
return $poll;
}
/**
* @param $admin_poll_id
* @throws DBALException
* @return mixed
*/
public function findByAdminId($admin_poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE admin_id = ?', [$admin_poll_id]);
$prepared->execute([$admin_poll_id]);
$poll = $prepared->fetch();
$prepared->closeCursor();
$poll->creation_date = Type::getType(Type::DATETIME)->convertToPhpValue($poll->creation_date, $this->connect->getDatabasePlatform());
$poll->end_date = Type::getType(Type::DATETIME)->convertToPhpValue($poll->end_date, $this->connect->getDatabasePlatform());
return $poll;
}
/**
* @param $poll_id
* @throws DBALException
* @return bool
*/
public function existsById($poll_id) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?');
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('poll') . ' WHERE id = ?');
$prepared->execute([$poll_id]);
return $prepared->rowCount() > 0;
}
/**
* @param $admin_poll_id
* @throws DBALException
* @return bool
*/
public function existsByAdminId($admin_poll_id) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('poll') . ' WHERE admin_id = ?');
$prepared->execute([$admin_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=?, 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, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]);
/**
* @param $poll
* @return bool
*/
public function update($poll)
{
return $this->connect->update(Utils::table('poll'), [
'title' => $poll->title,
'admin_name' => $poll->admin_name,
'admin_mail' => $poll->admin_mail,
'description' => $poll->description,
'end_date' => $poll->end_date->format('Y-m-d H:i:s'), # TODO : Harmonize dates between here and insert
'active' => $poll->active,
'editable' => $poll->editable >= 0 && $poll->editable <= 2 ? $poll->editable : 0,
'hidden' => $poll->hidden ? 1 : 0,
'password_hash' => $poll->password_hash,
'results_publicly_visible' => $poll->results_publicly_visible ? 1 : 0
], [
'id' => $poll->id
]) > 0;
}
function deleteById($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
return $prepared->execute([$poll_id]);
/**
* @param $poll_id
* @throws InvalidArgumentException
* @return bool
*/
public function deleteById($poll_id)
{
return $this->connect->delete(Utils::table('poll'), ['id' => $poll_id]) > 0;
}
/**
* Find old polls. Limit: 20.
*
* @throws DBALException
* @return array Array of old polls
*/
public function findOldPolls() {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE DATE_ADD(`end_date`, INTERVAL ' . PURGE_DELAY . ' DAY) < NOW() AND `end_date` != 0 LIMIT 20');
$prepared->execute([]);
public function findOldPolls()
{
$prepared = $this->connect->executeQuery('SELECT * FROM ' . Utils::table('poll') . ' WHERE DATE_ADD(end_date, INTERVAL ? DAY) < NOW() AND end_date != 0 LIMIT 20', [PURGE_DELAY]);
return $prepared->fetchAll();
}
@ -84,52 +153,53 @@ class PollRepository extends AbstractRepository {
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
* @param int $start The number of first entry to select
* @param int $limit The number of entries to find
* @throws DBALException
* @return array The found polls
*/
public function findAll($search, $start, $limit) {
// Polls
$request = "";
$request .= "SELECT p.*,";
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
$request .= " FROM `" . Utils::table('poll') . "` p";
$request .= " (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();
@ -139,26 +209,28 @@ class PollRepository extends AbstractRepository {
* Find all polls that are created with the given admin mail.
*
* @param string $mail Email address of the poll admin
* @throws DBALException
* @return array The list of matching polls
*/
public function findAllByAdminMail($mail) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('poll') . ' WHERE admin_mail = :admin_mail');
$prepared->execute(['admin_mail' => $mail]);
return $prepared->fetchAll();
}
/**
* Get the total number of polls in databse.
* Get the total number of polls in database.
*
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
* @throws DBALException
* @return int The number of polls
*/
public function count($search = null) {
// Total count
$prepared = $this->prepare('
SELECT count(1) nb
FROM `' . Utils::table('poll') . '` p
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)

View File

@ -18,7 +18,7 @@
*/
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\Connection;
class RepositoryFactory {
private static $connect;
@ -29,9 +29,9 @@ class RepositoryFactory {
private static $commentRepository;
/**
* @param FramaDB $connect
* @param Connection $connect
*/
static function init(FramaDB $connect) {
static function init(Connection $connect) {
self::$connect = $connect;
}

View File

@ -4,28 +4,24 @@
* 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 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\Repositories;
use Framadate\FramaDB;
use Framadate\Choice;
use Framadate\Utils;
class SlotRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
/**
* Insert a bulk of slots.
*
@ -33,11 +29,10 @@ class SlotRepository extends AbstractRepository {
* @param array $choices
*/
public function insertSlots($poll_id, $choices) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
foreach ($choices as $choice) {
/** @var Choice $choice */
// We prepared the slots (joined by comas)
$joinedSlots = '';
$joinedSlots = null;
$first = true;
foreach ($choice->getSlots() as $slot) {
if ($first) {
@ -49,16 +44,22 @@ class SlotRepository extends AbstractRepository {
}
// We execute the insertion
if (empty($joinedSlots)) {
$prepared->execute([$poll_id, $choice->getName(), null]);
} else {
$prepared->execute([$poll_id, $choice->getName(), $joinedSlots]);
}
$this->connect->insert(Utils::table('slot'), [
'poll_id' => $poll_id,
'title' => $choice->getName(),
'moments' => $joinedSlots
]);
}
}
function listByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
/**
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return array
*/
public function listByPollId($poll_id)
{
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('slot') . ' WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
@ -69,10 +70,11 @@ class SlotRepository extends AbstractRepository {
*
* @param $poll_id int The ID of the poll
* @param $datetime int The datetime of the slot
* @throws \Doctrine\DBAL\DBALException
* @return mixed Object The slot found, or null
*/
function findByPollIdAndDatetime($poll_id, $datetime) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('slot') . ' WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
$prepared->execute([$poll_id, $datetime]);
$slot = $prepared->fetch();
@ -89,10 +91,9 @@ class SlotRepository extends AbstractRepository {
* @param $moments mixed|null The moments joined with ","
* @return bool true if action succeeded
*/
function insert($poll_id, $title, $moments) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
return $prepared->execute([$poll_id, $title, $moments]);
function insert($poll_id, $title, $moments)
{
return $this->connect->insert(Utils::table('slot'), ['poll_id' => $poll_id, 'title' => $title, 'moments' => $moments]) > 0;
}
/**
@ -103,10 +104,9 @@ class SlotRepository extends AbstractRepository {
* @param $newMoments mixed The new moments
* @return bool|null true if action succeeded.
*/
function update($poll_id, $datetime, $newMoments) {
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
return $prepared->execute([$newMoments, $poll_id, $datetime]);
function update($poll_id, $datetime, $newMoments)
{
return $this->connect->update(Utils::table('slot'), ['moments' => $newMoments], ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
}
/**
@ -114,15 +114,21 @@ class SlotRepository extends AbstractRepository {
*
* @param $poll_id int The ID of the poll
* @param $datetime mixed The datetime of the slot
* @throws \Doctrine\DBAL\DBALException
* @return bool
*/
function deleteByDateTime($poll_id, $datetime) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
$prepared->execute([$poll_id, $datetime]);
public function deleteByDateTime($poll_id, $datetime)
{
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id, 'title' => $datetime]) > 0;
}
function deleteByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
/**
* @param $poll_id
* @throws \Doctrine\DBAL\DBALException
* @return bool
*/
public function deleteByPollId($poll_id)
{
return $this->connect->delete(Utils::table('slot'), ['poll_id' => $poll_id]) > 0;
}
}

View File

@ -1,30 +1,48 @@
<?php
namespace Framadate\Repositories;
use Framadate\FramaDB;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Query\QueryBuilder;
use Framadate\Utils;
class VoteRepository extends AbstractRepository {
function __construct(FramaDB $connect) {
parent::__construct($connect);
}
function allUserVotesByPollId($poll_id) {
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
/**
* @param $poll_id
* @throws DBALException
* @return array
*/
public function allUserVotesByPollId($poll_id)
{
$prepared = $this->prepare('SELECT * FROM ' . Utils::table('vote') . ' WHERE poll_id = ? ORDER BY id');
$prepared->execute([$poll_id]);
return $prepared->fetchAll();
}
function insertDefault($poll_id, $insert_position) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
/**
* Insert default values for slots
*
* @param string $poll_id
* @param integer $insert_position
* @return Statement|int
*/
public function insertDefault($poll_id, $insert_position)
{
$qb = $this->createQueryBuilder();
$sql = $this->connect->getDatabasePlatform()->getName() === 'sqlite' ?
$this->generateSQLForInsertDefaultSQLite($qb, $insert_position) :
$this->generateSQLForInsertDefault($qb, $insert_position)
;
$query = $qb->update(Utils::table('vote'))
->set('choices', $sql)
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($poll_id)))
;
return $query->execute();
}
function insert($poll_id, $name, $choices, $token) {
$prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices, uniqId) VALUES (?,?,?,?)');
$prepared->execute([$poll_id, $name, $choices, $token]);
function insert($poll_id, $name, $choices, $token, $mail) {
$this->connect->insert(Utils::table('vote'), ['poll_id' => $poll_id, 'name' => $name, 'choices' => $choices, 'uniqId' => $token, 'mail' => $mail]);
$newVote = new \stdClass();
$newVote->poll_id = $poll_id;
@ -32,26 +50,38 @@ class VoteRepository extends AbstractRepository {
$newVote->name = $name;
$newVote->choices = $choices;
$newVote->uniqId = $token;
$newVote->mail=$mail;
return $newVote;
}
function deleteById($poll_id, $vote_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
/**
* @param $poll_id
* @param $vote_id
* @throws DBALException
* @return bool
*/
public function deleteById($poll_id, $vote_id)
{
return $this->connect->delete(Utils::table('vote'), ['poll_id' => $poll_id, 'id' => $vote_id]) > 0;
}
return $prepared->execute([$poll_id, $vote_id]);
public function deleteOldVotesByPollId($poll_id, $votesToDelete) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY `poll_id` ASC LIMIT ' . $votesToDelete);
return $prepared->execute([$poll_id]);
}
/**
* Delete all votes of a given poll.
*
* @param $poll_id int The ID of the given poll.
* @throws DBALException
* @return bool|null true if action succeeded.
*/
function deleteByPollId($poll_id) {
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
return $prepared->execute([$poll_id]);
public function deleteByPollId($poll_id)
{
return $this->connect->delete(Utils::table('vote'), ['poll_id' => $poll_id]) > 0;
}
/**
@ -61,16 +91,37 @@ class VoteRepository extends AbstractRepository {
* @param $index int The index of the vote into the poll
* @return bool|null true if action succeeded.
*/
function deleteByIndex($poll_id, $index) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE poll_id = ?');
return $prepared->execute([$index, $index + 2, $poll_id]);
public function deleteByIndex($poll_id, $index)
{
$qb = $this->createQueryBuilder();
$sql = $this->connect->getDatabasePlatform()->getName() === 'sqlite' ?
$this->generateSQLForInsertDefaultSQLite($qb, $index, true) :
$this->generateSQLForInsertDefault($qb, $index, true)
;
$query = $qb->update(Utils::table('vote'))
->set('choices', $sql)
->where($qb->expr()->eq('poll_id', $qb->createNamedParameter($poll_id)))
;
return $query->execute();
}
function update($poll_id, $vote_id, $name, $choices) {
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
/**
* @param $poll_id
* @param $vote_id
* @param $name
* @param $choices
* @return bool
*/
public function update($poll_id, $vote_id, $name, $choices, $mail)
{
return $this->connect->update(Utils::table('vote'), [
'choices' => $choices,
'name' => $name,
'mail' => $mail,
], [
'poll_id' => $poll_id,
'id' => $vote_id,
]) > 0;
}
/**
@ -78,26 +129,60 @@ class VoteRepository extends AbstractRepository {
*
* @param int $poll_id ID of the poll
* @param string $name Name of the vote
* @throws DBALException
* @return bool true if vote already exists
*/
public function existsByPollIdAndName($poll_id, $name) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?');
$prepared = $this->prepare('SELECT 1 FROM ' . Utils::table('vote') . ' WHERE poll_id = ? AND name = ?');
$prepared->execute([$poll_id, $name]);
return $prepared->rowCount() > 0;
}
/**
* Check if name is already used for the given poll and another vote.
*
* @param int $poll_id ID of the poll
* @param string $name Name of the vote
* @param int $vote_id ID of the current vote
* @throws DBALException
* @return bool true if vote already exists
*/
public function existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id) {
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ? AND id != ?');
$prepared = $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;
}
/**
* @param QueryBuilder $qb
* @param $insert_position
* @param bool $delete
* @return string
*/
private function generateSQLForInsertDefaultSQLite($qb, $insert_position, $delete = false)
{
$position = $insert_position + ($delete ? 2 : 1);
return 'SUBSTR(choices, 1, '
. $qb->createNamedParameter($insert_position)
. ') || " " || SUBSTR(choices, '
. $qb->createNamedParameter($position)
. ')';
}
/**
* @param QueryBuilder $qb
* @param int $insert_position
* @param bool $delete
* @return string
*/
private function generateSQLForInsertDefault($qb, $insert_position, $delete = false)
{
$position = $insert_position + ($delete ? 2 : 1);
return 'CONCAT(SUBSTR(choices, 1, '
. $qb->createNamedParameter($insert_position)
. '), " ", SUBSTR(choices, '
. $qb->createNamedParameter($position)
. '))';
}
}

View File

@ -1,6 +1,5 @@
<?php
namespace Framadate\Security;
/**

View File

@ -1,10 +1,13 @@
<?php
namespace Framadate\Services;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ConnectionException;
use Doctrine\DBAL\DBALException;
use Framadate\Exception\MomentAlreadyExistsException;
use Framadate\FramaDB;
use Framadate\Form;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Utils;
use stdClass;
/**
* Class AdminPollService
@ -21,7 +24,7 @@ class AdminPollService {
private $voteRepository;
private $commentRepository;
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
function __construct(Connection $connect, PollService $pollService, LogService $logService) {
$this->connect = $connect;
$this->pollService = $pollService;
$this->logService = $logService;
@ -31,12 +34,18 @@ class AdminPollService {
$this->commentRepository = RepositoryFactory::commentRepository();
}
function updatePoll($poll) {
global $config;
if ($poll->end_date > $poll->creation_date) {
return $this->pollRepository->update($poll);
}
return false;
/**
* @param Form $poll
* @return bool
*/
public function updatePoll($poll) {
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);
}
/**
@ -91,7 +100,15 @@ class AdminPollService {
*/
function deleteEntirePoll($poll_id) {
$poll = $this->pollRepository->findById($poll_id);
$this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
$this->logService->logEntries(
'DELETE_POLL',
[
"id:$poll->id",
"format:$poll->format",
"admin:$poll->admin_name",
"mail:$poll->admin_mail"
]
);
// Delete the entire poll
$this->voteRepository->deleteByPollId($poll_id);
@ -110,7 +127,10 @@ class AdminPollService {
* @return bool true if action succeeded
*/
public function deleteDateSlot($poll, $slot) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
$this->logService->logEntries(
'DELETE_SLOT',
["id:$poll->id", 'slot:' . json_encode($slot)]
);
$datetime = $slot->title;
$moment = $slot->moment;
@ -158,7 +178,9 @@ class AdminPollService {
}
public function deleteClassicSlot($poll, $slot_title) {
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
$this->logService->logEntries(
['DELETE_SLOT', "id:$poll->id", "slot:$slot_title"]
);
$slots = $this->pollService->allSlotsByPoll($poll);
@ -197,36 +219,50 @@ class AdminPollService {
* @param $datetime int The datetime
* @param $new_moment string The moment's name
* @throws MomentAlreadyExistsException When the moment to add already exists in database
* @throws ConnectionException
*/
public function addDateSlot($poll_id, $datetime, $new_moment) {
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
$this->logService->logEntries(
'ADD_COLUMN',
["id:$poll_id", "datetime:$datetime", "moment:$new_moment"]
);
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
// Begin transaction
$this->connect->beginTransaction();
if ($result->slot !== null) {
$slot = $result->slot;
$moments = explode(',', $slot->moments);
// Check if moment already exists (maybe not necessary)
if (in_array($new_moment, $moments, true)) {
throw new MomentAlreadyExistsException();
}
// Update found slot
$moments[] = $new_moment;
$this->slotRepository->update($poll_id, $datetime, implode(',', $moments));
} else {
$this->slotRepository->insert($poll_id, $datetime, $new_moment);
try {
$slots = $this->slotRepository->listByPollId($poll_id);
$result = $this->findInsertPosition($slots, $datetime);
} catch (DBALException $e) {
$this->logService->log('ERROR', "Database error, couldn't find slot insert position" . $e->getMessage());
return;
}
$this->voteRepository->insertDefault($poll_id, $result->insert);
try {
// Begin transaction
$this->connect->beginTransaction();
// Commit transaction
$this->connect->commit();
if ($result->slot !== null) {
$slot = $result->slot;
$moments = explode(',', $slot->moments);
// Check if moment already exists (maybe not necessary)
if (in_array($new_moment, $moments, true)) {
throw new MomentAlreadyExistsException($slot, $new_moment);
}
// Update found slot
$moments[] = $new_moment;
$this->slotRepository->update($poll_id, $datetime, implode(',', $moments));
} else {
$this->slotRepository->insert($poll_id, $datetime, $new_moment);
}
$this->voteRepository->insertDefault($poll_id, $result->insert);
// Commit transaction
$this->connect->commit();
} catch (DBALException $e) {
$this->logService->log('ERROR', "Database error, couldn't insert date slot" . $e->getMessage());
$this->connect->rollBack();
}
}
/**
@ -238,9 +274,14 @@ class AdminPollService {
* @param $poll_id int The ID of the poll
* @param $title int The title
* @throws MomentAlreadyExistsException When the moment to add already exists in database
* @throws ConnectionException
* @throws \Doctrine\DBAL\DBALException
*/
public function addClassicSlot($poll_id, $title) {
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
$this->logService->logEntries(
'ADD_COLUMN',
["id:$poll_id", "title:$title"]
);
$slots = $this->slotRepository->listByPollId($poll_id);
@ -272,10 +313,10 @@ class AdminPollService {
*
* @param $slots array All the slots of the poll
* @param $datetime int The datetime of the new slot
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
* @return stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
*/
private function findInsertPosition($slots, $datetime) {
$result = new \stdClass();
$result = new stdClass();
$result->slot = null;
$result->insert = 0;
@ -292,14 +333,15 @@ class AdminPollService {
$result->insert += count($moments);
$result->slot = $slot;
break;
} elseif ($datetime < $rowDatetime) {
}
if ($datetime < $rowDatetime) {
// We have to insert before this slot
break;
}
$result->insert += count($moments);
}
$result->insert += count($moments);
}
return $result;
}
}

View File

@ -20,6 +20,7 @@ namespace Framadate\Services;
use DateTime;
use Egulias\EmailValidator\EmailValidator;
use Egulias\EmailValidator\Validation\RFCValidation;
use InvalidArgumentException;
/**
* This class helps to clean all inputs from the users or external services.
@ -115,14 +116,25 @@ class InputService {
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
}
public function filterCollectMail($collectMail) {
return filter_var($collectMail, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => COLLECT_MAIL_CHOICE_REGEX]]);
}
public function filterComment($comment) {
$comment = str_replace("\r\n", "\n", $comment);
return $this->returnIfNotBlank($comment);
}
/**
* @param string $date
* @return DateTime
*/
public function filterDate($date) {
$dDate = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0, 0);
return $dDate->format('Y-m-d H:i:s');
$dDate = parse_translation_date($date);
if ($dDate) {
return $dDate;
}
throw new InvalidArgumentException('Invalid date');
}
/**

View File

@ -9,7 +9,7 @@
*
* =============================
*
* Ce logiciel est r<EFBFBD>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
*
@ -17,6 +17,9 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
namespace Framadate\Services;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Framadate\Utils;
use Smarty;
@ -33,7 +36,9 @@ class InstallService {
'cleanUrl' => true,
// Database configuration
'dbConnectionString' => 'mysql:host=<HOST>;dbname=<SCHEMA>;port=3306',
'dbName' => 'framadate',
'dbPort' => 3306,
'dbHost' => 'localhost',
'dbUser' => 'root',
'dbPassword' => '',
'dbPrefix' => 'fd_',
@ -50,15 +55,15 @@ class InstallService {
public function install(Smarty &$smarty) {
// Check values are present
if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbConnectionString']) || empty($this->fields['dbUser'])) {
return $this->error('MISSING_VALUES');
if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbName']) || empty($this->fields['dbHost']) || empty($this->fields['dbPort']) || empty($this->fields['dbUser'])) {
return $this->error('Missing values');
}
// Connect to database
try {
$connect = $this->connectTo($this->fields['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']);
} catch(\Exception $e) {
return $this->error('CANT_CONNECT_TO_DATABASE', $e->getMessage());
$connect = $this->connectTo($this->fields);
} catch(\Doctrine\DBAL\DBALException $e) {
return $this->error('Unable to connect to database', $e->getMessage());
}
// Write configuration to conf.php file
@ -70,18 +75,20 @@ class InstallService {
}
/**
* Connect to PDO compatible source
*
* @param string $connectionString
* @param string $user
* @param string $password
* @return \PDO
* @param $fields
* @return \Doctrine\DBAL\Connection|null
*/
function connectTo($connectionString, $user, $password) {
$pdo = @new \PDO($connectionString, $user, $password);
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
return $pdo;
function connectTo($fields) {
$doctrineConfig = new Configuration();
$connectionParams = [
'dbname' => $fields['dbName'],
'user' => $fields['dbUser'],
'password' => $fields['dbPassword'],
'host' => $fields['dbHost'],
'driver' => $fields['dbDriver'],
'charset' => $fields['dbDriver'] === 'pdo_mysql' ? 'utf8mb4' : 'utf8',
];
return DriverManager::getConnection($connectionParams, $doctrineConfig);
}
function writeConfiguration(Smarty &$smarty) {
@ -96,6 +103,7 @@ class InstallService {
/**
* @param $content
* @return bool|int
*/
function writeToFile($content) {
return @file_put_contents(CONF_FILENAME, $content);

View File

@ -19,5 +19,19 @@ class LogService {
function log($tag, $message) {
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
}
/**
* Log a list of entries as a single message to the log file.
*
* @param $tag string A tag is used to quickly found a message when reading log file
* @param $entries array some entries to join with comma into a single message
*/
function logEntries($tag, $entries) {
$escapeCommas = function($value) {
return str_replace(',', '-', $value);
};
$message = join(', ', array_map($escapeCommas, $entries));
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
}
}

View File

@ -8,26 +8,51 @@ class MailService {
const MAILSERVICE_KEY = 'mailservice';
/**
* @var bool
*/
private $smtp_allowed;
/**
* @var array
*/
private $smtp_options = [];
/**
* @var bool
*/
private $use_sendmail;
/**
* @var LogService
*/
private $logService;
function __construct($smtp_allowed, $smtp_options = []) {
/**
* MailService constructor.
* @param $smtp_allowed
* @param array $smtp_options
* @param bool $use_sendmail
*/
public function __construct($smtp_allowed, $smtp_options = [], $use_sendmail = false) {
$this->logService = new LogService();
$this->smtp_allowed = $smtp_allowed;
if (true === is_array($smtp_options)) {
$this->smtp_options = $smtp_options;
}
$this->use_sendmail = $use_sendmail;
}
public function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL);
}
public function isEnabled() {
return $this->smtp_allowed === true;
}
public function send($to, $subject, $body, $msgKey = null) {
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
if ($this->isEnabled() && $this->canSendMsg($msgKey)) {
$mail = new PHPMailer(true);
$this->configureMailer($mail);
@ -45,12 +70,14 @@ class MailService {
$mail->Subject = $subject;
// Bodies
$body = $body . ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
$body = $body . ' <br/><br/>' . __('Mail', 'Thank you for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', "\"The road is long, but the way is clear…\"<br/>Framasoft lives only by your donations.<br/>Thank you in advance for your support https://soutenir.framasoft.org");
$mail->isHTML(true);
$mail->msgHTML($body, ROOT_DIR, true);
$mail->CharSet = PHPMailer::CHARSET_UTF8;
$mail->msgHTML($body, ROOT_DIR, function ($html) use ($mail) {
return $this->html2text($mail, $html);
});
// Build headers
$mail->CharSet = 'UTF-8';
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
$mail->addCustomHeader('Return-Path', '<>');
@ -82,7 +109,11 @@ class MailService {
* @param PHPMailer $mailer
*/
private function configureMailer(PHPMailer $mailer) {
$mailer->isSMTP();
if ($this->use_sendmail) {
$mailer->isSendmail();
} else {
$mailer->isSMTP();
}
$available_options = [
'host' => 'Host',
@ -99,4 +130,18 @@ class MailService {
}
}
}
/**
* Custom "advanced" callback to pass to msgHTML function
*
* @param PHPMailer $mailer a PHPMailer instance
* @param string $html the HTML body of an email
*/
private function html2text(PHPMailer $mailer, $html) {
$html = preg_replace('/<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/si', '${2}: ${1}', $html);
$html = preg_replace('/<br\/>/', "\n", $html);
return $mailer->html2text($html);
}
}

View File

@ -1,6 +1,5 @@
<?php
namespace Framadate\Services;
use \stdClass;
@ -14,10 +13,20 @@ class NotificationService {
const UPDATE_POLL = 10;
const DELETED_POLL = 11;
private $mailService;
const TEMPLATES_MAPPING = [
self::UPDATE_VOTE => 'mail/updated_vote.html.tpl',
self::ADD_VOTE => 'mail/added_vote.html.tpl',
self::ADD_COMMENT => 'mail/added_comment.html.tpl',
self::UPDATE_POLL => 'mail/updated_poll.html.tpl',
self::DELETED_POLL => 'mail/deleted_poll.html.tpl'
];
function __construct(MailService $mailService) {
private $mailService;
private $smarty;
function __construct(MailService $mailService, \Smarty $smarty) {
$this->mailService = $mailService;
$this->smarty = $smarty;
}
/**
@ -38,42 +47,24 @@ class NotificationService {
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
if (self::isParticipation($type)) {
$translationString = 'Poll\'s participation: %s';
$translationString = 'Poll participation: %s';
} else {
$translationString = 'Notification of poll: %s';
}
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', $translationString, $poll->title);
$message = '';
$this->smarty->assign('username', $name);
$this->smarty->assign('poll_title', $poll->title);
$this->smarty->assign('poll_url', Utils::getUrlSondage($poll->admin_id, true));
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
$link = '<a href="' . $urlSondage . '">' . $urlSondage . '</a>' . "\n\n";
$template_name = self::TEMPLATES_MAPPING[$type];
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;
if (!is_null($template_name)) {
$message = $this->smarty->fetch($template_name);
} else {
$message = '';
}
$messageTypeKey = $type . '-' . $poll->id;
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
}
@ -83,4 +74,40 @@ class NotificationService {
{
return $type >= self::UPDATE_POLL;
}
function sendPollCreationMails($creator_mail, $creator_name, $poll_name, $poll_id, $admin_poll_id) {
if (!$this->mailService->isEnabled() || !$this->mailService->isValidEmail($creator_mail)) {
return null;
}
$this->smarty->assign('poll_creator_name', Utils::htmlMailEscape($creator_name));
$this->smarty->assign('poll_name', Utils::htmlMailEscape($poll_name));
$this->smarty->assign('poll_url', Utils::getUrlSondage($poll_id));
$message_participants = $this->smarty->fetch('mail/participants_forward_email.html.tpl');
$this->mailService->send($creator_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Participant link') . '] ' . __('Generic', 'Poll') . ': ' . $poll_name, $message_participants);
$this->smarty->assign('poll_admin_url', Utils::getUrlSondage($admin_poll_id, true));
$message_admin = $this->smarty->fetch('mail/creation_notification_email.html.tpl');
$this->mailService->send($creator_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Message for the author') . '] ' . __('Generic', 'Poll') . ': ' . $poll_name, $message_admin);
}
function sendEditedVoteNotification($email, &$poll, $poll_id, $edited_vote_id) {
$url = Utils::getUrlSondage($poll_id, false, $edited_vote_id);
$this->smarty->assign('poll', $poll);
$this->smarty->assign('poll_id', $poll_id);
$this->smarty->assign('editedVoteUniqueId', $edited_vote_id);
$body = $this->smarty->fetch('mail/remember_edit_link.tpl');
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
$this->mailService->send($email, $subject, $body);
}
function sendFindPollsByMailNotification($mail, &$polls) {
$this->smarty->assign('polls', $polls);
$body = $this->smarty->fetch('mail/find_polls.tpl');
$this->mailService->send($mail, __('FindPolls', 'List of your polls') . ' - ' . NOMAPPLICATION, $body, 'SEND_POLLS');
}
}

View File

@ -18,26 +18,49 @@
*/
namespace Framadate\Services;
use DateInterval;
use DateTime;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\ConnectionException;
use Doctrine\DBAL\DBALException;
use Exception;
use Framadate\Exception\AlreadyExistsException;
use Framadate\Exception\ConcurrentEditionException;
use Framadate\Exception\ConcurrentVoteException;
use Framadate\Form;
use Framadate\FramaDB;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\Token;
use RuntimeException;
use stdClass;
class PollService {
private $connect;
private $logService;
private $pollRepository;
private $slotRepository;
private $voteRepository;
private $commentRepository;
/**
* @var NotificationService
*/
private $notificationService;
/**
* @var SessionService
*/
private $sessionService;
/**
* @var PurgeService
*/
private $purgeService;
function __construct(FramaDB $connect, LogService $logService) {
$this->connect = $connect;
/**
* @var LogService
*/
private $logService;
public function __construct(LogService $logService, NotificationService $notificationService, SessionService $sessionService, PurgeService $purgeService) {
$this->logService = $logService;
$this->notificationService = $notificationService;
$this->sessionService = $sessionService;
$this->purgeService = $purgeService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
$this->voteRepository = RepositoryFactory::voteRepository();
@ -48,26 +71,48 @@ 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
* @return stdClass|null The found poll, or null
*/
function findById($poll_id) {
if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id);
try {
if (preg_match(POLL_REGEX, $poll_id)) {
return $this->pollRepository->findById($poll_id);
}
} catch (DBALException $e) {
$this->logService->log('ERROR', 'Database error : ' . $e->getMessage());
}
return null;
}
/**
* @param $admin_poll_id
* @return mixed|null
*/
public function findByAdminId($admin_poll_id) {
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
return $this->pollRepository->findByAdminId($admin_poll_id);
try {
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
return $this->pollRepository->findByAdminId($admin_poll_id);
}
} catch (DBALException $e) {
$this->logService->log('ERROR', 'Database error : ' . $e->getMessage());
}
return null;
}
function allCommentsByPollId($poll_id) {
return $this->commentRepository->findAllByPollId($poll_id);
/**
* @param $poll_id
* @return array
*/
public function allCommentsByPollId($poll_id)
{
try {
return $this->commentRepository->findAllByPollId($poll_id);
} catch (DBALException $e) {
$this->logService->log('error', $e->getMessage());
return null;
}
}
function allVotesByPollId($poll_id) {
@ -88,74 +133,149 @@ class PollService {
* @param $name
* @param $choices
* @param $slots_hash
* @param string $mail
* @throws AlreadyExistsException
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @return bool
*/
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash) {
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash, $mail) {
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
// Update vote
$choices = implode($choices);
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices, $mail);
}
/**
* @param $poll_id
* @param $name
* @param $choices
* @param $slots_hash
* @param string $mail
* @throws AlreadyExistsException
* @throws ConcurrentEditionException
* @throws ConcurrentVoteException
* @return \stdClass
* @return stdClass
*/
function addVote($poll_id, $name, $choices, $slots_hash) {
function addVote($poll_id, $name, $choices, $slots_hash, $mail) {
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
// Insert new vote
$choices = implode($choices);
$token = $this->random(16);
return $this->voteRepository->insert($poll_id, $name, $choices, $token);
return $this->voteRepository->insert($poll_id, $name, $choices, $token, $mail);
}
function addComment($poll_id, $name, $comment) {
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
return true;
}
return $this->commentRepository->insert($poll_id, $name, $comment);
}
/**
* Main entry point for poll creation. This action does all the following:
* - Validate input data
* - Create the poll in database
* - Send emails
* - Cleanups post creation (session and purges)
*
* @param Form $form
* @return array
* @param string $end_date
* @return string|null returns the admin_id if the poll was created
*/
function createPoll(Form $form) {
// Generate poll IDs, loop while poll ID already exists
function doPollCreation(Form $form, $end_date) {
// Min/Max archive date
$min_expiry_time = $this->minExpiryDate();
$max_expiry_time = $this->maxExpiryDate();
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));
if (!empty($end_date)) {
$registredate = explode('/', $end_date);
if (is_array($registredate) && count($registredate) === 3) {
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
if ($time < $min_expiry_time) {
$form->end_date = $min_expiry_time;
} elseif ($max_expiry_time < $time) {
$form->end_date = $max_expiry_time;
} else {
$form->end_date = $time;
}
}
}
// Insert poll + slots
if (empty($form->end_date)) {
// By default, expiration date is 6 months after last day
$form->end_date = $max_expiry_time;
}
// Insert poll in database
list($poll_id, $admin_poll_id) = $this->createPoll($form);
// Send confirmation by mail if enabled
if (!is_null($ids)) {
// Everything went well
$this->notificationService->sendPollCreationMails($form->admin_mail, $form->admin_name, $form->title, $poll_id, $admin_poll_id);
// Clean Form data in session
$this->sessionService->removeAll('form');
// Creation message
$this->sessionService->set("Framadate", "messagePollCreated", TRUE);
// Delete old polls
$this->purgeService->repeatedCleanings();
} else {
// Add an error in the form
$form->errors = [
__('Error', 'GenericErrorPollCreation')
];
// TODO: change this to use sessionService function
$_SESSION['form'] = serialize($form);
}
return $admin_poll_id;
}
/**
* @param Form $form
* @throws ConnectionException
* @return array
*/
public function createPoll(Form $form) {
// Generate poll IDs, loop while poll ID already exists
$this->pollRepository->beginTransaction();
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
$this->pollRepository->commit();
try {
if (empty($form->id)) { // User want us to generate an id for him
do {
$poll_id = $this->random(16);
} while ($this->pollRepository->existsById($poll_id));
$admin_poll_id = $poll_id . $this->random(8);
} else { // User have chosen the poll id
$poll_id = $form->id;
do {
$admin_poll_id = $this->random(24);
} while ($this->pollRepository->existsByAdminId($admin_poll_id));
}
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
// Insert poll + slots
$this->pollRepository->insertPoll($poll_id, $admin_poll_id, $form);
$this->slotRepository->insertSlots($poll_id, $form->getChoices());
$this->pollRepository->commit();
return [$poll_id, $admin_poll_id];
$this->logService->log(
'CREATE_POLL',
'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail
);
return [$poll_id, $admin_poll_id];
} catch (DBALException $e) {
$this->pollRepository->rollback();
$this->logService->log('ERROR', "Poll couldn't be saved : " . $e->getMessage());
return null;
}
}
public function findAllByAdminMail($mail) {
@ -164,7 +284,7 @@ class PollService {
/**
* @param array $votes
* @param \stdClass $poll
* @param stdClass $poll
* @return array
*/
public function computeBestChoices($votes, $poll) {
@ -196,8 +316,8 @@ class PollService {
function splitSlots($slots) {
$splitted = [];
foreach ($slots as $slot) {
$obj = new \stdClass();
$obj->day = $slot->title;
$obj = new stdClass();
$obj->day = (new DateTime())->setTimestamp((int) $slot->title);
$obj->moments = explode(',', $slot->moments);
$splitted[] = $obj;
@ -219,11 +339,12 @@ class PollService {
function splitVotes($votes) {
$splitted = [];
foreach ($votes as $vote) {
$obj = new \stdClass();
$obj = new stdClass();
$obj->id = $vote->id;
$obj->name = $vote->name;
$obj->uniqId = $vote->uniqId;
$obj->choices = str_split($vote->choices);
$obj->mail = $vote->mail;
$splitted[] = $obj;
}
@ -232,18 +353,25 @@ class PollService {
}
/**
* @return int The max timestamp allowed for expiry date
* @throws Exception
* @return DateTime The max timestamp allowed for expiry date
*/
public function maxExpiryDate() {
global $config;
return time() + (86400 * $config['default_poll_duration']);
try {
global $config;
$default_poll_duration = isset($config['default_poll_duration']) ? $config['default_poll_duration'] : 60;
return (new DateTime())->add(new DateInterval('P' . $default_poll_duration . 'D'));
} catch (Exception $e) {
throw new RuntimeException('Configuration Exception');
}
}
/**
* @return int The min timestamp allowed for expiry date
* @throws Exception
* @return DateTime The min timestamp allowed for expiry date
*/
public function minExpiryDate() {
return time() + 86400;
return (new DateTime())->add(new DateInterval('P1D'));
}
/**
@ -257,7 +385,7 @@ class PollService {
}
/**
* @param \stdClass $poll
* @param stdClass $poll
* @return array
*/
private function computeEmptyBestChoices($poll)
@ -292,7 +420,7 @@ class PollService {
private function random($length) {
return Token::getToken($length);
}
/**
* @param $choices
* @param $poll_id
@ -310,20 +438,20 @@ class PollService {
} else {
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
}
if ($exists) {
throw new AlreadyExistsException();
}
$poll = $this->findById($poll_id);
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint
$this->checkMaxVotes($choices, $poll, $poll_id);
// Check if slots are still the same
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
}
/**
* This method checks if the hash send by the user is the same as the computed hash.
*
@ -342,7 +470,7 @@ class PollService {
* This method checks if the votes doesn't conflicts the maximum votes constraint
*
* @param $user_choice
* @param \stdClass $poll
* @param stdClass $poll
* @param string $poll_id
* @throws ConcurrentVoteException
*/

View File

@ -1,6 +1,7 @@
<?php
namespace Framadate\Services;
use Framadate\FramaDB;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Framadate\Repositories\RepositoryFactory;
/**
@ -15,7 +16,7 @@ class PurgeService {
private $voteRepository;
private $commentRepository;
function __construct(FramaDB $connect, LogService $logService) {
function __construct(Connection $connect, LogService $logService) {
$this->logService = $logService;
$this->pollRepository = RepositoryFactory::pollRepository();
$this->slotRepository = RepositoryFactory::slotRepository();
@ -23,52 +24,91 @@ class PurgeService {
$this->commentRepository = RepositoryFactory::commentRepository();
}
public function repeatedCleanings() {
$this->purgeOldPolls();
if (0 === time() % 10) {
$this->cleanDemoPoll();
}
}
/**
* This methode purges all old polls (the ones with end_date in past).
*
* @return bool true is action succeeded
*/
function purgeOldPolls() {
$oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls);
public function purgeOldPolls() {
try {
$oldPolls = $this->pollRepository->findOldPolls();
$count = count($oldPolls);
if ($count > 0) {
$this->logService->log('EXPIRATION', 'Going to purge ' . $count . ' poll(s)...');
if ($count > 0) {
$this->logService->log('EXPIRATION', 'Going to purge ' . $count . ' poll(s)...');
foreach ($oldPolls as $poll) {
if ($this->purgePollById($poll->id)) {
$this->logService->log('EXPIRATION_SUCCESS', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
} else {
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
foreach ($oldPolls as $poll) {
if ($this->purgePollById($poll->id)) {
$this->logService->log(
'EXPIRATION_SUCCESS',
'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name
);
} else {
$this->logService->log(
'EXPIRATION_FAILED',
'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name
);
}
}
}
return $count;
} catch (DBALException $e) {
$this->logService->log('ERROR', $e->getMessage());
return false;
}
return $count;
}
public function cleanDemoPoll() {
if (!defined("DEMO_POLL_ID") || !defined("DEMO_POLL_NUMBER_VOTES")) {
return;
}
$this->voteRepository->beginTransaction();
$demoVotes = $this->voteRepository->allUserVotesByPollId(DEMO_POLL_ID);
$votesToDelete = count($demoVotes) - DEMO_POLL_NUMBER_VOTES;
if ($votesToDelete > 0) {
$this->voteRepository->deleteOldVotesByPollId(DEMO_POLL_ID, $votesToDelete);
}
$this->voteRepository->commit();
}
/**
* This methode delete all data about a poll.
*
* @param $poll_id int The ID of the poll
* @return bool true is action succeeded
*/
function purgePollById($poll_id) {
private function purgePollById($poll_id) {
$done = true;
$this->pollRepository->beginTransaction();
$done &= $this->commentRepository->deleteByPollId($poll_id);
$done &= $this->voteRepository->deleteByPollId($poll_id);
$done &= $this->slotRepository->deleteByPollId($poll_id);
$done &= $this->pollRepository->deleteById($poll_id);
try {
$this->pollRepository->beginTransaction();
$done &= $this->commentRepository->deleteByPollId($poll_id);
$done &= $this->voteRepository->deleteByPollId($poll_id);
$done &= $this->slotRepository->deleteByPollId($poll_id);
$done &= $this->pollRepository->deleteById($poll_id);
if ($done) {
$this->pollRepository->commit();
} else {
$this->pollRepository->rollback();
if ($done) {
$this->pollRepository->commit();
} else {
$this->pollRepository->rollback();
}
} catch (DBALException $e) {
$this->logService->log('ERROR', $e->getMessage());
}
return $done;
}
}

View File

@ -1,6 +1,5 @@
<?php
namespace Framadate\Services;
class SessionService {
@ -55,9 +54,20 @@ class SessionService {
unset($_SESSION[$section][$key]);
}
/**
* Remove a session section
*
* @param $section
*/
public function removeAll($section) {
assert(!empty($section));
unset($_SESSION[$section]);
}
private function initSectionIfNeeded($section) {
if (!isset($_SESSION[$section])) {
$_SESSION[$section] = [];
}
}
}
}

View File

@ -25,15 +25,23 @@ class Utils {
* @return string Server name
*/
public static function get_server_name() {
$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'];
$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
$serverPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '';
$scheme = (
(defined('FORCE_HTTPS') && FORCE_HTTPS ) ||
(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
) ? 'https' : 'http';
$domain = defined('APP_URL') ? APP_URL : $serverName;
$port = in_array($serverPort, ['80', '443'], true) ? '' : ':' . $serverPort;
$dirname = dirname($_SERVER['SCRIPT_NAME']);
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
$dirname = str_replace('/admin', '', $dirname);
$dirname = str_replace('/action', '', $dirname);
$server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $dirname;
return $scheme . '://' . preg_replace('#//+#', '/', $server_name);
return $scheme . '://' . preg_replace('#//+#', '/', $domain . $port . $dirname);
}
/**

117
app/inc/config.test.php Normal file
View File

@ -0,0 +1,117 @@
<?php
/**
* This software is governed by the CeCILL-B license. If a copy of this license
* is not distributed with this file, you can obtain one at
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
*
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
*
* =============================
*
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
*
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// Fully qualified domain name of your webserver.
// If this is unset or empty, the servername is determined automatically.
// You *have to set this* if you are running Framadate behind a reverse proxy.
// const APP_URL = '<www.mydomain.fr>';
// Application name
const NOMAPPLICATION = 'Framadate';
// Database administrator email
const ADRESSEMAILADMIN = 'admin@app.tld';
// Email for automatic responses (you should set it to "no-reply")
const ADRESSEMAILREPONSEAUTO = 'no@reply';
// Database driver
const DB_DRIVER = FRAMADATE_DB_DRIVER;
// Database name
const DB_NAME = FRAMADATE_DB_NAME;
// Database host
const DB_HOST = FRAMADATE_DB_HOST;
// Database port
const DB_PORT = FRAMADATE_DB_PORT;
// Database user
const DB_USER = FRAMADATE_DB_USER;
// Database password
const DB_PASSWORD = FRAMADATE_DB_PASSWORD;
// Table name prefix
const TABLENAME_PREFIX = 'fd_';
// Name of the table that stores migration script already executed
const MIGRATION_TABLE = 'framadate_migration';
// Default Language
const DEFAULT_LANGUAGE = 'fr';
// List of supported languages, fake constant as arrays can be used as constants only in PHP >=5.6
$ALLOWED_LANGUAGES = [
'fr' => 'Français',
'en' => 'English',
'oc' => 'Occitan',
'es' => 'Español',
'de' => 'Deutsch',
'nl' => 'Dutch',
'it' => 'Italiano',
'br' => 'Brezhoneg',
'hu' => 'Magyar',
];
// Path to image file with the title
const IMAGE_TITRE = 'images/logo-framadate.png';
// Clean URLs, boolean
const URL_PROPRE = false;
// Use REMOTE_USER data provided by web server
const USE_REMOTE_USER = true;
// Path to the log file
const LOG_FILE = 'admin/stdout.log';
// Days (after expiration date) before purging a poll
const PURGE_DELAY = 60;
// Max slots per poll
const MAX_SLOTS_PER_POLL = 366;
// Number of seconds before we allow to resend an "Remember Edit Link" email.
const TIME_EDIT_LINK_EMAIL = 60;
// Config
$config = [
/* general config */
'use_smtp' => false, // use email for polls creation/modification/responses notification
'smtp_options' => [
'host' => 'localhost', // SMTP server (you could add many servers (main and backup for example) : use ";" like separator
'auth' => false, // Enable SMTP authentication
'username' => '', // SMTP username
'password' => '', // SMTP password
'secure' => '', // Enable encryption (false, tls or ssl)
'port' => 25, // TCP port to connect to
],
/* home */
'show_what_is_that' => true, // display "how to use" section
'show_the_software' => true, // display technical information about the software
'show_cultivate_your_garden' => true, // display "development and administration" information
/* create_classic_poll.php / create_date_poll.php */
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
/* create_classic_poll.php */
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
'markdown_editor_by_default' => true, // The markdown editor for the description is enabled by default
'provide_fork_awesome' => true, // Whether the build-in fork-awesome should be provided
];

View File

@ -18,7 +18,7 @@
*/
// FRAMADATE version
const VERSION = '1.1.8';
const VERSION = '1.2.0-alpha.1';
// PHP Needed version
const PHP_NEEDED_VERSION = '5.6';
@ -33,6 +33,7 @@ const CHOICE_REGEX = '/^[ 012]$/';
const BOOLEAN_REGEX = '/^(on|off|true|false|1|0)$/i';
const BOOLEAN_TRUE_REGEX = '/^(on|true|1)$/i';
const EDITABLE_CHOICE_REGEX = '/^[0-2]$/';
const COLLECT_MAIL_CHOICE_REGEX = '/^[0-3]$/';
const BASE64_REGEX = '/^[A-Za-z0-9]+$/';
const MD5_REGEX = '/^[A-Fa-f0-9]{32}$/';

View File

@ -17,29 +17,134 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
// Prepare I18N instance
$i18n = \o80\i18n\I18N::instance();
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
$i18n->setPath(__DIR__ . '/../../locale');
const DATE_FORMAT_FULL = 'EEEE d MMMM y';
const DATE_FORMAT_SHORT = 'EEEE d MMMM y';
const DATE_FORMAT_DAY = 'E d';
const DATE_FORMAT_DATE = 'dd-MM-y';
const DATE_FORMAT_MONTH_YEAR = 'MMMM y';
const DATE_FORMAT_DATETIME_SHORT = 'EEEE d';
// Change langauge when user asked for it
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES), true)) {
$_SESSION['lang'] = $_POST['lang'];
// Change session language when requested
if (isset($_REQUEST['lang'])
&& array_key_exists($_REQUEST['lang'], $ALLOWED_LANGUAGES)) {
$_SESSION['lang'] = $_REQUEST['lang'];
}
/* <html lang="$locale"> */
$i18n->get('', 'Something, just to load the dictionary');
$locale = str_replace('_', '-', $i18n->getLoadedLang());
// Try the user-specified locale, or the browser-specified locale.
if (isset($_SESSION['lang'])) {
$wanted_locale = $_SESSION['lang'];
} else {
$wanted_locale = locale_accept_from_http($_SERVER['HTTP_ACCEPT_LANGUAGE']);
}
// Use the best available locale.
$locale = locale_lookup(array_keys($ALLOWED_LANGUAGES), $wanted_locale, false, DEFAULT_LANGUAGE);
/* Date Format */
$date_format['txt_full'] = __('Date', 'FULL'); //summary in create_date_poll.php and removal date in choix_(date|autre).php
$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');
$date_format['txt_datetime_short'] = __('Date', 'DATETIME');
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { //%e can't be used on Windows platform, use %#d instead
foreach ($date_format as $k => $v) {
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
/**
* Formats a DateTime according to the IntlDateFormatter
*
* @param DateTime $date
* @param string $pattern
* @param $forceLocale
* @return string
*/
function date_format_intl(DateTime $date, $pattern = DATE_FORMAT_FULL, $forceLocale = null) {
global $locale;
$local_locale = $forceLocale || $locale;
$dateFormatter = IntlDateFormatter::create(
$local_locale,
IntlDateFormatter::FULL,
IntlDateFormatter::FULL,
date_default_timezone_get(),
IntlDateFormatter::GREGORIAN,
$pattern
);
return $dateFormatter->format($date);
}
/**
* Formats a DateTime according to a translated format
*
* @param DateTime $date
* @param string $pattern
* @return string
*/
function date_format_translation(DateTime $date, $pattern = 'Y-m-d') {
return $date->format(__('Date', $pattern));
}
/**
* Converts a string into a DateTime according to the IntlDateFormatter
*
* @param $dateString
* @param string $pattern
* @param string|null $forceLocale
* @return DateTime|null
*/
function parse_intl_date($dateString, $pattern = DATE_FORMAT_DATE, $forceLocale = null) {
global $locale;
$local_locale = $forceLocale || $locale;
$dateFormatter = IntlDateFormatter::create(
$local_locale,
IntlDateFormatter::FULL,
IntlDateFormatter::FULL,
date_default_timezone_get(),
IntlDateFormatter::GREGORIAN,
$pattern
);
$timestamp = $dateFormatter->parse($dateString);
try {
return (new DateTime())->setTimestamp($timestamp);
} catch (Exception $e) {
return null;
}
}
/**
* Converts a string into a DateTime according to a translated format
*
* @param string $dateString
* @param string $pattern
* @return DateTime
*/
function parse_translation_date($dateString, $pattern = 'Y-m-d') {
return DateTime::createFromFormat(__('Date', $pattern), $dateString);
}
/* i18n helper functions */
use Symfony\Component\Translation\Loader\PoFileLoader;
use Symfony\Component\Translation\Translator;
class __i18n {
private static $translator;
private static $fallbacktranslator;
public static function init($locale) {
self::$translator = new Translator($locale);
self::$translator->addLoader('pofile', new PoFileLoader());
self::$translator->addResource('pofile', ROOT_DIR . "po/{$locale}.po", $locale);
# Fallback:
# For Symfony/Translation, empty strings are valid, but in po files, untranslated strings are "".
# This means we cannot use the standard $translator->setFallbackLocales() mechanism :(
self::$fallbacktranslator = new Translator(DEFAULT_LANGUAGE);
self::$fallbacktranslator->addLoader('pofile', new PoFileLoader());
self::$fallbacktranslator->addResource('pofile', ROOT_DIR . "po/" . DEFAULT_LANGUAGE . ".po", DEFAULT_LANGUAGE);
}
public static function translate($key) {
return self::$translator->trans($key)
?: self::$fallbacktranslator->trans($key);
}
}
__i18n::init($locale);
function __($section, $key) {
return __i18n::translate($key);
}
function __f($section, $key, $args) {
$msg = __i18n::translate($key);
$args = array_slice(func_get_args(), 2);
return vsprintf($msg, $args);
}

View File

@ -16,12 +16,15 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\FramaDB;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\DriverManager;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Services\LogService;
// Autoloading of dependencies with Composer
require_once __DIR__ . '/../../vendor/autoload.php';
require_once __DIR__ . '/../../vendor/o80/i18n/src/shortcuts.php';
if (session_id() === '') {
session_start();
@ -32,17 +35,48 @@ if (ini_get('date.timezone') === '') {
}
define('ROOT_DIR', __DIR__ . '/../../');
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
$path = '/app/inc/config.php';
if (getenv('APP_ENV') === 'test') {
$path = '/app/inc/config.test.php';
// Read DB connection params from Environment Variables if possible
define('FRAMADATE_DB_DRIVER', getenv('FRAMADATE_DB_DRIVER') ? getenv('FRAMADATE_DB_DRIVER') : 'pdo_sqlite');
define('FRAMADATE_DB_NAME', getenv('FRAMADATE_DB_NAME') ? getenv('FRAMADATE_DB_NAME') : 'framadate');
define('FRAMADATE_DB_HOST', getenv('FRAMADATE_DB_HOST') ? getenv('FRAMADATE_DB_HOST') : '');
define('FRAMADATE_DB_PORT', getenv('FRAMADATE_DB_PORT') ? getenv('FRAMADATE_DB_PORT') : '');
define('FRAMADATE_DB_USER', getenv('FRAMADATE_DB_USER') ? getenv('FRAMADATE_DB_USER') : '');
define('FRAMADATE_DB_PASSWORD', getenv('FRAMADATE_DB_PASSWORD') ? getenv('FRAMADATE_DB_PASSWORD') : '');
}
define('CONF_FILENAME', ROOT_DIR . $path);
require_once __DIR__ . '/constants.php';
if (is_file(CONF_FILENAME)) {
@include_once __DIR__ . '/config.php';
@include_once CONF_FILENAME;
// Connection to database
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
RepositoryFactory::init($connect);
$err = 0;
$doctrineConfig = new Configuration();
$connectionParams = [
'dbname' => DB_NAME,
'user' => DB_USER,
'password' => DB_PASSWORD,
'host' => DB_HOST,
'driver' => DB_DRIVER,
'charset' => DB_DRIVER === 'pdo_mysql' ? 'utf8mb4' : 'utf8',
];
if (DB_DRIVER === 'pdo_sqlite') {
$connectionParams['path'] = ROOT_DIR . '/test_database.sqlite';
}
try {
$connect = DriverManager::getConnection($connectionParams, $doctrineConfig);
RepositoryFactory::init($connect);
$err = 0;
} catch (DBALException $e) {
$logger = new LogService();
$logger->log('ERROR', $e->getMessage());
}
} else {
define('NOMAPPLICATION', 'Framadate');
define('DEFAULT_LANGUAGE', 'fr');
@ -61,3 +95,6 @@ if (is_file(CONF_FILENAME)) {
require_once __DIR__ . '/i18n.php';
// Smarty
require_once __DIR__ . '/smarty.php';
require_once __DIR__ . '/services.php';
Services::init($connect, $smarty);

94
app/inc/services.php Normal file
View File

@ -0,0 +1,94 @@
<?php
use Doctrine\DBAL\Connection;
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\PurgeService;
use Framadate\Services\SecurityService;
use Framadate\Services\SessionService;
class Services {
private static $connect;
private static $smarty;
private static $adminPollService;
private static $inputService;
private static $logService;
private static $mailService;
private static $notificationService;
private static $pollService;
private static $purgeService;
private static $securityService;
private static $sessionService;
public static function init(Connection $connect, \Smarty $smarty) {
self::$connect = $connect;
self::$smarty = $smarty;
}
public static function adminPoll() {
if (self::$adminPollService === null) {
self::$adminPollService = new AdminPollService(self::$connect, self::poll(), self::log());
}
return self::$adminPollService;
}
public static function input() {
if (self::$inputService === null) {
self::$inputService = new InputService();
}
return self::$inputService;
}
public static function log() {
if (self::$logService === null) {
self::$logService = new LogService();
}
return self::$logService;
}
public static function mail() {
if (self::$mailService === null) {
self::$mailService = new MailService($config['use_smtp'], $config['smtp_options'], $config['use_sendmail']);
}
return self::$mailService;
}
public static function notification() {
if (self::$notificationService === null) {
self::$notificationService = new NotificationService(self::mail(), self::$smarty);
}
return self::$notificationService;
}
public static function poll() {
if (self::$pollService === null) {
self::$pollService = new PollService(self::log(), self::notification(), self::session(), self::purge());
}
return self::$pollService;
}
public static function purge() {
if (self::$purgeService === null) {
self::$purgeService = new PurgeService(self::$connect, self::log());
}
return self::$purgeService;
}
public static function session() {
if (self::$sessionService === null) {
self::$sessionService = new SessionService();
}
return self::$sessionService;
}
public static function security() {
if (self::$securityService === null) {
self::$securityService = new SecurityService();
}
return self::$securityService;
}
}

View File

@ -25,15 +25,18 @@ $smarty->setCompileDir(ROOT_DIR . COMPILE_DIR);
$smarty->setCacheDir(ROOT_DIR . '/cache/');
$smarty->caching = false;
$serverName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
$smarty->assign('APPLICATION_NAME', NOMAPPLICATION);
$smarty->assign('APPLICATION_VERSION', VERSION);
$smarty->assign('SERVER_URL', Utils::get_server_name());
$smarty->assign('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']);
$smarty->assign('TITLE_IMAGE', IMAGE_TITRE);
$smarty->assign('use_nav_js', strstr($_SERVER['SERVER_NAME'], 'framadate.org'));
$smarty->assign('use_nav_js', strstr($serverName, 'framadate.org'));
$smarty->assign('provide_fork_awesome', !isset($config['provide_fork_awesome']) || $config['provide_fork_awesome']);
$smarty->assign('locale', $locale);
$smarty->assign('langs', $ALLOWED_LANGUAGES);
$smarty->assign('date_format', $date_format);
if (isset($config['tracking_code'])) {
$smarty->assign('tracking_code', $config['tracking_code']);
}
@ -42,7 +45,8 @@ if (defined('FAVICON')) {
}
// Dev Mode
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
if (php_sapi_name() === 'cli-server') {
$smarty->caching = 0;
$smarty->force_compile = true;
$smarty->compile_check = true;
} else {
@ -77,6 +81,21 @@ function smarty_modifier_html($html) {
return Utils::htmlEscape($html);
}
/**
* markdown_to_text
* Retrieves a markdown string and tries to make a plain text value
*
* @param array $options
* @return string
*/
function smarty_function_markdown_to_text($options, Smarty_Internal_Template $template)
{
global $locale;
$text = strip_tags(Parsedown::instance()->text($options['markdown']));
$number_letters = (new NumberFormatter($locale, NumberFormatter::ORDINAL))->format($options['id'] + 1);
return $text !== '' ? $text : __f('Poll results', '%s option', $number_letters);
}
function smarty_modifier_html_special_chars($html) {
return Utils::htmlMailEscape($html);
}
@ -106,3 +125,38 @@ function smarty_modifier_locale_2_lang($locale) {
function path_for_datepicker_locale($lang) {
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
}
/**
* @param $date
* @param string $pattern
* @return string
*/
function smarty_modifier_date_format_intl(DateTime $date, $pattern) {
return date_format_intl($date, $pattern);
}
/**
* @param DateTime $date
* @return int
*/
function smarty_modifier_date_to_timestamp(DateTime $date) {
return $date->getTimestamp();
}
/**
* @param integer $timestamp
* @throws Exception
* @return DateTime
*/
function smarty_modifier_timestamp_to_date($timestamp) {
return (new DateTime())->setTimestamp((int) $timestamp);
}
/**
* @param DateTime $date
* @param string $pattern
* @return bool|DateTime
*/
function smarty_modifier_date_format_translation(DateTime $date, $pattern = 'Y-m-d') {
return date_format_translation($date, $pattern);
}

View File

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

View File

@ -30,9 +30,9 @@ function bandeau_titre($titre)
if(count($ALLOWED_LANGUAGES) > 1){
echo '<form method="post" action="" class="hidden-print">
<div class="input-group input-group-sm pull-right col-md-2 col-xs-4">
<select name="lang" class="form-control" title="' . __('Language selector', 'Select the language') . '" >' . liste_lang() . '</select>
<select name="lang" class="form-control" title="' . __('Language selector', 'Select language') . '" >' . liste_lang() . '</select>
<span class="input-group-btn">
<button type="submit" class="btn btn-default btn-sm" title="' . __('Language selector', 'Change the language') . '">OK</button>
<button type="submit" class="btn btn-default btn-sm" title="' . __('Language selector', 'Change language') . '">OK</button>
</span>
</div>
</form>';
@ -43,15 +43,6 @@ function bandeau_titre($titre)
<hr class="trait" role="presentation" />
</header>
<main role="main">';
global $connect;
$tables = $connect->allTables();
$diff = array_diff([Utils::table('comment'), Utils::table('poll'), Utils::table('slot'), Utils::table('vote')], $tables);
if (0 !== count($diff)) {
echo '<div class="alert alert-danger">' . __('Error', 'Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') . '</div>';
bandeau_pied();
die();
}
}
function liste_lang()

59
bin/doctrine Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env php
<?php
use Doctrine\DBAL\Migrations\Configuration\Configuration;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Framadate\Utils;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Style\SymfonyStyle;
try {
require_once __DIR__ . '/../app/inc/init.php';
$input = new ArgvInput();
$output = new ConsoleOutput();
$style = new SymfonyStyle($input, $output);
if ($connect === null) {
throw new \Exception("Undefined database connection\n");
}
// replace the ConsoleRunner::run() statement with:
$cli = new Application('Doctrine Command Line Interface', VERSION);
$cli->setCatchExceptions(true);
$helperSet = new HelperSet(
[
'db' => new ConnectionHelper($connect),
'question' => new QuestionHelper(),
]
);
$cli->setHelperSet($helperSet);
$migrateCommand = new \Doctrine\DBAL\Migrations\Tools\Console\Command\MigrateCommand();
$statusCommand = new \Doctrine\DBAL\Migrations\Tools\Console\Command\StatusCommand();
$migrationsDirectory = __DIR__ . '/../app/classes/Framadate/Migrations';
$configuration = new Configuration($connect);
$configuration->setMigrationsTableName(Utils::table(MIGRATION_TABLE) . '_new');
$configuration->setMigrationsDirectory($migrationsDirectory);
$configuration->setMigrationsNamespace('DoctrineMigrations');
$configuration->registerMigrationsFromDirectory($migrationsDirectory);
$migrateCommand->setMigrationConfiguration($configuration);
$statusCommand->setMigrationConfiguration($configuration);
// Register All Doctrine Commands
$cli->addCommands([$migrateCommand, $statusCommand]);
// Runs console application
$cli->run($input, $output);
} catch (\Exception $e) {
$style->error($e->getMessage());
exit(1);
}

View File

@ -1,46 +0,0 @@
<?php
include_once __DIR__ . '/app/inc/init.php';
?>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body><pre><?php
$goodLang = $_GET['good'];
$otherLang = $_GET['other'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true);
foreach ($good as $sectionName => $section) {
foreach ($section as $key => $value) {
$good[$sectionName][$key] = getFromOther($other, $key, $value, $otherLang);
}
}
echo json_encode($good, JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
function getFromOther($other, $goodKey, $default, $otherLang) {
foreach ($other as $sectionName => $section) {
foreach ($section as $key => $value) {
if (
strtolower($key) === strtolower($goodKey) ||
strtolower(trim($key)) === strtolower($goodKey) ||
strtolower(substr($key, 0, strlen($key) - 1)) === strtolower($goodKey) ||
strtolower(trim(substr(trim($key), 0, strlen($key) - 1))) === strtolower($goodKey)
) {
return $value;
}
}
}
echo '[-]' . $goodKey . "\n";
return strtoupper($otherLang) . '_' . $default;
}
?>
</pre>
</body>
</html>

View File

@ -1,68 +0,0 @@
<?php
include_once __DIR__ . '/app/inc/init.php';
?>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body><pre><?php
$goodLang = $_GET['good'];
$testLang = $_GET['test'];
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true);
$diffSection = false;
foreach ($good as $sectionName => $section) {
if (!isset($test[$sectionName])) {
echo '- section: ' . $sectionName . "\n";
$diffSection = true;
}
}
foreach ($test as $sectionName => $section) {
if (!isset($good[$sectionName])) {
echo '+ section: ' . $sectionName . "\n";
$diffSection = true;
}
}
if (!$diffSection and array_keys($good)!==array_keys($test)) {
var_dump(array_keys($good));
var_dump(array_keys($test));
} else {
echo 'All sections are in two langs.' . "\n";
}
$diff = [];
foreach ($good as $sectionName => $section) {
$diffSection = false;
foreach($section as $key=>$value) {
if (!isset($test[$sectionName][$key])) {
$diff[$sectionName]['-'][] = $key;
$diffSection = true;
}
}
if (!$diffSection and array_keys($good[$sectionName]) !== array_keys($test[$sectionName])) {
$diff[$sectionName]['order_good'] = array_keys($good[$sectionName]);
$diff[$sectionName]['order_test'] = array_keys($test[$sectionName]);
}
}
foreach ($test as $sectionName => $section) {
foreach($section as $key=>$value) {
if (!isset($good[$sectionName][$key])) {
$diff[$sectionName]['+'][] = $key;
}
}
}
if (count($diff) > 0) {
var_dump($diff);
}
?>
</pre>
</body>
</html>

View File

@ -3,7 +3,7 @@
"description": "Application to facilitate the schedule of events or classic polls",
"homepage": "https://framadate.org/",
"keywords": ["poll", "framadate"],
"version": "0.9.0",
"version": "1.2.0-alpha.1",
"license": "CECILL-B",
"type": "project",
@ -56,18 +56,22 @@
"require": {
"php": ">=5.6.0",
"ext-pdo": "*",
"ext-intl": "*",
"smarty/smarty": "^3.1",
"o80/i18n": "dev-develop",
"phpmailer/phpmailer": "~6.0",
"ircmaxell/password-compat": "dev-master",
"roave/security-advisories": "dev-master",
"erusev/parsedown": "^1.7",
"egulias/email-validator": "~2.1"
"egulias/email-validator": "~2.1",
"doctrine/dbal": "^2.5",
"doctrine/migrations": "^1.5",
"sensiolabs/ansi-to-html": "^1.1",
"symfony/translation": "^3.4"
},
"require-dev": {
"phpunit/phpunit": "^5.7",
"friendsofphp/php-cs-fixer": "~2.0"
"friendsofphp/php-cs-fixer": "~2.0",
"roave/security-advisories": "dev-master"
},
"autoload": {

2789
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,125 +17,96 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Choice;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\PollService;
use Framadate\Services\PurgeService;
use Framadate\Services\SessionService;
use Framadate\Form;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($connect, $logService);
$sessionService = new SessionService();
$pollService = Services::poll();
if (is_file('bandeaux_local.php')) {
include_once('bandeaux_local.php');
} else {
include_once('bandeaux.php');
include_once 'bandeaux.php';
}
// 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)) {
$max_expiry_time = $pollService->maxExpiryDate();
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
if ($form === null || !($form instanceof Form)) {
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation.'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
$smarty->display('error.tpl');
exit;
}
// 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();
}
// The poll format is AUTRE (other) if we are in this file
if (!isset($form->format)) {
$form->format = 'A';
}
// Step 4 : Data prepare before insert in DB
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}$#']]);
// The poll format is AUTRE (other)
if ($form->format !== 'A') {
$form->format = 'A';
$form->clearChoices();
}
if (!empty($enddate)) {
$registredate = explode('/', $enddate);
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
$step = 1;
} elseif (isset($_POST['confirmation'])) {
$step = 4;
} elseif (empty($form->errors) && empty($_POST['fin_sondage_autre']) ) {
$step = 2;
} else {
$step = 3;
}
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;
}
}
switch ($step) {
case 2: // Step 2/4 : Select choices of the poll
$choices = $form->getChoices();
$nb_choices = max( 5- count($choices), 0);
while ($nb_choices-- > 0) {
$c = new Choice('');
$form->addChoice($c);
}
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_expiry_time;
}
$_SESSION['form'] = serialize($form);
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// Display step 2
$smarty->assign('title', __('Step 2 classic', 'Poll options (2 of 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('allowMarkdown', $config['user_can_add_img_or_link']);
$smarty->assign('error', null);
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('Mail', "This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.");
$message .= '<br/><br/>';
$message .= Utils::htmlMailEscape($_SESSION['form']->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlMailEscape($_SESSION['form']->title) . '".<br/>';
$message .= sprintf(__('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($poll_id));
$message_admin = __('Mail', "This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above");
$message_admin .= sprintf(' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($admin_poll_id, true));
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . $_SESSION['form']->title, $message_admin);
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . $_SESSION['form']->title, $message);
}
}
// Clean Form data in $_SESSION
unset($_SESSION['form']);
// Delete old polls
$purgeService->purgeOldPolls();
// creation message
$sessionService->set("Framadate", "messagePollCreated", TRUE);
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
$smarty->display('create_classic_poll_step_2.tpl');
exit;
} // Step 3/4 : Confirm poll creation and choose a removal date
else if (isset($_POST['fin_sondage_autre'])) {
// Store choices in $_SESSION
if (isset($_POST['choices'])) {
$_SESSION['form']->clearChoices();
case 3: // Step 3/4 : Confirm poll creation and choose a removal date
// Handle Step2 submission
if (!empty($_POST['choices'])) {
// remove empty choices
$_POST['choices'] = array_filter($_POST['choices'], function ($c) {
return !empty($c);
});
$form->clearChoices();
// store choices in $_SESSION
foreach ($_POST['choices'] as $c) {
if (!empty($c)) {
$c = strip_tags($c);
$choice = new Choice($c);
$_SESSION['form']->addChoice($choice);
}
$c = strip_tags($c);
$choice = new Choice($c);
$form->addChoice($choice);
}
}
// Expiration date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
$_SESSION['form']->end_date = $max_expiry_time;
// Expiry date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
$form->end_date = $max_expiry_time;
// Summary
$summary = '<ol>';
foreach ($_SESSION['form']->getChoices() as $i=>$choice) {
foreach ($form->getChoices() as $i => $choice) {
/** @var Choice $choice */
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href)
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src)
preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href)
@ -157,100 +128,43 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
}
$summary .= '</ol>';
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); //textual date
$end_date_str = date_format_intl($max_expiry_time); //textual date
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
$_SESSION['form'] = serialize($form);
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 of 3)'));
$smarty->assign('summary', $summary);
$smarty->assign('end_date_str', $end_date_str);
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->assign('errors', $form->errors);
$smarty->display('create_classic_poll_step3.tpl');
$smarty->display('create_poll_step_3.tpl');
exit;
// Step 2/4 : Select choices of the poll
} else {
Utils::print_header(__('Step 2 classic', 'Poll subjects (2 on 3)'));
bandeau_titre(__('Step 2 classic', 'Poll subjects (2 on 3)'));
case 4:
// Step 4 : Do the poll creation
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">';
echo '
<div class="alert alert-info">
<p>' . __('Step 2 classic', 'To make a generic poll you need to propose at least two choices between differents subjects.') . '</p>
<p>' . __('Step 2 classic', 'You can add or remove additional choices with the buttons') . ' <span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . __('Generic', 'Remove') . '</span> <span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . __('Generic', 'Add') . '</span></p>';
if ($config['user_can_add_img_or_link']) {
echo ' <p>' . __('Step 2 classic', 'It\'s possible to propose links or images by using') . ' <a href="http://' . $locale . '.wikipedia.org/wiki/Markdown">' . __('Step 2 classic', 'the Markdown syntax') . '</a>.</p>';
// Read expiration date passed in POST parameters
$end_date = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
$admin_poll_id = $pollService->doPollCreation($form, $end_date);
if (!is_null($admin_poll_id)) {
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
} else {
// Redirect to current page
$referer = $_SERVER['HTTP_REFERER'];
header("Location: $referer");
}
echo ' </div>' . "\n";
// Fields choices : 5 by default
$choices = $_SESSION['form']->getChoices();
$nb_choices = max(count($choices), 5);
for ($i = 0; $i < $nb_choices; $i++) {
$choice = isset($choices[$i]) ? $choices[$i] : new Choice();
echo '
<div class="form-group choice-field">
<label for="choice' . $i . '" class="col-sm-2 control-label">' . __('Generic', 'Choice') . ' ' . ($i + 1) . '</label>
<div class="col-sm-10 input-group">
<input type="text" class="form-control" name="choices[]" size="40" value="' . $choice->getName() . '" id="choice' . $i . '" />';
if ($config['user_can_add_img_or_link']) {
echo '<span class="input-group-addon btn-link md-a-img" title="' . __('Step 2 classic', 'Add a link or an image') . ' - ' . __('Generic', 'Choice') . ' ' . ($i + 1) . '" ><span class="glyphicon glyphicon-picture"></span> <span class="glyphicon glyphicon-link"></span></span>';
}
echo '
</div>
</div>' . "\n";
}
echo '
<div class="col-md-4">
<div class="btn-group btn-group">
<button type="button" id="remove-a-choice" class="btn btn-default" title="' . __('Step 2 classic', 'Remove a choice') . '"><span class="glyphicon glyphicon-minus text-info"></span><span class="sr-only">' . __('Generic', 'Remove') . '</span></button>
<button type="button" id="add-a-choice" class="btn btn-default" title="' . __('Step 2 classic', 'Add a choice') . '"><span class="glyphicon glyphicon-plus text-success"></span><span class="sr-only">' . __('Generic', 'Add') . '</span></button>
</div>
</div>
<div class="col-md-8 text-right">
<a class="btn btn-default" href="' . Utils::get_server_name() . 'create_poll.php?type=classic" title="' . __('Step 2', 'Back to step 1') . '">' . __('Generic', 'Back') . '</a>
<button name="fin_sondage_autre" value="' . __('Generic', 'Next') . '" type="submit" class="btn btn-success disabled" title="' . __('Step 2', 'Go to step 3') . '">' . __('Generic', 'Next') . '</button>
</div>
</div>
</div>
<div class="modal fade" id="md-a-imgModal" tabindex="-1" role="dialog" aria-labelledby="md-a-imgModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">' . __('Generic', 'Close') . '</span></button>
<p class="modal-title" id="md-a-imgModalLabel">' . __('Step 2 classic', 'Add a link or an image') . '</p>
</div>
<div class="modal-body">
<p class="alert alert-info">' . __('Step 2 classic', 'These fields are optional. You can add a link, an image or both.') . '</p>
<div class="form-group">
<label for="md-img"><span class="glyphicon glyphicon-picture"></span> ' . __('Step 2 classic', 'URL of the image') . '</label>
<input id="md-img" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-a"><span class="glyphicon glyphicon-link"></span> ' . __('Generic', 'Link') . '</label>
<input id="md-a" type="text" placeholder="http://…" class="form-control" size="40" />
</div>
<div class="form-group">
<label for="md-text">' . __('Step 2 classic', 'Alternative text') . '</label>
<input id="md-text" type="text" class="form-control" size="40" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">' . __('Generic', 'Cancel') . '</button>
<button type="button" class="btn btn-primary">' . __('Generic', 'Add') . '</button>
</div>
</div>
</div>
</div>
</form>
<script type="text/javascript" src="js/app/framadatepicker.js"></script>
<script type="text/javascript" src="js/app/classic_poll.js"></script>
' . "\n";
bandeau_pied();
}
exit;
case 1:
default:
// Step 1/4 : error if $_SESSION from info_sondage are not valid
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
$smarty->display('error.tpl');
exit;
}

View File

@ -17,80 +17,76 @@
* 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\MailService;
use Framadate\Services\PollService;
use Framadate\Services\PurgeService;
use Framadate\Services\SessionService;
use Framadate\Form;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
/* Service */
/*---------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$purgeService = new PurgeService($connect, $logService);
$inputService = new InputService();
$sessionService = new SessionService();
$inputService = Services::input();
$pollService = Services::poll();
if (is_readable('bandeaux_local.php')) {
include_once('bandeaux_local.php');
}
// Min/Max archive date
$min_expiry_time = $pollService->minExpiryDate();
$max_expiry_time = $pollService->maxExpiryDate();
// The poll format is DATE
if ($_SESSION['form']->format !== 'D') {
$_SESSION['form']->format = 'D';
$_SESSION['form']->clearChoices();
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
if ($form === null || !($form instanceof Form)) {
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
$smarty->display('error.tpl');
exit;
}
if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) || ($config['use_smtp'] && !isset($_SESSION['form']->admin_mail))) {
// The poll format is DATE if we are in this file
if (!isset($form->format)) {
$form->format = 'D';
}
// If we come from another format, we need to clear choices
if ($form->format !== 'D') {
$form->format = 'D';
$form->clearChoices();
}
if (!isset($form->title) || !isset($form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
$step = 1;
} else if (!empty($_POST['confirmation'])) {
$step = 4;
} else if (empty($_POST['choixheures']) || isset($_SESSION['form']->totalchoixjour)) {
} else if (empty($form->errors) && (empty($_POST['choixheures']) || isset($form->totalchoixjour))) {
$step = 2;
} else {
$step = 3;
}
switch ($step) {
case 1:
// Step 1/4 : error if $_SESSION from info_sondage are not valid
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation.'));
$smarty->display('error.tpl');
exit;
case 2:
// Step 2/4 : Select dates of the poll
// Prefill form->choices
foreach ($_SESSION['form']->getChoices() as $c) {
foreach ($form->getChoices() as $c) {
/** @var Choice $c */
$count = 3 - count($c->getSlots());
for ($i = 0; $i < $count; $i++) {
$c->addSlot('');
}
}
$count = 3 - count($_SESSION['form']->getChoices());
$count = 3 - count($form->getChoices());
for ($i = 0; $i < $count; $i++) {
$c = new Choice('');
$c->addSlot('');
$c->addSlot('');
$c->addSlot('');
$_SESSION['form']->addChoice($c);
$form->addChoice($c);
}
$_SESSION['form'] = serialize($form);
// Display step 2
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 on 3)'));
$smarty->assign('choices', $_SESSION['form']->getChoices());
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 of 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('error', null);
$smarty->display('create_date_poll_step_2.tpl');
@ -109,8 +105,8 @@ switch ($step) {
// 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', $_SESSION['form']->getChoices());
$smarty->assign('title', __('Step 2 date', 'Poll dates (2 of 3)'));
$smarty->assign('choices', $form->getChoices());
$smarty->assign('error', __f('Error', 'You can\'t select more than %d dates', MAX_SLOTS_PER_POLL));
$smarty->display('create_date_poll_step_2.tpl');
@ -118,7 +114,7 @@ switch ($step) {
}
// Clear previous choices
$_SESSION['form']->clearChoices();
$form->clearChoices();
// Reorder moments to deal with suppressed dates
$moments = [];
@ -135,10 +131,10 @@ switch ($step) {
if (!empty($day)) {
// Add choice to Form data
$date = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $_POST['days'][$i])->setTime(0, 0, 0);
$time = $date->getTimestamp();
$date = DateTime::createFromFormat(__('Date', 'Y-m-d'), $_POST['days'][$i])->setTime(0, 0, 0);
$time = (string) $date->getTimestamp();
$choice = new Choice($time);
$_SESSION['form']->addChoice($choice);
$form->addChoice($choice);
$schedules = $inputService->filterArray($moments[$i], FILTER_DEFAULT);
for ($j = 0; $j < count($schedules); $j++) {
@ -148,14 +144,16 @@ switch ($step) {
}
}
}
$_SESSION['form']->sortChoices();
$form->sortChoices();
}
// Display step 3
$summary = '<ul>';
$choices = $_SESSION['form']->getChoices();
$choices = $form->getChoices();
foreach ($choices as $choice) {
$summary .= '<li>' . strftime($date_format['txt_full'], $choice->getName());
/** @var Choice $choice */
$date = (new DateTime())->setTimestamp((int) $choice->getName());
$summary .= '<li>' . $end_date_str = date_format_intl($date); //textual date
$first = true;
foreach ($choice->getSlots() as $slots) {
$summary .= $first ? ': ' : ', ';
@ -166,78 +164,43 @@ switch ($step) {
}
$summary .= '</ul>';
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); // textual date
$end_date_str = date_format_intl($max_expiry_time); //textual date
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
$_SESSION['form'] = serialize($form);
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 of 3)'));
$smarty->assign('summary', $summary);
$smarty->assign('end_date_str', $end_date_str);
$smarty->assign('default_poll_duration', $config['default_poll_duration']);
$smarty->assign('use_smtp', $config['use_smtp']);
$smarty->assign('errors', $form->errors);
$smarty->display('create_classic_poll_step3.tpl');
$smarty->display('create_poll_step_3.tpl');
exit;
case 4:
// Step 4 : Data prepare before insert in DB
// Step 4 : Do the poll creation
// Define expiration date
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
// Read expiration date passed in POST parameters
$end_date = 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);
$admin_poll_id = $pollService->doPollCreation($form, $end_date);
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 (!is_null($admin_poll_id)) {
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
} else {
// Redirect to current page
$referer = $_SERVER['HTTP_REFERER'];
header("Location: $referer");
}
exit;
if (empty($_SESSION['form']->end_date)) {
// By default, expiration date is 6 months after last day
$_SESSION['form']->end_date = $max_expiry_time;
}
// Insert poll in database
$ids = $pollService->createPoll($_SESSION['form']);
$poll_id = $ids[0];
$admin_poll_id = $ids[1];
// Send confirmation by mail if enabled
if ($config['use_smtp'] === true) {
$message = __('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_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 = 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);
}
}
// Clean Form data in $_SESSION
unset($_SESSION['form']);
// Delete old polls
$purgeService->purgeOldPolls();
// creation message
$sessionService->set("Framadate", "messagePollCreated", TRUE);
// Redirect to poll administration
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
case 1:
default:
// Step 1/4 : error if $_SESSION from info_sondage are not valid
$smarty->assign('title', __('Error', 'Error!'));
$smarty->assign('error', __('Error', 'You haven\'t filled the first section of the poll creation, or your session has expired.'));
$smarty->display('error.tpl');
exit;
}

View File

@ -20,7 +20,6 @@
use Framadate\Form;
use Framadate\Repositories\RepositoryFactory;
use Framadate\Security\PasswordHasher;
use Framadate\Services\InputService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
@ -30,25 +29,24 @@ const GO_TO_STEP_2 = 'gotostep2';
/* Services */
/*----------*/
$inputService = new InputService();
$inputService = Services::input();
$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') {
$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
@ -69,6 +67,8 @@ if ($goToStep2) {
$receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false;
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
$collect_users_mail = $inputService->filterCollectMail($_POST['collect_users_mail']);
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
$password = isset($_POST['password']) ? $_POST['password'] : null;
$password_repeat = isset($_POST['password_repeat']) ? $_POST['password_repeat'] : null;
$results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
@ -83,20 +83,21 @@ if ($goToStep2) {
$error_on_customized_url = false;
$error_on_ValueMax = false;
$_SESSION['form']->title = $title;
$_SESSION['form']->id = $customized_url;
$_SESSION['form']->use_customized_url = $use_customized_url;
$_SESSION['form']->use_ValueMax = $use_ValueMax;
$_SESSION['form']->ValueMax = $ValueMax;
$_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;
$_SESSION['form']->use_password = ($use_password !== null);
$_SESSION['form']->results_publicly_visible = ($results_publicly_visible !== null);
$form->title = $title;
$form->id = $customized_url;
$form->use_customized_url = $use_customized_url;
$form->use_ValueMax = $use_ValueMax;
$form->ValueMax = $ValueMax;
$form->admin_name = $name;
$form->admin_mail = $mail;
$form->description = $description;
$form->editable = $editable;
$form->receiveNewVotes = $receiveNewVotes;
$form->receiveNewComments = $receiveNewComments;
$form->hidden = $hidden;
$form->collect_users_mail = $collect_users_mail;
$form->use_password = ($use_password !== null);
$form->results_publicly_visible = ($results_publicly_visible !== null);
if ($config['use_smtp'] === true && empty($mail)) {
$error_on_mail = true;
@ -111,10 +112,10 @@ if ($goToStep2) {
$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');
$error_on_customized_url_msg = __('Error', 'Identifier is already used');
} else if (in_array($customized_url, ['admin', 'vote', 'action'], true)) {
$error_on_customized_url = true;
$error_on_customized_url_msg = __('Error', 'This id is not allowed');
$error_on_customized_url_msg = __('Error', 'This identifier is not allowed');
}
}
@ -149,13 +150,15 @@ if ($goToStep2) {
&& !$error_on_password && !$error_on_password_repeat &&!$error_on_ValueMax
) {
// If no errors, we hash the password if needed
if ($_SESSION['form']->use_password) {
$_SESSION['form']->password_hash = PasswordHasher::hash($password);
if ($form->use_password) {
$form->password_hash = PasswordHasher::hash($password);
} else {
$_SESSION['form']->password_hash = null;
$_SESSION['form']->results_publicly_visible = null;
$form->password_hash = null;
$form->results_publicly_visible = null;
}
$_SESSION['form'] = serialize($form);
if ($goToStep2 === 'date') {
header('Location:create_date_poll.php');
exit();
@ -167,11 +170,11 @@ if ($goToStep2) {
}
} else {
// Title Erreur !
$title = __('Error', 'Error!') . ' - ' . __('Step 1', 'Poll creation (1 on 3)');
$title = __('Error', 'Error!') . ' - ' . __('Step 1', 'Poll creation (1 of 3)');
}
} else {
// Title OK (formulaire pas encore rempli)
$title = __('Step 1', 'Poll creation (1 on 3)');
$title = __('Step 1', 'Poll creation (1 of 3)');
}
// Prepare error messages
@ -232,7 +235,7 @@ if (!empty($_POST[GO_TO_STEP_2])) {
if ($error_on_customized_url) {
$errors['customized_url']['aria'] = 'aria-describeby="customized_url" ';
$errors['customized_url']['class'] = ' has-error';
$errors['customized_url']['msg'] = isset($error_on_customized_url_msg) ? $error_on_customized_url_msg : __('Error', "Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.");
$errors['customized_url']['msg'] = isset($error_on_customized_url_msg) ? $error_on_customized_url_msg : __('Error', "Something is wrong with the format: Customized URLs should only consist of alphanumeric characters and hyphens.");
}
if ($error_on_description) {
@ -264,17 +267,17 @@ if (!empty($_POST[GO_TO_STEP_2])) {
if ($error_on_password) {
$errors['password']['aria'] = 'aria-describeby="poll_password_error" ';
$errors['password']['class'] = ' has-error';
$errors['password']['msg'] = __('Error', 'Password is empty');
$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');
$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');
$errors['ValueMax']['msg'] = __('Error', 'Error on amount of votes limitation: Value must be an integer greater than 0');
}
}
@ -289,20 +292,21 @@ $smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_defaul
$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('customized_url', Utils::fromPostOrDefault('customized_url', $_SESSION['form']->id));
$smarty->assign('use_customized_url', Utils::fromPostOrDefault('use_customized_url', $_SESSION['form']->use_customized_url));
$smarty->assign('ValueMax', Utils::fromPostOrDefault('ValueMax', $_SESSION['form']->ValueMax));
$smarty->assign('use_ValueMax', Utils::fromPostOrDefault('use_ValueMax', $_SESSION['form']->use_ValueMax));
$smarty->assign('poll_description', !empty($_POST['description']) ? $_POST['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('poll_use_password', Utils::fromPostOrDefault('use_password', $_SESSION['form']->use_password));
$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $_SESSION['form']->results_publicly_visible));
$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('collect_users_mail', Utils::fromPostOrDefault('collect_users_mail', $form->collect_users_mail));
$smarty->assign('poll_description', !empty($_POST['description']) ? $_POST['description'] : $form->description);
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $form->admin_name));
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $form->admin_mail));
$smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $form->editable));
$smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $form->receiveNewVotes));
$smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $form->receiveNewComments));
$smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $form->hidden));
$smarty->assign('poll_use_password', Utils::fromPostOrDefault('use_password', $form->use_password));
$smarty->assign('poll_results_publicly_visible', Utils::fromPostOrDefault('results_publicly_visible', $form->results_publicly_visible));
$smarty->assign('form', $form);
$smarty->display('create_poll.tpl');

View File

@ -24,6 +24,12 @@ body {
background:#eee;
}
.footer {
text-align: right;
margin-bottom: 1em;
margin-right: 1em;
}
.trait { /* hr */
background-color: #EEE;
height: 5px;
@ -449,19 +455,10 @@ span.edit-username-left {
border-color: #949494 !important;
}
table.results .bg-danger .glyphicon {
/* TODO : Refactor me ! */
table.results .bg-danger .glyphicon:not(.glyphicon-alert) {
opacity:0;
-moz-animation-name: hideNoIcon;
-moz-animation-iteration-count: 1;
-moz-animation-timing-function: ease-in;
-moz-animation-duration: 2s;
-webkit-animation-name: hideNoIcon;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease-in;
-webkit-animation-duration: 2s;
animation-name: hideNoIcon;
animation-iteration-count: 1;
animation-timing-function: ease-in;
@ -532,6 +529,11 @@ table.results > tbody > tr:hover > td .glyphicon {
height: 33px;
}
.datepicker {
/* workaround for the bootstrap-datepicker appearing below the #add_days modal */
z-index: 1060 !important;
}
/* create_classic_poll.php */
.md-a-img {
text-decoration:none !important;

47
docker-compose.yml Normal file
View File

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

30
docker/stretch/Dockerfile Normal file
View File

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

37
docker/stretch/README.md Normal file
View File

@ -0,0 +1,37 @@
# Framadate
**Cette image est en phase de test. NE PAS UTILISER EN PRODUCTION**
Image Docker pour le déploiement de Framadate
## Configuration
### Base de données
Pour fonctionner, Framadate a besoin d'une base de données. Dans notre cas nous utilisons MySQL, que l'on déploie avec Docker. Afin que Framadate fonctionne correctement, nous devons désactiver le *SQL MODE* `NO_ZERO_DATE` de MySQL. On peut donc déployer une base de données pour Framadate ainsi (avec Docker Compose):
```
framadate-db:
image: mysql:5.7
container_name: framadate-db
command: --sql-mode="ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
volumes:
- /path/to/data/volume:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=myrootpassword
- MYSQL_USER=framadate
- MYSQL_PASSWORD=myframadatepassword
- MYSQL_DATABASE=framadate
restart: always
```
### Framadate
Pour initialiser Framadate, on utilise plusieurs variables d'environnement :
- `DOMAIN`: sous domaine du serveur Framadate (ex: `framadate.picasoft.net`)
- `APP_NAME`: nom de l'application (`Framadate` par défaut)
- `ADMIN_MAIL`: adresse mail de l'administrateur du serveur
- `NO_REPLY_MAIL`: adresse mail qui servira à l'envoi des mails
- `MYSQL_USER`: utilisateur MySQL
- `MYSQL_PASSWORD`: mot de passe de l'utilisateur MySQL
- `MYSQL_DB`: nom de la base de données
- `MYSQL_HOST`: adresse du serveur de base de données
- `MYSQL_PORT`: port du serveur MySQL (`3306` par défaut)
- `ADMIN_USER`: utilisateur de l'interface d'administration
- `ADMIN_PASSWORD`: mot de passe de l'interface d'administration
- `DISABLE_SMTP`: mettre à `true` pour désactiver SMTP (sinon `false` par défaut)

View File

@ -0,0 +1,28 @@
<VirtualHost *:80>
DocumentRoot /var/www/framadate
# URL rewrite
<Directory "/">
AllowOverride All
</Directory>
# Admin folder
<Directory "/var/www/framadate/admin/">
AuthType Basic
AuthName "Administration"
AuthUserFile "/var/www/framadate/admin/.htpasswd"
Require valid-user
</Directory>
# Protection fichiers htpasswd et htaccess
<FilesMatch "^\.ht.*">
Deny from all
Satisfy all
ErrorDocument 403 "Accès refusé."
</FilesMatch>
# Logs
ErrorLog /dev/stdout
CustomLog /dev/stdout combined
</VirtualHost>

89
docker/stretch/entrypoint.sh Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
# Read environment variables or set default values
FRAMADATE_CONFIG=${FRAMADATE_CONFIG:-/var/www/framadate/app/inc/config.php}
DOMAIN=${DOMAIN-localhost}
FORCE_HTTPS=${FORCE_HTTPS-false}
APP_NAME=${APP_NAME-Framadate}
ADMIN_MAIL=${ADMIN_MAIL-}
NO_REPLY_MAIL=${NO_REPLY_MAIL-}
MYSQL_USER=${MYSQL_USER-user}
MYSQL_PASSWORD=${MYSQL_PASSWORD-password}
MYSQL_DB=${MYSQL_DB-framadate}
MYSQL_HOST=${MYSQL_HOST-mysql}
MYSQL_PORT=${MYSQL_PORT-3306}
DISABLE_SMTP=${DISABLE_SMTP-false}
# Add configuration file if not exist
if [ ! -f $FRAMADATE_CONFIG ]; then
echo "There is no configuration file. Create one with environment variables"
cp /var/www/framadate/tpl/admin/config.tpl $FRAMADATE_CONFIG
# Set values on configuration file
sed -i -E "s/^(\/\/ )?const APP_URL( )?=.*;/const APP_URL = '$DOMAIN';/g" $FRAMADATE_CONFIG
if [ "$FORCE_HTTPS" = true ]; then
sed -i -E "s/^(\/\/ )?const FORCE_HTTPS\\s*=.*;/const FORCE_HTTPS = true;/" $FRAMADATE_CONFIG
fi
sed -i -E "s/^(\/\/ )?const NOMAPPLICATION( )?=.*;/const NOMAPPLICATION = '$APP_NAME';/g" $FRAMADATE_CONFIG
# Configure mail
sed -i -E "s/^(\/\/ )?const ADRESSEMAILADMIN( )?=.*;/const ADRESSEMAILADMIN = '$ADMIN_MAIL';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const ADRESSEMAILREPONSEAUTO( )?=.*;/const ADRESSEMAILREPONSEAUTO = '$NO_REPLY_MAIL';/g" $FRAMADATE_CONFIG
# Database configuration
sed -i -E "s/^(\/\/ )?const DB_USER( )?=.*;/const DB_USER = '$MYSQL_USER';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DB_PASSWORD( )?=.*;/const DB_PASSWORD = '$MYSQL_PASSWORD';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DB_DRIVER( )?=.*;/const DB_DRIVER = 'pdo_mysql';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DB_NAME( )?=.*;/const DB_NAME = '$MYSQL_DB';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DB_HOST( )?=.*;/const DB_HOST = '$MYSQL_HOST';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DB_PORT( )?=.*;/const DB_PORT = '$MYSQL_PORT';/g" $FRAMADATE_CONFIG
# SMTP config
if [ "$DISABLE_SMTP" = "true" ]; then
sed -i -E "s/'use_smtp' => true,/'use_smtp' => false,/g" $FRAMADATE_CONFIG
fi
sed -i -E "s/SMTP_SERVER/${SMTP_SERVER:-localhost}/g" $FRAMADATE_CONFIG
# Framadate internal config
sed -i -E "s/^(\/\/ )?const TABLENAME_PREFIX( )?=.*;/const TABLENAME_PREFIX = 'fd_';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const MIGRATION_TABLE( )?=.*;/const MIGRATION_TABLE = 'framadate_migration';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const DEFAULT_LANGUAGE( )?=.*;/const DEFAULT_LANGUAGE = 'fr';/g" $FRAMADATE_CONFIG
sed -i -E "s/^(\/\/ )?const URL_PROPRE( )?=.*;/const URL_PROPRE = true;/g" $FRAMADATE_CONFIG
else
echo "Using existing config file " $FRAMADATE_CONFIG
fi
# Configure /admin basic auth
if [ ! -f /var/www/framadate/admin/.htpasswd ]; then
if [ "$ADMIN_USER" ] && [ "$ADMIN_PASSWORD" ]; then
htpasswd -bc /var/www/framadate/admin/.htpasswd $ADMIN_USER $ADMIN_PASSWORD
else
echo "!!! You need to configure ADMIN_USER and ADMIN_PASSWORD environment variables !!!"
exit 1
fi
fi
if [ "$ENV" = "dev" ]; then
echo Installing PHP development dependencies
composer install --no-interaction --no-progress
else
echo Installing PHP production dependencies
composer install -o --no-interaction --no-progress --prefer-dist --no-dev
composer dump-autoload --optimize --no-dev --classmap-authoritative
fi
# Await MySQL Container being ready
until /usr/bin/mysql --host=$MYSQL_HOST --user=$MYSQL_USER --password=$MYSQL_PASSWORD --silent --execute "SELECT 1;" $MYSQL_DB; do
>&2 echo "MySQL is unavailable - sleeping"
sleep 1
done
>&2 echo "Resuming setup"
echo "Setting up .htaccess"
cp /var/www/framadate/htaccess.txt /var/www/framadate/.htaccess
# Run Database migrations
echo "Running database migrations"
php /var/www/framadate/bin/doctrine migrations:status --no-interaction -vvv
php /var/www/framadate/bin/doctrine migrations:migrate --no-interaction -vvv
# Run apache server
# chown -R www-data:www-data /var/www/framadate
source /etc/apache2/envvars
exec apache2 -D FOREGROUND

11
docker/stretch/php.ini Normal file
View File

@ -0,0 +1,11 @@
apc.enable_cli = 1
date.timezone = UTC
session.auto_start = Off
short_open_tag = Off
# http://symfony.com/doc/current/performance.html
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.memory_consumption = 256
realpath_cache_size = 4096K
realpath_cache_ttl = 600

View File

@ -16,9 +16,6 @@
* 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\SecurityService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
@ -34,9 +31,8 @@ $poll = null;
/* Services */
/*----------*/
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$securityService = new SecurityService();
$pollService = Services::poll();
$securityService = Services::security();
/* PAGE */
/* ---- */
@ -53,7 +49,7 @@ if (!empty($_GET['poll'])) {
}
if (!$poll) {
$smarty->assign('error', __('Error', 'This poll doesn\'t exist !'));
$smarty->assign('error', __('Error', "This poll doesn't exist!"));
$smarty->display('error.tpl');
exit;
}
@ -73,11 +69,12 @@ $slots = $pollService->allSlotsByPoll($poll);
$votes = $pollService->allVotesByPollId($poll_id);
// CSV header
echo "\xEF\xBB\xBF"; // BOM character for UTF-8
if ($poll->format === 'D') {
$titles_line = ',';
$moments_line = ',';
foreach ($slots as $slot) {
$title = Utils::csvEscape(strftime($date_format['txt_date'], $slot->title));
$title = Utils::csvEscape($dateFormatter->format($slot->title));
$moments = explode(',', $slot->moments);
$titles_line .= str_repeat($title . ',', count($moments));
@ -104,7 +101,7 @@ foreach ($votes as $vote) {
$text = __('Generic', 'No');
break;
case 1:
$text = __('Generic', 'Ifneedbe');
$text = __('Generic', 'Under reserve');
break;
case 2:
$text = __('Generic', 'Yes');

View File

@ -18,17 +18,14 @@
*/
use Framadate\Message;
use Framadate\Services\LogService;
use Framadate\Services\MailService;
use Framadate\Services\PollService;
include_once __DIR__ . '/app/inc/init.php';
/* SERVICES */
/* -------- */
$logService = new LogService();
$pollService = new PollService($connect, $logService);
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
$notificationService = Services::notification();
$pollService = Services::poll();
/* PAGE */
/* ---- */
@ -40,10 +37,7 @@ if (!empty($_POST['mail'])) {
$polls = $pollService->findAllByAdminMail($mail);
if (count($polls) > 0) {
$smarty->assign('polls', $polls);
$body = $smarty->fetch('mail/find_polls.tpl');
$mailService->send($mail, __('FindPolls', 'List of your polls') . ' - ' . NOMAPPLICATION, $body, 'SEND_POLLS');
$notificationService->sendFindPollsByMailNotification($mail, $polls);
$message = new Message('success', __('FindPolls', 'Polls sent'));
} else {
$message = new Message('warning', __('Error', 'No polls found'));
@ -53,7 +47,8 @@ if (!empty($_POST['mail'])) {
}
}
$smarty->assign('title', __('Homepage', 'Where are my polls'));
$smarty->assign('title', __('Homepage', 'Where are my polls?'));
$smarty->assign('message', $message);
$smarty->assign('locale', $locale);
$smarty->display('find_polls.tpl');

View File

@ -17,7 +17,6 @@
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
use Framadate\Services\PollService;
use Framadate\Utils;
include_once __DIR__ . '/app/inc/init.php';
@ -27,23 +26,23 @@ if (!is_file(CONF_FILENAME)) {
exit;
}
/* SERVICES */
/* -------- */
$logService = '\Framadate\Services\LogService';
$pollService = new PollService($connect, new $logService());
/* PAGE */
/* ---- */
$demoPoll = $pollService->findById('aqg259dth55iuhwm');
$demoPollURL = "";
if (defined("DEMO_POLL_ID")) {
$demoPollURL = Utils::getUrlSondage(DEMO_POLL_ID);
}
$nbcol = max( $config['show_what_is_that'] + $config['show_the_software'] + $config['show_cultivate_your_garden'], 1 );
$smarty->assign('show_what_is_that', $config['show_what_is_that']);
$smarty->assign('show_the_software', $config['show_the_software']);
$smarty->assign('show_cultivate_your_garden', $config['show_cultivate_your_garden']);
$smarty->assign('col_size', 12 / $nbcol);
$smarty->assign('demo_poll', $demoPoll);
$smarty->assign('demo_poll_url', $demoPollURL);
$smarty->assign('title', __('Generic', 'Make your polls'));
$smarty->assign('title', __('Generic', 'Create your own polls'));
$smarty->display('index.tpl');

View File

@ -1,9 +1,67 @@
$(document).ready(function() {
/**
* Markdown Editor
* @type {MDEWrapper}
*/
wrapper = new MDEWrapper($('.js-desc textarea')[0], $('#rich-editor-button'), $('#simple-editor-button'));
var firstOpening = true;
/**
* Save a list of admin polls inside LocalStorage
* @param adminPolls
*/
function setAdminPolls(adminPolls) {
localStorage.setItem('admin_polls', JSON.stringify(adminPolls));
}
/**
* Add an admin poll inside LocalStorage
* @param adminPoll
*/
function addAdminPoll(adminPoll) {
var adminPolls = localStorage.getItem('admin_polls');
if (adminPolls === null) {
adminPolls = [];
} else {
adminPolls = JSON.parse(adminPolls);
}
/**
* Test if the poll is already inside the list
*/
var index = adminPolls.findIndex(function (existingPoll) {
return existingPoll.url === adminPoll.url;
});
if (index === -1) {
adminPolls.push(adminPoll);
} else { // if the poll is already present, we need to update the last access date
adminPolls[index] = adminPoll;
}
setAdminPolls(adminPolls);
}
var adminPoll = {
url: window.location.href,
title: $('#title-form h3').get(0).childNodes[0].nodeValue,
accessed: (new Date()).toISOString()
};
if (!localStorage.getItem('admin_polls')) {
setAdminPolls([adminPoll]);
} else {
addAdminPoll(adminPoll);
}
/**
* Initiate popovers
*/
$('[data-toggle="popover"]').popover();
/**
* Create node with text to use the Clipboard API
* @param text
* @returns {HTMLPreElement}
*/
function createNode(text) {
var node = document.createElement('pre');
node.style.width = '1px';
@ -14,6 +72,10 @@ $(document).ready(function() {
return node;
}
/**
* Copy a Node to use for Clipboard API
* @param node
*/
function copyNode(node) {
var selection = getSelection();
selection.removeAllRanges();
@ -26,6 +88,10 @@ $(document).ready(function() {
selection.removeAllRanges();
}
/**
* Copy a text inside the clipboard
* @param text
*/
function copyText(text) {
var node = createNode(text);
document.body.appendChild(node);

View File

@ -15,75 +15,74 @@
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
*/
(function () {
// 2 choices filled and you can submit
var submitChoicesAvalaible = function () {
$(document).ready(function () {
$(document.formulaire).on('submit', function (e) {
if (!isSubmitChoicesAvalaible()) {
e.preventDefault();
e.stopPropagation();
}
});
var $next = $('button[name="fin_sondage_autre"]');
var $removeAChoice = $('#remove-a-choice');
var $addAChoice = $('#add-a-choice');
var updateButtonState = function() {
var $choiceFields = $('.choice-field');
$removeAChoice.prop('disabled', function() { return $choiceFields.length <= 2; });
$next.prop('disabled', !isSubmitChoicesAvalaible());
}
var isSubmitChoicesAvalaible = function () {
return (countFilledChoices() >= 2);
};
var countFilledChoices = function() {
var nb_filled_choices = 0;
$('.choice-field input').each(function () {
if ($(this).val() != '') {
nb_filled_choices++;
}
});
if (nb_filled_choices >= 1) {
$('button[name="fin_sondage_autre"]').removeClass('disabled');
return true;
} else {
$('button[name="fin_sondage_autre"]').addClass('disabled');
return false;
}
return nb_filled_choices;
};
// Handle form submission
$(document.formulaire).on('submit', function (e) {
if (!submitChoicesAvalaible()) {
e.preventDefault();
e.stopPropagation();
}
$removeAChoice.on('click', function () {
$('.choice-field:last').remove();
updateButtonState();
});
// Button "Add a choice"
$('#add-a-choice').on('click', function () {
var nb_choices = $('.choice-field').length;
var last_choice = $('.choice-field:last');
$addAChoice.on('click', function () {
var $choiceFields = $('.choice-field');
var nb_choices = $choiceFields.length;
var last_choice = $choiceFields.last();
var new_choice = last_choice.html();
// label
var last_choice_label = last_choice.children('label').text();
var choice_text = last_choice_label.substring(0, last_choice_label.indexOf(' '));
// for and id
var re_id_choice = new RegExp('"choice' + (nb_choices - 1) + '"', 'g');
var new_choice_html = new_choice.replace(re_id_choice, '"choice' + nb_choices + '"')
.replace(last_choice_label, choice_text + ' ' + (nb_choices + 1))
.replace(/value="(.*?)"/g, 'value=""');
last_choice.after('<div class="form-group choice-field">' + new_choice_html + '</div>');
last_choice.after('<div class="form-group choice-field row">' + new_choice_html + '</div>');
$('#choice' + nb_choices).focus();
$('#remove-a-choice').removeClass('disabled');
});
// Button "Remove a choice"
$('#remove-a-choice').on('click', function () {
$('.choice-field:last').remove();
var nb_choices = $('.choice-field').length;
$('#choice' + (nb_choices - 1)).focus();
if (nb_choices == 1) {
$('#remove-a-choice').addClass('disabled');
}
submitChoicesAvalaible();
updateButtonState()
});
$(document).on('keyup, change', '.choice-field input', function () {
submitChoicesAvalaible();
updateButtonState();
});
submitChoicesAvalaible();
updateButtonState();
// Button to build markdown from: link + image-url + text
@ -92,7 +91,7 @@
var md_img = $('#md-img');
var md_val = $('#md-a');
$(document).on('click', '.md-a-img', function () {
$('.md-a-img').on('click' , function () {
md_a_imgModal.modal('show');
md_a_imgModal.find('.btn-primary').attr('value', $(this).prev().attr('id'));
$('#md-a-imgModalLabel').text($(this).attr('title'));
@ -116,6 +115,6 @@
md_img.val('');
md_val.val('');
md_text.val('');
submitChoicesAvalaible();
updateButtonState();
});
})();
});

View File

@ -39,6 +39,17 @@ $(document).ready(function () {
$("#use_customized_url").change(function () {
if ($(this).prop("checked")) {
$("#customized_url_options").removeClass("hidden");
// Check url pattern
$('#customized_url').on('input', function() {
var regex_url = /^[a-zA-Z0-9-]*$/
if (! regex_url.test(this.value)) {
$(this).parent(".input-group").addClass("has-error");
$(this).attr("aria-invalid", "true");
} else {
$(this).parent(".input-group").removeClass("has-error");
$(this).attr("aria-invalid", "false");
}
});
} else {
$("#customized_url_options").addClass("hidden");
}
@ -49,9 +60,9 @@ $(document).ready(function () {
*/
$("#use_ValueMax").change(function () {
if ($(this).prop("checked")) {
$("#ValueMax").removeClass("hidden");
$("#valueMaxWrapper").removeClass("hidden");
} else {
$("#ValueMax").addClass("hidden");
$("#valueMaxWrapper").addClass("hidden");
}
});
@ -67,6 +78,25 @@ $(document).ready(function () {
}
});
/**
* Hide/Show Warning collect_users_mail + editable by all
*/
$("input[name='collect_users_mail']").change(function(){
if (($("input[name='collect_users_mail']:checked").val() != 0) && ($("input[name='editable']:checked").val() == 1)) {
$("#collect_warning").removeClass("hidden");
} else {
$("#collect_warning").addClass("hidden");
}
});
$("input[name='editable']").change(function(){
if ($("input[name='collect_users_mail']:checked").val() != 0 && $("input[name='editable']:checked").val() == 1) {
$("#collect_warning").removeClass("hidden");
} else {
$("#collect_warning").addClass("hidden");
}
});
// Check cookies are enabled too
var cookieEnabled = function () {
var cookieEnabled = navigator.cookieEnabled;

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