aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorArthurHoaro <arthur@hoa.ro>2017-05-25 15:03:32 +0200
committerGitHub <noreply@github.com>2017-05-25 15:03:32 +0200
commit8b27824338eb445d69730c9b05f05b131ccea52f (patch)
tree1f45d68d65185277834bb416470df8a10684095f
parent7481dd6e66c132ae064613de19e73dab0ca2de19 (diff)
parent638364987a42f8f48665ac29fdcbfc83de1de9e3 (diff)
downloadShaarli-8b27824338eb445d69730c9b05f05b131ccea52f.tar.gz
Shaarli-8b27824338eb445d69730c9b05f05b131ccea52f.tar.zst
Shaarli-8b27824338eb445d69730c9b05f05b131ccea52f.zip
Merge pull request #819 from ArthurHoaro/feature/multi-delete
Bulk deletion
-rw-r--r--index.php19
-rw-r--r--tpl/default/css/shaarli.css13
-rw-r--r--tpl/default/js/shaarli.js66
-rw-r--r--tpl/default/linklist.html5
-rw-r--r--tpl/default/page.header.html7
5 files changed, 93 insertions, 17 deletions
diff --git a/index.php b/index.php
index fb8b96b5..40539a04 100644
--- a/index.php
+++ b/index.php
@@ -1311,18 +1311,21 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history)
1311 // -------- User clicked the "Delete" button when editing a link: Delete link from database. 1311 // -------- User clicked the "Delete" button when editing a link: Delete link from database.
1312 if ($targetPage == Router::$PAGE_DELETELINK) 1312 if ($targetPage == Router::$PAGE_DELETELINK)
1313 { 1313 {
1314 // We do not need to ask for confirmation:
1315 // - confirmation is handled by JavaScript
1316 // - we are protected from XSRF by the token.
1317
1318 if (! tokenOk($_GET['token'])) { 1314 if (! tokenOk($_GET['token'])) {
1319 die('Wrong token.'); 1315 die('Wrong token.');
1320 } 1316 }
1321 1317
1322 $id = intval(escape($_GET['lf_linkdate'])); 1318 if (strpos($_GET['lf_linkdate'], ' ') !== false) {
1323 $link = $LINKSDB[$id]; 1319 $ids = array_values(array_filter(preg_split('/\s+/', escape($_GET['lf_linkdate']))));
1324 $pluginManager->executeHooks('delete_link', $link); 1320 } else {
1325 unset($LINKSDB[$id]); 1321 $ids = [$_GET['lf_linkdate']];
1322 }
1323 foreach ($ids as $id) {
1324 $id = (int) escape($id);
1325 $link = $LINKSDB[$id];
1326 $pluginManager->executeHooks('delete_link', $link);
1327 unset($LINKSDB[$id]);
1328 }
1326 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk 1329 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk
1327 $history->deleteLink($link); 1330 $history->deleteLink($link);
1328 1331
diff --git a/tpl/default/css/shaarli.css b/tpl/default/css/shaarli.css
index ef9ee23b..4415a1b7 100644
--- a/tpl/default/css/shaarli.css
+++ b/tpl/default/css/shaarli.css
@@ -279,6 +279,19 @@ body, .pure-g [class*="pure-u"] {
279 } 279 }
280} 280}
281 281
282.subheader-form a.button {
283 color: #f5f5f5;
284 font-weight: bold;
285 text-decoration: none;
286 border: 2px solid #f5f5f5;
287 border-radius: 5px;
288 padding: 3px 10px;
289}
290
291.linklist-item-editbuttons .delete-checkbox {
292 display: none;
293}
294
282#header-login-form input[type="text"], #header-login-form input[type="password"] { 295#header-login-form input[type="text"], #header-login-form input[type="password"] {
283 width: 200px; 296 width: 200px;
284} 297}
diff --git a/tpl/default/js/shaarli.js b/tpl/default/js/shaarli.js
index 4d47fcd0..ceb1d1b8 100644
--- a/tpl/default/js/shaarli.js
+++ b/tpl/default/js/shaarli.js
@@ -216,14 +216,14 @@ window.onload = function () {
216 /** 216 /**
217 * Autofocus text fields 217 * Autofocus text fields
218 */ 218 */
219 // ES6 syntax 219 var autofocusElements = document.querySelectorAll('.autofocus');
220 let autofocusElements = document.querySelectorAll('.autofocus'); 220 var breakLoop = false;
221 for (let autofocusElement of autofocusElements) { 221 [].forEach.call(autofocusElements, function(autofocusElement) {
222 if (autofocusElement.value == '') { 222 if (autofocusElement.value == '' && ! breakLoop) {
223 autofocusElement.focus(); 223 autofocusElement.focus();
224 break; 224 breakLoop = true;
225 } 225 }
226 } 226 });
227 227
228 /** 228 /**
229 * Handle sub menus/forms 229 * Handle sub menus/forms
@@ -357,11 +357,61 @@ window.onload = function () {
357 var continent = document.getElementById('continent'); 357 var continent = document.getElementById('continent');
358 var city = document.getElementById('city'); 358 var city = document.getElementById('city');
359 if (continent != null && city != null) { 359 if (continent != null && city != null) {
360 continent.addEventListener('change', function(event) { 360 continent.addEventListener('change', function (event) {
361 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, true); 361 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, true);
362 }); 362 });
363 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, false); 363 hideTimezoneCities(city, continent.options[continent.selectedIndex].value, false);
364 } 364 }
365
366 /**
367 * Bulk actions
368 */
369 var linkCheckboxes = document.querySelectorAll('.delete-checkbox');
370 var bar = document.getElementById('actions');
371 [].forEach.call(linkCheckboxes, function(checkbox) {
372 checkbox.style.display = 'block';
373 checkbox.addEventListener('click', function(event) {
374 var count = 0;
375 var linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
376 [].forEach.call(linkCheckedCheckboxes, function(checkbox) {
377 count++;
378 });
379 if (count == 0 && bar.classList.contains('open')) {
380 bar.classList.toggle('open');
381 } else if (count > 0 && ! bar.classList.contains('open')) {
382 bar.classList.toggle('open');
383 }
384 });
385 });
386
387 var deleteButton = document.getElementById('actions-delete');
388 var token = document.querySelector('input[type="hidden"][name="token"]');
389 if (deleteButton != null && token != null) {
390 deleteButton.addEventListener('click', function(event) {
391 event.preventDefault();
392
393 var links = [];
394 var linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked');
395 [].forEach.call(linkCheckedCheckboxes, function(checkbox) {
396 links.push({
397 'id': checkbox.value,
398 'title': document.querySelector('.linklist-item[data-id="'+ checkbox.value +'"] .linklist-link').innerHTML
399 });
400 });
401
402 var message = 'Are you sure you want to delete '+ links.length +' links?\n';
403 message += 'This action is IRREVERSIBLE!\n\nTitles:\n';
404 var ids = '';
405 links.forEach(function(item) {
406 message += ' - '+ item['title'] +'\n';
407 ids += item['id'] +'+';
408 });
409
410 if (window.confirm(message)) {
411 window.location = '?delete_link&lf_linkdate='+ ids +'&token='+ token.value;
412 }
413 });
414 }
365}; 415};
366 416
367function activateFirefoxSocial(node) { 417function activateFirefoxSocial(node) {
@@ -397,7 +447,7 @@ function activateFirefoxSocial(node) {
397 */ 447 */
398function hideTimezoneCities(cities, currentContinent, reset = false) { 448function hideTimezoneCities(cities, currentContinent, reset = false) {
399 var first = true; 449 var first = true;
400 [].forEach.call(cities, function(option) { 450 [].forEach.call(cities, function (option) {
401 if (option.getAttribute('data-continent') != currentContinent) { 451 if (option.getAttribute('data-continent') != currentContinent) {
402 option.className = 'hidden'; 452 option.className = 'hidden';
403 } else { 453 } else {
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 57ef4567..6a4e14a6 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -15,6 +15,8 @@
15 {/if} 15 {/if}
16</div> 16</div>
17 17
18<input type="hidden" name="token" value="{$token}">
19
18<div id="search-linklist"> 20<div id="search-linklist">
19 21
20 <div class="pure-g"> 22 <div class="pure-g">
@@ -121,7 +123,7 @@
121 <div class="pure-u-lg-20-24 pure-u-22-24"> 123 <div class="pure-u-lg-20-24 pure-u-22-24">
122 {loop="links"} 124 {loop="links"}
123 <div class="anchor" id="{$value.shorturl}"></div> 125 <div class="anchor" id="{$value.shorturl}"></div>
124 <div class="linklist-item{if="$value.class"} {$value.class}{/if}"> 126 <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
125 127
126 <div class="linklist-item-title"> 128 <div class="linklist-item-title">
127 {if="isLoggedIn()"} 129 {if="isLoggedIn()"}
@@ -129,6 +131,7 @@
129 {if="$value.private"} 131 {if="$value.private"}
130 <span class="label label-private">{'Private'|t}</span> 132 <span class="label label-private">{'Private'|t}</span>
131 {/if} 133 {/if}
134 <input type="checkbox" class="delete-checkbox" value="{$value.id}">
132 <!-- FIXME! JS translation --> 135 <!-- FIXME! JS translation -->
133 <a href="?edit_link={$value.id}" title="{'Edit'|t}"><i class="fa fa-pencil-square-o edit-link"></i></a> 136 <a href="?edit_link={$value.id}" title="{'Edit'|t}"><i class="fa fa-pencil-square-o edit-link"></i></a>
134 <a href="#" title="{'Fold'|t}" class="fold-button"><i class="fa fa-chevron-up"></i></a> 137 <a href="#" title="{'Fold'|t}" class="fold-button"><i class="fa fa-chevron-up"></i></a>
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 9388ef79..6c71a718 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -122,6 +122,13 @@
122 </div> 122 </div>
123 </div> 123 </div>
124 </div> 124 </div>
125 <div id="actions" class="subheader-form">
126 <div class="pure-g">
127 <div class="pure-u-1">
128 <a href="" id="actions-delete" class="button">Delete</a>
129 </div>
130 </div>
131 </div>
125 {if="!isLoggedIn()"} 132 {if="!isLoggedIn()"}
126 <form method="post" name="loginform"> 133 <form method="post" name="loginform">
127 <div class="subheader-form" id="header-login-form"> 134 <div class="subheader-form" id="header-login-form">