aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--application/LinkDB.php40
-rw-r--r--application/LinkFilter.php41
-rw-r--r--application/PageBuilder.php1
-rw-r--r--index.php74
-rw-r--r--tests/LinkDBTest.php55
-rw-r--r--tests/LinkFilterTest.php3
-rw-r--r--tpl/default/css/shaarli.css45
-rw-r--r--tpl/default/linklist.html50
-rw-r--r--tpl/default/linklist.paging.html11
-rw-r--r--tpl/default/page.header.html39
11 files changed, 218 insertions, 143 deletions
diff --git a/README.md b/README.md
index c9e83899..5ca25720 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
1![Shaarli logo](doc/images/doc-logo.png) 1![Shaarli logo](doc/md/images/doc-logo.png)
2 2
3The personal, minimalist, super-fast, database free, bookmarking service. 3The personal, minimalist, super-fast, database free, bookmarking service.
4 4
diff --git a/application/LinkDB.php b/application/LinkDB.php
index 8ca0fab3..9308164a 100644
--- a/application/LinkDB.php
+++ b/application/LinkDB.php
@@ -417,21 +417,22 @@ You use the community supported version of the original Shaarli project, by Seba
417 * - searchterm: term search 417 * - searchterm: term search
418 * @param bool $casesensitive Optional: Perform case sensitive filter 418 * @param bool $casesensitive Optional: Perform case sensitive filter
419 * @param string $visibility return only all/private/public links 419 * @param string $visibility return only all/private/public links
420 * @param string $untaggedonly return only untagged links
420 * 421 *
421 * @return array filtered links, all links if no suitable filter was provided. 422 * @return array filtered links, all links if no suitable filter was provided.
422 */ 423 */
423 public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all') 424 public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all', $untaggedonly = false)
424 { 425 {
425 // Filter link database according to parameters. 426 // Filter link database according to parameters.
426 $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; 427 $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
427 $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; 428 $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
428 429
429 // Search tags + fullsearch - blank string parameter will return all links. 430 // Search tags + fullsearch - blank string parameter will return all links.
430 $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; 431 $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; // == "vuotext"
431 $request = [$searchtags, $searchterm]; 432 $request = [$searchtags, $searchterm];
432 433
433 $linkFilter = new LinkFilter($this); 434 $linkFilter = new LinkFilter($this);
434 return $linkFilter->filter($type, $request, $casesensitive, $visibility); 435 return $linkFilter->filter($type, $request, $casesensitive, $visibility, $untaggedonly);
435 } 436 }
436 437
437 /** 438 /**
@@ -464,6 +465,39 @@ You use the community supported version of the original Shaarli project, by Seba
464 } 465 }
465 466
466 /** 467 /**
468 * Rename or delete a tag across all links.
469 *
470 * @param string $from Tag to rename
471 * @param string $to New tag. If none is provided, the from tag will be deleted
472 *
473 * @return array|bool List of altered links or false on error
474 */
475 public function renameTag($from, $to)
476 {
477 if (empty($from)) {
478 return false;
479 }
480 $delete = empty($to);
481 // True for case-sensitive tag search.
482 $linksToAlter = $this->filterSearch(['searchtags' => $from], true);
483 foreach($linksToAlter as $key => &$value)
484 {
485 $tags = preg_split('/\s+/', trim($value['tags']));
486 if (($pos = array_search($from, $tags)) !== false) {
487 if ($delete) {
488 unset($tags[$pos]); // Remove tag.
489 } else {
490 $tags[$pos] = trim($to);
491 }
492 $value['tags'] = trim(implode(' ', array_unique($tags)));
493 $this[$value['id']] = $value;
494 }
495 }
496
497 return $linksToAlter;
498 }
499
500 /**
467 * Returns the list of days containing articles (oldest first) 501 * Returns the list of days containing articles (oldest first)
468 * Output: An array containing days (in format YYYYMMDD). 502 * Output: An array containing days (in format YYYYMMDD).
469 */ 503 */
diff --git a/application/LinkFilter.php b/application/LinkFilter.php
index 0e887d38..95519528 100644
--- a/application/LinkFilter.php
+++ b/application/LinkFilter.php
@@ -52,10 +52,11 @@ class LinkFilter
52 * @param mixed $request Filter content. 52 * @param mixed $request Filter content.
53 * @param bool $casesensitive Optional: Perform case sensitive filter if true. 53 * @param bool $casesensitive Optional: Perform case sensitive filter if true.
54 * @param string $visibility Optional: return only all/private/public links 54 * @param string $visibility Optional: return only all/private/public links
55 * @param string $untaggedonly Optional: return only untagged links. Applies only if $type includes FILTER_TAG
55 * 56 *
56 * @return array filtered link list. 57 * @return array filtered link list.
57 */ 58 */
58 public function filter($type, $request, $casesensitive = false, $visibility = 'all') 59 public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false)
59 { 60 {
60 if (! in_array($visibility, ['all', 'public', 'private'])) { 61 if (! in_array($visibility, ['all', 'public', 'private'])) {
61 $visibility = 'all'; 62 $visibility = 'all';
@@ -64,23 +65,34 @@ class LinkFilter
64 switch($type) { 65 switch($type) {
65 case self::$FILTER_HASH: 66 case self::$FILTER_HASH:
66 return $this->filterSmallHash($request); 67 return $this->filterSmallHash($request);
67 case self::$FILTER_TAG | self::$FILTER_TEXT: 68 case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext"
68 if (!empty($request)) { 69 $noRequest = empty($request) || (empty($request[0]) && empty($request[1]));
69 $filtered = $this->links; 70 if ($noRequest) {
70 if (isset($request[0])) { 71 if ($untaggedonly) {
71 $filtered = $this->filterTags($request[0], $casesensitive, $visibility); 72 return $this->filterUntagged($visibility);
72 }
73 if (isset($request[1])) {
74 $lf = new LinkFilter($filtered);
75 $filtered = $lf->filterFulltext($request[1], $visibility);
76 } 73 }
77 return $filtered; 74 return $this->noFilter($visibility);
78 } 75 }
79 return $this->noFilter($visibility); 76 if ($untaggedonly) {
77 $filtered = $this->filterUntagged($visibility);
78 } else {
79 $filtered = $this->links;
80 }
81 if (!empty($request[0])) {
82 $filtered = (new LinkFilter($filtered))->filterTags($request[0], $casesensitive, $visibility);
83 }
84 if (!empty($request[1])) {
85 $filtered = (new LinkFilter($filtered))->filterFulltext($request[1], $visibility);
86 }
87 return $filtered;
80 case self::$FILTER_TEXT: 88 case self::$FILTER_TEXT:
81 return $this->filterFulltext($request, $visibility); 89 return $this->filterFulltext($request, $visibility);
82 case self::$FILTER_TAG: 90 case self::$FILTER_TAG:
83 return $this->filterTags($request, $casesensitive, $visibility); 91 if ($untaggedonly) {
92 return $this->filterUntagged($visibility);
93 } else {
94 return $this->filterTags($request, $casesensitive, $visibility);
95 }
84 case self::$FILTER_DAY: 96 case self::$FILTER_DAY:
85 return $this->filterDay($request); 97 return $this->filterDay($request);
86 default: 98 default:
@@ -253,9 +265,6 @@ class LinkFilter
253 { 265 {
254 // Implode if array for clean up. 266 // Implode if array for clean up.
255 $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags; 267 $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags;
256 if ($tags === false) {
257 return $this->filterUntagged($visibility);
258 }
259 if (empty($tags)) { 268 if (empty($tags)) {
260 return $this->noFilter($visibility); 269 return $this->noFilter($visibility);
261 } 270 }
diff --git a/application/PageBuilder.php b/application/PageBuilder.php
index c86621a2..7a42400d 100644
--- a/application/PageBuilder.php
+++ b/application/PageBuilder.php
@@ -78,6 +78,7 @@ class PageBuilder
78 $this->tpl->assign('version', shaarli_version); 78 $this->tpl->assign('version', shaarli_version);
79 $this->tpl->assign('scripturl', index_url($_SERVER)); 79 $this->tpl->assign('scripturl', index_url($_SERVER));
80 $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? 80 $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links?
81 $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
81 $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); 82 $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli'));
82 if ($this->conf->exists('general.header_link')) { 83 if ($this->conf->exists('general.header_link')) {
83 $this->tpl->assign('titleLink', $this->conf->get('general.header_link')); 84 $this->tpl->assign('titleLink', $this->conf->get('general.header_link'));
diff --git a/index.php b/index.php
index 85486eb5..9025df58 100644
--- a/index.php
+++ b/index.php
@@ -287,6 +287,7 @@ function logout() {
287 unset($_SESSION['ip']); 287 unset($_SESSION['ip']);
288 unset($_SESSION['username']); 288 unset($_SESSION['username']);
289 unset($_SESSION['privateonly']); 289 unset($_SESSION['privateonly']);
290 unset($_SESSION['untaggedonly']);
290 } 291 }
291 setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); 292 setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH);
292} 293}
@@ -685,6 +686,7 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) {
685 * @param ConfigManager $conf Configuration Manager instance. 686 * @param ConfigManager $conf Configuration Manager instance.
686 * @param PluginManager $pluginManager Plugin Manager instance, 687 * @param PluginManager $pluginManager Plugin Manager instance,
687 * @param LinkDB $LINKSDB 688 * @param LinkDB $LINKSDB
689 * @param History $history instance
688 */ 690 */
689function renderPage($conf, $pluginManager, $LINKSDB, $history) 691function renderPage($conf, $pluginManager, $LINKSDB, $history)
690{ 692{
@@ -1017,6 +1019,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1017 exit; 1019 exit;
1018 } 1020 }
1019 1021
1022 // -------- User wants to see only untagged links (toggle)
1023 if (isset($_GET['untaggedonly'])) {
1024 $_SESSION['untaggedonly'] = !$_SESSION['untaggedonly'];
1025
1026 if (! empty($_SERVER['HTTP_REFERER'])) {
1027 $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('untaggedonly'));
1028 } else {
1029 $location = '?';
1030 }
1031 header('Location: '. $location);
1032 exit;
1033 }
1034
1020 // -------- Handle other actions allowed for non-logged in users: 1035 // -------- Handle other actions allowed for non-logged in users:
1021 if (!isLoggedIn()) 1036 if (!isLoggedIn())
1022 { 1037 {
@@ -1184,41 +1199,18 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1184 die('Wrong token.'); 1199 die('Wrong token.');
1185 } 1200 }
1186 1201
1187 // Delete a tag: 1202 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag']));
1188 if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { 1203 $LINKSDB->save($conf->get('resource.page_cache'));
1189 $needle = trim($_POST['fromtag']); 1204 foreach ($alteredLinks as $link) {
1190 // True for case-sensitive tag search. 1205 $history->updateLink($link);
1191 $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
1192 foreach($linksToAlter as $key=>$value)
1193 {
1194 $tags = explode(' ',trim($value['tags']));
1195 unset($tags[array_search($needle,$tags)]); // Remove tag.
1196 $value['tags']=trim(implode(' ',$tags));
1197 $LINKSDB[$key]=$value;
1198 $history->updateLink($LINKSDB[$key]);
1199 }
1200 $LINKSDB->save($conf->get('resource.page_cache'));
1201 echo '<script>alert("Tag was removed from '.count($linksToAlter).' links.");document.location=\'?do=changetag\';</script>';
1202 exit;
1203 }
1204
1205 // Rename a tag:
1206 if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) {
1207 $needle = trim($_POST['fromtag']);
1208 // True for case-sensitive tag search.
1209 $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true);
1210 foreach($linksToAlter as $key=>$value) {
1211 $tags = preg_split('/\s+/', trim($value['tags']));
1212 // Replace tags value.
1213 $tags[array_search($needle, $tags)] = trim($_POST['totag']);
1214 $value['tags'] = implode(' ', array_unique($tags));
1215 $LINKSDB[$key] = $value;
1216 $history->updateLink($LINKSDB[$key]);
1217 }
1218 $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk.
1219 echo '<script>alert("Tag was renamed in '.count($linksToAlter).' links.");document.location=\'?searchtags='.urlencode(escape($_POST['totag'])).'\';</script>';
1220 exit;
1221 } 1206 }
1207 $delete = empty($_POST['totag']);
1208 $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag']));
1209 $alert = $delete
1210 ? sprintf(t('The tag was removed from %d links.'), count($alteredLinks))
1211 : sprintf(t('The tag was renamed in %d links.'), count($alteredLinks));
1212 echo '<script>alert("'. $alert .'");document.location=\'?'. $redirect .'\';</script>';
1213 exit;
1222 } 1214 }
1223 1215
1224 // -------- User wants to add a link without using the bookmarklet: Show form. 1216 // -------- User wants to add a link without using the bookmarklet: Show form.
@@ -1651,7 +1643,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager)
1651 'searchtags' => $searchtags, 1643 'searchtags' => $searchtags,
1652 'searchterm' => $searchterm, 1644 'searchterm' => $searchterm,
1653 ]; 1645 ];
1654 $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility); 1646 $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility, !empty($_SESSION['untaggedonly']));
1655 } 1647 }
1656 1648
1657 // ---- Handle paging. 1649 // ---- Handle paging.
@@ -2245,6 +2237,12 @@ if (!isset($_SESSION['LINKS_PER_PAGE'])) {
2245 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); 2237 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
2246} 2238}
2247 2239
2240try {
2241 $history = new History($conf->get('resource.history'));
2242} catch(Exception $e) {
2243 die($e->getMessage());
2244}
2245
2248$linkDb = new LinkDB( 2246$linkDb = new LinkDB(
2249 $conf->get('resource.datastore'), 2247 $conf->get('resource.datastore'),
2250 isLoggedIn(), 2248 isLoggedIn(),
@@ -2253,12 +2251,6 @@ $linkDb = new LinkDB(
2253 $conf->get('redirector.encode_url') 2251 $conf->get('redirector.encode_url')
2254); 2252);
2255 2253
2256try {
2257 $history = new History($conf->get('resource.history'));
2258} catch(Exception $e) {
2259 die($e->getMessage());
2260}
2261
2262$container = new \Slim\Container(); 2254$container = new \Slim\Container();
2263$container['conf'] = $conf; 2255$container['conf'] = $conf;
2264$container['plugins'] = $pluginManager; 2256$container['plugins'] = $pluginManager;
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php
index 25438277..5b2f3667 100644
--- a/tests/LinkDBTest.php
+++ b/tests/LinkDBTest.php
@@ -487,4 +487,59 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
487 $this->assertEquals($linkIds[$cpt++], $key); 487 $this->assertEquals($linkIds[$cpt++], $key);
488 } 488 }
489 } 489 }
490
491 /**
492 * Test rename tag with a valid value present in multiple links
493 */
494 public function testRenameTagMultiple()
495 {
496 self::$refDB->write(self::$testDatastore);
497 $linkDB = new LinkDB(self::$testDatastore, true, false);
498
499 $res = $linkDB->renameTag('cartoon', 'Taz');
500 $this->assertEquals(3, count($res));
501 $this->assertContains(' Taz ', $linkDB[4]['tags']);
502 $this->assertContains(' Taz ', $linkDB[1]['tags']);
503 $this->assertContains(' Taz ', $linkDB[0]['tags']);
504 }
505
506 /**
507 * Test rename tag with a valid value
508 */
509 public function testRenameTagCaseSensitive()
510 {
511 self::$refDB->write(self::$testDatastore);
512 $linkDB = new LinkDB(self::$testDatastore, true, false, '');
513
514 $res = $linkDB->renameTag('sTuff', 'Taz');
515 $this->assertEquals(1, count($res));
516 $this->assertEquals('Taz', $linkDB[41]['tags']);
517 }
518
519 /**
520 * Test rename tag with invalid values
521 */
522 public function testRenameTagInvalid()
523 {
524 $linkDB = new LinkDB(self::$testDatastore, false, false);
525
526 $this->assertFalse($linkDB->renameTag('', 'test'));
527 $this->assertFalse($linkDB->renameTag('', ''));
528 // tag non existent
529 $this->assertEquals([], $linkDB->renameTag('test', ''));
530 $this->assertEquals([], $linkDB->renameTag('test', 'retest'));
531 }
532
533 /**
534 * Test delete tag with a valid value
535 */
536 public function testDeleteTag()
537 {
538 self::$refDB->write(self::$testDatastore);
539 $linkDB = new LinkDB(self::$testDatastore, true, false);
540
541 $res = $linkDB->renameTag('cartoon', null);
542 $this->assertEquals(3, count($res));
543 $this->assertNotContains('cartoon', $linkDB[4]['tags']);
544 }
490} 545}
diff --git a/tests/LinkFilterTest.php b/tests/LinkFilterTest.php
index 74162358..d796d3a3 100644
--- a/tests/LinkFilterTest.php
+++ b/tests/LinkFilterTest.php
@@ -63,10 +63,9 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
63 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '')) 63 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, ''))
64 ); 64 );
65 65
66 // Untagged only
67 $this->assertEquals( 66 $this->assertEquals(
68 self::$refDB->countUntaggedLinks(), 67 self::$refDB->countUntaggedLinks(),
69 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, false)) 68 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, /*$request=*/'', /*$casesensitive=*/false, /*$visibility=*/'all', /*$untaggedonly=*/true))
70 ); 69 );
71 70
72 $this->assertEquals( 71 $this->assertEquals(
diff --git a/tpl/default/css/shaarli.css b/tpl/default/css/shaarli.css
index 39bbd0a3..e1868c59 100644
--- a/tpl/default/css/shaarli.css
+++ b/tpl/default/css/shaarli.css
@@ -226,6 +226,12 @@ body, .pure-g [class*="pure-u"] {
226 border-radius: 2px; 226 border-radius: 2px;
227 color: #252525; 227 color: #252525;
228} 228}
229@media screen and (max-width: 64em) {
230 .searchform {
231 max-width: 260px;
232 margin: 0 auto;
233 }
234}
229 235
230/* because chrome */ 236/* because chrome */
231#search input[type="text"]::-webkit-input-placeholder, 237#search input[type="text"]::-webkit-input-placeholder,
@@ -236,43 +242,37 @@ body, .pure-g [class*="pure-u"] {
236#search button, 242#search button,
237#search-tagcloud button, 243#search-tagcloud button,
238#search-linklist button { 244#search-linklist button {
239 background: transparent; 245 padding: 4px 8px 6px 8px;
246 background-color: #1B926C;
247 color: #f5f5f5;
240 border: none; 248 border: none;
249 border-radius: 2px;
241} 250}
242 251
243#search button { 252#search-tagcloud button {
244 color: #f5f5f5; 253 width: 90%;
245} 254}
246 255
247#search-linklist button { 256@media screen and (max-width: 64em) {
248 color: #252525; 257 #search-linklist button {
258 width: 100%;
259 }
260 #search-linklist .awesomplete {
261 margin: 5px 0;
262 }
249} 263}
250 264
251#search button:hover, 265#search button:hover,
252#search-linklist button:hover { 266#search-linklist button:hover,
253 color: #fff;
254}
255#search-tagcloud button:hover { 267#search-tagcloud button:hover {
256 color: #d0d0d0; 268 color: #d0d0d0;
257} 269}
258 270
271#search,
259#search-linklist { 272#search-linklist {
260 padding: 5px 0; 273 padding: 6px 0;
261} 274}
262 275
263@media screen and (min-width: 64em) {
264 #search .searchform,
265 #search-linklist .searchform {
266 margin-right: 25px;
267 text-align: right;
268 }
269
270 #search .tagfilter,
271 #search-linklist .tagfilter {
272 margin-left: 25px;
273 text-align: left;
274 }
275}
276@media screen and (max-width: 64em) { 276@media screen and (max-width: 64em) {
277 #search, #search * { 277 #search, #search * {
278 visibility: hidden; 278 visibility: hidden;
@@ -321,7 +321,6 @@ body, .pure-g [class*="pure-u"] {
321} 321}
322 322
323.subheader-form input[type="text"], .subheader-form input[type="password"], .subheader-form .remember-me { 323.subheader-form input[type="text"], .subheader-form input[type="password"], .subheader-form .remember-me {
324 margin: 0 0 5px 0;
325 padding: 5px 5px 3px 15px; 324 padding: 5px 5px 3px 15px;
326 height: 20px; 325 height: 20px;
327 width: 20%; 326 width: 20%;
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 2568a5d6..685821e3 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -19,30 +19,21 @@
19 19
20<div id="search-linklist"> 20<div id="search-linklist">
21 21
22 <div class="pure-g"> 22 <form method="GET" class="pure-form searchform" name="searchform">
23 <div class="pure-u-1 pure-u-lg-1-2"> 23 <input type="text" tabindex="1" name="searchterm" class="searchterm" placeholder="{'Search text'|t}"
24 <form method="GET" class="searchform" name="searchform"> 24 {if="!empty($search_term)"}
25 <input type="text" tabindex="1" name="searchterm" placeholder="{'Search text'|t}" 25 value="{$search_term}"
26 {if="!empty($search_term)"} 26 {/if}
27 value="{$search_term}" 27 >
28 {/if} 28 <input type="text" tabindex="2" name="searchtags" class="searchtags" placeholder="{'Filter by tag'|t}"
29 > 29 {if="!empty($search_tags)"}
30 <button type="submit" class="search-button"><i class="fa fa-search"></i></button> 30 value="{$search_tags}"
31 </form> 31 {/if}
32 </div> 32 autocomplete="off" data-multiple data-autofirst data-minChars="1"
33 <div class="pure-u-1 pure-u-lg-1-2"> 33 data-list="{loop="$tags"}{$key}, {/loop}"
34 <form method="GET" class="tagfilter" name="tagfilter"> 34 >
35 <input type="text" tabindex="2" name="searchtags" placeholder="{'Filter by tag'|t}" 35 <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
36 {if="!empty($search_tags)"} 36 </form>
37 value="{$search_tags}"
38 {/if}
39 autocomplete="off" data-multiple data-autofirst data-minChars="1"
40 data-list="{loop="$tags"}{$key}, {/loop}"
41 >
42 <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
43 </form>
44 </div>
45 </div>
46</div> 37</div>
47 38
48{loop="$plugins_header.fields_toolbar"} 39{loop="$plugins_header.fields_toolbar"}
@@ -91,7 +82,7 @@
91 <div id="searchcriteria">{'Nothing found.'|t}</div> 82 <div id="searchcriteria">{'Nothing found.'|t}</div>
92 </div> 83 </div>
93 </div> 84 </div>
94 {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility)"} 85 {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility) or $untaggedonly"}
95 <div class="pure-g pure-alert pure-alert-success search-result"> 86 <div class="pure-g pure-alert pure-alert-success search-result">
96 <div class="pure-u-2-24"></div> 87 <div class="pure-u-2-24"></div>
97 <div class="pure-u-20-24"> 88 <div class="pure-u-20-24">
@@ -107,10 +98,6 @@
107 <a href="?removetag={function="urlencode($value)"}">{$value}<span class="remove"><i class="fa fa-times"></i></span></a> 98 <a href="?removetag={function="urlencode($value)"}">{$value}<span class="remove"><i class="fa fa-times"></i></span></a>
108 </span> 99 </span>
109 {/loop} 100 {/loop}
110 {elseif="$search_tags === false"}
111 <span class="label label-tag" title="{'Remove tag'|t}">
112 <a href="?">{'untagged'|t}<span class="remove"><i class="fa fa-times"></i></span></a>
113 </span>
114 {/if} 101 {/if}
115 {if="!empty($visibility)"} 102 {if="!empty($visibility)"}
116 {'with status'|t} 103 {'with status'|t}
@@ -118,6 +105,11 @@
118 {$visibility|t} 105 {$visibility|t}
119 </span> 106 </span>
120 {/if} 107 {/if}
108 {if="$untaggedonly"}
109 <span class="label label-private">
110 {'without any tag'|t}
111 </span>
112 {/if}
121 </div> 113 </div>
122 </div> 114 </div>
123 {/if} 115 {/if}
diff --git a/tpl/default/linklist.paging.html b/tpl/default/linklist.paging.html
index d8c1e76e..41e9fa34 100644
--- a/tpl/default/linklist.paging.html
+++ b/tpl/default/linklist.paging.html
@@ -6,10 +6,13 @@
6 {'Filters'|t} 6 {'Filters'|t}
7 </span> 7 </span>
8 {if="isLoggedIn()"} 8 {if="isLoggedIn()"}
9 <a href="?privateonly" title="{'Filter private links'|t}" 9 <a href="?privateonly" title="{'Filter private links'|t}"
10 class={if="$privateonly"}"filter-on"{else}"filter-off"{/if} 10 class={if="$privateonly"}"filter-on"{else}"filter-off"{/if}
11 ><i class="fa fa-key"></i></a> 11 ><i class="fa fa-key"></i></a>
12 {/if} 12 {/if}
13 <a href="?untaggedonly" title="{'Filter untagged links'|t}"
14 class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
15 ><i class="fa fa-tag"></i></a>
13 <a href="#" class="filter-off fold-all pure-u-lg-0" title="Fold all"> 16 <a href="#" class="filter-off fold-all pure-u-lg-0" title="Fold all">
14 <i class="fa fa-chevron-up"></i> 17 <i class="fa fa-chevron-up"></i>
15 </a> 18 </a>
@@ -55,4 +58,4 @@
55 </a> 58 </a>
56 </div> 59 </div>
57 </div> 60 </div>
58</div> \ No newline at end of file 61</div>
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 6c71a718..2411703c 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -97,30 +97,21 @@
97 97
98<div id="content"> 98<div id="content">
99 <div id="search" class="subheader-form"> 99 <div id="search" class="subheader-form">
100 <div class="pure-g"> 100 <form method="GET" class="pure-form searchform" name="searchform">
101 <div class="pure-u-1 pure-u-lg-1-2"> 101 <input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="{'Search text'|t}"
102 <form method="GET" class="searchform" name="searchform"> 102 {if="!empty($search_term)"}
103 <input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="{'Search text'|t}" 103 value="{$search_term}"
104 {if="!empty($search_term)"} 104 {/if}
105 value="{$search_term}" 105 >
106 {/if} 106 <input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="{'Filter by tag'|t}"
107 > 107 {if="!empty($search_tags)"}
108 <button type="submit" class="search-button"><i class="fa fa-search"></i></button> 108 value="{$search_tags}"
109 </form> 109 {/if}
110 </div> 110 autocomplete="off" data-multiple data-autofirst data-minChars="1"
111 <div class="pure-u-1 pure-u-lg-1-2"> 111 data-list="{loop="$tags"}{$key}, {/loop}"
112 <form method="GET" class="tagfilter" name="tagfilter"> 112 >
113 <input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="{'Filter by tag'|t}" 113 <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
114 {if="!empty($search_tags)"} 114 </form>
115 value="{$search_tags}"
116 {/if}
117 autocomplete="off" data-multiple data-autofirst data-minChars="1"
118 data-list="{loop="$tags"}{$key}, {/loop}"
119 >
120 <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
121 </form>
122 </div>
123 </div>
124 </div> 115 </div>
125 <div id="actions" class="subheader-form"> 116 <div id="actions" class="subheader-form">
126 <div class="pure-g"> 117 <div class="pure-g">