]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Using only one form in linklist.html + adding untaggedonly filter - fix #885 889/head
authorLucas Cimon <lucas.cimon@gmail.com>
Thu, 1 Jun 2017 15:55:26 +0000 (17:55 +0200)
committerLucas Cimon <lucas.cimon@gmail.com>
Sun, 30 Jul 2017 14:19:34 +0000 (16:19 +0200)
application/LinkDB.php
application/LinkFilter.php
application/PageBuilder.php
index.php
tests/LinkFilterTest.php
tpl/default/css/shaarli.css
tpl/default/linklist.html
tpl/default/linklist.paging.html
tpl/default/page.header.html

index 8ca0fab30d55cbee54501579375e7d0697f48e62..9e3efd6b4229a691dbd9028deb73d4428cbeeea1 100644 (file)
@@ -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);
     }
 
     /**
index 0e887d3805a2fa7ef2024f5da2d6b155dc4a870e..9551952886c0bee94c122983c5b3ce8c8d6332a2 100644 (file)
@@ -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);
         }
index c86621a254d4b3f262811e6d2a2588cac0ad7c2b..7a42400d88f6baa2444932d179985f5ad89cd14a 100644 (file)
@@ -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'));
index 85486eb50d72ea16be69fe59e261612356e2a435..5c292b04489cf55da21fb2b2071462c960a4ae99 100644 (file)
--- 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);
 }
@@ -1017,6 +1018,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())
     {
@@ -1651,7 +1665,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.
index 741623580ce30e0980638675db8f17b9e02d2e70..d796d3a301c469652bbb8bcc6295db0e539417cb 100644 (file)
@@ -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(
index 39bbd0a3d5048fd1821bc7997ac0314fcbade002..e1868c59aafaa5da7f37b5b6972ac5986be0d6fb 100644 (file)
@@ -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%;
index 2568a5d6525c1622d77ea9bdadaad9140dadc34a..685821e395bfb184208ad2a96c6fd583220bc4d4 100644 (file)
 
 <div id="search-linklist">
 
-  <div class="pure-g">
-    <div class="pure-u-1 pure-u-lg-1-2">
-      <form method="GET" class="searchform" name="searchform">
-        <input type="text" tabindex="1" name="searchterm" placeholder="{'Search text'|t}"
-               {if="!empty($search_term)"}
-               value="{$search_term}"
-               {/if}
-        >
-        <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
-      </form>
-    </div>
-    <div class="pure-u-1 pure-u-lg-1-2">
-      <form method="GET" class="tagfilter" name="tagfilter">
-        <input type="text" tabindex="2" name="searchtags" placeholder="{'Filter by tag'|t}"
-               {if="!empty($search_tags)"}
-               value="{$search_tags}"
-               {/if}
-        autocomplete="off" data-multiple data-autofirst data-minChars="1"
-        data-list="{loop="$tags"}{$key}, {/loop}"
-        >
-        <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
-      </form>
-    </div>
-  </div>
+  <form method="GET" class="pure-form searchform" name="searchform">
+    <input type="text" tabindex="1" name="searchterm" class="searchterm" placeholder="{'Search text'|t}"
+           {if="!empty($search_term)"}
+           value="{$search_term}"
+           {/if}
+    >
+    <input type="text" tabindex="2" name="searchtags" class="searchtags" placeholder="{'Filter by tag'|t}"
+           {if="!empty($search_tags)"}
+           value="{$search_tags}"
+           {/if}
+    autocomplete="off" data-multiple data-autofirst data-minChars="1"
+    data-list="{loop="$tags"}{$key}, {/loop}"
+    >
+    <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
+  </form>
 </div>
 
 {loop="$plugins_header.fields_toolbar"}
@@ -91,7 +82,7 @@
         <div id="searchcriteria">{'Nothing found.'|t}</div>
       </div>
     </div>
-  {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility)"}
+  {elseif="!empty($search_term) or $search_tags !== '' or !empty($visibility) or $untaggedonly"}
     <div class="pure-g pure-alert pure-alert-success search-result">
       <div class="pure-u-2-24"></div>
       <div class="pure-u-20-24">
                 <a href="?removetag={function="urlencode($value)"}">{$value}<span class="remove"><i class="fa fa-times"></i></span></a>
               </span>
           {/loop}
-        {elseif="$search_tags === false"}
-          <span class="label label-tag" title="{'Remove tag'|t}">
-            <a href="?">{'untagged'|t}<span class="remove"><i class="fa fa-times"></i></span></a>
-          </span>
         {/if}
         {if="!empty($visibility)"}
           {'with status'|t}
             {$visibility|t}
           </span>
         {/if}
+        {if="$untaggedonly"}
+          <span class="label label-private">
+            {'without any tag'|t}
+          </span>
+        {/if}
       </div>
     </div>
   {/if}
index d8c1e76eb1bb6a5115ced49031567b4014e52cc5..41e9fa342bc87139fa26d5b130079a40c8bb97bb 100644 (file)
@@ -6,10 +6,13 @@
           {'Filters'|t}
         </span>
         {if="isLoggedIn()"}
-          <a href="?privateonly" title="{'Filter private links'|t}"
-             class={if="$privateonly"}"filter-on"{else}"filter-off"{/if}
-          ><i class="fa fa-key"></i></a>
+        <a href="?privateonly" title="{'Filter private links'|t}"
+           class={if="$privateonly"}"filter-on"{else}"filter-off"{/if}
+        ><i class="fa fa-key"></i></a>
         {/if}
+        <a href="?untaggedonly" title="{'Filter untagged links'|t}"
+           class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
+        ><i class="fa fa-tag"></i></a>
         <a href="#" class="filter-off fold-all pure-u-lg-0" title="Fold all">
           <i class="fa fa-chevron-up"></i>
         </a>
@@ -55,4 +58,4 @@
       </a>
     </div>
   </div>
-</div>
\ No newline at end of file
+</div>
index 6c71a718371a3f93a010a2e246dbe16fd810a930..2411703c7e7925aa3106ef7510cf26a1888d6b0c 100644 (file)
 
 <div id="content">
   <div id="search" class="subheader-form">
-    <div class="pure-g">
-      <div class="pure-u-1 pure-u-lg-1-2">
-        <form method="GET" class="searchform" name="searchform">
-          <input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="{'Search text'|t}"
-                 {if="!empty($search_term)"}
-                 value="{$search_term}"
-                 {/if}
-          >
-          <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
-        </form>
-      </div>
-      <div class="pure-u-1 pure-u-lg-1-2">
-        <form method="GET" class="tagfilter" name="tagfilter">
-          <input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="{'Filter by tag'|t}"
-                 {if="!empty($search_tags)"}
-                 value="{$search_tags}"
-                 {/if}
-          autocomplete="off" data-multiple data-autofirst data-minChars="1"
-          data-list="{loop="$tags"}{$key}, {/loop}"
-          >
-          <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
-        </form>
-      </div>
-    </div>
+    <form method="GET" class="pure-form searchform" name="searchform">
+      <input type="text" tabindex="1" id="searchform_value" name="searchterm" placeholder="{'Search text'|t}"
+             {if="!empty($search_term)"}
+             value="{$search_term}"
+             {/if}
+      >
+      <input type="text" tabindex="2" name="searchtags" id="tagfilter_value" placeholder="{'Filter by tag'|t}"
+             {if="!empty($search_tags)"}
+             value="{$search_tags}"
+             {/if}
+      autocomplete="off" data-multiple data-autofirst data-minChars="1"
+      data-list="{loop="$tags"}{$key}, {/loop}"
+      >
+      <button type="submit" class="search-button"><i class="fa fa-search"></i></button>
+    </form>
   </div>
   <div id="actions" class="subheader-form">
     <div class="pure-g">