From c0946481dd3a8b3254556cebb4dce4229c04566f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Sat, 29 Aug 2020 21:54:12 +0800 Subject: [PATCH] Changed the CSS and JavaScript libraries from the hard-coded site-specific location to the settings with default to CDN download. --- .../templates/accounting/account_list.html | 2 +- .../accounting/report-balance-sheet.html | 2 +- .../templates/accounting/report-cash.html | 2 +- .../accounting/report-income-statement.html | 2 +- .../templates/accounting/report-journal.html | 2 +- .../templates/accounting/report-ledger.html | 2 +- .../accounting/report-trial-balance.html | 2 +- .../accounting/transaction_expense_form.html | 3 +- .../accounting/transaction_income_form.html | 3 +- .../accounting/transaction_sort_form.html | 2 +- .../accounting/transaction_transfer_form.html | 3 +- accounting/views.py | 4 +- mia_core/templatetags/mia_core.py | 54 ++++-- mia_core/utils.py | 159 +++++++++++++++++- 14 files changed, 216 insertions(+), 26 deletions(-) diff --git a/accounting/templates/accounting/account_list.html b/accounting/templates/accounting/account_list.html index 00defba..b8bb9ac 100644 --- a/accounting/templates/accounting/account_list.html +++ b/accounting/templates/accounting/account_list.html @@ -28,7 +28,7 @@ First written: 2020/8/7 {% block settings %} {% trans "Accounts" context "Accounting" as text %} {% setvar "title" text %} - {% setvar "use_datatables" True %} + {% add_lib "bootstrap4-datatables" %} {% static "accounting/js/account-list.js" as file %}{% add_js file %} {% endblock %} diff --git a/accounting/templates/accounting/report-balance-sheet.html b/accounting/templates/accounting/report-balance-sheet.html index 0fbab7f..374105a 100644 --- a/accounting/templates/accounting/report-balance-sheet.html +++ b/accounting/templates/accounting/report-balance-sheet.html @@ -29,7 +29,7 @@ First written: 2020/7/20 {% block settings %} {% blocktrans asvar title with prep_period=request.resolver_match.kwargs.period.prep_desc %}Balance Sheet {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/report-cash.html b/accounting/templates/accounting/report-cash.html index 9aae4c8..d038d97 100644 --- a/accounting/templates/accounting/report-cash.html +++ b/accounting/templates/accounting/report-cash.html @@ -28,7 +28,7 @@ First written: 2020/7/1 {% block settings %} {% blocktrans asvar title with account=request.resolver_match.kwargs.account.title prep_period=request.resolver_match.kwargs.period.prep_desc %}Cash Account for {{ account }} {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/report-income-statement.html b/accounting/templates/accounting/report-income-statement.html index 29753c7..84be338 100644 --- a/accounting/templates/accounting/report-income-statement.html +++ b/accounting/templates/accounting/report-income-statement.html @@ -28,7 +28,7 @@ First written: 2020/7/19 {% block settings %} {% blocktrans asvar title with prep_period=request.resolver_match.kwargs.period.prep_desc %}Income Statement {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/report-journal.html b/accounting/templates/accounting/report-journal.html index e644a35..b657f27 100644 --- a/accounting/templates/accounting/report-journal.html +++ b/accounting/templates/accounting/report-journal.html @@ -28,7 +28,7 @@ First written: 2020/7/17 {% block settings %} {% blocktrans asvar title with prep_period=request.resolver_match.kwargs.period.prep_desc %}Journal {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/report-ledger.html b/accounting/templates/accounting/report-ledger.html index 91b6e9e..69d9f68 100644 --- a/accounting/templates/accounting/report-ledger.html +++ b/accounting/templates/accounting/report-ledger.html @@ -28,7 +28,7 @@ First written: 2020/7/16 {% block settings %} {% blocktrans asvar title with account=request.resolver_match.kwargs.account.title prep_period=request.resolver_match.kwargs.period.prep_desc %}Ledger for {{ account }} {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/report-trial-balance.html b/accounting/templates/accounting/report-trial-balance.html index 2181bb2..e912c1d 100644 --- a/accounting/templates/accounting/report-trial-balance.html +++ b/accounting/templates/accounting/report-trial-balance.html @@ -28,7 +28,7 @@ First written: 2020/7/19 {% block settings %} {% blocktrans asvar title with prep_period=request.resolver_match.kwargs.period.prep_desc %}Trial Balance {{ prep_period }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_period_chooser" True %} + {% add_lib "period-chooser" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% endblock %} diff --git a/accounting/templates/accounting/transaction_expense_form.html b/accounting/templates/accounting/transaction_expense_form.html index 97288ef..e4ffd93 100644 --- a/accounting/templates/accounting/transaction_expense_form.html +++ b/accounting/templates/accounting/transaction_expense_form.html @@ -28,8 +28,7 @@ First written: 2020/7/23 {% block settings %} {% setvar "title" _("Cash Expense Transaction") %} - {% setvar "use_jqueryui" True %} - {% static "ext-libs/decimal/decimal.min.js" as file %}{% add_js file %} + {% add_lib "jquery-ui" "decimal-js" %} {% static "accounting/css/transactions.css" as file %}{% add_css file %} {% static "accounting/css/summary-helper.css" as file %}{% add_css file %} {% static "accounting/js/transaction-form.js" as file %}{% add_js file %} diff --git a/accounting/templates/accounting/transaction_income_form.html b/accounting/templates/accounting/transaction_income_form.html index af42b6f..45c77a8 100644 --- a/accounting/templates/accounting/transaction_income_form.html +++ b/accounting/templates/accounting/transaction_income_form.html @@ -28,8 +28,7 @@ First written: 2020/7/23 {% block settings %} {% setvar "title" _("Cash Income Transaction") %} - {% setvar "use_jqueryui" True %} - {% static "ext-libs/decimal/decimal.min.js" as file %}{% add_js file %} + {% add_lib "jquery-ui" "decimal-js" %} {% static "accounting/css/transactions.css" as file %}{% add_css file %} {% static "accounting/css/summary-helper.css" as file %}{% add_css file %} {% static "accounting/js/transaction-form.js" as file %}{% add_js file %} diff --git a/accounting/templates/accounting/transaction_sort_form.html b/accounting/templates/accounting/transaction_sort_form.html index bcc0602..dd2dde7 100644 --- a/accounting/templates/accounting/transaction_sort_form.html +++ b/accounting/templates/accounting/transaction_sort_form.html @@ -29,7 +29,7 @@ First written: 2020/8/6 {% block settings %} {% blocktrans asvar title with date=form.date|smart_date %}Reorder the Transactions in {{ date }}{% endblocktrans %} {% setvar "title" title %} - {% setvar "use_jqueryui" True %} + {% add_lib "jquery-ui" %} {% static "accounting/css/report.css" as file %}{% add_css file %} {% static "accounting/css/transactions-sort.css" as file %}{% add_css file %} {% static "accounting/js/transaction-sort.js" as file %}{% add_js file %} diff --git a/accounting/templates/accounting/transaction_transfer_form.html b/accounting/templates/accounting/transaction_transfer_form.html index 8e3c355..958a227 100644 --- a/accounting/templates/accounting/transaction_transfer_form.html +++ b/accounting/templates/accounting/transaction_transfer_form.html @@ -28,8 +28,7 @@ First written: 2020/7/23 {% block settings %} {% setvar "title" _("Transfer Transaction") %} - {% setvar "use_jqueryui" True %} - {% static "ext-libs/decimal/decimal.min.js" as file %}{% add_js file %} + {% add_lib "jquery-ui" "decimal-js" %} {% static "accounting/css/transactions.css" as file %}{% add_css file %} {% static "accounting/css/summary-helper.css" as file %}{% add_css file %} {% static "accounting/js/transaction-form.js" as file %}{% add_js file %} diff --git a/accounting/views.py b/accounting/views.py index cd3aee7..21697bb 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -39,12 +39,14 @@ from django.views.decorators.http import require_GET, require_POST from django.views.generic import ListView, DetailView from mia_core.period import Period -from mia_core.utils import Pagination, PaginationException +from mia_core.utils import Pagination, PaginationException, add_default_libs from mia_core.views import DeleteView, FormView, RedirectView from . import utils from .forms import AccountForm, TransactionForm, TransactionSortForm from .models import Record, Transaction, Account +add_default_libs("bootstrap4", "font-awesome-5", "i18n") + @method_decorator(require_GET, name="dispatch") class CashDefaultView(RedirectView): diff --git a/mia_core/templatetags/mia_core.py b/mia_core/templatetags/mia_core.py index edbbaf3..ce3b5f7 100644 --- a/mia_core/templatetags/mia_core.py +++ b/mia_core/templatetags/mia_core.py @@ -19,8 +19,9 @@ """ import datetime +import re from datetime import date -from typing import Any +from typing import Any, List import titlecase from django import template @@ -31,7 +32,7 @@ from django.utils import timezone from django.utils.safestring import SafeString from django.utils.translation import gettext -from mia_core.utils import UrlBuilder +from mia_core.utils import UrlBuilder, CssAndJavaScriptLibraries register = template.Library() @@ -103,6 +104,24 @@ def url_keep_return(context: RequestContext, url: str) -> str: return str(UrlBuilder(url).query(r=context.request.GET.get("r"))) +@register.simple_tag(takes_context=True) +def add_lib(context: RequestContext, *args) -> str: + """Adds CSS and JavaScript libraries. + + Args: + context: The request context. + args: The names of the CSS and JavaScript libraries. + + Returns: + An empty string. + """ + if "libs" not in context.dicts[0]: + context.dicts[0]["libs"] = CssAndJavaScriptLibraries(args) + else: + context.dicts[0]["libs"].use(args) + return "" + + @register.simple_tag(takes_context=True) def add_css(context: RequestContext, url: str) -> str: """Adds a local CSS file. The file is added to the "css" template @@ -113,11 +132,11 @@ def add_css(context: RequestContext, url: str) -> str: url: The URL or path of the CSS file. Returns: - An empty string + An empty string. """ - if "css" not in context.dicts[0]: - context.dicts[0]["css"] = [] - context.dicts[0]["css"].append(url) + if "libs" not in context.dicts[0]: + context.dicts[0]["libs"] = CssAndJavaScriptLibraries() + context.dicts[0]["libs"].add_css(url) return "" @@ -131,11 +150,11 @@ def add_js(context: RequestContext, url: str) -> str: url: The URL or path of the JavaScript file. Returns: - An empty string + An empty string. """ - if "js" not in context.dicts[0]: - context.dicts[0]["js"] = [] - context.dicts[0]["js"].append(url) + if "libs" not in context.dicts[0]: + context.dicts[0]["libs"] = CssAndJavaScriptLibraries() + context.dicts[0]["libs"].add_js(url) return "" @@ -215,3 +234,18 @@ def is_in_section(request: HttpRequest, section_name: str) -> bool: view_name = request.resolver_match.view_name return view_name == section_name\ or view_name.startswith(section_name + ".") + + +@register.filter +def is_static_url(target: str) -> bool: + """Returns whether the target URL is a static path + + Args: + target: The target, either a static path that need to be passed to + the static template tag, or an HTTP, HTTPS URL or absolute path + that should be displayed directly. + + Returns: + True if the target URL is a static path, or False otherwise. + """ + return not (re.match("^https?://", target) or target.startswith("/")) diff --git a/mia_core/utils.py b/mia_core/utils.py index 35e80c4..918519a 100644 --- a/mia_core/utils.py +++ b/mia_core/utils.py @@ -20,11 +20,12 @@ """ import random import urllib.parse -from typing import Dict, List, Any, Type +from typing import Dict, List, Any, Type, Tuple from django.conf import settings from django.db.models import Model from django.http import HttpRequest +from django.urls import reverse from django.utils.translation import pgettext, get_language @@ -438,3 +439,159 @@ class PaginationException(Exception): """ def __init__(self, url_builder: UrlBuilder): self.url = str(url_builder) + + +CDN_LIBRARIES = { + "jquery": {"css": [], "js": ["https://code.jquery.com/jquery-3.5.1.min.js"]}, + "bootstrap4": { + "css": ["https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"], + "js": ["https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js", + "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"]}, + "font-awesome-5": { + "css": ["https://use.fontawesome.com/releases/v5.14.0/css/all.css"], + "js": []}, + "bootstrap4-datatables": { + "css": ["https://cdn.datatables.net/1.10.21/css/jquery.dataTables.min.css", + "https://cdn.datatables.net/1.10.21/css/dataTables.bootstrap4.min.css"], + "js": ["https://cdn.datatables.net/1.10.21/js/jquery.dataTables.min.js", + "https://cdn.datatables.net/1.10.21/js/dataTables.bootstrap4.min.js"]}, + "jquery-ui": {"css": ["https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css"], + "js": ["https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"]}, + "bootstrap4-tempusdominus": { + "css": ["https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/css/tempusdominus-bootstrap-4.min.css"], + "js": ["https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment-with-locales.min.js", + "https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/js/tempusdominus-bootstrap-4.js"]}, + "decimal-js": {"css": [], + "js": ["https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.2.0/decimal.min.js"]}, + "period-chooser": {"css": ["mia_core/css/period-chooser.css"], + "js": ["mia_core/js/period-chooser.js"]} +} +DEFAULT_LIBS = [] + + +class CssAndJavaScriptLibraries: + """The CSS and JavaScript library resolver.""" + AVAILABLE_LIBS: List[str] = ["jquery", "bootstrap4", "font-awesome-5", + "bootstrap4-datatables", "jquery-ui", + "bootstrap4-tempusdominus", "decimal-js", + "i18n", "period-chooser"] + + def __init__(self, *args): + self._use: Dict[str, bool] = {x: False for x in self.AVAILABLE_LIBS} + self._add_default_libs() + # The specified libraries + if len(args) > 0: + libs = args[0] + invalid = [x for x in libs if x not in self.AVAILABLE_LIBS] + if len(invalid) > 0: + raise NameError("library %s invalid" % ", ".join(invalid)) + for lib in libs: + self._use[lib] = True + self._css = [] + try: + self._css = self._css + settings.DEFAULT_CSS + except AttributeError: + pass + self._js = [] + try: + self._css = self._css + settings.DEFAULT_JS + except AttributeError: + pass + + def _add_default_libs(self): + """Adds the default libraries.""" + invalid = [x for x in DEFAULT_LIBS if x not in self.AVAILABLE_LIBS] + if len(invalid) > 0: + raise NameError("library %s invalid" % ", ".join(invalid)) + for lib in DEFAULT_LIBS: + self._use[lib] = True + + def use(self, *args) -> None: + """Use the specific libraries. + + Args: + args: The libraries. + """ + if len(args) == 0: + return + libs = args[0] + invalid = [x for x in libs if x not in self.AVAILABLE_LIBS] + if len(invalid) > 0: + raise NameError("library %s invalid" % ", ".join(invalid)) + for lib in libs: + self._use[lib] = True + + def add_css(self, css) -> None: + """Adds a custom CSS file.""" + self._css.append(css) + + def add_js(self, js) -> None: + """Adds a custom JavaScript file.""" + self._js.append(js) + + def css(self) -> List[str]: + """Returns the stylesheet files to use.""" + use: Dict[str, bool] = self._solve_use_depencies() + css = [] + for lib in [x for x in self.AVAILABLE_LIBS if use[x]]: + if lib == "i18n": + continue + try: + css = css + settings.STATIC_LIBS[lib]["css"] + except AttributeError: + css = css + CDN_LIBRARIES[lib]["css"] + except TypeError: + css = css + CDN_LIBRARIES[lib]["css"] + except KeyError: + css = css + CDN_LIBRARIES[lib]["css"] + return css + self._css + + def js(self) -> List[str]: + """Returns the JavaScript files to use.""" + use: Dict[str, bool] = self._solve_use_depencies() + js = [] + for lib in [x for x in self.AVAILABLE_LIBS if use[x]]: + if lib == "i18n": + js.append(reverse("javascript-catalog")) + continue + try: + js = js + settings.STATIC_LIBS[lib]["js"] + except AttributeError: + js = js + CDN_LIBRARIES[lib]["js"] + except TypeError: + js = js + CDN_LIBRARIES[lib]["js"] + except KeyError: + js = js + CDN_LIBRARIES[lib]["js"] + return js + self._js + + def _solve_use_depencies(self) -> Dict[str, bool]: + """Solves and returns the library dependencies.""" + use: Dict[str, bool] = {x: self._use[x] for x in self._use} + if use["period-chooser"]: + use["bootstrap4-tempusdominus"] = True + if use["bootstrap4-tempusdominus"]: + use["bootstrap4"] = True + if use["bootstrap4-datatables"]: + use["bootstrap4"] = True + if use["jquery-ui"]: + use["jquery"] = True + if use["bootstrap4"]: + use["jquery"] = True + return use + + +def add_default_libs(*args) -> None: + """Adds the specified libraries to the default CSS and JavaScript + libraries. + + Args: + args: The libraries to be added to the default libraries + """ + libs = args + invalid = [x for x in libs + if x not in CssAndJavaScriptLibraries.AVAILABLE_LIBS] + if len(invalid) > 0: + raise NameError("library %s invalid" % ", ".join(invalid)) + for lib in libs: + if lib not in DEFAULT_LIBS: + DEFAULT_LIBS.append(lib)