diff --git a/plinth/modules/dynamicdns/forms.py b/plinth/modules/dynamicdns/forms.py index 22c62dee7..a7165661b 100644 --- a/plinth/modules/dynamicdns/forms.py +++ b/plinth/modules/dynamicdns/forms.py @@ -12,8 +12,8 @@ from plinth import cfg from plinth.utils import format_lazy -class ConfigureForm(forms.Form): - """Form to configure the Dynamic DNS client.""" +class DomainForm(forms.Form): + """Form to add/edit a domain in the Dynamic DNS client.""" help_update_url = \ gettext_lazy('The Variables <User>, <Pass>, <Ip>, ' '<Domain> may be used within the URL. For details ' diff --git a/plinth/modules/dynamicdns/static/dynamicdns.js b/plinth/modules/dynamicdns/static/dynamicdns.js index 73d3435d4..acc18d840 100644 --- a/plinth/modules/dynamicdns/static/dynamicdns.js +++ b/plinth/modules/dynamicdns/static/dynamicdns.js @@ -28,29 +28,29 @@ document.addEventListener('DOMContentLoaded', () => { const FREEDNS = 'https://freedns.afraid.org/dynamic/update.php?' + '_YOURAPIKEYHERE_'; - document.getElementById('id_service_type').addEventListener('change', () => { + document.getElementById('id_domain-service_type').addEventListener('change', () => { setMode(); - const service_type = document.getElementById('id_service_type').value; + const service_type = document.getElementById('id_domain-service_type').value; if (service_type === "noip.com") { - document.getElementById('id_update_url').value = NOIP; + document.getElementById('id_domain-update_url').value = NOIP; } else if (service_type === "freedns.afraid.org") { - document.getElementById('id_update_url').value = FREEDNS; + document.getElementById('id_domain-update_url').value = FREEDNS; } else { // GnuDIP and other - document.getElementById('id_update_url').value = ''; + document.getElementById('id_domain-update_url').value = ''; } }); - document.getElementById('id_show_password').addEventListener('change', () => { - if (document.getElementById('id_show_password').checked) { - document.getElementById('id_password').type = 'text'; + document.getElementById('id_domain-show_password').addEventListener('change', () => { + if (document.getElementById('id_domain-show_password').checked) { + document.getElementById('id_domain-password').type = 'text'; } else { - document.getElementById('id_password').type = 'password'; + document.getElementById('id_domain-password').type = 'password'; } }); function setMode() { - const service_type = document.getElementById('id_service_type').value; + const service_type = document.getElementById('id_domain-service_type').value; if (service_type === "gnudip") { setGnudipMode(); } else { @@ -62,19 +62,19 @@ document.addEventListener('DOMContentLoaded', () => { document.querySelectorAll('.form-group').forEach((element) => { element.style.display = 'block'; }); - document.getElementById('id_update_url').closest('.form-group').style.display = 'none'; - document.getElementById('id_disable_ssl_cert_check').closest('.form-group').style.display = 'none'; - document.getElementById('id_use_http_basic_auth').closest('.form-group').style.display = 'none'; - document.getElementById('id_use_ipv6').closest('.form-group').style.display = 'none'; - document.getElementById('id_server').closest('.form-group').style.display = 'block'; + document.getElementById('id_domain-update_url').closest('.form-group').style.display = 'none'; + document.getElementById('id_domain-disable_ssl_cert_check').closest('.form-group').style.display = 'none'; + document.getElementById('id_domain-use_http_basic_auth').closest('.form-group').style.display = 'none'; + document.getElementById('id_domain-use_ipv6').closest('.form-group').style.display = 'none'; + document.getElementById('id_domain-server').closest('.form-group').style.display = 'block'; } function setUpdateUrlMode() { - document.getElementById('id_update_url').closest('.form-group').style.display = 'block'; - document.getElementById('id_disable_ssl_cert_check').closest('.form-group').style.display = 'block'; - document.getElementById('id_use_http_basic_auth').closest('.form-group').style.display = 'block'; - document.getElementById('id_use_ipv6').closest('.form-group').style.display = 'block'; - document.getElementById('id_server').closest('.form-group').style.display = 'none'; + document.getElementById('id_domain-update_url').closest('.form-group').style.display = 'block'; + document.getElementById('id_domain-disable_ssl_cert_check').closest('.form-group').style.display = 'block'; + document.getElementById('id_domain-use_http_basic_auth').closest('.form-group').style.display = 'block'; + document.getElementById('id_domain-use_ipv6').closest('.form-group').style.display = 'block'; + document.getElementById('id_domain-server').closest('.form-group').style.display = 'none'; } setMode(); diff --git a/plinth/modules/dynamicdns/templates/dynamicdns-domain-delete.html b/plinth/modules/dynamicdns/templates/dynamicdns-domain-delete.html new file mode 100644 index 000000000..9affc6ead --- /dev/null +++ b/plinth/modules/dynamicdns/templates/dynamicdns-domain-delete.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% comment %} +# SPDX-License-Identifier: AGPL-3.0-or-later +{% endcomment %} + +{% load i18n %} + +{% block content %} + +
+ {% blocktrans trimmed %} + App configurations will be updated. + {% endblocktrans %} +
+ + + +{% endblock %} diff --git a/plinth/modules/dynamicdns/templates/dynamicdns-domain.html b/plinth/modules/dynamicdns/templates/dynamicdns-domain.html new file mode 100644 index 000000000..8943b5da9 --- /dev/null +++ b/plinth/modules/dynamicdns/templates/dynamicdns-domain.html @@ -0,0 +1,11 @@ +{% extends "form.html" %} +{% comment %} +# SPDX-License-Identifier: AGPL-3.0-or-later +{% endcomment %} + +{% load static %} + +{% block page_js %} + +{% endblock %} diff --git a/plinth/modules/dynamicdns/templates/dynamicdns.html b/plinth/modules/dynamicdns/templates/dynamicdns.html index 0a517321f..55a498fba 100644 --- a/plinth/modules/dynamicdns/templates/dynamicdns.html +++ b/plinth/modules/dynamicdns/templates/dynamicdns.html @@ -5,18 +5,20 @@ {% load bootstrap %} {% load i18n %} -{% load static %} - -{% block page_js %} - -{% endblock %} {% block extra_content %} -| {% trans "Last update" %} | {% trans "Result" %} | {% trans "IP Address" %} | +{% trans "Actions" %} | ||
|---|---|---|---|---|---|
| {{ domain.domain }} | ++ + {{ domain.domain }} + + | {{ domain.timestamp|timesince }} | {% if domain.result %} @@ -48,12 +58,28 @@ {% endif %} | {{ domain.ip_address|default_if_none:'-' }} | ++ + + + + + + |
{% trans "No domains configured." %}
{% endif %} {% endblock %} diff --git a/plinth/modules/dynamicdns/tests/test_functional.py b/plinth/modules/dynamicdns/tests/test_functional.py index cd0363e26..10df62575 100644 --- a/plinth/modules/dynamicdns/tests/test_functional.py +++ b/plinth/modules/dynamicdns/tests/test_functional.py @@ -76,8 +76,8 @@ class TestDynamicDNSApp(functional.BaseAppTests): @staticmethod def test_capitalized_domain_name(session_browser): """Test handling of capitalized domain name.""" - _configure(session_browser, _configs['gnudip1']) - _configure(session_browser, {'domain': 'FreedomBox.example.com'}) + config = dict(_configs['gnudip1'], domain='FreedomBox.example.com') + _configure(session_browser, config) _assert_has_config(session_browser, {'domain': 'freedombox.example.com'}) @@ -105,32 +105,54 @@ class TestDynamicDNSApp(functional.BaseAppTests): def _configure(browser, config): functional.nav_to_module(browser, 'dynamicdns') + current_domains = _get_domains(browser) + for domain in current_domains: + if domain.endswith('.example.com'): + _delete_domain(browser, domain) + + functional.nav_to_module(browser, 'dynamicdns') + functional.click_link_by_href(browser, + '/plinth/sys/dynamicdns/domain/add/') for key, value in config.items(): + field_id = f'id_domain-{key}' if key == 'service_type': - browser.find_by_id(f'id_{key}').select(value) + browser.find_by_id(field_id).select(value) elif isinstance(value, bool): if value: - browser.find_by_id(f'id_{key}').check() + browser.find_by_id(field_id).check() else: - browser.find_by_id(f'id_{key}').uncheck() + browser.find_by_id(field_id).uncheck() else: - browser.find_by_id(f'id_{key}').fill(value) + browser.find_by_id(field_id).fill(value) - functional.submit(browser, form_class='form-configuration') + functional.submit(browser, form_class='form-domain') def _assert_has_config(browser, config): functional.nav_to_module(browser, 'dynamicdns') + link = f'/plinth/sys/dynamicdns/domain/{config["domain"]}/edit/' + functional.click_link_by_href(browser, link) for key, value in config.items(): if key == 'password': continue + field_id = f'id_domain-{key}' if isinstance(value, bool): - assert browser.find_by_id(f'id_{key}').checked == value + assert browser.find_by_id(field_id).checked == value else: - assert value == browser.find_by_id(f'id_{key}').value + assert value == browser.find_by_id(field_id).value -def _get_domain(browser): +def _get_domains(browser): + """Return the list of configured domains.""" functional.nav_to_module(browser, 'dynamicdns') - return browser.find_by_id('id_domain').value + elements = browser.find_by_css('.domains-status .domain-name a') + return [element.text.strip() for element in elements] + + +def _delete_domain(browser, domain): + """Delete a given domain.""" + functional.nav_to_module(browser, 'dynamicdns') + link = f'/plinth/sys/dynamicdns/domain/{domain}/delete/' + functional.click_link_by_href(browser, link) + functional.submit(browser, form_class='form-delete') diff --git a/plinth/modules/dynamicdns/urls.py b/plinth/modules/dynamicdns/urls.py index af9f805d6..f92ecfd69 100644 --- a/plinth/modules/dynamicdns/urls.py +++ b/plinth/modules/dynamicdns/urls.py @@ -10,4 +10,10 @@ from . import views urlpatterns = [ re_path(r'^sys/dynamicdns/$', views.DynamicDNSAppView.as_view(), name='index'), + re_path(r'^sys/dynamicdns/domain/add/$', views.DomainView.as_view(), + name='domain-add'), + re_path(r'^sys/dynamicdns/domain/(?P