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) // -------- 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); 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"] { } } +.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; } 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 () { /** * 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 { 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 @@ {/if} + +