mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
Release v20.4 to unstable
-----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAl5m41EWHGp2YWxsZXJv eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICOUkEACjg8HvoAWTRjvIrQWzhGFw4h8p WiLmjiSfI8TZ7L+YwyJn5B1vSXnzIjeVxo8lnC9OAQbHWjpCppehNlKP0gb8Ue9U HCIp0yOUg/mVmSUSt/ZAenJbniNNZiNmKmwn7/c0B1yDpEI8u9d9hiXeeziarRw8 U+UlJXew3RqmgAizA1KyVg4Lkpanx0LHUndyHcSJjk7TSWJtUtESpbjriEWoAvz3 fKa2aLXJzrFCIFsNcQD4+RixpPpZKUsVJupdpHlJI/M+Udl+6GBgEe3nJ9GQwNIj UGTFjGE6kHmu0mEWpmKTFdjH5aIxaE2U3wCaGuhf9x4adwdYw1y5IYDi2MGdMcrC w/LI6fYERkHKlmUFIxQLgfzxpeADKk5GfuEMRs3funmKVOs3RLOVmr+4xq3htpHI o28gFEes40tot9aBc6nPnmeqXekfmvA/G18oBXOHBz32P+KezRBklG1MPVn1yz2s 3quxNc3AYA55mPTP2pyVnWeMdPQM+tWci9jR1t7wIOSJBB11+6rppj4F1TAH1hvj dgjFKFT0NVj/RQxJLFI/4McxC7QTXgmH/ab6paa0E2na2yhn36lC150TxnmtKd+m zKSJHnnDo/OFKM01F5RBqpk6zTMFkmx4ud1OzTxGkEdf2+xFfqa1AFIi+2Yj/Wtk VwLz31+fYgE7M8Yoyg== =g0x5 -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEfWrbdQ+RCFWJSEvmd8DHXntlCAgFAl5qGS8WHGp2YWxsZXJv eUBtYWlsYm94Lm9yZwAKCRB3wMdee2UICJ4REACYPuABNTr3Txm9nDmDVFUXLD01 ThZRaJiOY+BK/s360qeiM8pjYT1gh4wunCY6tabNjl1OsHQDYqXmqqp93cOOZzz6 xO2maQWH+yEy9Amvj9QCCcKC3fByCIOvcQz3xGSzrrcNBAQOczQKxZGY7Vm9SkBR 7bba3F7rE5eaIeeIL8H3o0MINBrhPmRLnS26wZa+GtIP3HqZyCUsOwf6mpLMW0du DVuimiWwQ+f/tuFtv2vVEObaA2Kvb5ZJxMruCB9ZRDxxnBvO8Cux0FkZRd1xsw7D vzbcAftA9YQyTwhZFSDCvgRM3VjAg3DF84i/CgGRfy0y7Qs5KdGPMMihc6M3Wnp7 Vq5UTndfXdZOaQnw9bD5rAr77o+G0aEE8rtV1k7urmO/S+VFJMTj5p0TYvxpzKCz VilIEPp8bLcA3c8ycSuzoz5igACNO526uyMIV9S+6oxuF6gzGe+DwdlTvqu1p923 w/ffrxhHMO2Li9WR1ZuQ2/g7CG4Tc+MIDjfmKQ54B56M/yA5WTOHT+OWj9b4bwTB /19jYtLnPfrZsHC9zQpqCoybJ6+KtIYtBX4/D7vHKmghaGAMgJpcS5oVxCp8Rf1r I3k7x3eo5jNEslcE0Hjpi3FQBmwRHNi4aRDnG41chP9uz3SR/0FgmySyv2HK813u rXCSxvUKmZgCTgH4HA== =Dhqd -----END PGP SIGNATURE----- Merge tag 'v20.4' into debian/buster-backports Release v20.4 to unstable Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
commit
55fd37304b
@ -1,4 +1,4 @@
|
||||
image: debian:unstable
|
||||
image: debian:testing
|
||||
|
||||
before_script:
|
||||
- export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -7,12 +7,15 @@ Helper script for configuring Shadowsocks.
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth.modules import shadowsocks
|
||||
|
||||
SHADOWSOCKS_CONFIG = '/etc/shadowsocks-libev/freedombox.json'
|
||||
SHADOWSOCKS_CONFIG_SYMLINK = '/etc/shadowsocks-libev/freedombox.json'
|
||||
SHADOWSOCKS_CONFIG_ACTUAL = \
|
||||
'/var/lib/private/shadowsocks-libev/freedombox/freedombox.json'
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
@ -26,6 +29,11 @@ def parse_arguments():
|
||||
subparsers.add_parser('merge-config',
|
||||
help='Merge JSON config from stdin with existing')
|
||||
|
||||
# Migrations
|
||||
subparsers.add_parser(
|
||||
'migrate-1-2',
|
||||
help='Move shadowsocks config file to a secure location')
|
||||
|
||||
subparsers.required = True
|
||||
return parser.parse_args()
|
||||
|
||||
@ -35,23 +43,25 @@ def subcommand_setup(_):
|
||||
# Only client socks5 proxy is supported for now. Disable the
|
||||
# server component.
|
||||
action_utils.service_disable('shadowsocks-libev')
|
||||
if not os.path.islink(SHADOWSOCKS_CONFIG_SYMLINK):
|
||||
os.symlink(SHADOWSOCKS_CONFIG_ACTUAL, SHADOWSOCKS_CONFIG_SYMLINK)
|
||||
|
||||
|
||||
def subcommand_get_config(arguments):
|
||||
def subcommand_get_config(_):
|
||||
"""Read and print Shadowsocks configuration."""
|
||||
try:
|
||||
print(open(SHADOWSOCKS_CONFIG, 'r').read())
|
||||
print(open(SHADOWSOCKS_CONFIG_SYMLINK, 'r').read())
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def subcommand_merge_config(arguments):
|
||||
def subcommand_merge_config(_):
|
||||
"""Configure Shadowsocks."""
|
||||
config = sys.stdin.read()
|
||||
config = json.loads(config)
|
||||
|
||||
try:
|
||||
current_config = open(SHADOWSOCKS_CONFIG, 'r').read()
|
||||
current_config = open(SHADOWSOCKS_CONFIG_SYMLINK, 'r').read()
|
||||
current_config = json.loads(current_config)
|
||||
except (OSError, json.JSONDecodeError):
|
||||
current_config = {}
|
||||
@ -59,16 +69,20 @@ def subcommand_merge_config(arguments):
|
||||
new_config = current_config
|
||||
new_config.update(config)
|
||||
new_config = json.dumps(new_config, indent=4, sort_keys=True)
|
||||
open(SHADOWSOCKS_CONFIG_SYMLINK, 'w').write(new_config)
|
||||
|
||||
# XXX: Config file with password is world-readable. This is the
|
||||
# same as the default config file, but find a way to avoid this.
|
||||
# See https://salsa.debian.org/freedombox-team/plinth/-/merge_requests/1724
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
open(SHADOWSOCKS_CONFIG, 'w').write(new_config)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
action_utils.service_restart(shadowsocks.managed_services[0])
|
||||
|
||||
|
||||
def subcommand_migrate_1_2(_):
|
||||
"""Move shadowsocks config file to a secure location."""
|
||||
if os.path.isfile(SHADOWSOCKS_CONFIG_SYMLINK): # ignoring symlinks
|
||||
os.makedirs('/var/lib/private/shadowsocks-libev/freedombox/',
|
||||
exist_ok=True)
|
||||
os.replace(SHADOWSOCKS_CONFIG_SYMLINK, SHADOWSOCKS_CONFIG_ACTUAL)
|
||||
os.symlink(SHADOWSOCKS_CONFIG_ACTUAL, SHADOWSOCKS_CONFIG_SYMLINK)
|
||||
|
||||
subprocess.check_call(['systemctl', 'daemon-reload'])
|
||||
action_utils.service_restart(shadowsocks.managed_services[0])
|
||||
|
||||
|
||||
|
||||
@ -333,6 +333,9 @@ def subcommand_usage_info(_):
|
||||
|
||||
def subcommand_validate_directory(arguments):
|
||||
"""Validate a directory"""
|
||||
if os.geteuid() == 0:
|
||||
raise RuntimeError('You must not be root to run this command')
|
||||
|
||||
directory = arguments.path
|
||||
|
||||
def part_exists(path):
|
||||
@ -349,14 +352,15 @@ def subcommand_validate_directory(arguments):
|
||||
if not os.path.exists(directory):
|
||||
# doesn't exist
|
||||
print('ValidationError: 1')
|
||||
return
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
# is not a directory
|
||||
print('ValidationError: 2')
|
||||
if not os.access(directory, os.R_OK):
|
||||
elif not os.access(directory, os.R_OK):
|
||||
# is not readable
|
||||
print('ValidationError: 3')
|
||||
if arguments.check_writable or arguments.check_creatable:
|
||||
elif arguments.check_writable or arguments.check_creatable:
|
||||
if not os.access(directory, os.W_OK):
|
||||
# is not writable
|
||||
print('ValidationError: 4')
|
||||
|
||||
60
debian/changelog
vendored
60
debian/changelog
vendored
@ -1,3 +1,63 @@
|
||||
plinth (20.4) unstable; urgency=medium
|
||||
|
||||
[ Thomas Vincent ]
|
||||
* Translated using Weblate (French)
|
||||
* Translated using Weblate (French)
|
||||
|
||||
[ Sunil Mohan Adapa ]
|
||||
* networks: Fixes for networks wizards
|
||||
* avahi: Use generic app view
|
||||
* privoxy: Use generic app view
|
||||
* infinoted: Move views to a separate views module
|
||||
* help: Rename views modules as 'views'
|
||||
* networks: Rename views modules as 'views'
|
||||
* diagnostics: Rename views modules, move utilities to main module
|
||||
* backups: cosmetic: Rename .inc file to .html
|
||||
* css: Merge responsive.css into main style file
|
||||
* css: cosmetic: Rename plinth.css to main.css
|
||||
* views: Don't send app to template context
|
||||
* app: Fix showing app name in port forwarding information
|
||||
* networks: Rename polkit JS authority rules file
|
||||
* firewalld: Add polkit JS authority rules files
|
||||
* networks: Show router wizard before Internet connection type wizard
|
||||
* networks: Don't show router wizard if not behind a router
|
||||
* networks: If topology wizard is skipped, skip router wizard too
|
||||
* apache: Handle transition to php 7.4
|
||||
|
||||
[ Joseph Nuthalapati ]
|
||||
* Translated using Weblate (Telugu)
|
||||
* shadowsocks: Move user settings to state directory
|
||||
|
||||
[ Veiko Aasa ]
|
||||
* storage: Directory selection form improvements
|
||||
* transmission: Allow one to submit download directory if it is creatable
|
||||
* plinth: Increase sqlite busy timeout from default 5s to 30s
|
||||
* upgrades: Clean apt cache every week
|
||||
* apps: Do not show status block if service is running
|
||||
* i2p: New style app page layout
|
||||
* quassel: Fix unable to disable application without choosing a domain name
|
||||
|
||||
[ Luis A. Arizmendi ]
|
||||
* Translated using Weblate (Spanish)
|
||||
|
||||
[ Nektarios Katakis ]
|
||||
* networks: Add form for network topology
|
||||
* networks: Add page for network topology form
|
||||
* networks: First boot view for network topology wizard
|
||||
* networks: First boot step for network topology wizard
|
||||
* networks: Save networks topology type to DB
|
||||
* networks: Update main networks page Internet connectivity section
|
||||
|
||||
[ Michael Breidenbach ]
|
||||
* Translated using Weblate (Swedish)
|
||||
|
||||
[ James Valleroy ]
|
||||
* ci: Switch to testing image
|
||||
* locale: Update translation strings
|
||||
* doc: Fetch latest manual
|
||||
|
||||
-- James Valleroy <jvalleroy@mailbox.org> Mon, 09 Mar 2020 20:01:44 -0400
|
||||
|
||||
plinth (20.3~bpo10+1) buster-backports; urgency=medium
|
||||
|
||||
* Rebuild for buster-backports.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1153,13 +1153,13 @@ dd if=temp/usr/lib/u-boot/A20-OLinuXino-Lime2/u-boot-sunxi-with-spl.bin of=<lime
|
||||
<section>
|
||||
<title>Screenshot</title>
|
||||
<para>
|
||||
<remark>Add when/if an interface is made for Plinth</remark>
|
||||
<remark>Add when/if an interface is made for <ulink url="https://wiki.debian.org/FreedomBox/Manual/FreedomBox#">FreedomBox</ulink></remark>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Using User websites</title>
|
||||
<para>The module is always enabled and offers no configuration from the Plinth web interface. Currently its existence is not even visible in the Plinth web interface. </para>
|
||||
<para>Using the modules capability to serve documents requires just to place the documents in the designated directory in a Plinth user's home directory in the filesystem. </para>
|
||||
<para>The module is always enabled and offers no configuration from the FreedomBox web interface. There is no configuration or status page shown for this module in the FreedomBox web interface. </para>
|
||||
<para>To serve documents, place the files in the designated directory in a FreedomBox user's home directory in the filesystem. </para>
|
||||
<para>This directory is: <emphasis role="strong">public_html</emphasis> </para>
|
||||
<para>Thus the absolute path for the directory of a user named fbx with home directory in /home/fbx will be <emphasis role="strong">/home/fbx/public_html</emphasis>. User websites will serve documents placed in this directory when requests for documents with the URI path "~fbx" are received. For the the example.org domain thus a request for the document example.org/~fbx/index.html will transfer the file in /home/fbx/public_html/index.html. </para>
|
||||
</section>
|
||||
@ -10094,6 +10094,68 @@ wget https://www.thinkpenguin.com/files/ath9k_firmware_free-version/htc_7010.fw]
|
||||
<section>
|
||||
<title>Release Notes</title>
|
||||
<para>The following are the release notes for each FreedomBox version. </para>
|
||||
<section>
|
||||
<title>FreedomBox 20.4 (2020-03-09)</title>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>apache: Handle transition to php 7.4 </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>app: Fix showing app name in port forwarding information </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>apps: Do not show status block if service is running </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>i2p: New style app page layout </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>locale: Update translations for French, Telugu, Spanish, and Swedish </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Add first boot step for network topology wizard </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Add form for network topology </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Don't show router wizard if not behind a router </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks, firewall: Support newer version of policykit </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Fixes for networks wizards access and user experience </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: If topology wizard is skipped, skip router wizard too </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Show router wizard before Internet connection type wizard </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>plinth: Increase sqlite busy timeout from default 5s to 30s </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>quassel: Fix unable to disable application without choosing a domain name </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>shadowsocks: Move user settings to state directory </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>storage: Directory selection form improvements </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>transmission: Allow to submit download directory if it is creatable </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>upgrades: Clean apt cache every week </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>views: Improve template security </para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>FreedomBox 20.3 (2020-02-24)</title>
|
||||
<itemizedlist>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1028,14 +1028,13 @@ dd if=temp/usr/lib/u-boot/A20-OLinuXino-Lime2/u-boot-sunxi-with-spl.bin of=<lime
|
||||
<section>
|
||||
<title>Captura de pantalla</title>
|
||||
<para>
|
||||
<remark>Añadir cuando/si se crea un interfaz para Plinth</remark>
|
||||
<remark>Añadir cuando/si se crea un interfaz para <ulink url="https://wiki.debian.org/es/FreedomBox/Manual/FreedomBox#">FreedomBox</ulink></remark>
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Usar User websites</title>
|
||||
<para>El módulo está siempre activado y no ofrece configuración desde el interfaz web de Plinth. Actualmente ni siquiera muestra que exista. </para>
|
||||
<para>Para servir documentos con el módulo solo se necesita poner los documentos en un subdirectorio designado <emphasis role="strong"><code>/home/<un_usuario_de_plinth>/public_html</code></emphasis>. </para>
|
||||
<para><code>User websites</code> servirá los documentos que haya en este directorio cuando se reciban peticiones con la URI <code>~<un_usuario_de_plinth></code>. Por tanto para un dominio <code>ejemplo.org</code> con un usuario <code>pepe</code> una petición <code>ejemplo.org/~pepe/index.html</code> transferirá el fichero <code>/home/pepe/public_html/index.html</code>. </para>
|
||||
<para>El módulo está siempre activado y el interfaz web de FreedomBox no ofrece configuración ni página de estado para este módulo. Para servir documentos con el módulo solo se necesita poner los documentos en un subdirectorio designado <emphasis role="strong"><code>/home/<un_usuario_de_plinth>/public_html</code></emphasis>. </para>
|
||||
<para><code>User websites</code> servirá los archivos que haya en este directorio cuando se reciban peticiones con la URI <code>~<un_usuario_de_freedombox></code>. Por tanto para un dominio <code>ejemplo.org</code> con un usuario <code>pepe</code> una petición <code>ejemplo.org/~pepe/index.html</code> transferirá el fichero <code>/home/pepe/public_html/index.html</code>. </para>
|
||||
</section>
|
||||
<section>
|
||||
<title>Usar SFTP para crear public_html y subir archivos</title>
|
||||
@ -9976,6 +9975,68 @@ wget https://www.thinkpenguin.com/files/ath9k_firmware_free-version/htc_7010.fw]
|
||||
<section>
|
||||
<title>Release Notes</title>
|
||||
<para>The following are the release notes for each FreedomBox version. </para>
|
||||
<section>
|
||||
<title>FreedomBox 20.4 (2020-03-09)</title>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>apache: Handle transition to php 7.4 </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>app: Fix showing app name in port forwarding information </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>apps: Do not show status block if service is running </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>i2p: New style app page layout </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>locale: Update translations for French, Telugu, Spanish, and Swedish </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Add first boot step for network topology wizard </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Add form for network topology </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Don't show router wizard if not behind a router </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks, firewall: Support newer version of policykit </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Fixes for networks wizards access and user experience </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: If topology wizard is skipped, skip router wizard too </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>networks: Show router wizard before Internet connection type wizard </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>plinth: Increase sqlite busy timeout from default 5s to 30s </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>quassel: Fix unable to disable application without choosing a domain name </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>shadowsocks: Move user settings to state directory </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>storage: Directory selection form improvements </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>transmission: Allow to submit download directory if it is creatable </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>upgrades: Clean apt cache every week </para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>views: Improve template security </para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
<section>
|
||||
<title>FreedomBox 20.3 (2020-02-24)</title>
|
||||
<itemizedlist>
|
||||
|
||||
@ -23,12 +23,12 @@ def get_service_module(service_name):
|
||||
|
||||
def is_running(browser, service_name):
|
||||
interface.nav_to_module(browser, get_service_module(service_name))
|
||||
return len(browser.find_by_css('.running-status.active')) != 0
|
||||
return len(browser.find_by_id('service-not-running')) == 0
|
||||
|
||||
|
||||
def is_not_running(browser, service_name):
|
||||
interface.nav_to_module(browser, get_service_module(service_name))
|
||||
return len(browser.find_by_css('.running-status.inactive')) != 0
|
||||
return len(browser.find_by_id('service-not-running')) != 0
|
||||
|
||||
|
||||
def eventually(function, args=[], timeout=30):
|
||||
|
||||
@ -3,4 +3,4 @@
|
||||
Package init file.
|
||||
"""
|
||||
|
||||
__version__ = '20.3'
|
||||
__version__ = '20.4'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -14,30 +14,18 @@
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch ".+\.ph(ar|p|tml)$">
|
||||
<IfFile /etc/php/7.3>
|
||||
SetHandler "proxy:unix:/run/php/php7.3-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/7.4>
|
||||
<If "-e '/run/php/php-fpm.sock'">
|
||||
SetHandler "proxy:unix:/run/php/php-fpm.sock|fcgi://localhost"
|
||||
</If>
|
||||
<ElseIf "-e '/run/php/php7.4-fpm.sock'">
|
||||
SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/7.5>
|
||||
SetHandler "proxy:unix:/run/php/php7.5-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/7.6>
|
||||
SetHandler "proxy:unix:/run/php/php7.6-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/8.0>
|
||||
SetHandler "proxy:unix:/run/php/php8.0-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/8.1>
|
||||
SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/8.2>
|
||||
SetHandler "proxy:unix:/run/php/php8.2-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
<IfFile /etc/php/8.3>
|
||||
SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
|
||||
</IfFile>
|
||||
</ElseIf>
|
||||
<ElseIf "-e '/run/php/php7.3-fpm.sock'">
|
||||
SetHandler "proxy:unix:/run/php/php7.3-fpm.sock|fcgi://localhost"
|
||||
</ElseIf>
|
||||
<Else>
|
||||
Require all denied
|
||||
</Else>
|
||||
</FilesMatch>
|
||||
<FilesMatch ".+\.phps$">
|
||||
# Deny access to raw php sources by default
|
||||
|
||||
@ -14,7 +14,6 @@ from plinth.modules.firewall.components import Firewall
|
||||
from plinth.modules.names.components import DomainType
|
||||
from plinth.signals import domain_added, domain_removed, post_hostname_change
|
||||
from plinth.utils import format_lazy
|
||||
from plinth.views import AppView
|
||||
|
||||
from .manifest import backup # noqa, pylint: disable=unused-import
|
||||
|
||||
@ -109,7 +108,3 @@ def on_post_hostname_change(sender, old_hostname, new_hostname, **kwargs):
|
||||
name=old_hostname + '.local')
|
||||
domain_added.send_robust(sender='avahi', domain_type='domain-type-local',
|
||||
name=new_hostname + '.local', services='__all__')
|
||||
|
||||
|
||||
class AvahiAppView(AppView):
|
||||
app_id = 'avahi'
|
||||
|
||||
@ -5,8 +5,8 @@ URLs for the service discovery module.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from plinth.modules.avahi import AvahiAppView
|
||||
from plinth.views import AppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/avahi/$', AvahiAppView.as_view(), name='index'),
|
||||
url(r'^sys/avahi/$', AppView.as_view(app_id='avahi'), name='index'),
|
||||
]
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
<h3>{% trans 'Existing Backups' %}</h3>
|
||||
|
||||
{% for repository in repositories %}
|
||||
{% include "backups_repository.inc" with uuid=repository.uuid %}
|
||||
{% include "backups_repository.html" with uuid=repository.uuid %}
|
||||
{% endfor %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -17,7 +17,6 @@ from .forms import BindForm
|
||||
class BindAppView(AppView): # pylint: disable=too-many-ancestors
|
||||
"""A specialized view for configuring Bind."""
|
||||
app_id = 'bind'
|
||||
show_status_block = True
|
||||
form_class = BindForm
|
||||
template_name = 'bind.html'
|
||||
port_forwarding_info = port_forwarding_info
|
||||
|
||||
@ -8,7 +8,6 @@ from plinth.views import AppView
|
||||
|
||||
class CockpitAppView(AppView):
|
||||
app_id = 'cockpit'
|
||||
show_status_block = True
|
||||
template_name = 'cockpit.html'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
|
||||
@ -22,7 +22,6 @@ class ConfigAppView(views.AppView):
|
||||
"""Serve configuration page."""
|
||||
form_class = ConfigurationForm
|
||||
app_id = 'config'
|
||||
show_status_block = False
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the current status"""
|
||||
|
||||
@ -17,7 +17,6 @@ class CoquelicotAppView(views.AppView):
|
||||
"""Serve configuration page."""
|
||||
app_id = 'coquelicot'
|
||||
form_class = CoquelicotForm
|
||||
show_status_block = True
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the status of the service to fill in the form."""
|
||||
|
||||
@ -18,5 +18,5 @@ class DelugeForm(DirectorySelectForm):
|
||||
check_creatable=True)
|
||||
super(DelugeForm, self).__init__(
|
||||
title=_('Download directory'),
|
||||
default='/var/lib/deluged/Downloads/', validator=validator, *args,
|
||||
default='/var/lib/deluged/Downloads', validator=validator, *args,
|
||||
**kw)
|
||||
|
||||
@ -4,7 +4,6 @@ Django views for Deluge.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import ugettext as _
|
||||
@ -25,8 +24,7 @@ class DelugeAppView(views.AppView):
|
||||
status = super().get_initial()
|
||||
configuration = json.loads(
|
||||
actions.superuser_run('deluge', ['get-configuration']))
|
||||
status['storage_path'] = os.path.normpath(
|
||||
configuration['download_location'])
|
||||
status['storage_path'] = configuration['download_location']
|
||||
return status
|
||||
|
||||
def form_valid(self, form):
|
||||
|
||||
@ -3,6 +3,11 @@
|
||||
FreedomBox app for system diagnostics.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import importlib
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from plinth import app as app_module
|
||||
@ -23,6 +28,12 @@ _description = [
|
||||
|
||||
app = None
|
||||
|
||||
logger = logging.Logger(__name__)
|
||||
|
||||
running_task = None
|
||||
|
||||
current_results = {}
|
||||
|
||||
|
||||
class DiagnosticsApp(app_module.App):
|
||||
"""FreedomBox app for diagnostics."""
|
||||
@ -59,3 +70,59 @@ def init():
|
||||
global app
|
||||
app = DiagnosticsApp()
|
||||
app.set_enabled(True)
|
||||
|
||||
|
||||
def start_task():
|
||||
"""Start the run task in a separate thread."""
|
||||
global running_task
|
||||
if running_task:
|
||||
raise Exception('Task already running')
|
||||
|
||||
running_task = threading.Thread(target=_run_on_all_enabled_modules_wrapper)
|
||||
running_task.start()
|
||||
|
||||
|
||||
def _run_on_all_enabled_modules_wrapper():
|
||||
"""Wrapper over actual task to catch exceptions."""
|
||||
try:
|
||||
run_on_all_enabled_modules()
|
||||
except Exception as exception:
|
||||
logger.exception('Error running diagnostics - %s', exception)
|
||||
current_results['error'] = str(exception)
|
||||
|
||||
global running_task
|
||||
running_task = None
|
||||
|
||||
|
||||
def run_on_all_enabled_modules():
|
||||
"""Run diagnostics on all the enabled modules and store the result."""
|
||||
global current_results
|
||||
current_results = {
|
||||
'apps': [],
|
||||
'results': collections.OrderedDict(),
|
||||
'progress_percentage': 0
|
||||
}
|
||||
|
||||
apps = []
|
||||
for app in app_module.App.list():
|
||||
# XXX: Implement more cleanly.
|
||||
# Don't run diagnostics on apps have not been setup yet.
|
||||
# However, run on apps that need an upgrade.
|
||||
module = importlib.import_module(app.__class__.__module__)
|
||||
if module.setup_helper.get_state() == 'needs-setup':
|
||||
continue
|
||||
|
||||
if not app.is_enabled():
|
||||
continue
|
||||
|
||||
if not app.has_diagnostics():
|
||||
continue
|
||||
|
||||
apps.append((app.app_id, app))
|
||||
current_results['results'][app.app_id] = None
|
||||
|
||||
current_results['apps'] = apps
|
||||
for current_index, (app_id, app) in enumerate(apps):
|
||||
current_results['results'][app_id] = app.diagnose()
|
||||
current_results['progress_percentage'] = \
|
||||
int((current_index + 1) * 100 / len(apps))
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
FreedomBox app for running diagnostics.
|
||||
"""
|
||||
|
||||
import collections
|
||||
import importlib
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from django.http import Http404
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from plinth.app import App
|
||||
from plinth.modules import diagnostics
|
||||
|
||||
logger = logging.Logger(__name__)
|
||||
|
||||
current_results = {}
|
||||
|
||||
_running_task = None
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Serve the index page"""
|
||||
if request.method == 'POST' and not _running_task:
|
||||
_start_task()
|
||||
|
||||
return TemplateResponse(
|
||||
request, 'diagnostics.html', {
|
||||
'app_info': diagnostics.app.info,
|
||||
'is_running': _running_task is not None,
|
||||
'results': current_results
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def diagnose_app(request, app_id):
|
||||
"""Return diagnostics for a particular app."""
|
||||
try:
|
||||
app = App.get(app_id)
|
||||
except KeyError:
|
||||
raise Http404('App does not exist')
|
||||
|
||||
return TemplateResponse(request, 'diagnostics_app.html', {
|
||||
'title': _('Diagnostic Test'),
|
||||
'app_id': app_id,
|
||||
'results': app.diagnose()
|
||||
})
|
||||
|
||||
|
||||
def _start_task():
|
||||
"""Start the run task in a separate thread."""
|
||||
global _running_task
|
||||
if _running_task:
|
||||
raise Exception('Task already running')
|
||||
|
||||
_running_task = threading.Thread(
|
||||
target=_run_on_all_enabled_modules_wrapper)
|
||||
_running_task.start()
|
||||
|
||||
|
||||
def _run_on_all_enabled_modules_wrapper():
|
||||
"""Wrapper over actual task to catch exceptions."""
|
||||
try:
|
||||
run_on_all_enabled_modules()
|
||||
except Exception as exception:
|
||||
logger.exception('Error running diagnostics - %s', exception)
|
||||
current_results['error'] = str(exception)
|
||||
|
||||
global _running_task
|
||||
_running_task = None
|
||||
|
||||
|
||||
def run_on_all_enabled_modules():
|
||||
"""Run diagnostics on all the enabled modules and store the result."""
|
||||
global current_results
|
||||
current_results = {
|
||||
'apps': [],
|
||||
'results': collections.OrderedDict(),
|
||||
'progress_percentage': 0
|
||||
}
|
||||
|
||||
apps = []
|
||||
for app in App.list():
|
||||
# XXX: Implement more cleanly.
|
||||
# Don't run diagnostics on apps have not been setup yet.
|
||||
# However, run on apps that need an upgrade.
|
||||
module = importlib.import_module(app.__class__.__module__)
|
||||
if module.setup_helper.get_state() == 'needs-setup':
|
||||
continue
|
||||
|
||||
if not app.is_enabled():
|
||||
continue
|
||||
|
||||
if not app.has_diagnostics():
|
||||
continue
|
||||
|
||||
apps.append((app.app_id, app))
|
||||
current_results['results'][app.app_id] = None
|
||||
|
||||
current_results['apps'] = apps
|
||||
for current_index, (app_id, app) in enumerate(apps):
|
||||
current_results['results'][app_id] = app.diagnose()
|
||||
current_results['progress_percentage'] = \
|
||||
int((current_index + 1) * 100 / len(apps))
|
||||
@ -5,7 +5,7 @@ URLs for the Diagnostics module
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import diagnostics as views
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/diagnostics/$', views.index, name='index'),
|
||||
|
||||
40
plinth/modules/diagnostics/views.py
Normal file
40
plinth/modules/diagnostics/views.py
Normal file
@ -0,0 +1,40 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
FreedomBox app for running diagnostics.
|
||||
"""
|
||||
|
||||
from django.http import Http404
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.decorators.http import require_POST
|
||||
|
||||
from plinth.app import App
|
||||
from plinth.modules import diagnostics
|
||||
|
||||
|
||||
def index(request):
|
||||
"""Serve the index page"""
|
||||
if request.method == 'POST' and not diagnostics.running_task:
|
||||
diagnostics.start_task()
|
||||
|
||||
return TemplateResponse(
|
||||
request, 'diagnostics.html', {
|
||||
'app_info': diagnostics.app.info,
|
||||
'is_running': diagnostics.running_task is not None,
|
||||
'results': diagnostics.current_results
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def diagnose_app(request, app_id):
|
||||
"""Return diagnostics for a particular app."""
|
||||
try:
|
||||
app = App.get(app_id)
|
||||
except KeyError:
|
||||
raise Http404('App does not exist')
|
||||
|
||||
return TemplateResponse(request, 'diagnostics_app.html', {
|
||||
'title': _('Diagnostic Test'),
|
||||
'app_id': app_id,
|
||||
'results': app.diagnose()
|
||||
})
|
||||
@ -0,0 +1,16 @@
|
||||
/*
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This file is used only by policykit-1 version > 0.105. A corresponding .pkla
|
||||
file is used by policykit-1 <= 0.105. See:
|
||||
https://davidz25.blogspot.com/2012/06/authorization-rules-in-polkit.html
|
||||
|
||||
*/
|
||||
|
||||
polkit.addRule(function(action, subject) {
|
||||
if ((action.id == "org.fedoraproject.FirewallD1.config.info" ||
|
||||
action.id == "org.fedoraproject.FirewallD1.config") &&
|
||||
subject.user == "plinth") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
@ -23,7 +23,6 @@ class GitwebAppView(views.AppView):
|
||||
"""Serve configuration page."""
|
||||
|
||||
app_id = 'gitweb'
|
||||
show_status_block = False
|
||||
template_name = 'gitweb_configure.html'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
|
||||
@ -7,7 +7,7 @@ from django.conf.urls import url
|
||||
|
||||
from plinth.utils import non_admin_view
|
||||
|
||||
from . import help as views
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^help/$', non_admin_view(views.index), name='index'),
|
||||
|
||||
@ -7,16 +7,31 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% block configuration %}
|
||||
{{ block.super }}
|
||||
|
||||
<h3>{% trans "Configuration" %}</h3>
|
||||
<h3>{% trans "I2P Proxies and Tunnels" %}</h3>
|
||||
{% for line in proxies_description %}
|
||||
<p>{{ line|safe }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<form class="form form-configuration" method="post">
|
||||
{% csrf_token %}
|
||||
<p>
|
||||
<a class="btn btn-primary" target="_blank" role="button"
|
||||
href="/i2p/i2ptunnel/"
|
||||
{{ is_enabled|yesno:',disabled="disabled"' }}>
|
||||
{% trans "Launch" %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary"
|
||||
value="{% trans "Update setup" %}"/>
|
||||
</form>
|
||||
<h3>{% trans "Anonymous Torrents" %}</h3>
|
||||
{% for line in torrents_description %}
|
||||
<p>{{ line|safe }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<p>
|
||||
<a class="btn btn-primary" target="_blank" role="button"
|
||||
href="/i2p/i2psnark/"
|
||||
{{ is_enabled|yesno:',disabled="disabled"' }}>
|
||||
{% trans "Launch" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
{% extends "app.html" %}
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block configuration %}
|
||||
{% for line in service_description %}
|
||||
<p>{{ line|safe }}</p>
|
||||
{% endfor %}
|
||||
|
||||
<p>
|
||||
<a class="btn btn-primary" target="_blank" role="button"
|
||||
href="{{ service_path }}">
|
||||
{% trans "Launch" %}
|
||||
</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
@ -4,12 +4,6 @@ URLs for the I2P module.
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from plinth.modules.i2p import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/i2p/$', views.I2PAppView.as_view(), name='index'),
|
||||
url(r'^apps/i2p/tunnels/?$', views.TunnelsView.as_view(), name='tunnels'),
|
||||
url(r'^apps/i2p/torrents/?$', views.TorrentsView.as_view(),
|
||||
name='torrents'),
|
||||
]
|
||||
urlpatterns = [url(r'^apps/i2p/$', views.I2PAppView.as_view(), name='index')]
|
||||
|
||||
@ -3,67 +3,16 @@
|
||||
Views for I2P application.
|
||||
"""
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
import plinth.modules.i2p as i2p
|
||||
from plinth.modules import i2p
|
||||
from plinth.views import AppView
|
||||
|
||||
subsubmenu = [{
|
||||
'url': reverse_lazy('i2p:index'),
|
||||
'text': ugettext_lazy('Configure')
|
||||
}, {
|
||||
'url': reverse_lazy('i2p:tunnels'),
|
||||
'text': ugettext_lazy('Proxies')
|
||||
}, {
|
||||
'url': reverse_lazy('i2p:torrents'),
|
||||
'text': ugettext_lazy('Anonymous torrents')
|
||||
}]
|
||||
|
||||
|
||||
class I2PAppView(AppView):
|
||||
"""Serve configuration page."""
|
||||
app_id = 'i2p'
|
||||
show_status_block = True
|
||||
template_name = 'i2p.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return the context data for rendering the template view."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = i2p.app.info.name
|
||||
context['app_info'] = i2p.app.info
|
||||
context['subsubmenu'] = subsubmenu
|
||||
context['port_forwarding_info'] = i2p.port_forwarding_info
|
||||
return context
|
||||
|
||||
|
||||
class ServiceBaseView(TemplateView):
|
||||
"""View to describe and launch a service."""
|
||||
service_description = None
|
||||
service_title = None
|
||||
service_path = None
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Add context data for template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['title'] = i2p.app.info.name
|
||||
context['app_info'] = i2p.app.info
|
||||
context['subsubmenu'] = subsubmenu
|
||||
context['is_enabled'] = i2p.app.is_enabled()
|
||||
context['service_title'] = self.service_title
|
||||
context['service_path'] = self.service_path
|
||||
context['service_description'] = self.service_description
|
||||
return context
|
||||
|
||||
|
||||
class TunnelsView(ServiceBaseView):
|
||||
"""View to describe and launch tunnel configuration."""
|
||||
template_name = 'i2p_service.html'
|
||||
service_title = _('I2P Proxies and Tunnels')
|
||||
service_path = '/i2p/i2ptunnel/'
|
||||
service_description = [
|
||||
proxies_description = [
|
||||
_('I2P lets you browse the Internet and hidden services (eepsites) '
|
||||
'anonymously. For this, your browser, preferably a Tor Browser, '
|
||||
'needs to be configured for a proxy.'),
|
||||
@ -71,15 +20,17 @@ class TunnelsView(ServiceBaseView):
|
||||
'proxies and tunnels may be configured using the tunnel '
|
||||
'configuration interface.'),
|
||||
]
|
||||
|
||||
|
||||
class TorrentsView(ServiceBaseView):
|
||||
"""View to describe and launch I2P torrents application."""
|
||||
template_name = 'i2p_service.html'
|
||||
service_title = _('Anonymous Torrents')
|
||||
service_path = '/i2p/i2psnark/'
|
||||
service_description = [
|
||||
torrents_description = [
|
||||
_('I2P provides an application to download files anonymously in a '
|
||||
'peer-to-peer network. Download files by adding torrents or '
|
||||
'create a new torrent to share a file.'),
|
||||
]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return the context data for rendering the template view."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['port_forwarding_info'] = i2p.port_forwarding_info
|
||||
context['proxies_description'] = self.proxies_description
|
||||
context['torrents_description'] = self.torrents_description
|
||||
|
||||
return context
|
||||
|
||||
@ -18,7 +18,6 @@ from .forms import IkiwikiCreateForm
|
||||
class IkiwikiAppView(views.AppView):
|
||||
"""Serve configuration page."""
|
||||
app_id = 'ikiwiki'
|
||||
show_status_block = False
|
||||
template_name = 'ikiwiki_configure.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@ -12,7 +12,6 @@ from plinth import cfg, frontpage, menu
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.utils import format_lazy
|
||||
from plinth.views import AppView
|
||||
|
||||
from .manifest import backup, clients # noqa, pylint: disable=unused-import
|
||||
|
||||
@ -83,11 +82,6 @@ def init():
|
||||
app.set_enabled(True)
|
||||
|
||||
|
||||
class InfinotedAppView(AppView):
|
||||
app_id = 'infinoted'
|
||||
port_forwarding_info = port_forwarding_info
|
||||
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
helper.install(managed_packages)
|
||||
|
||||
@ -5,7 +5,7 @@ URLs for the infinoted module.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from plinth.modules.infinoted import InfinotedAppView
|
||||
from .views import InfinotedAppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/infinoted/$', InfinotedAppView.as_view(), name='index'),
|
||||
|
||||
12
plinth/modules/infinoted/views.py
Normal file
12
plinth/modules/infinoted/views.py
Normal file
@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Views for the infinoted app.
|
||||
"""
|
||||
from plinth.modules import infinoted
|
||||
from plinth.views import AppView
|
||||
|
||||
|
||||
class InfinotedAppView(AppView):
|
||||
"""Main app view for Infinoted."""
|
||||
app_id = 'infinoted'
|
||||
port_forwarding_info = infinoted.port_forwarding_info
|
||||
@ -13,7 +13,6 @@ class JSXCAppView(AppView):
|
||||
"""Show ejabberd as an app."""
|
||||
app_id = 'jsxc'
|
||||
template_name = 'jsxc.html'
|
||||
show_status_block = False
|
||||
|
||||
|
||||
class JsxcView(TemplateView):
|
||||
|
||||
@ -22,7 +22,6 @@ class MediaWikiAppView(views.AppView):
|
||||
"""App configuration page."""
|
||||
app_id = 'mediawiki'
|
||||
form_class = MediaWikiForm
|
||||
show_status_block = False
|
||||
template_name = 'mediawiki.html'
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
@ -17,7 +17,6 @@ from .forms import MinetestForm
|
||||
class MinetestAppView(AppView): # pylint: disable=too-many-ancestors
|
||||
"""A specialized view for configuring minetest."""
|
||||
app_id = 'minetest'
|
||||
show_status_block = True
|
||||
template_name = 'minetest.html'
|
||||
form_class = MinetestForm
|
||||
port_forwarding_info = minetest.port_forwarding_info
|
||||
|
||||
@ -8,9 +8,5 @@ from django.conf.urls import url
|
||||
from plinth.views import AppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/mldonkey/$',
|
||||
AppView.as_view(
|
||||
app_id='mldonkey',
|
||||
show_status_block=True,
|
||||
), name='index'),
|
||||
url(r'^apps/mldonkey/$', AppView.as_view(app_id='mldonkey'), name='index')
|
||||
]
|
||||
|
||||
@ -20,13 +20,18 @@ managed_packages = ['network-manager', 'batctl']
|
||||
|
||||
first_boot_steps = [
|
||||
{
|
||||
'id': 'internet_connectivity_type_wizard',
|
||||
'url': 'networks:firstboot_internet_connection_type',
|
||||
'order': 3,
|
||||
'id': 'network_topology_wizard',
|
||||
'url': 'networks:network-topology-first-boot',
|
||||
'order': 2,
|
||||
},
|
||||
{
|
||||
'id': 'router_setup_wizard',
|
||||
'url': 'networks:firstboot_router_setup',
|
||||
'url': 'networks:router-configuration-first-boot',
|
||||
'order': 3,
|
||||
},
|
||||
{
|
||||
'id': 'internet_connectivity_type_wizard',
|
||||
'url': 'networks:internet-connection-type-first-boot',
|
||||
'order': 4,
|
||||
},
|
||||
]
|
||||
@ -42,6 +47,7 @@ logger = Logger(__name__)
|
||||
|
||||
app = None
|
||||
|
||||
NETWORK_TOPOLOGY_TYPE_KEY = 'networks_topology_type'
|
||||
ROUTER_CONFIGURATION_TYPE_KEY = 'networks_router_configuration_type'
|
||||
INTERNET_CONNECTION_TYPE_KEY = 'networks_internet_type'
|
||||
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
/*
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This file is used only by policykit-1 version > 0.105. A corresponding .pkla
|
||||
file is used by policykit-1 <= 0.105. See:
|
||||
https://davidz25.blogspot.com/2012/06/authorization-rules-in-polkit.html
|
||||
|
||||
*/
|
||||
|
||||
polkit.addRule(function(action, subject) {
|
||||
@ -285,6 +285,48 @@ requires clients to have the password to connect.'),
|
||||
return settings
|
||||
|
||||
|
||||
class NetworkTopologyForm(forms.Form):
|
||||
"""Form to ask the user for network topology.
|
||||
|
||||
That is how the FreedomBox is connected to the internal network topology.
|
||||
Store this information for future suggestions when setting up services.
|
||||
|
||||
"""
|
||||
network_topology = forms.ChoiceField(
|
||||
label=format_lazy(
|
||||
_('Choose how your {box_name} is connected to your network'),
|
||||
box_name=cfg.box_name),
|
||||
required=True,
|
||||
widget=forms.RadioSelect,
|
||||
choices=[
|
||||
('to_router',
|
||||
format_lazy(
|
||||
_('Connected to a router '
|
||||
'<p class="help-block">Your {box_name} gets its Internet '
|
||||
'connection from your router via Wi-Fi or Ethernet cable. '
|
||||
'This is a typical home setup.</p>'), box_name=cfg.box_name,
|
||||
allow_markup=True)),
|
||||
('as_router',
|
||||
format_lazy(
|
||||
_('{box_name} is your router '
|
||||
'<p class="help-block">Your {box_name} has multiple '
|
||||
'network interfaces such as multiple Ethernet ports or '
|
||||
'a Wi-Fi adapter. {box_name} is directly connected to the '
|
||||
'Internet and all your devices connect to {box_name} '
|
||||
'for their Internet connectivity.</p>'),
|
||||
box_name=cfg.box_name, allow_markup=True)),
|
||||
('direct',
|
||||
format_lazy(
|
||||
_('Directly connected to the Internet '
|
||||
'<p class="help-block">Your Internet connection is '
|
||||
'directly attached to your {box_name} and there are no '
|
||||
'other devices on the network. This can happen on '
|
||||
'community or cloud setups.</p>'), box_name=cfg.box_name,
|
||||
allow_markup=True)),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
class InternetConnectionTypeForm(forms.Form):
|
||||
"""Form for type of public/private IP address ISP provides.
|
||||
|
||||
@ -334,13 +376,18 @@ class InternetConnectionTypeForm(forms.Form):
|
||||
'{box_name} provides many workaround solutions but each '
|
||||
'solution has some limitations.</p>'),
|
||||
box_name=cfg.box_name, allow_markup=True)),
|
||||
('unknown',
|
||||
format_lazy(
|
||||
_('I do not know the type of connection my ISP provides '
|
||||
'<p class="help-block">You will be suggested the most '
|
||||
'conservative actions.</p>'), allow_markup=True)),
|
||||
],
|
||||
required=True,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
|
||||
|
||||
class RouterConfigurationWizardForm(forms.Form):
|
||||
class RouterConfigurationForm(forms.Form):
|
||||
"""Form to suggest how to configure a router.
|
||||
|
||||
Suggest depending on wan connectivity/specific setup. The choice will
|
||||
|
||||
@ -15,7 +15,9 @@
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<a href='{{ first_boot_next_step }}'>{% trans "Skip this step" %}</a>
|
||||
<input type="submit" class="btn btn-primary pull-right" value="{% trans "Next" %}"/>
|
||||
<input type="submit" class="btn btn-link" name="skip"
|
||||
value="{% trans "Skip this step" %}"/>
|
||||
<input type="submit" class="btn btn-primary pull-right" name="next"
|
||||
value="{% trans "Next" %}"/>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@ -27,12 +27,16 @@
|
||||
{% blocktrans trimmed %}
|
||||
My ISP provides a public IP address that may change over time.
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% elif internet_connectivity_type == "private_ip" %}
|
||||
{% blocktrans trimmed %}
|
||||
My ISP does not provide a public IP address.
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
I do not know the type of connection my ISP provides.
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
<a href="{% url 'networks:internet_connection_type_setup' %}" class="btn btn-default"
|
||||
<a href="{% url 'networks:internet-connection-type' %}" class="btn btn-default"
|
||||
role="button">
|
||||
{% trans 'Update...' %}
|
||||
</a>
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
<h1>
|
||||
{% blocktrans trimmed %}
|
||||
How is Your {{ box_name }} Connected to the Internet?
|
||||
{% endblocktrans %}
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Select an option that best describes how your {{ box_name }} is connected in
|
||||
your network. This information is used to guide you with further setup. It can
|
||||
be changed later.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
@ -0,0 +1,24 @@
|
||||
{% extends "base_firstboot.html" %}
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
{% include "network_topology_content.html" %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-link" name="skip"
|
||||
value="{% trans "Skip this step" %}"/>
|
||||
<input type="submit" class="btn btn-primary pull-right" name="next"
|
||||
value="{% trans "Next" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
43
plinth/modules/networks/templates/network_topology_main.html
Normal file
43
plinth/modules/networks/templates/network_topology_main.html
Normal file
@ -0,0 +1,43 @@
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
<h3>
|
||||
{% blocktrans trimmed %}
|
||||
{{ box_name }} Internet Connectivity
|
||||
{% endblocktrans %}
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The following best describes how your {{ box_name }} is connected in your
|
||||
network. This information is used only to suggest necessary configuration
|
||||
actions.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% if network_topology == "to_router" %}
|
||||
{% blocktrans trimmed %}
|
||||
Your {{ box_name }} gets its Internet connection from your router via Wi-Fi
|
||||
or Ethernet cable. This is a typical home setup.
|
||||
{% endblocktrans %}
|
||||
{% elif network_topology == "as_router" %}
|
||||
{% blocktrans trimmed %}
|
||||
Your {{ box_name }} is directly connected to the Internet and all your
|
||||
devices connect to {{ box_name }} for their Internet connectivity.
|
||||
{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans trimmed %}
|
||||
Your Internet connection is directly attached to your {{ box_name }} and there
|
||||
are no other devices on the network.
|
||||
{% endblocktrans %}
|
||||
{% endif %}
|
||||
<a href="{% url 'networks:network-topology' %}" class="btn btn-default"
|
||||
role="button">
|
||||
{% trans 'Update...' %}
|
||||
</a>
|
||||
</p>
|
||||
@ -0,0 +1,21 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
{% include "network_topology_content.html" %}
|
||||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary" value="{% trans "Submit" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@ -42,7 +42,7 @@
|
||||
{% block configuration %}
|
||||
{% include "connections_list.html" %}
|
||||
|
||||
{% include "router_configuration_main.html" %}
|
||||
{% include "network_topology_main.html" %}
|
||||
|
||||
{% include "internet_connectivity_main.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@ -15,8 +15,10 @@
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<a href='{{ first_boot_next_step }}'>{% trans "Skip this step" %}</a>
|
||||
<input type="submit" class="btn btn-primary pull-right" value="{% trans "Next" %}"/>
|
||||
<input type="submit" class="btn btn-link" name="skip"
|
||||
value="{% trans "Skip this step" %}"/>
|
||||
<input type="submit" class="btn btn-primary pull-right" name="next"
|
||||
value="{% trans "Next" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
<h3>
|
||||
{% blocktrans trimmed %}
|
||||
{{ box_name }} Internet Connectivity
|
||||
{% endblocktrans %}
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
The following best describes how your {{ box_name }} is connected in your
|
||||
network. This information is used only to suggest necessary configuration
|
||||
actions.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Your {{ box_name }} gets its Internet from your Router via Wi-Fi or
|
||||
Ethernet cable. This is a typical home setup.
|
||||
{% endblocktrans %}
|
||||
<a href="{% url 'networks:router_setup' %}" class="btn btn-default"
|
||||
role="button">
|
||||
{% trans 'Update...' %}
|
||||
</a>
|
||||
</p>
|
||||
@ -4,9 +4,8 @@ URLs for the Network module
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
from stronghold.decorators import public
|
||||
|
||||
from . import networks as views
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^sys/networks/$', views.index, name='index'),
|
||||
@ -27,16 +26,20 @@ urlpatterns = [
|
||||
r'(?P<interface_name>[^/]+)/)?$', views.add_wifi, name='add_wifi'),
|
||||
url(r'^sys/networks/(?P<uuid>[\w.@+-]+)/delete/$', views.delete,
|
||||
name='delete'),
|
||||
url(r'^sys/networks/router-setup-guide/$',
|
||||
views.router_configuration_help_page,
|
||||
name='router_setup'),
|
||||
url(r'^sys/networks/firstboot/router_setup/$',
|
||||
public(views.router_configuration_help_page),
|
||||
name='firstboot_router_setup'),
|
||||
url(r'^sys/networks/router-configuration/$',
|
||||
views.RouterConfigurationView.as_view(), name='router-configuration'),
|
||||
url(r'^sys/networks/firstboot/router-configuration/$',
|
||||
views.RouterConfigurationFirstBootView.as_view(),
|
||||
name='router-configuration-first-boot'),
|
||||
url(r'^sys/networks/internet-connection-type/$',
|
||||
views.internet_connection_type_help_page,
|
||||
name='internet_connection_type_setup'),
|
||||
url(r'^sys/networks/firstboot/internet_connection_type/$',
|
||||
public(views.internet_connection_type_help_page),
|
||||
name='firstboot_internet_connection_type'),
|
||||
views.InternetConnectionTypeView.as_view(),
|
||||
name='internet-connection-type'),
|
||||
url(r'^sys/networks/firstboot/internet-connection-type/$',
|
||||
views.InternetConnectionTypeFirstBootView.as_view(),
|
||||
name='internet-connection-type-first-boot'),
|
||||
url(r'^sys/networks/network-topology/$',
|
||||
views.NetworkTopologyView.as_view(), name='network-topology'),
|
||||
url(r'^sys/networks/firstboot/network-topology-first-boot/$',
|
||||
views.NetworkTopologyFirstBootView.as_view(),
|
||||
name='network-topology-first-boot'),
|
||||
]
|
||||
|
||||
@ -3,18 +3,20 @@
|
||||
import logging
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.http import require_POST
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
from plinth import kvstore, network
|
||||
from plinth.modules import first_boot, networks
|
||||
|
||||
from .forms import (ConnectionTypeSelectForm, EthernetForm, GenericForm,
|
||||
InternetConnectionTypeForm, PPPoEForm,
|
||||
RouterConfigurationWizardForm, WifiForm)
|
||||
InternetConnectionTypeForm, NetworkTopologyForm, PPPoEForm,
|
||||
RouterConfigurationForm, WifiForm)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -23,8 +25,10 @@ def index(request):
|
||||
"""Show connection list."""
|
||||
connections = network.get_connection_list()
|
||||
|
||||
network_topology = kvstore.get_default(networks.NETWORK_TOPOLOGY_TYPE_KEY,
|
||||
'to_router')
|
||||
internet_connection_type = kvstore.get_default(
|
||||
networks.INTERNET_CONNECTION_TYPE_KEY, None)
|
||||
networks.INTERNET_CONNECTION_TYPE_KEY, 'unknown')
|
||||
return TemplateResponse(
|
||||
request, 'networks_configuration.html', {
|
||||
'app_id': 'networks',
|
||||
@ -33,7 +37,8 @@ def index(request):
|
||||
'has_diagnostics': True,
|
||||
'is_enabled': True,
|
||||
'connections': connections,
|
||||
'internet_connectivity_type': internet_connection_type
|
||||
'network_topology': network_topology,
|
||||
'internet_connectivity_type': internet_connection_type,
|
||||
})
|
||||
|
||||
|
||||
@ -405,89 +410,133 @@ def delete(request, uuid):
|
||||
})
|
||||
|
||||
|
||||
def router_configuration_help_page(request):
|
||||
"""Show the router configuration wizard page/form.
|
||||
class NetworkTopologyView(FormView):
|
||||
"""View for local network topology form."""
|
||||
template_name = 'network_topology_update.html'
|
||||
form_class = NetworkTopologyForm
|
||||
success_url = reverse_lazy('networks:index')
|
||||
|
||||
Used both for fistboot step and same networks page.
|
||||
def get_initial(self):
|
||||
"""Get initial form data."""
|
||||
return {
|
||||
'network_topology':
|
||||
kvstore.get_default(networks.NETWORK_TOPOLOGY_TYPE_KEY,
|
||||
'to_router')
|
||||
}
|
||||
|
||||
"""
|
||||
is_firstboot = True \
|
||||
if 'firstboot' in request.build_absolute_uri() else False
|
||||
def form_valid(self, form):
|
||||
"""Save value to DB."""
|
||||
network_topology = form.cleaned_data['network_topology']
|
||||
logger.info('Updating network topology type with value %s' %
|
||||
network_topology)
|
||||
kvstore.set(networks.NETWORK_TOPOLOGY_TYPE_KEY, network_topology)
|
||||
if network_topology == 'to_router':
|
||||
self.success_url = reverse_lazy('networks:router-configuration')
|
||||
|
||||
if request.method == 'POST' and request.POST['router_config']:
|
||||
form = RouterConfigurationWizardForm(request.POST)
|
||||
if form.is_valid():
|
||||
logger.info('Updating router configuration setup with value: %s' %
|
||||
request.POST['router_config'])
|
||||
kvstore.set(networks.ROUTER_CONFIGURATION_TYPE_KEY,
|
||||
request.POST['router_config'])
|
||||
if is_firstboot:
|
||||
resp = reverse_lazy(first_boot.next_step())
|
||||
else:
|
||||
resp = reverse_lazy('networks:index')
|
||||
messages.success(request, _('Router configuration type saved.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
return redirect(resp)
|
||||
else:
|
||||
html = 'router_configuration_update.html'
|
||||
initial = {
|
||||
|
||||
class NetworkTopologyFirstBootView(NetworkTopologyView):
|
||||
"""View for network topology form during first wizard."""
|
||||
template_name = 'network_topology_firstboot.html'
|
||||
|
||||
def get_success_url(self):
|
||||
"""Return next fistboot step."""
|
||||
return reverse_lazy(first_boot.next_step())
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Mark the first wizard step as done, save value and redirect."""
|
||||
first_boot.mark_step_done('network_topology_wizard')
|
||||
if 'skip' in form.data:
|
||||
first_boot.mark_step_done('router_setup_wizard')
|
||||
return FormView.form_valid(self, form)
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class RouterConfigurationView(FormView):
|
||||
"""View for router configuration form."""
|
||||
template_name = 'router_configuration_update.html'
|
||||
form_class = RouterConfigurationForm
|
||||
success_url = reverse_lazy('networks:index')
|
||||
|
||||
def get_initial(self):
|
||||
"""Return initial data for the form."""
|
||||
return {
|
||||
'router_config':
|
||||
kvstore.get_default(networks.ROUTER_CONFIGURATION_TYPE_KEY,
|
||||
'not_configured'),
|
||||
'not_configured')
|
||||
}
|
||||
template_kwargs = {
|
||||
'form': RouterConfigurationWizardForm(initial=initial),
|
||||
}
|
||||
if is_firstboot:
|
||||
html = 'router_configuration_firstboot.html'
|
||||
|
||||
# mark step done on firstboot visit to get the next_step
|
||||
def form_valid(self, form):
|
||||
"""Save value to DB and redirect."""
|
||||
type_ = form.cleaned_data['router_config']
|
||||
logger.info('Updating router configuration: %s', type_)
|
||||
kvstore.set(networks.ROUTER_CONFIGURATION_TYPE_KEY, type_)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class RouterConfigurationFirstBootView(RouterConfigurationView):
|
||||
"""View for router configuration form during first wizard."""
|
||||
template_name = 'router_configuration_firstboot.html'
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
"""Don't show wizard step if FreedomBox is not behind a router."""
|
||||
network_topology = kvstore.get_default(
|
||||
networks.NETWORK_TOPOLOGY_TYPE_KEY, 'to_router')
|
||||
if network_topology != 'to_router':
|
||||
first_boot.mark_step_done('router_setup_wizard')
|
||||
template_kwargs.update({
|
||||
'first_boot_next_step': reverse_lazy(first_boot.next_step()),
|
||||
})
|
||||
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
|
||||
|
||||
return TemplateResponse(request, html, template_kwargs)
|
||||
return super().dispatch(request, *args, *kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
"""Return the next wizard step after this one."""
|
||||
return reverse_lazy(first_boot.next_step())
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Mark the first wizard step as done, save value and redirect."""
|
||||
first_boot.mark_step_done('router_setup_wizard')
|
||||
if 'skip' in form.data:
|
||||
return FormView.form_valid(self, form)
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def internet_connection_type_help_page(request):
|
||||
"""Show the internet connection type page.
|
||||
class InternetConnectionTypeView(FormView):
|
||||
"""View for Internet connection type form."""
|
||||
template_name = 'internet_connectivity_type.html'
|
||||
form_class = InternetConnectionTypeForm
|
||||
success_url = reverse_lazy('networks:index')
|
||||
|
||||
Used for first boot step and networks page.
|
||||
|
||||
"""
|
||||
is_firstboot = True \
|
||||
if 'firstboot' in request.build_absolute_uri() else False
|
||||
|
||||
if request.method == 'POST' and request.POST['internet_connection_type']:
|
||||
form = InternetConnectionTypeForm(request.POST)
|
||||
if form.is_valid():
|
||||
logger.info('Updating internet connectivity type with value: %s' %
|
||||
request.POST['internet_connection_type'])
|
||||
kvstore.set(
|
||||
networks.INTERNET_CONNECTION_TYPE_KEY,
|
||||
request.POST['internet_connection_type'],
|
||||
)
|
||||
if is_firstboot:
|
||||
return redirect(reverse_lazy(first_boot.next_step()))
|
||||
else:
|
||||
messages.success(request, _('Internet connection type saved.'))
|
||||
return redirect(reverse_lazy('networks:index'))
|
||||
else:
|
||||
html = 'internet_connectivity_type.html'
|
||||
initial = {
|
||||
def get_initial(self):
|
||||
"""Return initial data for the form."""
|
||||
return {
|
||||
'internet_connection_type':
|
||||
kvstore.get_default(networks.INTERNET_CONNECTION_TYPE_KEY,
|
||||
None)
|
||||
'unknown')
|
||||
}
|
||||
template_kwargs = {'form': InternetConnectionTypeForm(initial=initial)}
|
||||
if is_firstboot:
|
||||
html = 'internet_connectivity_firstboot.html'
|
||||
|
||||
# mark step done on firstboot visit to get the next_step
|
||||
first_boot.mark_step_done('internet_connectivity_type_wizard')
|
||||
template_kwargs.update({
|
||||
'first_boot_next_step': reverse_lazy(first_boot.next_step()),
|
||||
})
|
||||
def form_valid(self, form):
|
||||
"""Save value to DB and redirect."""
|
||||
type_ = form.cleaned_data['internet_connection_type']
|
||||
logger.info('Updating internet connectivity type: %s', type_)
|
||||
kvstore.set(networks.INTERNET_CONNECTION_TYPE_KEY, type_)
|
||||
return super().form_valid(form)
|
||||
|
||||
return TemplateResponse(request, html, template_kwargs)
|
||||
|
||||
class InternetConnectionTypeFirstBootView(InternetConnectionTypeView):
|
||||
"""View to show Internet connection type form during first wizard."""
|
||||
template_name = 'internet_connectivity_firstboot.html'
|
||||
|
||||
def get_success_url(self):
|
||||
"""Return the next wizard step after this one."""
|
||||
return reverse_lazy(first_boot.next_step())
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Mark the first wizard step as done, save value and redirect."""
|
||||
first_boot.mark_step_done('internet_connectivity_type_wizard')
|
||||
if 'skip' in form.data:
|
||||
return FormView.form_valid(self, form)
|
||||
|
||||
return super().form_valid(form)
|
||||
@ -48,7 +48,6 @@ def index(request):
|
||||
'port_forwarding_info': openvpn.port_forwarding_info,
|
||||
'status': status,
|
||||
'form': form,
|
||||
'show_status_block': True,
|
||||
'is_running': status['is_running'],
|
||||
'has_diagnostics': True,
|
||||
'is_enabled': status['enabled'],
|
||||
|
||||
@ -13,7 +13,6 @@ from plinth.daemon import Daemon
|
||||
from plinth.modules.apache.components import diagnose_url
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.utils import format_lazy
|
||||
from plinth.views import AppView
|
||||
|
||||
from .manifest import backup # noqa, pylint: disable=unused-import
|
||||
|
||||
@ -104,10 +103,6 @@ def setup(helper, old_version=None):
|
||||
helper.call('post', app.enable)
|
||||
|
||||
|
||||
class PrivoxyAppView(AppView):
|
||||
app_id = 'privoxy'
|
||||
|
||||
|
||||
def diagnose_url_with_proxy():
|
||||
"""Run a diagnostic on a URL with a proxy."""
|
||||
url = 'https://debian.org/' # Gives a simple redirect to www.
|
||||
|
||||
@ -5,8 +5,8 @@ URLs for the Privoxy module.
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from plinth.modules.privoxy import PrivoxyAppView
|
||||
from plinth.views import AppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/privoxy/$', PrivoxyAppView.as_view(), name='index'),
|
||||
url(r'^apps/privoxy/$', AppView.as_view(app_id='privoxy'), name='index'),
|
||||
]
|
||||
|
||||
@ -24,4 +24,5 @@ class QuasselForm(AppForm):
|
||||
help_text=_(
|
||||
'Select a domain to use TLS with. If the list is empty, please '
|
||||
'configure at least one domain with certificates.'),
|
||||
required=False,
|
||||
)
|
||||
|
||||
@ -20,7 +20,9 @@ class QuasselAppView(AppView):
|
||||
def form_valid(self, form):
|
||||
"""Change the access control of Radicale service."""
|
||||
data = form.cleaned_data
|
||||
if quassel.get_domain() != data['domain']:
|
||||
app_disable = form.initial['is_enabled'] and not data['is_enabled']
|
||||
|
||||
if not app_disable and quassel.get_domain() != data['domain']:
|
||||
quassel.set_domain(data['domain'])
|
||||
quassel.app.get_component(
|
||||
'letsencrypt-quassel').setup_certificates()
|
||||
|
||||
@ -8,7 +8,6 @@ from django.conf.urls import url
|
||||
from plinth.views import AppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/roundcube/$',
|
||||
AppView.as_view(app_id='roundcube', show_status_block=False),
|
||||
name='index'),
|
||||
url(r'^apps/roundcube/$', AppView.as_view(app_id='roundcube'),
|
||||
name='index')
|
||||
]
|
||||
|
||||
@ -17,7 +17,6 @@ class SearxAppView(views.AppView):
|
||||
"""Serve configuration page."""
|
||||
app_id = 'searx'
|
||||
form_class = SearxForm
|
||||
show_status_block = False
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the status of the service to fill in the form."""
|
||||
|
||||
@ -8,7 +8,5 @@ from django.conf.urls import url
|
||||
from plinth.views import AppView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^apps/shaarli/$',
|
||||
AppView.as_view(app_id='shaarli', show_status_block=False),
|
||||
name='index'),
|
||||
url(r'^apps/shaarli/$', AppView.as_view(app_id='shaarli'), name='index')
|
||||
]
|
||||
|
||||
@ -15,7 +15,7 @@ from plinth.utils import format_lazy
|
||||
|
||||
from .manifest import backup # noqa, pylint: disable=unused-import
|
||||
|
||||
version = 1
|
||||
version = 2
|
||||
|
||||
managed_services = ['shadowsocks-libev-local@freedombox']
|
||||
|
||||
@ -89,6 +89,11 @@ def init():
|
||||
|
||||
def setup(helper, old_version=None):
|
||||
"""Install and configure the module."""
|
||||
|
||||
if old_version == 1:
|
||||
helper.call('migration', actions.superuser_run, 'shadowsocks',
|
||||
['migrate-1-2'])
|
||||
|
||||
helper.install(managed_packages)
|
||||
helper.call('post', actions.superuser_run, 'shadowsocks', ['setup'])
|
||||
helper.call('post', app.enable)
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
[Service]
|
||||
StateDirectory=shadowsocks-libev/%i
|
||||
@ -7,7 +7,9 @@ from plinth.modules.backups.api import validate as validate_backup
|
||||
|
||||
backup = validate_backup({
|
||||
'secrets': {
|
||||
'files': ['/etc/shadowsocks-libev/freedombox.json']
|
||||
'files': [
|
||||
'/var/lib/private/shadowsocks-libev/freedombox/freedombox.json'
|
||||
]
|
||||
},
|
||||
'services': ['shadowsocks-libev-local@freedombox']
|
||||
})
|
||||
|
||||
@ -103,7 +103,7 @@ class DirectorySelectForm(AppForm):
|
||||
if title:
|
||||
self.fields['storage_dir'].label = title
|
||||
self.validator = validator
|
||||
self.default = os.path.normpath(default)
|
||||
self.default = default
|
||||
self.set_form_data()
|
||||
|
||||
def clean(self):
|
||||
|
||||
@ -250,45 +250,25 @@ class TestActions:
|
||||
assert output == error
|
||||
|
||||
@pytest.mark.usefixtures('needs_not_root')
|
||||
@pytest.mark.parametrize('directory', [{
|
||||
'path': '/missing',
|
||||
'error': '1'
|
||||
}, {
|
||||
'path': '/etc/os-release',
|
||||
'error': '2'
|
||||
}, {
|
||||
'path': '/root',
|
||||
'error': '3'
|
||||
}, {
|
||||
'path': '/',
|
||||
'error': ''
|
||||
}])
|
||||
def test_validate_directory(self, directory):
|
||||
@pytest.mark.parametrize('path,error', [('/missing', '1'),
|
||||
('/etc/os-release', '2'),
|
||||
('/root', '3'), ('/', ''),
|
||||
('/etc/..', '')])
|
||||
def test_validate_directory(self, path, error):
|
||||
"""Test that directory validation returns expected output."""
|
||||
self.assert_validate_directory(directory['path'], directory['error'])
|
||||
self.assert_validate_directory(path, error)
|
||||
|
||||
@pytest.mark.usefixtures('needs_not_root')
|
||||
@pytest.mark.parametrize('directory', [{
|
||||
'path': '/',
|
||||
'error': '4'
|
||||
}, {
|
||||
'path': '/tmp',
|
||||
'error': ''
|
||||
}])
|
||||
def test_validate_directory_writable(self, directory):
|
||||
@pytest.mark.parametrize('path,error', [('/', '4'), ('/tmp', '')])
|
||||
def test_validate_directory_writable(self, path, error):
|
||||
"""Test that directory writable validation returns expected output."""
|
||||
self.assert_validate_directory(directory['path'], directory['error'],
|
||||
check_writable=True)
|
||||
self.assert_validate_directory(path, error, check_writable=True)
|
||||
|
||||
@pytest.mark.usefixtures('needs_not_root')
|
||||
@pytest.mark.parametrize('directory', [{
|
||||
'path': '/var/lib/plinth_storage_test_not_exists',
|
||||
'error': '4'
|
||||
}, {
|
||||
'path': '/tmp/plint_storage_test_not_exists',
|
||||
'error': ''
|
||||
}])
|
||||
def test_validate_directory_creatable(self, directory):
|
||||
@pytest.mark.parametrize(
|
||||
'path,error', [('/var/lib/plinth_storage_test_not_exists', '4'),
|
||||
('/tmp/plint_storage_test_not_exists', ''),
|
||||
('/var/../tmp/plint_storage_test_not_exists', '')])
|
||||
def test_validate_directory_creatable(self, path, error):
|
||||
"""Test that directory creatable validation returns expected output."""
|
||||
self.assert_validate_directory(directory['path'], directory['error'],
|
||||
check_creatable=True)
|
||||
self.assert_validate_directory(path, error, check_creatable=True)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user