mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
kiwix: Fix various issues after review
- Fix icon paths in copyright file. - Minor refactoring. - Add Kiwix library link to app page as well as users may want to see the content available before installing the app. - Consolidate terminology to 'content package' for UI and just 'package' internally. - Drop unused SYSTEM_USER constant. - Simplify the ExecStart= in systemd service file. - Fix incorrect i18n caused by non-lazy formatting of strings. - Confirm that xml parsing is not vulnerable as expat library of required version is used in Debian bookworm. - Don't start the kiwix daemon when managing library if app is disabled. - Ignore errors when removing files during uninstallation. - Handle failures more gracefully when library XML file does not have required attributes. - Update SVG/PNG icons to adhere to FreedomBox guidelines. - Trim block translations in templates. - Drop comments/deadcode inside translation strings. - Drop a comment inside add content page that only makes sense with multiple methods for adding content. - tests: Don't use pkg_resources library as it is deprecated. We can use importlib.resources library in future if we run tests on zip installations. - Fix potential security issues while writing file to tmp directory. Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
34976ac4b0
commit
cfdf92cf0d
4
debian/copyright
vendored
4
debian/copyright
vendored
@ -132,8 +132,8 @@ Files: plinth/modules/janus/static/icons/janus.png
|
||||
Copyright: 2014-2022 Meetecho
|
||||
License: GPL-3 with OpenSSL exception
|
||||
|
||||
Files: static/themes/default/icons/kiwix.png
|
||||
static/themes/default/icons/kiwix.svg
|
||||
Files: plinth/modules/kiwix/static/icons/kiwix.png
|
||||
plinth/modules/kiwix/static/icons/kiwix.svg
|
||||
Copyright: 2020 The other Kiwix guy
|
||||
Comment: https://commons.wikimedia.org/wiki/File:Kiwix_logo_v3.svg
|
||||
License: CC-BY-SA-4.0
|
||||
|
||||
@ -5,13 +5,14 @@ FreedomBox app for Kiwix content server.
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import app as app_module, frontpage, menu, package
|
||||
from plinth import app as app_module
|
||||
from plinth import frontpage, menu, package
|
||||
from plinth.config import DropinConfigs
|
||||
from plinth.daemon import Daemon
|
||||
from plinth.modules.apache.components import Webserver
|
||||
from plinth.modules.backups.components import BackupRestore
|
||||
from plinth.modules.kiwix import manifest
|
||||
from plinth.modules.firewall.components import Firewall, FirewallLocalProtection
|
||||
from plinth.modules.firewall.components import (Firewall,
|
||||
FirewallLocalProtection)
|
||||
from plinth.modules.users.components import UsersAndGroups
|
||||
|
||||
from . import manifest, privileged
|
||||
@ -28,11 +29,13 @@ _description = [
|
||||
<li>Educational materials: PHET, TED Ed, Vikidia</li>
|
||||
<li>eBooks: Project Gutenberg</li>
|
||||
<li>Magazines: Low-tech Magazine</li>
|
||||
</ul>''')
|
||||
</ul>'''),
|
||||
_('You can <a href="https://library.kiwix.org" target="_blank" '
|
||||
'rel="noopener noreferrer">download</a> content packages from the Kiwix '
|
||||
'project or <a href="https://openzim.org/wiki/Build_your_ZIM_file" '
|
||||
'target="_blank" rel="noopener noreferrer">create</a> your own.'),
|
||||
]
|
||||
|
||||
SYSTEM_USER = 'kiwix'
|
||||
|
||||
|
||||
class KiwixApp(app_module.App):
|
||||
"""FreedomBox app for Kiwix."""
|
||||
@ -116,6 +119,6 @@ class KiwixApp(app_module.App):
|
||||
|
||||
|
||||
def validate_file_name(file_name: str):
|
||||
"""Check if the content archive file has a valid extension."""
|
||||
if not file_name.endswith(".zim"):
|
||||
raise ValueError(f"Expected a ZIM file. Found {file_name}")
|
||||
"""Check if the content package file has a valid extension."""
|
||||
if not file_name.endswith('.zim'):
|
||||
raise ValueError(f'Expected a ZIM file. Found {file_name}')
|
||||
|
||||
@ -13,7 +13,7 @@ Environment=HOME="/var/lib/kiwix-server-freedombox"
|
||||
Environment=LIBRARY_PATH="/var/lib/kiwix-server-freedombox/library_zim.xml"
|
||||
Environment=ARGS="--library --port=4201 --urlRootLocation=kiwix"
|
||||
ExecStartPre=sh -e -c "mkdir -p $HOME/content; library=$$(ls ${LIBRARY_PATH} 2>/dev/null || true); [ \"x$${library}\" = \"x\" ] && (mkdir -p \"${HOME}\" && echo '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<library version=\"20110515\">\n</library>' > \"${LIBRARY_PATH}\") || true"
|
||||
ExecStart=sh -e -c "exec /usr/bin/kiwix-serve $ARGS $LIBRARY_PATH"
|
||||
ExecStart=/usr/bin/kiwix-serve $ARGS $LIBRARY_PATH
|
||||
Restart=on-failure
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
DynamicUser=yes
|
||||
|
||||
@ -8,18 +8,21 @@ from django.core import validators
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from plinth import cfg
|
||||
from plinth.utils import format_lazy
|
||||
|
||||
from .privileged import KIWIX_HOME
|
||||
|
||||
|
||||
class AddContentForm(forms.Form):
|
||||
"""Form to create an empty library."""
|
||||
class AddPackageForm(forms.Form):
|
||||
"""Form to upload a content package to a library."""
|
||||
|
||||
# Would be nice to have a progress bar when uploading large files.
|
||||
file = forms.FileField(
|
||||
label=_('Upload File'), required=True, validators=[
|
||||
validators.FileExtensionValidator(
|
||||
['zim'], _('Content packages have to be in .zim format'))
|
||||
], help_text=_(f'''Uploaded ZIM files will be stored under
|
||||
{KIWIX_HOME}/content on your {cfg.box_name}. If Kiwix fails to add the file,
|
||||
it will be deleted immediately to save disk space.'''))
|
||||
], help_text=format_lazy(
|
||||
_('Uploaded ZIM files will be stored under {kiwix_home}/content '
|
||||
'on your {box_name}. If Kiwix fails to add the file, it will be '
|
||||
'deleted immediately to save disk space.'),
|
||||
box_name=_(cfg.box_name), kiwix_home=KIWIX_HOME))
|
||||
|
||||
@ -5,7 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from plinth.clients import validate
|
||||
|
||||
clients = validate([{
|
||||
'name': _('kiwix'),
|
||||
'name': _('Kiwix'),
|
||||
'platforms': [{
|
||||
'type': 'web',
|
||||
'url': '/kiwix'
|
||||
|
||||
@ -3,10 +3,11 @@
|
||||
Privileged actions for Kiwix content server.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
import subprocess
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from plinth import action_utils
|
||||
from plinth.actions import privileged
|
||||
@ -19,19 +20,19 @@ CONTENT_DIR = KIWIX_HOME / 'content'
|
||||
|
||||
|
||||
@privileged
|
||||
def add_content(file_name: str):
|
||||
def add_package(file_name: str):
|
||||
"""Adds a content package to Kiwix.
|
||||
|
||||
Adding packages is idempotent.
|
||||
|
||||
Users can add content to Kiwix in multiple ways:
|
||||
Users can add content to Kiwix in multiple ways:
|
||||
- Upload a ZIM file
|
||||
- Provide a link to the ZIM file
|
||||
- Provide a magnet link to the ZIM file
|
||||
|
||||
The commandline download manager aria2c is a dependency of kiwix-tools.
|
||||
aria2c is used for both HTTP and Magnet downloads.
|
||||
"""
|
||||
The commandline download manager aria2c is a dependency of kiwix-tools.
|
||||
aria2c is used for both HTTP and Magnet downloads.
|
||||
"""
|
||||
kiwix.validate_file_name(file_name)
|
||||
|
||||
# Moving files to the Kiwix library path ensures that
|
||||
@ -39,6 +40,8 @@ def add_content(file_name: str):
|
||||
zim_file_name = pathlib.Path(file_name).name
|
||||
CONTENT_DIR.mkdir(exist_ok=True)
|
||||
zim_file_dest = str(CONTENT_DIR / zim_file_name)
|
||||
shutil.chown(file_name, 'root', 'root')
|
||||
os.chmod(file_name, 0o644)
|
||||
shutil.move(file_name, zim_file_dest)
|
||||
|
||||
_kiwix_manage_add(zim_file_dest)
|
||||
@ -48,40 +51,51 @@ def _kiwix_manage_add(zim_file: str):
|
||||
subprocess.check_call(['kiwix-manage', LIBRARY_FILE, 'add', zim_file])
|
||||
|
||||
# kiwix-serve doesn't read the library file unless it is restarted.
|
||||
action_utils.service_restart('kiwix-server-freedombox')
|
||||
action_utils.service_try_restart('kiwix-server-freedombox')
|
||||
|
||||
|
||||
@privileged
|
||||
def uninstall():
|
||||
def uninstall() -> None:
|
||||
"""Remove all content during uninstall."""
|
||||
shutil.rmtree(str(CONTENT_DIR))
|
||||
LIBRARY_FILE.unlink()
|
||||
shutil.rmtree(str(CONTENT_DIR), ignore_errors=True)
|
||||
LIBRARY_FILE.unlink(missing_ok=True)
|
||||
|
||||
|
||||
@privileged
|
||||
def list_content_packages() -> dict[str, dict]:
|
||||
library = ET.parse(LIBRARY_FILE).getroot()
|
||||
def list_packages() -> dict[str, dict[str, str]]:
|
||||
"""Return the list of content packages configured in library file."""
|
||||
library = ElementTree.parse(LIBRARY_FILE).getroot()
|
||||
|
||||
# Relying on the fact that Python dictionaries maintain order of insertion.
|
||||
return {
|
||||
book.attrib['id']: {
|
||||
'title': book.attrib['title'],
|
||||
'description': book.attrib['description'],
|
||||
# strip '.zim' from the path
|
||||
'path': book.attrib['path'].split('/')[-1][:-4].lower()
|
||||
}
|
||||
for book in library
|
||||
}
|
||||
books = {}
|
||||
for book in library:
|
||||
path = book.attrib['path'].split('/')[-1]
|
||||
path = path.removesuffix('.zim').lower() # Strip '.zim' from the path
|
||||
try:
|
||||
books[book.attrib['id']] = {
|
||||
'title': book.attrib['title'],
|
||||
'description': book.attrib['description'],
|
||||
'path': path
|
||||
}
|
||||
except KeyError:
|
||||
pass # Ignore entries that don't have expected properties
|
||||
|
||||
return books
|
||||
|
||||
|
||||
@privileged
|
||||
def delete_content_package(zim_id: str):
|
||||
library = ET.parse(LIBRARY_FILE).getroot()
|
||||
def delete_package(zim_id: str):
|
||||
"""Remove a content package from the library file."""
|
||||
library = ElementTree.parse(LIBRARY_FILE).getroot()
|
||||
|
||||
for book in library:
|
||||
if book.attrib['id'] == zim_id:
|
||||
try:
|
||||
if book.attrib['id'] != zim_id:
|
||||
continue
|
||||
|
||||
subprocess.check_call(
|
||||
['kiwix-manage', LIBRARY_FILE, 'remove', zim_id])
|
||||
(KIWIX_HOME / book.attrib['path']).unlink()
|
||||
action_utils.service_restart('kiwix-server-freedombox')
|
||||
action_utils.service_try_restart('kiwix-server-freedombox')
|
||||
return
|
||||
except KeyError: # Expected properties not found on elements
|
||||
pass
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 18 KiB |
@ -1,20 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1"
|
||||
id="Layer_1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" 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:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1256 1256"
|
||||
style="enable-background:new 0 0 1256 1256;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 512 512"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="kiwix.svg"
|
||||
width="512"
|
||||
height="512"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs9" /><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.59282121"
|
||||
inkscape:cx="448.70189"
|
||||
inkscape:cy="609.79599"
|
||||
inkscape:window-width="1504"
|
||||
inkscape:window-height="1282"
|
||||
inkscape:window-x="26"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#010101;}
|
||||
</style>
|
||||
<path class="st0" d="M1165,764.1c-8.3-36.4-68.5-141.3-191.6-234.4c-22.5-17.1-42.8-31.3-59.7-42.6
|
||||
c24.6-105.3-103.3-232.3-228.1-172.5C596,230.3,496.1,195.9,404.2,197.3c-243.3,3.4-431,256.9-229.1,498.8c0.1,0.1,0.2,0.2,0.4,0.4
|
||||
c3.1,3.7,6.3,7.4,9.5,11.1c13.1,15.7,21.8,29.6,29.2,54.1L274.4,959h-21.3c-19.6,0-35.6,15.9-35.6,35.6h80.8l135.8,64.2
|
||||
c8.4-17.8,0.8-39-16.9-47.3l-35.6-16.8H484c0-19.6-15.9-35.6-35.6-35.6h-92.8c-16.2,0-30.6-10.6-35.3-26.1l-47.7-156.7
|
||||
c-11.9-41.2,15.4-68.1,41.1-71.3c23.4-2.9,35.2,12.2,46.2,48.8l42.4,139h-21.3c-19.6,0-35.6,15.9-35.6,35.6h80.8l135.8,64.2
|
||||
c8.4-17.8,0.8-39-16.9-47.3l-35.6-16.8h75.1c7.6,12.9,16.9,25.1,28,36.1c70,70,183.7,70,253.7,0s70-183.7,0-253.7s-183.7-70-253.7,0
|
||||
c-49.2,49.2-63.9,120-43.9,182h-85c-16.2,0-30.6-10.6-35.3-26.1L378,635.4l12-6.4c167.1-70.1,345.8,55.1,470.2-65.2
|
||||
c0.3-0.3,0.6-0.6,0.8-0.8c15.4-14,30.8-28.3,76.3,0.2c49,30.7,157.1,110.8,206.1,247.8C1143.5,811,1173.2,800.4,1165,764.1z
|
||||
M821.2,460.6c-0.4-18.7-15.6-33.7-34.5-33.7c-19,0-34.5,15.4-34.5,34.5c0,10.4,4.6,19.6,11.8,25.9c-25-4.8-43.8-26.6-43.8-52.9
|
||||
c0-29.8,24.1-53.9,53.9-53.9c29.8,0,53.9,24.1,53.9,53.9C828,443.9,825.5,452.8,821.2,460.6z"/>
|
||||
<path
|
||||
class="st0"
|
||||
d="m 511.31939,320.69463 c -3.94623,-17.30636 -32.56828,-67.181 -91.09611,-111.44534 -10.69761,-8.13018 -20.34923,-14.88156 -28.38433,-20.25414 11.69606,-50.06482 -49.11392,-110.446904 -108.45,-82.01502 C 240.78868,66.899736 193.29129,50.544276 149.59749,51.209906 33.920647,52.826436 -55.321204,173.35286 40.672028,288.36407 c 0.04754,0.0476 0.09509,0.0951 0.19018,0.19018 1.473893,1.75916 2.995331,3.51833 4.516769,5.27749 6.228389,7.46456 10.364799,14.0733 13.883124,25.72181 l 28.622054,93.80617 H 77.757083 c -9.318809,0 -16.925999,7.55964 -16.925999,16.926 h 38.416311 l 64.566025,30.52385 c 3.99378,-8.463 0.38036,-18.54253 -8.03509,-22.48876 l -16.926,-7.98755 h 48.68602 c 0,-9.31881 -7.55965,-16.926 -16.926,-16.926 h -44.1217 c -7.70228,0 -14.54876,-5.03976 -16.78337,-12.40923 L 87.028346,326.49511 c -5.657848,-19.58851 7.321921,-32.3781 19.540974,-33.89954 11.12551,-1.3788 16.73581,5.80048 21.96576,23.20193 l 20.15905,66.08747 h -10.12707 c -9.31881,0 -16.926,7.55964 -16.926,16.926 h 38.41631 l 64.56603,30.52385 c 3.99378,-8.463 0.38036,-18.54253 -8.03509,-22.48876 l -16.926,-7.98755 h 35.70625 c 3.61341,6.1333 8.03509,11.93378 13.31258,17.16373 33.28146,33.28145 87.34006,33.28145 120.62151,0 33.28146,-33.28146 33.28146,-87.34006 0,-120.62152 -33.28145,-33.28145 -87.34005,-33.28145 -120.62151,0 -23.39211,23.39211 -30.38122,57.05393 -20.87223,86.53179 h -40.4132 c -7.70228,0 -14.54875,-5.03976 -16.78336,-12.40923 l -33.47164,-110.01899 5.7054,-3.04287 c 79.44759,-33.329 164.4104,26.19726 223.5563,-30.9993 0.14264,-0.14264 0.28527,-0.28527 0.38036,-0.38036 7.32192,-6.65629 14.64384,-13.45522 36.27679,0.0951 23.29702,14.59629 74.69311,52.67979 97.99013,117.81636 0.0475,0 14.16839,-5.03977 10.2697,-22.29858 z M 347.85989,176.39574 c -0.19018,-8.8909 -7.41702,-16.02265 -16.40301,-16.02265 -9.03354,0 -16.403,7.32193 -16.403,16.40301 0,4.94467 2.18706,9.31881 5.6103,12.31414 -11.88623,-2.28216 -20.82468,-12.64696 -20.82468,-25.15127 0,-14.1684 11.45833,-25.62673 25.62672,-25.62673 14.16839,0 25.62672,11.45833 25.62672,25.62673 0,4.51676 -1.18862,8.74826 -3.23305,12.45677 z"
|
||||
id="path4"
|
||||
style="stroke-width:0.475449" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.3 KiB |
@ -1,58 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>{{ title }}</h3>
|
||||
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
You can <a href="https://library.kiwix.org"
|
||||
target="_blank" rel="noopener noreferrer">download</a>
|
||||
content packages from the Kiwix project or
|
||||
<a href="https://openzim.org/wiki/Build_your_ZIM_file"
|
||||
target="_blank" rel="noopener noreferrer">create</a> your own.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktrans %}
|
||||
Content packages can be added in the following ways:
|
||||
<ul>
|
||||
<li>upload a ZIM file</li>
|
||||
<!-- <li>upload a BitTorrent file to download a ZIM file</li> -->
|
||||
<!-- <li>provide a download link to a ZIM file</li> -->
|
||||
<!-- <li>provide a magnet link to download a ZIM file</li> -->
|
||||
<!-- <li>provide a file path to a ZIM file</li> -->
|
||||
</ul>
|
||||
|
||||
<!-- TODO Add this somewhere -->
|
||||
<!-- The Kiwix project recommends using BitTorrent for downloads. -->
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if max_filesize %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<!-- <span class="fa fa-exclamation-triangle" aria-hidden="true"></span> -->
|
||||
<!-- <span class="sr-only">{% trans "Caution:" %}</span> -->
|
||||
{% blocktrans trimmed %}
|
||||
You have {{ max_filesize }} of free disk space available.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form class="form form-kiwix" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary"
|
||||
value="{% trans "Upload file" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
39
plinth/modules/kiwix/templates/kiwix-add-package.html
Normal file
39
plinth/modules/kiwix/templates/kiwix-add-package.html
Normal file
@ -0,0 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
|
||||
{% load bootstrap %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h3>{{ title }}</h3>
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
You can <a href="https://library.kiwix.org" target="_blank"
|
||||
rel="noopener noreferrer">download</a> content packages from the Kiwix
|
||||
project or <a href="https://openzim.org/wiki/Build_your_ZIM_file"
|
||||
target="_blank" rel="noopener noreferrer">create</a> your own.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
{% if max_filesize %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{% blocktrans trimmed %}
|
||||
You have {{ max_filesize }} of free disk space available.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form class="form form-kiwix" enctype="multipart/form-data" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{{ form|bootstrap }}
|
||||
|
||||
<input type="submit" class="btn btn-primary"
|
||||
value="{% trans "Upload ZIM file" %}"/>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@ -15,7 +15,8 @@
|
||||
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Delete this package permanently? You may add it back later if you have a copy of the ZIM file.
|
||||
Delete this package permanently? You may add it back later if you have a
|
||||
copy of the ZIM file.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
|
||||
@ -8,32 +8,31 @@
|
||||
{% block configuration %}
|
||||
{{ block.super }}
|
||||
|
||||
<h3>{% trans "Manage Content" %}</h3>
|
||||
<h3>{% trans "Manage Content Packages" %}</h3>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<a href="{% url 'kiwix:add-content' %}" class="btn btn-default"
|
||||
<a href="{% url 'kiwix:add-package' %}" class="btn btn-default"
|
||||
role="button" title="{% trans 'Add a content package' %}">
|
||||
<span class="fa fa-plus" aria-hidden="true"></span>
|
||||
{% trans 'Add' %}
|
||||
{% trans 'Add Package' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{% if not packages %}
|
||||
<p>{% trans 'No content available.' %}</p>
|
||||
<p>{% trans 'No content packages available.' %}</p>
|
||||
{% else %}
|
||||
<div id="kiwix-packages" class="list-group list-group-two-column">
|
||||
{% for id, package in packages.items %}
|
||||
<div class="list-group-item">
|
||||
<a id="{{ id }}"
|
||||
class="primary"
|
||||
<a id="{{ id }}" class="primary"
|
||||
href="/kiwix/viewer#{{ package.path }}"
|
||||
title="{{ package.description }}">
|
||||
title="{{ package.description }}">
|
||||
{{ package.title }}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'kiwix:delete-content' id %}"
|
||||
<a href="{% url 'kiwix:delete-package' id %}"
|
||||
class="btn btn-default btn-sm secondary" role="button"
|
||||
title="{% blocktrans with title=package.title %}Delete package {{ title }}{% endblocktrans %}">
|
||||
<span class="fa fa-trash-o" aria-hidden="true"></span>
|
||||
|
||||
@ -1 +1 @@
|
||||
Nothing to see here.
|
||||
Nothing to see here.
|
||||
|
||||
@ -3,94 +3,97 @@
|
||||
Functional, browser based tests for Kiwix app.
|
||||
"""
|
||||
|
||||
import pkg_resources
|
||||
import pathlib
|
||||
from time import sleep
|
||||
|
||||
import pytest
|
||||
|
||||
from time import sleep
|
||||
from plinth.modules.kiwix.tests.test_privileged import ZIM_ID
|
||||
|
||||
from plinth.tests import functional
|
||||
|
||||
from .test_privileged import ZIM_ID
|
||||
|
||||
pytestmark = [pytest.mark.apps, pytest.mark.sso, pytest.mark.kiwix]
|
||||
|
||||
_default_url = functional.config['DEFAULT']['url']
|
||||
|
||||
ZIM_ID = 'bc4f8cdf-5626-2b13-3860-0033deddfbea'
|
||||
_data_dir = pathlib.Path(__file__).parent / 'data'
|
||||
|
||||
|
||||
class TestKiwixApp(functional.BaseAppTests):
|
||||
"""Basic functional tests for Kiwix app."""
|
||||
|
||||
app_name = 'kiwix'
|
||||
has_service = True
|
||||
has_web = True
|
||||
|
||||
def test_add_delete_content_package(self, session_browser):
|
||||
def test_add_delete_package(self, session_browser):
|
||||
"""Test adding/deleting content package to the library."""
|
||||
functional.app_enable(session_browser, 'kiwix')
|
||||
|
||||
zim_file = pkg_resources.resource_filename(
|
||||
'plinth.modules.kiwix.tests', 'data/FreedomBox.zim')
|
||||
_add_content_package(session_browser, zim_file)
|
||||
assert _is_content_package_listed(session_browser, 'freedombox')
|
||||
assert _is_content_package_available(session_browser, 'FreedomBox')
|
||||
zim_file = _data_dir / 'FreedomBox.zim'
|
||||
_add_package(session_browser, str(zim_file))
|
||||
assert _is_package_listed(session_browser, 'freedombox')
|
||||
assert _is_package_available(session_browser, 'FreedomBox')
|
||||
|
||||
_delete_content_package(session_browser, ZIM_ID)
|
||||
assert not _is_content_package_listed(session_browser, 'freedombox')
|
||||
assert not _is_content_package_available(session_browser, 'FreedomBox')
|
||||
_delete_package(session_browser, ZIM_ID)
|
||||
assert not _is_package_listed(session_browser, 'freedombox')
|
||||
assert not _is_package_available(session_browser, 'FreedomBox')
|
||||
|
||||
@pytest.mark.backups
|
||||
def test_backup_restore(self, session_browser):
|
||||
"""Test backing up and restoring."""
|
||||
functional.app_enable(session_browser, 'kiwix')
|
||||
|
||||
zim_file = pkg_resources.resource_filename(
|
||||
'plinth.modules.kiwix.tests', 'data/FreedomBox.zim')
|
||||
_add_content_package(session_browser, zim_file)
|
||||
zim_file = _data_dir / 'FreedomBox.zim'
|
||||
_add_package(session_browser, str(zim_file))
|
||||
functional.backup_create(session_browser, 'kiwix', 'test_kiwix')
|
||||
|
||||
_delete_content_package(session_browser, ZIM_ID)
|
||||
_delete_package(session_browser, ZIM_ID)
|
||||
functional.backup_restore(session_browser, 'kiwix', 'test_kiwix')
|
||||
|
||||
assert _is_content_package_listed(session_browser, 'freedombox')
|
||||
assert _is_content_package_available(session_browser, 'FreedomBox')
|
||||
assert _is_package_listed(session_browser, 'freedombox')
|
||||
assert _is_package_available(session_browser, 'FreedomBox')
|
||||
|
||||
def test_add_invalid_zim_file(self, session_browser):
|
||||
"""Test handling of invalid zim files."""
|
||||
functional.app_enable(session_browser, 'kiwix')
|
||||
|
||||
zim_file = pkg_resources.resource_filename(
|
||||
'plinth.modules.kiwix.tests', 'data/invalid.zim')
|
||||
_add_content_package(session_browser, zim_file)
|
||||
zim_file = _data_dir / 'invalid.zim'
|
||||
_add_package(session_browser, str(zim_file))
|
||||
|
||||
assert not _is_content_package_listed(session_browser, 'invalid')
|
||||
assert not _is_package_listed(session_browser, 'invalid')
|
||||
|
||||
|
||||
def _add_content_package(browser, file_name):
|
||||
browser.links.find_by_href('/plinth/apps/kiwix/content/add/').first.click()
|
||||
def _add_package(browser, file_name):
|
||||
"""Add a package by uploading the ZIM file in kiwix app page."""
|
||||
browser.links.find_by_href('/plinth/apps/kiwix/package/add/').first.click()
|
||||
browser.attach_file('kiwix-file', file_name)
|
||||
functional.submit(browser, form_class='form-kiwix')
|
||||
|
||||
|
||||
def _is_content_package_available(browser, title) -> bool:
|
||||
def _is_package_available(browser, title) -> bool:
|
||||
"""Check whether a ZIM file is available in Kiwix web interface."""
|
||||
browser.visit(f'{_default_url}/kiwix')
|
||||
sleep(1) # Allow time for the books to appear
|
||||
titles = browser.find_by_id('book__title')
|
||||
print(len(titles))
|
||||
print([title.value for title in titles])
|
||||
return any(map(lambda e: e.value == title, titles))
|
||||
return any(element.value == title for element in titles)
|
||||
|
||||
|
||||
def _is_content_package_listed(browser, name) -> bool:
|
||||
def _is_package_listed(browser, name) -> bool:
|
||||
"""Return whether a content package is list in kiwix app page."""
|
||||
functional.nav_to_module(browser, 'kiwix')
|
||||
links_found = browser.links.find_by_partial_href(f'/kiwix/viewer#{name}')
|
||||
return len(links_found) == 1
|
||||
|
||||
|
||||
def _delete_content_package(browser, zim_id):
|
||||
def _delete_package(browser, zim_id):
|
||||
"""Delete a content package from the kiwix app page."""
|
||||
functional.nav_to_module(browser, 'kiwix')
|
||||
link = browser.links.find_by_href(
|
||||
f'/plinth/apps/kiwix/content/{zim_id}/delete/')
|
||||
f'/plinth/apps/kiwix/package/{zim_id}/delete/')
|
||||
if not link:
|
||||
raise ValueError('ZIM file missing!')
|
||||
|
||||
link.first.click()
|
||||
functional.submit(browser, form_class='form-delete')
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Test module for Kiwix actions.
|
||||
"""
|
||||
|
||||
import pathlib
|
||||
import pkg_resources
|
||||
import shutil
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
@ -22,65 +22,57 @@ ZIM_ID = 'bc4f8cdf-5626-2b13-3860-0033deddfbea'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fixture_kiwix_home(tmpdir):
|
||||
"""Set Kiwix home to a new temporary directory
|
||||
initialized with an empty library file."""
|
||||
privileged.KIWIX_HOME = pathlib.Path(str(tmpdir / 'kiwix'))
|
||||
def fixture_kiwix_home(tmp_path):
|
||||
"""Create a new Kiwix home in a new temporary directory.
|
||||
|
||||
Initialize with a sample, valid library file.
|
||||
"""
|
||||
privileged.KIWIX_HOME = tmp_path / 'kiwix'
|
||||
privileged.KIWIX_HOME.mkdir()
|
||||
privileged.CONTENT_DIR = privileged.KIWIX_HOME / 'content'
|
||||
privileged.CONTENT_DIR.mkdir()
|
||||
privileged.LIBRARY_FILE = privileged.KIWIX_HOME / 'library_zim.xml'
|
||||
with open(privileged.LIBRARY_FILE, 'w', encoding='utf_8') as library_file:
|
||||
library_file.write(EMPTY_LIBRARY_CONTENTS)
|
||||
source_file = pathlib.Path(__file__).parent / 'data/sample_library_zim.xml'
|
||||
shutil.copy(source_file, privileged.LIBRARY_FILE)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def fixture_patch():
|
||||
"""Patch some underlying methods."""
|
||||
with patch('subprocess.check_call'), patch('subprocess.run'):
|
||||
with patch('subprocess.check_call'), patch('subprocess.run'), patch(
|
||||
'os.chown'):
|
||||
yield
|
||||
|
||||
|
||||
def test_add_content(tmpdir):
|
||||
def test_add_package(tmp_path):
|
||||
"""Test adding a content package to Kiwix."""
|
||||
some_dir = tmpdir / 'some' / 'dir'
|
||||
pathlib.Path(some_dir).mkdir(parents=True, exist_ok=True)
|
||||
some_dir = tmp_path / 'some' / 'dir'
|
||||
some_dir.mkdir(parents=True, exist_ok=True)
|
||||
zim_file_name = 'wikipedia_en_all_maxi_2022-05.zim'
|
||||
orig_file = some_dir / zim_file_name
|
||||
pathlib.Path(orig_file).touch()
|
||||
orig_file.touch()
|
||||
|
||||
privileged.add_content(str(orig_file))
|
||||
privileged.add_package(str(orig_file))
|
||||
assert (privileged.KIWIX_HOME / 'content' / zim_file_name).exists()
|
||||
assert not orig_file.exists()
|
||||
|
||||
|
||||
def test_list_content_packages():
|
||||
def test_list_packages():
|
||||
"""Test listing the content packages from a library file."""
|
||||
privileged.LIBRARY_FILE = pkg_resources.resource_filename(
|
||||
'plinth.modules.kiwix.tests', 'data/sample_library_zim.xml')
|
||||
content_packages = privileged.list_content_packages()
|
||||
assert content_packages[ZIM_ID] == {
|
||||
content = privileged.list_packages()
|
||||
assert content[ZIM_ID] == {
|
||||
'title': 'FreedomBox',
|
||||
'description': 'A sample content archive',
|
||||
'path': 'freedombox'
|
||||
}
|
||||
|
||||
|
||||
def test_delete_content_package():
|
||||
def test_delete_package():
|
||||
"""Test deleting one content package."""
|
||||
sample_library_file = pkg_resources.resource_filename(
|
||||
'plinth.modules.kiwix.tests', 'data/sample_library_zim.xml')
|
||||
|
||||
with open(sample_library_file, 'r',
|
||||
encoding='utf_8') as sample_library_file:
|
||||
with open(privileged.LIBRARY_FILE, 'w',
|
||||
encoding='utf_8') as library_file:
|
||||
library_file.write(sample_library_file.read())
|
||||
|
||||
zim_file = privileged.CONTENT_DIR / 'FreedomBox.zim'
|
||||
zim_file.touch()
|
||||
|
||||
privileged.delete_content_package(ZIM_ID)
|
||||
privileged.delete_package(ZIM_ID)
|
||||
|
||||
assert not zim_file.exists()
|
||||
# Cannot check that the book is removed from library_zim.xml
|
||||
|
||||
@ -3,19 +3,18 @@
|
||||
Test module for Kiwix validations.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from plinth.modules import kiwix
|
||||
|
||||
|
||||
class TestValidations(unittest.TestCase):
|
||||
def test_add_file_with_invalid_extension():
|
||||
"""Test that adding a file with invalid fails as expected."""
|
||||
with pytest.raises(ValueError):
|
||||
kiwix.validate_file_name('wikipedia.zip')
|
||||
|
||||
def test_add_file_with_invalid_extension(self):
|
||||
self.assertRaises(ValueError,
|
||||
lambda: kiwix.validate_file_name('wikipedia.zip'))
|
||||
# We don't support the legacy format of split zim files.
|
||||
with pytest.raises(ValueError):
|
||||
kiwix.validate_file_name('wikipedia_en_all_maxi_2022-05.zima')
|
||||
|
||||
# We don't support the legacy format of split zim files.
|
||||
self.assertRaises(
|
||||
ValueError, lambda: kiwix.validate_file_name(
|
||||
'wikipedia_en_all_maxi_2022-05.zima'))
|
||||
|
||||
kiwix.validate_file_name('wikipedia_en_all_maxi_2022-05.zim')
|
||||
kiwix.validate_file_name('wikipedia_en_all_maxi_2022-05.zim')
|
||||
|
||||
@ -3,15 +3,15 @@
|
||||
Test module for Kiwix views.
|
||||
"""
|
||||
|
||||
from plinth import module_loader
|
||||
from django import urls
|
||||
import pathlib
|
||||
from unittest.mock import call, patch
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.http.response import Http404
|
||||
from django.test.client import encode_multipart, RequestFactory
|
||||
|
||||
import pytest
|
||||
from django import urls
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.http.response import Http404
|
||||
|
||||
from plinth import module_loader
|
||||
from plinth.modules.kiwix import views
|
||||
|
||||
# For all tests, use plinth.urls instead of urls configured for testing
|
||||
@ -19,6 +19,8 @@ pytestmark = pytest.mark.urls('plinth.urls')
|
||||
|
||||
ZIM_ID = 'bc4f8cdf-5626-2b13-3860-0033deddfbea'
|
||||
|
||||
_data_dir = pathlib.Path(__file__).parent / 'data'
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True, scope='module')
|
||||
def fixture_kiwix_urls():
|
||||
@ -41,107 +43,88 @@ def make_request(request, view, **kwargs):
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def kiwix_patch():
|
||||
def fiture_kiwix_patch():
|
||||
"""Patch kiwix methods."""
|
||||
with patch('plinth.modules.kiwix.privileged.list_content_packages'
|
||||
) as list_libraries:
|
||||
with patch(
|
||||
'plinth.modules.kiwix.privileged.list_packages') as list_libraries:
|
||||
list_libraries.return_value = {
|
||||
ZIM_ID: {
|
||||
'title': 'TestExistingContentPackage',
|
||||
'title': 'TestExistingPackage',
|
||||
'description': 'A sample content package',
|
||||
'path': 'test_existing_content_package'
|
||||
'path': 'test_existing_package'
|
||||
}
|
||||
}
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def storage_info_patch():
|
||||
"""Patch storage info method."""
|
||||
with patch('plinth.modules.storage.get_mount_info') as get_mount_info:
|
||||
get_mount_info.return_value = {'free_bytes': 1000000000000}
|
||||
yield
|
||||
|
||||
|
||||
@patch('plinth.modules.kiwix.privileged.add_content')
|
||||
def test_add_content_package(add_content, rf):
|
||||
@patch('tempfile.TemporaryDirectory')
|
||||
@patch('plinth.modules.kiwix.privileged.add_package')
|
||||
def test_add_package(add_package, temp_dir_class, rf, tmp_path):
|
||||
"""Test that adding content view works."""
|
||||
with open('plinth/modules/kiwix/tests/data/FreedomBox.zim',
|
||||
'rb') as zim_file:
|
||||
post_data = {
|
||||
'kiwix-file': zim_file,
|
||||
}
|
||||
post_data = encode_multipart('BoUnDaRyStRiNg', post_data)
|
||||
request = rf.post(
|
||||
'', data=post_data, content_type='multipart/form-data; '
|
||||
'boundary=BoUnDaRyStRiNg')
|
||||
temp_dir_class.return_value.__enter__.return_value = str(tmp_path)
|
||||
with open(_data_dir / 'FreedomBox.zim', 'rb') as zim_file:
|
||||
post_data = {'kiwix-file': zim_file}
|
||||
request = rf.post('', data=post_data)
|
||||
response, messages = make_request(request,
|
||||
views.AddContentView.as_view())
|
||||
views.AddPackageView.as_view())
|
||||
assert response.status_code == 302
|
||||
assert response.url == urls.reverse('kiwix:index')
|
||||
assert list(messages)[0].message == 'Content package added.'
|
||||
add_content.assert_has_calls([call('/tmp/FreedomBox.zim')])
|
||||
add_package.assert_has_calls([call(f'{tmp_path}/FreedomBox.zim')])
|
||||
|
||||
|
||||
@patch('plinth.modules.kiwix.privileged.add_content')
|
||||
def test_add_content_package_failed(add_content, rf):
|
||||
@patch('plinth.modules.kiwix.privileged.add_package')
|
||||
def test_add_package_failed(add_package, rf):
|
||||
"""Test that adding content package fails in case of an error."""
|
||||
add_content.side_effect = RuntimeError('TestError')
|
||||
with open('plinth/modules/kiwix/tests/data/FreedomBox.zim',
|
||||
'rb') as zim_file:
|
||||
post_data = {
|
||||
'kiwix-file': zim_file,
|
||||
}
|
||||
post_data = encode_multipart('BoUnDaRyStRiNg', post_data)
|
||||
request = rf.post(
|
||||
'', data=post_data, content_type='multipart/form-data; '
|
||||
'boundary=BoUnDaRyStRiNg')
|
||||
add_package.side_effect = RuntimeError('TestError')
|
||||
with open(_data_dir / 'FreedomBox.zim', 'rb') as zim_file:
|
||||
post_data = {'kiwix-file': zim_file}
|
||||
request = rf.post('', data=post_data)
|
||||
response, messages = make_request(request,
|
||||
views.AddContentView.as_view())
|
||||
views.AddPackageView.as_view())
|
||||
assert response.status_code == 302
|
||||
assert response.url == urls.reverse('kiwix:index')
|
||||
assert list(messages)[0].message == \
|
||||
'Failed to add content package.'
|
||||
assert list(messages)[0].message == 'Failed to add content package.'
|
||||
|
||||
|
||||
@patch('plinth.app.App.get')
|
||||
def test_delete_package_confirmation_view(_app, rf):
|
||||
"""Test that deleting package confirmation shows correct title."""
|
||||
response, _ = make_request(rf.get(''), views.delete_content, zim_id=ZIM_ID)
|
||||
"""Test that deleting content confirmation shows correct title."""
|
||||
response, _ = make_request(rf.get(''), views.delete_package, zim_id=ZIM_ID)
|
||||
assert response.status_code == 200
|
||||
assert response.context_data['name'] == 'TestExistingContentPackage'
|
||||
assert response.context_data['name'] == 'TestExistingPackage'
|
||||
|
||||
|
||||
@patch('plinth.modules.kiwix.privileged.delete_content_package')
|
||||
@patch('plinth.modules.kiwix.privileged.delete_package')
|
||||
@patch('plinth.app.App.get')
|
||||
def test_delete_content_package(_app, delete_content_package, rf):
|
||||
def test_delete_package(_app, delete_package, rf):
|
||||
"""Test that deleting a content package works."""
|
||||
response, messages = make_request(rf.post(''), views.delete_content,
|
||||
response, messages = make_request(rf.post(''), views.delete_package,
|
||||
zim_id=ZIM_ID)
|
||||
assert response.status_code == 302
|
||||
assert response.url == urls.reverse('kiwix:index')
|
||||
assert list(messages)[0].message == 'TestExistingContentPackage deleted.'
|
||||
delete_content_package.assert_has_calls([call(ZIM_ID)])
|
||||
assert list(messages)[0].message == 'TestExistingPackage deleted.'
|
||||
delete_package.assert_has_calls([call(ZIM_ID)])
|
||||
|
||||
|
||||
@patch('plinth.modules.kiwix.privileged.delete_content_package')
|
||||
def test_delete_content_package_error(delete_content_package, rf):
|
||||
"""Test that deleting a content package shows an error when operation fails."""
|
||||
delete_content_package.side_effect = ValueError('TestError')
|
||||
response, messages = make_request(rf.post(''), views.delete_content,
|
||||
@patch('plinth.modules.kiwix.privileged.delete_package')
|
||||
def test_delete_package_error(delete_package, rf):
|
||||
"""Test that deleting content shows an error when operation fails."""
|
||||
delete_package.side_effect = ValueError('TestError')
|
||||
response, messages = make_request(rf.post(''), views.delete_package,
|
||||
zim_id=ZIM_ID)
|
||||
assert response.status_code == 302
|
||||
assert response.url == urls.reverse('kiwix:index')
|
||||
assert list(messages)[0].message == \
|
||||
'Could not delete TestExistingContentPackage: TestError'
|
||||
'Could not delete TestExistingPackage: TestError'
|
||||
|
||||
|
||||
def test_delete_content_package_non_existing(rf):
|
||||
"""Test that deleting a content package shows error when operation fails."""
|
||||
def test_delete_package_non_existing(rf):
|
||||
"""Test that deleting content shows error when operation fails."""
|
||||
with pytest.raises(Http404):
|
||||
make_request(rf.post(''), views.delete_content,
|
||||
make_request(rf.post(''), views.delete_package,
|
||||
zim_id='NonExistentZimId')
|
||||
|
||||
with pytest.raises(Http404):
|
||||
make_request(rf.get(''), views.delete_content,
|
||||
make_request(rf.get(''), views.delete_package,
|
||||
zim_id='NonExistentZimId')
|
||||
|
||||
@ -9,8 +9,8 @@ from . import views
|
||||
|
||||
urlpatterns = [
|
||||
re_path(r'^apps/kiwix/$', views.KiwixAppView.as_view(), name='index'),
|
||||
re_path(r'^apps/kiwix/content/add/$', views.AddContentView.as_view(),
|
||||
name='add-content'),
|
||||
re_path(r'^apps/kiwix/content/(?P<zim_id>[a-zA-Z0-9-]+)/delete/$',
|
||||
views.delete_content, name='delete-content'),
|
||||
re_path(r'^apps/kiwix/package/add/$', views.AddPackageView.as_view(),
|
||||
name='add-package'),
|
||||
re_path(r'^apps/kiwix/package/(?P<zim_id>[a-zA-Z0-9-]+)/delete/$',
|
||||
views.delete_package, name='delete-package'),
|
||||
]
|
||||
|
||||
@ -4,6 +4,7 @@ Views for the Kiwix module.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import tempfile
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
@ -27,21 +28,23 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class KiwixAppView(views.AppView):
|
||||
"""Serve configuration form."""
|
||||
|
||||
app_id = 'kiwix'
|
||||
template_name = 'kiwix.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Return additional context for rendering the template."""
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['packages'] = privileged.list_content_packages()
|
||||
context['packages'] = privileged.list_packages()
|
||||
return context
|
||||
|
||||
|
||||
class AddContentView(SuccessMessageMixin, FormView):
|
||||
"""View to add content in the form of ZIM files."""
|
||||
form_class = forms.AddContentForm
|
||||
class AddPackageView(SuccessMessageMixin, FormView):
|
||||
"""View to add content package in the form of ZIM files."""
|
||||
|
||||
form_class = forms.AddPackageForm
|
||||
prefix = 'kiwix'
|
||||
template_name = 'add-content-package.html'
|
||||
template_name = 'kiwix-add-package.html'
|
||||
success_url = reverse_lazy('kiwix:index')
|
||||
success_message = _('Content package added.')
|
||||
|
||||
@ -66,23 +69,26 @@ class AddContentView(SuccessMessageMixin, FormView):
|
||||
def form_valid(self, form):
|
||||
"""Store the uploaded file."""
|
||||
multipart_file = self.request.FILES['kiwix-file']
|
||||
zim_file_name = '/tmp/' + multipart_file.name
|
||||
with open(zim_file_name, 'wb+') as zim_file:
|
||||
for chunk in multipart_file.chunks():
|
||||
zim_file.write(chunk)
|
||||
|
||||
try:
|
||||
privileged.add_content(zim_file_name)
|
||||
except Exception:
|
||||
messages.error(self.request, _('Failed to add content package.'))
|
||||
return redirect(reverse_lazy('kiwix:index'))
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
zim_file_name = temp_dir + '/' + multipart_file.name
|
||||
with open(zim_file_name, 'wb+') as zim_file:
|
||||
for chunk in multipart_file.chunks():
|
||||
zim_file.write(chunk)
|
||||
|
||||
try:
|
||||
privileged.add_package(zim_file_name)
|
||||
except Exception:
|
||||
messages.error(self.request,
|
||||
_('Failed to add content package.'))
|
||||
return redirect(reverse_lazy('kiwix:index'))
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def delete_content(request, zim_id):
|
||||
def delete_package(request, zim_id):
|
||||
"""View to delete a library."""
|
||||
packages = privileged.list_content_packages()
|
||||
packages = privileged.list_packages()
|
||||
if zim_id not in packages:
|
||||
raise Http404
|
||||
|
||||
@ -90,8 +96,8 @@ def delete_content(request, zim_id):
|
||||
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
privileged.delete_content_package(zim_id)
|
||||
messages.success(request, _(f'{name} deleted.'))
|
||||
privileged.delete_package(zim_id)
|
||||
messages.success(request, _('{name} deleted.').format(name=name))
|
||||
except Exception as error:
|
||||
messages.error(
|
||||
request,
|
||||
@ -99,7 +105,7 @@ def delete_content(request, zim_id):
|
||||
name=name, error=error))
|
||||
return redirect(reverse_lazy('kiwix:index'))
|
||||
|
||||
return TemplateResponse(request, 'delete-content-package.html', {
|
||||
return TemplateResponse(request, 'kiwix-delete-package.html', {
|
||||
'title': app_module.App.get('kiwix').info.name,
|
||||
'name': name
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user