]> git.immae.eu Git - github/shaarli/Shaarli.git/commitdiff
Merge pull request #819 from ArthurHoaro/feature/multi-delete
authorArthurHoaro <arthur@hoa.ro>
Thu, 25 May 2017 13:03:32 +0000 (15:03 +0200)
committerGitHub <noreply@github.com>
Thu, 25 May 2017 13:03:32 +0000 (15:03 +0200)
Bulk deletion

index.php
tpl/default/css/shaarli.css
tpl/default/js/shaarli.js
tpl/default/linklist.html
tpl/default/page.header.html

index fb8b96b56cce50166fa786511339e99b9b6d1b86..40539a04666c1c16acbad9c604a5e7a2cef48cc0 100644 (file)
--- a/index.php
+++ b/index.php
@@ -1311,18 +1311,21 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
     // -------- User clicked the "Delete" button when editing a link: Delete link from database.
     if ($targetPage == Router::$PAGE_DELETELINK)
     {
-        // We do not need to ask for confirmation:
-        // - confirmation is handled by JavaScript
-        // - we are protected from XSRF by the token.
-
         if (! tokenOk($_GET['token'])) {
             die('Wrong token.');
         }
 
-        $id = intval(escape($_GET['lf_linkdate']));
-        $link = $LINKSDB[$id];
-        $pluginManager->executeHooks('delete_link', $link);
-        unset($LINKSDB[$id]);
+        if (strpos($_GET['lf_linkdate'], ' ') !== false) {
+            $ids = array_values(array_filter(preg_split('/\s+/', escape($_GET['lf_linkdate']))));
+        } else {
+            $ids = [$_GET['lf_linkdate']];
+        }
+        foreach ($ids as $id) {
+            $id = (int) escape($id);
+            $link = $LINKSDB[$id];
+            $pluginManager->executeHooks('delete_link', $link);
+            unset($LINKSDB[$id]);
+        }
         $LINKSDB->save($conf->get('resource.page_cache')); // save to disk
         $history->deleteLink($link);
 
index ef9ee23b28a2152f75d35dd5c6253654081a454d..4415a1b70f5a32fb0133cc9d0979b0961ba3fac8 100644 (file)
@@ -279,6 +279,19 @@ body, .pure-g [class*="pure-u"] {
     }
 }
 
+.subheader-form a.button {
+    color: #f5f5f5;
+    font-weight: bold;
+    text-decoration: none;
+    border: 2px solid #f5f5f5;
+    border-radius: 5px;
+    padding: 3px 10px;
+}
+
+.linklist-item-editbuttons .delete-checkbox {
+    display: none;
+}
+
 #header-login-form input[type="text"], #header-login-form input[type="password"] {
     width: 200px;
 }
