mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
ttrss: Remove app not available in Trixie
Tests: - Running 'make build install' remove the module loading include file for ttrss. - TT-RSS is no longer available in apps page. - Installing Tor works. Onion header is set correctly. Re-running app setup works. - RSS Bridge's description is updated as expected. Links work. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
parent
76b360bb68
commit
743b7bd163
3
Makefile
3
Makefile
@ -20,7 +20,8 @@ DISABLED_APPS_TO_REMOVE := \
|
||||
repro \
|
||||
tahoe \
|
||||
mldonkey \
|
||||
i2p
|
||||
i2p \
|
||||
ttrss
|
||||
|
||||
APP_FILES_TO_REMOVE := $(foreach app,$(DISABLED_APPS_TO_REMOVE),$(ENABLED_APPS_PATH)/$(app))
|
||||
|
||||
|
||||
9
debian/copyright
vendored
9
debian/copyright
vendored
@ -318,15 +318,6 @@ Copyright: Transmission Authors
|
||||
Comment: https://github.com/transmission/transmission/blob/master/gtk/icons/hicolor_apps_scalable_transmission.svg
|
||||
License: GPL-3
|
||||
|
||||
Files: plinth/modules/ttrss/static/icons/ttrss.png
|
||||
Copyright: Mark James <mjames@gmail.com>
|
||||
License: CC-BY-3.0
|
||||
|
||||
Files: plinth/modules/ttrss/static/icons/ttrss.svg
|
||||
Copyright: 2005 Andrew Dolgov
|
||||
Comment: https://git.tt-rss.org/fox/tt-rss/src/master/images/favicon-72px.png
|
||||
License: GPL-3+
|
||||
|
||||
Files: plinth/modules/upgrades/data/usr/share/augeas/lenses/aptsources.aug
|
||||
plinth/modules/upgrades/data/usr/share/augeas/lenses/tests/test_aptsources.aug
|
||||
Copyright: 2007-2025 David Lutterkort
|
||||
|
||||
@ -26,11 +26,12 @@ _description = [
|
||||
users_url=reverse_lazy('users:index')),
|
||||
format_lazy(
|
||||
_('You can use RSS-Bridge with <a href="{miniflux_url}">Miniflux</a> '
|
||||
'or <a href="{ttrss_url}">Tiny Tiny RSS</a> to follow various '
|
||||
'or <a href="{nextcloud_url}">Nextcloud News</a> to follow various '
|
||||
'websites. When adding a feed, enable authentication and use your '
|
||||
'{box_name} credentials.'),
|
||||
miniflux_url=reverse_lazy('miniflux:index'),
|
||||
ttrss_url=reverse_lazy('ttrss:index'), box_name=_(cfg.box_name)),
|
||||
nextcloud_url=reverse_lazy('nextcloud:index'),
|
||||
box_name=_(cfg.box_name)),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@ -471,7 +471,7 @@ def _set_onion_header(hidden_service):
|
||||
# https://community.torproject.org/onion-services/advanced/onion-location/
|
||||
hostname = hidden_service['hostname']
|
||||
config_contents = f'''# This file is managed by FreedomBox
|
||||
<LocationMatch "^(?!/(wordpress|mediawiki|tt-rss))/[^/]+">
|
||||
<LocationMatch "^(?!/(wordpress|mediawiki))/[^/]+">
|
||||
Header set Onion-Location "http://{hostname}%{{REQUEST_URI}}s"
|
||||
</LocationMatch>
|
||||
'''
|
||||
|
||||
@ -1,153 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""FreedomBox app to configure Tiny Tiny RSS."""
|
||||
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import app as app_module
|
||||
from plinth import cfg, frontpage, menu
|
||||
from plinth.config import DropinConfigs
|
||||
from plinth.daemon import Daemon, SharedDaemon
|
||||
from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.modules.firewall.components import Firewall
|
||||
from plinth.modules.users.components import UsersAndGroups
|
||||
from plinth.package import Packages, install
|
||||
from plinth.utils import Version, format_lazy
|
||||
|
||||
from . import manifest, privileged
|
||||
|
||||
_description = [
|
||||
_('Tiny Tiny RSS is a news feed (RSS/Atom) reader and aggregator, '
|
||||
'designed to allow reading news from any location, while feeling as '
|
||||
'close to a real desktop application as possible.'),
|
||||
format_lazy(
|
||||
_('When enabled, Tiny Tiny RSS can be accessed by '
|
||||
'<a href="{users_url}">any user</a> belonging to the '
|
||||
'feed-reader group.'), box_name=_(cfg.box_name),
|
||||
users_url=reverse_lazy('users:index')),
|
||||
format_lazy(
|
||||
_('When using a mobile or desktop application for Tiny Tiny RSS, use '
|
||||
'the URL <a href="/tt-rss/">/tt-rss</a> or '
|
||||
'<a href="/tt-rss-app/">/tt-rss-app</a> for connecting.'))
|
||||
]
|
||||
|
||||
|
||||
class TTRSSApp(app_module.App):
|
||||
"""FreedomBox app for TT-RSS."""
|
||||
|
||||
app_id = 'ttrss'
|
||||
|
||||
_version = 7
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Create components for the app."""
|
||||
super().__init__()
|
||||
|
||||
groups = {'feed-reader': _('Read and subscribe to news feeds')}
|
||||
|
||||
info = app_module.Info(app_id=self.app_id, version=self._version,
|
||||
name=_('Tiny Tiny RSS'), icon_filename='ttrss',
|
||||
description=_description,
|
||||
manual_page='TinyTinyRSS',
|
||||
clients=manifest.clients, tags=manifest.tags,
|
||||
donation_url='https://www.patreon.com/cthulhoo')
|
||||
self.add(info)
|
||||
|
||||
menu_item = menu.Menu('menu-ttrss', info.name, info.icon_filename,
|
||||
info.tags, 'ttrss:index', parent_url_name='apps')
|
||||
self.add(menu_item)
|
||||
|
||||
shortcut = frontpage.Shortcut('shortcut-ttrss', info.name,
|
||||
icon=info.icon_filename, url='/tt-rss',
|
||||
clients=info.clients, tags=info.tags,
|
||||
login_required=True,
|
||||
allowed_groups=list(groups))
|
||||
self.add(shortcut)
|
||||
|
||||
packages = Packages('packages-ttrss', [
|
||||
'tt-rss', 'postgresql', 'dbconfig-pgsql', 'php-pgsql',
|
||||
'python3-psycopg2'
|
||||
])
|
||||
self.add(packages)
|
||||
|
||||
dropin_configs = DropinConfigs('dropin-configs-ttrss', [
|
||||
'/etc/apache2/conf-available/tt-rss-plinth.conf',
|
||||
])
|
||||
self.add(dropin_configs)
|
||||
|
||||
firewall = Firewall('firewall-ttrss', info.name,
|
||||
ports=['http', 'https'], is_external=True)
|
||||
self.add(firewall)
|
||||
|
||||
webserver = Webserver('webserver-ttrss', 'tt-rss-plinth',
|
||||
urls=['https://{host}/tt-rss'],
|
||||
last_updated_version=5)
|
||||
self.add(webserver)
|
||||
|
||||
daemon1 = SharedDaemon('shared-daemon-ttrss-postgresql', 'postgresql')
|
||||
self.add(daemon1)
|
||||
|
||||
daemon2 = Daemon('daemon-ttrss', 'tt-rss')
|
||||
self.add(daemon2)
|
||||
|
||||
users_and_groups = UsersAndGroups('users-and-groups-ttrss',
|
||||
groups=groups)
|
||||
self.add(users_and_groups)
|
||||
|
||||
backup_restore = TTRSSBackupRestore('backup-restore-ttrss',
|
||||
**manifest.backup)
|
||||
self.add(backup_restore)
|
||||
|
||||
def enable(self):
|
||||
"""Enable components and API access."""
|
||||
super().enable()
|
||||
privileged.enable_api_access()
|
||||
|
||||
# Try to set the domain to one of the available TLS domains
|
||||
domain = privileged.get_domain()
|
||||
if not domain or domain == 'localhost':
|
||||
from plinth.modules import names
|
||||
domain = next(names.get_available_tls_domains(), None)
|
||||
privileged.set_domain(domain)
|
||||
|
||||
def setup(self, old_version):
|
||||
"""Install and configure the app."""
|
||||
privileged.pre_setup()
|
||||
super().setup(old_version)
|
||||
privileged.setup()
|
||||
if not old_version:
|
||||
self.enable()
|
||||
|
||||
def uninstall(self):
|
||||
"""De-configure and uninstall the app."""
|
||||
privileged.uninstall()
|
||||
super().uninstall()
|
||||
|
||||
def force_upgrade(self, packages):
|
||||
"""Force update package to resolve conffile prompts."""
|
||||
if 'tt-rss' not in packages:
|
||||
return False
|
||||
|
||||
# Allow tt-rss any lower version to upgrade to 21.*
|
||||
package = packages['tt-rss']
|
||||
if Version(package['new_version']) > Version('22~'):
|
||||
return False
|
||||
|
||||
install(['tt-rss'], force_configuration='new')
|
||||
privileged.setup()
|
||||
return True
|
||||
|
||||
|
||||
class TTRSSBackupRestore(BackupRestore):
|
||||
"""Component to backup/restore TT-RSS."""
|
||||
|
||||
def backup_pre(self, packet):
|
||||
"""Save database contents."""
|
||||
super().backup_pre(packet)
|
||||
privileged.dump_database()
|
||||
|
||||
def restore_post(self, packet):
|
||||
"""Restore database contents."""
|
||||
super().restore_post(packet)
|
||||
privileged.restore_database()
|
||||
@ -1,35 +0,0 @@
|
||||
# Restart the service every 120 seconds always. When tt-rss can't connect to a
|
||||
# database temporarily, it will exist with exit code 101. 120 seconds is the
|
||||
# default daemon sleep interval for tt-rss.
|
||||
[Service]
|
||||
CacheDirectory=tt-rss
|
||||
CapabilityBoundingSet=~CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER CAP_NET_ADMIN CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE CAP_KILL CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_LINUX_IMMUTABLE CAP_IPC_LOCK CAP_SYS_CHROOT CAP_BLOCK_SUSPEND CAP_LEASE CAP_SYS_PACCT CAP_SYS_TTY_CONFIG CAP_SYS_BOOT CAP_MAC_ADMIN CAP_MAC_OVERRIDE CAP_SYS_NICE CAP_SYS_RESOURCE
|
||||
DevicePolicy=closed
|
||||
LockPersonality=yes
|
||||
NoNewPrivileges=yes
|
||||
PrivateDevices=yes
|
||||
PrivateMounts=yes
|
||||
PrivateTmp=yes
|
||||
PrivateUsers=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectClock=yes
|
||||
ProtectHome=yes
|
||||
ProtectHostname=yes
|
||||
ProtectKernelLogs=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectProc=invisible
|
||||
ProtectSystem=strict
|
||||
RemoveIPC=yes
|
||||
Restart=always
|
||||
RestartSec=120s
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictNamespaces=yes
|
||||
RestrictSUIDSGID=yes
|
||||
RestrictRealtime=yes
|
||||
StateDirectory=tt-rss
|
||||
SystemCallArchitectures=native
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallFilter=~@resources
|
||||
SystemCallFilter=~@privileged
|
||||
SystemCallErrorNumber=EPERM
|
||||
@ -1,39 +0,0 @@
|
||||
##
|
||||
## On all sites, provide Tiny Tiny RSS on a default path: /tt-rss
|
||||
## Allow all valid LDAP users.
|
||||
##
|
||||
Alias /tt-rss /usr/share/tt-rss/www
|
||||
Alias /tt-rss-app /usr/share/tt-rss/www
|
||||
|
||||
<Location /tt-rss>
|
||||
# If a client sends 'Authorization' HTTP Header, perform Basic authorization
|
||||
# using LDAP, otherwise redirect to FreedomBox single sign-on. It is not
|
||||
# mandatory for the server to return HTTP 401 with 'WWW-Authenticate'. See
|
||||
# https://datatracker.ietf.org/doc/html/rfc2616#section-14.8
|
||||
<If "-n %{HTTP:Authorization}">
|
||||
Include includes/freedombox-auth-ldap.conf
|
||||
Require ldap-group cn=admin,ou=groups,dc=thisbox
|
||||
Require ldap-group cn=feed-reader,ou=groups,dc=thisbox
|
||||
</If>
|
||||
<Else>
|
||||
Include includes/freedombox-single-sign-on.conf
|
||||
<IfModule mod_auth_pubtkt.c>
|
||||
TKTAuthToken "feed-reader" "admin"
|
||||
</IfModule>
|
||||
</Else>
|
||||
</Location>
|
||||
|
||||
# URLs without further authentication. The URLs contain a unique key generated
|
||||
# and managed by tt-rss. This includes articles marked public or even other
|
||||
# categories.
|
||||
<Location /tt-rss/public.php>
|
||||
Require all granted
|
||||
</Location>
|
||||
|
||||
# Legacy configuration for apps that expect a HTTP 401 response
|
||||
# 'WWW-Authenticate' header.
|
||||
<Location /tt-rss-app>
|
||||
Include includes/freedombox-auth-ldap.conf
|
||||
Require ldap-group cn=admin,ou=groups,dc=thisbox
|
||||
Require ldap-group cn=feed-reader,ou=groups,dc=thisbox
|
||||
</Location>
|
||||
@ -1 +0,0 @@
|
||||
plinth.modules.ttrss
|
||||
@ -1,55 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth.clients import store_url
|
||||
|
||||
clients = [
|
||||
{
|
||||
'name':
|
||||
_('TTRSS-Reader'),
|
||||
'platforms': [{
|
||||
'type': 'store',
|
||||
'os': 'android',
|
||||
'store_name': 'google-play',
|
||||
'url': store_url('google-play', 'org.ttrssreader')
|
||||
}, {
|
||||
'type': 'store',
|
||||
'os': 'android',
|
||||
'store_name': 'f-droid',
|
||||
'url': store_url('f-droid', 'org.ttrssreader')
|
||||
}]
|
||||
},
|
||||
{
|
||||
'name':
|
||||
_('Geekttrss'),
|
||||
'platforms': [{
|
||||
'type': 'store',
|
||||
'os': 'android',
|
||||
'store_name': 'google-play',
|
||||
'url': store_url('google-play', 'com.geekorum.ttrss')
|
||||
}]
|
||||
},
|
||||
{
|
||||
'name': _('Tiny Tiny RSS'),
|
||||
'platforms': [{
|
||||
'type': 'web',
|
||||
'url': '/tt-rss'
|
||||
}]
|
||||
},
|
||||
]
|
||||
|
||||
backup = {
|
||||
'data': {
|
||||
'files': ['/var/lib/plinth/backups-data/ttrss-database.sql']
|
||||
},
|
||||
'secrets': {
|
||||
'files': [
|
||||
'/etc/tt-rss/database.php',
|
||||
'/etc/dbconfig-common/tt-rss.conf',
|
||||
]
|
||||
},
|
||||
'services': ['tt-rss']
|
||||
}
|
||||
|
||||
tags = [_('Feed reader'), _('News aggregation'), _('RSS'), _('ATOM')]
|
||||
@ -1,160 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Configure Tiny Tiny RSS."""
|
||||
|
||||
import augeas
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth.actions import privileged
|
||||
from plinth.db import postgres
|
||||
|
||||
CONFIG_FILE = '/etc/tt-rss/config.php'
|
||||
DEFAULT_FILE = '/etc/default/tt-rss'
|
||||
DATABASE_FILE = '/etc/tt-rss/database.php'
|
||||
DB_BACKUP_FILE = '/var/lib/plinth/backups-data/ttrss-database.sql'
|
||||
|
||||
|
||||
@privileged
|
||||
def pre_setup():
|
||||
"""Preseed debconf values before packages are installed."""
|
||||
action_utils.debconf_set_selections([
|
||||
'tt-rss tt-rss/database-type string pgsql',
|
||||
'tt-rss tt-rss/purge boolean true',
|
||||
'tt-rss tt-rss/dbconfig-remove boolean true',
|
||||
'tt-rss tt-rss/dbconfig-reinstall boolean true'
|
||||
])
|
||||
|
||||
|
||||
@privileged
|
||||
def get_domain() -> str | None:
|
||||
"""Get the domain set for Tiny Tiny RSS."""
|
||||
aug = load_augeas()
|
||||
|
||||
from urllib.parse import urlparse
|
||||
for match in aug.match('/files' + CONFIG_FILE + '/define'):
|
||||
if aug.get(match) == 'SELF_URL_PATH':
|
||||
url = aug.get(match + '/value').strip("'")
|
||||
return urlparse(url).netloc
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@privileged
|
||||
def set_domain(domain_name: str | None):
|
||||
"""Set the domain to be used by Tiny Tiny RSS."""
|
||||
if not domain_name:
|
||||
return
|
||||
|
||||
url = f"'https://{domain_name}/tt-rss/'"
|
||||
aug = load_augeas()
|
||||
|
||||
for match in aug.match('/files' + CONFIG_FILE + '/define'):
|
||||
if aug.get(match) == 'SELF_URL_PATH':
|
||||
aug.set(match + '/value', url)
|
||||
|
||||
aug.save()
|
||||
|
||||
|
||||
@privileged
|
||||
def setup():
|
||||
"""Setup Tiny Tiny RSS configuration."""
|
||||
aug = load_augeas()
|
||||
|
||||
aug.set('/files' + DEFAULT_FILE + '/DISABLED', '0')
|
||||
|
||||
skip_self_url_path_exists = False
|
||||
|
||||
for match in aug.match('/files' + CONFIG_FILE + '/define'):
|
||||
if aug.get(match) == 'PLUGINS':
|
||||
aug.set(match + '/value', "'auth_remote, note'")
|
||||
elif aug.get(match) == '_SKIP_SELF_URL_PATH_CHECKS':
|
||||
skip_self_url_path_exists = True
|
||||
aug.set(match + '/value', 'true')
|
||||
|
||||
if not skip_self_url_path_exists:
|
||||
aug.set('/files' + CONFIG_FILE + '/define[last() + 1]',
|
||||
'_SKIP_SELF_URL_PATH_CHECKS')
|
||||
aug.set('/files' + CONFIG_FILE + '/define[last()]/value', 'true')
|
||||
|
||||
aug.save()
|
||||
|
||||
config = _get_database_config()
|
||||
# dbconfig may not always setup the database and user account correctly.
|
||||
# Following, operation to create database/user is an idempotent operation.
|
||||
postgres.create_database(config['database'], config['user'],
|
||||
config['password'])
|
||||
|
||||
if action_utils.service_is_enabled('tt-rss'):
|
||||
action_utils.service_restart('tt-rss')
|
||||
|
||||
|
||||
def _get_database_config():
|
||||
"""Return the database configuration."""
|
||||
aug = load_augeas()
|
||||
|
||||
def _get_value(variable_name):
|
||||
"""Return the value of a variable from database configuration file."""
|
||||
return aug.get('/files' + DATABASE_FILE + '/$' + variable_name) \
|
||||
.strip("'\"")
|
||||
|
||||
return {
|
||||
'user': _get_value('dbuser'),
|
||||
'password': _get_value('dbpass'),
|
||||
'database': _get_value('dbname'),
|
||||
'host': _get_value('dbserver')
|
||||
}
|
||||
|
||||
|
||||
@privileged
|
||||
def enable_api_access():
|
||||
"""Enable API access so that tt-rss can be accessed through mobile app."""
|
||||
import psycopg2 # Only available post installation
|
||||
|
||||
config = _get_database_config()
|
||||
|
||||
connection = psycopg2.connect(database=config['database'],
|
||||
user=config['user'],
|
||||
password=config['password'],
|
||||
host=config['host'])
|
||||
cursor = connection.cursor()
|
||||
|
||||
cursor.execute("UPDATE ttrss_prefs SET def_value=true "
|
||||
"WHERE pref_name='ENABLE_API_ACCESS';")
|
||||
|
||||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
|
||||
@privileged
|
||||
def dump_database():
|
||||
"""Dump database to file."""
|
||||
config = _get_database_config()
|
||||
postgres.dump_database(DB_BACKUP_FILE, config['database'])
|
||||
|
||||
|
||||
@privileged
|
||||
def restore_database():
|
||||
"""Restore database from file."""
|
||||
config = _get_database_config()
|
||||
postgres.restore_database(DB_BACKUP_FILE, config['database'],
|
||||
config['user'], config['password'])
|
||||
|
||||
|
||||
def load_augeas():
|
||||
"""Initialize Augeas."""
|
||||
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
||||
augeas.Augeas.NO_MODL_AUTOLOAD)
|
||||
aug.set('/augeas/load/Shellvars/lens', 'Shellvars.lns')
|
||||
aug.set('/augeas/load/Shellvars/incl[last() + 1]', DEFAULT_FILE)
|
||||
aug.set('/augeas/load/Phpvars/lens', 'Phpvars.lns')
|
||||
aug.set('/augeas/load/Phpvars/incl[last() + 1]', CONFIG_FILE)
|
||||
aug.set('/augeas/load/Phpvars/incl[last() + 1]', DATABASE_FILE)
|
||||
aug.load()
|
||||
return aug
|
||||
|
||||
|
||||
@privileged
|
||||
def uninstall():
|
||||
"""Ensure that the database is removed."""
|
||||
# This setting set before deb package installation is not retrained,
|
||||
# somehow.
|
||||
action_utils.debconf_set_selections(['tt-rss tt-rss/purge boolean true'])
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
@ -1,88 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="512"
|
||||
height="512"
|
||||
viewBox="0 0 135.46667 135.46667"
|
||||
version="1.1"
|
||||
id="svg101357"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="ttrss.svg">
|
||||
<defs
|
||||
id="defs101351" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.9899495"
|
||||
inkscape:cx="211.53976"
|
||||
inkscape:cy="72.201459"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1764"
|
||||
inkscape:window-height="1413"
|
||||
inkscape:window-x="1260"
|
||||
inkscape:window-y="165"
|
||||
inkscape:window-maximized="0"
|
||||
units="px" />
|
||||
<metadata
|
||||
id="metadata101354">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-48.268459,86.306551)">
|
||||
<g
|
||||
id="g198979"
|
||||
style="display:inline;fill:#f18d36;fill-opacity:1"
|
||||
transform="matrix(0.77409524,0,0,0.77409524,14.208268,-115.72217)">
|
||||
<circle
|
||||
id="circle198940"
|
||||
r="24"
|
||||
cy="189"
|
||||
cx="68"
|
||||
style="fill:#f18d36;fill-opacity:1" />
|
||||
<path
|
||||
id="path198942"
|
||||
d="M 160,213 H 126 A 82,82 0 0 0 44,131 V 97 a 116,116 0 0 1 116,116 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#f18d36;fill-opacity:1" />
|
||||
<path
|
||||
id="path198944"
|
||||
d="M 184,213 A 140,140 0 0 0 44,73 V 38 a 175,175 0 0 1 175,175 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#f18d36;fill-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="text199007"
|
||||
d="m 135.048,-78.557287 -31.97143,8.229092 -5.254515,19.611233 H 82.763664 l -5.676249,21.185883 H 92.144579 L 81.401621,10.566616 c -2.510595,9.805309 -1.617376,16.924965 2.680407,21.360737 4.297783,4.435773 11.641561,6.654375 22.030512,6.654375 4.55251,0 9.203,-0.584668 13.95148,-1.75189 4.89655,-1.28402 9.0697,-2.91809 12.51972,-4.902485 l 1.94916,-18.384998 c -4.30671,2.568108 -8.85362,3.852332 -13.63958,3.852332 -7.70419,0 -10.5078,-3.910799 -8.41219,-11.7317143 l 9.43052,-35.1940517 h 26.26438 l 5.67625,-21.185883 H 127.5879 Z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:830.21112061px;line-height:1.25;font-family:'Work Sans';-inkscape-font-specification:'Work Sans Bold';letter-spacing:0px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:#2d4364;stroke-width:6.54638863;stroke-miterlimit:2.5999999;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@ -1,103 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
Functional, browser based tests for ttrss app.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from plinth.tests import functional
|
||||
|
||||
APP_ID = 'ttrss'
|
||||
|
||||
pytestmark = [pytest.mark.apps, pytest.mark.ttrss, pytest.mark.sso]
|
||||
|
||||
|
||||
class TestTTRSSApp(functional.BaseAppTests):
|
||||
"""Class to customize basic app tests for TTRSS."""
|
||||
|
||||
app_name = 'ttrss'
|
||||
has_service = True
|
||||
has_web = True
|
||||
|
||||
@pytest.mark.backups
|
||||
def test_backup_restore(self, session_browser):
|
||||
"""Test backup and restore of app data."""
|
||||
functional.app_enable(session_browser, APP_ID)
|
||||
_subscribe(session_browser)
|
||||
functional.backup_create(session_browser, APP_ID, 'test_ttrss')
|
||||
|
||||
functional.uninstall(session_browser, self.app_name)
|
||||
functional.backup_restore(session_browser, APP_ID, 'test_ttrss')
|
||||
|
||||
assert functional.service_is_running(session_browser, APP_ID)
|
||||
assert _is_subscribed(session_browser)
|
||||
|
||||
|
||||
def _ttrss_load_main_interface(browser):
|
||||
"""Load the TT-RSS interface."""
|
||||
functional.visit(browser, '/tt-rss/')
|
||||
overlay = browser.find_by_id('overlay')
|
||||
functional.eventually(lambda: not overlay.visible)
|
||||
|
||||
|
||||
def _expand_nodes(browser):
|
||||
"""If interface has category nodes collapsed, expand them."""
|
||||
nodes = browser.find_by_css('span.dijitTreeExpandoClosed')
|
||||
for node in nodes:
|
||||
node.click()
|
||||
|
||||
|
||||
def _is_feed_shown(browser, invert=False):
|
||||
"""Return whether the test feed is present."""
|
||||
_expand_nodes(browser)
|
||||
return browser.is_text_present('Planet Debian') != invert
|
||||
|
||||
|
||||
def _click_main_menu_item(browser, text):
|
||||
"""Select an item from the main actions menu."""
|
||||
browser.find_by_css('.action-chooser').click()
|
||||
browser.find_by_text(text).click()
|
||||
|
||||
|
||||
def _subscribe(browser):
|
||||
"""Subscribe to a feed in TT-RSS."""
|
||||
|
||||
def _already_subscribed_message():
|
||||
return browser.is_text_present(
|
||||
'You are already subscribed to this feed.')
|
||||
|
||||
_ttrss_load_main_interface(browser)
|
||||
|
||||
_click_main_menu_item(browser, 'Subscribe to feed...')
|
||||
browser.find_by_id('feedDlg_feedUrl').fill(
|
||||
'https://planet.debian.org/atom.xml')
|
||||
browser.find_by_text('Subscribe').click()
|
||||
add_dialog = browser.find_by_css('#feedAddDlg')
|
||||
functional.eventually(
|
||||
lambda: not add_dialog.visible or _already_subscribed_message())
|
||||
if _already_subscribed_message():
|
||||
browser.find_by_text('Cancel').click()
|
||||
functional.eventually(lambda: not add_dialog.visible)
|
||||
|
||||
|
||||
def _unsubscribe(browser):
|
||||
"""Unsubscribe from a feed in TT-RSS."""
|
||||
_ttrss_load_main_interface(browser)
|
||||
_expand_nodes(browser)
|
||||
|
||||
browser.find_by_text('Planet Debian').click()
|
||||
_click_main_menu_item(browser, 'Unsubscribe')
|
||||
|
||||
prompt = browser.get_alert()
|
||||
prompt.accept()
|
||||
|
||||
# Reload as sometimes the feed does not disappear immediately
|
||||
_ttrss_load_main_interface(browser)
|
||||
|
||||
assert functional.eventually(_is_feed_shown, [browser, True])
|
||||
|
||||
|
||||
def _is_subscribed(browser):
|
||||
"""Return whether subscribed to a feed in TT-RSS."""
|
||||
_ttrss_load_main_interface(browser)
|
||||
return _is_feed_shown(browser)
|
||||
@ -1,10 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""
|
||||
URLs for the Tiny Tiny RSS module.
|
||||
"""
|
||||
|
||||
from django.urls import re_path
|
||||
|
||||
from .views import TTRSSAppView
|
||||
|
||||
urlpatterns = [re_path(r'^apps/ttrss/$', TTRSSAppView.as_view(), name='index')]
|
||||
@ -1,33 +0,0 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
"""Django views for Tiny Tiny RSS app."""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth.forms import TLSDomainForm
|
||||
from plinth.views import AppView
|
||||
|
||||
from . import privileged
|
||||
|
||||
|
||||
class TTRSSAppView(AppView):
|
||||
"""Show TTRSS app main view."""
|
||||
|
||||
app_id = 'ttrss'
|
||||
form_class = TLSDomainForm
|
||||
|
||||
def get_initial(self):
|
||||
"""Return the values to fill in the form."""
|
||||
initial = super().get_initial()
|
||||
initial['domain'] = privileged.get_domain()
|
||||
return initial
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Change the domain of TT-RSS app."""
|
||||
data = form.cleaned_data
|
||||
old_data = form.initial
|
||||
if old_data['domain'] != data['domain']:
|
||||
privileged.set_domain(data['domain'])
|
||||
messages.success(self.request, _('Configuration updated'))
|
||||
|
||||
return super().form_valid(form)
|
||||
@ -335,9 +335,9 @@ def test_packages_remove_obsolete(apt_run):
|
||||
apt_run.assert_not_called() # No obsolete package to remove currently.
|
||||
|
||||
with patch('plinth.modules.upgrades.distupgrade.OBSOLETE_PACKAGES',
|
||||
['tt-rss', 'searx']):
|
||||
['searx']):
|
||||
distupgrade._packages_remove_obsolete()
|
||||
apt_run.assert_called_with(['remove', 'tt-rss', 'searx'])
|
||||
apt_run.assert_called_with(['remove', 'searx'])
|
||||
|
||||
|
||||
@patch('plinth.modules.upgrades.distupgrade._apt_run')
|
||||
|
||||
@ -52,7 +52,6 @@ _site_url = {
|
||||
'cockpit': '/_cockpit/',
|
||||
'syncthing': '/syncthing/',
|
||||
'rssbridge': '/rss-bridge/',
|
||||
'ttrss': '/tt-rss/',
|
||||
'sogo': '/SOGo/',
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user