Added new files to admin
This commit is contained in:
parent
b265abad78
commit
b5cb79eb04
9 changed files with 927 additions and 4 deletions
|
|
@ -28,6 +28,8 @@ for name_module, module_app in config.apps.items():
|
||||||
|
|
||||||
base_path=controller_mod.__file__.replace('__init__.py', '')
|
base_path=controller_mod.__file__.replace('__init__.py', '')
|
||||||
|
|
||||||
|
app_basedir=os.path.basename(base_path[0:len(base_path)-1])
|
||||||
|
|
||||||
media_path=base_path+'media/'
|
media_path=base_path+'media/'
|
||||||
|
|
||||||
dir_controllers=os.listdir(base_path)
|
dir_controllers=os.listdir(base_path)
|
||||||
|
|
@ -39,9 +41,10 @@ for name_module, module_app in config.apps.items():
|
||||||
subcontroller_path=module+'.'+controller.replace('.py', '')
|
subcontroller_path=module+'.'+controller.replace('.py', '')
|
||||||
subcontroller_mod=import_module(subcontroller_path)
|
subcontroller_mod=import_module(subcontroller_path)
|
||||||
|
|
||||||
if os.path.isfile(media_path):
|
if os.path.isdir(media_path):
|
||||||
if yes_static:
|
if yes_static:
|
||||||
app.mount("/mediafrom/"+name_app, StaticFiles(directory=media_path), name="static_"+name_app)
|
|
||||||
|
app.mount("/mediafrom/"+app_basedir, StaticFiles(directory=media_path), name="static_"+app_basedir)
|
||||||
|
|
||||||
fast_app=getattr(controller_mod, app_module)
|
fast_app=getattr(controller_mod, app_module)
|
||||||
|
|
||||||
|
|
|
||||||
27
parameciofast/libraries/config_admin.py
Normal file
27
parameciofast/libraries/config_admin.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
"""
|
||||||
|
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2023 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Variable base for admin modules
|
||||||
|
|
||||||
|
"""Variables for control admin modules for admin module
|
||||||
|
"""
|
||||||
|
|
||||||
|
config_admin=[]
|
||||||
|
"""list: List for save the differente admin configurations from paramecio2 modules
|
||||||
|
"""
|
||||||
228
parameciofast/libraries/i18n.py
Normal file
228
parameciofast/libraries/i18n.py
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parameciofast is a series of wrappers for Fastapi, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2023 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from importlib import import_module
|
||||||
|
import gettext
|
||||||
|
#from paramecio.citoplasma.sessions import get_session
|
||||||
|
import json
|
||||||
|
#from flask import session, has_request_context
|
||||||
|
import os
|
||||||
|
#from fastapi import Request
|
||||||
|
|
||||||
|
yes_session=False
|
||||||
|
|
||||||
|
i18n_module={}
|
||||||
|
|
||||||
|
def load_lang(*args):
|
||||||
|
"""A function for load the lang module dinamically
|
||||||
|
"""
|
||||||
|
|
||||||
|
for module in args:
|
||||||
|
|
||||||
|
lang_path=module[0]+'.i18n.'+module[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
i18n_module[lang_path]=import_module(lang_path)
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# here load the language
|
||||||
|
|
||||||
|
class PGetText:
|
||||||
|
|
||||||
|
# Dict where all gettext domain are saved -> domain=name, example, admin, libraries, pastafari2, etc...
|
||||||
|
|
||||||
|
l={}
|
||||||
|
|
||||||
|
def __init__(self, module_file):
|
||||||
|
|
||||||
|
module_dir=os.path.dirname(os.path.realpath(module_file))
|
||||||
|
|
||||||
|
module_name=os.path.basename(module_dir)
|
||||||
|
|
||||||
|
if module_name not in PGetText.l:
|
||||||
|
|
||||||
|
PGetText.l[module_name]={}
|
||||||
|
|
||||||
|
for i in I18n.dict_i18n:
|
||||||
|
|
||||||
|
if i not in PGetText.l[module_name]:
|
||||||
|
|
||||||
|
PGetText.l[module_name][i]=gettext.translation(module_name, module_dir+'/languages/', languages=[i], fallback=True)
|
||||||
|
PGetText.l[module_name][i].install()
|
||||||
|
|
||||||
|
self.module=module_name
|
||||||
|
|
||||||
|
def gettext(self, text):
|
||||||
|
|
||||||
|
return PGetText.l[self.module][I18n.get_default_lang()].gettext(text)
|
||||||
|
|
||||||
|
def pgettext(module_file):
|
||||||
|
|
||||||
|
module=os.path.dirname(os.path.realpath(module_file))
|
||||||
|
|
||||||
|
base_name=os.path.dirname(os.path.realpath(module))
|
||||||
|
|
||||||
|
l=gettext.translation(os.path.basename(base_name), module+'/languages/', languages=I18n.get_default_lang(), fallback=True)
|
||||||
|
|
||||||
|
return l.gettext
|
||||||
|
|
||||||
|
class I18n:
|
||||||
|
"""Class for i18n tasks
|
||||||
|
|
||||||
|
Class for i18n tasks, how, strings for every language supported, for now are en-US and es-ES. You can add more languages adding
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
default_lang (str): The default string lang used when get someone
|
||||||
|
dict_i18n (list): The list with default languages. You can add more calling it static variable in settings/config.py
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
default_lang='en-US'
|
||||||
|
|
||||||
|
dict_i18n=['en-US', 'es-ES']
|
||||||
|
|
||||||
|
l={}
|
||||||
|
|
||||||
|
def __init__(self, module, default_lang=None):
|
||||||
|
|
||||||
|
self.module=module
|
||||||
|
self.default_lang=I18n.default_lang
|
||||||
|
|
||||||
|
def slang(self, symbol, text_default, lang=None):
|
||||||
|
"""Method for get a string from selected language but object oriented
|
||||||
|
|
||||||
|
Method for get a string from selected language but object oriented
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol (str): The symbol used for identify the text string.
|
||||||
|
text_default (str): The text default used. You have use how base for translations.
|
||||||
|
"""
|
||||||
|
return I18n.lang(self.module, symbol, text_default, lang)
|
||||||
|
|
||||||
|
def tlang(self, text_default, lang=None):
|
||||||
|
"""Method for get a string from selected language but object oriented and using module and symbol by default
|
||||||
|
|
||||||
|
Method for get a string from selected language but object oriented and using module and symbol by default
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol (str): The symbol used for identify the text string.
|
||||||
|
text_default (str): The text default used. You have use how base for translations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
symbol=text_default[:60]
|
||||||
|
|
||||||
|
return I18n.lang(self.module, symbol, text_default)
|
||||||
|
|
||||||
|
#@staticmethod
|
||||||
|
#def set_lang(code_lang):
|
||||||
|
# if default_lang
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_default_lang():
|
||||||
|
"""Static method for get the default lang"""
|
||||||
|
|
||||||
|
lang=I18n.default_lang
|
||||||
|
|
||||||
|
#if has_request_context():
|
||||||
|
#lang=session.get('lang', lang)
|
||||||
|
|
||||||
|
return lang
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def lang(module, symbol, text_default):
|
||||||
|
"""Static method for get a string from selected language
|
||||||
|
|
||||||
|
Static method used to get the string of the selected language. If there is no string in the selected language, it returns text_default.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module (str): The module to which the translation string belongs
|
||||||
|
symbol (str): Simple symbol that is useful for identify the string
|
||||||
|
text_default (str): The text used by default when there are not translation in the selected language
|
||||||
|
"""
|
||||||
|
|
||||||
|
#if not lang:
|
||||||
|
# lang=I18n.get_default_lang()
|
||||||
|
|
||||||
|
lang=I18n.get_default_lang()
|
||||||
|
|
||||||
|
I18n.l[lang]=I18n.l.get(lang, {})
|
||||||
|
|
||||||
|
I18n.l[lang][module]=I18n.l[lang].get(module, {})
|
||||||
|
|
||||||
|
I18n.l[lang][module][symbol]=I18n.l[lang][module].get(symbol, text_default)
|
||||||
|
|
||||||
|
return I18n.l[lang][module][symbol]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extract_value(value):
|
||||||
|
"""Static method for get values from json lang array
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (json): Lang dict in json format
|
||||||
|
"""
|
||||||
|
|
||||||
|
value=json.loads(value)
|
||||||
|
|
||||||
|
lang=I18n.get_default_lang()
|
||||||
|
|
||||||
|
if value[lang]!='':
|
||||||
|
|
||||||
|
return value[lang]
|
||||||
|
|
||||||
|
return value[I18n.default_lang]
|
||||||
|
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def get_browser_lang():
|
||||||
|
|
||||||
|
return request.headers.get('Accept-Language', 'en-US')
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def lang_json(module, symbol, text_default):
|
||||||
|
"""Static method for return a language dict in JSON
|
||||||
|
|
||||||
|
Static method used to get the string of the selected language in JSON format. If there are not string in the selected language, it returns text_default.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module (str): The module to which the translation string belongs
|
||||||
|
symbol (str): Simple symbol that is useful for identify the string
|
||||||
|
text_default (str): The text used by default when there are not translation in the selected language
|
||||||
|
"""
|
||||||
|
|
||||||
|
arr_final={}
|
||||||
|
|
||||||
|
for l in I18n.dict_i18n:
|
||||||
|
arr_final[l]=I18n.lang(module, symbol, text_default, l)
|
||||||
|
|
||||||
|
return json.dumps(arr_final)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def session_lang(request):
|
||||||
|
|
||||||
|
return request.session.get('lang', I18n.get_default_lang())
|
||||||
|
|
||||||
|
common_pgettext=PGetText(__file__)
|
||||||
287
parameciofast/libraries/mtemplates.py
Normal file
287
parameciofast/libraries/mtemplates.py
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
"""
|
||||||
|
Paramecio2fm is a series of wrappers for Fastapi, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Template frontend from mako.
|
||||||
|
|
||||||
|
#import gettext
|
||||||
|
from mako.template import Template
|
||||||
|
#from flask import url_for as url_for_flask
|
||||||
|
from mako.lookup import TemplateLookup
|
||||||
|
from os import path
|
||||||
|
try:
|
||||||
|
from settings import config
|
||||||
|
except:
|
||||||
|
class config:
|
||||||
|
theme='default'
|
||||||
|
reloader=False
|
||||||
|
|
||||||
|
#import gettext
|
||||||
|
import sys
|
||||||
|
from parameciofast.libraries.i18n import I18n, PGetText
|
||||||
|
from parameciofast.libraries.urls import make_url, make_media_url, add_get_parameters
|
||||||
|
#from parameciofast.libraries.formsutils import csrf_token
|
||||||
|
|
||||||
|
framework='flask'
|
||||||
|
|
||||||
|
if hasattr(config, 'framework'):
|
||||||
|
framework=config.framework
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def _(text):
|
||||||
|
|
||||||
|
return gettext.gettext(text)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def env_theme(module, cache_enabled=True, cache_impl='', cache_args={}, module_directory="./tmp/modules"):
|
||||||
|
"""Function for create an environment for mako templates
|
||||||
|
|
||||||
|
Function for create an environment for mako templates. Really is a shortcut for TemplateLookup mako function. You can define cache options and module_directory for templates compiled
|
||||||
|
|
||||||
|
Args:
|
||||||
|
module (str): The module where the templates can be founded
|
||||||
|
cache_enabled (boolean): If True then mako template cache is enabled, is False, mako template cache is disabled.
|
||||||
|
cache_args (dict): Cache Args dict parameter for TemplateLookup function from Mako templates. View Mako Templates documentation.
|
||||||
|
module_directory (str): Module directory parameter for TemplateLookup function from Mako templates. View Mako Templates documentation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
template (TemplateLookup): Return TemplateLookup object
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
ext=module[len(module)-3:]
|
||||||
|
|
||||||
|
if ext=='.py':
|
||||||
|
|
||||||
|
module=path.dirname(module)
|
||||||
|
|
||||||
|
standard_templates=path.dirname(__file__)+'/templates'
|
||||||
|
|
||||||
|
standard_languages=path.dirname(__file__)+'/languages'
|
||||||
|
|
||||||
|
module_directory+='/'+module
|
||||||
|
|
||||||
|
module_templates=module+'/templates'
|
||||||
|
|
||||||
|
theme_templates='themes/'+config.theme+'/templates'
|
||||||
|
|
||||||
|
search_folders=[theme_templates, module_templates, standard_templates]
|
||||||
|
|
||||||
|
#if self.inject_folder is not None:
|
||||||
|
#search_folders.insert(1, self.inject_folder+'/templates')
|
||||||
|
|
||||||
|
#Standard templates
|
||||||
|
#print(standard_templates)
|
||||||
|
return TemplateLookup(directories=search_folders, default_filters=['h'], input_encoding='utf-8', encoding_errors='replace', cache_enabled=cache_enabled, cache_impl=cache_impl, cache_args=cache_args, module_directory=module_directory, filesystem_checks=config.reloader)
|
||||||
|
|
||||||
|
class PTemplate:
|
||||||
|
"""A class used how shortcuts for Mako template functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
templates_loaded={}
|
||||||
|
|
||||||
|
def __init__(self, environment, url_for):
|
||||||
|
"""A class used how shortcuts for Mako template functions.
|
||||||
|
|
||||||
|
This class is used to have a set of shortcuts and hooks to Mako templates functions and methods over a series of default options.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
environment (TemplateLookup): A TemplateLookup object generated with env_theme function
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
autoescape_ext (set): A set of extensions file where automatic autoescape is used
|
||||||
|
environment (TemplateLookup): A TemplateLookup object generated with env_theme function
|
||||||
|
filters (list): A list of functions used for add filters to your templates.
|
||||||
|
js (list): A list of javascript sources for generate js html load tags.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.autoescape_ext=('html', 'htm', 'xml', 'phtml', 'js')
|
||||||
|
|
||||||
|
self.env=environment
|
||||||
|
|
||||||
|
self.filters={}
|
||||||
|
|
||||||
|
self.js={}
|
||||||
|
|
||||||
|
base_name=path.dirname(path.realpath(__file__))
|
||||||
|
|
||||||
|
#self.i18n=I18n(base_name, default_lang)
|
||||||
|
|
||||||
|
#self.add_filter(self.i18n.lang)
|
||||||
|
|
||||||
|
#self.add_filter(make_url)
|
||||||
|
|
||||||
|
self.add_filter(make_media_url)
|
||||||
|
"""
|
||||||
|
if not url_for_function:
|
||||||
|
|
||||||
|
url_for=url_for_flask
|
||||||
|
|
||||||
|
self.add_filter(url_for)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
url_for=url_for_function
|
||||||
|
|
||||||
|
self.add_filter(url_for)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.add_filter(url_for)
|
||||||
|
|
||||||
|
#self.add_filter(csrf_token)
|
||||||
|
|
||||||
|
#self.add_filter(add_get_parameters)
|
||||||
|
|
||||||
|
self.add_filter(self.add_js)
|
||||||
|
|
||||||
|
self.add_filter(self.load_js)
|
||||||
|
|
||||||
|
# Loading language domain for gettext in templates
|
||||||
|
|
||||||
|
module_env=self.env.directories[1].replace('/templates', '')
|
||||||
|
|
||||||
|
#print(path.basename(module_env)+' '+base_name+'/languages/')
|
||||||
|
|
||||||
|
self.l=PGetText(module_env+'/app.py')
|
||||||
|
|
||||||
|
self.add_filter(self._)
|
||||||
|
|
||||||
|
#self.add_filter(self.i18n.slang)
|
||||||
|
|
||||||
|
#self.add_filter(self.i18n.tlang)
|
||||||
|
|
||||||
|
def _(self, text):
|
||||||
|
|
||||||
|
return self.l.gettext(text)
|
||||||
|
|
||||||
|
def add_js(self, js, module=''):
|
||||||
|
"""Function for add js to self.js attribute
|
||||||
|
|
||||||
|
Add a js file name to an attribute list called self.js with the <script> tag to add it to the header of an html code
|
||||||
|
|
||||||
|
Args:
|
||||||
|
js (str): The name of js file. Normally you save the file in media/js folder in module or theme
|
||||||
|
module (str): Optional. Where is saved the js file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dummy (str): Dummy return necessary because mako expect return something
|
||||||
|
"""
|
||||||
|
if not js in self.js:
|
||||||
|
self.js[js]='<script language="Javascript" src="{}"></script>'.format(make_media_url('js/'+js, module))
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def load_js(self):
|
||||||
|
|
||||||
|
return "\n".join(self.js.values())
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
def gettext(self, text):
|
||||||
|
return gettext.dgettext(self.domain_gettext, text)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def load_template(self, template_file, **arguments):
|
||||||
|
"""Load a mako template and return the result
|
||||||
|
|
||||||
|
Load a mako template and return the results with different arguments applied
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_file (str): The name of template file. The template is searched using configuration defined in self.env
|
||||||
|
**arguments (mixed): Extra arguments with variables passed to template
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
template (str): Return a template rendered using mako class from self.env
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.prepare_gettext:
|
||||||
|
|
||||||
|
module_lang_dir=self.env.directories[1].replace('/templates', '/locales')
|
||||||
|
|
||||||
|
module_name=path.basename(module_lang_dir.replace('/locales', ''))
|
||||||
|
|
||||||
|
en = gettext.translation(module_name, localedir=module_lang_dir, languages=['en'])
|
||||||
|
en.install()
|
||||||
|
_=en.gettext
|
||||||
|
|
||||||
|
arguments.update({'_': _})
|
||||||
|
"""
|
||||||
|
template = self.env.get_template(template_file)
|
||||||
|
|
||||||
|
arguments.update(self.filters)
|
||||||
|
|
||||||
|
return template.render(**arguments)
|
||||||
|
|
||||||
|
def guess_autoescape(self, template_name):
|
||||||
|
"""Simple helper method for get an extension from filename
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_name (str): The template name
|
||||||
|
"""
|
||||||
|
|
||||||
|
if template_name is None or '.' not in template_name:
|
||||||
|
return False
|
||||||
|
|
||||||
|
ext = template_name.rsplit('.', 1)[1]
|
||||||
|
|
||||||
|
return ext in self.autoescape_ext
|
||||||
|
|
||||||
|
def render_template(self, template_file, **arguments):
|
||||||
|
"""Experimental method for parse a template
|
||||||
|
|
||||||
|
Experimental method for parse a template, similar to load_template but try cache the template loaded
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_file (str): The name of template file. The template is searched using configuration defined in self.env
|
||||||
|
**arguments (mixed): Extra arguments with variables passed to template
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dummy (str): Dummy return necessary because mako expect return something
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not str(self.env.directories)+'/'+template_file in PTemplate.templates_loaded:
|
||||||
|
PTemplate.templates_loaded[str(self.env.directories)+'/'+template_file]=self.env.get_template(template_file)
|
||||||
|
|
||||||
|
arguments.update(self.filters)
|
||||||
|
|
||||||
|
return PTemplate.templates_loaded[str(self.env.directories)+'/'+template_file].render(**arguments)
|
||||||
|
|
||||||
|
def add_filter(self, filter_name):
|
||||||
|
"""Method for add filters to self.filters attributes
|
||||||
|
|
||||||
|
Method for add filters to self.filters attributes for use in templates
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filter_name (function): Filter function
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.filters[filter_name.__name__]=filter_name
|
||||||
|
|
||||||
|
def add_css_home_local(file_css, module):
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
#env=env_theme(__file__)
|
||||||
|
|
||||||
|
#standard_t=PTemplate(env)
|
||||||
|
|
||||||
89
parameciofast/libraries/session.py
Normal file
89
parameciofast/libraries/session.py
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from settings import config
|
||||||
|
from itsdangerous.url_safe import URLSafeTimedSerializer
|
||||||
|
|
||||||
|
import os
|
||||||
|
try:
|
||||||
|
import ujson as json
|
||||||
|
except:
|
||||||
|
import json
|
||||||
|
|
||||||
|
# Cookie session
|
||||||
|
# This save the session in a cookie for maximum performance. In next version i can use memcached or something for session
|
||||||
|
# In next versions have two secret_keys for more security.
|
||||||
|
|
||||||
|
class ParamecioSession:
|
||||||
|
|
||||||
|
def __init__(self, session_cookie):
|
||||||
|
|
||||||
|
self.serializer=URLSafeTimedSerializer(config.secret_key)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
self.session=self.serializer.loads(session_cookie)
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.session={}
|
||||||
|
|
||||||
|
def get(self, name, default_value):
|
||||||
|
|
||||||
|
if not name in self.session:
|
||||||
|
self.session[name]=default_value
|
||||||
|
|
||||||
|
return self.session[name]
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
|
||||||
|
return self.session[key]
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
|
||||||
|
self.session[key]=value
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
|
||||||
|
if key!='token':
|
||||||
|
del self.session[key]
|
||||||
|
|
||||||
|
def __contains__(self, key):
|
||||||
|
|
||||||
|
if key in self.session:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self.session
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.session.__str__()
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return self.session.keys()
|
||||||
|
"""
|
||||||
|
def remove(self):
|
||||||
|
response.delete_cookie(config.cookie_name, path="/")
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.remove()
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
|
||||||
|
# Here get the function for load session
|
||||||
|
|
||||||
|
#save_session(self.session['token'], self.session)
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
|
||||||
|
return self.serializer.dumps(session_cookie)
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
|
||||||
|
#token=self.session['token']
|
||||||
|
self.session={}
|
||||||
|
#self.save()
|
||||||
|
|
||||||
133
parameciofast/libraries/urls.py
Normal file
133
parameciofast/libraries/urls.py
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
"""
|
||||||
|
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
|
||||||
|
|
||||||
|
Copyright (C) 2023 Antonio de la Rosa Caballero
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from settings import config
|
||||||
|
except:
|
||||||
|
|
||||||
|
class config:
|
||||||
|
|
||||||
|
application_root='/'
|
||||||
|
domain_url='http://localhost/'
|
||||||
|
yes_static=False
|
||||||
|
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
def make_url(path, query_args={}):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for create urls for the system
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
path -- The path to the module
|
||||||
|
query_args -- a ser of get variables for add to url
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
get_query=''
|
||||||
|
|
||||||
|
if len(query_args)>0:
|
||||||
|
|
||||||
|
get_query='?'+urllib.parse.urlencode(query_args)
|
||||||
|
|
||||||
|
path=urllib.parse.quote_plus(path, safe='/')
|
||||||
|
|
||||||
|
return config.application_root+path+get_query
|
||||||
|
|
||||||
|
def make_url_domain(path, query_args={}):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for create urls for the system, using the domain_url config variable how prefix
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
path -- The path to the module
|
||||||
|
query_args -- a ser of get variables for add to url
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return config.domain_url+make_url(path, query_args)
|
||||||
|
|
||||||
|
def make_external_url(path, query_args={}):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for create urls for external systems
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
path -- The base url of the url
|
||||||
|
query_args -- a ser of get variables for add to url
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
get_query=''
|
||||||
|
|
||||||
|
if len(query_args)>0:
|
||||||
|
|
||||||
|
get_query='?'+urllib.parse.urlencode(query_args)
|
||||||
|
|
||||||
|
return path+get_query
|
||||||
|
|
||||||
|
def add_get_parameters(url, **args):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for add args to existent url
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
url -- The url
|
||||||
|
args -- a ser of get variables for add to url
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
added_url='&'
|
||||||
|
|
||||||
|
if url.find('?')==-1:
|
||||||
|
added_url='?'
|
||||||
|
|
||||||
|
get_query=urllib.parse.urlencode(args)
|
||||||
|
|
||||||
|
return url+added_url+get_query
|
||||||
|
|
||||||
|
|
||||||
|
if config.yes_static==True:
|
||||||
|
|
||||||
|
def make_media_url(file_path, module):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for create urls for media resources.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
file_path -- The relative path of module
|
||||||
|
module -- the module where you can find the resource
|
||||||
|
"""
|
||||||
|
|
||||||
|
return make_url('mediafrom/'+module+'/'+file_path)
|
||||||
|
#config.media_url+'mediafrom/'+module+'/'+file_path
|
||||||
|
else:
|
||||||
|
|
||||||
|
def make_media_url(file_path, module):
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is a method for create urls for media resources if config.yes_static is disabled..
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
file_path -- The relative path of module
|
||||||
|
module -- the module where you can find the resource
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
return config.media_url+urllib.parse.quote_plus(module)+'/'+urllib.parse.quote_plus(file_path, safe='/')
|
||||||
|
|
@ -3,6 +3,11 @@ from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from parameciofast.modules.fastadmin import admin_app
|
from parameciofast.modules.fastadmin import admin_app
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from parameciofast.fast import app
|
from parameciofast.fast import app
|
||||||
|
from parameciofast.libraries.i18n import I18n
|
||||||
|
from parameciofast.libraries.mtemplates import env_theme, PTemplate
|
||||||
|
|
||||||
|
env=env_theme(__file__)
|
||||||
|
t=PTemplate(env, app.url_path_for)
|
||||||
|
|
||||||
@admin_app.get('/', response_class=HTMLResponse)
|
@admin_app.get('/', response_class=HTMLResponse)
|
||||||
def home_admin(request: Request, paramecio_session: Annotated[str | None, Cookie(description='Cookie for validate into the admin site. The cookie name can change in you settings/config.py')] = None):
|
def home_admin(request: Request, paramecio_session: Annotated[str | None, Cookie(description='Cookie for validate into the admin site. The cookie name can change in you settings/config.py')] = None):
|
||||||
|
|
@ -17,8 +22,10 @@ def home_admin(request: Request, paramecio_session: Annotated[str | None, Cookie
|
||||||
|
|
||||||
|
|
||||||
@admin_app.get('/login', response_class=HTMLResponse)
|
@admin_app.get('/login', response_class=HTMLResponse)
|
||||||
def login_admin():
|
def login_admin(request: Request):
|
||||||
|
|
||||||
|
#session=request.session
|
||||||
|
|
||||||
|
i18n=I18n('admin', I18n.session_lang(request))
|
||||||
|
|
||||||
return "Login"
|
return t.load_template('login.phtml', title=i18n.tlang('Login'), tlang=i18n.tlang, url_for=app.url_path_for)
|
||||||
|
|
|
||||||
78
parameciofast/modules/fastadmin/media/css/layout.css
Normal file
78
parameciofast/modules/fastadmin/media/css/layout.css
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/* Loader page */
|
||||||
|
|
||||||
|
.loader-div {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index:999999;
|
||||||
|
/*background: url('../images/caledonian.png') no-repeat center 40%;
|
||||||
|
background-color: #ec1c24;*/
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: relative;
|
||||||
|
width: 10vw;
|
||||||
|
height: 5vw;
|
||||||
|
padding: 1.5vw;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span {
|
||||||
|
position: absolute;
|
||||||
|
height: 0.8vw;
|
||||||
|
width: 0.8vw;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(1) {
|
||||||
|
animation: loading-dotsA 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader span:nth-child(2) {
|
||||||
|
animation: loading-dotsB 0.5s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-dotsA {
|
||||||
|
0% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translateX(2vw);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translateY(2vw);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes loading-dotsB {
|
||||||
|
0% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: translateX(-2vw);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: translateY(-2vw);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
parameciofast/modules/fastadmin/templates/login.phtml
Normal file
71
parameciofast/modules/fastadmin/templates/login.phtml
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>${title}</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
<link href="${make_media_url('css/layout.css', 'fastadmin')}" rel="stylesheet" />
|
||||||
|
<%block name="css">
|
||||||
|
</%block>
|
||||||
|
<%block name="header_js">
|
||||||
|
</%block>
|
||||||
|
</head>
|
||||||
|
<body data-bs-theme="dark">
|
||||||
|
<div class="loader-div" id="loader-div" style="display:none;">
|
||||||
|
<span class="loader">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid m-0 p-0">
|
||||||
|
<div class="d-flex align-items-center" style="height:100vh;">
|
||||||
|
<div class="col-3"></div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header bg-primary">
|
||||||
|
${tlang('Login')}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST" action="${url_for('login_admin')}" id="login_form">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="username_form" class="form-label">${tlang('Username')}</label>
|
||||||
|
<input type="text" class="form-control form-control-lg" id="username_form" name="password" aria-describedby="username" autocomplete="off">
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
${tlang('You need a valid username and password')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password_form" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control form-control-lg" id="password_form" name="password" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 form-check">
|
||||||
|
<input type="checkbox" class="form-check-input" id="autologin" name="autologin">
|
||||||
|
<label class="form-check-label" for="autologin">${tlang('Autologin')}</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
document.documentElement.setAttribute('data-bs-theme','dark');
|
||||||
|
|
||||||
|
/*setTimeout(function () {
|
||||||
|
|
||||||
|
$('#loader-div').fadeOut(2000);
|
||||||
|
|
||||||
|
|
||||||
|
}, 1000);*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue