#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file was part of Flask-Bootstrap and was modified under the terms of
# its BSD License. Copyright (c) 2013, Marc Brinkmann. All rights reserved.
#
# This file was part of Bootstrap-Flask and was modified under the terms of
# its MIT License. Copyright (c) 2018 Grey Li. All rights reserved.
#
# This file is part of the
# Flask-FomanticUI Project (https://github.com/juniors90/Flask-FomanticUI/).
# Copyright (c) 2021, Ferreira Juan David
# License: MIT
# Full Text: https://github.com/juniors90/Flask-FomanticUI/blob/master/LICENSE
# =============================================================================
# DOCS
# =============================================================================
"""Flask-FomanticUI.
Implementation of Fomantic UI in Flask.
"""
# =============================================================================
# IMPORTS
# =============================================================================
import warnings
from flask import Blueprint, Markup, current_app, url_for
try: # pragma: no cover
from wtforms.fields import HiddenField
except ImportError: # pragma: no cover
# docstr-coverage:excused `no one is reading this anyways`
def is_hidden_field_filter(field):
raise RuntimeError("WTForms is not installed.")
else:
# docstr-coverage:excused `no one is reading this anyways`
[docs] def is_hidden_field_filter(field):
return isinstance(field, HiddenField)
cdn_base = "https://cdn.jsdelivr.net/npm"
# docstr-coverage:excused `no one is reading this anyways`
[docs]def raise_helper(message): # pragma: no cover
raise RuntimeError(message)
[docs]def get_table_titles(data, primary_key, primary_key_title):
"""Detect and build the table titles tuple from ORM object.
.. note::
Currently only support SQLAlchemy.
"""
if not data:
return []
titles = []
for k in data[0].__table__.columns.keys():
if not k.startswith("_"):
titles.append((k, k.replace("_", " ").title()))
titles[0] = (primary_key, primary_key_title)
return titles
# docstr-coverage:excused `no one is reading this anyways`
[docs]def link_css_with_sri(url, sri):
return f'<link rel="stylesheet" href="{url}" integrity="{sri}" crossorigin="anonymous">' # noqa: E501
# docstr-coverage:excused `no one is reading this anyways`
[docs]def simple_link_css(url):
return f'<link rel="stylesheet" href="{url}">'
# docstr-coverage:excused `no one is reading this anyways`
[docs]def scripts_with_sri(url, sri):
return f'<script src="{url}" integrity="{sri}" crossorigin="anonymous"></script>' # noqa: E501
# docstr-coverage:excused `no one is reading this anyways`
[docs]def simple_scripts_js(url):
return f'<script src="{url}"></script>'
class _FomanticUI(object):
"""Base extension class for different Fomantic UI versions.
.. versionadded:: 0.0.1
"""
fomantic_version = None
jquery_version = None
fomantic_css_integrity = None
fomantic_js_integrity = None
jquery_integrity = None
static_folder = None
fomantic_css_filename = "semantic.min.css"
fomantic_js_filename = "semantic.min.js"
jquery_filename = "jquery.min.js"
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
"""Application factory."""
# default settings
app.config.setdefault("FOMANTIC_SERVE_LOCAL", False)
app.config.setdefault("FOMANTIC_BUTTON_STYLE", "primary")
app.config.setdefault("FOMANTIC_BUTTON_SIZE", "")
app.config.setdefault("FOMANTIC_ICON_COLOR", None)
app.config.setdefault("FOMANTIC_ICON_SIZE", None)
app.config.setdefault(
"FOMANTIC_ERROR_HEADER", "Action Forbidden"
) # noqa: E501
app.config.setdefault(
"FOMANTIC_CHECKBOX_HEADER_ERROR", "Action Forbidden"
) # noqa: E501
app.config.setdefault(
"FOMANTIC_RADIO_HEADER_ERROR", "Action Forbidden"
) # noqa: E501
app.config.setdefault(
"FOMANTIC_MSG_CATEGORY", None
) # change primary by None
app.config.setdefault("FOMANTIC_TABLE_VIEW_TITLE", "View")
app.config.setdefault("FOMANTIC_TABLE_EDIT_TITLE", "Edit")
app.config.setdefault("FOMANTIC_TABLE_DELETE_TITLE", "Delete")
app.config.setdefault("FOMANTIC_TABLE_NEW_TITLE", "New")
if not hasattr(app, "extensions"): # pragma: no cover
app.extensions = {}
app.extensions["fomantic"] = self
blueprint = Blueprint(
"fomantic",
__name__,
static_folder=f"static/{self.static_folder}",
static_url_path=f"{app.static_url_path}",
template_folder="templates",
)
app.register_blueprint(blueprint)
app.jinja_env.globals["fomantic"] = self
app.jinja_env.globals[
"fomantic_is_hidden_field"
] = is_hidden_field_filter
app.jinja_env.globals["get_table_titles"] = get_table_titles
app.jinja_env.globals["warn"] = warnings.warn
app.jinja_env.globals["raise"] = raise_helper
app.jinja_env.add_extension("jinja2.ext.do")
def load_css(self, s_version=None, fomantic_sri=None):
"""Load Fomantic's css resources with given version.
Parameters
----------
s_version : str
The version of Fomantic UI.
fomantic_sri : str
Subresource Integrity.
Return
------
scripts_cdn : markupsafe.Markup
Fomantic UI CDN File.
"""
serve_local = current_app.config["FOMANTIC_SERVE_LOCAL"]
s_version = self.fomantic_version if s_version is None else s_version
fomantic_sri = self._get_sri("fomantic_css", s_version, fomantic_sri)
if serve_local:
base_path = "css"
url = url_for(
"fomantic.static",
filename=f"{base_path}/{self.fomantic_css_filename}",
)
else:
base_path = cdn_base + f"/fomantic-ui@{s_version}/dist/"
url = base_path + self.fomantic_css_filename
if fomantic_sri:
css = link_css_with_sri(url, fomantic_sri)
else:
css = f'<link rel="stylesheet" type="text/css" href="{url}">'
scripts_cdn = Markup(css)
return scripts_cdn
def _get_js_script(self, version, name, sri):
"""Get <script> tag for JavaScipt resources."""
serve_local = current_app.config["FOMANTIC_SERVE_LOCAL"]
paths = {
"fomantic-ui": f"{self.fomantic_js_filename}",
"jquery": f"{self.jquery_filename}",
}
if serve_local:
path = "js/semantic"
url = url_for("fomantic.static", filename=f"{path}/{paths[name]}")
else:
url = cdn_base + f"/{name}@{version}/dist/{paths[name]}"
if sri:
script_html = scripts_with_sri(url, sri)
else:
script_html = simple_scripts_js(url)
return script_html
def _get_sri(self, name, version, sri):
serve_local = current_app.config["FOMANTIC_SERVE_LOCAL"]
sris = {
"fomantic_css": self.fomantic_css_integrity,
"fomantic_js": self.fomantic_js_integrity,
"jquery": self.jquery_integrity,
}
versions = {
"fomantic_css": self.fomantic_version,
"fomantic_js": self.fomantic_version,
"jquery": self.jquery_version,
}
if sri is not None:
return sri
if version == versions[name] and serve_local is False:
return sris[name]
return None
def load_js(
self,
version=None,
jq_version=None, # noqa: C901
fomantic_sri=None,
jquery_sri=None,
):
"""Load Fomantic UI and other resources with given version.
Parameter
---------
version : str
The version of Fomantic UI.
jq_version : str
The version of jQuery (for Fomantic UI).
fomantic_sri : str
Subresource Integrity for Fomantic UI..
jquery_sri : str
Subresource Integrity for jQuery.
Return
------
scripts_cdn : markupsafe.Markup
Fomantic UI CDN File.
"""
fui_version = self.fomantic_version if version is None else version
fui_sri = self._get_sri("fomantic_js", fui_version, fomantic_sri)
fui_js = self._get_js_script(fui_version, "fomantic-ui", fui_sri)
jq_version = self.jquery_version if jq_version is None else jq_version
jquery_sri = self._get_sri("jquery", jq_version, jquery_sri)
jquery = self._get_js_script(jq_version, "jquery", jquery_sri)
scripts_cdn = Markup(
f"""{jquery}
{fui_js}"""
)
return scripts_cdn