diff --git a/plinth/modules/backups/forms.py b/plinth/modules/backups/forms.py index 39388a0b4..4340587fe 100644 --- a/plinth/modules/backups/forms.py +++ b/plinth/modules/backups/forms.py @@ -54,7 +54,7 @@ class CreateArchiveForm(forms.Form): regex=r'^[^{}/]*$', required=False, strip=True) selected_apps = forms.MultipleChoiceField( label=_('Included apps'), help_text=_('Apps to include in the backup'), - widget=forms.CheckboxSelectMultiple) + widget=forms.CheckboxSelectMultiple(attrs={'class': 'has-select-all'})) def __init__(self, *args, **kwargs): """Initialize the form with selectable apps.""" @@ -71,7 +71,7 @@ class CreateArchiveForm(forms.Form): class RestoreForm(forms.Form): selected_apps = forms.MultipleChoiceField( label=_('Select the apps you want to restore'), - widget=forms.CheckboxSelectMultiple) + widget=forms.CheckboxSelectMultiple(attrs={'class': 'has-select-all'})) def __init__(self, *args, **kwargs): """Initialize the form with selectable apps.""" diff --git a/plinth/modules/backups/static/select_all.js b/plinth/modules/backups/static/select_all.js deleted file mode 100644 index 01bd9d7d7..000000000 --- a/plinth/modules/backups/static/select_all.js +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-or-later -/* - This file is part of FreedomBox. - - @licstart The following is the entire license notice for the - JavaScript code in this page. - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - - @licend The above is the entire license notice - for the JavaScript code in this page. -*/ - -function addSelectAll() { - let ul = document.getElementById('id_backups-selected_apps'); - let li = document.createElement('li'); - - let label = document.createElement('label'); - label.for = "select_all"; - - let checkbox = document.createElement('input'); - checkbox.type = "checkbox"; - checkbox.checked = "checked"; - checkbox.id = "select-all"; - - label.appendChild(checkbox); - li.appendChild(label); - - ul.insertBefore(li, ul.childNodes[0]); -}; - -addSelectAll(); - -/* - * When there is a change on the "select all" checkbox,set the - * checked property of all the checkboxes to the value of the - * "select all" checkbox - */ -$("#select-all").change(function() { - $("[type=checkbox]").prop('checked', $(this).prop("checked")); -}); - - -$('[type=checkbox]').change(function() { - // If the rest of the checkbox items are checked check the "select all" checkbox as well - if ($('[type=checkbox]:checked').length == ($('[type=checkbox]').length - 1)) { - $("#select-all").prop('checked', true); - } - // Uncheck "select all" if one of the listed checkbox item is unchecked - if (false == $(this).prop("checked")) { - $("#select-all").prop('checked', false); - } -}); - - diff --git a/plinth/modules/backups/templates/backups_form.html b/plinth/modules/backups/templates/backups_form.html index d8ca081b6..2e08d5ebf 100644 --- a/plinth/modules/backups/templates/backups_form.html +++ b/plinth/modules/backups/templates/backups_form.html @@ -21,7 +21,3 @@ {% endblock %} - -{% block page_js %} - -{% endblock %} diff --git a/plinth/modules/backups/templates/backups_restore.html b/plinth/modules/backups/templates/backups_restore.html index cf523553e..ac80828ae 100644 --- a/plinth/modules/backups/templates/backups_restore.html +++ b/plinth/modules/backups/templates/backups_restore.html @@ -29,7 +29,3 @@

{% endblock %} - -{% block page_js %} - -{% endblock %} diff --git a/plinth/tests/functional/__init__.py b/plinth/tests/functional/__init__.py index 7b3be695e..11fbd6018 100644 --- a/plinth/tests/functional/__init__.py +++ b/plinth/tests/functional/__init__.py @@ -454,7 +454,8 @@ def backup_create(browser, app_name, archive_name=None): buttons = browser.find_link_by_href('/plinth/sys/backups/create/') submit(browser, buttons.first) - browser.find_by_id('select-all').uncheck() + eventually(browser.find_by_css, args=['.select-all']) + browser.find_by_css('.select-all').first.uncheck() if archive_name: browser.find_by_id('id_backups-name').fill(archive_name) diff --git a/static/themes/default/js/main.js b/static/themes/default/js/main.js index 5f7167a01..b9f6c5bf9 100644 --- a/static/themes/default/js/main.js +++ b/static/themes/default/js/main.js @@ -121,3 +121,66 @@ window.addEventListener('pageshow', function(event) { element.remove(); } }); + +/* + * Select all option for multiple checkboxes. + */ +document.addEventListener('DOMContentLoaded', function(event) { + let parents = document.querySelectorAll('ul.has-select-all'); + for (const parent of parents) { + let li = document.createElement('li'); + + let label = document.createElement('label'); + label.for = "select_all"; + + let checkbox = document.createElement('input'); + checkbox.type = "checkbox"; + checkbox.setAttribute('class', 'select-all'); + + label.appendChild(checkbox); + li.appendChild(label); + + parent.insertBefore(li, parent.childNodes[0]); + setSelectAllValue(parent); + + checkbox.addEventListener('change', onSelectAllChanged); + + options = parent.querySelectorAll('input.has-select-all'); + for (const option of options) { + option.addEventListener('change', onSelectAllOptionsChanged); + } + } +}); + +// When there is a change on the "select all" checkbox, set the checked property +// of all the checkboxes to the value of the "select all" checkbox +function onSelectAllChanged(event) { + const selectAllCheckbox = event.currentTarget; + const parent = selectAllCheckbox.parentElement.parentElement.parentElement; + const options = parent.querySelectorAll('input.has-select-all'); + for (const option of options) { + option.checked = selectAllCheckbox.checked; + } +} + +// When there is a change on a checkbox controlled by a select all checkbox, +// update the value of checkbox. +function onSelectAllOptionsChanged(event) { + const parent = event.currentTarget.parentElement.parentElement.parentElement; + setSelectAllValue(parent); +} + +// Set/reset the checked property of "select all" checkbox based on whether all +// checkboxes it controls are checked. +function setSelectAllValue(parent) { + const options = parent.querySelectorAll('input.has-select-all'); + let enableSelectAll = true; + for (const option of options) { + if (!option.checked) { + enableSelectAll = false; + break; + } + } + + parent.querySelector('.select-all').checked = enableSelectAll; +}