forked from FrederikBaerentsen/BrickTracker
Split the uncomfortably big admin view into smaller admin views
This commit is contained in:
parent
e2bcd61ace
commit
798226932f
@ -12,7 +12,13 @@ from bricktracker.navbar import Navbar
|
|||||||
from bricktracker.sql import close
|
from bricktracker.sql import close
|
||||||
from bricktracker.version import __version__
|
from bricktracker.version import __version__
|
||||||
from bricktracker.views.add import add_page
|
from bricktracker.views.add import add_page
|
||||||
from bricktracker.views.admin import admin_page
|
from bricktracker.views.admin.admin import admin_page
|
||||||
|
from bricktracker.views.admin.checkbox import admin_checkbox_page
|
||||||
|
from bricktracker.views.admin.database import admin_database_page
|
||||||
|
from bricktracker.views.admin.image import admin_image_page
|
||||||
|
from bricktracker.views.admin.instructions import admin_instructions_page
|
||||||
|
from bricktracker.views.admin.retired import admin_retired_page
|
||||||
|
from bricktracker.views.admin.theme import admin_theme_page
|
||||||
from bricktracker.views.error import error_404
|
from bricktracker.views.error import error_404
|
||||||
from bricktracker.views.index import index_page
|
from bricktracker.views.index import index_page
|
||||||
from bricktracker.views.instructions import instructions_page
|
from bricktracker.views.instructions import instructions_page
|
||||||
@ -60,9 +66,8 @@ def setup_app(app: Flask) -> None:
|
|||||||
# Register errors
|
# Register errors
|
||||||
app.register_error_handler(404, error_404)
|
app.register_error_handler(404, error_404)
|
||||||
|
|
||||||
# Register routes
|
# Register app routes
|
||||||
app.register_blueprint(add_page)
|
app.register_blueprint(add_page)
|
||||||
app.register_blueprint(admin_page)
|
|
||||||
app.register_blueprint(index_page)
|
app.register_blueprint(index_page)
|
||||||
app.register_blueprint(instructions_page)
|
app.register_blueprint(instructions_page)
|
||||||
app.register_blueprint(login_page)
|
app.register_blueprint(login_page)
|
||||||
@ -71,6 +76,15 @@ def setup_app(app: Flask) -> None:
|
|||||||
app.register_blueprint(set_page)
|
app.register_blueprint(set_page)
|
||||||
app.register_blueprint(wish_page)
|
app.register_blueprint(wish_page)
|
||||||
|
|
||||||
|
# Register admin routes
|
||||||
|
app.register_blueprint(admin_page)
|
||||||
|
app.register_blueprint(admin_checkbox_page)
|
||||||
|
app.register_blueprint(admin_database_page)
|
||||||
|
app.register_blueprint(admin_image_page)
|
||||||
|
app.register_blueprint(admin_instructions_page)
|
||||||
|
app.register_blueprint(admin_retired_page)
|
||||||
|
app.register_blueprint(admin_theme_page)
|
||||||
|
|
||||||
# An helper to make global variables available to the
|
# An helper to make global variables available to the
|
||||||
# request
|
# request
|
||||||
@app.before_request
|
@app.before_request
|
||||||
|
19
bricktracker/reload.py
Normal file
19
bricktracker/reload.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from .instructions_list import BrickInstructionsList
|
||||||
|
from .retired_list import BrickRetiredList
|
||||||
|
from .theme_list import BrickThemeList
|
||||||
|
|
||||||
|
|
||||||
|
# Reload everything related to a database after an operation
|
||||||
|
def reload() -> None:
|
||||||
|
# Failsafe
|
||||||
|
try:
|
||||||
|
# Reload the instructions
|
||||||
|
BrickInstructionsList(force=True)
|
||||||
|
|
||||||
|
# Reload retired sets
|
||||||
|
BrickRetiredList(force=True)
|
||||||
|
|
||||||
|
# Reload themes
|
||||||
|
BrickThemeList(force=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
@ -1,334 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from flask import (
|
|
||||||
Blueprint,
|
|
||||||
current_app,
|
|
||||||
g,
|
|
||||||
redirect,
|
|
||||||
request,
|
|
||||||
render_template,
|
|
||||||
send_file,
|
|
||||||
url_for,
|
|
||||||
)
|
|
||||||
from flask_login import login_required
|
|
||||||
from werkzeug.wrappers.response import Response
|
|
||||||
|
|
||||||
from ..configuration_list import BrickConfigurationList
|
|
||||||
from .exceptions import exception_handler
|
|
||||||
from ..instructions_list import BrickInstructionsList
|
|
||||||
from ..minifigure import BrickMinifigure
|
|
||||||
from ..part import BrickPart
|
|
||||||
from ..rebrickable_image import RebrickableImage
|
|
||||||
from ..retired_list import BrickRetiredList
|
|
||||||
from ..set import BrickSet
|
|
||||||
from ..sql_counter import BrickCounter
|
|
||||||
from ..sql_migration_list import BrickSQLMigrationList
|
|
||||||
from ..sql import BrickSQL
|
|
||||||
from ..theme_list import BrickThemeList
|
|
||||||
from .upload import upload_helper
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
admin_page = Blueprint('admin', __name__, url_prefix='/admin')
|
|
||||||
|
|
||||||
|
|
||||||
# Admin
|
|
||||||
@admin_page.route('/', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def admin() -> str:
|
|
||||||
database_counters: list[BrickCounter] = []
|
|
||||||
database_exception: Exception | None = None
|
|
||||||
database_upgrade_needed: bool = False
|
|
||||||
database_version: int = -1
|
|
||||||
nil_minifigure_name: str = ''
|
|
||||||
nil_minifigure_url: str = ''
|
|
||||||
nil_part_name: str = ''
|
|
||||||
nil_part_url: str = ''
|
|
||||||
|
|
||||||
# This view needs to be protected against SQL errors
|
|
||||||
try:
|
|
||||||
database = BrickSQL(failsafe=True)
|
|
||||||
database_upgrade_needed = database.upgrade_needed()
|
|
||||||
database_version = database.version
|
|
||||||
|
|
||||||
if not database_upgrade_needed:
|
|
||||||
database_counters = BrickSQL().count_records()
|
|
||||||
except Exception as e:
|
|
||||||
database_exception = e
|
|
||||||
|
|
||||||
# Warning
|
|
||||||
logger.warning('A database exception occured while loading the admin page: {exception}'.format( # noqa: E501
|
|
||||||
exception=str(e),
|
|
||||||
))
|
|
||||||
|
|
||||||
nil_minifigure_name = RebrickableImage.nil_minifigure_name()
|
|
||||||
nil_minifigure_url = RebrickableImage.static_url(
|
|
||||||
nil_minifigure_name,
|
|
||||||
'MINIFIGURES_FOLDER'
|
|
||||||
)
|
|
||||||
|
|
||||||
nil_part_name = RebrickableImage.nil_name()
|
|
||||||
nil_part_url = RebrickableImage.static_url(
|
|
||||||
nil_part_name,
|
|
||||||
'PARTS_FOLDER'
|
|
||||||
)
|
|
||||||
|
|
||||||
open_image = request.args.get('open_image', None)
|
|
||||||
open_instructions = request.args.get('open_instructions', None)
|
|
||||||
open_logout = request.args.get('open_logout', None)
|
|
||||||
open_retired = request.args.get('open_retired', None)
|
|
||||||
open_theme = request.args.get('open_theme', None)
|
|
||||||
|
|
||||||
open_database = (
|
|
||||||
open_image is None and
|
|
||||||
open_instructions is None and
|
|
||||||
open_logout is None and
|
|
||||||
open_retired is None and
|
|
||||||
open_theme is None
|
|
||||||
)
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
'admin.html',
|
|
||||||
configuration=BrickConfigurationList.list(),
|
|
||||||
database_counters=database_counters,
|
|
||||||
database_error=request.args.get('error'),
|
|
||||||
database_exception=database_exception,
|
|
||||||
database_upgrade_needed=database_upgrade_needed,
|
|
||||||
database_version=database_version,
|
|
||||||
instructions=BrickInstructionsList(),
|
|
||||||
nil_minifigure_name=nil_minifigure_name,
|
|
||||||
nil_minifigure_url=nil_minifigure_url,
|
|
||||||
nil_part_name=nil_part_name,
|
|
||||||
nil_part_url=nil_part_url,
|
|
||||||
open_database=open_database,
|
|
||||||
open_image=open_image,
|
|
||||||
open_instructions=open_instructions,
|
|
||||||
open_logout=open_logout,
|
|
||||||
open_retired=open_retired,
|
|
||||||
open_theme=open_theme,
|
|
||||||
retired=BrickRetiredList(),
|
|
||||||
theme=BrickThemeList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Delete the database
|
|
||||||
@admin_page.route('/delete-database', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def delete_database() -> str:
|
|
||||||
return render_template(
|
|
||||||
'admin.html',
|
|
||||||
delete_database=True,
|
|
||||||
error=request.args.get('error')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Actually delete the database
|
|
||||||
@admin_page.route('/delete-database', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__, post_redirect='admin.delete_database')
|
|
||||||
def do_delete_database() -> Response:
|
|
||||||
BrickSQL.delete()
|
|
||||||
|
|
||||||
# Reload the instructions
|
|
||||||
BrickInstructionsList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin'))
|
|
||||||
|
|
||||||
|
|
||||||
# Download the database
|
|
||||||
@admin_page.route('/download-database', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def download_database() -> Response:
|
|
||||||
# Create a file name with a timestamp embedded
|
|
||||||
name, extension = os.path.splitext(
|
|
||||||
os.path.basename(current_app.config['DATABASE_PATH'])
|
|
||||||
)
|
|
||||||
|
|
||||||
# Info
|
|
||||||
logger.info('The database has been downloaded')
|
|
||||||
|
|
||||||
return send_file(
|
|
||||||
current_app.config['DATABASE_PATH'],
|
|
||||||
as_attachment=True,
|
|
||||||
download_name='{name}-v{version}-{timestamp}{extension}'.format(
|
|
||||||
name=name,
|
|
||||||
version=BrickSQL(failsafe=True).version,
|
|
||||||
timestamp=datetime.now().astimezone(g.timezone).strftime(
|
|
||||||
current_app.config['DATABASE_TIMESTAMP_FORMAT']
|
|
||||||
),
|
|
||||||
extension=extension
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Drop the database
|
|
||||||
@admin_page.route('/drop-database', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def drop_database() -> str:
|
|
||||||
return render_template(
|
|
||||||
'admin.html',
|
|
||||||
drop_database=True,
|
|
||||||
error=request.args.get('error')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Actually drop the database
|
|
||||||
@admin_page.route('/drop-database', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__, post_redirect='admin.drop_database')
|
|
||||||
def do_drop_database() -> Response:
|
|
||||||
BrickSQL.drop()
|
|
||||||
|
|
||||||
# Reload the instructions
|
|
||||||
BrickInstructionsList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin'))
|
|
||||||
|
|
||||||
|
|
||||||
# Actually upgrade the database
|
|
||||||
@admin_page.route('/upgrade-database', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__, post_redirect='admin.upgrade_database')
|
|
||||||
def do_upgrade_database() -> Response:
|
|
||||||
BrickSQL(failsafe=True).upgrade()
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin'))
|
|
||||||
|
|
||||||
|
|
||||||
# Import a database
|
|
||||||
@admin_page.route('/import-database', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def import_database() -> str:
|
|
||||||
return render_template(
|
|
||||||
'admin.html',
|
|
||||||
import_database=True,
|
|
||||||
error=request.args.get('error')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Actually import a database
|
|
||||||
@admin_page.route('/import-database', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__, post_redirect='admin.import_database')
|
|
||||||
def do_import_database() -> Response:
|
|
||||||
file = upload_helper(
|
|
||||||
'database',
|
|
||||||
'admin.import_database',
|
|
||||||
extensions=['.db'],
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(file, Response):
|
|
||||||
return file
|
|
||||||
|
|
||||||
BrickSQL.upload(file)
|
|
||||||
|
|
||||||
# Reload the instructions
|
|
||||||
BrickInstructionsList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin'))
|
|
||||||
|
|
||||||
|
|
||||||
# Refresh the instructions cache
|
|
||||||
@admin_page.route('/refresh-instructions', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def refresh_instructions() -> Response:
|
|
||||||
BrickInstructionsList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_instructions=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Refresh the retired sets cache
|
|
||||||
@admin_page.route('/refresh-retired', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def refresh_retired() -> Response:
|
|
||||||
BrickRetiredList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_retired=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Refresh the themes cache
|
|
||||||
@admin_page.route('/refresh-themes', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def refresh_themes() -> Response:
|
|
||||||
BrickThemeList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_theme=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Update the default images
|
|
||||||
@admin_page.route('/update-image', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def update_image() -> Response:
|
|
||||||
# Abusing the object to create a 'nil' minifigure
|
|
||||||
RebrickableImage(
|
|
||||||
BrickSet(),
|
|
||||||
minifigure=BrickMinifigure(record={
|
|
||||||
'set_img_url': None,
|
|
||||||
})
|
|
||||||
).download()
|
|
||||||
|
|
||||||
# Abusing the object to create a 'nil' part
|
|
||||||
RebrickableImage(
|
|
||||||
BrickSet(),
|
|
||||||
part=BrickPart(record={
|
|
||||||
'part_img_url': None,
|
|
||||||
'part_img_url_id': None
|
|
||||||
})
|
|
||||||
).download()
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_image=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Update the themes file
|
|
||||||
@admin_page.route('/update-retired', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def update_retired() -> Response:
|
|
||||||
BrickRetiredList().update()
|
|
||||||
|
|
||||||
BrickRetiredList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_retired=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Update the themes file
|
|
||||||
@admin_page.route('/update-themes', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__)
|
|
||||||
def update_themes() -> Response:
|
|
||||||
BrickThemeList().update()
|
|
||||||
|
|
||||||
BrickThemeList(force=True)
|
|
||||||
|
|
||||||
return redirect(url_for('admin.admin', open_theme=True))
|
|
||||||
|
|
||||||
|
|
||||||
# Upgrade the database
|
|
||||||
@admin_page.route('/upgrade-database', methods=['GET'])
|
|
||||||
@login_required
|
|
||||||
@exception_handler(__file__, post_redirect='admin.admin')
|
|
||||||
def upgrade_database() -> str | Response:
|
|
||||||
database = BrickSQL(failsafe=True)
|
|
||||||
|
|
||||||
if not database.upgrade_needed():
|
|
||||||
return redirect(url_for('admin.admin'))
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
'admin.html',
|
|
||||||
upgrade_database=True,
|
|
||||||
migrations=BrickSQLMigrationList().pending(
|
|
||||||
database.version
|
|
||||||
),
|
|
||||||
error=request.args.get('error')
|
|
||||||
)
|
|
0
bricktracker/views/admin/__init__.py
Normal file
0
bricktracker/views/admin/__init__.py
Normal file
104
bricktracker/views/admin/admin.py
Normal file
104
bricktracker/views/admin/admin.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from flask import Blueprint, request, render_template
|
||||||
|
from flask_login import login_required
|
||||||
|
|
||||||
|
from ...configuration_list import BrickConfigurationList
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...instructions_list import BrickInstructionsList
|
||||||
|
from ...rebrickable_image import RebrickableImage
|
||||||
|
from ...retired_list import BrickRetiredList
|
||||||
|
from ...set_checkbox import BrickSetCheckbox
|
||||||
|
from ...set_checkbox_list import BrickSetCheckboxList
|
||||||
|
from ...sql_counter import BrickCounter
|
||||||
|
from ...sql import BrickSQL
|
||||||
|
from ...theme_list import BrickThemeList
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_page = Blueprint('admin', __name__, url_prefix='/admin')
|
||||||
|
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
@admin_page.route('/', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def admin() -> str:
|
||||||
|
brickset_checkboxes: list[BrickSetCheckbox] = []
|
||||||
|
database_counters: list[BrickCounter] = []
|
||||||
|
database_exception: Exception | None = None
|
||||||
|
database_upgrade_needed: bool = False
|
||||||
|
database_version: int = -1
|
||||||
|
nil_minifigure_name: str = ''
|
||||||
|
nil_minifigure_url: str = ''
|
||||||
|
nil_part_name: str = ''
|
||||||
|
nil_part_url: str = ''
|
||||||
|
|
||||||
|
# This view needs to be protected against SQL errors
|
||||||
|
try:
|
||||||
|
database = BrickSQL(failsafe=True)
|
||||||
|
database_upgrade_needed = database.upgrade_needed()
|
||||||
|
database_version = database.version
|
||||||
|
database_counters = BrickSQL().count_records()
|
||||||
|
|
||||||
|
brickset_checkboxes = BrickSetCheckboxList().list(all=True)
|
||||||
|
except Exception as e:
|
||||||
|
database_exception = e
|
||||||
|
|
||||||
|
# Warning
|
||||||
|
logger.warning('A database exception occured while loading the admin page: {exception}'.format( # noqa: E501
|
||||||
|
exception=str(e),
|
||||||
|
))
|
||||||
|
|
||||||
|
nil_minifigure_name = RebrickableImage.nil_minifigure_name()
|
||||||
|
nil_minifigure_url = RebrickableImage.static_url(
|
||||||
|
nil_minifigure_name,
|
||||||
|
'MINIFIGURES_FOLDER'
|
||||||
|
)
|
||||||
|
|
||||||
|
nil_part_name = RebrickableImage.nil_name()
|
||||||
|
nil_part_url = RebrickableImage.static_url(
|
||||||
|
nil_part_name,
|
||||||
|
'PARTS_FOLDER'
|
||||||
|
)
|
||||||
|
|
||||||
|
open_checkbox = request.args.get('open_checkbox', None)
|
||||||
|
open_image = request.args.get('open_image', None)
|
||||||
|
open_instructions = request.args.get('open_instructions', None)
|
||||||
|
open_logout = request.args.get('open_logout', None)
|
||||||
|
open_retired = request.args.get('open_retired', None)
|
||||||
|
open_theme = request.args.get('open_theme', None)
|
||||||
|
|
||||||
|
open_database = (
|
||||||
|
open_checkbox is None and
|
||||||
|
open_image is None and
|
||||||
|
open_instructions is None and
|
||||||
|
open_logout is None and
|
||||||
|
open_retired is None and
|
||||||
|
open_theme is None
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
configuration=BrickConfigurationList.list(),
|
||||||
|
brickset_checkboxes=brickset_checkboxes,
|
||||||
|
database_counters=database_counters,
|
||||||
|
database_error=request.args.get('error'),
|
||||||
|
database_exception=database_exception,
|
||||||
|
database_upgrade_needed=database_upgrade_needed,
|
||||||
|
database_version=database_version,
|
||||||
|
instructions=BrickInstructionsList(),
|
||||||
|
nil_minifigure_name=nil_minifigure_name,
|
||||||
|
nil_minifigure_url=nil_minifigure_url,
|
||||||
|
nil_part_name=nil_part_name,
|
||||||
|
nil_part_url=nil_part_url,
|
||||||
|
open_checkbox=open_checkbox,
|
||||||
|
open_database=open_database,
|
||||||
|
open_image=open_image,
|
||||||
|
open_instructions=open_instructions,
|
||||||
|
open_logout=open_logout,
|
||||||
|
open_retired=open_retired,
|
||||||
|
open_theme=open_theme,
|
||||||
|
retired=BrickRetiredList(),
|
||||||
|
theme=BrickThemeList(),
|
||||||
|
)
|
170
bricktracker/views/admin/database.py
Normal file
170
bricktracker/views/admin/database.py
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
current_app,
|
||||||
|
g,
|
||||||
|
redirect,
|
||||||
|
request,
|
||||||
|
render_template,
|
||||||
|
send_file,
|
||||||
|
url_for,
|
||||||
|
)
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...reload import reload
|
||||||
|
from ...sql_migration_list import BrickSQLMigrationList
|
||||||
|
from ...sql import BrickSQL
|
||||||
|
from ..upload import upload_helper
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_database_page = Blueprint(
|
||||||
|
'admin_database',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/database'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Delete the database
|
||||||
|
@admin_database_page.route('/delete', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def delete() -> str:
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
delete_database=True,
|
||||||
|
error=request.args.get('error')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Actually delete the database
|
||||||
|
@admin_database_page.route('/delete', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, post_redirect='admin_database.delete')
|
||||||
|
def do_delete() -> Response:
|
||||||
|
BrickSQL.delete()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
|
||||||
|
# Download the database
|
||||||
|
@admin_database_page.route('/download', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def download() -> Response:
|
||||||
|
# Create a file name with a timestamp embedded
|
||||||
|
name, extension = os.path.splitext(
|
||||||
|
os.path.basename(current_app.config['DATABASE_PATH'])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Info
|
||||||
|
logger.info('The database has been downloaded')
|
||||||
|
|
||||||
|
return send_file(
|
||||||
|
current_app.config['DATABASE_PATH'],
|
||||||
|
as_attachment=True,
|
||||||
|
download_name='{name}-v{version}-{timestamp}{extension}'.format(
|
||||||
|
name=name,
|
||||||
|
version=BrickSQL(failsafe=True).version,
|
||||||
|
timestamp=datetime.now().astimezone(g.timezone).strftime(
|
||||||
|
current_app.config['DATABASE_TIMESTAMP_FORMAT']
|
||||||
|
),
|
||||||
|
extension=extension
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Drop the database
|
||||||
|
@admin_database_page.route('/drop', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def drop() -> str:
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
drop_database=True,
|
||||||
|
error=request.args.get('error')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Actually drop the database
|
||||||
|
@admin_database_page.route('/drop', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, post_redirect='admin_database.drop')
|
||||||
|
def do_drop() -> Response:
|
||||||
|
BrickSQL.drop()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
|
||||||
|
# Actually upgrade the database
|
||||||
|
@admin_database_page.route('/upgrade', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, post_redirect='admin_database.upgrade')
|
||||||
|
def do_upgrade() -> Response:
|
||||||
|
BrickSQL(failsafe=True).upgrade()
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
|
||||||
|
# Import a database
|
||||||
|
@admin_database_page.route('/import', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def upload() -> str:
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
import_database=True,
|
||||||
|
error=request.args.get('error')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Actually import a database
|
||||||
|
@admin_database_page.route('/import', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, post_redirect='admin_database.upload')
|
||||||
|
def do_upload() -> Response:
|
||||||
|
file = upload_helper(
|
||||||
|
'database',
|
||||||
|
'admin_database.upload',
|
||||||
|
extensions=['.db'],
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(file, Response):
|
||||||
|
return file
|
||||||
|
|
||||||
|
BrickSQL.upload(file)
|
||||||
|
|
||||||
|
reload()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
|
||||||
|
# Upgrade the database
|
||||||
|
@admin_database_page.route('/upgrade', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__, post_redirect='admin.admin')
|
||||||
|
def upgrade() -> str | Response:
|
||||||
|
database = BrickSQL(failsafe=True)
|
||||||
|
|
||||||
|
if not database.upgrade_needed():
|
||||||
|
return redirect(url_for('admin.admin'))
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'admin.html',
|
||||||
|
upgrade_database=True,
|
||||||
|
migrations=BrickSQLMigrationList().pending(
|
||||||
|
database.version
|
||||||
|
),
|
||||||
|
error=request.args.get('error')
|
||||||
|
)
|
44
bricktracker/views/admin/image.py
Normal file
44
bricktracker/views/admin/image.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from flask import Blueprint, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...minifigure import BrickMinifigure
|
||||||
|
from ...part import BrickPart
|
||||||
|
from ...rebrickable_image import RebrickableImage
|
||||||
|
from ...set import BrickSet
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_image_page = Blueprint(
|
||||||
|
'admin_image',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/image'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Update the default images
|
||||||
|
@admin_image_page.route('/update', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def update() -> Response:
|
||||||
|
# Abusing the object to create a 'nil' minifigure
|
||||||
|
RebrickableImage(
|
||||||
|
BrickSet(),
|
||||||
|
minifigure=BrickMinifigure(record={
|
||||||
|
'set_img_url': None,
|
||||||
|
})
|
||||||
|
).download()
|
||||||
|
|
||||||
|
# Abusing the object to create a 'nil' part
|
||||||
|
RebrickableImage(
|
||||||
|
BrickSet(),
|
||||||
|
part=BrickPart(record={
|
||||||
|
'part_img_url': None,
|
||||||
|
'part_img_url_id': None
|
||||||
|
})
|
||||||
|
).download()
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_image=True))
|
26
bricktracker/views/admin/instructions.py
Normal file
26
bricktracker/views/admin/instructions.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from flask import Blueprint, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...instructions_list import BrickInstructionsList
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_instructions_page = Blueprint(
|
||||||
|
'admin_instructions',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/instructions'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Refresh the instructions cache
|
||||||
|
@admin_instructions_page.route('/refresh', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def refresh() -> Response:
|
||||||
|
BrickInstructionsList(force=True)
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_instructions=True))
|
38
bricktracker/views/admin/retired.py
Normal file
38
bricktracker/views/admin/retired.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from flask import Blueprint, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...retired_list import BrickRetiredList
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_retired_page = Blueprint(
|
||||||
|
'admin_retired',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/retired'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Refresh the retired sets cache
|
||||||
|
@admin_retired_page.route('/refresh', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def refresh() -> Response:
|
||||||
|
BrickRetiredList(force=True)
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_retired=True))
|
||||||
|
|
||||||
|
|
||||||
|
# Update the retired sets
|
||||||
|
@admin_retired_page.route('/update', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def update() -> Response:
|
||||||
|
BrickRetiredList().update()
|
||||||
|
|
||||||
|
BrickRetiredList(force=True)
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_retired=True))
|
38
bricktracker/views/admin/theme.py
Normal file
38
bricktracker/views/admin/theme.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from flask import Blueprint, redirect, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from werkzeug.wrappers.response import Response
|
||||||
|
|
||||||
|
from ..exceptions import exception_handler
|
||||||
|
from ...theme_list import BrickThemeList
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
admin_theme_page = Blueprint(
|
||||||
|
'admin_theme',
|
||||||
|
__name__,
|
||||||
|
url_prefix='/admin/theme'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Refresh the themes cache
|
||||||
|
@admin_theme_page.route('/refresh', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def refresh() -> Response:
|
||||||
|
BrickThemeList(force=True)
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_theme=True))
|
||||||
|
|
||||||
|
|
||||||
|
# Update the themes file
|
||||||
|
@admin_theme_page.route('/update', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
@exception_handler(__file__)
|
||||||
|
def update() -> Response:
|
||||||
|
BrickThemeList().update()
|
||||||
|
|
||||||
|
BrickThemeList(force=True)
|
||||||
|
|
||||||
|
return redirect(url_for('admin.admin', open_theme=True))
|
@ -9,13 +9,13 @@
|
|||||||
<p>Your database needs to be upgraded.</p>
|
<p>Your database needs to be upgraded.</p>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="text-end">
|
<div class="text-end">
|
||||||
<a href="{{ url_for('admin.upgrade_database') }}" class="btn btn-warning" role="button"><i class="ri-arrow-up-double-line"></i> Upgrade the database</a>
|
<a href="{{ url_for('admin_database.upgrade') }}" class="btn btn-warning" role="button"><i class="ri-arrow-up-double-line"></i> Upgrade the database</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p>The database file is: <code>{{ config['DATABASE_PATH'] }}</code> at version <span class="badge rounded-pill text-bg-light border fw-normal"><i class="ri-hashtag"></i>{{ database_version }}</span></p>
|
<p>The database file is: <code>{{ config['DATABASE_PATH'] }}</code> at version <span class="badge rounded-pill text-bg-light border fw-normal"><i class="ri-hashtag"></i>{{ database_version }}</span></p>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url_for('admin.download_database') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
|
<a href="{{ url_for('admin_database.download') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
|
||||||
</p>
|
</p>
|
||||||
{% if database_counters %}
|
{% if database_counters %}
|
||||||
<h5 class="border-bottom">Records</h5>
|
<h5 class="border-bottom">Records</h5>
|
||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
{{ accordion.header('Database danger zone', 'database-danger', 'admin', danger=true, class='text-end') }}
|
{{ accordion.header('Database danger zone', 'database-danger', 'admin', danger=true, class='text-end') }}
|
||||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||||
<a href="{{ url_for('admin.import_database') }}" class="btn btn-warning" role="button"><i class="ri-upload-line"></i> Import a database file</a>
|
<a href="{{ url_for('admin_database.upload') }}" class="btn btn-warning" role="button"><i class="ri-upload-line"></i> Import a database file</a>
|
||||||
<a href="{{ url_for('admin.drop_database') }}" class="btn btn-danger" role="button"><i class="ri-close-line"></i> Drop the database</a>
|
<a href="{{ url_for('admin_database.drop') }}" class="btn btn-danger" role="button"><i class="ri-close-line"></i> Drop the database</a>
|
||||||
<a href="{{ url_for('admin.delete_database') }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete the database file</a>
|
<a href="{{ url_for('admin_database.delete') }}" class="btn btn-danger" role="button"><i class="ri-delete-bin-2-line"></i> Delete the database file</a>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% import 'macro/accordion.html' as accordion %}
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||||
<form action="{{ url_for('admin.do_delete_database') }}" method="post">
|
<form action="{{ url_for('admin_database.do_delete') }}" method="post">
|
||||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||||
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete the database file</strong>. This action is irreversible.</div>
|
<div class="alert alert-danger text-center" role="alert">You are about to <strong>delete the database file</strong>. This action is irreversible.</div>
|
||||||
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% import 'macro/accordion.html' as accordion %}
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true, class='text-end') }}
|
||||||
<form action="{{ url_for('admin.do_drop_database') }}" method="post">
|
<form action="{{ url_for('admin_database.do_drop') }}" method="post">
|
||||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||||
<div class="alert alert-danger text-center" role="alert">You are about to <strong>drop all the tables from the database</strong>. This action is irreversible.</div>
|
<div class="alert alert-danger text-center" role="alert">You are about to <strong>drop all the tables from the database</strong>. This action is irreversible.</div>
|
||||||
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% import 'macro/accordion.html' as accordion %}
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true) }}
|
{{ accordion.header('Database danger zone', 'database-danger', 'admin', expanded=true, danger=true) }}
|
||||||
<form action="{{ url_for('admin.do_import_database') }}" method="post" enctype="multipart/form-data">
|
<form action="{{ url_for('admin_database.do_upload') }}" method="post" enctype="multipart/form-data">
|
||||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||||
<div class="alert alert-warning text-center" role="alert">You are about to <strong>import a database file</strong>. This will replace the <strong>whole content</strong> of the database. This action is irreversible.</div>
|
<div class="alert alert-warning text-center" role="alert">You are about to <strong>import a database file</strong>. This will replace the <strong>whole content</strong> of the database. This action is irreversible.</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% import 'macro/accordion.html' as accordion %}
|
{% import 'macro/accordion.html' as accordion %}
|
||||||
|
|
||||||
{{ accordion.header('Database', 'database', 'admin', expanded=true, icon='database-2-line') }}
|
{{ accordion.header('Database', 'database', 'admin', expanded=true, icon='database-2-line') }}
|
||||||
<form action="{{ url_for('admin.do_upgrade_database') }}" method="post">
|
<form action="{{ url_for('admin_database.do_upgrade') }}" method="post">
|
||||||
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
{% if error %}<div class="alert alert-danger text-start" role="alert"><strong>Error:</strong> {{ error }}.</div>{% endif %}
|
||||||
<div class="alert alert-warning text-center" role="alert">
|
<div class="alert alert-warning text-center" role="alert">
|
||||||
You are about to <strong>upgrade your database file</strong>. This action is irreversible.<br>
|
You are about to <strong>upgrade your database file</strong>. This action is irreversible.<br>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div class="text-end">
|
<div class="text-end">
|
||||||
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
<a class="btn btn-danger" href="{{ url_for('admin.admin') }}" role="button"><i class="ri-arrow-left-long-line"></i> Back to the admin</a>
|
||||||
<a href="{{ url_for('admin.download_database') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
|
<a href="{{ url_for('admin_database.download') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Download the database file</a>
|
||||||
<button type="submit" class="btn btn-warning"><i class="ri-arrow-up-double-line"></i> Upgrade the database</button>
|
<button type="submit" class="btn btn-warning"><i class="ri-arrow-up-double-line"></i> Upgrade the database</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
<img class="table-img border mx-1" src="{{ nil_part_url }}" alt="{{ nil_part_name }}">
|
<img class="table-img border mx-1" src="{{ nil_part_url }}" alt="{{ nil_part_name }}">
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url_for('admin.update_image') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the images</a>
|
<a href="{{ url_for('admin_image.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the images</a>
|
||||||
</p>
|
</p>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
@ -27,6 +27,6 @@
|
|||||||
</p>
|
</p>
|
||||||
<h5 class="border-bottom">Refresh</h5>
|
<h5 class="border-bottom">Refresh</h5>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url_for('admin.refresh_instructions') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the instructions cache</a>
|
<a href="{{ url_for('admin_instructions.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the instructions cache</a>
|
||||||
</p>
|
</p>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<h5 class="border-bottom">Refresh</h5>
|
<h5 class="border-bottom">Refresh</h5>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url_for('admin.refresh_retired') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the retired sets cache</a>
|
<a href="{{ url_for('admin_retired.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the retired sets cache</a>
|
||||||
<a href="{{ url_for('admin.update_retired') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the retired sets file</a>
|
<a href="{{ url_for('admin_retired.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the retired sets file</a>
|
||||||
</p>
|
</p>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<h5 class="border-bottom">Refresh</h5>
|
<h5 class="border-bottom">Refresh</h5>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url_for('admin.refresh_themes') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the themes cache</a>
|
<a href="{{ url_for('admin_theme.refresh') }}" class="btn btn-primary" role="button"><i class="ri-refresh-line"></i> Refresh the themes cache</a>
|
||||||
<a href="{{ url_for('admin.update_themes') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the themes file</a>
|
<a href="{{ url_for('admin_theme.update') }}" class="btn btn-primary" role="button"><i class="ri-download-line"></i> Update the themes file</a>
|
||||||
</p>
|
</p>
|
||||||
{{ accordion.footer() }}
|
{{ accordion.footer() }}
|
||||||
|
Loading…
Reference in New Issue
Block a user