From: VirtualTam Date: Sat, 5 Aug 2017 08:40:35 +0000 (+0200) Subject: Merge pull request #910 from virtualtam/documentation/improvements X-Git-Tag: v0.9.1~1^2~8 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=b4ff0afb24db6e4cb3543bbd71f01bbb0716b144;hp=29712e905b8a9bdf1eaa21cbda3ca7c9eb215937;p=github%2Fshaarli%2FShaarli.git Merge pull request #910 from virtualtam/documentation/improvements Include generated doc in release archives, remove HTML from SCM --- diff --git a/README.md b/README.md index c9e83899..5ca25720 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Shaarli logo](doc/images/doc-logo.png) +![Shaarli logo](doc/md/images/doc-logo.png) The personal, minimalist, super-fast, database free, bookmarking service. 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 * - searchterm: term search * @param bool $casesensitive Optional: Perform case sensitive filter * @param string $visibility return only all/private/public links + * @param string $untaggedonly return only untagged links * * @return array filtered links, all links if no suitable filter was provided. */ - public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all') + public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all', $untaggedonly = false) { // Filter link database according to parameters. $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; // Search tags + fullsearch - blank string parameter will return all links. - $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; + $type = LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT; // == "vuotext" $request = [$searchtags, $searchterm]; $linkFilter = new LinkFilter($this); - return $linkFilter->filter($type, $request, $casesensitive, $visibility); + return $linkFilter->filter($type, $request, $casesensitive, $visibility, $untaggedonly); } /** @@ -463,6 +464,39 @@ You use the community supported version of the original Shaarli project, by Seba return $tags; } + /** + * Rename or delete a tag across all links. + * + * @param string $from Tag to rename + * @param string $to New tag. If none is provided, the from tag will be deleted + * + * @return array|bool List of altered links or false on error + */ + public function renameTag($from, $to) + { + if (empty($from)) { + return false; + } + $delete = empty($to); + // True for case-sensitive tag search. + $linksToAlter = $this->filterSearch(['searchtags' => $from], true); + foreach($linksToAlter as $key => &$value) + { + $tags = preg_split('/\s+/', trim($value['tags'])); + if (($pos = array_search($from, $tags)) !== false) { + if ($delete) { + unset($tags[$pos]); // Remove tag. + } else { + $tags[$pos] = trim($to); + } + $value['tags'] = trim(implode(' ', array_unique($tags))); + $this[$value['id']] = $value; + } + } + + return $linksToAlter; + } + /** * Returns the list of days containing articles (oldest first) * Output: An array containing days (in format YYYYMMDD). 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 * @param mixed $request Filter content. * @param bool $casesensitive Optional: Perform case sensitive filter if true. * @param string $visibility Optional: return only all/private/public links + * @param string $untaggedonly Optional: return only untagged links. Applies only if $type includes FILTER_TAG * * @return array filtered link list. */ - public function filter($type, $request, $casesensitive = false, $visibility = 'all') + public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false) { if (! in_array($visibility, ['all', 'public', 'private'])) { $visibility = 'all'; @@ -64,23 +65,34 @@ class LinkFilter switch($type) { case self::$FILTER_HASH: return $this->filterSmallHash($request); - case self::$FILTER_TAG | self::$FILTER_TEXT: - if (!empty($request)) { - $filtered = $this->links; - if (isset($request[0])) { - $filtered = $this->filterTags($request[0], $casesensitive, $visibility); - } - if (isset($request[1])) { - $lf = new LinkFilter($filtered); - $filtered = $lf->filterFulltext($request[1], $visibility); + case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext" + $noRequest = empty($request) || (empty($request[0]) && empty($request[1])); + if ($noRequest) { + if ($untaggedonly) { + return $this->filterUntagged($visibility); } - return $filtered; + return $this->noFilter($visibility); } - return $this->noFilter($visibility); + if ($untaggedonly) { + $filtered = $this->filterUntagged($visibility); + } else { + $filtered = $this->links; + } + if (!empty($request[0])) { + $filtered = (new LinkFilter($filtered))->filterTags($request[0], $casesensitive, $visibility); + } + if (!empty($request[1])) { + $filtered = (new LinkFilter($filtered))->filterFulltext($request[1], $visibility); + } + return $filtered; case self::$FILTER_TEXT: return $this->filterFulltext($request, $visibility); case self::$FILTER_TAG: - return $this->filterTags($request, $casesensitive, $visibility); + if ($untaggedonly) { + return $this->filterUntagged($visibility); + } else { + return $this->filterTags($request, $casesensitive, $visibility); + } case self::$FILTER_DAY: return $this->filterDay($request); default: @@ -253,9 +265,6 @@ class LinkFilter { // Implode if array for clean up. $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags; - if ($tags === false) { - return $this->filterUntagged($visibility); - } if (empty($tags)) { return $this->noFilter($visibility); } 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 $this->tpl->assign('version', shaarli_version); $this->tpl->assign('scripturl', index_url($_SERVER)); $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? + $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); if ($this->conf->exists('general.header_link')) { $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() { unset($_SESSION['ip']); unset($_SESSION['username']); unset($_SESSION['privateonly']); + unset($_SESSION['untaggedonly']); } setcookie('shaarli_staySignedIn', FALSE, 0, WEB_PATH); } @@ -685,6 +686,7 @@ function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager) { * @param ConfigManager $conf Configuration Manager instance. * @param PluginManager $pluginManager Plugin Manager instance, * @param LinkDB $LINKSDB + * @param History $history instance */ function renderPage($conf, $pluginManager, $LINKSDB, $history) { @@ -1017,6 +1019,19 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) exit; } + // -------- User wants to see only untagged links (toggle) + if (isset($_GET['untaggedonly'])) { + $_SESSION['untaggedonly'] = !$_SESSION['untaggedonly']; + + if (! empty($_SERVER['HTTP_REFERER'])) { + $location = generateLocation($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST'], array('untaggedonly')); + } else { + $location = '?'; + } + header('Location: '. $location); + exit; + } + // -------- Handle other actions allowed for non-logged in users: if (!isLoggedIn()) { @@ -1184,41 +1199,18 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history) die('Wrong token.'); } - // Delete a tag: - if (isset($_POST['deletetag']) && !empty($_POST['fromtag'])) { - $needle = trim($_POST['fromtag']); - // True for case-sensitive tag search. - $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); - foreach($linksToAlter as $key=>$value) - { - $tags = explode(' ',trim($value['tags'])); - unset($tags[array_search($needle,$tags)]); // Remove tag. - $value['tags']=trim(implode(' ',$tags)); - $LINKSDB[$key]=$value; - $history->updateLink($LINKSDB[$key]); - } - $LINKSDB->save($conf->get('resource.page_cache')); - echo ''; - exit; - } - - // Rename a tag: - if (isset($_POST['renametag']) && !empty($_POST['fromtag']) && !empty($_POST['totag'])) { - $needle = trim($_POST['fromtag']); - // True for case-sensitive tag search. - $linksToAlter = $LINKSDB->filterSearch(array('searchtags' => $needle), true); - foreach($linksToAlter as $key=>$value) { - $tags = preg_split('/\s+/', trim($value['tags'])); - // Replace tags value. - $tags[array_search($needle, $tags)] = trim($_POST['totag']); - $value['tags'] = implode(' ', array_unique($tags)); - $LINKSDB[$key] = $value; - $history->updateLink($LINKSDB[$key]); - } - $LINKSDB->save($conf->get('resource.page_cache')); // Save to disk. - echo ''; - exit; + $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag'])); + $LINKSDB->save($conf->get('resource.page_cache')); + foreach ($alteredLinks as $link) { + $history->updateLink($link); } + $delete = empty($_POST['totag']); + $redirect = $delete ? 'do=changetag' : 'searchtags='. urlencode(escape($_POST['totag'])); + $alert = $delete + ? sprintf(t('The tag was removed from %d links.'), count($alteredLinks)) + : sprintf(t('The tag was renamed in %d links.'), count($alteredLinks)); + echo ''; + exit; } // -------- User wants to add a link without using the bookmarklet: Show form. @@ -1651,7 +1643,7 @@ function buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager) 'searchtags' => $searchtags, 'searchterm' => $searchterm, ]; - $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility); + $linksToDisplay = $LINKSDB->filterSearch($request, false, $visibility, !empty($_SESSION['untaggedonly'])); } // ---- Handle paging. @@ -2245,6 +2237,12 @@ if (!isset($_SESSION['LINKS_PER_PAGE'])) { $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); } +try { + $history = new History($conf->get('resource.history')); +} catch(Exception $e) { + die($e->getMessage()); +} + $linkDb = new LinkDB( $conf->get('resource.datastore'), isLoggedIn(), @@ -2253,12 +2251,6 @@ $linkDb = new LinkDB( $conf->get('redirector.encode_url') ); -try { - $history = new History($conf->get('resource.history')); -} catch(Exception $e) { - die($e->getMessage()); -} - $container = new \Slim\Container(); $container['conf'] = $conf; $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 $this->assertEquals($linkIds[$cpt++], $key); } } + + /** + * Test rename tag with a valid value present in multiple links + */ + public function testRenameTagMultiple() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', 'Taz'); + $this->assertEquals(3, count($res)); + $this->assertContains(' Taz ', $linkDB[4]['tags']); + $this->assertContains(' Taz ', $linkDB[1]['tags']); + $this->assertContains(' Taz ', $linkDB[0]['tags']); + } + + /** + * Test rename tag with a valid value + */ + public function testRenameTagCaseSensitive() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false, ''); + + $res = $linkDB->renameTag('sTuff', 'Taz'); + $this->assertEquals(1, count($res)); + $this->assertEquals('Taz', $linkDB[41]['tags']); + } + + /** + * Test rename tag with invalid values + */ + public function testRenameTagInvalid() + { + $linkDB = new LinkDB(self::$testDatastore, false, false); + + $this->assertFalse($linkDB->renameTag('', 'test')); + $this->assertFalse($linkDB->renameTag('', '')); + // tag non existent + $this->assertEquals([], $linkDB->renameTag('test', '')); + $this->assertEquals([], $linkDB->renameTag('test', 'retest')); + } + + /** + * Test delete tag with a valid value + */ + public function testDeleteTag() + { + self::$refDB->write(self::$testDatastore); + $linkDB = new LinkDB(self::$testDatastore, true, false); + + $res = $linkDB->renameTag('cartoon', null); + $this->assertEquals(3, count($res)); + $this->assertNotContains('cartoon', $linkDB[4]['tags']); + } } 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 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '')) ); - // Untagged only $this->assertEquals( self::$refDB->countUntaggedLinks(), - count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, false)) + count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, /*$request=*/'', /*$casesensitive=*/false, /*$visibility=*/'all', /*$untaggedonly=*/true)) ); $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"] { border-radius: 2px; color: #252525; } +@media screen and (max-width: 64em) { + .searchform { + max-width: 260px; + margin: 0 auto; + } +} /* because chrome */ #search input[type="text"]::-webkit-input-placeholder, @@ -236,43 +242,37 @@ body, .pure-g [class*="pure-u"] { #search button, #search-tagcloud button, #search-linklist button { - background: transparent; + padding: 4px 8px 6px 8px; + background-color: #1B926C; + color: #f5f5f5; border: none; + border-radius: 2px; } -#search button { - color: #f5f5f5; +#search-tagcloud button { + width: 90%; } -#search-linklist button { - color: #252525; +@media screen and (max-width: 64em) { + #search-linklist button { + width: 100%; + } + #search-linklist .awesomplete { + margin: 5px 0; + } } #search button:hover, -#search-linklist button:hover { - color: #fff; -} +#search-linklist button:hover, #search-tagcloud button:hover { color: #d0d0d0; } +#search, #search-linklist { - padding: 5px 0; + padding: 6px 0; } -@media screen and (min-width: 64em) { - #search .searchform, - #search-linklist .searchform { - margin-right: 25px; - text-align: right; - } - - #search .tagfilter, - #search-linklist .tagfilter { - margin-left: 25px; - text-align: left; - } -} @media screen and (max-width: 64em) { #search, #search * { visibility: hidden; @@ -321,7 +321,6 @@ body, .pure-g [class*="pure-u"] { } .subheader-form input[type="text"], .subheader-form input[type="password"], .subheader-form .remember-me { - margin: 0 0 5px 0; padding: 5px 5px 3px 15px; height: 20px; 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 @@ {loop="$plugins_header.fields_toolbar"} @@ -91,7 +82,7 @@
{'Nothing found.'|t}
- {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility)"} + {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility) or $untaggedonly"}
@@ -107,10 +98,6 @@ {$value} {/loop} - {elseif="$search_tags === false"} - - {'untagged'|t} - {/if} {if="!empty($visibility)"} {'with status'|t} @@ -118,6 +105,11 @@ {$visibility|t} {/if} + {if="$untaggedonly"} + + {'without any tag'|t} + + {/if}
{/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 @@ {'Filters'|t} {if="isLoggedIn()"} - + {/if} + @@ -55,4 +58,4 @@ - \ No newline at end of file + 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 @@