diff options
-rw-r--r-- | .travis.yml | 9 | ||||
-rw-r--r-- | Makefile | 24 | ||||
-rw-r--r-- | application/HttpUtils.php | 28 | ||||
-rw-r--r-- | application/config/ConfigManager.php | 3 | ||||
-rw-r--r-- | doc/md/Release-Shaarli.md | 6 | ||||
-rw-r--r-- | doc/md/Shaarli-configuration.md | 7 | ||||
-rw-r--r-- | doc/md/Unit-tests-Docker.md | 56 | ||||
-rw-r--r-- | docker/test/alpine36/Dockerfile | 34 | ||||
-rw-r--r-- | docker/test/debian8/Dockerfile | 35 | ||||
-rw-r--r-- | docker/test/debian9/Dockerfile | 36 | ||||
-rw-r--r-- | docker/test/ubuntu16/Dockerfile | 36 | ||||
-rw-r--r-- | index.php | 92 | ||||
-rw-r--r-- | mkdocs.yml | 1 | ||||
-rw-r--r-- | tests/HttpUtils/IsHttpsTest.php | 36 | ||||
-rw-r--r-- | tests/languages/de/UtilsDeTest.php | 12 | ||||
-rw-r--r-- | tests/languages/en/UtilsEnTest.php | 12 | ||||
-rw-r--r-- | tests/languages/fr/UtilsFrTest.php | 12 | ||||
-rw-r--r-- | tpl/default/css/shaarli.css | 2 | ||||
-rw-r--r-- | tpl/default/js/shaarli.js | 9 | ||||
-rw-r--r-- | tpl/default/loginform.html | 3 | ||||
-rw-r--r-- | tpl/vintage/loginform.html | 4 |
21 files changed, 385 insertions, 72 deletions
diff --git a/.travis.yml b/.travis.yml index 26535ad3..b6b9bddf 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -1,12 +1,6 @@ | |||
1 | sudo: false | 1 | sudo: false |
2 | dist: precise | 2 | dist: trusty |
3 | language: php | 3 | language: php |
4 | addons: | ||
5 | apt: | ||
6 | packages: | ||
7 | - locales | ||
8 | - language-pack-de | ||
9 | - language-pack-fr | ||
10 | cache: | 4 | cache: |
11 | directories: | 5 | directories: |
12 | - $HOME/.composer/cache | 6 | - $HOME/.composer/cache |
@@ -18,6 +12,7 @@ php: | |||
18 | install: | 12 | install: |
19 | - composer self-update | 13 | - composer self-update |
20 | - composer install --prefer-dist | 14 | - composer install --prefer-dist |
15 | - locale -a | ||
21 | script: | 16 | script: |
22 | - make clean | 17 | - make clean |
23 | - make check_permissions | 18 | - make check_permissions |
@@ -19,6 +19,16 @@ PHP_COMMA_SOURCE = index.php,application,tests,plugins | |||
19 | all: static_analysis_summary check_permissions test | 19 | all: static_analysis_summary check_permissions test |
20 | 20 | ||
21 | ## | 21 | ## |
22 | # Docker test adapter | ||
23 | # | ||
24 | # Shaarli sources and vendored libraries are copied from a shared volume | ||
25 | # to a user-owned directory to enable running tests as a non-root user. | ||
26 | ## | ||
27 | docker_%: | ||
28 | rsync -az /shaarli/ ~/shaarli/ | ||
29 | cd ~/shaarli && make $* | ||
30 | |||
31 | ## | ||
22 | # Concise status of the project | 32 | # Concise status of the project |
23 | # These targets are non-blocking: || exit 0 | 33 | # These targets are non-blocking: || exit 0 |
24 | ## | 34 | ## |
@@ -159,14 +169,14 @@ composer_dependencies: clean | |||
159 | find vendor/ -name ".git" -type d -exec rm -rf {} + | 169 | find vendor/ -name ".git" -type d -exec rm -rf {} + |
160 | 170 | ||
161 | ### generate a release tarball and include 3rd-party dependencies | 171 | ### generate a release tarball and include 3rd-party dependencies |
162 | release_tar: composer_dependencies doc_html | 172 | release_tar: composer_dependencies htmldoc |
163 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD | 173 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD |
164 | tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/ | 174 | tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/ |
165 | tar rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/ | 175 | tar rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/ |
166 | gzip $(ARCHIVE_VERSION).tar | 176 | gzip $(ARCHIVE_VERSION).tar |
167 | 177 | ||
168 | ### generate a release zip and include 3rd-party dependencies | 178 | ### generate a release zip and include 3rd-party dependencies |
169 | release_zip: composer_dependencies doc_html | 179 | release_zip: composer_dependencies htmldoc |
170 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD | 180 | git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD |
171 | mkdir -p $(ARCHIVE_PREFIX)/{doc,vendor} | 181 | mkdir -p $(ARCHIVE_PREFIX)/{doc,vendor} |
172 | rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/ | 182 | rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/ |
@@ -195,17 +205,11 @@ doxygen: clean | |||
195 | @rm -rf doxygen | 205 | @rm -rf doxygen |
196 | @( cat Doxyfile ; echo "PROJECT_NUMBER=`git describe`" ) | doxygen - | 206 | @( cat Doxyfile ; echo "PROJECT_NUMBER=`git describe`" ) | doxygen - |
197 | 207 | ||
198 | ### Convert local markdown documentation to HTML | 208 | ### generate HTML documentation from Markdown pages with MkDocs |
199 | # | 209 | htmldoc: |
200 | # For all pages: | ||
201 | # - convert GitHub-flavoured relative links to standard Markdown | ||
202 | # - generate html documentation with mkdocs | ||
203 | htmlpages: | ||
204 | python3 -m venv venv/ | 210 | python3 -m venv venv/ |
205 | bash -c 'source venv/bin/activate; \ | 211 | bash -c 'source venv/bin/activate; \ |
206 | pip install mkdocs; \ | 212 | pip install mkdocs; \ |
207 | mkdocs build' | 213 | mkdocs build' |
208 | find doc/html/ -type f -exec chmod a-x '{}' \; | 214 | find doc/html/ -type f -exec chmod a-x '{}' \; |
209 | rm -r venv | 215 | rm -r venv |
210 | |||
211 | doc_html: authors htmlpages | ||
diff --git a/application/HttpUtils.php b/application/HttpUtils.php index 88a1efdb..00835966 100644 --- a/application/HttpUtils.php +++ b/application/HttpUtils.php | |||
@@ -401,3 +401,31 @@ function getIpAddressFromProxy($server, $trustedIps) | |||
401 | 401 | ||
402 | return array_pop($ips); | 402 | return array_pop($ips); |
403 | } | 403 | } |
404 | |||
405 | /** | ||
406 | * Returns true if Shaarli's currently browsed in HTTPS. | ||
407 | * Supports reverse proxies (if the headers are correctly set). | ||
408 | * | ||
409 | * @param array $server $_SERVER. | ||
410 | * | ||
411 | * @return bool true if HTTPS, false otherwise. | ||
412 | */ | ||
413 | function is_https($server) | ||
414 | { | ||
415 | |||
416 | if (isset($server['HTTP_X_FORWARDED_PORT'])) { | ||
417 | // Keep forwarded port | ||
418 | if (strpos($server['HTTP_X_FORWARDED_PORT'], ',') !== false) { | ||
419 | $ports = explode(',', $server['HTTP_X_FORWARDED_PORT']); | ||
420 | $port = trim($ports[0]); | ||
421 | } else { | ||
422 | $port = $server['HTTP_X_FORWARDED_PORT']; | ||
423 | } | ||
424 | |||
425 | if ($port == '443') { | ||
426 | return true; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | return ! empty($server['HTTPS']); | ||
431 | } | ||
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 0fc5a5c7..32f6ef6d 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php | |||
@@ -327,7 +327,10 @@ class ConfigManager | |||
327 | 327 | ||
328 | $this->setEmpty('privacy.default_private_links', false); | 328 | $this->setEmpty('privacy.default_private_links', false); |
329 | $this->setEmpty('privacy.hide_public_links', false); | 329 | $this->setEmpty('privacy.hide_public_links', false); |
330 | $this->setEmpty('privacy.force_login', false); | ||
330 | $this->setEmpty('privacy.hide_timestamps', false); | 331 | $this->setEmpty('privacy.hide_timestamps', false); |
332 | // default state of the 'remember me' checkbox of the login form | ||
333 | $this->setEmpty('privacy.remember_user_default', true); | ||
331 | 334 | ||
332 | $this->setEmpty('thumbnail.enable_thumbnails', true); | 335 | $this->setEmpty('thumbnail.enable_thumbnails', true); |
333 | $this->setEmpty('thumbnail.enable_localcache', true); | 336 | $this->setEmpty('thumbnail.enable_localcache', true); |
diff --git a/doc/md/Release-Shaarli.md b/doc/md/Release-Shaarli.md index 974a7438..e22eabc9 100644 --- a/doc/md/Release-Shaarli.md +++ b/doc/md/Release-Shaarli.md | |||
@@ -46,6 +46,12 @@ TBA | |||
46 | 46 | ||
47 | 47 | ||
48 | ## Increment the version code, update docs, create and push a signed tag | 48 | ## Increment the version code, update docs, create and push a signed tag |
49 | ### Update the list of Git contributors | ||
50 | ```bash | ||
51 | $ make authors | ||
52 | $ git commit -s -m "Update AUTHORS" | ||
53 | ``` | ||
54 | |||
49 | ### Create and merge a Pull Request | 55 | ### Create and merge a Pull Request |
50 | This one is pretty straightforward ;-) | 56 | This one is pretty straightforward ;-) |
51 | 57 | ||
diff --git a/doc/md/Shaarli-configuration.md b/doc/md/Shaarli-configuration.md index 188a3c09..37486414 100644 --- a/doc/md/Shaarli-configuration.md +++ b/doc/md/Shaarli-configuration.md | |||
@@ -90,7 +90,10 @@ _These settings should not be edited_ | |||
90 | 90 | ||
91 | - **default_private_links**: Check the private checkbox by default for every new link. | 91 | - **default_private_links**: Check the private checkbox by default for every new link. |
92 | - **hide_public_links**: All links are hidden while logged out. | 92 | - **hide_public_links**: All links are hidden while logged out. |
93 | - **force_login**: if **hide_public_links** and this are set to `true`, all anonymous users are redirected to the login page. | ||
93 | - **hide_timestamps**: Timestamps are hidden. | 94 | - **hide_timestamps**: Timestamps are hidden. |
95 | - **remember_user_default**: Default state of the login page's *remember me* checkbox | ||
96 | - `true`: checked by default, `false`: unchecked by default | ||
94 | 97 | ||
95 | ### Feed | 98 | ### Feed |
96 | 99 | ||
@@ -192,7 +195,9 @@ _These settings should not be edited_ | |||
192 | "privacy": { | 195 | "privacy": { |
193 | "default_private_links": true, | 196 | "default_private_links": true, |
194 | "hide_public_links": false, | 197 | "hide_public_links": false, |
195 | "hide_timestamps": false | 198 | "force_login": false, |
199 | "hide_timestamps": false, | ||
200 | "remember_user_default": true | ||
196 | }, | 201 | }, |
197 | "thumbnail": { | 202 | "thumbnail": { |
198 | "enable_thumbnails": true, | 203 | "enable_thumbnails": true, |
diff --git a/doc/md/Unit-tests-Docker.md b/doc/md/Unit-tests-Docker.md new file mode 100644 index 00000000..c2de7cc7 --- /dev/null +++ b/doc/md/Unit-tests-Docker.md | |||
@@ -0,0 +1,56 @@ | |||
1 | ## Running tests inside Docker containers | ||
2 | |||
3 | Read first: | ||
4 | |||
5 | - [Docker 101](docker/docker-101.md) | ||
6 | - [Docker resources](docker/resources.md) | ||
7 | - [Unit tests](Unit-tests.md) | ||
8 | |||
9 | ### Docker test images | ||
10 | |||
11 | Test Dockerfiles are located under `docker/tests/<distribution>/Dockerfile`, | ||
12 | and can be used to build Docker images to run Shaarli test suites under common | ||
13 | Linux environments. | ||
14 | |||
15 | Dockerfiles are provided for the following environments: | ||
16 | |||
17 | - `alpine36` - [Alpine 3.6](https://www.alpinelinux.org/downloads/) | ||
18 | - `debian8` - [Debian 8 Jessie](https://www.debian.org/DebianJessie) (oldstable) | ||
19 | - `debian9` - [Debian 9 Stretch](https://wiki.debian.org/DebianStretch) (stable) | ||
20 | - `ubuntu16` - [Ubuntu 16.04 Xenial Xerus](http://releases.ubuntu.com/16.04/) (LTS) | ||
21 | |||
22 | What's behind the curtains: | ||
23 | |||
24 | - each image provides: | ||
25 | - a base Linux OS | ||
26 | - Shaarli PHP dependencies (OS packages) | ||
27 | - test PHP dependencies (OS packages) | ||
28 | - Composer | ||
29 | - the local workspace is mapped to the container's `/shaarli/` directory, | ||
30 | - the files are rsync'd to so tests are run using a standard Linux user account | ||
31 | (running tests as `root` would bypass permission checks and may hide issues) | ||
32 | - the tests are run inside the container. | ||
33 | |||
34 | ### Building test images | ||
35 | |||
36 | ```bash | ||
37 | # build the Debian 9 Docker image | ||
38 | $ cd /path/to/shaarli | ||
39 | $ cd docker/test/debian9 | ||
40 | $ docker build -t shaarli-test:debian9 . | ||
41 | ``` | ||
42 | |||
43 | ### Running tests | ||
44 | |||
45 | ```bash | ||
46 | $ cd /path/to/shaarli | ||
47 | |||
48 | # install/update 3rd-party test dependencies | ||
49 | $ composer install --prefer-dist | ||
50 | |||
51 | # run tests using the freshly built image | ||
52 | $ docker run -v $PWD:/shaarli shaarli-test:debian9 docker_test | ||
53 | |||
54 | # run the full test campaign | ||
55 | $ docker run -v $PWD:/shaarli shaarli-test:debian9 docker_all_tests | ||
56 | ``` | ||
diff --git a/docker/test/alpine36/Dockerfile b/docker/test/alpine36/Dockerfile new file mode 100644 index 00000000..fa84f6e2 --- /dev/null +++ b/docker/test/alpine36/Dockerfile | |||
@@ -0,0 +1,34 @@ | |||
1 | FROM alpine:3.6 | ||
2 | MAINTAINER Shaarli Community | ||
3 | |||
4 | RUN apk --update --no-cache add \ | ||
5 | ca-certificates \ | ||
6 | curl \ | ||
7 | make \ | ||
8 | php7 \ | ||
9 | php7-ctype \ | ||
10 | php7-curl \ | ||
11 | php7-dom \ | ||
12 | php7-gd \ | ||
13 | php7-iconv \ | ||
14 | php7-intl \ | ||
15 | php7-json \ | ||
16 | php7-mbstring \ | ||
17 | php7-openssl \ | ||
18 | php7-phar \ | ||
19 | php7-session \ | ||
20 | php7-simplexml \ | ||
21 | php7-tokenizer \ | ||
22 | php7-xdebug \ | ||
23 | php7-xml \ | ||
24 | php7-zlib \ | ||
25 | rsync | ||
26 | |||
27 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer | ||
28 | |||
29 | RUN mkdir /shaarli | ||
30 | WORKDIR /shaarli | ||
31 | VOLUME /shaarli | ||
32 | |||
33 | ENTRYPOINT ["make"] | ||
34 | CMD [] | ||
diff --git a/docker/test/debian8/Dockerfile b/docker/test/debian8/Dockerfile new file mode 100644 index 00000000..eaa34e9b --- /dev/null +++ b/docker/test/debian8/Dockerfile | |||
@@ -0,0 +1,35 @@ | |||
1 | FROM debian:jessie | ||
2 | MAINTAINER Shaarli Community | ||
3 | |||
4 | ENV TERM dumb | ||
5 | ENV DEBIAN_FRONTEND noninteractive | ||
6 | ENV LANG en_US.UTF-8 | ||
7 | ENV LANGUAGE en_US:en | ||
8 | |||
9 | RUN apt-get update \ | ||
10 | && apt-get install --no-install-recommends -y \ | ||
11 | ca-certificates \ | ||
12 | curl \ | ||
13 | locales \ | ||
14 | make \ | ||
15 | php5 \ | ||
16 | php5-curl \ | ||
17 | php5-gd \ | ||
18 | php5-intl \ | ||
19 | php5-xdebug \ | ||
20 | rsync \ | ||
21 | && apt-get clean | ||
22 | |||
23 | RUN locale-gen en_US.UTF-8 \ | ||
24 | && locale-gen de_DE.UTF-8 \ | ||
25 | && locale-gen fr_FR.UTF-8 | ||
26 | |||
27 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | ||
28 | RUN chmod 755 /usr/local/bin/composer | ||
29 | |||
30 | RUN mkdir /shaarli | ||
31 | WORKDIR /shaarli | ||
32 | VOLUME /shaarli | ||
33 | |||
34 | ENTRYPOINT ["make"] | ||
35 | CMD [] | ||
diff --git a/docker/test/debian9/Dockerfile b/docker/test/debian9/Dockerfile new file mode 100644 index 00000000..3ab4b93d --- /dev/null +++ b/docker/test/debian9/Dockerfile | |||
@@ -0,0 +1,36 @@ | |||
1 | FROM debian:stretch | ||
2 | MAINTAINER Shaarli Community | ||
3 | |||
4 | ENV TERM dumb | ||
5 | ENV DEBIAN_FRONTEND noninteractive | ||
6 | ENV LANG en_US.UTF-8 | ||
7 | ENV LANGUAGE en_US:en | ||
8 | |||
9 | RUN apt-get update \ | ||
10 | && apt-get install --no-install-recommends -y \ | ||
11 | ca-certificates \ | ||
12 | curl \ | ||
13 | locales \ | ||
14 | make \ | ||
15 | php7.0 \ | ||
16 | php7.0-curl \ | ||
17 | php7.0-gd \ | ||
18 | php7.0-intl \ | ||
19 | php7.0-xml \ | ||
20 | php-xdebug \ | ||
21 | rsync \ | ||
22 | && apt-get clean | ||
23 | |||
24 | RUN locale-gen en_US.UTF-8 \ | ||
25 | && locale-gen de_DE.UTF-8 \ | ||
26 | && locale-gen fr_FR.UTF-8 | ||
27 | |||
28 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | ||
29 | RUN chmod 755 /usr/local/bin/composer | ||
30 | |||
31 | RUN mkdir /shaarli | ||
32 | WORKDIR /shaarli | ||
33 | VOLUME /shaarli | ||
34 | |||
35 | ENTRYPOINT ["make"] | ||
36 | CMD [] | ||
diff --git a/docker/test/ubuntu16/Dockerfile b/docker/test/ubuntu16/Dockerfile new file mode 100644 index 00000000..e53ed9e3 --- /dev/null +++ b/docker/test/ubuntu16/Dockerfile | |||
@@ -0,0 +1,36 @@ | |||
1 | FROM ubuntu:16.04 | ||
2 | MAINTAINER Shaarli Community | ||
3 | |||
4 | ENV TERM dumb | ||
5 | ENV DEBIAN_FRONTEND noninteractive | ||
6 | ENV LANG en_US.UTF-8 | ||
7 | ENV LANGUAGE en_US:en | ||
8 | |||
9 | RUN apt-get update \ | ||
10 | && apt-get install --no-install-recommends -y \ | ||
11 | ca-certificates \ | ||
12 | curl \ | ||
13 | language-pack-de \ | ||
14 | language-pack-en \ | ||
15 | language-pack-fr \ | ||
16 | locales \ | ||
17 | make \ | ||
18 | php7.0 \ | ||
19 | php7.0-curl \ | ||
20 | php7.0-gd \ | ||
21 | php7.0-intl \ | ||
22 | php7.0-xml \ | ||
23 | php-xdebug \ | ||
24 | rsync \ | ||
25 | && apt-get clean | ||
26 | |||
27 | ADD https://getcomposer.org/composer.phar /usr/local/bin/composer | ||
28 | RUN chmod 755 /usr/local/bin/composer | ||
29 | |||
30 | RUN useradd -m dev \ | ||
31 | && mkdir /shaarli | ||
32 | USER dev | ||
33 | WORKDIR /shaarli | ||
34 | |||
35 | ENTRYPOINT ["make"] | ||
36 | CMD [] | ||
@@ -583,20 +583,29 @@ function showDailyRSS($conf) { | |||
583 | */ | 583 | */ |
584 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | 584 | function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) |
585 | { | 585 | { |
586 | $day=date('Ymd',strtotime('-1 day')); // Yesterday, in format YYYYMMDD. | 586 | $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD. |
587 | if (isset($_GET['day'])) $day=$_GET['day']; | 587 | if (isset($_GET['day'])) { |
588 | $day = $_GET['day']; | ||
589 | } | ||
588 | 590 | ||
589 | $days = $LINKSDB->days(); | 591 | $days = $LINKSDB->days(); |
590 | $i = array_search($day,$days); | 592 | $i = array_search($day, $days); |
591 | if ($i===false) { $i=count($days)-1; $day=$days[$i]; } | 593 | if ($i === false && count($days)) { |
592 | $previousday=''; | 594 | // no links for day, but at least one day with links |
593 | $nextday=''; | 595 | $i = count($days) - 1; |
594 | if ($i!==false) | 596 | $day = $days[$i]; |
595 | { | ||
596 | if ($i>=1) $previousday=$days[$i-1]; | ||
597 | if ($i<count($days)-1) $nextday=$days[$i+1]; | ||
598 | } | 597 | } |
598 | $previousday = ''; | ||
599 | $nextday = ''; | ||
599 | 600 | ||
601 | if ($i !== false) { | ||
602 | if ($i >= 1) { | ||
603 | $previousday=$days[$i - 1]; | ||
604 | } | ||
605 | if ($i < count($days) - 1) { | ||
606 | $nextday = $days[$i + 1]; | ||
607 | } | ||
608 | } | ||
600 | try { | 609 | try { |
601 | $linksToDisplay = $LINKSDB->filterDay($day); | 610 | $linksToDisplay = $LINKSDB->filterDay($day); |
602 | } catch (Exception $exc) { | 611 | } catch (Exception $exc) { |
@@ -605,9 +614,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
605 | } | 614 | } |
606 | 615 | ||
607 | // We pre-format some fields for proper output. | 616 | // We pre-format some fields for proper output. |
608 | foreach($linksToDisplay as $key=>$link) | 617 | foreach($linksToDisplay as $key => $link) { |
609 | { | ||
610 | |||
611 | $taglist = explode(' ',$link['tags']); | 618 | $taglist = explode(' ',$link['tags']); |
612 | uasort($taglist, 'strcasecmp'); | 619 | uasort($taglist, 'strcasecmp'); |
613 | $linksToDisplay[$key]['taglist']=$taglist; | 620 | $linksToDisplay[$key]['taglist']=$taglist; |
@@ -621,21 +628,22 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager) | |||
621 | so I manually spread entries with a simple method: I roughly evaluate the | 628 | so I manually spread entries with a simple method: I roughly evaluate the |
622 | height of a div according to title and description length. | 629 | height of a div according to title and description length. |
623 | */ | 630 | */ |
624 | $columns=array(array(),array(),array()); // Entries to display, for each column. | 631 | $columns = array(array(), array(), array()); // Entries to display, for each column. |
625 | $fill=array(0,0,0); // Rough estimate of columns fill. | 632 | $fill = array(0, 0, 0); // Rough estimate of columns fill. |
626 | foreach($linksToDisplay as $key=>$link) | 633 | foreach($linksToDisplay as $key => $link) { |
627 | { | ||
628 | // Roughly estimate length of entry (by counting characters) | 634 | // Roughly estimate length of entry (by counting characters) |
629 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. | 635 | // Title: 30 chars = 1 line. 1 line is 30 pixels height. |
630 | // Description: 836 characters gives roughly 342 pixel height. | 636 | // Description: 836 characters gives roughly 342 pixel height. |
631 | // This is not perfect, but it's usually OK. | 637 | // This is not perfect, but it's usually OK. |
632 | $length=strlen($link['title'])+(342*strlen($link['description']))/836; | 638 | $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836; |
633 | if ($link['thumbnail']) $length +=100; // 1 thumbnails roughly takes 100 pixels height. | 639 | if ($link['thumbnail']) { |
640 | $length += 100; // 1 thumbnails roughly takes 100 pixels height. | ||
641 | } | ||
634 | // Then put in column which is the less filled: | 642 | // Then put in column which is the less filled: |
635 | $smallest=min($fill); // find smallest value in array. | 643 | $smallest = min($fill); // find smallest value in array. |
636 | $index=array_search($smallest,$fill); // find index of this smallest value. | 644 | $index = array_search($smallest, $fill); // find index of this smallest value. |
637 | array_push($columns[$index],$link); // Put entry in this column. | 645 | array_push($columns[$index], $link); // Put entry in this column. |
638 | $fill[$index]+=$length; | 646 | $fill[$index] += $length; |
639 | } | 647 | } |
640 | 648 | ||
641 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); | 649 | $dayDate = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $day.'_000000'); |
@@ -710,6 +718,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) | |||
710 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; | 718 | $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; |
711 | $targetPage = Router::findPage($query, $_GET, isLoggedIn()); | 719 | $targetPage = Router::findPage($query, $_GET, isLoggedIn()); |
712 | 720 | ||
721 | if ( | ||
722 | // if the user isn't logged in | ||
723 | !isLoggedIn() && | ||
724 | // and Shaarli doesn't have public content... | ||
725 | $conf->get('privacy.hide_public_links') && | ||
726 | // and is configured to enforce the login | ||
727 | $conf->get('privacy.force_login') && | ||
728 | // and the current page isn't already the login page | ||
729 | $targetPage !== Router::$PAGE_LOGIN && | ||
730 | // and the user is not requesting a feed (which would lead to a different content-type as expected) | ||
731 | $targetPage !== Router::$PAGE_FEED_ATOM && | ||
732 | $targetPage !== Router::$PAGE_FEED_RSS | ||
733 | ) { | ||
734 | // force current page to be the login page | ||
735 | $targetPage = Router::$PAGE_LOGIN; | ||
736 | } | ||
737 | |||
713 | // Call plugin hooks for header, footer and includes, specifying which page will be rendered. | 738 | // Call plugin hooks for header, footer and includes, specifying which page will be rendered. |
714 | // Then assign generated data to RainTPL. | 739 | // Then assign generated data to RainTPL. |
715 | $common_hooks = array( | 740 | $common_hooks = array( |
@@ -737,6 +762,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) | |||
737 | $PAGE->assign('username', escape($_GET['username'])); | 762 | $PAGE->assign('username', escape($_GET['username'])); |
738 | } | 763 | } |
739 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); | 764 | $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); |
765 | // add default state of the 'remember me' checkbox | ||
766 | $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); | ||
740 | $PAGE->renderPage('loginform'); | 767 | $PAGE->renderPage('loginform'); |
741 | exit; | 768 | exit; |
742 | } | 769 | } |
@@ -1055,10 +1082,10 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) | |||
1055 | // -------- Display the Tools menu if requested (import/export/bookmarklet...) | 1082 | // -------- Display the Tools menu if requested (import/export/bookmarklet...) |
1056 | if ($targetPage == Router::$PAGE_TOOLS) | 1083 | if ($targetPage == Router::$PAGE_TOOLS) |
1057 | { | 1084 | { |
1058 | $data = array( | 1085 | $data = [ |
1059 | 'pageabsaddr' => index_url($_SERVER), | 1086 | 'pageabsaddr' => index_url($_SERVER), |
1060 | 'sslenabled' => !empty($_SERVER['HTTPS']) | 1087 | 'sslenabled' => is_https($_SERVER), |
1061 | ); | 1088 | ]; |
1062 | $pluginManager->executeHooks('render_tools', $data); | 1089 | $pluginManager->executeHooks('render_tools', $data); |
1063 | 1090 | ||
1064 | foreach ($data as $key => $value) { | 1091 | foreach ($data as $key => $value) { |
@@ -1320,10 +1347,17 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) | |||
1320 | die('Wrong token.'); | 1347 | die('Wrong token.'); |
1321 | } | 1348 | } |
1322 | 1349 | ||
1323 | if (strpos($_GET['lf_linkdate'], ' ') !== false) { | 1350 | $ids = trim($_GET['lf_linkdate']); |
1324 | $ids = array_values(array_filter(preg_split('/\s+/', escape($_GET['lf_linkdate'])))); | 1351 | if (strpos($ids, ' ') !== false) { |
1352 | // multiple, space-separated ids provided | ||
1353 | $ids = array_values(array_filter(preg_split('/\s+/', escape($ids)))); | ||
1325 | } else { | 1354 | } else { |
1326 | $ids = [$_GET['lf_linkdate']]; | 1355 | // only a single id provided |
1356 | $ids = [$ids]; | ||
1357 | } | ||
1358 | // assert at least one id is given | ||
1359 | if(!count($ids)){ | ||
1360 | die('no id provided'); | ||
1327 | } | 1361 | } |
1328 | foreach ($ids as $id) { | 1362 | foreach ($ids as $id) { |
1329 | $id = (int) escape($id); | 1363 | $id = (int) escape($id); |
@@ -45,6 +45,7 @@ pages: | |||
45 | - Static analysis: Static-analysis.md | 45 | - Static analysis: Static-analysis.md |
46 | - Theming: Theming.md | 46 | - Theming: Theming.md |
47 | - Unit tests: Unit-tests.md | 47 | - Unit tests: Unit-tests.md |
48 | - Unit tests inside Docker: Unit-tests-Docker.md | ||
48 | - About: | 49 | - About: |
49 | - FAQ: FAQ.md | 50 | - FAQ: FAQ.md |
50 | - Community & Related software: Community-&-Related-software.md | 51 | - Community & Related software: Community-&-Related-software.md |
diff --git a/tests/HttpUtils/IsHttpsTest.php b/tests/HttpUtils/IsHttpsTest.php new file mode 100644 index 00000000..097f2bcf --- /dev/null +++ b/tests/HttpUtils/IsHttpsTest.php | |||
@@ -0,0 +1,36 @@ | |||
1 | <?php | ||
2 | |||
3 | |||
4 | /** | ||
5 | * Class IsHttpsTest | ||
6 | * | ||
7 | * Test class for is_https() function. | ||
8 | */ | ||
9 | class IsHttpsTest extends PHPUnit_Framework_TestCase | ||
10 | { | ||
11 | |||
12 | /** | ||
13 | * Test is_https with HTTPS values. | ||
14 | */ | ||
15 | public function testIsHttpsTrue() | ||
16 | { | ||
17 | $this->assertTrue(is_https(['HTTPS' => true])); | ||
18 | $this->assertTrue(is_https(['HTTPS' => '1'])); | ||
19 | $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => 443])); | ||
20 | $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '443'])); | ||
21 | $this->assertTrue(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '443,123,456,'])); | ||
22 | } | ||
23 | |||
24 | /** | ||
25 | * Test is_https with HTTP values. | ||
26 | */ | ||
27 | public function testIsHttpsFalse() | ||
28 | { | ||
29 | $this->assertFalse(is_https([])); | ||
30 | $this->assertFalse(is_https(['HTTPS' => false])); | ||
31 | $this->assertFalse(is_https(['HTTPS' => '0'])); | ||
32 | $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => 123])); | ||
33 | $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => '123'])); | ||
34 | $this->assertFalse(is_https(['HTTPS' => false, 'HTTP_X_FORWARDED_PORT' => ',123,456,'])); | ||
35 | } | ||
36 | } | ||
diff --git a/tests/languages/de/UtilsDeTest.php b/tests/languages/de/UtilsDeTest.php index 6c9c9adc..4569c923 100644 --- a/tests/languages/de/UtilsDeTest.php +++ b/tests/languages/de/UtilsDeTest.php | |||
@@ -81,12 +81,12 @@ class UtilsDeTest extends UtilsTest | |||
81 | } | 81 | } |
82 | 82 | ||
83 | /** | 83 | /** |
84 | * Test autoLocale with multiples value, the second one is valid | 84 | * Test autoLocale with multiples value, the second one is available |
85 | */ | 85 | */ |
86 | public function testAutoLocaleMultipleSecondValid() | 86 | public function testAutoLocaleMultipleSecondAvailable() |
87 | { | 87 | { |
88 | $current = setlocale(LC_ALL, 0); | 88 | $current = setlocale(LC_ALL, 0); |
89 | $header = 'pt_BR,fr-fr'; | 89 | $header = 'mag_IN,fr-fr'; |
90 | autoLocale($header); | 90 | autoLocale($header); |
91 | $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0)); | 91 | $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0)); |
92 | 92 | ||
@@ -106,12 +106,12 @@ class UtilsDeTest extends UtilsTest | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Test autoLocale with an invalid value: defaults to en_US. | 109 | * Test autoLocale with an unavailable value: defaults to en_US. |
110 | */ | 110 | */ |
111 | public function testAutoLocaleInvalid() | 111 | public function testAutoLocaleUnavailable() |
112 | { | 112 | { |
113 | $current = setlocale(LC_ALL, 0); | 113 | $current = setlocale(LC_ALL, 0); |
114 | autoLocale('pt_BR'); | 114 | autoLocale('mag_IN'); |
115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); | 115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); |
116 | 116 | ||
117 | setlocale(LC_ALL, $current); | 117 | setlocale(LC_ALL, $current); |
diff --git a/tests/languages/en/UtilsEnTest.php b/tests/languages/en/UtilsEnTest.php index d8680b2b..a74063ae 100644 --- a/tests/languages/en/UtilsEnTest.php +++ b/tests/languages/en/UtilsEnTest.php | |||
@@ -81,12 +81,12 @@ class UtilsEnTest extends UtilsTest | |||
81 | } | 81 | } |
82 | 82 | ||
83 | /** | 83 | /** |
84 | * Test autoLocale with multiples value, the second one is valid | 84 | * Test autoLocale with multiples value, the second one is available |
85 | */ | 85 | */ |
86 | public function testAutoLocaleMultipleSecondValid() | 86 | public function testAutoLocaleMultipleSecondAvailable() |
87 | { | 87 | { |
88 | $current = setlocale(LC_ALL, 0); | 88 | $current = setlocale(LC_ALL, 0); |
89 | $header = 'pt_BR,fr-fr'; | 89 | $header = 'mag_IN,fr-fr'; |
90 | autoLocale($header); | 90 | autoLocale($header); |
91 | $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0)); | 91 | $this->assertEquals('fr_FR.utf8', setlocale(LC_ALL, 0)); |
92 | 92 | ||
@@ -106,12 +106,12 @@ class UtilsEnTest extends UtilsTest | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Test autoLocale with an invalid value: defaults to en_US. | 109 | * Test autoLocale with an unavailable value: defaults to en_US. |
110 | */ | 110 | */ |
111 | public function testAutoLocaleInvalid() | 111 | public function testAutoLocaleUnavailable() |
112 | { | 112 | { |
113 | $current = setlocale(LC_ALL, 0); | 113 | $current = setlocale(LC_ALL, 0); |
114 | autoLocale('pt_BR'); | 114 | autoLocale('mag_IN'); |
115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); | 115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); |
116 | 116 | ||
117 | setlocale(LC_ALL, $current); | 117 | setlocale(LC_ALL, $current); |
diff --git a/tests/languages/fr/UtilsFrTest.php b/tests/languages/fr/UtilsFrTest.php index 0d50a878..3dbb126f 100644 --- a/tests/languages/fr/UtilsFrTest.php +++ b/tests/languages/fr/UtilsFrTest.php | |||
@@ -81,12 +81,12 @@ class UtilsFrTest extends UtilsTest | |||
81 | } | 81 | } |
82 | 82 | ||
83 | /** | 83 | /** |
84 | * Test autoLocale with multiples value, the second one is valid | 84 | * Test autoLocale with multiples value, the second one is available |
85 | */ | 85 | */ |
86 | public function testAutoLocaleMultipleSecondValid() | 86 | public function testAutoLocaleMultipleSecondAvailable() |
87 | { | 87 | { |
88 | $current = setlocale(LC_ALL, 0); | 88 | $current = setlocale(LC_ALL, 0); |
89 | $header = 'pt_BR,de-de'; | 89 | $header = 'mag_IN,de-de'; |
90 | autoLocale($header); | 90 | autoLocale($header); |
91 | $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0)); | 91 | $this->assertEquals('de_DE.utf8', setlocale(LC_ALL, 0)); |
92 | 92 | ||
@@ -106,12 +106,12 @@ class UtilsFrTest extends UtilsTest | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * Test autoLocale with an invalid value: defaults to en_US. | 109 | * Test autoLocale with an unavailable value: defaults to en_US. |
110 | */ | 110 | */ |
111 | public function testAutoLocaleInvalid() | 111 | public function testAutoLocaleUnavailable() |
112 | { | 112 | { |
113 | $current = setlocale(LC_ALL, 0); | 113 | $current = setlocale(LC_ALL, 0); |
114 | autoLocale('pt_BR'); | 114 | autoLocale('mag_IN'); |
115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); | 115 | $this->assertEquals('en_US.utf8', setlocale(LC_ALL, 0)); |
116 | 116 | ||
117 | setlocale(LC_ALL, $current); | 117 | setlocale(LC_ALL, $current); |
diff --git a/tpl/default/css/shaarli.css b/tpl/default/css/shaarli.css index e1868c59..ba589723 100644 --- a/tpl/default/css/shaarli.css +++ b/tpl/default/css/shaarli.css | |||
@@ -539,7 +539,7 @@ body, .pure-g [class*="pure-u"] { | |||
539 | } | 539 | } |
540 | 540 | ||
541 | .linklist-item-title a:visited .linklist-link { | 541 | .linklist-item-title a:visited .linklist-link { |
542 | color: #555555; | 542 | color: #2a4c41; |
543 | } | 543 | } |
544 | 544 | ||
545 | .linklist-item-title a:hover, .linklist-item-title .linklist-link:hover{ | 545 | .linklist-item-title a:hover, .linklist-item-title .linklist-link:hover{ |
diff --git a/tpl/default/js/shaarli.js b/tpl/default/js/shaarli.js index 4f49affa..1c66ebbd 100644 --- a/tpl/default/js/shaarli.js +++ b/tpl/default/js/shaarli.js | |||
@@ -401,14 +401,14 @@ window.onload = function () { | |||
401 | 401 | ||
402 | var message = 'Are you sure you want to delete '+ links.length +' links?\n'; | 402 | var message = 'Are you sure you want to delete '+ links.length +' links?\n'; |
403 | message += 'This action is IRREVERSIBLE!\n\nTitles:\n'; | 403 | message += 'This action is IRREVERSIBLE!\n\nTitles:\n'; |
404 | var ids = ''; | 404 | var ids = []; |
405 | links.forEach(function(item) { | 405 | links.forEach(function(item) { |
406 | message += ' - '+ item['title'] +'\n'; | 406 | message += ' - '+ item['title'] +'\n'; |
407 | ids += item['id'] +'+'; | 407 | ids.push(item['id']); |
408 | }); | 408 | }); |
409 | 409 | ||
410 | if (window.confirm(message)) { | 410 | if (window.confirm(message)) { |
411 | window.location = '?delete_link&lf_linkdate='+ ids +'&token='+ token.value; | 411 | window.location = '?delete_link&lf_linkdate='+ ids.join('+') +'&token='+ token.value; |
412 | } | 412 | } |
413 | }); | 413 | }); |
414 | } | 414 | } |
@@ -607,10 +607,11 @@ function htmlEntities(str) | |||
607 | function activateFirefoxSocial(node) { | 607 | function activateFirefoxSocial(node) { |
608 | var loc = location.href; | 608 | var loc = location.href; |
609 | var baseURL = loc.substring(0, loc.lastIndexOf("/") + 1); | 609 | var baseURL = loc.substring(0, loc.lastIndexOf("/") + 1); |
610 | var title = document.title; | ||
610 | 611 | ||
611 | // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable. | 612 | // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable. |
612 | var data = { | 613 | var data = { |
613 | name: "{$shaarlititle}", | 614 | name: title, |
614 | description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.", | 615 | description: "The personal, minimalist, super-fast, database free, bookmarking service by the Shaarli community.", |
615 | author: "Shaarli", | 616 | author: "Shaarli", |
616 | version: "1.0.0", | 617 | version: "1.0.0", |
diff --git a/tpl/default/loginform.html b/tpl/default/loginform.html index eb6d8378..5777a218 100644 --- a/tpl/default/loginform.html +++ b/tpl/default/loginform.html | |||
@@ -30,7 +30,8 @@ | |||
30 | </div> | 30 | </div> |
31 | <div class="remember-me"> | 31 | <div class="remember-me"> |
32 | <input type="checkbox" name="longlastingsession" id="longlastingsessionform" | 32 | <input type="checkbox" name="longlastingsession" id="longlastingsessionform" |
33 | checked="checked" tabindex="22"> | 33 | {if="$remember_user_default"}checked="checked"{/if} |
34 | tabindex="22"> | ||
34 | <label for="longlastingsessionform">{'Remember me'|t}</label> | 35 | <label for="longlastingsessionform">{'Remember me'|t}</label> |
35 | </div> | 36 | </div> |
36 | <div> | 37 | <div> |
diff --git a/tpl/vintage/loginform.html b/tpl/vintage/loginform.html index 84176385..1becd44f 100644 --- a/tpl/vintage/loginform.html +++ b/tpl/vintage/loginform.html | |||
@@ -24,7 +24,9 @@ | |||
24 | </label> | 24 | </label> |
25 | <input type="submit" value="Login" class="bigbutton" tabindex="4"> | 25 | <input type="submit" value="Login" class="bigbutton" tabindex="4"> |
26 | <label for="longlastingsession"> | 26 | <label for="longlastingsession"> |
27 | <input type="checkbox" name="longlastingsession" id="longlastingsession" tabindex="3"> | 27 | <input type="checkbox" name="longlastingsession" |
28 | id="longlastingsession" tabindex="3" | ||
29 | {if="$remember_user_default"}checked="checked"{/if}> | ||
28 | Stay signed in (Do not check on public computers)</label> | 30 | Stay signed in (Do not check on public computers)</label> |
29 | <input type="hidden" name="token" value="{$token}"> | 31 | <input type="hidden" name="token" value="{$token}"> |
30 | {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if} | 32 | {if="$returnurl"}<input type="hidden" name="returnurl" value="{$returnurl}">{/if} |