aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--application/Languages.php3
-rw-r--r--application/bookmark/BookmarkArray.php9
-rw-r--r--application/bookmark/BookmarkFileService.php13
-rw-r--r--application/bookmark/BookmarkServiceInterface.php9
-rw-r--r--application/feed/FeedBuilder.php2
-rw-r--r--application/front/ShaarliMiddleware.php2
-rw-r--r--application/legacy/LegacyController.php32
-rw-r--r--application/render/PageBuilder.php2
-rw-r--r--assets/default/js/base.js19
-rw-r--r--assets/default/scss/shaarli.scss5
-rw-r--r--inc/languages/jp/LC_MESSAGES/shaarli.po (renamed from inc/languages/ja/LC_MESSAGES/shaarli.po)0
-rw-r--r--plugins/default_colors/default_colors.php56
-rw-r--r--tests/feed/FeedBuilderTest.php12
-rw-r--r--tests/front/controller/admin/ConfigureControllerTest.php2
-rw-r--r--tests/legacy/LegacyControllerTest.php8
-rw-r--r--tests/plugins/PluginDefaultColorsTest.php19
-rw-r--r--tpl/default/linklist.html12
-rw-r--r--tpl/default/linklist.paging.html13
18 files changed, 151 insertions, 67 deletions
diff --git a/application/Languages.php b/application/Languages.php
index 5cda802e..d83e0765 100644
--- a/application/Languages.php
+++ b/application/Languages.php
@@ -179,9 +179,10 @@ class Languages
179 { 179 {
180 return [ 180 return [
181 'auto' => t('Automatic'), 181 'auto' => t('Automatic'),
182 'de' => t('German'),
182 'en' => t('English'), 183 'en' => t('English'),
183 'fr' => t('French'), 184 'fr' => t('French'),
184 'de' => t('German'), 185 'jp' => t('Japanese'),
185 ]; 186 ];
186 } 187 }
187} 188}
diff --git a/application/bookmark/BookmarkArray.php b/application/bookmark/BookmarkArray.php
index d87d43b4..3bd5eb20 100644
--- a/application/bookmark/BookmarkArray.php
+++ b/application/bookmark/BookmarkArray.php
@@ -234,16 +234,17 @@ class BookmarkArray implements \Iterator, \Countable, \ArrayAccess
234 * 234 *
235 * Also update the urls and ids mapping arrays. 235 * Also update the urls and ids mapping arrays.
236 * 236 *
237 * @param string $order ASC|DESC 237 * @param string $order ASC|DESC
238 * @param bool $ignoreSticky If set to true, sticky bookmarks won't be first
238 */ 239 */
239 public function reorder($order = 'DESC') 240 public function reorder(string $order = 'DESC', bool $ignoreSticky = false): void
240 { 241 {
241 $order = $order === 'ASC' ? -1 : 1; 242 $order = $order === 'ASC' ? -1 : 1;
242 // Reorder array by dates. 243 // Reorder array by dates.
243 usort($this->bookmarks, function ($a, $b) use ($order) { 244 usort($this->bookmarks, function ($a, $b) use ($order, $ignoreSticky) {
244 /** @var $a Bookmark */ 245 /** @var $a Bookmark */
245 /** @var $b Bookmark */ 246 /** @var $b Bookmark */
246 if ($a->isSticky() !== $b->isSticky()) { 247 if (false === $ignoreSticky && $a->isSticky() !== $b->isSticky()) {
247 return $a->isSticky() ? -1 : 1; 248 return $a->isSticky() ? -1 : 1;
248 } 249 }
249 return $a->getCreated() < $b->getCreated() ? 1 * $order : -1 * $order; 250 return $a->getCreated() < $b->getCreated() ? 1 * $order : -1 * $order;
diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php
index b3a90ed4..e3a61146 100644
--- a/application/bookmark/BookmarkFileService.php
+++ b/application/bookmark/BookmarkFileService.php
@@ -114,8 +114,13 @@ class BookmarkFileService implements BookmarkServiceInterface
114 /** 114 /**
115 * @inheritDoc 115 * @inheritDoc
116 */ 116 */
117 public function search($request = [], $visibility = null, $caseSensitive = false, $untaggedOnly = false) 117 public function search(
118 { 118 $request = [],
119 $visibility = null,
120 $caseSensitive = false,
121 $untaggedOnly = false,
122 bool $ignoreSticky = false
123 ) {
119 if ($visibility === null) { 124 if ($visibility === null) {
120 $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; 125 $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC;
121 } 126 }
@@ -124,6 +129,10 @@ class BookmarkFileService implements BookmarkServiceInterface
124 $searchtags = isset($request['searchtags']) ? $request['searchtags'] : ''; 129 $searchtags = isset($request['searchtags']) ? $request['searchtags'] : '';
125 $searchterm = isset($request['searchterm']) ? $request['searchterm'] : ''; 130 $searchterm = isset($request['searchterm']) ? $request['searchterm'] : '';
126 131
132 if ($ignoreSticky) {
133 $this->bookmarks->reorder('DESC', true);
134 }
135
127 return $this->bookmarkFilter->filter( 136 return $this->bookmarkFilter->filter(
128 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, 137 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
129 [$searchtags, $searchterm], 138 [$searchtags, $searchterm],
diff --git a/application/bookmark/BookmarkServiceInterface.php b/application/bookmark/BookmarkServiceInterface.php
index ce8bd912..b9b483eb 100644
--- a/application/bookmark/BookmarkServiceInterface.php
+++ b/application/bookmark/BookmarkServiceInterface.php
@@ -49,10 +49,17 @@ interface BookmarkServiceInterface
49 * @param string $visibility 49 * @param string $visibility
50 * @param bool $caseSensitive 50 * @param bool $caseSensitive
51 * @param bool $untaggedOnly 51 * @param bool $untaggedOnly
52 * @param bool $ignoreSticky
52 * 53 *
53 * @return Bookmark[] 54 * @return Bookmark[]
54 */ 55 */
55 public function search($request = [], $visibility = null, $caseSensitive = false, $untaggedOnly = false); 56 public function search(
57 $request = [],
58 $visibility = null,
59 $caseSensitive = false,
60 $untaggedOnly = false,
61 bool $ignoreSticky = false
62 );
56 63
57 /** 64 /**
58 * Get a single bookmark by its ID. 65 * Get a single bookmark by its ID.
diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php
index 269ad877..3653c32f 100644
--- a/application/feed/FeedBuilder.php
+++ b/application/feed/FeedBuilder.php
@@ -102,7 +102,7 @@ class FeedBuilder
102 } 102 }
103 103
104 // Optionally filter the results: 104 // Optionally filter the results:
105 $linksToDisplay = $this->linkDB->search($userInput); 105 $linksToDisplay = $this->linkDB->search($userInput, null, false, false, true);
106 106
107 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput); 107 $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput);
108 108
diff --git a/application/front/ShaarliMiddleware.php b/application/front/ShaarliMiddleware.php
index c015c0c6..d1aa1399 100644
--- a/application/front/ShaarliMiddleware.php
+++ b/application/front/ShaarliMiddleware.php
@@ -94,7 +94,7 @@ class ShaarliMiddleware
94 && $this->container->conf->get('privacy.force_login') 94 && $this->container->conf->get('privacy.force_login')
95 // and the current page isn't already the login page 95 // and the current page isn't already the login page
96 // and the user is not requesting a feed (which would lead to a different content-type as expected) 96 // and the user is not requesting a feed (which would lead to a different content-type as expected)
97 && !in_array($next->getName(), ['login', 'atom', 'rss'], true) 97 && !in_array($next->getName(), ['login', 'processLogin', 'atom', 'rss'], true)
98 ) { 98 ) {
99 throw new UnauthorizedException(); 99 throw new UnauthorizedException();
100 } 100 }
diff --git a/application/legacy/LegacyController.php b/application/legacy/LegacyController.php
index 26465d2c..e16dd0f4 100644
--- a/application/legacy/LegacyController.php
+++ b/application/legacy/LegacyController.php
@@ -40,28 +40,33 @@ class LegacyController extends ShaarliVisitorController
40 public function post(Request $request, Response $response): Response 40 public function post(Request $request, Response $response): Response
41 { 41 {
42 $parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : ''; 42 $parameters = count($request->getQueryParams()) > 0 ? '?' . http_build_query($request->getQueryParams()) : '';
43 $route = '/admin/shaare';
43 44
44 if (!$this->container->loginManager->isLoggedIn()) { 45 if (!$this->container->loginManager->isLoggedIn()) {
45 return $this->redirect($response, '/login' . $parameters); 46 return $this->redirect($response, '/login?returnurl='. $this->getBasePath() . $route . $parameters);
46 } 47 }
47 48
48 return $this->redirect($response, '/admin/shaare' . $parameters); 49 return $this->redirect($response, $route . $parameters);
49 } 50 }
50 51
51 /** Legacy route: ?addlink= */ 52 /** Legacy route: ?addlink= */
52 protected function addlink(Request $request, Response $response): Response 53 protected function addlink(Request $request, Response $response): Response
53 { 54 {
55 $route = '/admin/add-shaare';
56
54 if (!$this->container->loginManager->isLoggedIn()) { 57 if (!$this->container->loginManager->isLoggedIn()) {
55 return $this->redirect($response, '/login'); 58 return $this->redirect($response, '/login?returnurl=' . $this->getBasePath() . $route);
56 } 59 }
57 60
58 return $this->redirect($response, '/admin/add-shaare'); 61 return $this->redirect($response, $route);
59 } 62 }
60 63
61 /** Legacy route: ?do=login */ 64 /** Legacy route: ?do=login */
62 protected function login(Request $request, Response $response): Response 65 protected function login(Request $request, Response $response): Response
63 { 66 {
64 return $this->redirect($response, '/login'); 67 $returnUrl = $request->getQueryParam('returnurl');
68
69 return $this->redirect($response, '/login' . ($returnUrl ? '?returnurl=' . $returnUrl : ''));
65 } 70 }
66 71
67 /** Legacy route: ?do=logout */ 72 /** Legacy route: ?do=logout */
@@ -127,4 +132,21 @@ class LegacyController extends ShaarliVisitorController
127 132
128 return $this->redirect($response, '/feed/' . $feedType . $parameters); 133 return $this->redirect($response, '/feed/' . $feedType . $parameters);
129 } 134 }
135
136 /** Legacy route: ?do=configure */
137 protected function configure(Request $request, Response $response): Response
138 {
139 $route = '/admin/configure';
140
141 if (!$this->container->loginManager->isLoggedIn()) {
142 return $this->redirect($response, '/login?returnurl=' . $this->getBasePath() . $route);
143 }
144
145 return $this->redirect($response, $route);
146 }
147
148 protected function getBasePath(): string
149 {
150 return $this->container->basePath ?: '';
151 }
130} 152}
diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php
index 7a716673..c52e3b76 100644
--- a/application/render/PageBuilder.php
+++ b/application/render/PageBuilder.php
@@ -149,6 +149,8 @@ class PageBuilder
149 149
150 $this->tpl->assign('formatter', $this->conf->get('formatter', 'default')); 150 $this->tpl->assign('formatter', $this->conf->get('formatter', 'default'));
151 151
152 $this->tpl->assign('links_per_page', $this->session['LINKS_PER_PAGE']);
153
152 // To be removed with a proper theme configuration. 154 // To be removed with a proper theme configuration.
153 $this->tpl->assign('conf', $this->conf); 155 $this->tpl->assign('conf', $this->conf);
154 } 156 }
diff --git a/assets/default/js/base.js b/assets/default/js/base.js
index 0f29799d..27938823 100644
--- a/assets/default/js/base.js
+++ b/assets/default/js/base.js
@@ -25,16 +25,18 @@ function findParent(element, tagName, attributes) {
25/** 25/**
26 * Ajax request to refresh the CSRF token. 26 * Ajax request to refresh the CSRF token.
27 */ 27 */
28function refreshToken(basePath) { 28function refreshToken(basePath, callback) {
29 console.log('refresh');
30 const xhr = new XMLHttpRequest(); 29 const xhr = new XMLHttpRequest();
31 xhr.open('GET', `${basePath}/admin/token`); 30 xhr.open('GET', `${basePath}/admin/token`);
32 xhr.onload = () => { 31 xhr.onload = () => {
33 const elements = document.querySelectorAll('input[name="token"]'); 32 const elements = document.querySelectorAll('input[name="token"]');
34 [...elements].forEach((element) => { 33 [...elements].forEach((element) => {
35 console.log(element);
36 element.setAttribute('value', xhr.responseText); 34 element.setAttribute('value', xhr.responseText);
37 }); 35 });
36
37 if (callback) {
38 callback(xhr.response);
39 }
38 }; 40 };
39 xhr.send(); 41 xhr.send();
40} 42}
@@ -622,4 +624,15 @@ function init(description) {
622 [...autocompleteFields].forEach((autocompleteField) => { 624 [...autocompleteFields].forEach((autocompleteField) => {
623 awesomepletes.push(createAwesompleteInstance(autocompleteField)); 625 awesomepletes.push(createAwesompleteInstance(autocompleteField));
624 }); 626 });
627
628 const exportForm = document.querySelector('#exportform');
629 if (exportForm != null) {
630 exportForm.addEventListener('submit', (event) => {
631 event.preventDefault();
632
633 refreshToken(basePath, () => {
634 event.target.submit();
635 });
636 });
637 }
625})(); 638})();
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index 759dff29..7ab09d3f 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -616,6 +616,11 @@ body,
616 padding: 5px; 616 padding: 5px;
617 text-decoration: none; 617 text-decoration: none;
618 color: $dark-grey; 618 color: $dark-grey;
619
620 &.selected {
621 background: var(--main-color);
622 color: $white;
623 }
619 } 624 }
620 625
621 input { 626 input {
diff --git a/inc/languages/ja/LC_MESSAGES/shaarli.po b/inc/languages/jp/LC_MESSAGES/shaarli.po
index b420bb51..b420bb51 100644
--- a/inc/languages/ja/LC_MESSAGES/shaarli.po
+++ b/inc/languages/jp/LC_MESSAGES/shaarli.po
diff --git a/plugins/default_colors/default_colors.php b/plugins/default_colors/default_colors.php
index 1928cc9f..e1fd5cfb 100644
--- a/plugins/default_colors/default_colors.php
+++ b/plugins/default_colors/default_colors.php
@@ -15,6 +15,8 @@ const DEFAULT_COLORS_PLACEHOLDERS = [
15 'DEFAULT_COLORS_DARK_MAIN', 15 'DEFAULT_COLORS_DARK_MAIN',
16]; 16];
17 17
18const DEFAULT_COLORS_CSS_FILE = '/default_colors/default_colors.css';
19
18/** 20/**
19 * Display an error if the plugin is active a no color is configured. 21 * Display an error if the plugin is active a no color is configured.
20 * 22 *
@@ -24,58 +26,62 @@ const DEFAULT_COLORS_PLACEHOLDERS = [
24 */ 26 */
25function default_colors_init($conf) 27function default_colors_init($conf)
26{ 28{
27 $params = ''; 29 $params = [];
28 foreach (DEFAULT_COLORS_PLACEHOLDERS as $placeholder) { 30 foreach (DEFAULT_COLORS_PLACEHOLDERS as $placeholder) {
29 $params .= trim($conf->get('plugins.'. $placeholder, '')); 31 $value = trim($conf->get('plugins.'. $placeholder, ''));
32 if (strlen($value) > 0) {
33 $params[$placeholder] = $value;
34 }
30 } 35 }
31 36
32 if (empty($params)) { 37 if (empty($params)) {
33 $error = t('Default colors plugin error: '. 38 $error = t('Default colors plugin error: '.
34 'This plugin is active and no custom color is configured.'); 39 'This plugin is active and no custom color is configured.');
35 return array($error); 40 return [$error];
41 }
42
43 // Colors are defined but the custom CSS file does not exist -> generate it
44 if (!file_exists(PluginManager::$PLUGINS_PATH . DEFAULT_COLORS_CSS_FILE)) {
45 default_colors_generate_css_file($params);
36 } 46 }
37} 47}
38 48
39/** 49/**
40 * When plugin parameters are saved, we regenerate the custom CSS file with provided settings. 50 * When linklist is displayed, include default_colors CSS file.
41 * 51 *
42 * @param array $data $_POST array 52 * @param array $data - header data.
43 * 53 *
44 * @return array Updated $_POST array 54 * @return mixed - header data with default_colors CSS file added.
45 */ 55 */
46function hook_default_colors_save_plugin_parameters($data) 56function hook_default_colors_render_includes($data)
47{ 57{
48 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css'; 58 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
49 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css.template'); 59 if (file_exists($file )) {
50 $content = ''; 60 $data['css_files'][] = $file ;
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 } 61 }
60 62
61 return $data; 63 return $data;
62} 64}
63 65
64/** 66/**
65 * When linklist is displayed, include default_colors CSS file. 67 * Regenerate the custom CSS file with provided settings.
66 *
67 * @param array $data - header data.
68 * 68 *
69 * @return mixed - header data with default_colors CSS file added. 69 * @param array $params Plugin configuration (CSS rules)
70 */ 70 */
71function hook_default_colors_render_includes($data) 71function default_colors_generate_css_file($params): void
72{ 72{
73 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css'; 73 $file = PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css';
74 if (file_exists($file )) { 74 $template = file_get_contents(PluginManager::$PLUGINS_PATH . '/default_colors/default_colors.css.template');
75 $data['css_files'][] = $file ; 75 $content = '';
76 foreach (DEFAULT_COLORS_PLACEHOLDERS as $rule) {
77 $content .= !empty($params[$rule])
78 ? default_colors_format_css_rule($params, $rule) .';'. PHP_EOL
79 : '';
76 } 80 }
77 81
78 return $data; 82 if (! empty($content)) {
83 file_put_contents($file, sprintf($template, $content));
84 }
79} 85}
80 86
81/** 87/**
diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php
index 5c2aaedb..fe37d5f2 100644
--- a/tests/feed/FeedBuilderTest.php
+++ b/tests/feed/FeedBuilderTest.php
@@ -87,7 +87,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
87 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 87 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
88 88
89 // Test first not pinned link (note link) 89 // Test first not pinned link (note link)
90 $link = $data['links'][array_keys($data['links'])[2]]; 90 $link = $data['links'][array_keys($data['links'])[0]];
91 $this->assertEquals(41, $link['id']); 91 $this->assertEquals(41, $link['id']);
92 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 92 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
93 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']); 93 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
@@ -128,7 +128,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
128 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null); 128 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
129 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 129 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
130 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); 130 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
131 $link = $data['links'][array_keys($data['links'])[2]]; 131 $link = $data['links'][array_keys($data['links'])[0]];
132 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); 132 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
133 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); 133 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
134 } 134 }
@@ -173,7 +173,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
173 $feedBuilder->setLocale(self::$LOCALE); 173 $feedBuilder->setLocale(self::$LOCALE);
174 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria); 174 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
175 $this->assertEquals(3, count($data['links'])); 175 $this->assertEquals(3, count($data['links']));
176 $link = $data['links'][array_keys($data['links'])[2]]; 176 $link = $data['links'][array_keys($data['links'])[0]];
177 $this->assertEquals(41, $link['id']); 177 $this->assertEquals(41, $link['id']);
178 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 178 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
179 } 179 }
@@ -195,7 +195,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
195 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 195 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
196 $this->assertTrue($data['usepermalinks']); 196 $this->assertTrue($data['usepermalinks']);
197 // First link is a permalink 197 // First link is a permalink
198 $link = $data['links'][array_keys($data['links'])[2]]; 198 $link = $data['links'][array_keys($data['links'])[0]];
199 $this->assertEquals(41, $link['id']); 199 $this->assertEquals(41, $link['id']);
200 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 200 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
201 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']); 201 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
@@ -203,7 +203,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
203 $this->assertContains('Direct link', $link['description']); 203 $this->assertContains('Direct link', $link['description']);
204 $this->assertContains('http://host.tld/shaare/WDWyig', $link['description']); 204 $this->assertContains('http://host.tld/shaare/WDWyig', $link['description']);
205 // Second link is a direct link 205 // Second link is a direct link
206 $link = $data['links'][array_keys($data['links'])[3]]; 206 $link = $data['links'][array_keys($data['links'])[1]];
207 $this->assertEquals(8, $link['id']); 207 $this->assertEquals(8, $link['id']);
208 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']); 208 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
209 $this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']); 209 $this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']);
@@ -270,7 +270,7 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
270 ); 270 );
271 271
272 // Test first link (note link) 272 // Test first link (note link)
273 $link = $data['links'][array_keys($data['links'])[2]]; 273 $link = $data['links'][array_keys($data['links'])[0]];
274 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']); 274 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']);
275 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']); 275 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']);
276 $this->assertContains('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']); 276 $this->assertContains('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
diff --git a/tests/front/controller/admin/ConfigureControllerTest.php b/tests/front/controller/admin/ConfigureControllerTest.php
index f2f84bac..612f20f1 100644
--- a/tests/front/controller/admin/ConfigureControllerTest.php
+++ b/tests/front/controller/admin/ConfigureControllerTest.php
@@ -62,7 +62,7 @@ class ConfigureControllerTest extends TestCase
62 static::assertSame('privacy.hide_public_links', $assignedVariables['hide_public_links']); 62 static::assertSame('privacy.hide_public_links', $assignedVariables['hide_public_links']);
63 static::assertSame('api.enabled', $assignedVariables['api_enabled']); 63 static::assertSame('api.enabled', $assignedVariables['api_enabled']);
64 static::assertSame('api.secret', $assignedVariables['api_secret']); 64 static::assertSame('api.secret', $assignedVariables['api_secret']);
65 static::assertCount(4, $assignedVariables['languages']); 65 static::assertCount(5, $assignedVariables['languages']);
66 static::assertArrayHasKey('gd_enabled', $assignedVariables); 66 static::assertArrayHasKey('gd_enabled', $assignedVariables);
67 static::assertSame('thumbnails.mode', $assignedVariables['thumbnails_mode']); 67 static::assertSame('thumbnails.mode', $assignedVariables['thumbnails_mode']);
68 } 68 }
diff --git a/tests/legacy/LegacyControllerTest.php b/tests/legacy/LegacyControllerTest.php
index 759a5b2a..4e52f3e1 100644
--- a/tests/legacy/LegacyControllerTest.php
+++ b/tests/legacy/LegacyControllerTest.php
@@ -66,11 +66,11 @@ class LegacyControllerTest extends TestCase
66 { 66 {
67 return [ 67 return [
68 ['post', [], '/admin/shaare', true], 68 ['post', [], '/admin/shaare', true],
69 ['post', [], '/login', false], 69 ['post', [], '/login?returnurl=/subfolder/admin/shaare', false],
70 ['post', ['title' => 'test'], '/admin/shaare?title=test', true], 70 ['post', ['title' => 'test'], '/admin/shaare?title=test', true],
71 ['post', ['title' => 'test'], '/login?title=test', false], 71 ['post', ['title' => 'test'], '/login?returnurl=/subfolder/admin/shaare?title=test', false],
72 ['addlink', [], '/admin/add-shaare', true], 72 ['addlink', [], '/admin/add-shaare', true],
73 ['addlink', [], '/login', false], 73 ['addlink', [], '/login?returnurl=/subfolder/admin/add-shaare', false],
74 ['login', [], '/login', true], 74 ['login', [], '/login', true],
75 ['login', [], '/login', false], 75 ['login', [], '/login', false],
76 ['logout', [], '/admin/logout', true], 76 ['logout', [], '/admin/logout', true],
@@ -94,6 +94,8 @@ class LegacyControllerTest extends TestCase
94 ['opensearch', [], '/open-search', true], 94 ['opensearch', [], '/open-search', true],
95 ['dailyrss', [], '/daily-rss', false], 95 ['dailyrss', [], '/daily-rss', false],
96 ['dailyrss', [], '/daily-rss', true], 96 ['dailyrss', [], '/daily-rss', true],
97 ['configure', [], '/login?returnurl=/subfolder/admin/configure', false],
98 ['configure', [], '/admin/configure', true],
97 ]; 99 ];
98 } 100 }
99} 101}
diff --git a/tests/plugins/PluginDefaultColorsTest.php b/tests/plugins/PluginDefaultColorsTest.php
index b9951cca..9835dfa3 100644
--- a/tests/plugins/PluginDefaultColorsTest.php
+++ b/tests/plugins/PluginDefaultColorsTest.php
@@ -2,7 +2,6 @@
2 2
3namespace Shaarli\Plugin\DefaultColors; 3namespace Shaarli\Plugin\DefaultColors;
4 4
5use DateTime;
6use PHPUnit\Framework\TestCase; 5use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\LinkDB; 6use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
@@ -57,6 +56,8 @@ class PluginDefaultColorsTest extends TestCase
57 $conf->set('plugins.DEFAULT_COLORS_BACKGROUND', 'value'); 56 $conf->set('plugins.DEFAULT_COLORS_BACKGROUND', 'value');
58 $errors = default_colors_init($conf); 57 $errors = default_colors_init($conf);
59 $this->assertEmpty($errors); 58 $this->assertEmpty($errors);
59
60 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
60 } 61 }
61 62
62 /** 63 /**
@@ -72,9 +73,9 @@ class PluginDefaultColorsTest extends TestCase
72 /** 73 /**
73 * Test the save plugin parameters hook with all colors specified. 74 * Test the save plugin parameters hook with all colors specified.
74 */ 75 */
75 public function testSavePluginParametersAll() 76 public function testGenerateCssFile()
76 { 77 {
77 $post = [ 78 $params = [
78 'other1' => true, 79 'other1' => true,
79 'DEFAULT_COLORS_MAIN' => 'blue', 80 'DEFAULT_COLORS_MAIN' => 'blue',
80 'DEFAULT_COLORS_BACKGROUND' => 'pink', 81 'DEFAULT_COLORS_BACKGROUND' => 'pink',
@@ -82,7 +83,7 @@ class PluginDefaultColorsTest extends TestCase
82 'DEFAULT_COLORS_DARK_MAIN' => 'green', 83 'DEFAULT_COLORS_DARK_MAIN' => 'green',
83 ]; 84 ];
84 85
85 hook_default_colors_save_plugin_parameters($post); 86 default_colors_generate_css_file($params);
86 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); 87 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
87 $content = file_get_contents($file); 88 $content = file_get_contents($file);
88 $expected = ':root { 89 $expected = ':root {
@@ -98,16 +99,16 @@ class PluginDefaultColorsTest extends TestCase
98 /** 99 /**
99 * Test the save plugin parameters hook with only one color specified. 100 * Test the save plugin parameters hook with only one color specified.
100 */ 101 */
101 public function testSavePluginParametersSingle() 102 public function testGenerateCssFileSingle()
102 { 103 {
103 $post = [ 104 $params = [
104 'other1' => true, 105 'other1' => true,
105 'DEFAULT_COLORS_BACKGROUND' => 'pink', 106 'DEFAULT_COLORS_BACKGROUND' => 'pink',
106 'other2' => ['yep'], 107 'other2' => ['yep'],
107 'DEFAULT_COLORS_DARK_MAIN' => '', 108 'DEFAULT_COLORS_DARK_MAIN' => '',
108 ]; 109 ];
109 110
110 hook_default_colors_save_plugin_parameters($post); 111 default_colors_generate_css_file($params);
111 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); 112 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
112 $content = file_get_contents($file); 113 $content = file_get_contents($file);
113 $expected = ':root { 114 $expected = ':root {
@@ -121,9 +122,9 @@ class PluginDefaultColorsTest extends TestCase
121 /** 122 /**
122 * Test the save plugin parameters hook with no color specified. 123 * Test the save plugin parameters hook with no color specified.
123 */ 124 */
124 public function testSavePluginParametersNone() 125 public function testGenerateCssFileNone()
125 { 126 {
126 hook_default_colors_save_plugin_parameters([]); 127 default_colors_generate_css_file([]);
127 $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css'); 128 $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css');
128 } 129 }
129 130
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index c7617b22..2475f5fd 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -272,7 +272,17 @@
272 <i class="fa fa-trash" aria-hidden="true"></i> 272 <i class="fa fa-trash" aria-hidden="true"></i>
273 </a> 273 </a>
274 &middot; 274 &middot;
275 <a href="{$base_path}/admin/shaare/{$value.id}" aria-label="{$strEdit}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i></a> 275 <a href="{$base_path}/admin/shaare/{$value.id}" aria-label="{$strEdit}" title="{$strEdit}">
276 <i class="fa fa-pencil-square-o edit-link" aria-hidden="true"></i>
277 </a>
278 &middot;
279 <a href="{$base_path}/admin/shaare/{$value.id}/pin?token={$token}"
280 aria-label="{$strToggleSticky}"
281 title="{$strToggleSticky}"
282 class="pin-link {if="$value.sticky"}pinned-link{/if}"
283 >
284 <i class="fa fa-thumb-tack" aria-hidden="true"></i>
285 </a>
276 {/if} 286 {/if}
277 </div> 287 </div>
278 </div> 288 </div>
diff --git a/tpl/default/linklist.paging.html b/tpl/default/linklist.paging.html
index 20853330..aa637868 100644
--- a/tpl/default/linklist.paging.html
+++ b/tpl/default/linklist.paging.html
@@ -55,11 +55,16 @@
55 55
56 <div class="linksperpage pure-u-1-3"> 56 <div class="linksperpage pure-u-1-3">
57 <div class="pure-u-0 pure-u-lg-visible">{'Links per page'|t}</div> 57 <div class="pure-u-0 pure-u-lg-visible">{'Links per page'|t}</div>
58 <a href="{$base_path}/links-per-page?nb=20">20</a> 58 <a href="{$base_path}/links-per-page?nb=20"
59 <a href="{$base_path}/links-per-page?nb=50">50</a> 59 {if="$links_per_page == 20"}class="selected"{/if}>20</a>
60 <a href="{$base_path}/links-per-page?nb=100">100</a> 60 <a href="{$base_path}/links-per-page?nb=50"
61 {if="$links_per_page == 50"}class="selected"{/if}>50</a>
62 <a href="{$base_path}/links-per-page?nb=100"
63 {if="$links_per_page == 100"}class="selected"{/if}>100</a>
61 <form method="GET" class="pure-u-0 pure-u-lg-visible" action="{$base_path}/links-per-page"> 64 <form method="GET" class="pure-u-0 pure-u-lg-visible" action="{$base_path}/links-per-page">
62 <input type="text" name="nb" placeholder="133"> 65 <input type="text" name="nb" placeholder="133"
66 {if="$links_per_page != 20 && $links_per_page != 50 && $links_per_page != 100"}
67 value="{$links_per_page}"{/if}>
63 </form> 68 </form>
64 <a href="#" class="filter-off fold-all pure-u-0 pure-u-lg-visible" aria-label="{'Fold all'|t}" title="{'Fold all'|t}"> 69 <a href="#" class="filter-off fold-all pure-u-0 pure-u-lg-visible" aria-label="{'Fold all'|t}" title="{'Fold all'|t}">
65 <i class="fa fa-chevron-up" aria-hidden="true"></i> 70 <i class="fa fa-chevron-up" aria-hidden="true"></i>