aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.dev/.sasslintrc8
-rw-r--r--.gitignore1
-rw-r--r--AUTHORS10
-rw-r--r--CHANGELOG.md45
-rw-r--r--README.md4
-rw-r--r--assets/default/scss/shaarli.scss59
-rw-r--r--doc/md/Plugin-System.md17
-rw-r--r--doc/md/Plugins.md4
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.po53
-rw-r--r--index.php25
-rw-r--r--plugins/default_colors/default_colors.css.template3
-rw-r--r--plugins/default_colors/default_colors.meta5
-rw-r--r--plugins/default_colors/default_colors.php111
-rw-r--r--plugins/demo_plugin/demo_plugin.meta3
-rw-r--r--plugins/demo_plugin/demo_plugin.php19
-rw-r--r--tests/plugins/PluginDefaultColorsTest.php195
16 files changed, 502 insertions, 60 deletions
diff --git a/.dev/.sasslintrc b/.dev/.sasslintrc
index ac406d7b..47c3145d 100644
--- a/.dev/.sasslintrc
+++ b/.dev/.sasslintrc
@@ -2,9 +2,11 @@ options:
2 max-warnings: 0 2 max-warnings: 0
3rules: 3rules:
4 property-sort-order: 4 property-sort-order:
5 - 1 5 - 0
6 - 6# Sort order rule does not work with CSS variables: https://github.com/sasstools/sass-lint/issues/1161
7 order: 'concentric' 7# - 1
8# -
9# order: 'concentric'
8 no-important: 10 no-important:
9 - 0 11 - 0
10 no-vendor-prefixes: 12 no-vendor-prefixes:
diff --git a/.gitignore b/.gitignore
index c54d9b69..b21d2118 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,6 +28,7 @@ phpdoc.xml
28 28
29# User plugin configuration 29# User plugin configuration
30plugins/*/config.php 30plugins/*/config.php
31plugins/default_colors/default_colors.css
31 32
32# HTML documentation 33# HTML documentation
33doc/html/ 34doc/html/
diff --git a/AUTHORS b/AUTHORS
index 7fa2734a..755d1bae 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,13 +1,13 @@
1 734 ArthurHoaro <arthur@hoa.ro> 1 769 ArthurHoaro <arthur@hoa.ro>
2 400 VirtualTam <virtualtam@flibidi.net> 2 401 VirtualTam <virtualtam@flibidi.net>
3 215 nodiscc <nodiscc@gmail.com> 3 216 nodiscc <nodiscc@gmail.com>
4 56 Sébastien Sauvage <sebsauvage@sebsauvage.net> 4 56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
5 15 Florian Eula <eula.florian@gmail.com> 5 15 Florian Eula <eula.florian@gmail.com>
6 13 Emilien Klein <emilien@klein.st> 6 13 Emilien Klein <emilien@klein.st>
7 13 Luce Carević <lcarevic@access42.net>
7 12 Nicolas Danelon <hi@nicolasmd.com.ar> 8 12 Nicolas Danelon <hi@nicolasmd.com.ar>
8 9 Willi Eggeling <thewilli@gmail.com> 9 9 Willi Eggeling <thewilli@gmail.com>
9 8 Christophe HENRY <christophe.henry@sbgodin.fr> 10 8 Christophe HENRY <christophe.henry@sbgodin.fr>
10 7 Luce Carević <lcarevic@access42.net>
11 6 B. van Berkum <dev@dotmpe.com> 11 6 B. van Berkum <dev@dotmpe.com>
12 6 llune <llune@users.noreply.github.com> 12 6 llune <llune@users.noreply.github.com>
13 5 Lucas Cimon <lucas.cimon@gmail.com> 13 5 Lucas Cimon <lucas.cimon@gmail.com>
@@ -16,6 +16,7 @@
16 4 Alexandre Alapetite <alexandre@alapetite.fr> 16 4 Alexandre Alapetite <alexandre@alapetite.fr>
17 4 David Sferruzza <david.sferruzza@gmail.com> 17 4 David Sferruzza <david.sferruzza@gmail.com>
18 4 Immánuel Fodor <immanuelfactor+github@gmail.com> 18 4 Immánuel Fodor <immanuelfactor+github@gmail.com>
19 3 Agurato <mail.vmonot@gmail.com>
19 3 Teromene <teromene@teromene.fr> 20 3 Teromene <teromene@teromene.fr>
20 2 Alexandre G.-Raymond <alex@ndre.gr> 21 2 Alexandre G.-Raymond <alex@ndre.gr>
21 2 Chris Kuethe <chris.kuethe@gmail.com> 22 2 Chris Kuethe <chris.kuethe@gmail.com>
@@ -57,6 +58,7 @@
57 1 Mark Gerarts <mark.gerarts@gmail.com> 58 1 Mark Gerarts <mark.gerarts@gmail.com>
58 1 Marsup <marsup@gmail.com> 59 1 Marsup <marsup@gmail.com>
59 1 Neros <contact@neros.fr> 60 1 Neros <contact@neros.fr>
61 1 Rajat Hans <rajathans9@gmail.com>
60 1 Sbgodin <Sbgodin@users.noreply.github.com> 62 1 Sbgodin <Sbgodin@users.noreply.github.com>
61 1 TsT <tst2005@gmail.com> 63 1 TsT <tst2005@gmail.com>
62 1 dimtion <zizou.xena@gmail.com> 64 1 dimtion <zizou.xena@gmail.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 865e0370..189ed13d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file.
4The format is based on [Keep a Changelog](http://keepachangelog.com/) 4The format is based on [Keep a Changelog](http://keepachangelog.com/)
5and this project adheres to [Semantic Versioning](http://semver.org/). 5and this project adheres to [Semantic Versioning](http://semver.org/).
6 6
7## [v0.11.0](https://github.com/shaarli/Shaarli/releases/tag/v0.11.0) - 2019-07-27
8
9**Shaarli no longer officially support PHP 5.6 and PHP 7.0 as they've reached end of life.**
10
11**Shaarli classes now use namespace, third party plugins need to update.**
12
13### Added
14- Add optional PHP extension to composer suggestions.
15- composer: enforce PHP security advisories
16- phpDocumentor configuration and make target
17- Run unit tests against PHP 7.3
18- Bunch of accessibility improvements to the default template, thanks to @llune
19- Bulk actions: set visibility
20- Display sticky label in linklist
21- Add print CSS rules to the default template
22- New setting to automatically retrieve description for new bookmarks
23- Plugin to override default template colors
24
25### Changed
26- Shaarli now uses namespaces for its classes.
27- Rewrite IP ban management
28- Default template: slightly lighten visited link color
29- Hide select all button on mobile view
30- Switch from FontAwesome v4.x to ForkAwesome
31- Daily - display the current day instead of the previous one
32
33### Fixed
34- Do not check the IP address with session protection disabled
35- API: update test regexes to comply with PCRE2
36- Optimize and cleanup imports
37- ensure HTML tags are stripped from OpenGraph description
38- Documentation invalid links
39- Thumbnails disabling if PHP GD is not installed
40- Warning if links sticky status isn't set
41- Fix button overlapping on mobile in linklist
42- Do not try to retrieve thumbnails for internal link
43- Update node-sass to fix a vulnerability in node tar dependency
44- armhf Dockerfile
45- Default template: Responsive issue with delete button fix
46- Persist sticky status on bookmark update
47
48### Removed
49- Doxygen configuration
50- redirector setting
51- QRCode link to an external service
7 52
8## [v0.10.4](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) - 2019-04-16 53## [v0.10.4](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) - 2019-04-16
9### Fixed 54### Fixed
diff --git a/README.md b/README.md
index 21f2eae7..6c841c63 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@ _Do you want to share the links you discover?_
6_Shaarli is a minimalist link sharing service that you can install on your own server._ 6_Shaarli is a minimalist link sharing service that you can install on your own server._
7_It is designed to be personal (single-user), fast and handy._ 7_It is designed to be personal (single-user), fast and handy._
8 8
9[![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7) 9[![](https://img.shields.io/badge/stable-v0.10.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4)
10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli) 10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
11&bull; 11&bull;
12[![](https://img.shields.io/badge/latest-v0.10.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) 12[![](https://img.shields.io/badge/latest-v0.11.0-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.11.0)
13[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli) 13[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
14&bull; 14&bull;
15[![](https://img.shields.io/badge/master-v0.11.x-blue.svg)](https://github.com/shaarli/Shaarli) 15[![](https://img.shields.io/badge/master-v0.11.x-blue.svg)](https://github.com/shaarli/Shaarli)
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index 9e5464a0..61e382b6 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -25,9 +25,15 @@ $warning-text: #97600d;
25$form-input-border: #d8d8d8; 25$form-input-border: #d8d8d8;
26$form-input-background: #eee; 26$form-input-background: #eee;
27 27
28:root {
29 --main-color: #{$main-green};
30 --background-color: #{$background-color};
31 --dark-main-color: #{$dark-green};
32}
33
28// General 34// General
29body { 35body {
30 background: $background-color; 36 background: var(--background-color);
31} 37}
32 38
33.strong { 39.strong {
@@ -143,7 +149,7 @@ body,
143} 149}
144 150
145.pure-alert-success { 151.pure-alert-success {
146 background-color: $main-green; 152 background-color: var(--main-color);
147} 153}
148 154
149.pure-alert-warning { 155.pure-alert-warning {
@@ -169,7 +175,7 @@ body,
169 top: 0; 175 top: 0;
170 transition: max-height .5s; 176 transition: max-height .5s;
171 z-index: 999; 177 z-index: 999;
172 background: $main-green; 178 background: var(--main-color);
173 width: 100%; 179 width: 100%;
174 // Hack to transition with auto height: http://stackoverflow.com/a/8331169/1484919 180 // Hack to transition with auto height: http://stackoverflow.com/a/8331169/1484919
175 max-height: 45px; 181 max-height: 45px;
@@ -322,7 +328,7 @@ body,
322 button { 328 button {
323 border: 0; 329 border: 0;
324 border-radius: 2px; 330 border-radius: 2px;
325 background-color: $main-green; 331 background-color: var(--main-color);
326 padding: 4px 8px 6px; 332 padding: 4px 8px 6px;
327 color: $almost-white; 333 color: $almost-white;
328 } 334 }
@@ -358,7 +364,7 @@ body,
358.search-tagcloud { 364.search-tagcloud {
359 button { 365 button {
360 &:hover { 366 &:hover {
361 color: $background-color; 367 color: var(--background-color);
362 } 368 }
363 } 369 }
364} 370}
@@ -389,7 +395,7 @@ body,
389 position: fixed; 395 position: fixed;
390 visibility: hidden; 396 visibility: hidden;
391 z-index: 999; 397 z-index: 999;
392 background: $main-green; 398 background: var(--main-color);
393 padding: 5px 0; 399 padding: 5px 0;
394 width: 100%; 400 width: 100%;
395 height: 30px; 401 height: 30px;
@@ -411,7 +417,7 @@ body,
411 margin: 0 0 5px; 417 margin: 0 0 5px;
412 border: 1px solid $almost-white; 418 border: 1px solid $almost-white;
413 border-radius: 2px; 419 border-radius: 2px;
414 background: $main-green; 420 background: var(--main-color);
415 padding: 4px 0; 421 padding: 4px 0;
416 width: 100px; 422 width: 100px;
417 height: 28px; 423 height: 28px;
@@ -419,7 +425,7 @@ body,
419 425
420 &:hover { 426 &:hover {
421 background: $almost-white; 427 background: $almost-white;
422 color: $main-green; 428 color: var(--main-color);
423 } 429 }
424 } 430 }
425 431
@@ -558,7 +564,7 @@ body,
558 } 564 }
559 565
560 .filter-on { 566 .filter-on {
561 background: $main-green; 567 background: var(--main-color);
562 color: $light-green; 568 color: $light-green;
563 } 569 }
564 570
@@ -697,7 +703,7 @@ body,
697 703
698 &:visited { 704 &:visited {
699 .linklist-link { 705 .linklist-link {
700 color: $dark-green; 706 color: var(--dark-main-color);
701 } 707 }
702 } 708 }
703 709
@@ -708,7 +714,7 @@ body,
708 } 714 }
709 715
710 .linklist-link { 716 .linklist-link {
711 color: $main-green; 717 color: var(--main-color);
712 font-size: 1.1em; 718 font-size: 1.1em;
713 719
714 &:hover { 720 &:hover {
@@ -783,14 +789,14 @@ body,
783 789
784 a { 790 a {
785 text-decoration: none; 791 text-decoration: none;
786 color: $main-green; 792 color: var(--main-color);
787 793
788 &:hover { 794 &:hover {
789 color: $dark-grey; 795 color: $dark-grey;
790 } 796 }
791 797
792 &:visited { 798 &:visited {
793 color: $dark-green; 799 color: var(--dark-main-color);
794 } 800 }
795 } 801 }
796} 802}
@@ -888,7 +894,7 @@ body,
888 &::before { 894 &::before {
889 display: block; 895 display: block;
890 margin: 10px auto; 896 margin: 10px auto;
891 background: linear-gradient(to right, $background-color, $dark-grey, $background-color); 897 background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
892 width: 80%; 898 width: 80%;
893 height: 1px; 899 height: 1px;
894 content: ''; 900 content: '';
@@ -917,7 +923,7 @@ body,
917 margin: 15px 5px; 923 margin: 15px 5px;
918 border: 0; 924 border: 0;
919 box-shadow: 1px 1px 1px $form-input-border, -1px -1px 6px $form-input-border, -1px 1px 2px $form-input-border, 1px -1px 2px $form-input-border; 925 box-shadow: 1px 1px 1px $form-input-border, -1px -1px 6px $form-input-border, -1px 1px 2px $form-input-border, 1px -1px 2px $form-input-border;
920 background: $main-green; 926 background: var(--main-color);
921 min-width: 150px; 927 min-width: 150px;
922 height: 35px; 928 height: 35px;
923 vertical-align: center; 929 vertical-align: center;
@@ -941,7 +947,7 @@ body,
941 padding: 10px 0; 947 padding: 10px 0;
942 width: 100%; 948 width: 100%;
943 text-align: center; 949 text-align: center;
944 color: $main-green; 950 color: var(--main-color);
945 } 951 }
946 952
947 .window-subtitle { 953 .window-subtitle {
@@ -950,7 +956,7 @@ body,
950 956
951 a { 957 a {
952 text-decoration: none; 958 text-decoration: none;
953 color: $main-green; 959 color: var(--main-color);
954 font-weight: bold; 960 font-weight: bold;
955 961
956 &.button { 962 &.button {
@@ -1072,7 +1078,6 @@ body,
1072 .page-form { 1078 .page-form {
1073 .submit-buttons { 1079 .submit-buttons {
1074 .button { 1080 .button {
1075 display: block;
1076 margin: auto; 1081 margin: auto;
1077 } 1082 }
1078 } 1083 }
@@ -1278,7 +1283,7 @@ form {
1278 1283
1279 .pure-button { 1284 .pure-button {
1280 &:hover { 1285 &:hover {
1281 background-color: $main-green; 1286 background-color: var(--main-color);
1282 background-image: none; 1287 background-image: none;
1283 color: $almost-white; 1288 color: $almost-white;
1284 } 1289 }
@@ -1362,7 +1367,7 @@ form {
1362 } 1367 }
1363 1368
1364 .validate-rename-tag { 1369 .validate-rename-tag {
1365 color: $main-green; 1370 color: var(--main-color);
1366 } 1371 }
1367} 1372}
1368 1373
@@ -1458,7 +1463,7 @@ form {
1458 &::after { 1463 &::after {
1459 display: block; 1464 display: block;
1460 margin: 10px auto; 1465 margin: 10px auto;
1461 background: linear-gradient(to right, $background-color, $dark-grey, $background-color); 1466 background: linear-gradient(to right, var(--background-color), $dark-grey, var(--background-color));
1462 width: 90%; 1467 width: 90%;
1463 height: 1px; 1468 height: 1px;
1464 content: ''; 1469 content: '';
@@ -1508,14 +1513,14 @@ form {
1508.daily-entry-description { 1513.daily-entry-description {
1509 a { 1514 a {
1510 text-decoration: none; 1515 text-decoration: none;
1511 color: $main-green; 1516 color: var(--main-color);
1512 1517
1513 &:hover { 1518 &:hover {
1514 text-shadow: 1px 1px $background-linklist-info; 1519 text-shadow: 1px 1px $background-linklist-info;
1515 } 1520 }
1516 1521
1517 &:visited { 1522 &:visited {
1518 color: $dark-green; 1523 color: var(--dark-main-color);
1519 } 1524 }
1520 } 1525 }
1521} 1526}
@@ -1572,12 +1577,12 @@ form {
1572} 1577}
1573 1578
1574.pure-button-shaarli { 1579.pure-button-shaarli {
1575 background-color: $main-green; 1580 background-color: var(--main-color);
1576} 1581}
1577 1582
1578.progressbar { 1583.progressbar {
1579 border-radius: 6px; 1584 border-radius: 6px;
1580 background-color: $main-green; 1585 background-color: var(--main-color);
1581 padding: 1px; 1586 padding: 1px;
1582 1587
1583 > div { 1588 > div {
@@ -1586,8 +1591,8 @@ form {
1586 -45deg, 1591 -45deg,
1587 $almost-white, 1592 $almost-white,
1588 $almost-white 6px, 1593 $almost-white 6px,
1589 $background-color 6px, 1594 var(--background-color) 6px,
1590 $background-color 12px 1595 var(--background-color) 12px
1591 ); 1596 );
1592 width: 0%; 1597 width: 0%;
1593 height: 10px; 1598 height: 10px;
diff --git a/doc/md/Plugin-System.md b/doc/md/Plugin-System.md
index cbec04c0..9b0d3a7d 100644
--- a/doc/md/Plugin-System.md
+++ b/doc/md/Plugin-System.md
@@ -137,6 +137,7 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha
137| [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. | 137| [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |
138| [save_link](#save_link) | Allow to alter the link being saved in the datastore. | 138| [save_link](#save_link) | Allow to alter the link being saved in the datastore. |
139| [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. | 139| [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |
140| [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
140 141
141 142
142 143
@@ -471,6 +472,22 @@ Allow to execute any action before the link is actually removed from the datasto
471- created 472- created
472- updated 473- updated
473 474
475
476#### save_plugin_parameters
477
478Triggered when the plugin parameters are saved from the plugin administration page.
479
480Plugins can perform an action every times their settings are updated.
481For example it is used to update the CSS file of the `default_colors` plugins.
482
483##### Data
484
485`$data` input contains the `$_POST` array.
486
487So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
488the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
489
490
474## Guide for template designer 491## Guide for template designer
475 492
476### Plugin administration 493### Plugin administration
diff --git a/doc/md/Plugins.md b/doc/md/Plugins.md
index 954442e2..3e261815 100644
--- a/doc/md/Plugins.md
+++ b/doc/md/Plugins.md
@@ -63,8 +63,12 @@ Usage of each plugin is documented in it's README file:
63 63
64 * `addlink-toolbar`: Adds the addlink input on the linklist page 64 * `addlink-toolbar`: Adds the addlink input on the linklist page
65 * `archiveorg`: For each link, add an Archive.org icon 65 * `archiveorg`: For each link, add an Archive.org icon
66 * `default_colors`: Override default theme colors.
67 * `isso`: Let visitor comment your shaares on permalinks with Isso.
66 * [`markdown`](https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md): Render shaare description with Markdown syntax. 68 * [`markdown`](https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md): Render shaare description with Markdown syntax.
69 * `piwik`: A plugin that adds Piwik tracking code to Shaarli pages.
67 * [`playvideos`](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md): Add a button in the toolbar allowing to watch all videos. 70 * [`playvideos`](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md): Add a button in the toolbar allowing to watch all videos.
71 * `pubsubhubbub`: Enable PubSubHubbub feed publishing
68 * `qrcode`: For each link, add a QRCode icon. 72 * `qrcode`: For each link, add a QRCode icon.
69 * [`wallabag`](https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md): For each link, add a Wallabag icon to save it in your instance. 73 * [`wallabag`](https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md): For each link, add a Wallabag icon to save it in your instance.
70 74
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
index 611296f1..026d0101 100644
--- a/inc/languages/fr/LC_MESSAGES/shaarli.po
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.po
@@ -1,8 +1,8 @@
1msgid "" 1msgid ""
2msgstr "" 2msgstr ""
3"Project-Id-Version: Shaarli\n" 3"Project-Id-Version: Shaarli\n"
4"POT-Creation-Date: 2019-07-06 12:14+0200\n" 4"POT-Creation-Date: 2019-07-13 10:45+0200\n"
5"PO-Revision-Date: 2019-07-06 12:17+0200\n" 5"PO-Revision-Date: 2019-07-13 10:49+0200\n"
6"Last-Translator: \n" 6"Last-Translator: \n"
7"Language-Team: Shaarli\n" 7"Language-Team: Shaarli\n"
8"Language: fr_FR\n" 8"Language: fr_FR\n"
@@ -403,7 +403,7 @@ msgstr "Note : "
403 403
404#: index.php:1424 404#: index.php:1424
405msgid "Invalid link ID provided" 405msgid "Invalid link ID provided"
406msgstr "" 406msgstr "ID du lien non valide"
407 407
408#: index.php:1444 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65 408#: index.php:1444 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65
409msgid "Export" 409msgid "Export"
@@ -428,15 +428,15 @@ msgstr ""
428msgid "Plugin administration" 428msgid "Plugin administration"
429msgstr "Administration des plugins" 429msgstr "Administration des plugins"
430 430
431#: index.php:1615 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 431#: index.php:1616 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
432msgid "Thumbnails update" 432msgid "Thumbnails update"
433msgstr "Mise à jour des miniatures" 433msgstr "Mise à jour des miniatures"
434 434
435#: index.php:1781 435#: index.php:1782
436msgid "Search: " 436msgid "Search: "
437msgstr "Recherche : " 437msgstr "Recherche : "
438 438
439#: index.php:1824 439#: index.php:1825
440#, php-format 440#, php-format
441msgid "" 441msgid ""
442"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the " 442"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
@@ -455,7 +455,7 @@ msgstr ""
455"des cookies. Nous vous recommandons d'accéder à votre serveur depuis son " 455"des cookies. Nous vous recommandons d'accéder à votre serveur depuis son "
456"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>" 456"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>"
457 457
458#: index.php:1834 458#: index.php:1835
459msgid "Click to try again." 459msgid "Click to try again."
460msgstr "Cliquer ici pour réessayer." 460msgstr "Cliquer ici pour réessayer."
461 461
@@ -480,7 +480,33 @@ msgstr "Voir sur archive.org"
480msgid "For each link, add an Archive.org icon." 480msgid "For each link, add an Archive.org icon."
481msgstr "Pour chaque lien, ajoute une icône pour Archive.org." 481msgstr "Pour chaque lien, ajoute une icône pour Archive.org."
482 482
483#: plugins/demo_plugin/demo_plugin.php:465 483#: plugins/default_colors/default_colors.php:33
484msgid ""
485"Default colors plugin error: This plugin is active and no custom color is "
486"configured."
487msgstr ""
488"Erreur du plugin default colors : ce plugin est actif et aucune couleur "
489"n'est configurée."
490
491#: plugins/default_colors/default_colors.php:107
492msgid "Override default theme colors. Use any CSS valid color."
493msgstr ""
494"Remplacer les couleurs du thème par défaut. Utiliser n'importe quelle "
495"couleur CSS valide."
496
497#: plugins/default_colors/default_colors.php:108
498msgid "Main color (navbar green)"
499msgstr "Couleur principale (vert de la barre de navigation)"
500
501#: plugins/default_colors/default_colors.php:109
502msgid "Background color (light grey)"
503msgstr "Couleur de fond (gris léger)"
504
505#: plugins/default_colors/default_colors.php:110
506msgid "Dark main color (e.g. visited links)"
507msgstr "Couleur principale sombre (ex : les liens visités)"
508
509#: plugins/demo_plugin/demo_plugin.php:482
484msgid "" 510msgid ""
485"A demo plugin covering all use cases for template designers and plugin " 511"A demo plugin covering all use cases for template designers and plugin "
486"developers." 512"developers."
@@ -488,6 +514,14 @@ msgstr ""
488"Une extension de démonstration couvrant tous les cas d'utilisation pour les " 514"Une extension de démonstration couvrant tous les cas d'utilisation pour les "
489"designers de thèmes et les développeurs d'extensions." 515"designers de thèmes et les développeurs d'extensions."
490 516
517#: plugins/demo_plugin/demo_plugin.php:483
518msgid "This is a parameter dedicated to the demo plugin. It'll be suffixed."
519msgstr "Ceci est un paramètre dédié au plugin de démo. Il sera suffixé."
520
521#: plugins/demo_plugin/demo_plugin.php:484
522msgid "Other demo parameter"
523msgstr "Un autre paramètre de démo"
524
491#: plugins/isso/isso.php:22 525#: plugins/isso/isso.php:22
492msgid "" 526msgid ""
493"Isso plugin error: Please define the \"ISSO_SERVER\" setting in the plugin " 527"Isso plugin error: Please define the \"ISSO_SERVER\" setting in the plugin "
@@ -703,9 +737,8 @@ msgstr ""
703"miniatures." 737"miniatures."
704 738
705#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:162 739#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:162
706#| msgid "Enable thumbnails"
707msgid "Synchonize thumbnails" 740msgid "Synchonize thumbnails"
708msgstr "" 741msgstr "Synchroniser les miniatures"
709 742
710#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29 743#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
711msgid "title" 744msgid "title"
diff --git a/index.php b/index.php
index 957d8d9a..9e473936 100644
--- a/index.php
+++ b/index.php
@@ -1126,22 +1126,24 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1126 1126
1127 // lf_id should only be present if the link exists. 1127 // lf_id should only be present if the link exists.
1128 $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId(); 1128 $id = isset($_POST['lf_id']) ? intval(escape($_POST['lf_id'])) : $LINKSDB->getNextId();
1129 $link['id'] = $id;
1129 // Linkdate is kept here to: 1130 // Linkdate is kept here to:
1130 // - use the same permalink for notes as they're displayed when creating them 1131 // - use the same permalink for notes as they're displayed when creating them
1131 // - let users hack creation date of their posts 1132 // - let users hack creation date of their posts
1132 // See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare 1133 // See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare
1133 $linkdate = escape($_POST['lf_linkdate']); 1134 $linkdate = escape($_POST['lf_linkdate']);
1135 $link['created'] = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate);
1134 if (isset($LINKSDB[$id])) { 1136 if (isset($LINKSDB[$id])) {
1135 // Edit 1137 // Edit
1136 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); 1138 $link['updated'] = new DateTime();
1137 $updated = new DateTime(); 1139 $link['shorturl'] = $LINKSDB[$id]['shorturl'];
1138 $shortUrl = $LINKSDB[$id]['shorturl']; 1140 $link['sticky'] = isset($LINKSDB[$id]['sticky']) ? $LINKSDB[$id]['sticky'] : false;
1139 $new = false; 1141 $new = false;
1140 } else { 1142 } else {
1141 // New link 1143 // New link
1142 $created = DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $linkdate); 1144 $link['updated'] = null;
1143 $updated = null; 1145 $link['shorturl'] = link_small_hash($link['created'], $id);
1144 $shortUrl = link_small_hash($created, $id); 1146 $link['sticky'] = false;
1145 $new = true; 1147 $new = true;
1146 } 1148 }
1147 1149
@@ -1157,17 +1159,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1157 } 1159 }
1158 $url = whitelist_protocols(trim($_POST['lf_url']), $conf->get('security.allowed_protocols')); 1160 $url = whitelist_protocols(trim($_POST['lf_url']), $conf->get('security.allowed_protocols'));
1159 1161
1160 $link = array( 1162 $link = array_merge($link, [
1161 'id' => $id,
1162 'title' => trim($_POST['lf_title']), 1163 'title' => trim($_POST['lf_title']),
1163 'url' => $url, 1164 'url' => $url,
1164 'description' => $_POST['lf_description'], 1165 'description' => $_POST['lf_description'],
1165 'private' => (isset($_POST['lf_private']) ? 1 : 0), 1166 'private' => (isset($_POST['lf_private']) ? 1 : 0),
1166 'created' => $created,
1167 'updated' => $updated,
1168 'tags' => str_replace(',', ' ', $tags), 1167 'tags' => str_replace(',', ' ', $tags),
1169 'shorturl' => $shortUrl, 1168 ]);
1170 );
1171 1169
1172 // If title is empty, use the URL as title. 1170 // If title is empty, use the URL as title.
1173 if ($link['title'] == '') { 1171 if ($link['title'] == '') {
@@ -1181,8 +1179,6 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1181 $link['thumbnail'] = $thumbnailer->get($url); 1179 $link['thumbnail'] = $thumbnailer->get($url);
1182 } 1180 }
1183 1181
1184 $link['sticky'] = isset($link['sticky']) ? $link['sticky'] : false;
1185
1186 $pluginManager->executeHooks('save_link', $link); 1182 $pluginManager->executeHooks('save_link', $link);
1187 1183
1188 $LINKSDB[$id] = $link; 1184 $LINKSDB[$id] = $link;
@@ -1567,6 +1563,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1567 if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) { 1563 if ($targetPage == Router::$PAGE_SAVE_PLUGINSADMIN) {
1568 try { 1564 try {
1569 if (isset($_POST['parameters_form'])) { 1565 if (isset($_POST['parameters_form'])) {
1566 $pluginManager->executeHooks('save_plugin_parameters', $_POST);
1570 unset($_POST['parameters_form']); 1567 unset($_POST['parameters_form']);
1571 foreach ($_POST as $param => $value) { 1568 foreach ($_POST as $param => $value) {
1572 $conf->set('plugins.'. $param, escape($value)); 1569 $conf->set('plugins.'. $param, escape($value));
diff --git a/plugins/default_colors/default_colors.css.template b/plugins/default_colors/default_colors.css.template
new file mode 100644
index 00000000..87e22a08
--- /dev/null
+++ b/plugins/default_colors/default_colors.css.template
@@ -0,0 +1,3 @@
1:root {
2%s
3}
diff --git a/plugins/default_colors/default_colors.meta b/plugins/default_colors/default_colors.meta
new file mode 100644
index 00000000..108962c6
--- /dev/null
+++ b/plugins/default_colors/default_colors.meta
@@ -0,0 +1,5 @@
1description="Override default theme colors. Use any CSS valid color."
2parameters="DEFAULT_COLORS_MAIN;DEFAULT_COLORS_BACKGROUND;DEFAULT_COLORS_DARK_MAIN"
3parameter.DEFAULT_COLORS_MAIN="Main color (navbar green)"
4parameter.DEFAULT_COLORS_BACKGROUND="Background color (light grey)"
5parameter.DEFAULT_COLORS_DARK_MAIN="Dark main color (e.g. visited links)"
diff --git a/plugins/default_colors/default_colors.php b/plugins/default_colors/default_colors.php
new file mode 100644
index 00000000..1928cc9f
--- /dev/null
+++ b/plugins/default_colors/default_colors.php
@@ -0,0 +1,111 @@
1<?php
2
3/**
4 * Plugin default_colors.
5 *
6 * Allow users to easily overrides colors of the default theme.
7 */
8
9use Shaarli\Config\ConfigManager;
10use Shaarli\Plugin\PluginManager;
11
12const DEFAULT_COLORS_PLACEHOLDERS = [
13 'DEFAULT_COLORS_MAIN',
14 'DEFAULT_COLORS_BACKGROUND',
15 'DEFAULT_COLORS_DARK_MAIN',
16];
17
18/**
19 * Display an error if the plugin is active a no color is configured.
20 *
21 * @param $conf ConfigManager instance
22 *
23 * @return array|null The errors array or null of there is none.
24 */
25function default_colors_init($conf)
26{
27 $params = '';
28 foreach (DEFAULT_COLORS_PLACEHOLDERS as $placeholder) {
29 $params .= trim($conf->get('plugins.'. $placeholder, ''));
30 }
31
32 if (empty($params)) {
33 $error = t('Default colors plugin error: '.
34 'This plugin is active and no custom color is configured.');
35 return array($error);
36 }
37}
38
39/**
40 * When plugin parameters are saved, we regenerate the custom CSS file with provided settings.
41 *
42 * @param array $data $_POST array
43 *
44 * @return array Updated $_POST array
45 */
46function hook_default_colors_save_plugin_parameters($data)
47{
48 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
49 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css.template');
50 $content = '';
51 foreach (DEFAULT_COLORS_PLACEHOLDERS as $rule) {
52 $content .= ! empty($data[$rule])
53 ? default_colors_format_css_rule($data, $rule) .';'. PHP_EOL
54 : '';
55 }
56
57 if (! empty($content)) {
58 file_put_contents($file, sprintf($template, $content));
59 }
60
61 return $data;
62}
63
64/**
65 * When linklist is displayed, include default_colors CSS file.
66 *
67 * @param array $data - header data.
68 *
69 * @return mixed - header data with default_colors CSS file added.
70 */
71function hook_default_colors_render_includes($data)
72{
73 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
74 if (file_exists($file )) {
75 $data['css_files'][] = $file ;
76 }
77
78 return $data;
79}
80
81/**
82 * Create a valid CSS rule from parameters settings and plugin parameter.
83 *
84 * @param array $data $_POST array
85 * @param string $parameter Plugin parameter name
86 *
87 * @return string CSS rules for the provided parameter and its matching value.
88 */
89function default_colors_format_css_rule($data, $parameter)
90{
91 if (empty($data[$parameter])) {
92 return '';
93 }
94
95 $key = str_replace('DEFAULT_COLORS_', '', $parameter);
96 $key = str_replace('_', '-', strtolower($key)) .'-color';
97 return ' --'. $key .': '. $data[$parameter];
98}
99
100
101/**
102 * This function is never called, but contains translation calls for GNU gettext extraction.
103 */
104function default_colors_translation()
105{
106 // meta
107 t('Override default theme colors. Use any CSS valid color.');
108 t('Main color (navbar green)');
109 t('Background color (light grey)');
110 t('Dark main color (e.g. visited links)');
111}
diff --git a/plugins/demo_plugin/demo_plugin.meta b/plugins/demo_plugin/demo_plugin.meta
index b063ecb7..cd616441 100644
--- a/plugins/demo_plugin/demo_plugin.meta
+++ b/plugins/demo_plugin/demo_plugin.meta
@@ -1 +1,4 @@
1description="A demo plugin covering all use cases for template designers and plugin developers." 1description="A demo plugin covering all use cases for template designers and plugin developers."
2parameters="DEMO_PLUGIN_PARAMETER;DEMO_PLUGIN_OTHER_PARAMETER"
3parameter.DEMO_PLUGIN_PARAMETER="This is a parameter dedicated to the demo plugin. It'll be suffixed."
4parameter.DEMO_PLUGIN_OTHER_PARAMETER="Other demo parameter"
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php
index 95ea7fe2..71ba7495 100644
--- a/plugins/demo_plugin/demo_plugin.php
+++ b/plugins/demo_plugin/demo_plugin.php
@@ -457,10 +457,29 @@ function hook_demo_plugin_render_feed($data)
457} 457}
458 458
459/** 459/**
460 * When plugin parameters are saved.
461 *
462 * @param array $data $_POST array
463 *
464 * @return array Updated $_POST array
465 */
466function hook_demo_plugin_save_plugin_parameters($data)
467{
468 // Here we edit the provided value, but we can use this to generate config files, etc.
469 if (! empty($data['DEMO_PLUGIN_PARAMETER']) && ! endsWith($data['DEMO_PLUGIN_PARAMETER'], '_SUFFIX')) {
470 $data['DEMO_PLUGIN_PARAMETER'] .= '_SUFFIX';
471 }
472
473 return $data;
474}
475
476/**
460 * This function is never called, but contains translation calls for GNU gettext extraction. 477 * This function is never called, but contains translation calls for GNU gettext extraction.
461 */ 478 */
462function demo_dummy_translation() 479function demo_dummy_translation()
463{ 480{
464 // meta 481 // meta
465 t('A demo plugin covering all use cases for template designers and plugin developers.'); 482 t('A demo plugin covering all use cases for template designers and plugin developers.');
483 t('This is a parameter dedicated to the demo plugin. It\'ll be suffixed.');
484 t('Other demo parameter');
466} 485}
diff --git a/tests/plugins/PluginDefaultColorsTest.php b/tests/plugins/PluginDefaultColorsTest.php
new file mode 100644
index 00000000..b9951cca
--- /dev/null
+++ b/tests/plugins/PluginDefaultColorsTest.php
@@ -0,0 +1,195 @@
1<?php
2
3namespace Shaarli\Plugin\DefaultColors;
4
5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Plugin\PluginManager;
10
11require_once 'plugins/default_colors/default_colors.php';
12
13/**
14 * Class PluginDefaultColorsTest
15 *
16 * Test the DefaultColors plugin (allowing to override default template colors).
17 */
18class PluginDefaultColorsTest extends TestCase
19{
20 /**
21 * Reset plugin path
22 */
23 public function setUp()
24 {
25 PluginManager::$PLUGINS_PATH = 'sandbox';
26 mkdir(PluginManager::$PLUGINS_PATH . '/default_colors/');
27 copy(
28 'plugins/default_colors/default_colors.css.template',
29 PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css.template'
30 );
31 }
32
33 /**
34 * Remove sandbox files and folder
35 */
36 public function tearDown()
37 {
38 if (file_exists('sandbox/default_colors/default_colors.css.template')) {
39 unlink('sandbox/default_colors/default_colors.css.template');
40 }
41
42 if (file_exists('sandbox/default_colors/default_colors.css')) {
43 unlink('sandbox/default_colors/default_colors.css');
44 }
45
46 if (is_dir('sandbox/default_colors')) {
47 rmdir('sandbox/default_colors');
48 }
49 }
50
51 /**
52 * Test DefaultColors init without errors.
53 */
54 public function testDefaultColorsInitNoError()
55 {
56 $conf = new ConfigManager('');
57 $conf->set('plugins.DEFAULT_COLORS_BACKGROUND', 'value');
58 $errors = default_colors_init($conf);
59 $this->assertEmpty($errors);
60 }
61
62 /**
63 * Test DefaultColors init with errors.
64 */
65 public function testDefaultColorsInitError()
66 {
67 $conf = new ConfigManager('');
68 $errors = default_colors_init($conf);
69 $this->assertNotEmpty($errors);
70 }
71
72 /**
73 * Test the save plugin parameters hook with all colors specified.
74 */
75 public function testSavePluginParametersAll()
76 {
77 $post = [
78 'other1' => true,
79 'DEFAULT_COLORS_MAIN' => 'blue',
80 'DEFAULT_COLORS_BACKGROUND' => 'pink',
81 'other2' => ['yep'],
82 'DEFAULT_COLORS_DARK_MAIN' => 'green',
83 ];
84
85 hook_default_colors_save_plugin_parameters($post);
86 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
87 $content = file_get_contents($file);
88 $expected = ':root {
89 --main-color: blue;
90 --background-color: pink;
91 --dark-main-color: green;
92
93}
94';
95 $this->assertEquals($expected, $content);
96 }
97
98 /**
99 * Test the save plugin parameters hook with only one color specified.
100 */
101 public function testSavePluginParametersSingle()
102 {
103 $post = [
104 'other1' => true,
105 'DEFAULT_COLORS_BACKGROUND' => 'pink',
106 'other2' => ['yep'],
107 'DEFAULT_COLORS_DARK_MAIN' => '',
108 ];
109
110 hook_default_colors_save_plugin_parameters($post);
111 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
112 $content = file_get_contents($file);
113 $expected = ':root {
114 --background-color: pink;
115
116}
117';
118 $this->assertEquals($expected, $content);
119 }
120
121 /**
122 * Test the save plugin parameters hook with no color specified.
123 */
124 public function testSavePluginParametersNone()
125 {
126 hook_default_colors_save_plugin_parameters([]);
127 $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css');
128 }
129
130 /**
131 * Make sure that the CSS is properly included by the include hook.
132 */
133 public function testIncludeWithFile()
134 {
135 $data = [
136 'css_files' => ['file1'],
137 'js_files' => ['file2'],
138 ];
139 touch($file = 'sandbox/default_colors/default_colors.css');
140 $processedData = hook_default_colors_render_includes($data);
141
142 $this->assertCount(2, $processedData['css_files']);
143 $this->assertEquals($file, $processedData['css_files'][1]);
144 $this->assertCount(1, $processedData['js_files']);
145 }
146
147 /**
148 * Make sure that the CSS is not included by the include hook if the CSS file does not exist.
149 */
150 public function testIncludeWithoutFile()
151 {
152 $data = [
153 'css_files' => ['file1'],
154 'js_files' => ['file2'],
155 ];
156 $processedData = hook_default_colors_render_includes($data);
157
158 $this->assertEquals($data, $processedData);
159 }
160
161 /**
162 * Test helper function which generates CSS rules with valid input.
163 */
164 public function testFormatCssRuleValid()
165 {
166 $data = [
167 'other1' => true,
168 'DEFAULT_COLORS_BLIP_BLOP' => 'shinyColor',
169 'other2' => ['yep'],
170 ];
171 $result = default_colors_format_css_rule($data, 'DEFAULT_COLORS_BLIP_BLOP');
172 $this->assertEquals(' --blip-blop-color: shinyColor', $result);
173
174 $data = ['unknown-parameter' => true];
175 $result = default_colors_format_css_rule($data, 'unknown-parameter');
176 $this->assertEquals(' --unknown-parameter-color: 1', $result);
177 }
178
179 /**
180 * Test helper function which generates CSS rules with invalid input.
181 */
182 public function testFormatCssRuleInvalid()
183 {
184 $result = default_colors_format_css_rule([], 'DEFAULT_COLORS_BLIP_BLOP');
185 $this->assertEmpty($result);
186
187 $data = [
188 'other1' => true,
189 'DEFAULT_COLORS_BLIP_BLOP' => 'shinyColor',
190 'other2' => ['yep'],
191 ];
192 $result = default_colors_format_css_rule($data, '');
193 $this->assertEmpty($result);
194 }
195}