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:
Sunil Mohan Adapa 2025-11-08 07:09:09 -08:00 committed by James Valleroy
parent 76b360bb68
commit 743b7bd163
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
18 changed files with 8 additions and 693 deletions

View File

@ -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
View File

@ -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

View File

@ -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)),
]

View File

@ -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>
'''

View File

@ -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()

View File

@ -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

View File

@ -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>

View File

@ -1 +0,0 @@
plinth.modules.ttrss

View File

@ -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')]

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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')]

View File

@ -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)

View File

@ -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')

View File

@ -52,7 +52,6 @@ _site_url = {
'cockpit': '/_cockpit/',
'syncthing': '/syncthing/',
'rssbridge': '/rss-bridge/',
'ttrss': '/tt-rss/',
'sogo': '/SOGo/',
}