bump 2.2.0
This commit is contained in:
parent
20dcb3659a
commit
143f6b5b44
3
.gitignore
vendored
3
.gitignore
vendored
@ -19,4 +19,5 @@ var/run/*
|
|||||||
.env
|
.env
|
||||||
venv/
|
venv/
|
||||||
node_modules
|
node_modules
|
||||||
tmp.py
|
tmp.py
|
||||||
|
!/plugins/user/Dashboard/*
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
0
data/www/plugins/.gitkeep
Executable file
0
data/www/plugins/.gitkeep
Executable file
3
data/www/plugins/dashboard/css/dashboard.css
Normal file
3
data/www/plugins/dashboard/css/dashboard.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.inner h3 {
|
||||||
|
color: red !important;
|
||||||
|
}
|
||||||
@ -115,9 +115,24 @@ main {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
padding: 0 10px 40px 10px;
|
padding: 10px 10px 40px 10px;
|
||||||
background: $layoutBackground;
|
background: $layoutBackground;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: $gscaleD;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 18px;
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
color: $gscale6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
class WebDirConstant:
|
class WebDirConstant:
|
||||||
|
|
||||||
FOLDER_TEMPLATES = "views"
|
FOLDER_TEMPLATES = "views"
|
||||||
@ -6,6 +5,6 @@ class WebDirConstant:
|
|||||||
FOLDER_STATIC_WEB_UPLOADS = "uploads"
|
FOLDER_STATIC_WEB_UPLOADS = "uploads"
|
||||||
FOLDER_STATIC_WEB_ASSETS = "www"
|
FOLDER_STATIC_WEB_ASSETS = "www"
|
||||||
FOLDER_PLUGIN_HOOK = "hook"
|
FOLDER_PLUGIN_HOOK = "hook"
|
||||||
|
FOLDER_PLUGIN_STATIC_SRC = "static"
|
||||||
|
FOLDER_PLUGIN_STATIC_DST = "plugins"
|
||||||
FOLDER_CONTROLLER = "controller"
|
FOLDER_CONTROLLER = "controller"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ class CoreController(ObController):
|
|||||||
self._app.add_url_rule('/favicon.ico', 'favicon', self.favicon, methods=['GET'])
|
self._app.add_url_rule('/favicon.ico', 'favicon', self.favicon, methods=['GET'])
|
||||||
|
|
||||||
def manifest(self):
|
def manifest(self):
|
||||||
with open("{}/manifest.jinja.json".format(self.get_template_folder()), 'r') as file:
|
with open("{}/manifest.jinja.json".format(self.get_template_dir()), 'r') as file:
|
||||||
template_content = file.read()
|
template_content = file.read()
|
||||||
|
|
||||||
rendered_content = render_template_string(template_content)
|
rendered_content = render_template_string(template_content)
|
||||||
@ -19,4 +19,4 @@ class CoreController(ObController):
|
|||||||
return self._app.response_class(rendered_content, mimetype='application/json')
|
return self._app.response_class(rendered_content, mimetype='application/json')
|
||||||
|
|
||||||
def favicon(self):
|
def favicon(self):
|
||||||
return send_file("{}/favicon.ico".format(self.get_web_folder()), mimetype='image/x-icon')
|
return send_file("{}/favicon.ico".format(self.get_web_dir()), mimetype='image/x-icon')
|
||||||
@ -28,11 +28,11 @@ class ObController(abc.ABC):
|
|||||||
|
|
||||||
return self._plugin
|
return self._plugin
|
||||||
|
|
||||||
def get_template_folder(self):
|
def get_template_dir(self):
|
||||||
return self._web_server.get_template_folder()
|
return self._web_server.get_template_dir()
|
||||||
|
|
||||||
def get_web_folder(self):
|
def get_web_dir(self):
|
||||||
return self._web_server.get_web_folder()
|
return self._web_server.get_web_dir()
|
||||||
|
|
||||||
def reload_web_server(self):
|
def reload_web_server(self):
|
||||||
self._web_server.reload()
|
self._web_server.reload()
|
||||||
@ -45,3 +45,6 @@ class ObController(abc.ABC):
|
|||||||
|
|
||||||
def get_external_storage_server(self):
|
def get_external_storage_server(self):
|
||||||
return self._kernel.external_storage_server
|
return self._kernel.external_storage_server
|
||||||
|
|
||||||
|
def render_view(self, template_file: str, **parameters: dict) -> str:
|
||||||
|
return self._template_renderer.render_view(template_file, self.plugin(), **parameters)
|
||||||
|
|||||||
@ -13,8 +13,6 @@ from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
|
|||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.service.TemplateRenderer import TemplateRenderer
|
from src.service.TemplateRenderer import TemplateRenderer
|
||||||
from src.constant.WebDirConstant import WebDirConstant
|
from src.constant.WebDirConstant import WebDirConstant
|
||||||
from src.service.AliasFileSystemLoader import AliasFileSystemLoader
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ObPlugin(abc.ABC):
|
class ObPlugin(abc.ABC):
|
||||||
@ -26,7 +24,7 @@ class ObPlugin(abc.ABC):
|
|||||||
self._plugin_dir = plugin_dir
|
self._plugin_dir = plugin_dir
|
||||||
self._model_store = model_store
|
self._model_store = model_store
|
||||||
self._template_renderer = template_renderer
|
self._template_renderer = template_renderer
|
||||||
self._rendering_env = self._init_rendering_env()
|
self._rendering_env = template_renderer.init_rendering_env(self._plugin_dir)
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def use_id(self) -> str:
|
def use_id(self) -> str:
|
||||||
@ -60,6 +58,9 @@ class ObPlugin(abc.ABC):
|
|||||||
def get_plugin_variable_name(self, name: str) -> str:
|
def get_plugin_variable_name(self, name: str) -> str:
|
||||||
return "{}_{}".format(self.get_plugin_variable_prefix(), name)
|
return "{}_{}".format(self.get_plugin_variable_prefix(), name)
|
||||||
|
|
||||||
|
def get_plugin_static_src_dir(self) -> str:
|
||||||
|
return "{}/{}".format(self._plugin_dir, WebDirConstant.FOLDER_PLUGIN_STATIC_SRC)
|
||||||
|
|
||||||
def add_variable(self, name: str, value='', section: str = '', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', description_edition: str = '', selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None, refresh_player: bool = False) -> Variable:
|
def add_variable(self, name: str, value='', section: str = '', type: VariableType = VariableType.STRING, editable: bool = True, description: str = '', description_edition: str = '', selectables: Optional[Dict[str, str]] = None, unit: Optional[VariableUnit] = None, refresh_player: bool = False) -> Variable:
|
||||||
return self._model_store.variable().set_variable(
|
return self._model_store.variable().set_variable(
|
||||||
name=self.get_plugin_variable_name(name),
|
name=self.get_plugin_variable_name(name),
|
||||||
@ -81,32 +82,9 @@ class ObPlugin(abc.ABC):
|
|||||||
def add_functional_hook_registration(self, hook: HookType, priority: int = 0, function=None) -> FunctionalHookRegistration:
|
def add_functional_hook_registration(self, hook: HookType, priority: int = 0, function=None) -> FunctionalHookRegistration:
|
||||||
return FunctionalHookRegistration(plugin=self, hook=hook, priority=priority, function=function)
|
return FunctionalHookRegistration(plugin=self, hook=hook, priority=priority, function=function)
|
||||||
|
|
||||||
def _init_rendering_env(self) -> Environment:
|
|
||||||
alias_paths = {
|
|
||||||
"::": "{}/".format(WebDirConstant.FOLDER_TEMPLATES),
|
|
||||||
"@": "{}/{}/".format(self._plugin_dir.replace(self._kernel.get_application_dir(), ''), WebDirConstant.FOLDER_TEMPLATES)
|
|
||||||
}
|
|
||||||
|
|
||||||
env = Environment(
|
|
||||||
loader=AliasFileSystemLoader(
|
|
||||||
searchpath=self._kernel.get_application_dir(),
|
|
||||||
alias_paths=alias_paths
|
|
||||||
),
|
|
||||||
autoescape=select_autoescape(['html', 'xml'])
|
|
||||||
)
|
|
||||||
|
|
||||||
return env
|
|
||||||
|
|
||||||
def render_view(self, template_file: str, **parameters: dict) -> str:
|
|
||||||
template = self.get_rendering_env().get_template(template_file)
|
|
||||||
|
|
||||||
return template.render(
|
|
||||||
request=request,
|
|
||||||
url_for=url_for,
|
|
||||||
**parameters,
|
|
||||||
**self._template_renderer.get_view_globals(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def translate(self, token, resolve=False) -> Union[Dict, str]:
|
def translate(self, token, resolve=False) -> Union[Dict, str]:
|
||||||
token = token if token.startswith(self.use_id()) else "{}_{}".format(self.use_id(), token)
|
token = token if token.startswith(self.use_id()) else "{}_{}".format(self.use_id(), token)
|
||||||
return self._model_store.lang().translate(token) if resolve else token
|
return self._model_store.lang().translate(token) if resolve else token
|
||||||
|
|
||||||
|
def render_view(self, template_file: str, **parameters: dict) -> str:
|
||||||
|
return self._template_renderer.render_view(template_file, self, **parameters)
|
||||||
|
|||||||
@ -38,4 +38,6 @@ class HookType(Enum):
|
|||||||
H_ROOT_JAVASCRIPT = 'h_root_javascript'
|
H_ROOT_JAVASCRIPT = 'h_root_javascript'
|
||||||
H_ROOT_NAV_ELEMENT_START = 'h_root_nav_element_start'
|
H_ROOT_NAV_ELEMENT_START = 'h_root_nav_element_start'
|
||||||
H_ROOT_NAV_ELEMENT_END = 'h_root_nav_element_end'
|
H_ROOT_NAV_ELEMENT_END = 'h_root_nav_element_end'
|
||||||
|
H_ROOT_PILL_ELEMENT_START = 'h_root_pill_element_start'
|
||||||
|
H_ROOT_PILL_ELEMENT_END = 'h_root_pill_element_end'
|
||||||
H_ROOT_FOOTER = 'h_root_footer'
|
H_ROOT_FOOTER = 'h_root_footer'
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import inspect
|
import inspect
|
||||||
import importlib
|
import importlib
|
||||||
@ -14,6 +15,7 @@ from src.model.enum.VariableType import VariableType
|
|||||||
from src.model.enum.HookType import HookType
|
from src.model.enum.HookType import HookType
|
||||||
from src.model.hook.HookRegistration import HookRegistration
|
from src.model.hook.HookRegistration import HookRegistration
|
||||||
from src.model.hook.StaticHookRegistration import StaticHookRegistration
|
from src.model.hook.StaticHookRegistration import StaticHookRegistration
|
||||||
|
from src.util.UtilFile import copy_files
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
|
||||||
|
|
||||||
@ -164,6 +166,12 @@ class PluginStore:
|
|||||||
# WEB CONTROLLERS
|
# WEB CONTROLLERS
|
||||||
self.load_controllers(plugin)
|
self.load_controllers(plugin)
|
||||||
|
|
||||||
|
# STATIC FILES
|
||||||
|
static_src = plugin.get_plugin_static_src_dir()
|
||||||
|
static_dst = self._web_server.get_plugin_static_dst_dir(plugin.use_id())
|
||||||
|
if os.path.exists(static_src):
|
||||||
|
copy_files(static_src, static_dst)
|
||||||
|
|
||||||
def clean_dead_variables(self) -> None:
|
def clean_dead_variables(self) -> None:
|
||||||
for variable_name, variable in self._dead_variables_candidates.items():
|
for variable_name, variable in self._dead_variables_candidates.items():
|
||||||
logging.debug("Removing dead plugin variable {}".format(variable_name))
|
logging.debug("Removing dead plugin variable {}".format(variable_name))
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import Flask, send_from_directory, Markup, url_for
|
from flask import Flask, send_from_directory, Markup, url_for, request
|
||||||
from typing import List
|
from flask_login import current_user
|
||||||
|
from typing import List, Optional
|
||||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||||
from src.service.ModelStore import ModelStore
|
from src.service.ModelStore import ModelStore
|
||||||
from src.model.enum.HookType import HookType
|
from src.model.enum.HookType import HookType
|
||||||
@ -10,6 +11,7 @@ from src.model.hook.HookRegistration import HookRegistration
|
|||||||
from src.model.hook.StaticHookRegistration import StaticHookRegistration
|
from src.model.hook.StaticHookRegistration import StaticHookRegistration
|
||||||
from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
|
from src.model.hook.FunctionalHookRegistration import FunctionalHookRegistration
|
||||||
from src.constant.WebDirConstant import WebDirConstant
|
from src.constant.WebDirConstant import WebDirConstant
|
||||||
|
from src.service.AliasFileSystemLoader import AliasFileSystemLoader
|
||||||
from src.util.utils import get_safe_cron_descriptor, \
|
from src.util.utils import get_safe_cron_descriptor, \
|
||||||
is_cron_in_datetime_moment, \
|
is_cron_in_datetime_moment, \
|
||||||
is_cron_in_week_moment, \
|
is_cron_in_week_moment, \
|
||||||
@ -23,6 +25,7 @@ class TemplateRenderer:
|
|||||||
self._kernel = kernel
|
self._kernel = kernel
|
||||||
self._model_store = model_store
|
self._model_store = model_store
|
||||||
self._render_hook = render_hook
|
self._render_hook = render_hook
|
||||||
|
self._rendering_env = self.init_rendering_env()
|
||||||
|
|
||||||
def cron_descriptor(self, expression: str, use_24hour_time_format=True) -> str:
|
def cron_descriptor(self, expression: str, use_24hour_time_format=True) -> str:
|
||||||
return get_safe_cron_descriptor(expression, use_24hour_time_format, self._model_store.lang().get_lang(local_with_country=True))
|
return get_safe_cron_descriptor(expression, use_24hour_time_format, self._model_store.lang().get_lang(local_with_country=True))
|
||||||
@ -80,3 +83,44 @@ class TemplateRenderer:
|
|||||||
content.append(hook_registration.function())
|
content.append(hook_registration.function())
|
||||||
|
|
||||||
return Markup("".join(content))
|
return Markup("".join(content))
|
||||||
|
|
||||||
|
def init_rendering_env(self, base_folder: Optional[str] = None) -> Environment:
|
||||||
|
base_folder = "{}/".format(base_folder.replace(self._kernel.get_application_dir(), '')) if base_folder else ''
|
||||||
|
|
||||||
|
alias_paths = {
|
||||||
|
"::": "{}/".format(WebDirConstant.FOLDER_TEMPLATES),
|
||||||
|
"@": "{}{}/".format(base_folder, WebDirConstant.FOLDER_TEMPLATES)
|
||||||
|
}
|
||||||
|
|
||||||
|
env = Environment(
|
||||||
|
loader=AliasFileSystemLoader(
|
||||||
|
searchpath=self._kernel.get_application_dir(),
|
||||||
|
alias_paths=alias_paths
|
||||||
|
),
|
||||||
|
autoescape=select_autoescape(['html', 'xml'])
|
||||||
|
)
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
def get_rendering_env(self) -> Environment:
|
||||||
|
return self._rendering_env
|
||||||
|
|
||||||
|
def render_view(self, template_file: str, plugin: Optional = None, **parameters: dict) -> str:
|
||||||
|
base_rendering_env = plugin.get_rendering_env() if plugin else self.get_rendering_env()
|
||||||
|
template = base_rendering_env.get_template(template_file)
|
||||||
|
|
||||||
|
if plugin:
|
||||||
|
parameters['STATIC_PLUGIN_PREFIX'] = "/{}/{}/{}/{}/".format(
|
||||||
|
WebDirConstant.FOLDER_STATIC,
|
||||||
|
WebDirConstant.FOLDER_STATIC_WEB_ASSETS,
|
||||||
|
WebDirConstant.FOLDER_PLUGIN_STATIC_DST,
|
||||||
|
plugin.use_id()
|
||||||
|
)
|
||||||
|
|
||||||
|
return template.render(
|
||||||
|
request=request,
|
||||||
|
url_for=url_for,
|
||||||
|
current_user=current_user,
|
||||||
|
**parameters,
|
||||||
|
**self.get_view_globals(),
|
||||||
|
)
|
||||||
|
|||||||
@ -53,20 +53,23 @@ class WebServer:
|
|||||||
def get_app(self):
|
def get_app(self):
|
||||||
return self._app
|
return self._app
|
||||||
|
|
||||||
def get_template_folder(self) -> str:
|
def get_template_dir(self) -> str:
|
||||||
return "{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_TEMPLATES)
|
return "{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_TEMPLATES)
|
||||||
|
|
||||||
def get_static_folder(self) -> str:
|
def get_static_dir(self) -> str:
|
||||||
return "{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_STATIC)
|
return "{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_STATIC)
|
||||||
|
|
||||||
def get_web_folder(self) -> str:
|
def get_web_dir(self) -> str:
|
||||||
return "{}/{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_ASSETS)
|
return "{}/{}/{}".format(self._kernel.get_application_dir(), WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_ASSETS)
|
||||||
|
|
||||||
|
def get_plugin_static_dst_dir(self, plugin_id: str) -> str:
|
||||||
|
return "{}/{}/{}".format(self.get_web_dir(), WebDirConstant.FOLDER_PLUGIN_STATIC_DST, plugin_id)
|
||||||
|
|
||||||
def _setup_flask_app(self) -> None:
|
def _setup_flask_app(self) -> None:
|
||||||
self._app = Flask(
|
self._app = Flask(
|
||||||
__name__,
|
__name__,
|
||||||
template_folder=self.get_template_folder(),
|
template_folder=self.get_template_dir(),
|
||||||
static_folder=self.get_static_folder(),
|
static_folder=self.get_static_dir(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self._app.config['UPLOAD_FOLDER'] = "{}/{}".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_UPLOADS)
|
self._app.config['UPLOAD_FOLDER'] = "{}/{}".format(WebDirConstant.FOLDER_STATIC, WebDirConstant.FOLDER_STATIC_WEB_UPLOADS)
|
||||||
@ -118,5 +121,5 @@ class WebServer:
|
|||||||
def _setup_web_errors(self) -> None:
|
def _setup_web_errors(self) -> None:
|
||||||
@self._app.errorhandler(404)
|
@self._app.errorhandler(404)
|
||||||
def not_found(e):
|
def not_found(e):
|
||||||
return send_from_directory(self.get_template_folder(), 'core/error404.html'), 404
|
return send_from_directory(self.get_template_dir(), 'core/error404.html'), 404
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
import math
|
import math
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
def randomize_filename(old_filename: str) -> str:
|
def randomize_filename(old_filename: str) -> str:
|
||||||
@ -17,3 +18,20 @@ def convert_size(size_bytes):
|
|||||||
p = math.pow(1024, i)
|
p = math.pow(1024, i)
|
||||||
s = round(size_bytes / p, 2)
|
s = round(size_bytes / p, 2)
|
||||||
return f"{s} {size_name[i]}"
|
return f"{s} {size_name[i]}"
|
||||||
|
|
||||||
|
|
||||||
|
def copy_files(src, dst):
|
||||||
|
if not os.path.exists(dst):
|
||||||
|
os.makedirs(dst)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(src):
|
||||||
|
# Construct the destination path
|
||||||
|
dst_path = os.path.join(dst, os.path.relpath(root, src))
|
||||||
|
if not os.path.exists(dst_path):
|
||||||
|
os.makedirs(dst_path)
|
||||||
|
|
||||||
|
for file in files:
|
||||||
|
# Copy each file to the destination
|
||||||
|
src_file = os.path.join(root, file)
|
||||||
|
dst_file = os.path.join(dst_path, file)
|
||||||
|
shutil.copy2(src_file, dst_file)
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
2.1.1
|
2.2.0
|
||||||
|
|||||||
@ -168,25 +168,14 @@
|
|||||||
|
|
||||||
{% if authenticated_view %}
|
{% if authenticated_view %}
|
||||||
<div class="context-bar {{ 'hidden' if not show_context_bar }}">
|
<div class="context-bar {{ 'hidden' if not show_context_bar }}">
|
||||||
{% if current_dynmenu %}
|
|
||||||
<div class="context-menu">
|
{% block pill_menu %}
|
||||||
<div class="inner">
|
{% if current_dynmenu %}
|
||||||
<ul class="pills">
|
{% with pills=current_dynmenu.pills %}
|
||||||
{% for menu in current_dynmenu.pills %}
|
{% include 'core/pill-menu.jinja.html' %}
|
||||||
<li class="{{ 'active' if active_route == menu.route or active_route == menu.route_alt }}">
|
{% endwith %}
|
||||||
{% set href = menu.url_for if menu.url_for else url_for(menu.route) %}
|
{% endif %}
|
||||||
<a href="{{ href }}">
|
{% endblock %}
|
||||||
<span class="icon">
|
|
||||||
<i class="fa {{ menu.icon }}"></i>
|
|
||||||
</span>
|
|
||||||
{{ menu.name }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
<div class="context-divider"></div>
|
<div class="context-divider"></div>
|
||||||
|
|||||||
19
views/core/pill-menu.jinja.html
Normal file
19
views/core/pill-menu.jinja.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<div class="context-menu">
|
||||||
|
<div class="inner">
|
||||||
|
<ul class="pills">
|
||||||
|
{{ HOOK(H_ROOT_PILL_ELEMENT_START) }}
|
||||||
|
{% for menu in pills %}
|
||||||
|
<li class="{{ 'active' if active_route == menu.route or active_route == menu.route_alt }}">
|
||||||
|
{% set href = menu.url_for if menu.url_for else url_for(menu.route) %}
|
||||||
|
<a href="{{ href }}">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fa {{ menu.icon }}"></i>
|
||||||
|
</span>
|
||||||
|
{{ menu.name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
{{ HOOK(H_ROOT_PILL_ELEMENT_END) }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -36,7 +36,7 @@
|
|||||||
<div class="top-actions">
|
<div class="top-actions">
|
||||||
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
{{ HOOK(H_SLIDESHOW_CONTENT_TOOLBAR_ACTIONS_START) }}
|
||||||
|
|
||||||
<button type="button" class="folder-add btn-neutral">
|
<button type="button" class="btn folder-add btn-neutral">
|
||||||
<i class="fa fa-folder-plus icon-left"></i>
|
<i class="fa fa-folder-plus icon-left"></i>
|
||||||
{{ l.common_new_folder }}
|
{{ l.common_new_folder }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user