Compare commits
502 Commits
1.0.2
...
chapril-1.
Author | SHA1 | Date | |
---|---|---|---|
ff6abe4001 | |||
6d8c60c88b | |||
|
cd9b00d0e9 | ||
|
36681dcdea | ||
|
125045bb74 | ||
|
c2f20b1ab2 | ||
|
cffbeaf51d | ||
|
7343463c74 | ||
|
7465852eb4 | ||
|
f30fe54c4c | ||
|
ac932e55f6 | ||
|
fb04860c78 | ||
|
9322a41d0c | ||
|
65cbc8b4b4 | ||
|
fc353ce708 | ||
|
7603bed6d9 | ||
|
6144f33e9f | ||
|
2995851e2a | ||
|
0d4bbe8f2d | ||
|
dffd7edf42 | ||
|
e7f7e26141 | ||
|
3de07eb565 | ||
|
9239954123 | ||
|
76e59902e4 | ||
|
2c3148fe9e | ||
|
5a5c233a5e | ||
|
9c969f8896 | ||
|
3b9dcd8085 | ||
|
bd12f33e12 | ||
|
f877e4a27f | ||
|
86934e9753 | ||
|
12ce504421 | ||
|
0aa11211f3 | ||
|
61a63c55bd | ||
|
03dbceea5a | ||
|
d971e015a6 | ||
|
bfd2fe5351 | ||
|
d87243873f | ||
|
6e40f1cf02 | ||
ff62406853 | |||
8d9770c584 | |||
eed7da32bd | |||
|
02229c671b | ||
|
e0028dc813 | ||
|
ecee1b5025 | ||
|
ab211a93e8 | ||
|
6ec188da23 | ||
|
709b48f293 | ||
|
b46430ebe2 | ||
|
36cef8cc39 | ||
|
a5c7df64b2 | ||
|
c266373344 | ||
|
76f936b0cf | ||
|
fa6bd17365 | ||
|
2ce6e56f8a | ||
|
9dd52be963 | ||
|
299b87a2b6 | ||
|
00ad08d037 | ||
|
12d4bdb857 | ||
|
15e72d6957 | ||
|
dda9c851ab | ||
|
cb7823574e | ||
|
eab3150ba6 | ||
|
69e69efe32 | ||
|
dcd30e0974 | ||
|
d0e01434b7 | ||
f1587f7b36 | |||
9068919c47 | |||
c269048008 | |||
4ec047218a | |||
d0ffcd0dba | |||
fe0558955b | |||
a48b48abc8 | |||
75ee2ef419 | |||
|
8357c0e757 | ||
|
6d2101a3cb | ||
|
1b87fac9fa | ||
|
de56c42d64 | ||
f6855dce8f | |||
6030c3da3a | |||
163630bc66 | |||
a540df01ef | |||
2db36d63bd | |||
415637f4f8 | |||
88c62dc13d | |||
51b27b6b73 | |||
bbe505ed41 | |||
0bab6f939f | |||
c6fda43581 | |||
fd91b558a3 | |||
|
e8747ffef6 | ||
|
0900c92fd6 | ||
|
b30d7f2076 | ||
|
ee8a423e00 | ||
23f933595b | |||
a75ce8aab5 | |||
b108cbbd15 | |||
4d58bc9371 | |||
|
bf3313118a | ||
|
ec53d9c504 | ||
|
5b29497859 | ||
|
30045fd56d | ||
add73d3189 | |||
80f99c9c68 | |||
f3aab00199 | |||
b9715d7142 | |||
2c7d79dca2 | |||
4cf50cb7bf | |||
1bf1656d87 | |||
8d76fae8a1 | |||
|
0e72dafbf6 | ||
|
e35589e1a8 | ||
|
4fa7ae12d4 | ||
|
15e1d1cc18 | ||
|
ca347b024d | ||
4106e2ca96 | |||
|
faa2ee1289 | ||
|
46338cc078 | ||
cb10c87d07 | |||
ebe9add057 | |||
13c2543a7a | |||
c2eb45666a | |||
30c94c7aa1 | |||
4fe0b2097f | |||
f4debb7da8 | |||
506c56aa28 | |||
782a42d571 | |||
205c9230ad | |||
46356b9744 | |||
25def10867 | |||
|
e3060e18d2 | ||
|
84ac58c430 | ||
|
79586319fc | ||
|
70db1e91e0 | ||
|
5ffd4361e4 | ||
fcfc8786fa | |||
7ba226af4a | |||
07368eb2d5 | |||
9a6784c6a6 | |||
|
2badf15074 | ||
|
b40957f484 | ||
|
a6e337e046 | ||
|
0da24788d0 | ||
|
cad9dac189 | ||
|
bc28281477 | ||
|
be128e8158 | ||
|
a1a7d180f8 | ||
|
15e78ba070 | ||
|
88fb61660a | ||
a31dcd60d6 | |||
7088684b2e | |||
f62e0408de | |||
|
59569071e8 | ||
|
30e77a77c4 | ||
|
367b239549 | ||
|
8ce671e4d9 | ||
|
901f907dd4 | ||
|
a80602e3d5 | ||
|
53572dbc1f | ||
|
b2fc3a9d53 | ||
|
baadea8e74 | ||
|
075ba1739c | ||
|
9829c826ef | ||
|
32492cf119 | ||
|
c5a2f299c5 | ||
|
d72cf4ce24 | ||
|
f931516f24 | ||
|
4035193377 | ||
|
4838577d9d | ||
|
8e8b8a63cb | ||
|
3db84de4aa | ||
|
2a33310d4d | ||
|
ff7bf2b2a2 | ||
|
45b646b178 | ||
|
8e6373c964 | ||
|
a092777657 | ||
|
412b409397 | ||
|
be1f29773b | ||
|
46df3b6fce | ||
|
addaa95ce7 | ||
|
852c2c0d2a | ||
|
402f2abf64 | ||
|
179235eaf9 | ||
|
c4f27dc6e0 | ||
|
f921d383a9 | ||
|
c180090a59 | ||
|
659cb1ec75 | ||
|
445dac2e51 | ||
|
20e5f5c130 | ||
|
cb03c5c054 | ||
|
f470d63ab0 | ||
|
2cf458960e | ||
|
f2078ce2f5 | ||
|
a4ad96c78f | ||
|
e6f6df5eb6 | ||
|
c27c747726 | ||
|
f726adf2ee | ||
|
2c3dfa8f59 | ||
|
08f7ca141d | ||
|
61ac8fcf76 | ||
|
37df499a37 | ||
|
c79b32984d | ||
|
0151361cc9 | ||
|
9ce586347b | ||
|
4e4e21cef2 | ||
|
54fd8464d9 | ||
|
a003254c7d | ||
|
5b4b8a8606 | ||
|
a367a4abac | ||
|
25e62c238d | ||
|
6f257f416a | ||
|
599f3104f6 | ||
|
1d8244fd99 | ||
|
3c9b8d3708 | ||
|
9da92fd775 | ||
|
72de7bff1c | ||
|
d1870e516e | ||
|
56d6e015c1 | ||
|
37c2df781a | ||
|
cdd1840217 | ||
|
d2268e8543 | ||
|
6855cf4a47 | ||
|
48c3551e61 | ||
|
7823a23e72 | ||
|
2fd590fad6 | ||
|
b553305a0e | ||
|
3aaa7ee629 | ||
|
48134d70f7 | ||
|
05223bfd7b | ||
|
3df48b25f7 | ||
|
004fda4f96 | ||
|
7134581535 | ||
|
2d0699184c | ||
|
21c6c7684e | ||
|
7d7f39190f | ||
|
4d88760ce1 | ||
|
ebbee26a59 | ||
|
0cd41782d1 | ||
|
ce16034c84 | ||
|
2935fdf644 | ||
|
d35848ed5a | ||
|
5d0a388285 | ||
|
3e09125719 | ||
|
5a171277cb | ||
|
07a311239a | ||
|
6ed3a0c3cf | ||
|
71c3498475 | ||
|
d079935714 | ||
|
4631b8d4c4 | ||
|
1673ed8c71 | ||
|
17f16cd0f8 | ||
|
edb5a84cf9 | ||
|
77afe10e44 | ||
|
955465f5f9 | ||
|
d3eedbcbf6 | ||
|
0e9075a985 | ||
|
991f51aac0 | ||
|
bfc986a66e | ||
|
4e021c2bc3 | ||
|
129dcba693 | ||
|
2991b9e1d3 | ||
|
5b5330ceb9 | ||
|
18cbfb7b75 | ||
|
cf48c9c2d7 | ||
|
eccae40718 | ||
|
65d8fd9639 | ||
|
3705e6c493 | ||
|
ea3e391b87 | ||
|
05e9ea1589 | ||
|
0e022f51c1 | ||
|
f344169157 | ||
|
6c2e44e400 | ||
|
3bd1999e15 | ||
|
2711b3a019 | ||
|
e9458c1839 | ||
|
edee5626f4 | ||
|
89c5ff3637 | ||
|
a1fbe3946a | ||
|
7a8549cfd7 | ||
|
4f402f60e7 | ||
|
a97bcf4d6a | ||
|
0bd4f46535 | ||
|
72e0791e23 | ||
|
05ee7bf256 | ||
|
a78d3b3e01 | ||
|
ff3dcc9249 | ||
|
8931255033 | ||
|
9305a82cb2 | ||
|
b9f4577fbb | ||
|
915fe17bfc | ||
|
26761c97a6 | ||
|
eadf775bb3 | ||
|
7ffd7f0b21 | ||
|
bac91c9182 | ||
|
d8abafc78e | ||
|
0951a89c33 | ||
|
678070af8f | ||
|
fdbded4310 | ||
|
847dcf2b92 | ||
|
c4ec2bbc41 | ||
|
ee807db380 | ||
|
3637e052d5 | ||
|
f340fbe046 | ||
|
6839065ece | ||
|
1c8e0061b5 | ||
|
c5b50050c9 | ||
|
2907a20c9f | ||
|
15b173569d | ||
|
87b89bc609 | ||
|
1d89074965 | ||
|
32ffa5f3e3 | ||
|
710304c183 | ||
|
991088119f | ||
|
872e30939c | ||
|
755cd18d1a | ||
|
8720595874 | ||
|
7e12443bc2 | ||
|
01dc23125a | ||
|
77f05b1c21 | ||
|
bf753480dd | ||
|
801bc08a81 | ||
|
e6ed712123 | ||
|
bd931676ae | ||
|
d106d3ba41 | ||
|
d9f50e8706 | ||
|
1de98223db | ||
|
2328b2c871 | ||
|
c350a7a012 | ||
|
f11fd8c541 | ||
|
4bbbaf19ed | ||
|
efda4e97b0 | ||
|
b29bbb5c9a | ||
|
02fdf05786 | ||
|
4e2a48685a | ||
|
27f4c1045a | ||
|
ea575522a9 | ||
|
6e1386fa4d | ||
|
a442cc5ee7 | ||
|
ff47a21ac2 | ||
|
76b19ee9ba | ||
|
2d39a83a7f | ||
|
64ff414900 | ||
|
2a01afe4b8 | ||
|
70f296377f | ||
|
95c4737953 | ||
|
2b25d8c301 | ||
|
c95c13e069 | ||
|
05e4b820fb | ||
|
c2cbc6eb0e | ||
|
4dc62b7da2 | ||
|
2310333f8d | ||
|
e9deb8d22e | ||
|
1740a1d949 | ||
|
78ba609c5c | ||
|
013c62eb9c | ||
|
0a9c5095e5 | ||
|
83d3565472 | ||
|
89dd2fbc35 | ||
|
819220997c | ||
|
deb1da2635 | ||
|
1e8d3a1bf0 | ||
|
b8907fb1b6 | ||
|
006e5c8c10 | ||
|
3157d6a590 | ||
|
9827070b3d | ||
|
c2e00ef4d6 | ||
|
1e423e880e | ||
|
6b04d3542e | ||
|
54772d42f7 | ||
|
e8f37a33bd | ||
|
2ebe57a828 | ||
|
21568fc6ef | ||
|
af8c00f2b5 | ||
|
ba04557ac9 | ||
|
c4ccecd91e | ||
|
00e4d205e6 | ||
|
3f4cf0f721 | ||
|
678db4a115 | ||
|
f982aa54d5 | ||
|
68a47dba73 | ||
|
1b105ba1d8 | ||
|
3183b54dbf | ||
|
10dca1c2c5 | ||
|
f18ce7845d | ||
|
4e8d77f9af | ||
|
2f2378a80f | ||
|
60e4e9f9b1 | ||
|
b93ff95a54 | ||
|
0f2313e273 | ||
|
7cfd2117e8 | ||
|
2bc9eb62d0 | ||
|
8b79c7f43a | ||
|
9897a024fc | ||
|
9a69f64132 | ||
|
1d75ef0ea2 | ||
|
6a2ee5604e | ||
|
a2df40b924 | ||
|
d33cc58cf7 | ||
|
c03bd0216f | ||
|
71d8ef5134 | ||
|
f8e00084be | ||
|
724133f6ae | ||
|
e9199d8396 | ||
|
9229b5cb13 | ||
|
53cf6498ed | ||
|
d69de89f2f | ||
|
3642bf87d4 | ||
|
ca653cf568 | ||
|
de6de7d6ee | ||
|
9bb4cab2d8 | ||
|
495db1b748 | ||
|
adcea7d04b | ||
|
254130559f | ||
|
fd3526c9c7 | ||
|
e8c3984a07 | ||
|
2205156a46 | ||
|
be44e53eda | ||
|
b0e6b82877 | ||
|
61e3d0735f | ||
|
00a755fb10 | ||
|
df2576445d | ||
|
8f6c665c3d | ||
|
b1d996f7d0 | ||
|
2a9c663c8c | ||
|
7e05d8aef8 | ||
|
61f57ee41c | ||
|
e6c2d035fe | ||
|
5bb3d394a1 | ||
|
5012ed32db | ||
|
03727577e8 | ||
|
57d6e1c0a4 | ||
|
545f167a7b | ||
|
0a0b3b233b | ||
|
44aca0a7e7 | ||
|
e17abed619 | ||
|
c5a50a7cad | ||
|
48de916f81 | ||
|
8f6a31737d | ||
|
14451ade7a | ||
|
56620e4a43 | ||
|
716b171a36 | ||
|
cf21a10314 | ||
|
79ae5bb77d | ||
|
c6a98e16e8 | ||
|
250a6bba65 | ||
|
c7992be6bb | ||
|
7c1b3b163d | ||
|
c9c4e945b8 | ||
|
1206975d30 | ||
|
1cfba5efee | ||
|
4881b3c8b2 | ||
|
0fd5348ff5 | ||
|
5be042a014 | ||
|
205dd08013 | ||
|
65872d1c7b | ||
|
1872204d1b | ||
|
451b299bc3 | ||
|
c5b9c4152d | ||
|
18dd7f58b0 | ||
|
469d288136 | ||
|
9cb2ce6fe6 | ||
|
e79a0bf683 | ||
|
c2cfdf4ff6 | ||
|
65c8bb4d77 | ||
|
ec43f91df5 | ||
|
fcf8f5d621 | ||
|
4d14dd1b70 | ||
|
d762c38e81 | ||
|
5c11e5fe4d | ||
|
075083bab8 | ||
|
a86d39c49d | ||
|
6a06c71c50 | ||
|
4059956b02 | ||
|
dc9babd2ed | ||
|
c01238b635 | ||
|
525a5ab4b1 | ||
|
2b946dda4d | ||
|
fc19a02cde | ||
|
f99c402e6d | ||
|
2144febf95 | ||
|
8672153a7e | ||
|
f2562ce5fb | ||
|
dc61be5de5 | ||
|
91e74dc835 | ||
|
c88d4daf3c | ||
|
cd581a9d29 | ||
|
5be1c07d8b | ||
|
3181204228 | ||
|
9d465487da | ||
|
2e794ea7da | ||
|
d37ec75550 | ||
|
d80b54349a | ||
|
9f564b958e | ||
|
ecb79e3dc1 | ||
|
1ddc451829 | ||
|
bcb289406e | ||
|
a37d621fab | ||
|
f681a6f0e8 | ||
|
83e0cae47a | ||
|
beee7874dc | ||
|
1c633d74d9 | ||
|
67ec67d50f |
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.htaccess
|
||||
.htdigest
|
||||
.htpasswd
|
||||
admin/stdout.log
|
||||
composer.phar
|
||||
@ -7,7 +8,10 @@ nav
|
||||
app/inc/config.php
|
||||
vendor
|
||||
cache/
|
||||
tpl_c/
|
||||
tpl_c/*
|
||||
!tpl_c/.gitkeep
|
||||
.php-cs-fixer.cache
|
||||
.zanata-cache/
|
||||
|
||||
# Temp files
|
||||
*~
|
||||
@ -21,3 +25,6 @@ Thumbs.db
|
||||
.project
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
#ics temp file
|
||||
out.ics
|
||||
|
62
.gitlab-ci.yml
Normal file
62
.gitlab-ci.yml
Normal file
@ -0,0 +1,62 @@
|
||||
image: framasoft/framadate-ci:7.3-pdo_mysql
|
||||
stages:
|
||||
- test
|
||||
- deploy
|
||||
|
||||
# Run php-cs-fixer and phpunit on all branches
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- composer install -o --no-interaction --no-progress --prefer-dist
|
||||
- php vendor/bin/php-cs-fixer fix --verbose --dry-run
|
||||
- vendor/bin/phpunit --bootstrap app/tests/bootstrap.php --debug app/tests
|
||||
image: framasoft/framadate-ci:${PHP_VERSION}-pdo_mysql
|
||||
parallel:
|
||||
matrix:
|
||||
- PHP_VERSION:
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
cache:
|
||||
paths:
|
||||
- vendor/
|
||||
|
||||
# Create artifacts on master
|
||||
pages:
|
||||
stage: deploy
|
||||
script:
|
||||
- latesttag=$(git describe --tags)
|
||||
- git checkout ${latesttag}
|
||||
- composer install -o --no-interaction --no-progress --prefer-dist --no-dev
|
||||
- composer dump-autoload --optimize --no-dev --classmap-authoritative
|
||||
- mkdir framadate
|
||||
- mv `ls -A | grep -v framadate` ./framadate
|
||||
- echo $latesttag > framadate/VERSION
|
||||
- find framadate/ -type d -exec chmod 750 {} \;
|
||||
- find framadate/ -type f -exec chmod 640 {} \;
|
||||
- rm -rf framadate/.git
|
||||
- export RELEASE_ZIP="framadate-${CI_COMMIT_TAG}.zip"
|
||||
- zip -r $RELEASE_ZIP framadate
|
||||
- mkdir .public
|
||||
- cp $RELEASE_ZIP .public/latest.zip
|
||||
- mv .public public
|
||||
- if [[ -z $GITLAB_API_TOKEN ]]; then exit; fi
|
||||
- export PROJECT_API_URL="https://framagit.org/api/v4/projects/${CI_PROJECT_ID}"
|
||||
- export DESCRIPTION_URL="${PROJECT_API_URL}/repository/tags/${CI_COMMIT_TAG}"
|
||||
- export RELEASE_URL="${DESCRIPTION_URL}/release"
|
||||
- 'export HEADER="Private-Token: ${GITLAB_API_TOKEN}"'
|
||||
- export artifactUrl=$(curl -s --request POST --header "${HEADER}" --form "file=@${RELEASE_ZIP}" "${PROJECT_API_URL}/uploads" | jq .url)
|
||||
- export artifactAbsoluteUrl="${CI_PROJECT_URL}${artifactUrl}"
|
||||
- export description=$(curl -s --header "${HEADER}" "${DESCRIPTION_URL}" | jq .release.description | sed -e 's@"@@g')
|
||||
- if [[ $description == 'null' ]]; then export METHOD="POST"; echo -e 'You can download the release zip here:'" [${RELEASE_ZIP}](${artifactAbsoluteUrl})" > /tmp/text; fi
|
||||
- if [[ $description != 'null' ]]; then export METHOD="PUT"; echo -e "${description}\n\n"'You can download the release zip here:'" [${RELEASE_ZIP}](${artifactAbsoluteUrl})" > /tmp/text; fi
|
||||
- curl -s --request $METHOD --data-urlencode "description@/tmp/text" --header "${HEADER}" "${RELEASE_URL}"
|
||||
- curl -s --request POST --header "${HEADER}" --data name="${RELEASE_ZIP}" --data url="${artifactAbsoluteUrl}" "${PROJECT_API_URL}/releases/${CI_COMMIT_TAG}/assets/links"
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
only:
|
||||
- tags
|
||||
except:
|
||||
- (beta|alpha)
|
48
.php-cs-fixer.dist.php
Normal file
48
.php-cs-fixer.dist.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRiskyAllowed(true)
|
||||
->setRules([
|
||||
'array_syntax' => [
|
||||
'syntax' => 'short'
|
||||
],
|
||||
'combine_consecutive_unsets' => true,
|
||||
'heredoc_to_nowdoc' => true,
|
||||
'no_extra_blank_lines' => [
|
||||
'tokens' => [
|
||||
'break',
|
||||
'continue',
|
||||
'extra',
|
||||
'return',
|
||||
'throw',
|
||||
'use',
|
||||
'parenthesis_brace_block',
|
||||
'square_brace_block',
|
||||
'curly_brace_block'
|
||||
]
|
||||
],
|
||||
'no_unreachable_default_argument_value' => true,
|
||||
'no_useless_else' => true,
|
||||
'no_useless_return' => true,
|
||||
'ordered_class_elements' => true,
|
||||
'ordered_imports' => true,
|
||||
'php_unit_strict' => true,
|
||||
'phpdoc_order' => true,
|
||||
// 'psr4' => true,
|
||||
'strict_comparison' => true,
|
||||
'strict_param' => true,
|
||||
'concat_space' => [
|
||||
'spacing' => 'one'
|
||||
],
|
||||
])
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude([
|
||||
'vendor',
|
||||
'var',
|
||||
'web',
|
||||
'tpl_c'
|
||||
])
|
||||
->in(__DIR__)
|
||||
)
|
||||
;
|
9
.po2json.sh
Executable file
9
.po2json.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
po2json -i po/en.po -t locale/en.json --progress none -o po/default.json
|
||||
|
||||
for i in po/*.po
|
||||
do
|
||||
j=$(echo $i | cut -d '.' -f 1 | cut -d '/' -f 2)
|
||||
po2json -i $i -t locale/en.json --progress none | ./.renest_json.pl > po/$j.json
|
||||
mv po/$j.json locale/
|
||||
done
|
40
.renest_json.pl
Executable file
40
.renest_json.pl
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use JSON;
|
||||
|
||||
my $json = JSON->new->utf8->space_before(0)->space_after(1)->indent(4)->canonical(1);
|
||||
|
||||
my $en_file = 'po/default.json';
|
||||
my $en;
|
||||
{
|
||||
open my $fh, '<', $en_file or die;
|
||||
local $/ = undef;
|
||||
$en = <$fh>;
|
||||
close $fh;
|
||||
}
|
||||
|
||||
$en = $json->decode($en);
|
||||
|
||||
my $new_json = {};
|
||||
my $old_json = '';
|
||||
|
||||
while (defined(my $line = <STDIN>)) {
|
||||
$old_json .= $line;
|
||||
}
|
||||
|
||||
$old_json = $json->decode($old_json);
|
||||
for my $key (keys %{$old_json}) {
|
||||
my $index = index($key, '.');
|
||||
my $real_key = substr($key, 0, $index++);
|
||||
my $trad_key = substr($key, $index);
|
||||
|
||||
if ($old_json->{$key}) {
|
||||
$new_json->{$real_key}->{$trad_key} = $old_json->{$key};
|
||||
} else {
|
||||
$new_json->{$real_key}->{$trad_key} = $en->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
print $json->encode($new_json);
|
210
CHANGELOG.md
210
CHANGELOG.md
@ -1,5 +1,215 @@
|
||||
# Changelog de framadate
|
||||
|
||||
## 1.1.19
|
||||
|
||||
23-12-2021
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove the X-Mailer header in e-mails, as this causes some email servers to see emails sent by Framadate as spam
|
||||
|
||||
## 1.1.18
|
||||
20-12-2021
|
||||
|
||||
### Changed
|
||||
- Dependency updates
|
||||
- Replace abandonned SimpleMDE with EasyMDE fork
|
||||
|
||||
### Fixed
|
||||
- Enforce the instance expiration limits when editing the poll expiration date once created, from poll admin
|
||||
- Fixed some HTML markup validity
|
||||
|
||||
### Translations
|
||||
- Fixed a missing french language key
|
||||
- Enable Catalan language
|
||||
|
||||
## 1.1.17
|
||||
18-10-2021
|
||||
### Added
|
||||
|
||||
- Allow to export to ICS the best choices
|
||||
|
||||
### Changed
|
||||
|
||||
- Allow configuring AuthType for MailService
|
||||
|
||||
### Security
|
||||
|
||||
- Fix an XSS possibility in the result graph
|
||||
|
||||
## 1.1.16
|
||||
22-03-2021
|
||||
### Changed
|
||||
|
||||
- **Framadate now requires the `mbstring` PHP extension.** Make sure it's installed and activated before updating.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Handle poll creator names being too long properly
|
||||
|
||||
|
||||
## 1.1.15
|
||||
22-03-2021
|
||||
### Security
|
||||
|
||||
- Fixed cross-site scripting (XSS) attacks in poll description markdown preview. All administrators are encouraged to upgrade, especially if you have sensitive services and data on the same domain name.
|
||||
This was reported by @martgil
|
||||
https://framagit.org/framasoft/framadate/framadate/-/issues/546
|
||||
|
||||
## 1.1.14
|
||||
08-03-2021
|
||||
### Fixed
|
||||
|
||||
- Avoid error with a name too long https://framagit.org/framasoft/framadate/framadate/-/issues/530
|
||||
|
||||
## 1.1.13
|
||||
08-03-2021
|
||||
### Fixed
|
||||
|
||||
- Fixed error when closing a poll https://framagit.org/framasoft/framadate/framadate/-/issues/532
|
||||
|
||||
## 1.1.12
|
||||
18-12-2020
|
||||
|
||||
### Changed
|
||||
|
||||
* Framadate now requires PHP 7.3
|
||||
|
||||
## 1.1.11
|
||||
18-12-2020
|
||||
### Fixed
|
||||
|
||||
- Fixed translations keys missing into emails https://framagit.org/framasoft/framadate/framadate/-/issues/463
|
||||
|
||||
### Translations
|
||||
|
||||
- Added Catalan translation
|
||||
|
||||
## 1.1.10
|
||||
|
||||
### Fixed
|
||||
* Remove .git folder inside releases.
|
||||
* Create releases through CI
|
||||
|
||||
## 1.1.9
|
||||
|
||||
### Fixed
|
||||
- Fixes session issue https://framagit.org/framasoft/framadate/framadate/issues/255
|
||||
- Fixes bug when editing column https://framagit.org/framasoft/framadate/framadate/issues/379
|
||||
- Fix mail subject escaping https://framagit.org/framasoft/framadate/framadate/issues/375
|
||||
|
||||
## 1.1.8
|
||||
### Fixed
|
||||
- Stop creating `tpl_c` directory in releases and add a `.gitkeep`
|
||||
- Show database connection issue details on installation panel
|
||||
- Set the proper file rights on release packages
|
||||
- Added `session.cookie_httponly = 1` to local php.ini file
|
||||
|
||||
## 1.1.7
|
||||
### Fixed
|
||||
|
||||
- Fix issue with maximum number of participants https://framagit.org/framasoft/framadate/issues/353 (thanks to @lohmeyer for reporting it)
|
||||
|
||||
## 1.1.6
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bump dependencies, including PHPMailer to version 6.x
|
||||
- Fix an small issue with Smarty template
|
||||
|
||||
## 1.1.5
|
||||
### Fixed
|
||||
|
||||
- Restrict custom poll URLs against app urls (thanks @mosterdt)
|
||||
- Add a parameter to disable build-in font-awesome (thanks @mm)
|
||||
- Fix an XSS security issue with time slots (thanks https://bitsoffreedom.nl for responsibly disclosing it).
|
||||
|
||||
## 1.1.4
|
||||
|
||||
### Fixed
|
||||
* Add Fork-awesome, remove dependency to Font-Awesome Bootstrap CDN, add an option to disable it (https://framagit.org/framasoft/framadate/merge_requests/300 - @tcit)
|
||||
|
||||
## 1.1.3
|
||||
|
||||
### Fixed
|
||||
* Fixing issue when no choice is selected introducted in https://framagit.org/framasoft/framadate/merge_requests/284 (https://framagit.org/framasoft/framadate/merge_requests/298 - @mm)
|
||||
|
||||
## 1.1.2
|
||||
|
||||
### Fixed
|
||||
- Use Parsedown's Safe Mode
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Send email with correct vote address (thanks to @lohmeyer for finding it)
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Warning
|
||||
|
||||
**Framadate now requires PHP 5.6** to be used (it should still work under 5.4 but will not be supported anymore).
|
||||
|
||||
### Features
|
||||
|
||||
- Markdown editor for descriptions ! (@Antonin)
|
||||
- Adding a maximum participants number (@SuperNach0)
|
||||
- Allow setting SMTP config (Simon LEBLANC)
|
||||
- Allow admins to give the vote link back to the voters (@mm, @tcit)
|
||||
- Sending voters emails to remind themselves their voting url now works (@mm)
|
||||
|
||||
### Enhancements
|
||||
|
||||
- UI improvements for responsive design (@marjolaine-v)
|
||||
- Better coherence for visible results and passwords (@TDavid)
|
||||
- Added an edit button on the right when too many options (@SuperNach0)
|
||||
- Emails with international characters are now allowed (added an unit test) (@mm)
|
||||
|
||||
### Translations
|
||||
|
||||
**New strings are available, don't hesitate to head to <https://trad.framasoft.org/zanata/project/view/framadate> to translate them into your language !**
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reschedule function (https://framagit.org/framasoft/framadate/issues/203) (@TDavid)
|
||||
- lang attribute must be a valid IETF language tag (@Rudloff)
|
||||
- Fix datepicker js locale file path
|
||||
- Fix everyone can always vote #267
|
||||
- Fix MySQL error with `NO_ZERO_DATE` #224
|
||||
- SimpleMDE Markdown Editor has been updated the latest version to remove console.log calls
|
||||
- Fix width of `if need be` vote option and missing parenthesis
|
||||
- Remove autocomplete on date fields
|
||||
- Various fixes for value max error handling
|
||||
- New error strings for bad formatted inputs (admin name, wrong value max option)
|
||||
- Email is now a email field (better for virtual keyboards) and is html required as well as title
|
||||
- Advanced settings for poll are now opened if there's error within them
|
||||
- css fixes for pictures inside columns, and little space between editor and description text area (@marjolaine-v)
|
||||
- released zip files now have proper chmod rights (@tcit)
|
||||
- Best choices now work properly when there's no votes (@mm)
|
||||
- Don't allow an existing name when updating a vote (@mm)
|
||||
- Keep vote selections when there's an error on the name (@mm)
|
||||
- Add a message « Your poll has been created » at the end of the poll form process (@mm)
|
||||
|
||||
### Documentation
|
||||
|
||||
- Move everything to wiki, translate everything to English
|
||||
|
||||
### Technical
|
||||
|
||||
- Continuous Integration handles the release process
|
||||
- Translations with Zanata : https://trad.framasoft.org/zanata/project/view/framadate (@luc)
|
||||
- Style fixes with PHP-CS
|
||||
- Libraries updated
|
||||
- Improved a few docs
|
||||
- Use own Framadate Docker Image for CI
|
||||
- https://beta.framadate.org now gets the latest translations for each deployment (@luc)
|
||||
- A CI job tells if translations strings are up-to-date (@luc)
|
||||
|
||||
## 1.0.3
|
||||
|
||||
- Corrections de wording (fr / en)
|
||||
|
||||
## Version 1.0 (Erik - Markus - Ecmu - Julien - Imre - Luc - Pierre - Antonin - Olivier)
|
||||
- Amélioration : Conserver les votes en cours lors que l'utilisateur envoie un commentaire
|
||||
- Amélioration : Les mails sont envoyés en multipart pour les lecteurs ne supportant pas HTML
|
||||
|
83
INSTALL.md
83
INSTALL.md
@ -1,82 +1 @@
|
||||
# Pré-installation
|
||||
|
||||
## Base de données
|
||||
|
||||
Framadate fonctionne indépendemment de la base SQL utilisée.
|
||||
|
||||
Cependant la base de donnée doit être créée au préalable,
|
||||
après avoir renseigné les paramètres de la base de données, créez la.
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```bash
|
||||
su - pgsql
|
||||
createdb framadate
|
||||
```
|
||||
|
||||
Attention : Si vous créez la base de données avec l'utilisateur "pgsql",
|
||||
il vous faudra faire un "grant all on <chaque table> to `framadate`" pour donner les droits à l'utilisateur `framadate` de lire et modifier la base.
|
||||
Les tables de l'applications sont décrites plus loin dans ce fichier dans la partie "Tables de la base de données".
|
||||
|
||||
### MySQL
|
||||
|
||||
```sql
|
||||
-- Créer une base de données
|
||||
CREATE DATABASE IF NOT EXISTS `framadate_db` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
|
||||
|
||||
-- Créer un utilisateur
|
||||
CREATE USER 'framadate_user'@'localhost' IDENTIFIED BY '<password>';
|
||||
GRANT ALL PRIVILEGES ON `framadate_db`.* TO 'framadate_user'@'localhost';
|
||||
```
|
||||
|
||||
# Installation
|
||||
|
||||
Pour installer l'application Framadate, rendez-vous sur la page http://monsite/admin/install.php et remplisez le formulaire.
|
||||
|
||||
Une fois le formulaire rempli et envoyé, un script va générer le fichier `app/inc/config.php` puis vous rediriger vers la page de migration.
|
||||
|
||||
La page de migration s'occupe :
|
||||
- D'installer toute la base de données (tables + données d'exemple)
|
||||
- De mettre à jour la base de données lors des mises à jour de l'applciation.
|
||||
|
||||
! Attention, le chargement de la page de migration peu prendre du temps étant donné qu'elle applique toutes les mises à jours requises !
|
||||
|
||||
# Accès à la page administrateur
|
||||
|
||||
Le répertoire `admin/` fournit l'accès à certainnes actions et informations à protéger.
|
||||
|
||||
Il convient de mettre en place un couple de fichiers `.htaccess`/`.htpasswd`, pour restreindre l'accès à la page d'administration de l'application.
|
||||
Il existe une multitude de tutoriels sur internet à ce sujet.
|
||||
|
||||
# Journal de l'application
|
||||
|
||||
Un fichier `admin/stdout.log` doit être créé et accessible en écriture
|
||||
par votre serveur Web. Quelque chose comme devrait convenir:
|
||||
|
||||
```bash
|
||||
touch admin/stdout.log
|
||||
chmod 700 admin/stdout.log
|
||||
chown www-data admin/stdout.log
|
||||
```
|
||||
|
||||
# Maintenance
|
||||
|
||||
Framadate dispose d'une possibilité de mise en maintenance par le biais d'un fichier `.htaccess`.
|
||||
|
||||
La section `<Directory>` relative à Framadate, dans la configuration d'Apache doit au moins contenir :
|
||||
`AllowOverride AuthConfig Options`
|
||||
|
||||
Le fichier `.htaccess` correspondant doit être modifier pour y configurer
|
||||
l'adresse IP depuis laquelle s'effectue la maintenance.
|
||||
N'oubliez pas de le recommenter en intégralité une fois la maintenance effectuée.
|
||||
|
||||
# Tables de la base de données
|
||||
|
||||
Voici la structure des tables de l'application, le nom des tables est donné sans préfixe.
|
||||
|
||||
La base se compose de quatre tables :
|
||||
|
||||
- `poll` : Le paramètrage des sondages;
|
||||
- `slot` : les choix disponibles pour chaque sondage;
|
||||
- `vote` : les votes effectués par les utilisateurs pour chaque sondage;
|
||||
- `comment` : les commentaires apportés à chaque sondage.
|
||||
# [Now available here](https://framagit.org/framasoft/framadate/wikis/home)
|
12
Makefile
Normal file
12
Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
locales:
|
||||
json2po -P -i locale/en.json -t locale/en.json -o po/framadate.pot
|
||||
|
||||
push-locales: locales
|
||||
zanata-cli -q -B push
|
||||
|
||||
pull-locales:
|
||||
zanata-cli -q -B pull --min-doc-percent 50
|
||||
./.po2json.sh
|
||||
|
||||
stats-locales:
|
||||
zanata-cli -q stats
|
78
README.md
78
README.md
@ -1,85 +1,41 @@
|
||||
# Présentation du projet
|
||||
# Framadate
|
||||
|
||||
![Gitlab](https://git.framasoft.org/assets/logo-black-f52905a40830b30aa287f784b537c823.png)[https://git.framasoft.org](https://git.framasoft.org)
|
||||
![English](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) Framadate is an online service for planning an appointment or making a decision quickly and easily. No registration is required.
|
||||
|
||||
![English](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ae/Flag_of_the_United_Kingdom.svg/20px-Flag_of_the_United_Kingdom.svg.png) **Framasoft uses GitLab** for the development of its free softwares. Our Github repositories are only mirrors.
|
||||
If you want to work with us, **fork us on [git.framasoft.org](https://git.framasoft.org)**. (no registration needed, you can sign in with your Github account)
|
||||
![Français](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png) Framadate est un service en ligne permettant de planifier un rendez-vous ou prendre des décisions rapidement et simplement. Aucune inscription préalable n’est nécessaire.
|
||||
|
||||
![Français](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Flag_of_France.svg/20px-Flag_of_France.svg.png) **Framasoft utilise GitLab** pour le développement de ses logiciels libres. Nos dépôts Github ne sont que des mirroirs.
|
||||
Si vous souhaitez travailler avec nous, **forkez-nous sur [git.framasoft.org](https://git.framasoft.org)**. (l'inscription n'est pas nécessaire, vous pouvez vous connecter avec votre compte Github)
|
||||
**Framadate is now in maintenance mode.** [Read more](https://framagit.org/framasoft/framadate/framadate/-/issues/545#note_920869)
|
||||
|
||||
---
|
||||
|
||||
# Compatibilités des navigateurs
|
||||
_Dernière mise à jour le 21 avril 2014_
|
||||
|
||||
| Navigateur | Version testée |
|
||||
|------------|-------------------------|
|
||||
| Firefox | Ubuntu 13.10/FF28 |
|
||||
| Chrome | Ubuntu 13.10/Chromium33 |
|
||||
| Opera | (non testé) |
|
||||
| Konqueror | (non testé) |
|
||||
| Links | (non testé, inutile) |
|
||||
| Safari | (non testé) |
|
||||
| IE | Win7/IE9 |
|
||||
|
||||
# Installation
|
||||
|
||||
Un fichier est dédié à l'installation de framadate : [INSTALL.md](INSTALL.md).
|
||||
Follow the instructions on our Wiki : <https://framagit.org/framasoft/framadate/framadate/-/wikis/home>
|
||||
|
||||
# Comment contribuer
|
||||
# Contribute
|
||||
|
||||
## De votre côté
|
||||
|
||||
1. Créer un compte sur [https://git.framasoft.org](https://git.framasoft.org)
|
||||
1. Créer un fork du projet principal : [Créer le fork](https://git.framasoft.org/framasoft/framadate/fork/new)
|
||||
1. Créer une branche nommée feature/[Description]
|
||||
* Où [Description] est une description en anglais très courte de ce qui va être fait
|
||||
1. Faire des commits dans votre branche
|
||||
1. Pusher la branche sur votre fork
|
||||
1. Demander une merge request
|
||||
|
||||
## La suite se passe de notre côté
|
||||
|
||||
1. Quelqu'un relira votre travail
|
||||
* Essayez de rendre ce travail plus facile en organisant vos commits
|
||||
1. S'il y a des remarques sur le travail, le relecteur fera des commentaires sur la merge request
|
||||
1. Si la merge request lui semble correcte il peut merger votre travail avec la branche **develop**
|
||||
|
||||
## Corrections suite à une relecture
|
||||
|
||||
La relecture de la merge request peut vous amener à faire des corrections.
|
||||
Vous pouvez faire ces corrections dans votre branche, ce qui aura pour effet de les ajouter à la merge request.
|
||||
|
||||
## Comprendre le code
|
||||
|
||||
Un fichier est dédié à l'appréhension du code de framadate : [Document technique](doc/TECHNICAL.md).
|
||||
## Code
|
||||
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/coding>
|
||||
|
||||
# Traductions
|
||||
|
||||
Les traductions se trouvent dans le dossier `locale`. Chaque langue est dans un fichier JSON différent organisé par section.
|
||||
Follow the instructions on <https://framagit.org/framasoft/framadate/framadate/-/wikis/translating>
|
||||
|
||||
# Synthèses des librairies utilisées
|
||||
# Used libraries
|
||||
|
||||
[Smarty](http://www.smarty.net/),
|
||||
gestion des templates pour PHP
|
||||
|
||||
[o80-i18n](https://github.com/olivierperez/o80-i18n),
|
||||
système d'internationalisation
|
||||
|
||||
[PHP 5.4.4](http://php.net)
|
||||
|
||||
PostgreSQL ou MySQL
|
||||
* PHP [PHP 7.3](http://php.net)
|
||||
* Templating [Smarty](http://www.smarty.net/),
|
||||
* I18N [o80-i18n](https://framagit.org/framasoft/framadate/o80-i18nn)
|
||||
* Database: MySQL or MariaDB.
|
||||
|
||||
---
|
||||
|
||||
Framadate est un fork du projet [STUdS](https://sourcesup.cru.fr/projects/studs/), il motorise framadate.org pour framasoft.org
|
||||
Framadate is a fork of the [STUdS](https://sourcesup.cru.fr/projects/studs/) project, that works at framadate.org for framasoft.org
|
||||
|
||||
Les auteurs principaux de Framadate sont :
|
||||
Previous main authors of Framadate were :
|
||||
* Simon LEBLANC
|
||||
* Pierre-Yves GOSSET
|
||||
|
||||
Les auteurs principaux du projet STUdS sont :
|
||||
Main authors of project STUdS are :
|
||||
* Guilhem BORGHESI
|
||||
* Raphaël DROZ
|
||||
|
||||
|
@ -16,13 +16,13 @@
|
||||
* 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\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;
|
||||
use Framadate\Message;
|
||||
|
||||
include_once __DIR__ . '/../app/inc/init.php';
|
||||
|
||||
@ -33,16 +33,16 @@ $poll_id = null;
|
||||
$poll = null;
|
||||
$message = null;
|
||||
$result = false;
|
||||
$comments = array();
|
||||
$comments = [];
|
||||
$is_admin = false;
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp']);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$securityService = new SecurityService();
|
||||
|
||||
@ -57,23 +57,23 @@ if (!empty($_POST['poll'])) {
|
||||
if (!empty($_POST['poll_admin'])) {
|
||||
$admin_poll_id = filter_input(INPUT_POST, 'poll_admin', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (strlen($admin_poll_id) === 24) {
|
||||
$is_admin = ($pollService->findByAdminId($admin_poll_id) != null);
|
||||
$is_admin = ($pollService->findByAdminId($admin_poll_id) !== null);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$poll) {
|
||||
$message = new Message('error', __('Error', 'This poll doesn\'t exist !'));
|
||||
} else if ($poll && !$securityService->canAccessPoll($poll) && !$is_admin) {
|
||||
} else if (!$is_admin && !$securityService->canAccessPoll($poll)) {
|
||||
$message = new Message('error', __('Password', 'Wrong password'));
|
||||
} else {
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$comment = $inputService->filterComment($_POST['comment']);
|
||||
|
||||
if ($name == null) {
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
|
||||
if ($message == null) {
|
||||
if ($message === null) {
|
||||
// Add comment
|
||||
$result = $pollService->addComment($poll_id, $name, $comment);
|
||||
if ($result) {
|
||||
@ -88,8 +88,10 @@ if (!$poll) {
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
$smarty->assign('comments', $comments);
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
$comments_html = $smarty->fetch('part/comments_list.tpl');
|
||||
|
||||
$response = array('result' => $result, 'message' => $message, 'comments' => $comments_html);
|
||||
$response = ['result' => $result, 'message' => $message, 'comments' => $comments_html];
|
||||
|
||||
echo json_encode($response);
|
||||
echo json_encode($response, JSON_THROW_ON_ERROR);
|
||||
|
@ -17,19 +17,19 @@
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\LogService;
|
||||
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']);
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$pollService = new PollService($logService);
|
||||
|
||||
$result = false;
|
||||
$message = null;
|
||||
@ -45,7 +45,7 @@ if (!empty($_POST['poll'])) {
|
||||
$token = $sessionService->get("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$token_form_value = empty($_POST['token']) ? null : $_POST['token'];
|
||||
$editedVoteUniqueId = filter_input(INPUT_POST, 'editedVoteUniqueId', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
if (is_null($poll) || $config['use_smtp'] == false || is_null($token) || is_null($token_form_value)
|
||||
if ($config['use_smtp'] === false || is_null($poll) || is_null($token) || is_null($token_form_value)
|
||||
|| !$token->check($token_form_value) || is_null($editedVoteUniqueId)) {
|
||||
$message = new Message('error', __('Error', 'Something is going wrong...'));
|
||||
}
|
||||
@ -69,7 +69,6 @@ if (is_null($message)) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_null($message)) {
|
||||
$url = Utils::getUrlSondage($poll_id, false, $editedVoteUniqueId);
|
||||
|
||||
@ -78,9 +77,9 @@ if (is_null($message)) {
|
||||
$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);
|
||||
$subject = '[' . NOMAPPLICATION . '][' . __('EditLink', 'REMINDER') . '] ' . __f('EditLink', 'Edit link for poll "%s"', $poll->title);
|
||||
|
||||
//$mailService->send($email, $subject, $body);
|
||||
$mailService->send($email, $subject, $body);
|
||||
$sessionService->remove("Common", SESSION_EDIT_LINK_TOKEN);
|
||||
$sessionService->set("Common", SESSION_EDIT_LINK_TIME, time());
|
||||
|
||||
@ -90,7 +89,6 @@ if (is_null($message)) {
|
||||
|
||||
$smarty->error_reporting = E_ALL & ~E_NOTICE;
|
||||
|
||||
$response = array('result' => $result, 'message' => $message);
|
||||
$response = ['result' => $result, 'message' => $message];
|
||||
|
||||
|
||||
echo json_encode($response);
|
||||
echo json_encode($response, JSON_THROW_ON_ERROR);
|
||||
|
@ -20,7 +20,7 @@
|
||||
use Framadate\Message;
|
||||
use Framadate\Utils;
|
||||
|
||||
define('ROOT_DIR', __DIR__ . '/../');
|
||||
const ROOT_DIR = __DIR__ . '/../';
|
||||
|
||||
/**
|
||||
* Checking for missing vendors.
|
||||
@ -35,7 +35,7 @@ if (!file_exists(ROOT_DIR . 'vendor/autoload.php') || !file_exists(ROOT_DIR . 'v
|
||||
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() == '') {
|
||||
if (session_id() === '') {
|
||||
session_start();
|
||||
}
|
||||
$ALLOWED_LANGUAGES = [
|
||||
@ -46,10 +46,10 @@ $ALLOWED_LANGUAGES = [
|
||||
'de' => 'Deutsch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
'ca' => 'Català',
|
||||
];
|
||||
const DEFAULT_LANGUAGE = 'en';
|
||||
require_once ROOT_DIR . 'app/inc/i18n.php';
|
||||
require_once '../app/inc/php_version.php';
|
||||
|
||||
/**
|
||||
* Function to sort messages by type (priorise errors on warning, warning on info, etc.)
|
||||
@ -58,29 +58,28 @@ require_once '../app/inc/php_version.php';
|
||||
* @param Message $b
|
||||
* @return int
|
||||
*/
|
||||
function compareCheckMessage(Message $a, Message $b)
|
||||
function compareCheckMessage(Message $a, Message $b): int
|
||||
{
|
||||
$values = array(
|
||||
$values = [
|
||||
'danger' => 0,
|
||||
'warning' => 1,
|
||||
'info' => 2,
|
||||
'success' => 3
|
||||
);
|
||||
];
|
||||
$vA = $values[$a->type];
|
||||
$vB = $values[$b->type];
|
||||
|
||||
if ($vA == $vB) {
|
||||
if ($vA === $vB) {
|
||||
return 0;
|
||||
}
|
||||
return ($vA < $vB) ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vars
|
||||
*/
|
||||
$messages = array();
|
||||
$inc_directory = ROOT_DIR. 'app/inc/';
|
||||
$messages = [];
|
||||
$inc_directory = ROOT_DIR . 'app/inc/';
|
||||
$conf_filename = $inc_directory . 'config.php';
|
||||
|
||||
/**
|
||||
@ -88,10 +87,10 @@ $conf_filename = $inc_directory . 'config.php';
|
||||
*/
|
||||
|
||||
// PHP Version
|
||||
if (PHP_VERSION_ID >= php_string_to_version_id(PHP_NEEDED_VERSION)) {
|
||||
$messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).',phpversion(), PHP_NEEDED_VERSION));
|
||||
if (version_compare(PHP_VERSION, PHP_NEEDED_VERSION) >= 0) {
|
||||
$messages[] = new Message('info', __f('Check','PHP version %s is enough (needed at least PHP %s).', PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION, PHP_NEEDED_VERSION));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.',phpversion(), PHP_NEEDED_VERSION));
|
||||
$messages[] = new Message('danger', __f('Check','Your PHP version (%s) is too old. This application needs at least PHP %s.', PHP_VERSION, PHP_NEEDED_VERSION));
|
||||
}
|
||||
|
||||
// INTL extension
|
||||
@ -101,8 +100,17 @@ if (extension_loaded('intl')) {
|
||||
$messages[] = new Message('danger', __('Check','You need to enable the PHP Intl extension.'));
|
||||
}
|
||||
|
||||
// Is template compile dir writable ?
|
||||
if (is_writable(ROOT_DIR . COMPILE_DIR)) {
|
||||
// mbstring extension
|
||||
if (extension_loaded('mbstring')) {
|
||||
$messages[] = new Message('info', __('Check','PHP mbstring extension is enabled.'));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __('Check','You need to enable the PHP mbstring extension.'));
|
||||
}
|
||||
|
||||
// Is template compile dir exists and writable ?
|
||||
if (!file_exists(ROOT_DIR . COMPILE_DIR)) {
|
||||
$messages[] = new Message('danger', __f('Check','The template compile directory (%s) doesn\'t exist in "%s". Retry the installation process.', COMPILE_DIR, realpath(ROOT_DIR)));
|
||||
} elseif (is_writable(ROOT_DIR . COMPILE_DIR)) {
|
||||
$messages[] = new Message('info', __f('Check','The template compile directory (%s) is writable.', realpath(ROOT_DIR . COMPILE_DIR)));
|
||||
} else {
|
||||
$messages[] = new Message('danger', __f('Check','The template compile directory (%s) is not writable.', realpath(ROOT_DIR . COMPILE_DIR)));
|
||||
@ -112,9 +120,9 @@ if (is_writable(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) dos not exists.', $inc_directory, $conf_filename));
|
||||
$messages[] = new Message('danger', __f('Check','The config file directory (%s) is not writable and the config file (%s) does not exists.', $inc_directory, $conf_filename));
|
||||
}
|
||||
|
||||
// Security
|
||||
@ -124,6 +132,12 @@ if (extension_loaded('openssl')) {
|
||||
$messages[] = new Message('warning', __('Check','Consider enabling the PHP extension OpenSSL for increased security.'));
|
||||
}
|
||||
|
||||
if (ini_get('session.cookie_httponly') === '1') {
|
||||
$messages[] = new Message('info', __('Check', 'Cookies are served from HTTP only.'));
|
||||
} else {
|
||||
$messages[] = new Message('warning', __('Check', "Consider setting « session.cookie_httponly = 1 » inside your php.ini or add « php_value session.cookie_httponly 1 » to your .htaccess so that cookies can't be accessed through Javascript."));
|
||||
}
|
||||
|
||||
// Datetime
|
||||
$timezone = ini_get('date.timezone');
|
||||
if (!empty($timezone)) {
|
||||
@ -132,14 +146,13 @@ if (!empty($timezone)) {
|
||||
$messages[] = new Message('warning', __('Check','Consider setting the date.timezone in php.ini.'));
|
||||
}
|
||||
|
||||
|
||||
// The percentage of steps needed to be ready to launch the application
|
||||
$errors = 0;
|
||||
$warnings = 0;
|
||||
foreach ($messages as $message) {
|
||||
if ($message->type == 'danger') {
|
||||
if ($message->type === 'danger') {
|
||||
$errors++;
|
||||
} else if ($message->type == 'warning') {
|
||||
} else if ($message->type === 'warning') {
|
||||
$warnings++;
|
||||
}
|
||||
}
|
||||
@ -170,11 +183,11 @@ usort($messages, 'compareCheckMessage');
|
||||
<body>
|
||||
<div class="container ombre">
|
||||
<div class="row">
|
||||
<form method="get" action="" class="hidden-print">
|
||||
<form method="get" class="hidden-print">
|
||||
<div class="input-group input-group-sm pull-right col-xs-12 col-sm-2">
|
||||
<select name="lang" class="form-control" title="<?=__('Language selector', 'Select the language')?>" >
|
||||
<?php foreach ($ALLOWED_LANGUAGES as $lang_key => $language) { ?>
|
||||
<option lang="fr" <?php if (substr($lang_key, 0, 2)==$locale) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
|
||||
<option lang="fr" <?php if (strpos($lang_key, $locale) === 0) { echo 'selected';} ?> value="<?=substr($lang_key, 0, 2)?>"><?=$language?></option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<span class="input-group-btn">
|
||||
@ -196,9 +209,9 @@ usort($messages, 'compareCheckMessage');
|
||||
<div>
|
||||
<?php
|
||||
foreach ($messages as $message) {
|
||||
echo '<div class="alert alert-'. $message->type .'" role="alert">';
|
||||
echo '<div class="alert alert-' . $message->type . '" role="alert">';
|
||||
echo Utils::htmlEscape($message->message);
|
||||
echo '<span class="sr-only">'. $message->type .'</span>';
|
||||
echo '<span class="sr-only">' . $message->type . '</span>';
|
||||
echo '</div>';
|
||||
}
|
||||
?>
|
||||
@ -208,7 +221,7 @@ usort($messages, 'compareCheckMessage');
|
||||
<a class="btn btn-info" role="button" href=""><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?= __('Check', 'Check again') ?></a>
|
||||
<?php
|
||||
if (!is_file($conf_filename)) {
|
||||
if ($errors == 0) {
|
||||
if ($errors === 0) {
|
||||
?>
|
||||
<a class="btn btn-primary" role="button" href="<?= Utils::get_server_name() . 'admin/install.php' ?>"><span class=" glyphicon glyphicon-arrow-right" aria-hidden="true"></span> <?= __('Check', 'Continue the installation') ?></a>
|
||||
<?php
|
||||
|
91
admin/cron_purge.php
Normal file
91
admin/cron_purge.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright 2018 Christian P. MOMON <cmomon@april.org>
|
||||
*
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
*/
|
||||
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SecurityService;
|
||||
|
||||
// /////////////////////////////////////////////////
|
||||
// ////////// include_once __DIR__ . '/../app/inc/init.php';
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
|
||||
define('ROOT_DIR', __DIR__ . '/../');
|
||||
|
||||
// Autoloading of dependencies with Composer
|
||||
require_once ROOT_DIR . '/vendor/autoload.php';
|
||||
require_once ROOT_DIR . '/vendor/o80/i18n/src/shortcuts.php';
|
||||
|
||||
if (ini_get('date.timezone') === '') {
|
||||
date_default_timezone_set('Europe/Paris');
|
||||
}
|
||||
|
||||
require_once ROOT_DIR . '/app/inc/constants.php';
|
||||
|
||||
define('CONF_FILENAME', ROOT_DIR . '/app/inc/config.php');
|
||||
if (is_file(CONF_FILENAME)) {
|
||||
@include_once CONF_FILENAME;
|
||||
|
||||
// Connection to database
|
||||
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
|
||||
RepositoryFactory::init($connect);
|
||||
$err = 0;
|
||||
} else {
|
||||
define('NOMAPPLICATION', 'Framadate');
|
||||
define('DEFAULT_LANGUAGE', 'fr');
|
||||
define('IMAGE_TITRE', 'images/logo-framadate.png');
|
||||
define('LOG_FILE', 'admin/stdout.log');
|
||||
}
|
||||
|
||||
require_once ROOT_DIR . '/app/inc/i18n.php';
|
||||
|
||||
// /////////////////////////////////////////////////
|
||||
|
||||
/* Variables */
|
||||
/* --------- */
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
$logService = new LogService();
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$securityService = new SecurityService();
|
||||
$inputService = new InputService();
|
||||
|
||||
/* Action */
|
||||
/* ------ */
|
||||
$logService->log('CRON PURGE', 'Cron purge starting…');
|
||||
|
||||
$ended = false;
|
||||
$iterationCount = 0;
|
||||
$totalCount = 0;
|
||||
while (!$ended)
|
||||
{
|
||||
$count = $purgeService->purgeOldPolls();
|
||||
$logService->log('CRON PURGE', 'count='.$count);
|
||||
if ($count == 0)
|
||||
{
|
||||
$ended = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iterationCount += 1;
|
||||
$totalCount += $count;
|
||||
}
|
||||
}
|
||||
|
||||
$logService->log('CRON PURGE', 'Purged '.$totalCount.' poll(s) in '.$iterationCount.' iterations.');
|
||||
$logService->log('CRON PURGE', 'Cron purge done.');
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
echo date("Y-m-d H:i:s").": cron purge done.\n"
|
||||
|
||||
?>
|
@ -29,6 +29,7 @@ if (is_file(CONF_FILENAME)) {
|
||||
|
||||
$error = null;
|
||||
$installService = new InstallService();
|
||||
$result['details'] = null;
|
||||
|
||||
if (!empty($_POST)) {
|
||||
$installService->updateFields($_POST);
|
||||
@ -37,12 +38,13 @@ if (!empty($_POST)) {
|
||||
if ($result['status'] === 'OK') {
|
||||
header(('Location: ' . Utils::get_server_name() . 'admin/migration.php'));
|
||||
exit;
|
||||
} else {
|
||||
$error = __('Error', $result['code']);
|
||||
}
|
||||
|
||||
$error = __('Error', $result['code']);
|
||||
}
|
||||
|
||||
$smarty->assign('error', $error);
|
||||
$smarty->assign('error_details', $result['details']);
|
||||
$smarty->assign('title', __('Admin', 'Installation'));
|
||||
$smarty->assign('fields', $installService->getFields());
|
||||
$smarty->display('admin/install.tpl');
|
@ -17,15 +17,18 @@
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
use Framadate\Migration\From_0_0_to_0_8_Migration;
|
||||
use Framadate\Migration\From_0_8_to_0_9_Migration;
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9;
|
||||
use Framadate\Migration\AddColumn_receiveNewComments_For_0_9;
|
||||
use Framadate\Migration\AddColumn_uniqId_In_vote_For_0_9;
|
||||
use Framadate\Migration\AddColumn_hidden_In_poll_For_0_9;
|
||||
use Framadate\Migration\Alter_Comment_table_for_name_length;
|
||||
use Framadate\Migration\Alter_Comment_table_adding_date;
|
||||
use Framadate\Migration\Generate_uniqId_for_old_votes;
|
||||
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;
|
||||
@ -42,21 +45,25 @@ $migrations = [
|
||||
new AddColumn_receiveNewComments_For_0_9(),
|
||||
new AddColumn_uniqId_In_vote_For_0_9(),
|
||||
new AddColumn_hidden_In_poll_For_0_9(),
|
||||
new AddColumn_ValueMax_In_poll_For_1_1(),
|
||||
new Generate_uniqId_for_old_votes(),
|
||||
new RPadVotes_from_0_8(),
|
||||
new 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 Increase_pollId_size(),
|
||||
new AddColumn_ValueMax_In_poll_For_1_1(),
|
||||
new Fix_MySQL_No_Zero_Date(),
|
||||
];
|
||||
// ---------------------------------------
|
||||
|
||||
// Check if MIGRATION_TABLE already exists
|
||||
/** @var FramaDB $connect */
|
||||
$tables = $connect->allTables();
|
||||
$pdo = $connect->getPDO();
|
||||
$prefixedMigrationTable = Utils::table(MIGRATION_TABLE);
|
||||
|
||||
if (!in_array($prefixedMigrationTable, $tables)) {
|
||||
if (!in_array($prefixedMigrationTable, $tables, true)) {
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . $prefixedMigrationTable . '` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
@ -104,7 +111,6 @@ foreach ($migrations as $migration) {
|
||||
} else {
|
||||
$countSkipped++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$countTotal = $countSucceeded + $countFailed + $countSkipped;
|
||||
|
@ -50,7 +50,7 @@ $poll_to_delete = null;
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$adminPollService = new AdminPollService($connect, $pollService, $logService);
|
||||
$superAdminService = new SuperAdminService();
|
||||
$securityService = new SecurityService();
|
||||
|
@ -34,14 +34,14 @@ $message = null;
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$purgeService = new PurgeService($logService);
|
||||
$securityService = new SecurityService();
|
||||
$inputService = new InputService();
|
||||
|
||||
/* POST */
|
||||
/*-----*/
|
||||
|
||||
$action = $inputService->filterName(isset($_POST['action']) ? $_POST['action'] : null);
|
||||
$action = $inputService->filterName($_POST['action'] ?? null);
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
|
109
adminstuds.php
109
adminstuds.php
@ -19,15 +19,17 @@
|
||||
use Framadate\Editable;
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Exception\MomentAlreadyExistsException;
|
||||
use Framadate\Message;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Services\AdminPollService;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\NotificationService;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
@ -45,11 +47,12 @@ $editingVoteId = 0;
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$adminPollService = new AdminPollService($connect, $pollService, $logService);
|
||||
$inputService = new InputService();
|
||||
$mailService = new MailService($config['use_smtp']);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$notificationService = new NotificationService($mailService);
|
||||
$sessionService = new SessionService();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
@ -69,6 +72,18 @@ if ($poll) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// creation message
|
||||
// -------------------------------
|
||||
|
||||
$messagePollCreated = $sessionService->get("Framadate", "messagePollCreated", FALSE);
|
||||
|
||||
if ($messagePollCreated) {
|
||||
$sessionService->remove("Framadate", "messagePollCreated");
|
||||
|
||||
$message = new Message('success', __('adminstuds', 'The poll is created.'));
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Update poll info
|
||||
// -------------------------------
|
||||
@ -79,26 +94,26 @@ if (isset($_POST['update_poll_info'])) {
|
||||
'rules', 'expiration_date', 'name', 'hidden', 'removePassword', 'password']);
|
||||
|
||||
// Update the right poll field
|
||||
if ($field == 'title') {
|
||||
if ($field === 'title') {
|
||||
$title = $inputService->filterTitle($_POST['title']);
|
||||
if ($title) {
|
||||
$poll->title = $title;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'admin_mail') {
|
||||
} elseif ($field === 'admin_mail') {
|
||||
$admin_mail = $inputService->filterMail($_POST['admin_mail']);
|
||||
if ($admin_mail) {
|
||||
$poll->admin_mail = $admin_mail;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'description') {
|
||||
} elseif ($field === 'description') {
|
||||
$description = $inputService->filterDescription($_POST['description']);
|
||||
if ($description) {
|
||||
$poll->description = $description;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'rules') {
|
||||
$rules = strip_tags($_POST['rules']);
|
||||
} elseif ($field === 'rules') {
|
||||
$rules = (int) strip_tags($_POST['rules']);
|
||||
switch ($rules) {
|
||||
case 0:
|
||||
$poll->active = false;
|
||||
@ -121,39 +136,61 @@ if (isset($_POST['update_poll_info'])) {
|
||||
$updated = true;
|
||||
break;
|
||||
}
|
||||
} elseif ($field == 'expiration_date') {
|
||||
$expiration_date = $inputService->filterDate($_POST['expiration_date']);
|
||||
if ($expiration_date) {
|
||||
$poll->end_date = $expiration_date;
|
||||
} elseif ($field === 'expiration_date') {
|
||||
$givenExpirationDate = $inputService->parseDate($_POST['expiration_date']);
|
||||
$expiration_date = $inputService->validateDate($givenExpirationDate, $pollService->minExpiryDate(), $pollService->maxExpiryDate());
|
||||
if ($poll->end_date !== $expiration_date->format('Y-m-d H:i:s')) {
|
||||
$poll->end_date = $expiration_date->format('Y-m-d H:i:s');
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'name') {
|
||||
$admin_name = $inputService->filterName($_POST['name']);
|
||||
} elseif ($field === 'name') {
|
||||
$admin_name = $_POST['name'];
|
||||
$admin_name = mb_substr($admin_name, 0, 32);
|
||||
$admin_name = $inputService->filterName($admin_name);
|
||||
if ($admin_name) {
|
||||
$poll->admin_name = $admin_name;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'hidden') {
|
||||
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
|
||||
if ($hidden != $poll->hidden) {
|
||||
} elseif ($field === 'hidden') {
|
||||
$hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
|
||||
if ($hidden !== $poll->hidden) {
|
||||
$poll->hidden = $hidden;
|
||||
$poll->results_publicly_visible = false;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'removePassword') {
|
||||
$removePassword = isset($_POST['removePassword']) ? $inputService->filterBoolean($_POST['removePassword']) : false;
|
||||
} elseif ($field === 'removePassword') {
|
||||
$removePassword = isset($_POST['removePassword']) && $inputService->filterBoolean($_POST['removePassword']);
|
||||
if ($removePassword) {
|
||||
$poll->results_publicly_visible = false;
|
||||
$poll->password_hash = null;
|
||||
$updated = true;
|
||||
}
|
||||
} elseif ($field == 'password') {
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) ? $inputService->filterBoolean($_POST['resultsPubliclyVisible']) : false;
|
||||
} elseif ($field === 'password') {
|
||||
$password = $_POST['password'] ?? null;
|
||||
|
||||
/**
|
||||
* Did the user choose results to be publicly visible ?
|
||||
*/
|
||||
$resultsPubliclyVisible = isset($_POST['resultsPubliclyVisible']) && $inputService->filterBoolean($_POST['resultsPubliclyVisible']);
|
||||
/**
|
||||
* If there's one, save the password
|
||||
*/
|
||||
if (!empty($password)) {
|
||||
$poll->password_hash = PasswordHasher::hash($password);
|
||||
$updated = true;
|
||||
}
|
||||
if ($resultsPubliclyVisible != $poll->results_publicly_visible) {
|
||||
|
||||
/**
|
||||
* If not pasword was set and the poll should be hidden, hide the results
|
||||
*/
|
||||
if ($poll->password_hash === null || $poll->hidden === true) {
|
||||
$poll->results_publicly_visible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't have a password, the poll is hidden and we change the results public visibility
|
||||
*/
|
||||
if ($resultsPubliclyVisible !== $poll->results_publicly_visible && $poll->password_hash !== null && $poll->hidden === false) {
|
||||
$poll->results_publicly_visible = $resultsPubliclyVisible;
|
||||
$updated = true;
|
||||
}
|
||||
@ -181,6 +218,8 @@ if (!empty($_GET['vote'])) {
|
||||
// Something to save (edit or add)
|
||||
// -------------------------------
|
||||
|
||||
$selectedNewVotes = [];
|
||||
|
||||
if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$editedVote = filter_input(INPUT_POST, 'save', FILTER_VALIDATE_INT);
|
||||
@ -190,11 +229,11 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
if (empty($editedVote)) {
|
||||
$message = new Message('danger', __('Error', 'Something is going wrong...'));
|
||||
}
|
||||
if (count($choices) != count($_POST['choices'])) {
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message == null) {
|
||||
if ($message === null) {
|
||||
// Update vote
|
||||
try {
|
||||
$result = $pollService->updateVote($poll_id, $editedVote, $name, $choices, $slots_hash);
|
||||
@ -203,8 +242,12 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
} else {
|
||||
$message = new Message('danger', __('Error', 'Update vote failed'));
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', 'The name you\'ve chosen already exist in this poll!'));
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
} elseif (isset($_POST['save'])) { // Add a new vote
|
||||
@ -212,14 +255,14 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
$choices = $inputService->filterArray($_POST['choices'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => CHOICE_REGEX]]);
|
||||
$slots_hash = $inputService->filterMD5($_POST['control']);
|
||||
|
||||
if ($name == null) {
|
||||
if ($name === null) {
|
||||
$message = new Message('danger', __('Error', 'The name is invalid.'));
|
||||
}
|
||||
if (count($choices) != count($_POST['choices'])) {
|
||||
if (count($choices) !== count($_POST['choices'])) {
|
||||
$message = new Message('danger', __('Error', 'There is a problem with your choices'));
|
||||
}
|
||||
|
||||
if ($message == null) {
|
||||
if ($message === null) {
|
||||
// Add vote
|
||||
try {
|
||||
$result = $pollService->addVote($poll_id, $name, $choices, $slots_hash);
|
||||
@ -230,8 +273,11 @@ if (!empty($_POST['save'])) { // Save edition of an old vote
|
||||
}
|
||||
} catch (AlreadyExistsException $aee) {
|
||||
$message = new Message('danger', __('Error', 'You already voted'));
|
||||
$selectedNewVotes = $choices;
|
||||
} catch (ConcurrentEditionException $cee) {
|
||||
$message = new Message('danger', __('Error', 'Poll has been updated before you vote'));
|
||||
} catch (ConcurrentVoteException $cve) {
|
||||
$message = new Message('danger', __('Error', "Your vote wasn't counted, because someone voted in the meantime and it conflicted with your choices and the poll conditions. Please retry."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,7 +447,6 @@ $slots = $pollService->allSlotsByPoll($poll);
|
||||
$votes = $pollService->allVotesByPollId($poll_id);
|
||||
$comments = $pollService->allCommentsByPollId($poll_id);
|
||||
|
||||
|
||||
// Assign data to template
|
||||
$smarty->assign('poll_id', $poll_id);
|
||||
$smarty->assign('admin_poll_id', $admin_poll_id);
|
||||
@ -412,7 +457,7 @@ $smarty->assign('deletion_date', strtotime($poll->end_date) + PURGE_DELAY * 8640
|
||||
$smarty->assign('slots', $poll->format === 'D' ? $pollService->splitSlots($slots) : $slots);
|
||||
$smarty->assign('slots_hash', $pollService->hashSlots($slots));
|
||||
$smarty->assign('votes', $pollService->splitVotes($votes));
|
||||
$smarty->assign('best_choices', $pollService->computeBestChoices($votes));
|
||||
$smarty->assign('best_choices', $pollService->computeBestChoices($votes, $poll));
|
||||
$smarty->assign('comments', $comments);
|
||||
$smarty->assign('editingVoteId', $editingVoteId);
|
||||
$smarty->assign('message', $message);
|
||||
@ -421,5 +466,7 @@ $smarty->assign('hidden', false);
|
||||
$smarty->assign('accessGranted', true);
|
||||
$smarty->assign('resultPubliclyVisible', true);
|
||||
$smarty->assign('editedVoteUniqueId', '');
|
||||
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
|
||||
$smarty->assign('selectedNewVotes', $selectedNewVotes);
|
||||
|
||||
$smarty->display('studs.tpl');
|
||||
|
@ -33,27 +33,26 @@ class Choice
|
||||
public function __construct($name='')
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->slots = array();
|
||||
$this->slots = [];
|
||||
}
|
||||
|
||||
public function addSlot($slot)
|
||||
public function addSlot($slot): void
|
||||
{
|
||||
$this->slots[] = $slot;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getSlots()
|
||||
public function getSlots(): array
|
||||
{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
static function compare(Choice $a, Choice $b)
|
||||
public static function compare(Choice $a, Choice $b): int
|
||||
{
|
||||
return strcmp($a->name, $b->name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,19 +19,17 @@
|
||||
|
||||
namespace Framadate;
|
||||
|
||||
|
||||
/**
|
||||
* Class Editable
|
||||
*
|
||||
* Is used to specify the poll's edition permissions.
|
||||
* @TODO : wait to use the SplEnum
|
||||
*
|
||||
* @package Framadate
|
||||
*/
|
||||
class Editable { // extends SplEnum
|
||||
const __default = self::EDITABLE_BY_ALL;
|
||||
|
||||
const NOT_EDITABLE = 0;
|
||||
const EDITABLE_BY_ALL = 1;
|
||||
const EDITABLE_BY_OWN = 2;
|
||||
public const NOT_EDITABLE = 0;
|
||||
public const EDITABLE_BY_ALL = 1;
|
||||
public const EDITABLE_BY_OWN = 2;
|
||||
}
|
@ -2,8 +2,4 @@
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class AlreadyExistsException extends \Exception {
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,4 @@
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class ConcurrentEditionException extends \Exception {
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
}
|
||||
|
10
app/classes/Framadate/Exception/ConcurrentVoteException.php
Normal file
10
app/classes/Framadate/Exception/ConcurrentVoteException.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
/**
|
||||
* Class ConcurrentVoteException
|
||||
*
|
||||
* Thrown when a poll has a maximum votes constraint for options, and a vote happened since the poll was rendered
|
||||
*/
|
||||
class ConcurrentVoteException extends \Exception {
|
||||
}
|
@ -2,8 +2,4 @@
|
||||
namespace Framadate\Exception;
|
||||
|
||||
class MomentAlreadyExistsException extends \Exception {
|
||||
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
}
|
||||
|
10
app/classes/Framadate/Exception/PollNotFoundException.php
Normal file
10
app/classes/Framadate/Exception/PollNotFoundException.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Framadate\Exception;
|
||||
|
||||
/**
|
||||
* Class PollNotFoundException
|
||||
*
|
||||
* Thrown when a poll isn't found in a critical process
|
||||
*/
|
||||
class PollNotFoundException extends \Exception {
|
||||
}
|
@ -20,7 +20,6 @@ namespace Framadate;
|
||||
|
||||
class Form
|
||||
{
|
||||
|
||||
public $title;
|
||||
public $id;
|
||||
public $description;
|
||||
@ -29,10 +28,11 @@ class Form
|
||||
public $format;
|
||||
public $end_date;
|
||||
public $choix_sondage;
|
||||
public $ValueMax;
|
||||
|
||||
/**
|
||||
* Tells if users can modify their choices.
|
||||
* @var \Framadate\Editable
|
||||
* @var int
|
||||
*/
|
||||
public $editable;
|
||||
|
||||
@ -50,6 +50,12 @@ class Form
|
||||
* If true, only the poll maker can see the poll's results
|
||||
* @var boolean
|
||||
*/
|
||||
public $use_ValueMax;
|
||||
|
||||
/**
|
||||
* if true, there will be a limit of voters per option
|
||||
* @var boolean
|
||||
*/
|
||||
public $hidden;
|
||||
|
||||
/**
|
||||
@ -86,11 +92,12 @@ class Form
|
||||
$this->clearChoices();
|
||||
}
|
||||
|
||||
public function clearChoices() {
|
||||
$this->choices = array();
|
||||
public function clearChoices(): void
|
||||
{
|
||||
$this->choices = [];
|
||||
}
|
||||
|
||||
public function addChoice(Choice $choice)
|
||||
public function addChoice(Choice $choice): void
|
||||
{
|
||||
$this->choices[] = $choice;
|
||||
}
|
||||
@ -100,9 +107,8 @@ class Form
|
||||
return $this->choices;
|
||||
}
|
||||
|
||||
public function sortChoices()
|
||||
public function sortChoices(): void
|
||||
{
|
||||
usort($this->choices, array('Framadate\Choice', 'compare'));
|
||||
usort($this->choices, [Choice::class, 'compare']);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,19 +23,21 @@ use PDO;
|
||||
class FramaDB {
|
||||
/**
|
||||
* PDO Object, connection to database.
|
||||
* @var PDO
|
||||
*/
|
||||
private $pdo = null;
|
||||
private $pdo;
|
||||
|
||||
function __construct($connection_string, $user, $password) {
|
||||
$this->pdo = new \PDO($connection_string, $user, $password);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
public function __construct(string $connection_string, string $user, string $password) {
|
||||
$this->pdo = new PDO($connection_string, $user, $password);
|
||||
$this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PDO Connection to database
|
||||
* @return PDO Connection to database
|
||||
*/
|
||||
function getPDO() {
|
||||
public function getPDO(): PDO
|
||||
{
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
@ -44,42 +46,50 @@ class FramaDB {
|
||||
*
|
||||
* @return array The array of table names
|
||||
*/
|
||||
function allTables() {
|
||||
$result = $this->pdo->query('SHOW TABLES');
|
||||
$schemas = $result->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
return $schemas;
|
||||
public function allTables(): array
|
||||
{
|
||||
return $this->pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
function prepare($sql) {
|
||||
/**
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function prepare(string $sql) {
|
||||
return $this->pdo->prepare($sql);
|
||||
}
|
||||
|
||||
function beginTransaction() {
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->pdo->beginTransaction();
|
||||
}
|
||||
|
||||
function commit() {
|
||||
public function commit(): void
|
||||
{
|
||||
$this->pdo->commit();
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->pdo->rollback();
|
||||
}
|
||||
|
||||
function errorCode() {
|
||||
public function errorCode(): ?string {
|
||||
return $this->pdo->errorCode();
|
||||
}
|
||||
|
||||
function errorInfo() {
|
||||
public function errorInfo(): array
|
||||
{
|
||||
return $this->pdo->errorInfo();
|
||||
}
|
||||
|
||||
function query($sql) {
|
||||
/**
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function query($sql) {
|
||||
return $this->pdo->query($sql);
|
||||
}
|
||||
|
||||
public function lastInsertId() {
|
||||
public function lastInsertId(): string {
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
namespace Framadate;
|
||||
|
||||
class Message {
|
||||
|
||||
var $type;
|
||||
var $message;
|
||||
var $link;
|
||||
@ -27,7 +26,7 @@ class Message {
|
||||
var $linkIcon;
|
||||
var $includeTemplate;
|
||||
|
||||
function __construct($type, $message, $link=null, $linkTitle=null, $linkIcon=null, $includeTemplate=null) {
|
||||
public function __construct($type, $message, $link=null, $linkTitle=null, $linkIcon=null, $includeTemplate=null) {
|
||||
$this->type = $type;
|
||||
$this->message = $message;
|
||||
$this->link = $link;
|
||||
@ -35,6 +34,4 @@ class Message {
|
||||
$this->linkIcon = $linkIcon;
|
||||
$this->includeTemplate = $includeTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration adds the field Value_Max on the poll table.
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 0.9
|
||||
*/
|
||||
class AddColumn_ValueMax_In_poll_For_1_1 implements Migration {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description():string {
|
||||
return 'Add column "ValueMax" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterPollTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
ADD `ValueMax` TINYINT NULL;');
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration adds the field hidden on the poll table.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class AddColumn_hidden_In_poll_For_0_9 implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,8 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string
|
||||
{
|
||||
return 'Add column "hidden" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
@ -44,12 +45,13 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool
|
||||
{
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.9 are presents
|
||||
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
|
||||
@ -59,19 +61,20 @@ class AddColumn_hidden_In_poll_For_0_9 implements Migration {
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool
|
||||
{
|
||||
$this->alterPollTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
ADD `hidden` TINYINT( 1 ) NOT NULL DEFAULT "0"');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration adds the field receiveNewComments on the poll table.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class AddColumn_receiveNewComments_For_0_9 implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,8 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string
|
||||
{
|
||||
return 'Add column "receiveNewComments" for version 0.9';
|
||||
}
|
||||
|
||||
@ -44,12 +45,13 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool
|
||||
{
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.9 are presents
|
||||
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
|
||||
@ -59,20 +61,21 @@ class AddColumn_receiveNewComments_For_0_9 implements Migration {
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool
|
||||
{
|
||||
$this->alterPollTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
ADD `receiveNewComments` TINYINT(1) DEFAULT \'0\'
|
||||
AFTER `receiveNewVotes`');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration adds the field uniqId on the vote table.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'Add column "uniqId" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
@ -44,12 +44,12 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.9 are presents
|
||||
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
|
||||
@ -59,21 +59,21 @@ class AddColumn_uniqId_In_vote_For_0_9 implements Migration {
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterPollTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('vote') . '`
|
||||
ADD `uniqId` CHAR(16) NOT NULL
|
||||
AFTER `id`,
|
||||
ADD INDEX (`uniqId`) ;');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration adds the fields password_hash and results_publicly_visible on the poll table.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
function description(): string {
|
||||
return 'Add columns "password_hash" and "results_publicly_visible" in table "vote" for version 0.9';
|
||||
}
|
||||
|
||||
@ -44,12 +44,12 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.9 are presents
|
||||
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
|
||||
@ -59,20 +59,20 @@ class AddColumns_password_hash_And_results_publicly_visible_In_poll_For_0_9 impl
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterPollTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
ADD `password_hash` VARCHAR(255) NULL DEFAULT NULL ,
|
||||
ADD `results_publicly_visible` TINYINT(1) NULL DEFAULT NULL');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration alter the comment table to add a date column.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 1.0
|
||||
*/
|
||||
class Alter_Comment_table_adding_date implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class Alter_Comment_table_adding_date implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description():string {
|
||||
return 'Alter the comment table to add a date column.';
|
||||
}
|
||||
|
||||
@ -44,29 +44,29 @@ class Alter_Comment_table_adding_date implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterCommentTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterCommentTable(\PDO $pdo) {
|
||||
private function alterCommentTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('comment') . '`
|
||||
ADD `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ;');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration alter the comment table to set a length to the name column.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 1.0
|
||||
*/
|
||||
class Alter_Comment_table_for_name_length implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class Alter_Comment_table_for_name_length implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'Alter the comment table to set a length to the name column.';
|
||||
}
|
||||
|
||||
@ -44,29 +44,29 @@ class Alter_Comment_table_for_name_length implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterCommentTable($pdo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterCommentTable(\PDO $pdo) {
|
||||
private function alterCommentTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('comment') . '`
|
||||
CHANGE `name` `name` VARCHAR( 64 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ;');
|
||||
}
|
||||
|
||||
}
|
||||
|
72
app/classes/Framadate/Migration/Fix_MySQL_No_Zero_Date.php
Normal file
72
app/classes/Framadate/Migration/Fix_MySQL_No_Zero_Date.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration sets Poll.end_date to NULL by default
|
||||
*
|
||||
* @package Framadate\Migration
|
||||
* @version 1.1
|
||||
*/
|
||||
class Fix_MySQL_No_Zero_Date implements Migration {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
public function description(): string {
|
||||
return 'Sets Poll end_date to NULL by default (work around MySQL NO_ZERO_DATE)';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true if the Migration should be executed.
|
||||
*/
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->prepare("SELECT Column_Default from Information_Schema.Columns where Table_Name = ? AND Column_Name = ?;");
|
||||
$stmt->bindValue(1, Utils::table('poll'));
|
||||
$stmt->bindValue(2, 'end_date');
|
||||
$stmt->execute();
|
||||
$default = $stmt->fetch(PDO::FETCH_COLUMN);
|
||||
|
||||
$driver_name = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
|
||||
|
||||
return $default !== null && $driver_name === 'mysql';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool if the execution succeeded
|
||||
*/
|
||||
public function execute(PDO $pdo): bool {
|
||||
$pdo->exec('ALTER TABLE ' . Utils::table('poll') . ' MODIFY end_date TIMESTAMP NULL DEFAULT NULL;');
|
||||
return true;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Class From_0_0_to_0_8_Migration
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.8
|
||||
*/
|
||||
class From_0_0_to_0_8_Migration implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class From_0_0_to_0_8_Migration implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'First installation of the Framadate application (v0.8)';
|
||||
}
|
||||
|
||||
@ -44,12 +44,12 @@ class From_0_0_to_0_8_Migration implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES like \'' . TABLENAME_PREFIX . '%\''); //issue187 : pouvoir installer framadate dans une base contenant d'autres tables.
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$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)]);
|
||||
@ -59,10 +59,10 @@ class From_0_0_to_0_8_Migration implements Migration {
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `sondage` (
|
||||
`id_sondage` char(16) NOT NULL,
|
||||
@ -72,7 +72,7 @@ CREATE TABLE IF NOT EXISTS `sondage` (
|
||||
`titre` text,
|
||||
`id_sondage_admin` char(24) DEFAULT NULL,
|
||||
`date_creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`date_fin` timestamp NOT NULL,
|
||||
`date_fin` timestamp NULL DEFAULT NULL,
|
||||
`format` varchar(2) DEFAULT NULL,
|
||||
`mailsonde` tinyint(1) DEFAULT \'0\',
|
||||
`statut` int(11) NOT NULL DEFAULT \'1\' COMMENT \'1 = actif ; 0 = inactif ; \',
|
||||
@ -105,5 +105,6 @@ CREATE TABLE IF NOT EXISTS `user_studs` (
|
||||
PRIMARY KEY (`id_users`),
|
||||
KEY `id_sondage` (`id_sondage`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This class executes the aciton in database to migrate data from version 0.8 to 0.9.
|
||||
@ -27,8 +28,7 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class From_0_8_to_0_9_Migration implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,7 +36,7 @@ class From_0_8_to_0_9_Migration implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'From 0.8 to 0.9';
|
||||
}
|
||||
|
||||
@ -44,12 +44,12 @@ class From_0_8_to_0_9_Migration implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the Migration should be executed.
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.8 are presents
|
||||
$diff = array_diff(['sondage', 'sujet_studs', 'comments', 'user_studs'], $tables);
|
||||
@ -59,10 +59,10 @@ class From_0_8_to_0_9_Migration implements Migration {
|
||||
/**
|
||||
* This method is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->createPollTable($pdo);
|
||||
$this->createCommentTable($pdo);
|
||||
$this->createSlotTable($pdo);
|
||||
@ -80,7 +80,8 @@ class From_0_8_to_0_9_Migration implements Migration {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createPollTable(\PDO $pdo) {
|
||||
private function createPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
|
||||
`id` CHAR(16) NOT NULL,
|
||||
@ -90,7 +91,7 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
|
||||
`admin_name` VARCHAR(64) DEFAULT NULL,
|
||||
`admin_mail` VARCHAR(128) DEFAULT NULL,
|
||||
`creation_date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`end_date` TIMESTAMP NOT NULL,
|
||||
`end_date` TIMESTAMP NULL DEFAULT NULL,
|
||||
`format` VARCHAR(1) DEFAULT NULL,
|
||||
`editable` TINYINT(1) DEFAULT \'0\',
|
||||
`receiveNewVotes` TINYINT(1) DEFAULT \'0\',
|
||||
@ -101,7 +102,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('poll') . '` (
|
||||
DEFAULT CHARSET = utf8');
|
||||
}
|
||||
|
||||
private function migrateFromSondageToPoll(\PDO $pdo) {
|
||||
private function migrateFromSondageToPoll(PDO $pdo): void
|
||||
{
|
||||
$select = $pdo->query('
|
||||
SELECT
|
||||
`id_sondage`,
|
||||
@ -127,7 +129,7 @@ INSERT INTO `' . Utils::table('poll') . '`
|
||||
(`id`, `admin_id`, `title`, `description`, `admin_name`, `admin_mail`, `creation_date`, `end_date`, `format`, `editable`, `receiveNewVotes`, `active`)
|
||||
VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
|
||||
$insert->execute([
|
||||
$row->id_sondage,
|
||||
$row->id_sondage_admin,
|
||||
@ -145,7 +147,8 @@ VALUE (?,?,?,?,?,?,?,?,?,?,?,?)');
|
||||
}
|
||||
}
|
||||
|
||||
private function createSlotTable(\PDO $pdo) {
|
||||
private function createSlotTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
@ -159,7 +162,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
|
||||
DEFAULT CHARSET = utf8');
|
||||
}
|
||||
|
||||
private function migrateFromSujetStudsToSlot(\PDO $pdo) {
|
||||
private function migrateFromSujetStudsToSlot(PDO $pdo): void
|
||||
{
|
||||
$stmt = $pdo->query('SELECT * FROM sujet_studs');
|
||||
$sujets = $stmt->fetchAll();
|
||||
$slots = [];
|
||||
@ -181,7 +185,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('slot') . '` (
|
||||
}
|
||||
}
|
||||
|
||||
private function createCommentTable(\PDO $pdo) {
|
||||
private function createCommentTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
@ -195,7 +200,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('comment') . '` (
|
||||
DEFAULT CHARSET = utf8');
|
||||
}
|
||||
|
||||
private function migrateFromCommentsToComment(\PDO $pdo) {
|
||||
private function migrateFromCommentsToComment(PDO $pdo): void
|
||||
{
|
||||
$select = $pdo->query('
|
||||
SELECT
|
||||
`id_sondage`,
|
||||
@ -207,7 +213,7 @@ SELECT
|
||||
INSERT INTO `' . Utils::table('comment') . '` (`poll_id`, `name`, `comment`)
|
||||
VALUE (?,?,?)');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
|
||||
$insert->execute([
|
||||
$row->id_sondage,
|
||||
$this->unescape($row->usercomment),
|
||||
@ -216,7 +222,8 @@ VALUE (?,?,?)');
|
||||
}
|
||||
}
|
||||
|
||||
private function createVoteTable(\PDO $pdo) {
|
||||
private function createVoteTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
@ -230,7 +237,8 @@ CREATE TABLE IF NOT EXISTS `' . Utils::table('vote') . '` (
|
||||
DEFAULT CHARSET = utf8');
|
||||
}
|
||||
|
||||
private function migrateFromUserStudsToVote(\PDO $pdo) {
|
||||
private function migrateFromUserStudsToVote(PDO $pdo): void
|
||||
{
|
||||
$select = $pdo->query('
|
||||
SELECT
|
||||
`id_sondage`,
|
||||
@ -242,7 +250,7 @@ SELECT
|
||||
INSERT INTO `' . Utils::table('vote') . '` (`poll_id`, `name`, `choices`)
|
||||
VALUE (?,?,?)');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
|
||||
$insert->execute([
|
||||
$row->id_sondage,
|
||||
$this->unescape($row->nom),
|
||||
@ -251,7 +259,8 @@ VALUE (?,?,?)');
|
||||
}
|
||||
}
|
||||
|
||||
private function transformSujetToSlot($sujet) {
|
||||
private function transformSujetToSlot($sujet): array
|
||||
{
|
||||
$slots = [];
|
||||
$ex = explode(',', $sujet->sujet);
|
||||
$isDatePoll = strpos($sujet->sujet, '@');
|
||||
@ -265,14 +274,14 @@ VALUE (?,?,?)');
|
||||
$slots[] = $slot;
|
||||
} else { // Date poll
|
||||
$values = explode('@', $atomicSlot);
|
||||
if ($lastSlot == null || $lastSlot->title !== $values[0]) {
|
||||
if ($lastSlot === null || $lastSlot->title !== $values[0]) {
|
||||
$lastSlot = new \stdClass();
|
||||
$lastSlot->poll_id = $sujet->id_sondage;
|
||||
$lastSlot->title = $values[0];
|
||||
$lastSlot->moments = count($values) == 2 ? $values[1] : '-';
|
||||
$lastSlot->moments = count($values) === 2 ? $values[1] : '-';
|
||||
$slots[] = $lastSlot;
|
||||
} else {
|
||||
$lastSlot->moments .= ',' . (count($values) == 2 ? $values[1] : '-');
|
||||
$lastSlot->moments .= ',' . (count($values) === 2 ? $values[1] : '-');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -280,14 +289,16 @@ VALUE (?,?,?)');
|
||||
return $slots;
|
||||
}
|
||||
|
||||
private function dropOldTables(\PDO $pdo) {
|
||||
private function dropOldTables(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('DROP TABLE `comments`');
|
||||
$pdo->exec('DROP TABLE `sujet_studs`');
|
||||
$pdo->exec('DROP TABLE `user_studs`');
|
||||
$pdo->exec('DROP TABLE `sondage`');
|
||||
}
|
||||
|
||||
private function unescape($value) {
|
||||
private function unescape(string $value): string
|
||||
{
|
||||
return stripslashes(html_entity_decode($value, ENT_QUOTES));
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Security\Token;
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* This migration generate uniqId for all legacy votes.
|
||||
@ -28,17 +29,16 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class Generate_uniqId_for_old_votes implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'Generate "uniqId" in "vote" table for all legacy votes';
|
||||
}
|
||||
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
$tables = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// Check if tables of v0.9 are presents
|
||||
$diff = array_diff([Utils::table('poll'), Utils::table('slot'), Utils::table('vote'), Utils::table('comment')], $tables);
|
||||
@ -48,11 +48,10 @@ class Generate_uniqId_for_old_votes implements Migration {
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true is the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
|
||||
public function execute(PDO $pdo): bool {
|
||||
$pdo->beginTransaction();
|
||||
$this->generateUniqIdsForEmptyOnes($pdo);
|
||||
$pdo->commit();
|
||||
@ -60,7 +59,8 @@ class Generate_uniqId_for_old_votes implements Migration {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function generateUniqIdsForEmptyOnes($pdo) {
|
||||
private function generateUniqIdsForEmptyOnes(PDO $pdo): void
|
||||
{
|
||||
$select = $pdo->query('
|
||||
SELECT `id`
|
||||
FROM `' . Utils::table('vote') . '`
|
||||
@ -71,7 +71,7 @@ UPDATE `' . Utils::table('vote') . '`
|
||||
SET `uniqid` = :uniqid
|
||||
WHERE `id` = :id');
|
||||
|
||||
while ($row = $select->fetch(\PDO::FETCH_OBJ)) {
|
||||
while ($row = $select->fetch(PDO::FETCH_OBJ)) {
|
||||
$token = Token::getToken(16);
|
||||
$update->execute([
|
||||
'uniqid' => $token,
|
||||
@ -79,5 +79,4 @@ UPDATE `' . Utils::table('vote') . '`
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,10 +2,10 @@
|
||||
namespace Framadate\Migration;
|
||||
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
class Increase_pollId_size implements Migration {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -13,7 +13,7 @@ class Increase_pollId_size implements Migration {
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'Increase the size of id column in poll table';
|
||||
}
|
||||
|
||||
@ -21,45 +21,50 @@ class Increase_pollId_size implements Migration {
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true if the Migration should be executed
|
||||
*/
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(PDO $pdo): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true if the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo) {
|
||||
public function execute(PDO $pdo): bool {
|
||||
$this->alterCommentTable($pdo);
|
||||
$this->alterPollTable($pdo);
|
||||
$this->alterSlotTable($pdo);
|
||||
$this->alterVoteTable($pdo);
|
||||
return true;
|
||||
}
|
||||
|
||||
private function alterCommentTable(\PDO $pdo) {
|
||||
private function alterCommentTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('comment') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterPollTable(\PDO $pdo) {
|
||||
private function alterPollTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('poll') . '`
|
||||
CHANGE `id` `id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterSlotTable(\PDO $pdo) {
|
||||
private function alterSlotTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('slot') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
}
|
||||
|
||||
private function alterVoteTable(\PDO $pdo) {
|
||||
private function alterVoteTable(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('
|
||||
ALTER TABLE `' . Utils::table('vote') . '`
|
||||
CHANGE `poll_id` `poll_id` VARCHAR(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;');
|
||||
|
@ -18,31 +18,30 @@
|
||||
*/
|
||||
namespace Framadate\Migration;
|
||||
|
||||
interface Migration {
|
||||
use PDO;
|
||||
|
||||
interface Migration {
|
||||
/**
|
||||
* This method should describe in english what is the purpose of the migration class.
|
||||
*
|
||||
* @return string The description of the migration class
|
||||
*/
|
||||
function description();
|
||||
public function description(): string;
|
||||
|
||||
/**
|
||||
* This method could check if the execute method should be called.
|
||||
* It is called before the execute method.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true if the Migration should be executed
|
||||
*/
|
||||
function preCondition(\PDO $pdo);
|
||||
public function preCondition(PDO $pdo): bool;
|
||||
|
||||
/**
|
||||
* This methode is called only one time in the migration page.
|
||||
*
|
||||
* @param \PDO $pdo The connection to database
|
||||
* @param PDO $pdo The connection to database
|
||||
* @return bool true if the execution succeeded
|
||||
*/
|
||||
function execute(\PDO $pdo);
|
||||
|
||||
public function execute(PDO $pdo): bool;
|
||||
}
|
||||
|
@ -4,16 +4,16 @@
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphael DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Migration;
|
||||
@ -28,12 +28,11 @@ use Framadate\Utils;
|
||||
* @version 0.9
|
||||
*/
|
||||
class RPadVotes_from_0_8 implements Migration {
|
||||
|
||||
function description() {
|
||||
public function description(): string {
|
||||
return 'RPad votes from version 0.8.';
|
||||
}
|
||||
|
||||
function preCondition(\PDO $pdo) {
|
||||
public function preCondition(\PDO $pdo): bool {
|
||||
$stmt = $pdo->query('SHOW TABLES');
|
||||
$tables = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
||||
|
||||
@ -42,8 +41,7 @@ class RPadVotes_from_0_8 implements Migration {
|
||||
return count($diff) === 0;
|
||||
}
|
||||
|
||||
function execute(\PDO $pdo) {
|
||||
|
||||
public function execute(\PDO $pdo): bool {
|
||||
$pdo->beginTransaction();
|
||||
$this->rpadVotes($pdo);
|
||||
$pdo->commit();
|
||||
@ -51,14 +49,15 @@ class RPadVotes_from_0_8 implements Migration {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function rpadVotes($pdo) {
|
||||
$pdo->exec('UPDATE '. Utils::table('vote') .' fv
|
||||
private function rpadVotes(\PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('UPDATE ' . Utils::table('vote') . ' fv
|
||||
INNER JOIN (
|
||||
SELECT v.id, RPAD(v.choices, inn.slots_count, \'0\') new_choices
|
||||
FROM '. Utils::table('vote') .' v
|
||||
FROM ' . Utils::table('vote') . ' v
|
||||
INNER JOIN
|
||||
(SELECT s.poll_id, SUM(IFNULL(LENGTH(s.moments) - LENGTH(REPLACE(s.moments, \',\', \'\')) + 1, 1)) slots_count
|
||||
FROM '. Utils::table('slot') .' s
|
||||
FROM ' . Utils::table('slot') . ' s
|
||||
GROUP BY s.poll_id
|
||||
ORDER BY s.poll_id) inn ON inn.poll_id = v.poll_id
|
||||
WHERE LENGTH(v.choices) != inn.slots_count
|
||||
|
@ -4,7 +4,6 @@ namespace Framadate\Repositories;
|
||||
use Framadate\FramaDB;
|
||||
|
||||
abstract class AbstractRepository {
|
||||
|
||||
/**
|
||||
* @var FramaDB
|
||||
*/
|
||||
@ -14,32 +13,40 @@ abstract class AbstractRepository {
|
||||
* PollRepository constructor.
|
||||
* @param FramaDB $connect
|
||||
*/
|
||||
function __construct(FramaDB $connect) {
|
||||
public function __construct(FramaDB $connect) {
|
||||
$this->connect = $connect;
|
||||
}
|
||||
|
||||
public function beginTransaction() {
|
||||
public function beginTransaction(): void
|
||||
{
|
||||
$this->connect->beginTransaction();
|
||||
}
|
||||
|
||||
public function commit() {
|
||||
public function commit(): void
|
||||
{
|
||||
$this->connect->commit();
|
||||
}
|
||||
|
||||
function rollback() {
|
||||
public function rollback(): void
|
||||
{
|
||||
$this->connect->rollback();
|
||||
}
|
||||
|
||||
public function prepare($sql) {
|
||||
/**
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function prepare(string $sql) {
|
||||
return $this->connect->prepare($sql);
|
||||
}
|
||||
|
||||
function query($sql) {
|
||||
/**
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function query($sql) {
|
||||
return $this->connect->query($sql);
|
||||
}
|
||||
|
||||
function lastInsertId() {
|
||||
public function lastInsertId(): string {
|
||||
return $this->connect->lastInsertId();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
<?php
|
||||
namespace Framadate\Repositories;
|
||||
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
|
||||
class CommentRepository extends AbstractRepository {
|
||||
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
function findAllByPollId($poll_id) {
|
||||
/**
|
||||
* @return array|false
|
||||
*/
|
||||
public function findAllByPollId(string $poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('comment') . '` WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute(array($poll_id));
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
@ -20,18 +17,20 @@ class CommentRepository extends AbstractRepository {
|
||||
/**
|
||||
* Insert a new comment.
|
||||
*
|
||||
* @param $poll_id
|
||||
* @param $name
|
||||
* @param $comment
|
||||
* @param string $poll_id
|
||||
* @param string $name
|
||||
* @param string $comment
|
||||
* @return bool
|
||||
*/
|
||||
function insert($poll_id, $name, $comment) {
|
||||
public function insert(string $poll_id, string $name, string $comment): bool
|
||||
{
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('comment') . '` (poll_id, name, comment) VALUES (?,?,?)');
|
||||
|
||||
return $prepared->execute([$poll_id, $name, $comment]);
|
||||
}
|
||||
|
||||
function deleteById($poll_id, $comment_id) {
|
||||
public function deleteById(string $poll_id, int $comment_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id, $comment_id]);
|
||||
@ -40,20 +39,21 @@ class CommentRepository extends AbstractRepository {
|
||||
/**
|
||||
* Delete all comments of a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the given poll.
|
||||
* @param string $poll_id The ID of the given poll.
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function deleteByPollId($poll_id) {
|
||||
public function deleteByPollId(string $poll_id): ?bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('comment') . '` WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
|
||||
public function exists($poll_id, $name, $comment) {
|
||||
public function exists(string $poll_id, string $name, string $comment): bool
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('comment') . '` WHERE poll_id = ? AND name = ? AND comment = ?');
|
||||
$prepared->execute(array($poll_id, $name, $comment));
|
||||
$prepared->execute([$poll_id, $name, $comment]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,62 +6,62 @@ use Framadate\Utils;
|
||||
use PDO;
|
||||
|
||||
class PollRepository extends AbstractRepository {
|
||||
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
public function insertPoll($poll_id, $admin_poll_id, $form) {
|
||||
public function insertPoll(string $poll_id, string $admin_poll_id, $form): void
|
||||
{
|
||||
$sql = 'INSERT INTO `' . Utils::table('poll') . '`
|
||||
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible)
|
||||
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?)';
|
||||
(id, admin_id, title, description, admin_name, admin_mail, end_date, format, editable, receiveNewVotes, receiveNewComments, hidden, password_hash, results_publicly_visible,ValueMax)
|
||||
VALUES (?,?,?,?,?,?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?)';
|
||||
$prepared = $this->prepare($sql);
|
||||
$prepared->execute(array($poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, ($form->editable>=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));
|
||||
$prepared->execute([$poll_id, $admin_poll_id, $form->title, $form->description, $form->admin_name, $form->admin_mail, $form->end_date, $form->format, ($form->editable>=0 && $form->editable<=2) ? $form->editable : 0, $form->receiveNewVotes ? 1 : 0, $form->receiveNewComments ? 1 : 0, $form->hidden ? 1 : 0, $form->password_hash, $form->results_publicly_visible ? 1 : 0,$form->ValueMax]);
|
||||
}
|
||||
|
||||
function findById($poll_id) {
|
||||
public function findById(string $poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
$prepared->execute(array($poll_id));
|
||||
$prepared->execute([$poll_id]);
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
public function findByAdminId(string $admin_poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
|
||||
|
||||
$prepared->execute(array($admin_poll_id));
|
||||
$prepared->execute([$admin_poll_id]);
|
||||
$poll = $prepared->fetch();
|
||||
$prepared->closeCursor();
|
||||
|
||||
return $poll;
|
||||
}
|
||||
|
||||
public function existsById($poll_id) {
|
||||
public function existsById(string $poll_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
$prepared->execute(array($poll_id));
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
public function existsByAdminId($admin_poll_id) {
|
||||
public function existsByAdminId(string $admin_poll_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('poll') . '` WHERE admin_id = ?');
|
||||
|
||||
$prepared->execute(array($admin_poll_id));
|
||||
$prepared->execute([$admin_poll_id]);
|
||||
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
function update($poll) {
|
||||
public function update($poll): bool
|
||||
{
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('poll') . '` SET title=?, admin_name=?, admin_mail=?, description=?, end_date=?, active=?, editable=?, hidden=?, password_hash=?, results_publicly_visible=? WHERE id = ?');
|
||||
|
||||
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]);
|
||||
return $prepared->execute([$poll->title, $poll->admin_name, $poll->admin_mail, $poll->description, $poll->end_date, $poll->active ? 1 : 0, ($poll->editable>=0 && $poll->editable<=2) ? $poll->editable : 0, $poll->hidden ? 1 : 0, $poll->password_hash, $poll->results_publicly_visible ? 1 : 0, $poll->id]);
|
||||
}
|
||||
|
||||
function deleteById($poll_id) {
|
||||
public function deleteById($poll_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('poll') . '` WHERE id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
@ -72,7 +72,8 @@ class PollRepository extends AbstractRepository {
|
||||
*
|
||||
* @return array Array of old polls
|
||||
*/
|
||||
public function findOldPolls() {
|
||||
public function findOldPolls(): array
|
||||
{
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE DATE_ADD(`end_date`, INTERVAL ' . PURGE_DELAY . ' DAY) < NOW() AND `end_date` != 0 LIMIT 20');
|
||||
$prepared->execute([]);
|
||||
|
||||
@ -80,37 +81,57 @@ class PollRepository extends AbstractRepository {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search polls in databse.
|
||||
* Search polls in database.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>..., 'mail'=>...]
|
||||
* @param int $start The number of first entry to select
|
||||
* @param int $limit The number of entries to find
|
||||
* @return array The found polls
|
||||
*/
|
||||
public function findAll($search, $start, $limit) {
|
||||
public function findAll(array $search, int $start, int $limit): array
|
||||
{
|
||||
// Polls
|
||||
$prepared = $this->prepare('
|
||||
SELECT p.*,
|
||||
(SELECT count(1) FROM `' . Utils::table('vote') . '` v WHERE p.id=v.poll_id) votes
|
||||
FROM `' . Utils::table('poll') . '` p
|
||||
WHERE (:id = "" OR p.id LIKE :id)
|
||||
AND (:title = "" OR p.title LIKE :title)
|
||||
AND (:name = "" OR p.admin_name LIKE :name)
|
||||
AND (:mail = "" OR p.admin_mail LIKE :mail)
|
||||
ORDER BY p.title ASC
|
||||
LIMIT :start, :limit
|
||||
');
|
||||
|
||||
$poll = $search['poll'] . '%';
|
||||
$title = '%' . $search['title'] . '%';
|
||||
$name = '%' . $search['name'] . '%';
|
||||
$mail = '%' . $search['mail'] . '%';
|
||||
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':mail', $mail, PDO::PARAM_STR);
|
||||
$request = "SELECT p.*,";
|
||||
$request .= " (SELECT count(1) FROM `" . Utils::table('vote') . "` v WHERE p.id=v.poll_id) votes";
|
||||
$request .= " FROM `" . Utils::table('poll') . "` p";
|
||||
$request .= " WHERE 1";
|
||||
|
||||
$values = [];
|
||||
|
||||
if (!empty($search["poll"])) {
|
||||
$request .= " AND p.id LIKE :poll";
|
||||
$values["poll"] = "{$search["poll"]}%";
|
||||
}
|
||||
|
||||
$fields = [
|
||||
// key of $search => column name
|
||||
"title" => "title",
|
||||
"name" => "admin_name",
|
||||
"mail" => "admin_mail",
|
||||
];
|
||||
|
||||
foreach ($fields as $searchKey => $columnName) {
|
||||
if (empty($search[$searchKey])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$request .= " AND p.$columnName LIKE :$searchKey";
|
||||
$values[$searchKey] = "%$search[$searchKey]%";
|
||||
}
|
||||
|
||||
$request .= " ORDER BY p.title ASC";
|
||||
$request .= " LIMIT :start, :limit";
|
||||
|
||||
$prepared = $this->prepare($request);
|
||||
|
||||
foreach ($values as $searchKey => $value) {
|
||||
$prepared->bindParam(":$searchKey", $value, PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$prepared->bindParam(':start', $start, PDO::PARAM_INT);
|
||||
$prepared->bindParam(':limit', $limit, PDO::PARAM_INT);
|
||||
|
||||
$prepared->execute();
|
||||
|
||||
return $prepared->fetchAll();
|
||||
@ -122,20 +143,22 @@ SELECT p.*,
|
||||
* @param string $mail Email address of the poll admin
|
||||
* @return array The list of matching polls
|
||||
*/
|
||||
public function findAllByAdminMail($mail) {
|
||||
public function findAllByAdminMail(string $mail): array
|
||||
{
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('poll') . '` WHERE admin_mail = :admin_mail');
|
||||
$prepared->execute(array('admin_mail' => $mail));
|
||||
$prepared->execute(['admin_mail' => $mail]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of polls in databse.
|
||||
* Get the total number of polls in database.
|
||||
*
|
||||
* @param array $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
|
||||
* @param array|null $search Array of search : ['id'=>..., 'title'=>..., 'name'=>...]
|
||||
* @return int The number of polls
|
||||
*/
|
||||
public function count($search = null) {
|
||||
public function count(array $search = null): int
|
||||
{
|
||||
// Total count
|
||||
$prepared = $this->prepare('
|
||||
SELECT count(1) nb
|
||||
@ -145,22 +168,15 @@ SELECT count(1) nb
|
||||
AND (:name = "" OR p.admin_name LIKE :name)
|
||||
ORDER BY p.title ASC');
|
||||
|
||||
$poll = $search == null ? '' : $search['poll'] . '%';
|
||||
$title = $search == null ? '' : '%' . $search['title'] . '%';
|
||||
$name = $search == null ? '' : '%' . $search['name'] . '%';
|
||||
$poll = $search === null ? '' : $search['poll'] . '%';
|
||||
$title = $search === null ? '' : '%' . $search['title'] . '%';
|
||||
$name = $search === null ? '' : '%' . $search['name'] . '%';
|
||||
$prepared->bindParam(':id', $poll, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':title', $title, PDO::PARAM_STR);
|
||||
$prepared->bindParam(':name', $name, PDO::PARAM_STR);
|
||||
|
||||
$prepared->execute();
|
||||
$count = $prepared->fetch();
|
||||
|
||||
/*echo '---';
|
||||
print_r($count);
|
||||
echo '---';
|
||||
exit;*/
|
||||
|
||||
return $count->nb;
|
||||
return $prepared->fetch()->nb;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,16 +4,16 @@
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Repositories;
|
||||
@ -21,7 +21,6 @@ namespace Framadate\Repositories;
|
||||
use Framadate\FramaDB;
|
||||
|
||||
class RepositoryFactory {
|
||||
|
||||
private static $connect;
|
||||
|
||||
private static $pollRepository;
|
||||
@ -32,15 +31,16 @@ class RepositoryFactory {
|
||||
/**
|
||||
* @param FramaDB $connect
|
||||
*/
|
||||
static function init(FramaDB $connect) {
|
||||
public static function init(FramaDB $connect): void {
|
||||
self::$connect = $connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PollRepository The singleton of PollRepository
|
||||
*/
|
||||
static function pollRepository() {
|
||||
if (self::$pollRepository == null) {
|
||||
public static function pollRepository(): PollRepository
|
||||
{
|
||||
if (self::$pollRepository === null) {
|
||||
self::$pollRepository = new PollRepository(self::$connect);
|
||||
}
|
||||
|
||||
@ -50,8 +50,9 @@ class RepositoryFactory {
|
||||
/**
|
||||
* @return SlotRepository The singleton of SlotRepository
|
||||
*/
|
||||
static function slotRepository() {
|
||||
if (self::$slotRepository == null) {
|
||||
public static function slotRepository(): SlotRepository
|
||||
{
|
||||
if (self::$slotRepository === null) {
|
||||
self::$slotRepository = new SlotRepository(self::$connect);
|
||||
}
|
||||
|
||||
@ -61,8 +62,9 @@ class RepositoryFactory {
|
||||
/**
|
||||
* @return VoteRepository The singleton of VoteRepository
|
||||
*/
|
||||
static function voteRepository() {
|
||||
if (self::$voteRepository == null) {
|
||||
public static function voteRepository(): VoteRepository
|
||||
{
|
||||
if (self::$voteRepository === null) {
|
||||
self::$voteRepository = new VoteRepository(self::$connect);
|
||||
}
|
||||
|
||||
@ -72,12 +74,12 @@ class RepositoryFactory {
|
||||
/**
|
||||
* @return CommentRepository The singleton of CommentRepository
|
||||
*/
|
||||
static function commentRepository() {
|
||||
if (self::$commentRepository == null) {
|
||||
public static function commentRepository(): CommentRepository
|
||||
{
|
||||
if (self::$commentRepository === null) {
|
||||
self::$commentRepository = new CommentRepository(self::$connect);
|
||||
}
|
||||
|
||||
return self::$commentRepository;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,16 +4,16 @@
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphael DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphael DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Repositories;
|
||||
@ -22,22 +22,17 @@ use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
|
||||
class SlotRepository extends AbstractRepository {
|
||||
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a bulk of slots.
|
||||
*
|
||||
* @param int $poll_id
|
||||
* @param string $poll_id
|
||||
* @param array $choices
|
||||
*/
|
||||
public function insertSlots($poll_id, $choices) {
|
||||
public function insertSlots(string $poll_id, array $choices): void
|
||||
{
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?, ?, ?)');
|
||||
|
||||
foreach ($choices as $choice) {
|
||||
|
||||
// We prepared the slots (joined by comas)
|
||||
$joinedSlots = '';
|
||||
$first = true;
|
||||
@ -52,17 +47,19 @@ class SlotRepository extends AbstractRepository {
|
||||
|
||||
// We execute the insertion
|
||||
if (empty($joinedSlots)) {
|
||||
$prepared->execute(array($poll_id, $choice->getName(), null));
|
||||
$prepared->execute([$poll_id, $choice->getName(), null]);
|
||||
} else {
|
||||
$prepared->execute(array($poll_id, $choice->getName(), $joinedSlots));
|
||||
$prepared->execute([$poll_id, $choice->getName(), $joinedSlots]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function listByPollId($poll_id) {
|
||||
/**
|
||||
* @return array|false
|
||||
*/
|
||||
public function listByPollId(string $poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute(array($poll_id));
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
@ -70,11 +67,11 @@ class SlotRepository extends AbstractRepository {
|
||||
/**
|
||||
* Find the slot into poll for a given datetime.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $datetime int The datetime of the slot
|
||||
* @return mixed Object The slot found, or null
|
||||
*/
|
||||
function findByPollIdAndDatetime($poll_id, $datetime) {
|
||||
public function findByPollIdAndDatetime(string $poll_id, $datetime) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND SUBSTRING_INDEX(title, \'@\', 1) = ?');
|
||||
|
||||
$prepared->execute([$poll_id, $datetime]);
|
||||
@ -87,12 +84,13 @@ class SlotRepository extends AbstractRepository {
|
||||
/**
|
||||
* Insert a new slot into a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $title mixed The title of the slot
|
||||
* @param $moments mixed|null The moments joined with ","
|
||||
* @return bool true if action succeeded
|
||||
*/
|
||||
function insert($poll_id, $title, $moments) {
|
||||
public function insert(string $poll_id, string $title, ?string $moments): bool
|
||||
{
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('slot') . '` (poll_id, title, moments) VALUES (?,?,?)');
|
||||
|
||||
return $prepared->execute([$poll_id, $title, $moments]);
|
||||
@ -101,12 +99,13 @@ class SlotRepository extends AbstractRepository {
|
||||
/**
|
||||
* Update a slot into a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $datetime int The datetime of the slot to update
|
||||
* @param $newMoments mixed The new moments
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function update($poll_id, $datetime, $newMoments) {
|
||||
public function update(string $poll_id, $datetime, $newMoments): ?bool
|
||||
{
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('slot') . '` SET moments = ? WHERE poll_id = ? AND title = ?');
|
||||
|
||||
return $prepared->execute([$newMoments, $poll_id, $datetime]);
|
||||
@ -115,18 +114,19 @@ class SlotRepository extends AbstractRepository {
|
||||
/**
|
||||
* Delete a entire slot from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id int The ID of the poll
|
||||
* @param $datetime mixed The datetime of the slot
|
||||
*/
|
||||
function deleteByDateTime($poll_id, $datetime) {
|
||||
public function deleteByDateTime(string $poll_id, $datetime): void
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ? AND title = ?');
|
||||
$prepared->execute([$poll_id, $datetime]);
|
||||
}
|
||||
|
||||
function deleteByPollId($poll_id) {
|
||||
public function deleteByPollId(string $poll_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('slot') . '` WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,25 +5,24 @@ use Framadate\FramaDB;
|
||||
use Framadate\Utils;
|
||||
|
||||
class VoteRepository extends AbstractRepository {
|
||||
|
||||
function __construct(FramaDB $connect) {
|
||||
parent::__construct($connect);
|
||||
}
|
||||
|
||||
function allUserVotesByPollId($poll_id) {
|
||||
/**
|
||||
* @return array|false
|
||||
*/
|
||||
public function allUserVotesByPollId(string $poll_id) {
|
||||
$prepared = $this->prepare('SELECT * FROM `' . Utils::table('vote') . '` WHERE poll_id = ? ORDER BY id');
|
||||
$prepared->execute(array($poll_id));
|
||||
$prepared->execute([$poll_id]);
|
||||
|
||||
return $prepared->fetchAll();
|
||||
}
|
||||
|
||||
function insertDefault($poll_id, $insert_position) {
|
||||
public function insertDefault(string $poll_id, int $insert_position): bool
|
||||
{
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTRING(choices, 1, ?), " ", SUBSTRING(choices, ?)) WHERE poll_id = ?'); //#51 : default value for unselected vote
|
||||
|
||||
return $prepared->execute([$insert_position, $insert_position + 1, $poll_id]);
|
||||
}
|
||||
|
||||
function insert($poll_id, $name, $choices, $token) {
|
||||
public function insert(string $poll_id, string $name, string $choices, string $token): \stdClass {
|
||||
$prepared = $this->prepare('INSERT INTO `' . Utils::table('vote') . '` (poll_id, name, choices, uniqId) VALUES (?,?,?,?)');
|
||||
$prepared->execute([$poll_id, $name, $choices, $token]);
|
||||
|
||||
@ -37,7 +36,8 @@ class VoteRepository extends AbstractRepository {
|
||||
return $newVote;
|
||||
}
|
||||
|
||||
function deleteById($poll_id, $vote_id) {
|
||||
public function deleteById(string $poll_id, int $vote_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id, $vote_id]);
|
||||
@ -46,10 +46,11 @@ class VoteRepository extends AbstractRepository {
|
||||
/**
|
||||
* Delete all votes of a given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the given poll.
|
||||
* @param string $poll_id The ID of the given poll.
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function deleteByPollId($poll_id) {
|
||||
public function deleteByPollId(string $poll_id): ?bool
|
||||
{
|
||||
$prepared = $this->prepare('DELETE FROM `' . Utils::table('vote') . '` WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$poll_id]);
|
||||
@ -58,17 +59,19 @@ class VoteRepository extends AbstractRepository {
|
||||
/**
|
||||
* Delete all votes made on given moment index.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $index int The index of the vote into the poll
|
||||
* @return bool|null true if action succeeded.
|
||||
*/
|
||||
function deleteByIndex($poll_id, $index) {
|
||||
public function deleteByIndex(string $poll_id, int $index): ?bool
|
||||
{
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = CONCAT(SUBSTR(choices, 1, ?), SUBSTR(choices, ?)) WHERE poll_id = ?');
|
||||
|
||||
return $prepared->execute([$index, $index + 2, $poll_id]);
|
||||
}
|
||||
|
||||
function update($poll_id, $vote_id, $name, $choices) {
|
||||
public function update(string $poll_id, string $vote_id, string $name, $choices): bool
|
||||
{
|
||||
$prepared = $this->prepare('UPDATE `' . Utils::table('vote') . '` SET choices = ?, name = ? WHERE poll_id = ? AND id = ?');
|
||||
|
||||
return $prepared->execute([$choices, $name, $poll_id, $vote_id]);
|
||||
@ -77,14 +80,30 @@ class VoteRepository extends AbstractRepository {
|
||||
/**
|
||||
* Check if name is already used for the given poll.
|
||||
*
|
||||
* @param int $poll_id ID of the poll
|
||||
* @param string $poll_id ID of the poll
|
||||
* @param string $name Name of the vote
|
||||
* @return bool true if vote already exists
|
||||
*/
|
||||
public function existsByPollIdAndName($poll_id, $name) {
|
||||
public function existsByPollIdAndName(string $poll_id, string $name): bool
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ?');
|
||||
$prepared->execute(array($poll_id, $name));
|
||||
$prepared->execute([$poll_id, $name]);
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if name is already used for the given poll and another vote.
|
||||
*
|
||||
* @param string $poll_id ID of the poll
|
||||
* @param string $name Name of the vote
|
||||
* @param int $vote_id ID of the current vote
|
||||
* @return bool true if vote already exists
|
||||
*/
|
||||
public function existsByPollIdAndNameAndVoteId(string $poll_id, string $name, int $vote_id): bool
|
||||
{
|
||||
$prepared = $this->prepare('SELECT 1 FROM `' . Utils::table('vote') . '` WHERE poll_id = ? AND name = ? AND id != ?');
|
||||
$prepared->execute([$poll_id, $name, $vote_id]);
|
||||
return $prepared->rowCount() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Security;
|
||||
|
||||
/**
|
||||
@ -11,14 +10,13 @@ namespace Framadate\Security;
|
||||
* @package Framadate\Security
|
||||
*/
|
||||
class PasswordHasher {
|
||||
|
||||
/**
|
||||
* Hash a password
|
||||
*
|
||||
* @param string $password the password to hash.
|
||||
* @return false|string the hashed password, or false on failure. The used algorithm, cost and salt are returned as part of the hash.
|
||||
*/
|
||||
public static function hash($password) {
|
||||
public static function hash(string $password) {
|
||||
return password_hash($password, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
@ -29,7 +27,8 @@ class PasswordHasher {
|
||||
* @param string $hash the hash to compare.
|
||||
* @return bool
|
||||
*/
|
||||
public static function verify($password, $hash) {
|
||||
public static function verify(string $password, string $hash): bool
|
||||
{
|
||||
return password_verify($password, $hash);
|
||||
}
|
||||
}
|
@ -2,36 +2,35 @@
|
||||
namespace Framadate\Security;
|
||||
|
||||
class Token {
|
||||
|
||||
const DEFAULT_LENGTH = 64;
|
||||
public const DEFAULT_LENGTH = 64;
|
||||
private $time;
|
||||
private $value;
|
||||
private $length;
|
||||
private static $codeAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
|
||||
|
||||
function __construct($length = self::DEFAULT_LENGTH) {
|
||||
public function __construct($length = self::DEFAULT_LENGTH) {
|
||||
$this->length = $length;
|
||||
$this->time = time() + TOKEN_TIME;
|
||||
$this->value = $this->generate();
|
||||
}
|
||||
|
||||
private function generate() {
|
||||
return self::getToken($this->length);
|
||||
}
|
||||
|
||||
public function getTime() {
|
||||
public function getTime(): int
|
||||
{
|
||||
return $this->time;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
public function getValue(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function isGone() {
|
||||
public function isGone(): bool
|
||||
{
|
||||
return $this->time < time();
|
||||
}
|
||||
|
||||
public function check($value) {
|
||||
public function check($value): bool
|
||||
{
|
||||
return $value === $this->value;
|
||||
}
|
||||
|
||||
@ -42,7 +41,8 @@ class Token {
|
||||
* @param bool $crypto_strong If passed, tells if the token is "cryptographically strong" or not.
|
||||
* @return string
|
||||
*/
|
||||
public static function getToken($length = self::DEFAULT_LENGTH, &$crypto_strong = false) {
|
||||
public static function getToken(int $length = self::DEFAULT_LENGTH, bool &$crypto_strong = false): string
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
openssl_random_pseudo_bytes(1, $crypto_strong); // Fake use to see if the algorithm used was "cryptographically strong"
|
||||
return self::getSecureToken($length);
|
||||
@ -50,7 +50,8 @@ class Token {
|
||||
return self::getUnsecureToken($length);
|
||||
}
|
||||
|
||||
public static function getUnsecureToken($length) {
|
||||
public static function getUnsecureToken(int $length): string
|
||||
{
|
||||
$string = '';
|
||||
mt_srand();
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
@ -63,7 +64,8 @@ class Token {
|
||||
/**
|
||||
* @author http://stackoverflow.com/a/13733588
|
||||
*/
|
||||
public static function getSecureToken($length){
|
||||
public static function getSecureToken(int $length): string
|
||||
{
|
||||
$token = "";
|
||||
for($i=0;$i<$length;$i++){
|
||||
$token .= self::$codeAlphabet[self::crypto_rand_secure(0,strlen(self::$codeAlphabet))];
|
||||
@ -71,22 +73,33 @@ class Token {
|
||||
return $token;
|
||||
}
|
||||
|
||||
private function generate(): string
|
||||
{
|
||||
return self::getToken($this->length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322
|
||||
*
|
||||
* @param int $max
|
||||
*
|
||||
* @psalm-param 0 $min
|
||||
* @psalm-param 0|positive-int $max
|
||||
*/
|
||||
private static function crypto_rand_secure($min, $max) {
|
||||
private static function crypto_rand_secure(int $min, $max): int {
|
||||
$range = $max - $min;
|
||||
if ($range < 0) return $min; // not so random...
|
||||
// not so random...
|
||||
if ($range < 0) {
|
||||
return $min;
|
||||
}
|
||||
$log = log($range, 2);
|
||||
$bytes = (int) ($log / 8) + 1; // length in bytes
|
||||
$bits = (int) $log + 1; // length in bits
|
||||
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
|
||||
do {
|
||||
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
|
||||
$rnd = $rnd & $filter; // discard irrelevant bits
|
||||
$rnd &= $filter; // discard irrelevant bits
|
||||
} while ($rnd >= $range);
|
||||
return $min + $rnd;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ use Framadate\Utils;
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class AdminPollService {
|
||||
|
||||
private $connect;
|
||||
private $pollService;
|
||||
private $logService;
|
||||
@ -22,7 +21,7 @@ class AdminPollService {
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
|
||||
public function __construct(FramaDB $connect, PollService $pollService, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
$this->pollService = $pollService;
|
||||
$this->logService = $logService;
|
||||
@ -32,33 +31,38 @@ class AdminPollService {
|
||||
$this->commentRepository = RepositoryFactory::commentRepository();
|
||||
}
|
||||
|
||||
function updatePoll($poll) {
|
||||
public function updatePoll($poll): bool
|
||||
{
|
||||
global $config;
|
||||
if ($poll->end_date > $poll->creation_date) {
|
||||
return $this->pollRepository->update($poll);
|
||||
} else {
|
||||
return false;
|
||||
|
||||
if ($poll->end_date < $poll->creation_date) {
|
||||
$poll->end_date = $poll->creation_date;
|
||||
} elseif ($poll->end_date > $this->pollService->maxExpiryDate()) {
|
||||
$poll->end_date = $this->pollService->maxExpiryDate();
|
||||
}
|
||||
|
||||
return $this->pollRepository->update($poll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a comment from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $comment_id int The ID of the comment
|
||||
* @return mixed true is action succeeded
|
||||
*/
|
||||
function deleteComment($poll_id, $comment_id) {
|
||||
public function deleteComment(string $poll_id, int $comment_id) {
|
||||
return $this->commentRepository->deleteById($poll_id, $comment_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all comments of a poll.
|
||||
*
|
||||
* @param $poll_id int The ID a the poll
|
||||
* @param string $poll_id The ID a the poll
|
||||
* @return bool|null true is action succeeded
|
||||
*/
|
||||
function cleanComments($poll_id) {
|
||||
public function cleanComments(string $poll_id): ?bool
|
||||
{
|
||||
$this->logService->log("CLEAN_COMMENTS", "id:$poll_id");
|
||||
return $this->commentRepository->deleteByPollId($poll_id);
|
||||
}
|
||||
@ -66,21 +70,23 @@ class AdminPollService {
|
||||
/**
|
||||
* Delete a vote from a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $vote_id int The ID of the vote
|
||||
* @return mixed true is action succeeded
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function deleteVote($poll_id, $vote_id) {
|
||||
public function deleteVote(string $poll_id, int $vote_id): bool
|
||||
{
|
||||
return $this->voteRepository->deleteById($poll_id, $vote_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all votes of a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @return bool|null true is action succeeded
|
||||
*/
|
||||
function cleanVotes($poll_id) {
|
||||
public function cleanVotes(string $poll_id): ?bool
|
||||
{
|
||||
$this->logService->log('CLEAN_VOTES', 'id:' . $poll_id);
|
||||
return $this->voteRepository->deleteByPollId($poll_id);
|
||||
}
|
||||
@ -88,10 +94,11 @@ class AdminPollService {
|
||||
/**
|
||||
* Delete the entire given poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $poll_id string The ID of the poll
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function deleteEntirePoll($poll_id) {
|
||||
public function deleteEntirePoll(string $poll_id): bool
|
||||
{
|
||||
$poll = $this->pollRepository->findById($poll_id);
|
||||
$this->logService->log('DELETE_POLL', "id:$poll->id, format:$poll->format, admin:$poll->admin_name, mail:$poll->admin_mail");
|
||||
|
||||
@ -111,7 +118,8 @@ class AdminPollService {
|
||||
* @param object $slot The slot informations (datetime + moment)
|
||||
* @return bool true if action succeeded
|
||||
*/
|
||||
public function deleteDateSlot($poll, $slot) {
|
||||
public function deleteDateSlot(object $poll, object $slot): bool
|
||||
{
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . json_encode($slot));
|
||||
|
||||
$datetime = $slot->title;
|
||||
@ -120,9 +128,11 @@ class AdminPollService {
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
|
||||
// We can't delete the last slot
|
||||
if ($poll->format == 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
|
||||
if ($poll->format === 'D' && count($slots) === 1 && strpos($slots[0]->moments, ',') === false) {
|
||||
return false;
|
||||
} elseif ($poll->format == 'A' && count($slots) === 1) {
|
||||
}
|
||||
|
||||
if ($poll->format === 'A' && count($slots) === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -135,8 +145,8 @@ class AdminPollService {
|
||||
$moments = explode(',', $aSlot->moments);
|
||||
|
||||
foreach ($moments as $rowMoment) {
|
||||
if ($datetime == $aSlot->title) {
|
||||
if ($moment == $rowMoment) {
|
||||
if ($datetime === $aSlot->title) {
|
||||
if ($moment === $rowMoment) {
|
||||
$indexToDelete = $index;
|
||||
} else {
|
||||
$newMoments[] = $rowMoment;
|
||||
@ -159,7 +169,8 @@ class AdminPollService {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deleteClassicSlot($poll, $slot_title) {
|
||||
public function deleteClassicSlot($poll, string $slot_title): bool
|
||||
{
|
||||
$this->logService->log('DELETE_SLOT', 'id:' . $poll->id . ', slot:' . $slot_title);
|
||||
|
||||
$slots = $this->pollService->allSlotsByPoll($poll);
|
||||
@ -173,7 +184,7 @@ class AdminPollService {
|
||||
|
||||
// Search the index of the slot to delete
|
||||
foreach ($slots as $aSlot) {
|
||||
if ($slot_title == $aSlot->title) {
|
||||
if ($slot_title === $aSlot->title) {
|
||||
$indexToDelete = $index;
|
||||
}
|
||||
$index++;
|
||||
@ -195,12 +206,13 @@ class AdminPollService {
|
||||
* <li>Create a new moment if a slot already exists for the given date</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param $datetime int The datetime
|
||||
* @param $new_moment string The moment's name
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
*/
|
||||
public function addDateSlot($poll_id, $datetime, $new_moment) {
|
||||
public function addDateSlot(string $poll_id, int $datetime, string $new_moment): void
|
||||
{
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', datetime:' . $datetime . ', moment:' . $new_moment);
|
||||
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
@ -209,19 +221,18 @@ class AdminPollService {
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
if ($result->slot != null) {
|
||||
if ($result->slot !== null) {
|
||||
$slot = $result->slot;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
// Check if moment already exists (maybe not necessary)
|
||||
if (in_array($new_moment, $moments)) {
|
||||
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);
|
||||
}
|
||||
@ -230,7 +241,6 @@ class AdminPollService {
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -239,25 +249,25 @@ class AdminPollService {
|
||||
* <li>Create a new slot if no one exists for the given title</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param $title int The title
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @param string $title The title
|
||||
* @throws MomentAlreadyExistsException When the moment to add already exists in database
|
||||
*/
|
||||
public function addClassicSlot($poll_id, $title) {
|
||||
public function addClassicSlot(string $poll_id, string $title): void
|
||||
{
|
||||
$this->logService->log('ADD_COLUMN', 'id:' . $poll_id . ', title:' . $title);
|
||||
|
||||
$slots = $this->slotRepository->listByPollId($poll_id);
|
||||
|
||||
// Check if slot already exists
|
||||
$titles = array_map(function ($slot) {
|
||||
$titles = array_map(static function ($slot) {
|
||||
return $slot->title;
|
||||
}, $slots);
|
||||
if (in_array($title, $titles)) {
|
||||
if (in_array($title, $titles, true)) {
|
||||
// The moment already exists
|
||||
throw new MomentAlreadyExistsException();
|
||||
}
|
||||
|
||||
|
||||
// Begin transaction
|
||||
$this->connect->beginTransaction();
|
||||
|
||||
@ -268,7 +278,6 @@ class AdminPollService {
|
||||
|
||||
// Commit transaction
|
||||
$this->connect->commit();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -280,7 +289,7 @@ class AdminPollService {
|
||||
* @param $datetime int The datetime of the new slot
|
||||
* @return \stdClass An object like this one: {insert:X, slot:Y} where Y can be null.
|
||||
*/
|
||||
private function findInsertPosition($slots, $datetime) {
|
||||
private function findInsertPosition(array $slots, int $datetime) {
|
||||
$result = new \stdClass();
|
||||
$result->slot = null;
|
||||
$result->insert = 0;
|
||||
@ -290,24 +299,23 @@ class AdminPollService {
|
||||
|
||||
// Search where to insert new column
|
||||
foreach ($slots as $k=>$slot) {
|
||||
$rowDatetime = $slot->title;
|
||||
$rowDatetime = (int) $slot->title;
|
||||
$moments = explode(',', $slot->moments);
|
||||
|
||||
if ($datetime == $rowDatetime) {
|
||||
if ($datetime === $rowDatetime) {
|
||||
// Here we have to insert at the end of a slot
|
||||
$result->insert += count($moments);
|
||||
$result->slot = $slot;
|
||||
break;
|
||||
} elseif ($datetime < $rowDatetime) {
|
||||
}
|
||||
|
||||
if ($datetime < $rowDatetime) {
|
||||
// We have to insert before this slot
|
||||
break;
|
||||
} else {
|
||||
$result->insert += count($moments);
|
||||
}
|
||||
$result->insert += count($moments);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
182
app/classes/Framadate/Services/ICalService.php
Normal file
182
app/classes/Framadate/Services/ICalService.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
/**
|
||||
* This software is governed by the CeCILL-B license. If a copy of this license
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
|
||||
use DateTime;
|
||||
use Framadate\Utils;
|
||||
use Sabre\VObject;
|
||||
|
||||
class ICalService {
|
||||
/**
|
||||
* Creates an ical-File and initiates the download. If possible, the provided time is used, else an all day event is created.
|
||||
*/
|
||||
public function getEvent($poll, string $start_day, string $start_time): void
|
||||
{
|
||||
if(!$this->dayIsReadable($start_day)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ical_text = "";
|
||||
$elements = explode("-", $start_time);
|
||||
$end_time = null;
|
||||
if(count($elements) === 2) {
|
||||
$start_time = trim($elements[0]);
|
||||
$end_time = trim($elements[1]);
|
||||
}
|
||||
$start_time = $this->reviseTimeString($start_time);
|
||||
if($end_time !== null) {
|
||||
$end_time = $this->reviseTimeString($end_time);
|
||||
}
|
||||
if($start_time !== null) {
|
||||
if($end_time !== null) {
|
||||
$ical_text = $this->getTimedEvent($poll, $start_day . " " . $start_time, $start_day . " " . $end_time);
|
||||
} else {
|
||||
$ical_text = $this->getTimedEvent1Hour($poll, $start_day . " " . $start_time);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$date = DateTime::createFromFormat('d-m-Y', $start_day);
|
||||
$day = $date->format('Ymd');
|
||||
$ical_text = $this->getAllDayEvent($poll, $day);
|
||||
}
|
||||
$this->provideFile($poll->title, $ical_text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls getTimedEvent with one hour as a time slot, starting at $start_daytime
|
||||
*/
|
||||
public function getTimedEvent1Hour($poll, string $start_daytime): string
|
||||
{
|
||||
$end_daytime = date(DATE_ATOM, strtotime('+1 hours', strtotime($start_daytime)));
|
||||
return $this->getTimedEvent($poll, $start_daytime, $end_daytime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for an ical event including the time
|
||||
*/
|
||||
public function getTimedEvent($poll, string $start_daytime, string $end_daytime): string
|
||||
{
|
||||
$vcalendar = new VObject\Component\VCalendar([
|
||||
'VEVENT' => [
|
||||
'SUMMARY' => $poll->title,
|
||||
'DESCRIPTION' => $this->stripMD($poll->description),
|
||||
'DTSTART' => new DateTime($start_daytime),
|
||||
'DTEND' => new DateTime($end_daytime)
|
||||
],
|
||||
'PRODID' => ICAL_PRODID
|
||||
]);
|
||||
return $vcalendar->serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the text for an ical event if the time is not known
|
||||
*/
|
||||
public function getAllDayEvent($poll, string $day): string
|
||||
{
|
||||
$vcalendar = new VObject\Component\VCalendar();
|
||||
$vevent = $vcalendar->add('VEVENT');
|
||||
$vevent->add('SUMMARY', $poll->title);
|
||||
$vevent->add('DESCRIPTION', $this->stripMD($poll->description));
|
||||
$dtstart = $vevent->add('DTSTART', $day);
|
||||
$dtstart['VALUE'] = 'DATE';
|
||||
unset($vcalendar->PRODID);
|
||||
$vcalendar->add('PRODID', ICAL_PRODID);
|
||||
return $vcalendar->serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file and initiates the download
|
||||
* @param string $title
|
||||
* @param string $ical_text
|
||||
*/
|
||||
public function provideFile(string $title, string $ical_text): void
|
||||
{
|
||||
header('Content-Description: File Transfer');
|
||||
header('Content-Disposition: attachment; filename=' . $this->stripTitle($title) . ICAL_ENDING);
|
||||
header('Expires: 0');
|
||||
header('Cache-Control: must-revalidate');
|
||||
header('Pragma: public');
|
||||
header("Content-Type: text/calendar");
|
||||
echo $ical_text;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reformats a string value into a time readable by DateTime
|
||||
* @param string $time
|
||||
* @return string the corrected value, null if the format is unknown
|
||||
*/
|
||||
public function reviseTimeString(string $time): ?string
|
||||
{
|
||||
// 24-hour clock / international format
|
||||
if (preg_match('/^\d\d(:)\d\d$/', $time)) {
|
||||
return $time;
|
||||
}
|
||||
// 12-hour clock / using am and pm
|
||||
|
||||
if (preg_match('/^\d[0-2]?:?\d{0,2}\s?[aApP][mM]$/', $time)) {
|
||||
return $this->formatTime($time);
|
||||
}
|
||||
// french format HHhMM or HHh
|
||||
|
||||
if (preg_match('/^\d\d?[hH]\d?\d?$/', $time)) {
|
||||
return $this->formatTime(str_pad(str_ireplace("H", ":", $time), 5, "0"));
|
||||
}
|
||||
// Number only
|
||||
|
||||
if (preg_match('/^\d{1,4}$/', $time)) {
|
||||
return $this->formatTime(str_pad(str_pad($time, 2, "0", STR_PAD_LEFT), 4, "0"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $day
|
||||
* @return false|int 1 if the day string can be parsed, 0 if not and false if an error occured
|
||||
*/
|
||||
public function dayIsReadable(string $day) {
|
||||
return preg_match('/^\d{2}-\d{2}-\d{4}$/', $day);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $time
|
||||
* @return string date string in format H:i (e.g. 19:00)
|
||||
*/
|
||||
public function formatTime(string $time): string
|
||||
{
|
||||
return date("H:i", strtotime($time));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts MD Code to HTML, then strips HTML away
|
||||
*/
|
||||
public function stripMD(string $string): string
|
||||
{
|
||||
return strip_tags(Utils::markdown($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips a string so it's usable as a file name (only digits, letters and underline allowed)
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function stripTitle(string $string): ?string {
|
||||
return preg_replace('/[^a-z0-9_]+/', '-', strtolower($string));
|
||||
}
|
||||
}
|
@ -17,24 +17,28 @@
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
use function __;
|
||||
use DateTime;
|
||||
use Egulias\EmailValidator\EmailValidator;
|
||||
use Egulias\EmailValidator\Validation\RFCValidation;
|
||||
use o80\i18n\CantLoadDictionaryException;
|
||||
|
||||
/**
|
||||
* This class helps to clean all inputs from the users or external services.
|
||||
*/
|
||||
class InputService {
|
||||
|
||||
function __construct() {}
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* This method filter an array calling "filter_var" on each items.
|
||||
* Only items validated are added at their own indexes, the others are not returned.
|
||||
* @param array $arr The array to filter
|
||||
* @param int $type The type of filter to apply
|
||||
* @param array|null $options The associative array of options
|
||||
* @param array|int $options The associative array of options
|
||||
* @return array The filtered array
|
||||
*/
|
||||
function filterArray(array $arr, $type, $options = null) {
|
||||
public function filterArray(array $arr, int $type, $options = 0): array
|
||||
{
|
||||
$newArr = [];
|
||||
|
||||
foreach($arr as $id=>$item) {
|
||||
@ -47,53 +51,116 @@ class InputService {
|
||||
return $newArr;
|
||||
}
|
||||
|
||||
function filterAllowedValues($value, array $allowedValues) {
|
||||
public function filterAllowedValues($value, array $allowedValues) {
|
||||
return in_array($value, $allowedValues, true) ? $value : null;
|
||||
}
|
||||
|
||||
public function filterTitle($title) {
|
||||
public function filterTitle($title): ?string
|
||||
{
|
||||
return $this->returnIfNotBlank($title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string
|
||||
*/
|
||||
public function filterId($id) {
|
||||
$filtered = filter_var($id, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
return $filtered ? substr($filtered, 0, 64) : false;
|
||||
}
|
||||
|
||||
public function filterName($name) {
|
||||
public function filterName($name): ?string
|
||||
{
|
||||
$filtered = trim($name);
|
||||
return $this->returnIfNotBlank($filtered);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string
|
||||
*/
|
||||
public function filterMail($mail) {
|
||||
return filter_var($mail, FILTER_VALIDATE_EMAIL);
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// formatting
|
||||
|
||||
$mail = trim($mail);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// e-mail validation
|
||||
|
||||
$resultat = FALSE;
|
||||
|
||||
$validator = new EmailValidator();
|
||||
|
||||
if ($validator->isValid($mail, new RFCValidation())) {
|
||||
$resultat = $mail;
|
||||
}
|
||||
|
||||
public function filterDescription($description) {
|
||||
$description = str_replace("\r\n", "\n", $description);
|
||||
return $description;
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// return
|
||||
|
||||
return $resultat;
|
||||
}
|
||||
|
||||
public function filterDescription($description): string {
|
||||
return str_replace("\r\n", "\n", $description);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string
|
||||
*/
|
||||
public function filterMD5($control) {
|
||||
return filter_var($control, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => MD5_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterBoolean($boolean) {
|
||||
return !!filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
public function filterInteger($int) {
|
||||
return filter_var($int, FILTER_VALIDATE_INT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
public function filterValueMax($int)
|
||||
{
|
||||
return $this->filterInteger($int) >= 1 ? $this->filterInteger($int) : false;
|
||||
}
|
||||
|
||||
public function filterBoolean($boolean): bool
|
||||
{
|
||||
return (bool)filter_var($boolean, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_TRUE_REGEX]]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string
|
||||
*/
|
||||
public function filterEditable($editable) {
|
||||
return filter_var($editable, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => EDITABLE_CHOICE_REGEX]]);
|
||||
}
|
||||
|
||||
public function filterComment($comment) {
|
||||
public function filterComment($comment): ?string
|
||||
{
|
||||
$comment = str_replace("\r\n", "\n", $comment);
|
||||
return $this->returnIfNotBlank($comment);
|
||||
}
|
||||
|
||||
public function filterDate($date) {
|
||||
$dDate = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0, 0);
|
||||
return $dDate->format('Y-m-d H:i:s');
|
||||
public function validateDate(DateTime $date, DateTime $minDate, DateTime $maxDate): DateTime {
|
||||
if ($date < $minDate) {
|
||||
return $minDate;
|
||||
}
|
||||
|
||||
if ($maxDate < $date) {
|
||||
return $maxDate;
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CantLoadDictionaryException
|
||||
* @return DateTime|false
|
||||
*/
|
||||
public function parseDate(string $date) {
|
||||
return DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $date)->setTime(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +169,8 @@ class InputService {
|
||||
* @param string $filtered The value
|
||||
* @return string|null
|
||||
*/
|
||||
private function returnIfNotBlank($filtered) {
|
||||
private function returnIfNotBlank(string $filtered): ?string
|
||||
{
|
||||
if ($filtered) {
|
||||
$withoutSpaces = str_replace(' ', '', $filtered);
|
||||
if (!empty($withoutSpaces)) {
|
||||
@ -112,5 +180,4 @@ class InputService {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,15 +17,17 @@
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
use function __f;
|
||||
use Exception;
|
||||
use Framadate\Utils;
|
||||
use PDO;
|
||||
use Smarty;
|
||||
|
||||
/**
|
||||
* This class helps to clean all inputs from the users or external services.
|
||||
*/
|
||||
class InstallService {
|
||||
|
||||
private $fields = array(
|
||||
private $fields = [
|
||||
// General
|
||||
'appName' => 'Framadate',
|
||||
'appMail' => '',
|
||||
@ -39,26 +41,29 @@ class InstallService {
|
||||
'dbPassword' => '',
|
||||
'dbPrefix' => 'fd_',
|
||||
'migrationTable' => 'framadate_migration'
|
||||
);
|
||||
];
|
||||
|
||||
function __construct() {}
|
||||
public function __construct() {}
|
||||
|
||||
public function updateFields($data) {
|
||||
public function updateFields($data): void
|
||||
{
|
||||
foreach ($data as $field => $value) {
|
||||
$this->fields[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function install(Smarty &$smarty) {
|
||||
public function install(Smarty &$smarty): array
|
||||
{
|
||||
// Check values are present
|
||||
if (empty($this->fields['appName']) || empty($this->fields['appMail']) || empty($this->fields['defaultLanguage']) || empty($this->fields['dbConnectionString']) || empty($this->fields['dbUser'])) {
|
||||
return $this->error('MISSING_VALUES');
|
||||
}
|
||||
|
||||
// Connect to database
|
||||
try {
|
||||
$connect = $this->connectTo($this->fields['dbConnectionString'], $this->fields['dbUser'], $this->fields['dbPassword']);
|
||||
if (!$connect) {
|
||||
return $this->error('CANT_CONNECT_TO_DATABASE');
|
||||
} catch(Exception $e) {
|
||||
return $this->error('CANT_CONNECT_TO_DATABASE', $e->getMessage());
|
||||
}
|
||||
|
||||
// Write configuration to conf.php file
|
||||
@ -69,18 +74,26 @@ class InstallService {
|
||||
return $this->ok();
|
||||
}
|
||||
|
||||
function connectTo($connectionString, $user, $password) {
|
||||
try {
|
||||
$pdo = @new \PDO($connectionString, $user, $password);
|
||||
$pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE, \PDO::FETCH_OBJ);
|
||||
$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
/**
|
||||
* Connect to PDO compatible source
|
||||
*
|
||||
* @param string $connectionString
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @return PDO
|
||||
*/
|
||||
public function connectTo(string $connectionString, string $user, string $password): PDO
|
||||
{
|
||||
$pdo = @new PDO($connectionString, $user, $password);
|
||||
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
return $pdo;
|
||||
} catch(\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writeConfiguration(Smarty &$smarty) {
|
||||
/**
|
||||
* @return false|int
|
||||
*/
|
||||
public function writeConfiguration(Smarty &$smarty) {
|
||||
foreach($this->fields as $field=>$value) {
|
||||
$smarty->assign($field, $value);
|
||||
}
|
||||
@ -92,34 +105,39 @@ class InstallService {
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return false|int
|
||||
*/
|
||||
function writeToFile($content) {
|
||||
public function writeToFile(string $content) {
|
||||
return @file_put_contents(CONF_FILENAME, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
function ok() {
|
||||
return array(
|
||||
public function ok(): array
|
||||
{
|
||||
return [
|
||||
'status' => 'OK',
|
||||
'msg' => __f('Installation', 'Ended', Utils::get_server_name())
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $msg
|
||||
* @param string $msg
|
||||
* @param string $details
|
||||
* @return array
|
||||
*/
|
||||
function error($msg) {
|
||||
return array(
|
||||
public function error(string $msg, string $details = ''): array
|
||||
{
|
||||
return [
|
||||
'status' => 'ERROR',
|
||||
'code' => $msg
|
||||
);
|
||||
'code' => $msg,
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
|
||||
public function getFields() {
|
||||
public function getFields(): array
|
||||
{
|
||||
return $this->fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ namespace Framadate\Services;
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class LogService {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -17,9 +16,8 @@ class LogService {
|
||||
* @param $tag string A tag is used to quickly found a message when reading log file
|
||||
* @param $message string some message
|
||||
*/
|
||||
function log($tag, $message) {
|
||||
public function log(string $tag, string $message): void
|
||||
{
|
||||
error_log(date('Ymd His') . ' [' . $tag . '] ' . $message . "\n", 3, ROOT_DIR . LOG_FILE);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,31 +1,43 @@
|
||||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use PHPMailer;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
|
||||
class MailService {
|
||||
public const DELAY_BEFORE_RESEND = 300;
|
||||
|
||||
public const MAILSERVICE_KEY = 'mailservice';
|
||||
|
||||
private $smtp_allowed;
|
||||
|
||||
const DELAY_BEFORE_RESEND = 300;
|
||||
|
||||
const MAILSERVICE_KEY = 'mailservice';
|
||||
private $smtp_options = [];
|
||||
|
||||
private $logService;
|
||||
|
||||
function __construct($smtp_allowed) {
|
||||
public function __construct($smtp_allowed, $smtp_options = []) {
|
||||
$this->logService = new LogService();
|
||||
$this->smtp_allowed = $smtp_allowed;
|
||||
if (true === is_array($smtp_options)) {
|
||||
$this->smtp_options = $smtp_options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false|string
|
||||
*/
|
||||
public function isValidEmail($email) {
|
||||
return filter_var($email, FILTER_VALIDATE_EMAIL);
|
||||
}
|
||||
|
||||
function send($to, $subject, $body, $msgKey = null) {
|
||||
if ($this->smtp_allowed == true && $this->canSendMsg($msgKey)) {
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function send(string $to, string $subject, string $body, ?string $msgKey = null): void
|
||||
{
|
||||
if ($this->smtp_allowed === true && $this->canSendMsg($msgKey)) {
|
||||
$mail = new PHPMailer(true);
|
||||
$mail->isSMTP();
|
||||
$this->configureMailer($mail);
|
||||
|
||||
// From
|
||||
$mail->FromName = NOMAPPLICATION;
|
||||
@ -41,7 +53,7 @@ class MailService {
|
||||
$mail->Subject = $subject;
|
||||
|
||||
// Bodies
|
||||
$body = $body . ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
|
||||
$body .= ' <br/><br/>' . __('Mail', 'Thanks for your trust.') . ' <br/>' . NOMAPPLICATION . ' <hr/>' . __('Mail', 'FOOTER');
|
||||
$mail->isHTML(true);
|
||||
$mail->msgHTML($body, ROOT_DIR, true);
|
||||
|
||||
@ -49,6 +61,7 @@ class MailService {
|
||||
$mail->CharSet = 'UTF-8';
|
||||
$mail->addCustomHeader('Auto-Submitted', 'auto-generated');
|
||||
$mail->addCustomHeader('Return-Path', '<>');
|
||||
$mail->XMailer = ' ';
|
||||
|
||||
// Send mail
|
||||
$mail->send();
|
||||
@ -57,20 +70,50 @@ class MailService {
|
||||
$this->logService->log('MAIL', 'Mail sent to: ' . $to . ', key: ' . $msgKey);
|
||||
|
||||
// Store the mail sending date
|
||||
$this->initializeSession();
|
||||
$_SESSION[self::MAILSERVICE_KEY][$msgKey] = time();
|
||||
}
|
||||
}
|
||||
|
||||
function canSendMsg($msgKey) {
|
||||
if ($msgKey == null) {
|
||||
public function canSendMsg(?string $msgKey): bool
|
||||
{
|
||||
if ($msgKey === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
|
||||
$_SESSION[self::MAILSERVICE_KEY] = [];
|
||||
}
|
||||
$this->initializeSession();
|
||||
return !isset($_SESSION[self::MAILSERVICE_KEY][$msgKey]) || time() - $_SESSION[self::MAILSERVICE_KEY][$msgKey] > self::DELAY_BEFORE_RESEND;
|
||||
}
|
||||
|
||||
}
|
||||
private function initializeSession(): void {
|
||||
if (!isset($_SESSION[self::MAILSERVICE_KEY])) {
|
||||
$_SESSION[self::MAILSERVICE_KEY] = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the mailer with the options
|
||||
*
|
||||
* @param PHPMailer $mailer
|
||||
*/
|
||||
private function configureMailer(PHPMailer $mailer): void
|
||||
{
|
||||
$mailer->isSMTP();
|
||||
|
||||
$available_options = [
|
||||
'host' => 'Host',
|
||||
'auth' => 'SMTPAuth',
|
||||
'authtype' => 'AuthType',
|
||||
'username' => 'Username',
|
||||
'password' => 'Password',
|
||||
'secure' => 'SMTPSecure',
|
||||
'port' => 'Port',
|
||||
];
|
||||
|
||||
foreach ($available_options as $config_option => $mailer_option) {
|
||||
if (true === isset($this->smtp_options[$config_option]) && false === empty($this->smtp_options[$config_option])) {
|
||||
$mailer->{$mailer_option} = $this->smtp_options[$config_option];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Utils;
|
||||
use \stdClass;
|
||||
|
||||
use function __;
|
||||
use function __f;
|
||||
use Framadate\Utils;
|
||||
use o80\i18n\CantLoadDictionaryException;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
class NotificationService {
|
||||
|
||||
const UPDATE_VOTE = 1;
|
||||
const ADD_VOTE = 2;
|
||||
const ADD_COMMENT = 3;
|
||||
const UPDATE_POLL = 10;
|
||||
const DELETED_POLL = 11;
|
||||
public const UPDATE_VOTE = 1;
|
||||
public const ADD_VOTE = 2;
|
||||
public const ADD_COMMENT = 3;
|
||||
public const UPDATE_POLL = 10;
|
||||
public const DELETED_POLL = 11;
|
||||
|
||||
private $mailService;
|
||||
|
||||
function __construct(MailService $mailService) {
|
||||
public function __construct(MailService $mailService) {
|
||||
$this->mailService = $mailService;
|
||||
}
|
||||
|
||||
@ -28,15 +28,20 @@ class NotificationService {
|
||||
* @param $poll stdClass The poll
|
||||
* @param $name string The name user who triggered the notification
|
||||
* @param $type int cf: Constants on the top of this page
|
||||
* @throws Exception|CantLoadDictionaryException
|
||||
*/
|
||||
function sendUpdateNotification(stdClass $poll, $type, $name='') {
|
||||
public function sendUpdateNotification($poll, int $type, string $name=''): void
|
||||
{
|
||||
if (!isset($_SESSION['mail_sent'])) {
|
||||
$_SESSION['mail_sent'] = [];
|
||||
}
|
||||
|
||||
if ($poll->receiveNewVotes) {
|
||||
$isVoteAndCanSendIt = ($type === self::UPDATE_VOTE || $type === self::ADD_VOTE) && $poll->receiveNewVotes;
|
||||
$isCommentAndCanSendIt = $type === self::ADD_COMMENT && $poll->receiveNewComments;
|
||||
$isOtherType = $type !== self::UPDATE_VOTE && $type !== self::ADD_VOTE && $type !== self::ADD_COMMENT;
|
||||
|
||||
if (self::isParticipation($type)) {
|
||||
if ($isVoteAndCanSendIt || $isCommentAndCanSendIt || $isOtherType) {
|
||||
if ($this->isParticipation($type)) {
|
||||
$translationString = 'Poll\'s participation: %s';
|
||||
} else {
|
||||
$translationString = 'Notification of poll: %s';
|
||||
@ -44,7 +49,6 @@ class NotificationService {
|
||||
|
||||
$subject = '[' . NOMAPPLICATION . '] ' . __f('Mail', $translationString, $poll->title);
|
||||
|
||||
|
||||
$message = '';
|
||||
|
||||
$urlSondage = Utils::getUrlSondage($poll->admin_id, true);
|
||||
@ -72,17 +76,17 @@ class NotificationService {
|
||||
case self::DELETED_POLL:
|
||||
$message = __f('Mail', 'Someone just delete your poll %s.', Utils::htmlEscape($poll->title)) . "\n\n";
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$messageTypeKey = $type . '-' . $poll->id;
|
||||
if ($poll->admin_mail) {
|
||||
$this->mailService->send($poll->admin_mail, $subject, $message, $messageTypeKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isParticipation($type)
|
||||
public function isParticipation(int $type): bool
|
||||
{
|
||||
return $type >= self::UPDATE_POLL;
|
||||
}
|
||||
|
||||
}
|
@ -18,17 +18,19 @@
|
||||
*/
|
||||
namespace Framadate\Services;
|
||||
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Framadate\Exception\AlreadyExistsException;
|
||||
use Framadate\Exception\ConcurrentEditionException;
|
||||
use Framadate\Exception\ConcurrentVoteException;
|
||||
use Framadate\Exception\PollNotFoundException;
|
||||
use Framadate\Form;
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Security\Token;
|
||||
use Framadate\Utils;
|
||||
use stdClass;
|
||||
|
||||
class PollService {
|
||||
|
||||
private $connect;
|
||||
private $logService;
|
||||
|
||||
private $pollRepository;
|
||||
@ -36,8 +38,7 @@ class PollService {
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, LogService $logService) {
|
||||
$this->connect = $connect;
|
||||
public function __construct(LogService $logService) {
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
@ -48,10 +49,10 @@ class PollService {
|
||||
/**
|
||||
* Find a poll from its ID.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @return \stdClass|null The found poll, or null
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @return stdClass|null The found poll, or null
|
||||
*/
|
||||
function findById($poll_id) {
|
||||
public function findById(string $poll_id) {
|
||||
if (preg_match(POLL_REGEX, $poll_id)) {
|
||||
return $this->pollRepository->findById($poll_id);
|
||||
}
|
||||
@ -59,7 +60,7 @@ class PollService {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function findByAdminId($admin_poll_id) {
|
||||
public function findByAdminId(string $admin_poll_id) {
|
||||
if (preg_match(ADMIN_POLL_REGEX, $admin_poll_id)) {
|
||||
return $this->pollRepository->findByAdminId($admin_poll_id);
|
||||
}
|
||||
@ -67,63 +68,75 @@ class PollService {
|
||||
return null;
|
||||
}
|
||||
|
||||
function allCommentsByPollId($poll_id) {
|
||||
public function allCommentsByPollId(string $poll_id) {
|
||||
return $this->commentRepository->findAllByPollId($poll_id);
|
||||
}
|
||||
|
||||
function allVotesByPollId($poll_id) {
|
||||
public function allVotesByPollId(string $poll_id) {
|
||||
return $this->voteRepository->allUserVotesByPollId($poll_id);
|
||||
}
|
||||
|
||||
function allSlotsByPoll($poll) {
|
||||
public function allSlotsByPoll(stdClass $poll) {
|
||||
$slots = $this->slotRepository->listByPollId($poll->id);
|
||||
if ($poll->format == 'D') {
|
||||
if ($poll->format === 'D') {
|
||||
$this->sortSlorts($slots);
|
||||
}
|
||||
return $slots;
|
||||
}
|
||||
|
||||
public function updateVote($poll_id, $vote_id, $name, $choices, $slots_hash) {
|
||||
$poll = $this->findById($poll_id);
|
||||
|
||||
// Check if slots are still the same
|
||||
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
|
||||
/**
|
||||
* @param string $poll_id
|
||||
* @param int $vote_id
|
||||
* @param string $name
|
||||
* @param array $choices
|
||||
* @param string $slots_hash
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @return bool
|
||||
*/
|
||||
public function updateVote(string $poll_id, int $vote_id, string $name, array $choices, string $slots_hash): bool
|
||||
{
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name, $vote_id);
|
||||
|
||||
// Update vote
|
||||
$choices = implode($choices);
|
||||
return $this->voteRepository->update($poll_id, $vote_id, $name, $choices);
|
||||
return $this->voteRepository->update($poll_id, $vote_id, $name, implode($choices));
|
||||
}
|
||||
|
||||
function addVote($poll_id, $name, $choices, $slots_hash) {
|
||||
$poll = $this->findById($poll_id);
|
||||
|
||||
// Check if slots are still the same
|
||||
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
|
||||
|
||||
// Check if vote already exists
|
||||
if ($this->voteRepository->existsByPollIdAndName($poll_id, $name)) {
|
||||
throw new AlreadyExistsException();
|
||||
}
|
||||
/**
|
||||
* @param string $poll_id
|
||||
* @param string $name
|
||||
* @param array $choices
|
||||
* @param string $slots_hash
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @throws PollNotFoundException
|
||||
* @throws AlreadyExistsException
|
||||
* @return stdClass
|
||||
*/
|
||||
public function addVote(string $poll_id, string $name, array $choices, string $slots_hash): stdClass
|
||||
{
|
||||
$this->checkVoteConstraints($choices, $poll_id, $slots_hash, $name);
|
||||
|
||||
// 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, implode($choices), $this->random(16));
|
||||
}
|
||||
|
||||
function addComment($poll_id, $name, $comment) {
|
||||
public function addComment($poll_id, $name, $comment): bool
|
||||
{
|
||||
if ($this->commentRepository->exists($poll_id, $name, $comment)) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->commentRepository->insert($poll_id, $name, $comment);
|
||||
}
|
||||
|
||||
return $this->commentRepository->insert($poll_id, $name, $comment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Form $form
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
function createPoll(Form $form) {
|
||||
public function createPoll(Form $form): array
|
||||
{
|
||||
// Generate poll IDs, loop while poll ID already exists
|
||||
|
||||
if (empty($form->id)) { // User want us to generate an id for him
|
||||
@ -131,13 +144,11 @@ class PollService {
|
||||
$poll_id = $this->random(16);
|
||||
} while ($this->pollRepository->existsById($poll_id));
|
||||
$admin_poll_id = $poll_id . $this->random(8);
|
||||
|
||||
} else { // User have choosen the poll id
|
||||
$poll_id = $form->id;
|
||||
do {
|
||||
$admin_poll_id = $this->random(24);
|
||||
} while ($this->pollRepository->existsByAdminId($admin_poll_id));
|
||||
|
||||
}
|
||||
|
||||
// Insert poll + slots
|
||||
@ -148,15 +159,27 @@ class PollService {
|
||||
|
||||
$this->logService->log('CREATE_POLL', 'id:' . $poll_id . ', title: ' . $form->title . ', format:' . $form->format . ', admin:' . $form->admin_name . ', mail:' . $form->admin_mail);
|
||||
|
||||
return array($poll_id, $admin_poll_id);
|
||||
return [$poll_id, $admin_poll_id];
|
||||
}
|
||||
|
||||
public function findAllByAdminMail($mail) {
|
||||
public function findAllByAdminMail($mail): array
|
||||
{
|
||||
return $this->pollRepository->findAllByAdminMail($mail);
|
||||
}
|
||||
|
||||
function computeBestChoices($votes) {
|
||||
$result = ['y' => [0], 'inb' => [0]];
|
||||
/**
|
||||
* @param array $votes
|
||||
* @param stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
public function computeBestChoices(array $votes, $poll): array
|
||||
{
|
||||
if (0 === count($votes)) {
|
||||
return $this->computeEmptyBestChoices($poll);
|
||||
}
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
|
||||
// if there are votes
|
||||
foreach ($votes as $vote) {
|
||||
$choices = str_split($vote->choices);
|
||||
foreach ($choices as $i => $choice) {
|
||||
@ -164,10 +187,10 @@ class PollService {
|
||||
$result['inb'][$i] = 0;
|
||||
$result['y'][$i] = 0;
|
||||
}
|
||||
if ($choice == 1) {
|
||||
if ($choice === "1") {
|
||||
$result['inb'][$i]++;
|
||||
}
|
||||
if ($choice == 2) {
|
||||
if ($choice === "2") {
|
||||
$result['y'][$i]++;
|
||||
}
|
||||
}
|
||||
@ -176,10 +199,11 @@ class PollService {
|
||||
return $result;
|
||||
}
|
||||
|
||||
function splitSlots($slots) {
|
||||
$splitted = array();
|
||||
public function splitSlots($slots): array
|
||||
{
|
||||
$splitted = [];
|
||||
foreach ($slots as $slot) {
|
||||
$obj = new \stdClass();
|
||||
$obj = new stdClass();
|
||||
$obj->day = $slot->title;
|
||||
$obj->moments = explode(',', $slot->moments);
|
||||
|
||||
@ -193,16 +217,18 @@ class PollService {
|
||||
* @param $slots array The slots to hash
|
||||
* @return string The hash
|
||||
*/
|
||||
public function hashSlots($slots) {
|
||||
return md5(array_reduce($slots, function($carry, $item) {
|
||||
public function hashSlots(array $slots): string
|
||||
{
|
||||
return md5(array_reduce($slots, static function($carry, $item) {
|
||||
return $carry . $item->id . '@' . $item->moments . ';';
|
||||
}));
|
||||
}
|
||||
|
||||
function splitVotes($votes) {
|
||||
$splitted = array();
|
||||
public function splitVotes(array $votes): array
|
||||
{
|
||||
$splitted = [];
|
||||
foreach ($votes as $vote) {
|
||||
$obj = new \stdClass();
|
||||
$obj = new stdClass();
|
||||
$obj->id = $vote->id;
|
||||
$obj->name = $vote->name;
|
||||
$obj->uniqId = $vote->uniqId;
|
||||
@ -214,23 +240,109 @@ class PollService {
|
||||
return $splitted;
|
||||
}
|
||||
|
||||
private function random($length) {
|
||||
/**
|
||||
* @throws Exception
|
||||
* @return DateTime The max date allowed for expiry date
|
||||
*/
|
||||
public function maxExpiryDate(): DateTime {
|
||||
global $config;
|
||||
return (new DateTime())->add(new DateInterval('P' . $config['default_poll_duration'] . 'D'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTime The min date allowed for expiry date
|
||||
*/
|
||||
public function minExpiryDate(): DateTime
|
||||
{
|
||||
return (new DateTime())->add(new DateInterval('P1D'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function sortSlorts(array &$slots): array {
|
||||
uasort($slots, static function ($a, $b) {
|
||||
if ($a->title === $b->title) {
|
||||
return 0;
|
||||
}
|
||||
return $a->title > $b->title ? 1 : -1;
|
||||
});
|
||||
return $slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stdClass $poll
|
||||
* @return array
|
||||
*/
|
||||
private function computeEmptyBestChoices($poll): array
|
||||
{
|
||||
$result = ['y' => [], 'inb' => []];
|
||||
// if there is no votes, calculates the number of slot
|
||||
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
|
||||
if ($poll->format === 'A') {
|
||||
// poll format classic
|
||||
|
||||
for ($i = 0, $iMax = count($slots); $i < $iMax; $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
} else {
|
||||
// poll format date
|
||||
|
||||
$slots = $this->splitSlots($slots);
|
||||
|
||||
foreach ($slots as $slot) {
|
||||
for ($i = 0, $iMax = count($slot->moments); $i < $iMax; $i++) {
|
||||
$result['y'][] = 0;
|
||||
$result['inb'][] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function random(int $length): string
|
||||
{
|
||||
return Token::getToken($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The max timestamp allowed for expiry date
|
||||
* @param array $choices
|
||||
* @param string $poll_id
|
||||
* @param string $slots_hash
|
||||
* @param string $name
|
||||
* @param bool|int $vote_id
|
||||
* @throws AlreadyExistsException
|
||||
* @throws ConcurrentEditionException
|
||||
* @throws ConcurrentVoteException
|
||||
* @throws PollNotFoundException
|
||||
*/
|
||||
public function maxExpiryDate() {
|
||||
global $config;
|
||||
return time() + (86400 * $config['default_poll_duration']);
|
||||
private function checkVoteConstraints(array $choices, string $poll_id, string $slots_hash, string $name, $vote_id = false): void
|
||||
{
|
||||
// Check if vote already exists with the same name
|
||||
if (false === $vote_id) {
|
||||
$exists = $this->voteRepository->existsByPollIdAndName($poll_id, $name);
|
||||
} else {
|
||||
$exists = $this->voteRepository->existsByPollIdAndNameAndVoteId($poll_id, $name, $vote_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int The min timestamp allowed for expiry date
|
||||
*/
|
||||
public function minExpiryDate() {
|
||||
return time() + 86400;
|
||||
if ($exists) {
|
||||
throw new AlreadyExistsException();
|
||||
}
|
||||
|
||||
$poll = $this->findById($poll_id);
|
||||
|
||||
if (!$poll) {
|
||||
throw new PollNotFoundException();
|
||||
}
|
||||
|
||||
// Check that no-one voted in the meantime and it conflicts the maximum votes constraint
|
||||
$this->checkMaxVotes($choices, $poll, $poll_id);
|
||||
|
||||
// Check if slots are still the same
|
||||
$this->checkThatSlotsDidntChanged($poll, $slots_hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -240,7 +352,8 @@ class PollService {
|
||||
* @param $slots_hash string The hash sent by the user
|
||||
* @throws ConcurrentEditionException Thrown when hashes are differents
|
||||
*/
|
||||
private function checkThatSlotsDidntChanged($poll, $slots_hash) {
|
||||
private function checkThatSlotsDidntChanged(stdClass $poll, string $slots_hash): void
|
||||
{
|
||||
$slots = $this->allSlotsByPoll($poll);
|
||||
if ($slots_hash !== $this->hashSlots($slots)) {
|
||||
throw new ConcurrentEditionException();
|
||||
@ -248,13 +361,25 @@ class PollService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
* This method checks if the votes doesn't conflicts the maximum votes constraint
|
||||
*
|
||||
* @param $user_choice
|
||||
* @param stdClass $poll
|
||||
* @param string $poll_id
|
||||
* @throws ConcurrentVoteException
|
||||
*/
|
||||
public function sortSlorts(&$slots) {
|
||||
uasort($slots, function ($a, $b) {
|
||||
return $a->title > $b->title;
|
||||
});
|
||||
return $slots;
|
||||
private function checkMaxVotes(array $user_choice, $poll, string $poll_id): void
|
||||
{
|
||||
$votes = $this->allVotesByPollId($poll_id);
|
||||
if (count($votes) <= 0) {
|
||||
return;
|
||||
}
|
||||
$best_choices = $this->computeBestChoices($votes, $poll);
|
||||
foreach ($best_choices['y'] as $i => $nb_choice) {
|
||||
// if for this option we have reached maximum value and user wants to add itself too
|
||||
if ($poll->ValueMax !== null && $nb_choice >= $poll->ValueMax && $user_choice[$i] === "2") {
|
||||
throw new ConcurrentVoteException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,14 +9,13 @@ use Framadate\Repositories\RepositoryFactory;
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class PurgeService {
|
||||
|
||||
private $logService;
|
||||
private $pollRepository;
|
||||
private $slotRepository;
|
||||
private $voteRepository;
|
||||
private $commentRepository;
|
||||
|
||||
function __construct(FramaDB $connect, LogService $logService) {
|
||||
public function __construct(LogService $logService) {
|
||||
$this->logService = $logService;
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
$this->slotRepository = RepositoryFactory::slotRepository();
|
||||
@ -27,9 +26,10 @@ class PurgeService {
|
||||
/**
|
||||
* This methode purges all old polls (the ones with end_date in past).
|
||||
*
|
||||
* @return bool true is action succeeded
|
||||
* @return int number of purged polls
|
||||
*/
|
||||
function purgeOldPolls() {
|
||||
public function purgeOldPolls(): int
|
||||
{
|
||||
$oldPolls = $this->pollRepository->findOldPolls();
|
||||
$count = count($oldPolls);
|
||||
|
||||
@ -38,9 +38,9 @@ class PurgeService {
|
||||
|
||||
foreach ($oldPolls as $poll) {
|
||||
if ($this->purgePollById($poll->id)) {
|
||||
$this->logService->log('EXPIRATION_SUCCESS', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: '.$poll->format . ', admin: ' . $poll->admin_name);
|
||||
$this->logService->log('EXPIRATION_SUCCESS', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
|
||||
} else {
|
||||
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: '.$poll->format . ', admin: ' . $poll->admin_name);
|
||||
$this->logService->log('EXPIRATION_FAILED', 'id: ' . $poll->id . ', title:' . $poll->title . ', format: ' . $poll->format . ', admin: ' . $poll->admin_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,10 +51,11 @@ class PurgeService {
|
||||
/**
|
||||
* This methode delete all data about a poll.
|
||||
*
|
||||
* @param $poll_id int The ID of the poll
|
||||
* @param string $poll_id The ID of the poll
|
||||
* @return bool true is action succeeded
|
||||
*/
|
||||
function purgePollById($poll_id) {
|
||||
public function purgePollById(string $poll_id): bool
|
||||
{
|
||||
$done = true;
|
||||
|
||||
$this->pollRepository->beginTransaction();
|
||||
@ -71,6 +72,4 @@ class PurgeService {
|
||||
|
||||
return $done;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\Security\Token;
|
||||
use Framadate\Security\PasswordHasher;
|
||||
use Framadate\Security\Token;
|
||||
|
||||
class SecurityService {
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19,9 +18,10 @@ class SecurityService {
|
||||
* </ul>
|
||||
*
|
||||
* @param $tokan_name string The name of the CSRF token
|
||||
* @return Token The token
|
||||
* @return string The token
|
||||
*/
|
||||
function getToken($tokan_name) {
|
||||
function getToken(string $tokan_name): string
|
||||
{
|
||||
if (!isset($_SESSION['tokens'])) {
|
||||
$_SESSION['tokens'] = [];
|
||||
}
|
||||
@ -39,7 +39,8 @@ class SecurityService {
|
||||
* @param $csrf string Value to check
|
||||
* @return bool true if the token is well checked
|
||||
*/
|
||||
public function checkCsrf($tokan_name, $csrf) {
|
||||
public function checkCsrf(string $tokan_name, string $csrf): bool
|
||||
{
|
||||
$checked = $_SESSION['tokens'][$tokan_name]->getValue() === $csrf;
|
||||
|
||||
if($checked) {
|
||||
@ -55,21 +56,21 @@ class SecurityService {
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @return bool true if the current session can access this poll
|
||||
*/
|
||||
public function canAccessPoll($poll) {
|
||||
public function canAccessPoll($poll): bool
|
||||
{
|
||||
if (is_null($poll->password_hash)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
|
||||
$currentPassword = isset($_SESSION['poll_security'][$poll->id]) ? $_SESSION['poll_security'][$poll->id] : null;
|
||||
$currentPassword = $_SESSION['poll_security'][$poll->id] ?? null;
|
||||
if (!empty($currentPassword) && PasswordHasher::verify($currentPassword, $poll->password_hash)) {
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
unset($_SESSION['poll_security'][$poll->id]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit to the session a poll password
|
||||
@ -77,18 +78,18 @@ class SecurityService {
|
||||
* @param $poll \stdClass The poll which we seek access
|
||||
* @param $password string the password to compare
|
||||
*/
|
||||
public function submitPollAccess($poll, $password) {
|
||||
public function submitPollAccess($poll, string $password): void
|
||||
{
|
||||
if (!empty($password)) {
|
||||
$this->ensureSessionPollSecurityIsCreated();
|
||||
$_SESSION['poll_security'][$poll->id] = $password;
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureSessionPollSecurityIsCreated() {
|
||||
private function ensureSessionPollSecurityIsCreated(): void
|
||||
{
|
||||
if (!isset($_SESSION['poll_security'])) {
|
||||
$_SESSION['poll_security'] = array();
|
||||
$_SESSION['poll_security'] = [];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framadate\Services;
|
||||
|
||||
|
||||
class SessionService {
|
||||
|
||||
/**
|
||||
* Get value of $key in $section, or $defaultValue
|
||||
*
|
||||
@ -20,12 +17,7 @@ class SessionService {
|
||||
|
||||
$this->initSectionIfNeeded($section);
|
||||
|
||||
$returnValue = $defaultValue;
|
||||
if (isset($_SESSION[$section][$key])) {
|
||||
$returnValue = $_SESSION[$section][$key];
|
||||
}
|
||||
|
||||
return $returnValue;
|
||||
return $_SESSION[$section][$key] ?? $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +27,8 @@ class SessionService {
|
||||
* @param $key
|
||||
* @param $value
|
||||
*/
|
||||
public function set($section, $key, $value) {
|
||||
public function set($section, $key, $value): void
|
||||
{
|
||||
assert(!empty($key));
|
||||
assert(!empty($section));
|
||||
|
||||
@ -50,17 +43,18 @@ class SessionService {
|
||||
* @param $section
|
||||
* @param $key
|
||||
*/
|
||||
public function remove($section, $key) {
|
||||
public function remove($section, $key): void
|
||||
{
|
||||
assert(!empty($key));
|
||||
assert(!empty($section));
|
||||
|
||||
unset($_SESSION[$section][$key]);
|
||||
}
|
||||
|
||||
private function initSectionIfNeeded($section) {
|
||||
private function initSectionIfNeeded($section): void
|
||||
{
|
||||
if (!isset($_SESSION[$section])) {
|
||||
$_SESSION[$section] = array();
|
||||
$_SESSION[$section] = [];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -9,10 +9,9 @@ use Framadate\Repositories\RepositoryFactory;
|
||||
* @package Framadate\Services
|
||||
*/
|
||||
class SuperAdminService {
|
||||
|
||||
private $pollRepository;
|
||||
|
||||
function __construct() {
|
||||
public function __construct() {
|
||||
$this->pollRepository = RepositoryFactory::pollRepository();
|
||||
}
|
||||
|
||||
@ -24,15 +23,13 @@ class SuperAdminService {
|
||||
* @param int $limit The limit size
|
||||
* @return array ['polls' => The {$limit} polls, 'count' => Entries found by the query, 'total' => Total count]
|
||||
*/
|
||||
public function findAllPolls($search, $page, $limit) {
|
||||
public function findAllPolls(array $search, int $page, int $limit): array
|
||||
{
|
||||
$start = $page * $limit;
|
||||
$polls = $this->pollRepository->findAll($search, $start, $limit);
|
||||
$count = $this->pollRepository->count($search);
|
||||
$total = $this->pollRepository->count();
|
||||
|
||||
|
||||
return ['polls' => $polls, 'count' => $count, 'total' => $total];
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,16 +18,19 @@
|
||||
*/
|
||||
namespace Framadate;
|
||||
|
||||
use Parsedown;
|
||||
|
||||
class Utils {
|
||||
/**
|
||||
* @return string Server name
|
||||
*/
|
||||
public static function get_server_name() {
|
||||
public static function get_server_name(): string
|
||||
{
|
||||
$scheme = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')) ? 'https' : 'http';
|
||||
$port = in_array($_SERVER['SERVER_PORT'], [80, 443]) ? '' : ':' . $_SERVER['SERVER_PORT'];
|
||||
$port = in_array($_SERVER['SERVER_PORT'], ['80', '443'], true) ? '' : ':' . $_SERVER['SERVER_PORT'];
|
||||
$dirname = dirname($_SERVER['SCRIPT_NAME']);
|
||||
$dirname = $dirname === '\\' ? '/' : $dirname . '/';
|
||||
$dirname = str_replace('/admin', '', $dirname);
|
||||
$dirname = str_replace(['/admin', '/action'], '', $dirname);
|
||||
$server_name = (defined('APP_URL') ? APP_URL : $_SERVER['SERVER_NAME']) . $port . $dirname;
|
||||
|
||||
return $scheme . '://' . preg_replace('#//+#', '/', $server_name);
|
||||
@ -35,9 +38,10 @@ class Utils {
|
||||
|
||||
/**
|
||||
* @param string $title
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public static function print_header($title = '') {
|
||||
public static function print_header($title = ''): void {
|
||||
global $locale;
|
||||
|
||||
echo '<!DOCTYPE html>
|
||||
@ -57,17 +61,17 @@ class Utils {
|
||||
<link rel="stylesheet" href="' . self::get_server_name() . 'css/style.css" />
|
||||
<link rel="stylesheet" href="' . self::get_server_name() . 'css/frama.css" />
|
||||
<link rel="stylesheet" href="' . self::get_server_name() . 'css/print.css" media="print" />
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/jquery-1.11.1.min.js"></script>
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>';
|
||||
if ('en' != $locale) {
|
||||
<script src="' . self::get_server_name() . 'js/jquery-3.6.0.min.js"></script>
|
||||
<script src="' . self::get_server_name() . 'js/bootstrap.min.js"></script>
|
||||
<script src="' . self::get_server_name() . 'js/bootstrap-datepicker.js"></script>';
|
||||
if ('en' !== $locale) {
|
||||
echo '
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
|
||||
<script src="' . self::get_server_name() . 'js/locales/bootstrap-datepicker.' . $locale . '.js"></script>';
|
||||
}
|
||||
echo '
|
||||
<script type="text/javascript" src="' . self::get_server_name() . 'js/core.js"></script>';
|
||||
<script src="' . self::get_server_name() . 'js/core.js"></script>';
|
||||
if (is_file($_SERVER['DOCUMENT_ROOT'] . "/nav/nav.js")) {
|
||||
echo '<script src="/nav/nav.js" id="nav_js" type="text/javascript" charset="utf-8"></script><!-- /Framanav -->';
|
||||
echo '<script src="/nav/nav.js" id="nav_js" charset="utf-8"></script><!-- /Framanav -->';
|
||||
}
|
||||
|
||||
echo '
|
||||
@ -81,11 +85,14 @@ class Utils {
|
||||
* @param string $id The poll's id
|
||||
* @param bool $admin True to generate an admin URL, false for a public one
|
||||
* @param string $vote_id (optional) The vote's unique id
|
||||
* @param string|null $action
|
||||
* @param string|null $action_value
|
||||
* @return string The poll's URL.
|
||||
*/
|
||||
public static function getUrlSondage($id, $admin = false, $vote_id = '', $action = null, $action_value = null) {
|
||||
public static function getUrlSondage(string $id, bool $admin = false, string $vote_id = '', string $action = null, string $action_value = null): string
|
||||
{
|
||||
// URL-Encode $action_value
|
||||
$action_value = $action_value == null ? null : Utils::base64url_encode($action_value);
|
||||
$action_value = $action_value ? self::base64url_encode($action_value) : null;
|
||||
|
||||
if (URL_PROPRE) {
|
||||
if ($admin === true) {
|
||||
@ -93,10 +100,10 @@ class Utils {
|
||||
} else {
|
||||
$url = self::get_server_name() . $id;
|
||||
}
|
||||
if ($vote_id != '') {
|
||||
if ($vote_id !== '') {
|
||||
$url .= '/vote/' . $vote_id . "#edit";
|
||||
} elseif ($action != null) {
|
||||
if ($action_value != null) {
|
||||
} elseif ($action) {
|
||||
if ($action_value) {
|
||||
$url .= '/action/' . $action . '/' . $action_value;
|
||||
} else {
|
||||
$url .= '/action/' . $action;
|
||||
@ -108,10 +115,10 @@ class Utils {
|
||||
} else {
|
||||
$url = self::get_server_name() . 'studs.php?poll=' . $id;
|
||||
}
|
||||
if ($vote_id != '') {
|
||||
if ($vote_id !== '') {
|
||||
$url .= '&vote=' . $vote_id . "#edit";
|
||||
} elseif ($action != null) {
|
||||
if ($action_value != null) {
|
||||
} elseif ($action) {
|
||||
if ($action_value) {
|
||||
$url .= '&' . $action . "=" . $action_value;
|
||||
} else {
|
||||
$url .= '&' . $action . "=";
|
||||
@ -127,77 +134,77 @@ class Utils {
|
||||
*
|
||||
* @param mixed $object The object to print.
|
||||
*/
|
||||
public static function debug($object) {
|
||||
public static function debug($object): void
|
||||
{
|
||||
echo '<pre>';
|
||||
print_r($object);
|
||||
echo '</pre>';
|
||||
}
|
||||
|
||||
public static function table($tableName) {
|
||||
public static function table(string $tableName): string
|
||||
{
|
||||
return TABLENAME_PREFIX . $tableName;
|
||||
}
|
||||
|
||||
public static function markdown($md, $clear) {
|
||||
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $md, $md_a_img); // Markdown [![alt](src)](href)
|
||||
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $md, $md_img); // Markdown ![alt](src)
|
||||
preg_match_all('/\[(.*?)\]\((.*?)\)/', $md, $md_a); // Markdown [text](href)
|
||||
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
|
||||
public static function markdown(string $md, bool $clear=false, bool $line=true): string
|
||||
{
|
||||
$parseDown = new Parsedown();
|
||||
|
||||
$text = self::htmlEscape($md_a_img[1][0]);
|
||||
$html = '<a href="' . self::htmlEscape($md_a_img[3][0]) . '"><img src="' . self::htmlEscape($md_a_img[2][0]) . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" /></a>';
|
||||
|
||||
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
|
||||
|
||||
$text = self::htmlEscape($md_img[1][0]);
|
||||
$html = '<img src="' . self::htmlEscape($md_img[2][0]) . '" class="img-responsive" alt="' . $text . '" title="' . $text . '" />';
|
||||
|
||||
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
|
||||
|
||||
$text = self::htmlEscape($md_a[1][0]);
|
||||
$html = '<a href="' . $md_a[2][0] . '">' . $text . '</a>';
|
||||
|
||||
} else { // text only
|
||||
|
||||
$text = self::htmlEscape($md);
|
||||
$html = $text;
|
||||
$parseDown
|
||||
->setBreaksEnabled(true)
|
||||
->setSafeMode(true)
|
||||
;
|
||||
|
||||
if ($line) {
|
||||
$html = $parseDown->line($md);
|
||||
} else {
|
||||
$md = preg_replace_callback(
|
||||
'#( ){2,}#',
|
||||
static function ($m) {
|
||||
return str_repeat(' ', strlen($m[0]));
|
||||
},
|
||||
$md
|
||||
);
|
||||
$html = $parseDown->text($md);
|
||||
}
|
||||
|
||||
$text = strip_tags($html);
|
||||
|
||||
return $clear ? $text : $html;
|
||||
}
|
||||
|
||||
public static function htmlEscape($html) {
|
||||
public static function htmlEscape(string $html): string {
|
||||
return htmlentities($html, ENT_HTML5 | ENT_QUOTES);
|
||||
}
|
||||
|
||||
public static function htmlMailEscape($html) {
|
||||
public static function htmlMailEscape(string $html): string
|
||||
{
|
||||
return htmlspecialchars($html, ENT_HTML5 | ENT_QUOTES);
|
||||
}
|
||||
|
||||
public static function csvEscape($text) {
|
||||
$escaped = str_replace('"', '""', $text);
|
||||
$escaped = str_replace("\r\n", '', $escaped);
|
||||
$escaped = str_replace("\n", '', $escaped);
|
||||
public static function csvEscape(string $text): string
|
||||
{
|
||||
$escaped = str_replace(['"', "\r\n", "\n"], ['""', '', ''], $text);
|
||||
$escaped = preg_replace("/^(=|\+|\-|\@)/", "'$1", $escaped);
|
||||
|
||||
return '"' . $escaped . '"';
|
||||
}
|
||||
|
||||
public static function cleanFilename($title) {
|
||||
public static function cleanFilename(string $title): string {
|
||||
$cleaned = preg_replace('[^a-zA-Z0-9._-]', '_', $title);
|
||||
$cleaned = preg_replace(' {2,}', ' ', $cleaned);
|
||||
|
||||
return $cleaned;
|
||||
return preg_replace(' {2,}', ' ', $cleaned);
|
||||
}
|
||||
|
||||
public static function fromPostOrDefault($postKey, $default = '') {
|
||||
public static function fromPostOrDefault(string $postKey, ?string $default = '') {
|
||||
return !empty($_POST[$postKey]) ? $_POST[$postKey] : $default;
|
||||
}
|
||||
|
||||
public static function base64url_encode($input) {
|
||||
public static function base64url_encode(string $input): string
|
||||
{
|
||||
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
public static function base64url_decode($input) {
|
||||
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT));
|
||||
public static function base64url_decode(string $input): string {
|
||||
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) % 4, '=', STR_PAD_RIGHT), true);
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +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)
|
||||
*/
|
||||
|
||||
// Fully qualified domain name of your webserver.
|
||||
// If this is unset or empty, the servername is determined automatically.
|
||||
// You *have to set this* if you are running Framedate behind a reverse proxy.
|
||||
// const APP_URL = '<www.mydomain.fr>';
|
||||
|
||||
// Application name
|
||||
const NOMAPPLICATION = '<Application name>';
|
||||
|
||||
// Database administrator email
|
||||
const ADRESSEMAILADMIN = '<email address>';
|
||||
|
||||
// Email for automatic responses (you should set it to "no-reply")
|
||||
const ADRESSEMAILREPONSEAUTO = '<no-reply@mydomain.com>';
|
||||
|
||||
// Database user
|
||||
const DB_USER= '<database user>';
|
||||
|
||||
// Database password
|
||||
const DB_PASSWORD = '<database password>';
|
||||
|
||||
// Database server name, leave empty to use a socket
|
||||
const DB_CONNECTION_STRING = 'mysql:host=<database host>;dbname=<database name>;port=<database port>';
|
||||
|
||||
// Name of the table that store migration script already executed
|
||||
const MIGRATION_TABLE = 'framadate_migration';
|
||||
|
||||
// Table name prefix
|
||||
const TABLENAME_PREFIX = 'fd_';
|
||||
|
||||
// Default Language
|
||||
const DEFAULT_LANGUAGE = 'fr';
|
||||
|
||||
// List of supported languages, fake constant as arrays can be used as constants only in PHP >=5.6
|
||||
$ALLOWED_LANGUAGES = [
|
||||
'fr' => 'Français',
|
||||
'en' => 'English',
|
||||
'oc' => 'Occitan',
|
||||
'es' => 'Español',
|
||||
'de' => 'Deutsch',
|
||||
'nl' => 'Dutch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
];
|
||||
|
||||
// Nom et emplacement du fichier image contenant le titre
|
||||
const IMAGE_TITRE = 'images/logo-framadate.png';
|
||||
|
||||
// Clean URLs, boolean
|
||||
const URL_PROPRE = false;
|
||||
|
||||
// Use REMOTE_USER data provided by web server
|
||||
const USE_REMOTE_USER = true;
|
||||
|
||||
// Path to the log file
|
||||
const LOG_FILE = 'admin/stdout.log';
|
||||
|
||||
// Days (after expiration date) before purge a poll
|
||||
const PURGE_DELAY = 60;
|
||||
|
||||
// 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' => true, // use email for polls creation/modification/responses notification
|
||||
/* home */
|
||||
'show_what_is_that' => true, // display "how to use" section
|
||||
'show_the_software' => true, // display technical information about the software
|
||||
'show_cultivate_your_garden' => true, // display "developpement and administration" information
|
||||
/* create_classic_poll.php / create_date_poll.php */
|
||||
'default_poll_duration' => 180, // default values for the new poll duration (number of days).
|
||||
/* create_classic_poll.php */
|
||||
'user_can_add_img_or_link' => true, // user can add link or URL when creating his poll.
|
||||
];
|
@ -18,10 +18,10 @@
|
||||
*/
|
||||
|
||||
// FRAMADATE version
|
||||
const VERSION = '1.0';
|
||||
const VERSION = '1.1.19';
|
||||
|
||||
// PHP Needed version
|
||||
const PHP_NEEDED_VERSION = '5.4.4';
|
||||
const PHP_NEEDED_VERSION = '7.3';
|
||||
|
||||
// Config constants
|
||||
const COMPILE_DIR = '/tpl_c/';
|
||||
@ -42,3 +42,6 @@ const SESSION_EDIT_LINK_TIME = "EditLinkMail";
|
||||
|
||||
// CSRF (300s = 5min)
|
||||
const TOKEN_TIME = 300;
|
||||
|
||||
const ICAL_ENDING = ".ics";
|
||||
const ICAL_PRODID = "-//Framasoft//Framadate//EN";
|
||||
|
@ -18,18 +18,20 @@
|
||||
*/
|
||||
|
||||
// Prepare I18N instance
|
||||
$i18n = \o80\i18n\I18N::instance();
|
||||
use o80\i18n\I18N;
|
||||
|
||||
$i18n = I18N::instance();
|
||||
$i18n->setDefaultLang(DEFAULT_LANGUAGE);
|
||||
$i18n->setPath(__DIR__ . '/../../locale');
|
||||
|
||||
// Change langauge when user asked for it
|
||||
if (isset($_POST['lang']) && is_string($_POST['lang']) && in_array($_POST['lang'], array_keys($ALLOWED_LANGUAGES))) {
|
||||
// Change language when user asked for it
|
||||
if (isset($_POST['lang']) && is_string($_POST['lang']) && array_key_exists($_POST['lang'], $ALLOWED_LANGUAGES)) {
|
||||
$_SESSION['lang'] = $_POST['lang'];
|
||||
}
|
||||
|
||||
/* <html lang="$locale"> */
|
||||
$i18n->get('', 'Something, just to load the dictionary');
|
||||
$locale = $i18n->getLoadedLang();
|
||||
$locale = str_replace('_', '-', $i18n->getLoadedLang());
|
||||
|
||||
/* Date Format */
|
||||
$date_format['txt_full'] = __('Date', 'FULL'); //summary in create_date_poll.php and removal date in choix_(date|autre).php
|
||||
@ -38,7 +40,7 @@ $date_format['txt_day'] = __('Date', 'DAY');
|
||||
$date_format['txt_date'] = __('Date', 'DATE');
|
||||
$date_format['txt_month_year'] = __('Date', 'MONTH_YEAR');
|
||||
$date_format['txt_datetime_short'] = __('Date', 'DATETIME');
|
||||
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { //%e can't be used on Windows platform, use %#d instead
|
||||
if (PHP_OS_FAMILY === 'Windows') { //%e can't be used on Windows platform, use %#d instead
|
||||
foreach ($date_format as $k => $v) {
|
||||
$date_format[$k] = preg_replace('#(?<!%)((?:%%)*)%e#', '\1%#d', $v); //replace %e by %#d for windows
|
||||
}
|
||||
|
@ -18,16 +18,17 @@
|
||||
*/
|
||||
use Framadate\FramaDB;
|
||||
use Framadate\Repositories\RepositoryFactory;
|
||||
use Framadate\Utils;
|
||||
|
||||
// Autoloading of dependencies with Composer
|
||||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../../vendor/o80/i18n/src/shortcuts.php';
|
||||
|
||||
if (session_id() == '') {
|
||||
if (session_id() === '') {
|
||||
session_start();
|
||||
}
|
||||
|
||||
if (ini_get('date.timezone') == '') {
|
||||
if (ini_get('date.timezone') === '') {
|
||||
date_default_timezone_set('Europe/Paris');
|
||||
}
|
||||
|
||||
@ -39,10 +40,17 @@ require_once __DIR__ . '/constants.php';
|
||||
if (is_file(CONF_FILENAME)) {
|
||||
@include_once __DIR__ . '/config.php';
|
||||
|
||||
try {
|
||||
// Connection to database
|
||||
$connect = new FramaDB(DB_CONNECTION_STRING, DB_USER, DB_PASSWORD);
|
||||
RepositoryFactory::init($connect);
|
||||
$err = 0;
|
||||
} catch (PDOException $e) {
|
||||
if ($_SERVER['SCRIPT_NAME'] !== '/maintenance.php') {
|
||||
header(('Location: ' . Utils::get_server_name() . 'maintenance.php'));
|
||||
exit;
|
||||
}
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
define('NOMAPPLICATION', 'Framadate');
|
||||
define('DEFAULT_LANGUAGE', 'fr');
|
||||
@ -55,6 +63,7 @@ if (is_file(CONF_FILENAME)) {
|
||||
'de' => 'Deutsch',
|
||||
'it' => 'Italiano',
|
||||
'br' => 'Brezhoneg',
|
||||
'ca' => 'Català'
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
|
||||
function php_version_id_to_string($versionId) {
|
||||
$major = substr($versionId, 0, 2);
|
||||
$minor = substr($versionId, 2, 2);
|
||||
$release = substr($versionId, 4, 2);
|
||||
return $major.'.'.$minor.'.'.$release;
|
||||
}
|
||||
function php_version_to_version_id($major, $minor, $release) {
|
||||
return ($major * 10000 +$minor * 100 + $release);
|
||||
}
|
||||
function php_string_to_version_id($version) {
|
||||
$version = explode('.', $version);
|
||||
return php_version_to_version_id($version[0], $version[1], $version[2]);
|
||||
}
|
||||
|
||||
|
||||
if (!defined('PHP_VERSION_ID')) {
|
||||
$version = explode('.',PHP_VERSION);
|
||||
define('PHP_VERSION_ID', php_version_to_version_id($version[0], $version[1], $version[2]));
|
||||
}
|
||||
if (PHP_VERSION_ID < 50207) { // This constants do not exists before 5.2.7
|
||||
define('PHP_MAJOR_VERSION', $version[0]);
|
||||
define('PHP_MINOR_VERSION', $version[1]);
|
||||
define('PHP_RELEASE_VERSION', $version[2]);
|
||||
}
|
||||
|
||||
?>
|
@ -30,24 +30,30 @@ $smarty->assign('SERVER_URL', Utils::get_server_name());
|
||||
$smarty->assign('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']);
|
||||
$smarty->assign('TITLE_IMAGE', IMAGE_TITRE);
|
||||
$smarty->assign('use_nav_js', strstr($_SERVER['SERVER_NAME'], 'framadate.org'));
|
||||
$smarty->assign('provide_fork_awesome', !isset($config['provide_fork_awesome']) || $config['provide_fork_awesome']);
|
||||
$smarty->assign('locale', $locale);
|
||||
$smarty->assign('langs', $ALLOWED_LANGUAGES);
|
||||
$smarty->assign('date_format', $date_format);
|
||||
if (isset($config['tracking_code'])) {
|
||||
$smarty->assign('tracking_code', $config['tracking_code']);
|
||||
}
|
||||
if (defined('FAVICON')) {
|
||||
$smarty->assign('favicon', FAVICON);
|
||||
}
|
||||
|
||||
// Dev Mode
|
||||
if (isset($_SERVER['FRAMADATE_DEVMODE']) && $_SERVER['FRAMADATE_DEVMODE']) {
|
||||
$smarty->force_compile = true;
|
||||
$smarty->compile_check = true;
|
||||
|
||||
} else {
|
||||
$smarty->force_compile = false;
|
||||
$smarty->compile_check = false;
|
||||
}
|
||||
|
||||
|
||||
function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
|
||||
function smarty_function_poll_url($params, Smarty_Internal_Template $template): string
|
||||
{
|
||||
$poll_id = filter_var($params['id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]);
|
||||
$admin = (isset($params['admin']) && $params['admin']) ? true : false;
|
||||
$admin = isset($params['admin']) && $params['admin'];
|
||||
$action = (isset($params['action']) && !empty($params['action'])) ? Utils::htmlEscape($params['action']) : false;
|
||||
$action_value = (isset($params['action_value']) && !empty($params['action_value'])) ? $params['action_value'] : false;
|
||||
$vote_unique_id = isset($params['vote_id']) ? filter_var($params['vote_id'], FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => POLL_REGEX]]) : '';
|
||||
@ -57,17 +63,66 @@ function smarty_function_poll_url($params, Smarty_Internal_Template $template) {
|
||||
return Utils::getUrlSondage($poll_id, $admin, $vote_unique_id, $action, $action_value);
|
||||
}
|
||||
|
||||
function smarty_modifier_markdown($md, $clear = false) {
|
||||
return Utils::markdown($md, $clear);
|
||||
function smarty_modifier_markdown(string $md, bool $clear = false, bool $inline=true): string
|
||||
{
|
||||
return Utils::markdown($md, $clear, $inline);
|
||||
}
|
||||
|
||||
function smarty_modifier_resource($link) {
|
||||
function smarty_modifier_resource(string $link): string
|
||||
{
|
||||
return Utils::get_server_name() . $link;
|
||||
}
|
||||
function smarty_modifier_addslashes_single_quote($string) {
|
||||
function smarty_modifier_addslashes_single_quote(string $string): string
|
||||
{
|
||||
return addcslashes($string, '\\\'');
|
||||
}
|
||||
|
||||
function smarty_modifier_html($html) {
|
||||
function smarty_modifier_addslashes(string $string): string
|
||||
{
|
||||
return addslashes($string);
|
||||
}
|
||||
|
||||
function smarty_modifier_html(?string $html): string
|
||||
{
|
||||
if (!$html) {
|
||||
return '';
|
||||
}
|
||||
return Utils::htmlEscape($html);
|
||||
}
|
||||
|
||||
function smarty_modifier_html_special_chars(string $html): string
|
||||
{
|
||||
return Utils::htmlMailEscape($html);
|
||||
}
|
||||
|
||||
function smarty_modifier_datepicker_path(string $lang): string
|
||||
{
|
||||
$i = 0;
|
||||
while (!is_file(path_for_datepicker_locale($lang)) && $i < 3) {
|
||||
$lang_arr = explode('-', $lang);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
$lang = $lang_arr[0];
|
||||
} else {
|
||||
$lang = 'en';
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
return 'js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
||||
|
||||
function smarty_modifier_locale_2_lang(string $locale): string
|
||||
{
|
||||
$lang_arr = explode('-', $locale);
|
||||
if ($lang_arr && count($lang_arr) > 1) {
|
||||
return $lang_arr[0];
|
||||
}
|
||||
return $locale;
|
||||
}
|
||||
|
||||
function path_for_datepicker_locale(string $lang): string
|
||||
{
|
||||
return __DIR__ . '/../../js/locales/bootstrap-datepicker.' . $lang . '.js';
|
||||
}
|
||||
|
||||
# Customization #4871 par Didier le 28/08/2021.
|
||||
$smarty->assign('VERSION',VERSION);
|
@ -1,13 +1,15 @@
|
||||
<?php
|
||||
namespace Framadate;
|
||||
|
||||
abstract class FramaTestCase extends \PHPUnit_Framework_TestCase {
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
protected function getTestResourcePath($resourcepath) {
|
||||
return __DIR__ . '/../resources/'.$resourcepath;
|
||||
abstract class FramaTestCase extends TestCase {
|
||||
protected function getTestResourcePath(string $resourcepath): string
|
||||
{
|
||||
return __DIR__ . '/../resources/' . $resourcepath;
|
||||
}
|
||||
|
||||
protected function readTestResource($resourcepath) {
|
||||
protected function readTestResource(string $resourcepath) {
|
||||
return file_get_contents($this->getTestResourcePath($resourcepath));
|
||||
}
|
||||
|
||||
@ -19,5 +21,4 @@ abstract class FramaTestCase extends \PHPUnit_Framework_TestCase {
|
||||
$params = array_slice(func_get_args(), 2); // get all the parameters after $methodName
|
||||
return $reflectionMethod->invokeArgs($object, $params);
|
||||
}
|
||||
|
||||
}
|
||||
|
34
app/tests/Framadate/Services/InputServiceUnitTest.php
Normal file
34
app/tests/Framadate/Services/InputServiceUnitTest.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace Framadate\Services;
|
||||
|
||||
use Framadate\FramaTestCase;
|
||||
|
||||
class InputServiceUnitTest extends FramaTestCase
|
||||
{
|
||||
public function liste_emails(): array
|
||||
{
|
||||
return [
|
||||
// valids addresses
|
||||
"valid address" => ["example@example.com", "example@example.com"],
|
||||
"local address" => ["test@localhost", "test@localhost"],
|
||||
"IP address" => ["ip.email@127.0.0.1", "ip.email@127.0.0.1"],
|
||||
"with spaces arround" => [" with@spaces ", "with@spaces"],
|
||||
"unicode caracters" => ["unicode.éà@idn-œ.com", "unicode.éà@idn-œ.com"],
|
||||
// invalids addresses
|
||||
"without domain" => ["without-domain", FALSE],
|
||||
"space inside" => ["example example@example.com", FALSE],
|
||||
"forbidden chars" => ["special_chars.@example.com", FALSE],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider liste_emails
|
||||
*/
|
||||
public function test_filterMail($email, $expected): void
|
||||
{
|
||||
$inputService = new InputService();
|
||||
$filtered = $inputService->filterMail($email);
|
||||
|
||||
$this->assertSame($expected, $filtered);
|
||||
}
|
||||
}
|
@ -4,12 +4,10 @@ namespace Framadate\Services;
|
||||
use Framadate\FramaTestCase;
|
||||
|
||||
class MailServiceUnitTest extends FramaTestCase {
|
||||
const MSG_KEY = '666';
|
||||
public const MSG_KEY = '666';
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
function should_send_a_2nd_mail_after_a_good_interval() {
|
||||
public function test_should_send_a_2nd_mail_after_a_good_interval(): void
|
||||
{
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time() - 1000];
|
||||
@ -18,13 +16,11 @@ class MailServiceUnitTest extends FramaTestCase {
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertEquals(true, $canSendMsg);
|
||||
$this->assertTrue($canSendMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
function should_not_send_2_mails_in_a_short_interval() {
|
||||
public function test_should_not_send_2_mails_in_a_short_interval(): void
|
||||
{
|
||||
// Given
|
||||
$mailService = new MailService(true);
|
||||
$_SESSION[MailService::MAILSERVICE_KEY] = [self::MSG_KEY => time()];
|
||||
@ -33,7 +29,6 @@ class MailServiceUnitTest extends FramaTestCase {
|
||||
$canSendMsg = $mailService->canSendMsg(self::MSG_KEY);
|
||||
|
||||
// Then
|
||||
$this->assertEquals(false, $canSendMsg);
|
||||
$this->assertFalse($canSendMsg);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
$loader = require __DIR__ . '/../../vendor/autoload.php';
|
||||
$loader->addPsr4('Framadate\\', __DIR__.'/Framadate');
|
||||
$loader->addPsr4('Framadate\\', __DIR__ . '/Framadate');
|
||||
|
25
bandeaux.php
25
bandeaux.php
@ -24,22 +24,22 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
function bandeau_titre($titre)
|
||||
{
|
||||
global $ALLOWED_LANGUAGES;
|
||||
$img = ( IMAGE_TITRE ) ? '<img src="'. Utils::get_server_name(). IMAGE_TITRE. '" alt="'.NOMAPPLICATION.'" class="img-responsive">' : '';
|
||||
$img = ( IMAGE_TITRE ) ? '<img src="' . Utils::get_server_name() . IMAGE_TITRE . '" alt="' . NOMAPPLICATION . '" class="img-responsive">' : '';
|
||||
echo '
|
||||
<header role="banner">';
|
||||
if(count($ALLOWED_LANGUAGES) > 1){
|
||||
echo '<form method="post" action="" class="hidden-print">
|
||||
echo '<form method="post" class="hidden-print">
|
||||
<div class="input-group input-group-sm pull-right col-md-2 col-xs-4">
|
||||
<select name="lang" class="form-control" title="'. __('Language selector', 'Select the language') .'" >' . liste_lang() . '</select>
|
||||
<select name="lang" class="form-control" title="' . __('Language selector', 'Select the language') . '" >' . liste_lang() . '</select>
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn-default btn-sm" title="'. __('Language selector', 'Change the language') .'">OK</button>
|
||||
<button type="submit" class="btn btn-default btn-sm" title="' . __('Language selector', 'Change the language') . '">OK</button>
|
||||
</span>
|
||||
</div>
|
||||
</form>';
|
||||
}
|
||||
echo '
|
||||
<h1><a href="' . Utils::get_server_name() . '" title="' . __('Generic', 'Home') . ' - ' . NOMAPPLICATION . '">' . $img . '</a></h1>
|
||||
<h2 class="lead"><i>'. $titre .'</i></h2>
|
||||
<h2 class="lead"><i>' . $titre . '</i></h2>
|
||||
<hr class="trait" role="presentation" />
|
||||
</header>
|
||||
<main role="main">';
|
||||
@ -47,25 +47,24 @@ function bandeau_titre($titre)
|
||||
global $connect;
|
||||
$tables = $connect->allTables();
|
||||
$diff = array_diff([Utils::table('comment'), Utils::table('poll'), Utils::table('slot'), Utils::table('vote')], $tables);
|
||||
if (0 != count($diff)) {
|
||||
echo '<div class="alert alert-danger">'. __('Error', 'Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') .'</div>';
|
||||
if (0 !== count($diff)) {
|
||||
echo '<div class="alert alert-danger">' . __('Error', 'Framadate is not properly installed, please check the "INSTALL" to setup the database before continuing.') . '</div>';
|
||||
bandeau_pied();
|
||||
die();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function liste_lang()
|
||||
function liste_lang(): string
|
||||
{
|
||||
global $ALLOWED_LANGUAGES; global $locale;
|
||||
|
||||
$str = '';
|
||||
|
||||
foreach ($ALLOWED_LANGUAGES as $k => $v ) {
|
||||
if (substr($k,0,2)==$locale) {
|
||||
$str .= '<option lang="'.substr($k,0,2).'" selected value="' . $k . '">' . $v . '</option>' . "\n" ;
|
||||
if (strpos($k, $locale) === 0) {
|
||||
$str .= '<option lang="' . substr($k,0,2) . '" selected value="' . $k . '">' . $v . '</option>' . "\n" ;
|
||||
} else {
|
||||
$str .= '<option lang="'.substr($k,0,2).'" value="' . $k . '">' . $v . '</option>' . "\n" ;
|
||||
$str .= '<option lang="' . substr($k,0,2) . '" value="' . $k . '">' . $v . '</option>' . "\n" ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,5 +77,5 @@ function bandeau_pied()
|
||||
</main>
|
||||
</div> <!-- .container -->
|
||||
</body>
|
||||
</html>'."\n";
|
||||
</html>' . "\n";
|
||||
}
|
||||
|
@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
$goodLang = $_GET['good'];
|
||||
$otherLang = $_GET['other'];
|
||||
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
|
||||
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true);
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
$other = json_decode(file_get_contents(__DIR__ . '/locale/' . $otherLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
foreach ($good as $sectionName => $section) {
|
||||
foreach ($section as $key => $value) {
|
||||
@ -19,15 +19,15 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode($good, JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
|
||||
echo json_encode($good, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT | ~(JSON_ERROR_UTF8 | JSON_HEX_QUOT | JSON_HEX_APOS));
|
||||
|
||||
function getFromOther($other, $goodKey, $default, $otherLang) {
|
||||
function getFromOther($other, $goodKey, $default, $otherLang): string {
|
||||
foreach ($other as $sectionName => $section) {
|
||||
foreach ($section as $key => $value) {
|
||||
if (
|
||||
strtolower($key) === strtolower($goodKey) ||
|
||||
stripos($key, strtolower($goodKey)) === 0 ||
|
||||
strtolower(trim($key)) === strtolower($goodKey) ||
|
||||
strtolower(substr($key, 0, strlen($key) - 1)) === strtolower($goodKey) ||
|
||||
strtolower(trim(substr(trim($key), 0, strlen($key) - 1))) === strtolower($goodKey)
|
||||
) {
|
||||
return $value;
|
||||
|
13
compare.php
13
compare.php
@ -10,8 +10,8 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
$goodLang = $_GET['good'];
|
||||
$testLang = $_GET['test'];
|
||||
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true);
|
||||
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true);
|
||||
$good = json_decode(file_get_contents(__DIR__ . '/locale/' . $goodLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
$test = json_decode(file_get_contents(__DIR__ . '/locale/' . $testLang . '.json'), true, 512, JSON_THROW_ON_ERROR);
|
||||
|
||||
$diffSection = false;
|
||||
|
||||
@ -28,15 +28,14 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$diffSection and array_keys($good)!=array_keys($test)) {
|
||||
if (!$diffSection and array_keys($good)!==array_keys($test)) {
|
||||
var_dump(array_keys($good));
|
||||
var_dump(array_keys($test));
|
||||
} else {
|
||||
echo 'All sections are in two langs.' . "\n";
|
||||
}
|
||||
|
||||
|
||||
$diff = array();
|
||||
$diff = [];
|
||||
|
||||
foreach ($good as $sectionName => $section) {
|
||||
$diffSection = false;
|
||||
@ -47,8 +46,8 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$diffSection and array_keys($good[$sectionName]) != array_keys($test[$sectionName])) {
|
||||
$diff[$sectionName]['order_good'] = array_keys($good[$sectionName]);
|
||||
if (!$diffSection and array_keys($section) !== array_keys($test[$sectionName])) {
|
||||
$diff[$sectionName]['order_good'] = array_keys($section);
|
||||
$diff[$sectionName]['order_test'] = array_keys($test[$sectionName]);
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,96 @@
|
||||
{
|
||||
"name": "framasoft/framadate",
|
||||
"description": "Application to facilitate the schedule of events or classic polls",
|
||||
"homepage": "https://framadate.org/",
|
||||
"keywords": [
|
||||
"poll"
|
||||
"poll",
|
||||
"framadate"
|
||||
],
|
||||
"version": "0.9.0",
|
||||
"license": "CECILL-B",
|
||||
|
||||
"type": "project",
|
||||
|
||||
"support": {
|
||||
"issues": "https://framagit.org/framasoft/framadate/issues"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas CITHAREL",
|
||||
"email": "tcit@tcit.fr",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "JosephK",
|
||||
"email": "joseph@framasoft.org",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Olivier PEREZ",
|
||||
"email": "olivier@olivierperez.fr",
|
||||
"role": "Former maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Antonin MURTIN",
|
||||
"email": "antonin.murtin@gmail.com",
|
||||
"role": "Former developper"
|
||||
},
|
||||
{
|
||||
"name": "Simon LEBLANC",
|
||||
"role": "Former developper",
|
||||
"email": "contact@leblanc-simon.eu"
|
||||
},
|
||||
{
|
||||
"name": "Pierre-Yves GOSSET",
|
||||
"role": "Former developper",
|
||||
"email": "pyg@framasoft.org"
|
||||
},
|
||||
{
|
||||
"name": "Guilhem BORGHESI",
|
||||
"role": "Studs developper",
|
||||
"email": "borghesi@unistra.fr"
|
||||
},
|
||||
{
|
||||
"name": "Raphaël DROZ",
|
||||
"role": "Studs developper",
|
||||
"email": "raphael.droz@gmail.com"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||
"cs:fix": "php-cs-fixer fix",
|
||||
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './build/*' -not -path './tests/integration/vendor/*' -print0 | xargs -0 -n1 php -l"
|
||||
},
|
||||
"require": {
|
||||
"smarty/smarty": "3.1.21",
|
||||
"php": ">=7.3.0",
|
||||
"ext-pdo": "*",
|
||||
"ext-json": "*",
|
||||
"smarty/smarty": "^4.0",
|
||||
"o80/i18n": "dev-develop",
|
||||
"phpmailer/phpmailer": "~5.2",
|
||||
"ircmaxell/password-compat": "dev-master"
|
||||
"phpmailer/phpmailer": "~6.2",
|
||||
"ircmaxell/password-compat": "dev-master",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"erusev/parsedown": "^1.7",
|
||||
"egulias/email-validator": "^3.1",
|
||||
"sabre/vobject": "~4.1"
|
||||
},
|
||||
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.5"
|
||||
"phpunit/phpunit": "^9",
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"vimeo/psalm": "^4.15"
|
||||
},
|
||||
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://framagit.org/framasoft/framadate/o80-i18n"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Framadate\\": "app/classes/Framadate/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
5389
composer.lock
generated
5389
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -16,21 +16,25 @@
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Utils;
|
||||
use Framadate\Choice;
|
||||
use Framadate\Services\InputService;
|
||||
use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
/* Service */
|
||||
/*---------*/
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp']);
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$purgeService = new PurgeService($logService);
|
||||
$sessionService = new SessionService();
|
||||
$inputService = new InputService();
|
||||
|
||||
if (is_file('bandeaux_local.php')) {
|
||||
include_once('bandeaux_local.php');
|
||||
@ -38,71 +42,49 @@ if (is_file('bandeaux_local.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)) {
|
||||
$form = unserialize($_SESSION['form']);
|
||||
|
||||
// Step 1/4 : error if $_SESSION from info_sondage are not valid
|
||||
if (empty($form->title) || empty($form->admin_name) || ($config['use_smtp'] && empty($form->admin_mail))) {
|
||||
$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;
|
||||
|
||||
} else {
|
||||
// Min/Max archive date
|
||||
$min_expiry_time = $pollService->minExpiryDate();
|
||||
$max_expiry_time = $pollService->maxExpiryDate();
|
||||
|
||||
// The poll format is AUTRE (other)
|
||||
if ($_SESSION['form']->format !== 'A') {
|
||||
$_SESSION['form']->format = 'A';
|
||||
$_SESSION['form']->clearChoices();
|
||||
}
|
||||
// The poll format is other (A) if we are in this file
|
||||
if (!isset($form->format)) {
|
||||
$form->format = 'A';
|
||||
}
|
||||
// If we come from another format, we need to clear choices
|
||||
if (isset($form->format) && $form->format !== 'A') {
|
||||
$form->format = 'A';
|
||||
$form->clearChoices();
|
||||
}
|
||||
|
||||
// Step 4 : Data prepare before insert in DB
|
||||
if (isset($_POST['confirmation'])) {
|
||||
|
||||
// Define expiration date
|
||||
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
|
||||
|
||||
if (!empty($enddate)) {
|
||||
$registredate = explode('/', $enddate);
|
||||
|
||||
if (is_array($registredate) && count($registredate) == 3) {
|
||||
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
|
||||
|
||||
if ($time < $min_expiry_time) {
|
||||
$_SESSION['form']->end_date = $min_expiry_time;
|
||||
} elseif ($max_expiry_time < $time) {
|
||||
$_SESSION['form']->end_date = $max_expiry_time;
|
||||
} else {
|
||||
$_SESSION['form']->end_date = $time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($_SESSION['form']->end_date)) {
|
||||
// By default, expiration date is 6 months after last day
|
||||
$_SESSION['form']->end_date = $max_expiry_time;
|
||||
}
|
||||
$expiration_date = $inputService->parseDate($_POST['enddate']);
|
||||
$form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
|
||||
|
||||
// Insert poll in database
|
||||
$ids = $pollService->createPoll($_SESSION['form']);
|
||||
$ids = $pollService->createPoll($form);
|
||||
$poll_id = $ids[0];
|
||||
$admin_poll_id = $ids[1];
|
||||
|
||||
|
||||
// Send confirmation by mail if enabled
|
||||
if ($config['use_smtp'] === true) {
|
||||
$message = __('Mail', "This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.");
|
||||
$message .= '<br/><br/>';
|
||||
$message .= Utils::htmlMailEscape($_SESSION['form']->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlMailEscape($_SESSION['form']->title) . '".<br/>';
|
||||
$message .= Utils::htmlMailEscape($form->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlMailEscape($form->title) . '".<br/>';
|
||||
$message .= sprintf(__('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($poll_id));
|
||||
|
||||
$message_admin = __('Mail', "This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above");
|
||||
$message_admin .= sprintf(' :<br/><br/><a href="%1$s">%1$s</a>', Utils::getUrlSondage($admin_poll_id, true));
|
||||
|
||||
if ($mailService->isValidEmail($_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);
|
||||
if ($mailService->isValidEmail($form->admin_mail)) {
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message_admin);
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,62 +94,56 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
|
||||
// Delete old polls
|
||||
$purgeService->purgeOldPolls();
|
||||
|
||||
// creation message
|
||||
$sessionService->set("Framadate", "messagePollCreated", TRUE);
|
||||
|
||||
// Redirect to poll administration
|
||||
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
|
||||
exit;
|
||||
|
||||
} // Step 3/4 : Confirm poll creation and choose a removal date
|
||||
else if (isset($_POST['fin_sondage_autre'])) {
|
||||
|
||||
// Store choices in $_SESSION
|
||||
if (isset($_POST['choices'])) {
|
||||
$_SESSION['form']->clearChoices();
|
||||
$form->clearChoices();
|
||||
foreach ($_POST['choices'] as $c) {
|
||||
if (!empty($c)) {
|
||||
$c = strip_tags($c);
|
||||
$choice = new Choice($c);
|
||||
$_SESSION['form']->addChoice($choice);
|
||||
$form->addChoice($choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expiration date is initialised with config parameter. Value will be modified in step 4 if user has defined an other date
|
||||
$_SESSION['form']->end_date = $max_expiry_time;
|
||||
$form->end_date = $pollService->maxExpiryDate()->format('Y-m-d H:i:s');
|
||||
|
||||
// Summary
|
||||
$summary = '<ol>';
|
||||
foreach ($_SESSION['form']->getChoices() as $i=>$choice) {
|
||||
|
||||
foreach ($form->getChoices() as $i=>$choice) {
|
||||
preg_match_all('/\[!\[(.*?)\]\((.*?)\)\]\((.*?)\)/', $choice->getName(), $md_a_img); // Markdown [![alt](src)](href)
|
||||
preg_match_all('/!\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_img); // Markdown ![alt](src)
|
||||
preg_match_all('/\[(.*?)\]\((.*?)\)/', $choice->getName(), $md_a); // Markdown [text](href)
|
||||
if (isset($md_a_img[2][0]) && $md_a_img[2][0] != '' && isset($md_a_img[3][0]) && $md_a_img[3][0] != '') { // [![alt](src)](href)
|
||||
|
||||
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] != '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
if (isset($md_a_img[2][0], $md_a_img[3][0]) && $md_a_img[2][0] !== '' && $md_a_img[3][0] !== '') { // [![alt](src)](href)
|
||||
$li_subject_text = (isset($md_a_img[1][0]) && $md_a_img[1][0] !== '') ? stripslashes($md_a_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
$li_subject_html = '<a href="' . $md_a_img[3][0] . '"><img src="' . $md_a_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" /></a>';
|
||||
|
||||
} elseif (isset($md_img[2][0]) && $md_img[2][0] != '') { // ![alt](src)
|
||||
|
||||
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] != '') ? stripslashes($md_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
} elseif (isset($md_img[2][0]) && $md_img[2][0] !== '') { // ![alt](src)
|
||||
$li_subject_text = (isset($md_img[1][0]) && $md_img[1][0] !== '') ? stripslashes($md_img[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
$li_subject_html = '<img src="' . $md_img[2][0] . '" class="img-responsive" alt="' . $li_subject_text . '" />';
|
||||
|
||||
} elseif (isset($md_a[2][0]) && $md_a[2][0] != '') { // [text](href)
|
||||
|
||||
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] != '') ? stripslashes($md_a[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
} elseif (isset($md_a[2][0]) && $md_a[2][0] !== '') { // [text](href)
|
||||
$li_subject_text = (isset($md_a[1][0]) && $md_a[1][0] !== '') ? stripslashes($md_a[1][0]) : __('Generic', 'Choice') . ' ' . ($i + 1);
|
||||
$li_subject_html = '<a href="' . $md_a[2][0] . '">' . $li_subject_text . '</a>';
|
||||
|
||||
} else { // text only
|
||||
|
||||
$li_subject_text = stripslashes($choice->getName());
|
||||
$li_subject_html = $li_subject_text;
|
||||
|
||||
}
|
||||
|
||||
$summary .= '<li>' . $li_subject_html . '</li>' . "\n";
|
||||
}
|
||||
$summary .= '</ol>';
|
||||
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); //textual date
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); //textual date
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
|
||||
$smarty->assign('summary', $summary);
|
||||
@ -183,7 +159,7 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
|
||||
bandeau_titre(__('Step 2 classic', 'Poll subjects (2 on 3)'));
|
||||
|
||||
echo '
|
||||
<form name="formulaire" action="' . Utils::get_server_name() . 'create_classic_poll.php" method="POST" class="form-horizontal" role="form">
|
||||
<form name="formulaire" action="' . Utils::get_server_name() . 'create_classic_poll.php" method="POST" class="form-horizontal">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">';
|
||||
echo '
|
||||
@ -196,10 +172,10 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
|
||||
echo ' </div>' . "\n";
|
||||
|
||||
// Fields choices : 5 by default
|
||||
$choices = $_SESSION['form']->getChoices();
|
||||
$choices = $form->getChoices();
|
||||
$nb_choices = max(count($choices), 5);
|
||||
for ($i = 0; $i < $nb_choices; $i++) {
|
||||
$choice = isset($choices[$i]) ? $choices[$i] : new Choice();
|
||||
$choice = $choices[$i] ?? new Choice();
|
||||
echo '
|
||||
<div class="form-group choice-field">
|
||||
<label for="choice' . $i . '" class="col-sm-2 control-label">' . __('Generic', 'Choice') . ' ' . ($i + 1) . '</label>
|
||||
@ -257,11 +233,9 @@ if (empty($_SESSION['form']->title) || empty($_SESSION['form']->admin_name) || (
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="js/app/framadatepicker.js"></script>
|
||||
<script type="text/javascript" src="js/app/classic_poll.js"></script>
|
||||
<script src="js/app/framadatepicker.js"></script>
|
||||
<script src="js/app/classic_poll.js"></script>
|
||||
' . "\n";
|
||||
|
||||
bandeau_pied();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft https://framagit.org/framasoft/framadate/)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
@ -14,7 +14,7 @@
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft https://git.framasoft.org/framasoft/framadate/)
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft https://framagit.org/framasoft/framadate/)
|
||||
*/
|
||||
use Framadate\Choice;
|
||||
use Framadate\Services\InputService;
|
||||
@ -22,6 +22,7 @@ use Framadate\Services\LogService;
|
||||
use Framadate\Services\MailService;
|
||||
use Framadate\Services\PollService;
|
||||
use Framadate\Services\PurgeService;
|
||||
use Framadate\Services\SessionService;
|
||||
use Framadate\Utils;
|
||||
|
||||
include_once __DIR__ . '/app/inc/init.php';
|
||||
@ -29,30 +30,33 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
/* Service */
|
||||
/*---------*/
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp']);
|
||||
$purgeService = new PurgeService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
$purgeService = new PurgeService($logService);
|
||||
$inputService = new InputService();
|
||||
$sessionService = new SessionService();
|
||||
|
||||
if (is_readable('bandeaux_local.php')) {
|
||||
include_once('bandeaux_local.php');
|
||||
}
|
||||
|
||||
// Min/Max archive date
|
||||
$min_expiry_time = $pollService->minExpiryDate();
|
||||
$max_expiry_time = $pollService->maxExpiryDate();
|
||||
$form = unserialize($_SESSION['form']);
|
||||
|
||||
// The poll format is DATE
|
||||
if ($_SESSION['form']->format !== 'D') {
|
||||
$_SESSION['form']->format = 'D';
|
||||
$_SESSION['form']->clearChoices();
|
||||
// The poll format is DATE if we are in this file
|
||||
if (!isset($form->format)) {
|
||||
$form->format = 'D';
|
||||
}
|
||||
// If we come from another format, we need to clear choices
|
||||
if (isset($form->format) && $form->format !== 'D') {
|
||||
$form->format = 'D';
|
||||
$form->clearChoices();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['form']->title) || !isset($_SESSION['form']->admin_name) || ($config['use_smtp'] && !isset($_SESSION['form']->admin_mail))) {
|
||||
if (!isset($form->title, $form->admin_name) || ($config['use_smtp'] && !isset($form->admin_mail))) {
|
||||
$step = 1;
|
||||
} else if (!empty($_POST['confirmation'])) {
|
||||
$step = 4;
|
||||
} else if (empty($_POST['choixheures']) || isset($_SESSION['form']->totalchoixjour)) {
|
||||
} else if (empty($_POST['choixheures']) || isset($form->totalchoixjour)) {
|
||||
$step = 2;
|
||||
} else {
|
||||
$step = 3;
|
||||
@ -66,43 +70,44 @@ switch ($step) {
|
||||
$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('choices', $form->getChoices());
|
||||
$smarty->assign('error', null);
|
||||
|
||||
$smarty->display('create_date_poll_step_2.tpl');
|
||||
exit;
|
||||
|
||||
|
||||
case 3:
|
||||
// Step 3/4 : Confirm poll creation
|
||||
|
||||
// Handle Step2 submission
|
||||
if (!empty($_POST['days'])) {
|
||||
// Remove empty dates
|
||||
$_POST['days'] = array_filter($_POST['days'], function ($d) {
|
||||
$_POST['days'] = array_filter($_POST['days'], static function ($d) {
|
||||
return !empty($d);
|
||||
});
|
||||
|
||||
@ -110,7 +115,7 @@ switch ($step) {
|
||||
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('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,10 +123,10 @@ switch ($step) {
|
||||
}
|
||||
|
||||
// Clear previous choices
|
||||
$_SESSION['form']->clearChoices();
|
||||
$form->clearChoices();
|
||||
|
||||
// Reorder moments to deal with suppressed dates
|
||||
$moments = array();
|
||||
$moments = [];
|
||||
$i = 0;
|
||||
while(count($moments) < count($_POST['days'])) {
|
||||
if (!empty($_POST['horaires' . $i])) {
|
||||
@ -130,8 +135,7 @@ switch ($step) {
|
||||
$i++;
|
||||
}
|
||||
|
||||
|
||||
for ($i = 0; $i < count($_POST['days']); $i++) {
|
||||
for ($i = 0, $iMax = count($_POST['days']); $i < $iMax; $i++) {
|
||||
$day = $_POST['days'][$i];
|
||||
|
||||
if (!empty($day)) {
|
||||
@ -139,22 +143,22 @@ switch ($step) {
|
||||
$date = DateTime::createFromFormat(__('Date', 'datetime_parseformat'), $_POST['days'][$i])->setTime(0, 0, 0);
|
||||
$time = $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++) {
|
||||
for ($j = 0, $jMax = count($schedules); $j < $jMax; $j++) {
|
||||
if (!empty($schedules[$j])) {
|
||||
$choice->addSlot(strip_tags($schedules[$j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$_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());
|
||||
$first = true;
|
||||
@ -167,7 +171,9 @@ switch ($step) {
|
||||
}
|
||||
$summary .= '</ul>';
|
||||
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $max_expiry_time)); // textual date
|
||||
$end_date_str = utf8_encode(strftime($date_format['txt_date'], $pollService->maxExpiryDate()->getTimestamp())); // textual date
|
||||
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
$smarty->assign('title', __('Step 3', 'Removal date and confirmation (3 on 3)'));
|
||||
$smarty->assign('summary', $summary);
|
||||
@ -178,46 +184,23 @@ switch ($step) {
|
||||
$smarty->display('create_classic_poll_step3.tpl');
|
||||
exit;
|
||||
|
||||
|
||||
case 4:
|
||||
// Step 4 : Data prepare before insert in DB
|
||||
|
||||
// Define expiration date
|
||||
$enddate = filter_input(INPUT_POST, 'enddate', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '#^[0-9]{2}/[0-9]{2}/[0-9]{4}$#']]);
|
||||
|
||||
|
||||
if (!empty($enddate)) {
|
||||
$registredate = explode('/', $enddate);
|
||||
|
||||
if (is_array($registredate) && count($registredate) == 3) {
|
||||
$time = mktime(0, 0, 0, $registredate[1], $registredate[0], $registredate[2]);
|
||||
|
||||
if ($time < $min_expiry_time) {
|
||||
$_SESSION['form']->end_date = $min_expiry_time;
|
||||
} elseif ($max_expiry_time < $time) {
|
||||
$_SESSION['form']->end_date = $max_expiry_time;
|
||||
} else {
|
||||
$_SESSION['form']->end_date = $time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($_SESSION['form']->end_date)) {
|
||||
// By default, expiration date is 6 months after last day
|
||||
$_SESSION['form']->end_date = $max_expiry_time;
|
||||
}
|
||||
$expiration_date = $inputService->parseDate($_POST['enddate']);
|
||||
$form->end_date = $inputService->validateDate($expiration_date, $pollService->minExpiryDate(), $pollService->maxExpiryDate())->getTimestamp();
|
||||
|
||||
// Insert poll in database
|
||||
$ids = $pollService->createPoll($_SESSION['form']);
|
||||
$ids = $pollService->createPoll($form);
|
||||
$poll_id = $ids[0];
|
||||
$admin_poll_id = $ids[1];
|
||||
|
||||
|
||||
// Send confirmation by mail if enabled
|
||||
if ($config['use_smtp'] === true) {
|
||||
$message = __('Mail', "This is the message you have to send to the people you want to poll. \nNow, you have to send this message to everyone you want to poll.");
|
||||
$message .= '<br/><br/>';
|
||||
$message .= Utils::htmlEscape($_SESSION['form']->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlEscape($_SESSION['form']->title) . '".<br/>';
|
||||
$message .= Utils::htmlEscape($form->admin_name) . ' ' . __('Mail', 'hast just created a poll called') . ' : "' . Utils::htmlEscape($form->title) . '".<br/>';
|
||||
$message .= __('Mail', 'Thanks for filling the poll at the link above') . ' :<br/><br/><a href="%1$s">%1$s</a>';
|
||||
|
||||
$message_admin = __('Mail', "This message should NOT be sent to the polled people. It is private for the poll's creator.\n\nYou can now modify it at the link above");
|
||||
@ -226,9 +209,9 @@ switch ($step) {
|
||||
$message = sprintf($message, Utils::getUrlSondage($poll_id));
|
||||
$message_admin = sprintf($message_admin, Utils::getUrlSondage($admin_poll_id, true));
|
||||
|
||||
if ($mailService->isValidEmail($_SESSION['form']->admin_mail)) {
|
||||
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message_admin);
|
||||
$mailService->send($_SESSION['form']->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($_SESSION['form']->title), $message);
|
||||
if ($mailService->isValidEmail($form->admin_mail)) {
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'Author\'s message') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message_admin);
|
||||
$mailService->send($form->admin_mail, '[' . NOMAPPLICATION . '][' . __('Mail', 'For sending to the polled users') . '] ' . __('Generic', 'Poll') . ': ' . Utils::htmlEscape($form->title), $message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,6 +221,9 @@ switch ($step) {
|
||||
// Delete old polls
|
||||
$purgeService->purgeOldPolls();
|
||||
|
||||
// creation message
|
||||
$sessionService->set("Framadate", "messagePollCreated", TRUE);
|
||||
|
||||
// Redirect to poll administration
|
||||
header('Location:' . Utils::getUrlSondage($admin_poll_id, true));
|
||||
exit;
|
||||
|
177
create_poll.php
177
create_poll.php
@ -27,7 +27,6 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
|
||||
const GO_TO_STEP_2 = 'gotostep2';
|
||||
|
||||
|
||||
/* Services */
|
||||
/*----------*/
|
||||
|
||||
@ -36,38 +35,43 @@ $pollRepository = RepositoryFactory::pollRepository();
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
$form = isset($_SESSION['form']) ? unserialize($_SESSION['form']) : null;
|
||||
|
||||
if (!isset($_SESSION['form'])) {
|
||||
$_SESSION['form'] = new Form();
|
||||
if ($form === null && !($form instanceof Form)) {
|
||||
$form = new Form();
|
||||
}
|
||||
|
||||
// Type de sondage
|
||||
if (isset($_GET['type']) && $_GET['type'] == 'date' ||
|
||||
isset($_POST['type']) && $_POST['type'] == 'date'
|
||||
if ((isset($_GET['type']) && $_GET['type'] === 'date') ||
|
||||
(isset($_POST['type']) && $_POST['type'] === 'date')
|
||||
) {
|
||||
$poll_type = 'date';
|
||||
$_SESSION['form']->choix_sondage = $poll_type;
|
||||
$form->choix_sondage = $poll_type;
|
||||
} else {
|
||||
$poll_type = 'classic';
|
||||
$_SESSION['form']->choix_sondage = $poll_type;
|
||||
$form->choix_sondage = $poll_type;
|
||||
}
|
||||
|
||||
// We clean the data
|
||||
$goToStep2 = filter_input(INPUT_POST, GO_TO_STEP_2, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^(date|classic)$/']]);
|
||||
if ($goToStep2) {
|
||||
$title = $inputService->filterTitle($_POST['title']);
|
||||
$use_customized_url = isset($_POST['use_customized_url']) ? $inputService->filterBoolean($_POST['use_customized_url']) : false;
|
||||
$customized_url = $use_customized_url == true ? $inputService->filterId($_POST['customized_url']) : null;
|
||||
$name = $inputService->filterName($_POST['name']);
|
||||
$mail = $config['use_smtp'] == true ? $inputService->filterMail($_POST['mail']) : null;
|
||||
|
||||
$use_ValueMax = isset($_POST['use_ValueMax']) && $inputService->filterBoolean($_POST['use_ValueMax']);
|
||||
$ValueMax = $use_ValueMax === true ? $inputService->filterValueMax($_POST['ValueMax']) : null;
|
||||
|
||||
$use_customized_url = isset($_POST['use_customized_url']) && $inputService->filterBoolean($_POST['use_customized_url']);
|
||||
$customized_url = $use_customized_url === true ? $inputService->filterId($_POST['customized_url']) : null;
|
||||
$name = mb_substr($inputService->filterName($_POST['name']), 0, 32);
|
||||
$mail = $config['use_smtp'] === true ? $inputService->filterMail($_POST['mail']) : null;
|
||||
$description = $inputService->filterDescription($_POST['description']);
|
||||
$editable = $inputService->filterEditable($_POST['editable']);
|
||||
$receiveNewVotes = isset($_POST['receiveNewVotes']) ? $inputService->filterBoolean($_POST['receiveNewVotes']) : false;
|
||||
$receiveNewComments = isset($_POST['receiveNewComments']) ? $inputService->filterBoolean($_POST['receiveNewComments']) : false;
|
||||
$hidden = isset($_POST['hidden']) ? $inputService->filterBoolean($_POST['hidden']) : false;
|
||||
$receiveNewVotes = isset($_POST['receiveNewVotes']) && $inputService->filterBoolean($_POST['receiveNewVotes']);
|
||||
$receiveNewComments = isset($_POST['receiveNewComments']) && $inputService->filterBoolean($_POST['receiveNewComments']);
|
||||
$hidden = isset($_POST['hidden']) && $inputService->filterBoolean($_POST['hidden']);
|
||||
$use_password = filter_input(INPUT_POST, 'use_password', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
$password_repeat = isset($_POST['password_repeat']) ? $_POST['password_repeat'] : null;
|
||||
$password = $_POST['password'] ?? null;
|
||||
$password_repeat = $_POST['password_repeat'] ?? null;
|
||||
$results_publicly_visible = filter_input(INPUT_POST, 'results_publicly_visible', FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => BOOLEAN_REGEX]]);
|
||||
|
||||
// On initialise également les autres variables
|
||||
@ -78,26 +82,26 @@ if ($goToStep2) {
|
||||
$error_on_password = false;
|
||||
$error_on_password_repeat = false;
|
||||
$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']->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->use_password = ($use_password !== null);
|
||||
$form->results_publicly_visible = ($results_publicly_visible !== null);
|
||||
|
||||
|
||||
if ($config['use_smtp'] == true) {
|
||||
if (empty($mail)) {
|
||||
if ($config['use_smtp'] === true && empty($mail)) {
|
||||
$error_on_mail = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($title !== $_POST['title']) {
|
||||
$error_on_title = true;
|
||||
@ -109,9 +113,16 @@ if ($goToStep2) {
|
||||
} else if ($pollRepository->existsById($customized_url)) {
|
||||
$error_on_customized_url = true;
|
||||
$error_on_customized_url_msg = __('Error', 'Poll id already used');
|
||||
} else if (in_array($customized_url, ['admin', 'vote', 'action'], true)) {
|
||||
$error_on_customized_url = true;
|
||||
$error_on_customized_url_msg = __('Error', 'This id is not allowed');
|
||||
}
|
||||
}
|
||||
|
||||
if ($use_ValueMax && $ValueMax === false) {
|
||||
$error_on_ValueMax = true;
|
||||
}
|
||||
|
||||
if ($name !== $_POST['name']) {
|
||||
$error_on_name = true;
|
||||
}
|
||||
@ -121,7 +132,7 @@ if ($goToStep2) {
|
||||
}
|
||||
|
||||
// Si pas d'erreur dans l'adresse alors on change de page vers date ou autre
|
||||
if ($config['use_smtp'] == true) {
|
||||
if ($config['use_smtp'] === true) {
|
||||
$email_OK = $mail && !$error_on_mail;
|
||||
} else {
|
||||
$email_OK = true;
|
||||
@ -130,33 +141,33 @@ if ($goToStep2) {
|
||||
if ($use_password) {
|
||||
if (empty($password)) {
|
||||
$error_on_password = true;
|
||||
} else if ($password != $password_repeat) {
|
||||
} else if ($password !== $password_repeat) {
|
||||
$error_on_password_repeat = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($title && $name && $email_OK && !$error_on_title && !$error_on_customized_url && !$error_on_description && !$error_on_name
|
||||
&& !$error_on_password && !$error_on_password_repeat
|
||||
&& !$error_on_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;
|
||||
}
|
||||
|
||||
if ($goToStep2 == 'date') {
|
||||
$_SESSION['form'] = serialize($form);
|
||||
|
||||
if ($goToStep2 === 'date') {
|
||||
header('Location:create_date_poll.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($goToStep2 == 'classic') {
|
||||
if ($goToStep2 === 'classic') {
|
||||
header('Location:create_classic_poll.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Title Erreur !
|
||||
$title = __('Error', 'Error!') . ' - ' . __('Step 1', 'Poll creation (1 on 3)');
|
||||
@ -167,43 +178,48 @@ if ($goToStep2) {
|
||||
}
|
||||
|
||||
// Prepare error messages
|
||||
$errors = array(
|
||||
'title' => array(
|
||||
$errors = [
|
||||
'title' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'customized_url' => array(
|
||||
],
|
||||
'customized_url' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'description' => array(
|
||||
],
|
||||
'description' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'name' => array(
|
||||
],
|
||||
'name' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'email' => array(
|
||||
],
|
||||
'email' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'password' => array(
|
||||
],
|
||||
'password' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
),
|
||||
'password_repeat' => array(
|
||||
],
|
||||
'ValueMax' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
)
|
||||
);
|
||||
],
|
||||
'password_repeat' => [
|
||||
'msg' => '',
|
||||
'aria' => '',
|
||||
'class' => ''
|
||||
],
|
||||
];
|
||||
|
||||
if (!empty($_POST[GO_TO_STEP_2])) {
|
||||
if (empty($_POST['title'])) {
|
||||
@ -219,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');
|
||||
$errors['customized_url']['msg'] = $error_on_customized_url_msg ?? __('Error', "Something is wrong with the format: customized urls should only consist of alphanumeric characters and hyphens.");
|
||||
}
|
||||
|
||||
if ($error_on_description) {
|
||||
@ -232,10 +248,14 @@ if (!empty($_POST[GO_TO_STEP_2])) {
|
||||
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['name']['class'] = ' has-error';
|
||||
$errors['name']['msg'] = __('Error', 'Enter a name');
|
||||
} elseif (mb_strlen($inputService->filterName($_POST['name'])) > 32) {
|
||||
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['name']['class'] = ' has-error';
|
||||
$errors['name']['msg'] = __('Error', "Name is limited to 32 characters");
|
||||
} elseif ($error_on_name) {
|
||||
$errors['name']['aria'] = 'aria-describeby="poll_name_error" ';
|
||||
$errors['name']['class'] = ' has-error';
|
||||
$errors['name']['msg'] = __('Error', 'Something is wrong with the format');
|
||||
$errors['name']['msg'] = __('Error', "Something is wrong with the format: name shouldn't have any spaces before or after");
|
||||
}
|
||||
|
||||
if (empty($_POST['mail'])) {
|
||||
@ -258,6 +278,11 @@ if (!empty($_POST[GO_TO_STEP_2])) {
|
||||
$errors['password_repeat']['class'] = ' has-error';
|
||||
$errors['password_repeat']['msg'] = __('Error', 'Passwords do not match');
|
||||
}
|
||||
if ($error_on_ValueMax) {
|
||||
$errors['ValueMax']['aria'] = 'aria-describeby="poll_ValueMax" ';
|
||||
$errors['ValueMax']['class'] = ' has-error';
|
||||
$errors['ValueMax']['msg'] = __('Error', 'Error on amount of voters limitation : value must be an integer greater than 0');
|
||||
}
|
||||
}
|
||||
|
||||
$useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']);
|
||||
@ -265,22 +290,26 @@ $useRemoteUser = USE_REMOTE_USER && isset($_SERVER['REMOTE_USER']);
|
||||
$smarty->assign('title', $title);
|
||||
$smarty->assign('useRemoteUser', $useRemoteUser);
|
||||
$smarty->assign('errors', $errors);
|
||||
$smarty->assign('advanced_errors', $goToStep2 && ($error_on_ValueMax || $error_on_customized_url || $error_on_password || $error_on_password_repeat));
|
||||
$smarty->assign('use_smtp', $config['use_smtp']);
|
||||
$smarty->assign('default_to_marldown_editor', $config['markdown_editor_by_default']);
|
||||
$smarty->assign('goToStep2', GO_TO_STEP_2);
|
||||
|
||||
$smarty->assign('poll_type', $poll_type);
|
||||
$smarty->assign('poll_title', Utils::fromPostOrDefault('title', $_SESSION['form']->title));
|
||||
$smarty->assign('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('poll_description', Utils::fromPostOrDefault('description', $_SESSION['form']->description));
|
||||
$smarty->assign('poll_name', Utils::fromPostOrDefault('name', $_SESSION['form']->admin_name));
|
||||
$smarty->assign('poll_mail', Utils::fromPostOrDefault('mail', $_SESSION['form']->admin_mail));
|
||||
$smarty->assign('poll_editable', Utils::fromPostOrDefault('editable', $_SESSION['form']->editable));
|
||||
$smarty->assign('poll_receiveNewVotes', Utils::fromPostOrDefault('receiveNewVotes', $_SESSION['form']->receiveNewVotes));
|
||||
$smarty->assign('poll_receiveNewComments', Utils::fromPostOrDefault('receiveNewComments', $_SESSION['form']->receiveNewComments));
|
||||
$smarty->assign('poll_hidden', Utils::fromPostOrDefault('hidden', $_SESSION['form']->hidden));
|
||||
$smarty->assign('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('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');
|
||||
|
@ -23,9 +23,3 @@
|
||||
border-top: 0 none;
|
||||
content: "";
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
#optionnal {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
261
css/bootstrap-theme.css
vendored
261
css/bootstrap-theme.css
vendored
@ -1,9 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap v3.2.0 (http://getbootstrap.com)
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
@ -29,6 +28,35 @@
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn-default.disabled,
|
||||
.btn-primary.disabled,
|
||||
.btn-success.disabled,
|
||||
.btn-info.disabled,
|
||||
.btn-warning.disabled,
|
||||
.btn-danger.disabled,
|
||||
.btn-default[disabled],
|
||||
.btn-primary[disabled],
|
||||
.btn-success[disabled],
|
||||
.btn-info[disabled],
|
||||
.btn-warning[disabled],
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
fieldset[disabled] .btn-primary,
|
||||
fieldset[disabled] .btn-success,
|
||||
fieldset[disabled] .btn-info,
|
||||
fieldset[disabled] .btn-warning,
|
||||
fieldset[disabled] .btn-danger {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-default .badge,
|
||||
.btn-primary .badge,
|
||||
.btn-success .badge,
|
||||
.btn-info .badge,
|
||||
.btn-warning .badge,
|
||||
.btn-danger .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
@ -55,34 +83,66 @@
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-default:disabled,
|
||||
.btn-default[disabled] {
|
||||
.btn-default.disabled,
|
||||
.btn-default[disabled],
|
||||
fieldset[disabled] .btn-default,
|
||||
.btn-default.disabled:hover,
|
||||
.btn-default[disabled]:hover,
|
||||
fieldset[disabled] .btn-default:hover,
|
||||
.btn-default.disabled:focus,
|
||||
.btn-default[disabled]:focus,
|
||||
fieldset[disabled] .btn-default:focus,
|
||||
.btn-default.disabled.focus,
|
||||
.btn-default[disabled].focus,
|
||||
fieldset[disabled] .btn-default.focus,
|
||||
.btn-default.disabled:active,
|
||||
.btn-default[disabled]:active,
|
||||
fieldset[disabled] .btn-default:active,
|
||||
.btn-default.disabled.active,
|
||||
.btn-default[disabled].active,
|
||||
fieldset[disabled] .btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2));
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #2d6ca2;
|
||||
background-color: #265a88;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #2d6ca2;
|
||||
border-color: #2b669a;
|
||||
background-color: #265a88;
|
||||
border-color: #245580;
|
||||
}
|
||||
.btn-primary:disabled,
|
||||
.btn-primary[disabled] {
|
||||
background-color: #2d6ca2;
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled],
|
||||
fieldset[disabled] .btn-primary,
|
||||
.btn-primary.disabled:hover,
|
||||
.btn-primary[disabled]:hover,
|
||||
fieldset[disabled] .btn-primary:hover,
|
||||
.btn-primary.disabled:focus,
|
||||
.btn-primary[disabled]:focus,
|
||||
fieldset[disabled] .btn-primary:focus,
|
||||
.btn-primary.disabled.focus,
|
||||
.btn-primary[disabled].focus,
|
||||
fieldset[disabled] .btn-primary.focus,
|
||||
.btn-primary.disabled:active,
|
||||
.btn-primary[disabled]:active,
|
||||
fieldset[disabled] .btn-primary:active,
|
||||
.btn-primary.disabled.active,
|
||||
.btn-primary[disabled].active,
|
||||
fieldset[disabled] .btn-primary.active {
|
||||
background-color: #265a88;
|
||||
background-image: none;
|
||||
}
|
||||
.btn-success {
|
||||
@ -105,8 +165,24 @@
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:disabled,
|
||||
.btn-success[disabled] {
|
||||
.btn-success.disabled,
|
||||
.btn-success[disabled],
|
||||
fieldset[disabled] .btn-success,
|
||||
.btn-success.disabled:hover,
|
||||
.btn-success[disabled]:hover,
|
||||
fieldset[disabled] .btn-success:hover,
|
||||
.btn-success.disabled:focus,
|
||||
.btn-success[disabled]:focus,
|
||||
fieldset[disabled] .btn-success:focus,
|
||||
.btn-success.disabled.focus,
|
||||
.btn-success[disabled].focus,
|
||||
fieldset[disabled] .btn-success.focus,
|
||||
.btn-success.disabled:active,
|
||||
.btn-success[disabled]:active,
|
||||
fieldset[disabled] .btn-success:active,
|
||||
.btn-success.disabled.active,
|
||||
.btn-success[disabled].active,
|
||||
fieldset[disabled] .btn-success.active {
|
||||
background-color: #419641;
|
||||
background-image: none;
|
||||
}
|
||||
@ -130,8 +206,24 @@
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:disabled,
|
||||
.btn-info[disabled] {
|
||||
.btn-info.disabled,
|
||||
.btn-info[disabled],
|
||||
fieldset[disabled] .btn-info,
|
||||
.btn-info.disabled:hover,
|
||||
.btn-info[disabled]:hover,
|
||||
fieldset[disabled] .btn-info:hover,
|
||||
.btn-info.disabled:focus,
|
||||
.btn-info[disabled]:focus,
|
||||
fieldset[disabled] .btn-info:focus,
|
||||
.btn-info.disabled.focus,
|
||||
.btn-info[disabled].focus,
|
||||
fieldset[disabled] .btn-info.focus,
|
||||
.btn-info.disabled:active,
|
||||
.btn-info[disabled]:active,
|
||||
fieldset[disabled] .btn-info:active,
|
||||
.btn-info.disabled.active,
|
||||
.btn-info[disabled].active,
|
||||
fieldset[disabled] .btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
background-image: none;
|
||||
}
|
||||
@ -155,8 +247,24 @@
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:disabled,
|
||||
.btn-warning[disabled] {
|
||||
.btn-warning.disabled,
|
||||
.btn-warning[disabled],
|
||||
fieldset[disabled] .btn-warning,
|
||||
.btn-warning.disabled:hover,
|
||||
.btn-warning[disabled]:hover,
|
||||
fieldset[disabled] .btn-warning:hover,
|
||||
.btn-warning.disabled:focus,
|
||||
.btn-warning[disabled]:focus,
|
||||
fieldset[disabled] .btn-warning:focus,
|
||||
.btn-warning.disabled.focus,
|
||||
.btn-warning[disabled].focus,
|
||||
fieldset[disabled] .btn-warning.focus,
|
||||
.btn-warning.disabled:active,
|
||||
.btn-warning[disabled]:active,
|
||||
fieldset[disabled] .btn-warning:active,
|
||||
.btn-warning.disabled.active,
|
||||
.btn-warning[disabled].active,
|
||||
fieldset[disabled] .btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
background-image: none;
|
||||
}
|
||||
@ -180,8 +288,24 @@
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:disabled,
|
||||
.btn-danger[disabled] {
|
||||
.btn-danger.disabled,
|
||||
.btn-danger[disabled],
|
||||
fieldset[disabled] .btn-danger,
|
||||
.btn-danger.disabled:hover,
|
||||
.btn-danger[disabled]:hover,
|
||||
fieldset[disabled] .btn-danger:hover,
|
||||
.btn-danger.disabled:focus,
|
||||
.btn-danger[disabled]:focus,
|
||||
fieldset[disabled] .btn-danger:focus,
|
||||
.btn-danger.disabled.focus,
|
||||
.btn-danger[disabled].focus,
|
||||
fieldset[disabled] .btn-danger.focus,
|
||||
.btn-danger.disabled:active,
|
||||
.btn-danger[disabled]:active,
|
||||
fieldset[disabled] .btn-danger:active,
|
||||
.btn-danger.disabled.active,
|
||||
.btn-danger[disabled].active,
|
||||
fieldset[disabled] .btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
background-image: none;
|
||||
}
|
||||
@ -203,12 +327,12 @@
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #357ebd;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-color: #2e6da4;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
@ -223,12 +347,13 @@
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .open > a,
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3));
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
|
||||
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
|
||||
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
@ -245,13 +370,15 @@
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .open > a,
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
|
||||
background-image: -o-linear-gradient(top, #222 0%, #282828 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828));
|
||||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
|
||||
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
|
||||
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
@ -265,6 +392,19 @@
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
|
||||
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
@ -315,11 +455,11 @@
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
||||
background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9));
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
@ -367,14 +507,19 @@
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #3071a9;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
||||
background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3));
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
|
||||
text-shadow: 0 -1px 0 #286090;
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3278b3;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.list-group-item.active .badge,
|
||||
.list-group-item.active:hover .badge,
|
||||
.list-group-item.active:focus .badge {
|
||||
text-shadow: none;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
@ -389,11 +534,11 @@
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd));
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
|
||||
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
|
File diff suppressed because one or more lines are too long
7
css/bootstrap-theme.min.css
vendored
7
css/bootstrap-theme.min.css
vendored
File diff suppressed because one or more lines are too long
1
css/bootstrap-theme.min.css.map
Normal file
1
css/bootstrap-theme.min.css.map
Normal file
File diff suppressed because one or more lines are too long
1232
css/bootstrap.css
vendored
1232
css/bootstrap.css
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
7
css/bootstrap.min.css
vendored
7
css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
1
css/bootstrap.min.css.map
Normal file
1
css/bootstrap.min.css.map
Normal file
File diff suppressed because one or more lines are too long
7
css/easymde.min.css
vendored
Normal file
7
css/easymde.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2431
css/fork-awesome.css
Normal file
2431
css/fork-awesome.css
Normal file
File diff suppressed because it is too large
Load Diff
4
css/fork-awesome.min.css
vendored
Normal file
4
css/fork-awesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
css/fork-awesome.min.css.map
Normal file
1
css/fork-awesome.min.css.map
Normal file
File diff suppressed because one or more lines are too long
10
css/jquery-ui.min.css
vendored
10
css/jquery-ui.min.css
vendored
File diff suppressed because one or more lines are too long
101
css/style.css
101
css/style.css
@ -37,6 +37,10 @@ body {
|
||||
margin: 15px auto 30px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Commentaires */
|
||||
div.comment{
|
||||
margin-bottom: 10px;
|
||||
@ -60,8 +64,6 @@ a:focus { /* a11y */
|
||||
outline:#000 dotted 1px;
|
||||
}
|
||||
|
||||
header, footer {
|
||||
}
|
||||
main {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@ -69,15 +71,19 @@ header h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.container {
|
||||
padding:30px;
|
||||
padding: 30px;
|
||||
}
|
||||
.container .jumbotron {
|
||||
padding:20px 20px;
|
||||
padding: 20px 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.container .jumbotron p {
|
||||
font-size: 1em;
|
||||
}
|
||||
.container .jumbotron .btn-group >.btn {
|
||||
margin-bottom: 20px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.summary h4 {
|
||||
margin-top:0;
|
||||
@ -115,9 +121,15 @@ header h1 {
|
||||
/* Description du sondage */
|
||||
/* studs.php et adminstuds.php */
|
||||
header .lead {
|
||||
padding:10px 0;
|
||||
padding: 10px 0;
|
||||
margin:0;
|
||||
}
|
||||
header form .input-group .form-control {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
header form .input-group .input-group-btn {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#admin-link, #public-link {
|
||||
cursor:text;
|
||||
@ -136,10 +148,26 @@ header .lead {
|
||||
|
||||
.poll-description {
|
||||
font-family: inherit;
|
||||
white-space: pre-wrap;
|
||||
word-break: initial;
|
||||
}
|
||||
|
||||
/** Description in markdown **/
|
||||
.form-group .CodeMirror, .form-group .CodeMirror-scroll {
|
||||
min-height: 200px;
|
||||
}
|
||||
#description-form .CodeMirror {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
|
||||
.editor-toolbar {
|
||||
margin-top: 10px;
|
||||
background-color: #eee;
|
||||
}
|
||||
#poll_comments {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
h4.control-label {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
@ -155,7 +183,7 @@ caption {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.results a.btn-default.btn-sm {
|
||||
.results a.btn-default.btn-sm, .best-choice .list-unstyled a.btn-default.btn-sm {
|
||||
padding: 3px 7px;
|
||||
font-size: 0.7em;
|
||||
}
|
||||
@ -212,13 +240,8 @@ caption {
|
||||
|
||||
/* Tableau du sondage */
|
||||
#tableContainer {
|
||||
overflow-x:hidden;
|
||||
margin:5px auto;
|
||||
}
|
||||
|
||||
#tableContainer:focus,
|
||||
#tableContainer:hover {
|
||||
overflow-x:auto;
|
||||
margin:5px auto;
|
||||
}
|
||||
|
||||
table.results {
|
||||
@ -245,7 +268,7 @@ table.results tbody td {
|
||||
table.results thead th {
|
||||
text-align:center;
|
||||
border:2px solid white;
|
||||
padding:5px;
|
||||
padding: 5px;
|
||||
min-width:40px;
|
||||
font-size:12px;
|
||||
max-width:100px;
|
||||
@ -253,9 +276,13 @@ table.results thead th {
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
|
||||
table.results thead th img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
table.results thead .btn {
|
||||
margin:0 auto;
|
||||
display:block;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.results th.rbd.day,
|
||||
@ -305,11 +332,22 @@ table.results .btn-link.btn-sm {
|
||||
padding-right:30px;
|
||||
}
|
||||
|
||||
/* Formulaire de création de sondage */
|
||||
@media (max-width: 767px) {
|
||||
#formulaire .col-xs-12 {
|
||||
padding-left: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Formulaire de vote */
|
||||
#vote-form td ul, #vote-form td label {
|
||||
margin:0;
|
||||
font-size:12px;
|
||||
}
|
||||
#vote-form td label {
|
||||
padding: 1px 3px;
|
||||
}
|
||||
|
||||
#vote-form td {
|
||||
border-top:2px solid white;
|
||||
@ -321,11 +359,11 @@ table.results .btn-link.btn-sm {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0px, 0px, 0px, 0px);
|
||||
border: 0px none;
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.choice input:focus + label {
|
||||
@ -333,17 +371,25 @@ table.results .btn-link.btn-sm {
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.choice {
|
||||
width:32px;
|
||||
width: 35px;
|
||||
margin:0 auto !important;
|
||||
}
|
||||
|
||||
.choice label {
|
||||
cursor:pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td.btn-edit {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
span.edit-username-left {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.yes .btn, .ifneedbe .btn, .no .btn {
|
||||
width:32px;
|
||||
color:#555;
|
||||
width: 35px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.yes .btn,.yes .btn:hover {
|
||||
@ -501,6 +547,12 @@ table.results > tbody > tr:hover > td .glyphicon {
|
||||
#poll_search {
|
||||
cursor: pointer;
|
||||
}
|
||||
.table-of-polls {
|
||||
overflow-x: scroll;
|
||||
margin-bottom: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Studs */
|
||||
.password_request {
|
||||
@ -511,3 +563,8 @@ table.results > tbody > tr:hover > td .glyphicon {
|
||||
#password-form .btn-cancel {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
white-space: normal;
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ $poll = null;
|
||||
/*----------*/
|
||||
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$pollService = new PollService($logService);
|
||||
$securityService = new SecurityService();
|
||||
|
||||
/* PAGE */
|
||||
@ -110,7 +110,7 @@ foreach ($votes as $vote) {
|
||||
$text = __('Generic', 'Yes');
|
||||
break;
|
||||
default:
|
||||
$text = 'unkown';
|
||||
$text = __('Generic', 'Unknown');
|
||||
}
|
||||
echo Utils::csvEscape($text);
|
||||
echo ',';
|
||||
|
@ -4,16 +4,16 @@
|
||||
* is not distributed with this file, you can obtain one at
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt
|
||||
*
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of STUdS (initial project): Guilhem BORGHESI (borghesi@unistra.fr) and Raphaël DROZ
|
||||
* Authors of Framadate/OpenSondage: Framasoft (https://github.com/framasoft)
|
||||
*
|
||||
* =============================
|
||||
*
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* Ce logiciel est régi par la licence CeCILL-B. Si une copie de cette licence
|
||||
* ne se trouve pas avec ce fichier vous pouvez l'obtenir sur
|
||||
* http://www.cecill.info/licences/Licence_CeCILL-B_V1-fr.txt
|
||||
*
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de STUdS (projet initial) : Guilhem BORGHESI (borghesi@unistra.fr) et Raphaël DROZ
|
||||
* Auteurs de Framadate/OpenSondage : Framasoft (https://github.com/framasoft)
|
||||
*/
|
||||
|
||||
@ -27,8 +27,8 @@ include_once __DIR__ . '/app/inc/init.php';
|
||||
/* SERVICES */
|
||||
/* -------- */
|
||||
$logService = new LogService();
|
||||
$pollService = new PollService($connect, $logService);
|
||||
$mailService = new MailService($config['use_smtp']);
|
||||
$pollService = new PollService($logService);
|
||||
$mailService = new MailService($config['use_smtp'], $config['smtp_options']);
|
||||
|
||||
/* PAGE */
|
||||
/* ---- */
|
||||
@ -43,7 +43,7 @@ if (!empty($_POST['mail'])) {
|
||||
$smarty->assign('polls', $polls);
|
||||
$body = $smarty->fetch('mail/find_polls.tpl');
|
||||
|
||||
$mailService->send($mail, __('FindPolls', 'List of your polls').' - '.NOMAPPLICATION, $body, 'SEND_POLLS');
|
||||
$mailService->send($mail, __('FindPolls', 'List of your polls') . ' - ' . NOMAPPLICATION, $body, 'SEND_POLLS');
|
||||
$message = new Message('success', __('FindPolls', 'Polls sent'));
|
||||
} else {
|
||||
$message = new Message('warning', __('Error', 'No polls found'));
|
||||
@ -53,7 +53,6 @@ if (!empty($_POST['mail'])) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$smarty->assign('title', __('Homepage', 'Where are my polls'));
|
||||
$smarty->assign('message', $message);
|
||||
|
||||
|
BIN
fonts/forkawesome-webfont.eot
Normal file
BIN
fonts/forkawesome-webfont.eot
Normal file
Binary file not shown.
2692
fonts/forkawesome-webfont.svg
Normal file
2692
fonts/forkawesome-webfont.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 443 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user