index 4d47fcd0c2cd4aaf3be8080e84c61782adabd247..ceb1d1b863fd096a437ff24e8ac1b661e07347f8 100644 (file)
@@ -216,14 +216,14 @@ window.onload = function () {
     /**
      * Autofocus text fields
      */
-    // ES6 syntax
-    let autofocusElements = document.querySelectorAll('.autofocus');
-    for (let autofocusElement of autofocusElements) {
-        if (autofocusElement.value == '') {
+    var autofocusElements = document.querySelectorAll('.autofocus');
+    var breakLoop = false;
+    [].forEach.call(autofocusElements, function(autofocusElement) {
+        if (autofocusElement.value == '' && ! breakLoop) {
             autofocusElement.focus();
-            break;
+            breakLoop = true;
         }
-    }
+    });
 
     /**
      * Handle sub menus/forms
@@ -357,11 +357,61 @@ window.onload = function () {
     var continent = document.getElementById('continent');
     var city = document.getElementById('city');
     if (continent != null && city != null) {
-        continent.addEventListener('change', function(event) {
+        continent.addEventListener('change', function (event) {
             hideTimezoneCities(city, continent.options[continent.selectedIndex].value, true);
         });
         hideTimezoneCities(city, continent.options[continent.selectedIndex].value, false);
     }
+
+    /**
+     * Bulk actions
+     */
+    var linkCheckboxes = document.querySelectorAll('.delete-checkbox');
+    var bar = document.getElementById('actions');
+    [].forEach.call(linkCheckboxes, function(checkbox) {
+        checkbox.style.display = 'block';
+        checkbox.addEventListener('click', function(event) {
+            var count = 0;
+            var linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
+            [].forEach.call(linkCheckedCheckboxes, function(checkbox) {
+                count++;
+            });
+            if (count == 0 && bar.classList.contains('open')) {
+                bar.classList.toggle('open');
+            } else if (count > 0 && ! bar.classList.contains('open')) {
+                bar.classList.toggle('open');
+            }
+        });
+    });
+
+    var deleteButton = document.getElementById('actions-delete');
+    var token = document.querySelector('input[type="hidden"][name="token"]');
+    if (deleteButton != null && token != null) {
+        deleteButton.addEventListener('click', function(event) {
+            event.preventDefault();
+
+            var links = [];
+            var linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
+            [].forEach.call(linkCheckedCheckboxes, function(checkbox) {
+                links.push({
+                    'id': checkbox.value,
+                    'title': document.querySelector('.linklist-item[data-id="'+ checkbox.value +'"] .linklist-link').innerHTML
+                });
+            });
+
+            var message = 'Are you sure you want to delete '+ links.length +' links?\n';
+            message += 'This action is IRREVERSIBLE!\n\nTitles:\n';
+            var ids = '';
+            links.forEach(function(item) {
+                message += '  - '+ item['title'] +'\n';
+                ids += item['id'] +'+';
+            });
+
+            if (window.confirm(message)) {
+                window.location = '?delete_link&lf_linkdate='+ ids +'&token='+ token.value;
+            }
+        });
+    }
 };
 
 function activateFirefoxSocial(node) {
@@ -397,7 +447,7 @@ function activateFirefoxSocial(node) {
  */
 function hideTimezoneCities(cities, currentContinent, reset = false) {
     var first = true;
-    [].forEach.call(cities, function(option) {
+    [].forEach.call(cities, function (option) {
         if (option.getAttribute('data-continent') != currentContinent) {
             option.className = 'hidden';
         } else {
index 57ef4567a8ee754c1f308c3b153e6d0188223b86..6a4e14a6a820b2bf0629b07ab312c642fee2abac 100644 (file)
@@ -15,6 +15,8 @@
   {/if}
 </div>
 
+<input type="hidden" name="token" value="{$token}">
+
 <div id="search-linklist">
 
   <div class="pure-g">
     <div class="pure-u-lg-20-24 pure-u-22-24">
       {loop="links"}
         <div class="anchor" id="{$value.shorturl}"></div>
-        <div class="linklist-item{if="$value.class"} {$value.class}{/if}">
+        <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
 
           <div class="linklist-item-title">
             {if="isLoggedIn()"}
                 {if="$value.private"}
                   <span class="label label-private">{'Private'|t}</span>
                 {/if}
+                <input type="checkbox" class="delete-checkbox" value="{$value.id}">
                 <!-- FIXME! JS translation -->
                 <a href="?edit_link={$value.id}" title="{'Edit'|t}"><i class="fa fa-pencil-square-o edit-link"></i></a>
                 <a href="#" title="{'Fold'|t}" class="fold-button"><i class="fa fa-chevron-up"></i></a>
index 9388ef79e9cdc42ca3e1b9c288a21df91d4255ce..6c71a718371a3f93a010a2e246dbe16fd810a930 100644 (file)
       </div>
     </div>
   </div>
+  <div id="actions" class="subheader-form">
+    <div class="pure-g">
+      <div class="pure-u-1">
+        <a href="" id="actions-delete" class="button">Delete</a>
+      </div>
+    </div>
+  </div>
   {if="!isLoggedIn()"}
     <form method="post" name="loginform">
       <div class="subheader-form" id="header-login-form">