Compare commits

..

No commits in common. "master" and "dark_theme" have entirely different histories.

110 changed files with 844 additions and 3138 deletions

2
.gitignore vendored
View file

@ -47,7 +47,7 @@ coverage.xml
.hypothesis/
# Translations
#*.mo
*.mo
*.pot
# Django stuff:

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "paramecio2/modules/admin/media/js/jsutils"]
path = paramecio2/modules/admin/media/js/jsutils
url = https://git.cuchulu.com/paramecio/jsutils.git

View file

@ -1,21 +1,23 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
@ -60,7 +72,7 @@ modification follow.
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
@ -633,29 +635,41 @@ the "copyright" line and a pointer to where the full notice is found.
Copyright (C) <year> <name of author>
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
it under the terms of the GNU 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.
GNU 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/>.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -12,7 +12,7 @@ Also, you need the next software installed in your os:
### Python 3.10 or later.
Paramecio should work fine in 3.5-3.9 but is tested in 3.10, 3.11 and 3.12 python 3 versions actually.
Paramecio should work fine in 3.5-3.9 but is tested in 3.10 and 3.11 python 3 versions actually.
In Debian and Ubuntu you can install Python 3 using the next command: `apt-get install python3`.
@ -20,7 +20,7 @@ In Fedora and other Red Hat derived distros you can use `yum install python3`. I
### MySQL or MariaDB database servers.
MariaDB 10.6 and later are recommended but is compatible with previous versions.
MariaDB 10.6 and later are recommended but is compatible with MariaDB 10.
In Debian and Ubuntu you can install MariaDB using the next command: `apt-get install mariadb-server`.
@ -53,7 +53,7 @@ You can install the framework using the next command in your server:
or if you want development version:
`pip3 install git+https://git.cuchulu.com/paramecio/paramecio2fm.git`
`pip3 install git+https://git.cuchulu.com/absurdo/paramecio2fm.git`
This command will install in your server paramecio framework with its dependencies.
@ -67,6 +67,3 @@ If you get an error in your installation of dependencies how MarkupSafe or SqlAl
`apt-get install python3-mako python3-sqlalchemy` and try pip3 command again.
## Documentation
You can find paramecio2 documentation in this [site](https://docs.cuchulu.com/paramecio2/)

View file

@ -1,5 +1,5 @@
==========================================================
Paramecio is a simple framework based in flask and mako.
Paramecio is a simple framework based in bottle and mako.
==========================================================

View file

@ -1,19 +0,0 @@
"""
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
Copyright (C) 2025 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/>.
"""

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from flask import Flask, session, url_for, request, send_file, abort
from settings import config
from importlib import import_module
@ -24,28 +5,6 @@ import os
import sys
import inspect
from paramecio2.libraries.datetime import set_timezone
try:
import ujson as json
except:
import json
flask_session=False
if hasattr(config, 'flask_session'):
flask_session=config.flask_session
if flask_session:
from flask_session import Session
from redis import Redis
SESSION_TYPE='redis'
SESSION_REDIS=Redis(host='localhost', port=6379)
swagger_app=False
if hasattr(config, 'swagger_app'):
swagger_app=config.swagger_app
def start_app():
@ -88,13 +47,7 @@ def start_app():
workdir=os.getcwd()
arr_module_path={}
# Load blueprints from json file (for installed modules using paramecio2 utilities)
if os.path.isfile('./settings/modules.json'):
with open('./settings/modules.json') as f:
json_apps=json.loads(f.read())
config.apps=json_apps | config.apps
# Load blueprints
for key_app, added_app in config.apps.items():
@ -106,14 +59,9 @@ def start_app():
app_name=getattr(controller_path, added_app[1])
#print(os.path.dirname(sys.modules[added_app[0]].__file__))
#print(key_app)
arr_module_path[key_app]=os.path.dirname(sys.modules[added_app[0]].__file__)
for controller in dir_controllers:
if controller.find('.py')!=-1 and not controller.startswith('__'):
if controller.find('.py')!=-1 and controller.find('__init__')==-1:
controller_py=controller.replace('.py', '')
@ -121,8 +69,7 @@ def start_app():
a=import_module(module_app)
#arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__)
arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__)
if config.application_root=='/':
app.register_blueprint(app_name, url_prefix=added_app[2])
else:
@ -148,10 +95,6 @@ def start_app():
if not os.path.isfile(file_path):
#file_path=workdir+'/modules/'+module+'/media/'+media_file
if not module in arr_module_path:
abort(404)
file_path=arr_module_path[module]+'/media/'+media_file
if not os.path.isfile(file_path):
@ -159,12 +102,4 @@ def start_app():
return send_file(file_path)
if swagger_app:
from flasgger import Swagger
swagger=Swagger(app)
if flask_session:
app.config.from_object(__name__)
Session(app)
return app

View file

@ -1,5 +1,4 @@
from paramecio2.libraries.db.webmodel import WebModel
from paramecio2.libraries.load_apps import load_extra_modules
secret_key="im smoking fool"
@ -11,8 +10,6 @@ static_url_path='/media/default'
apps={'welcome': ['paramecio2.modules.welcome', 'welcome_app', '/']}
apps=load_extra_modules(apps)
default_module='welcome'
theme="default"

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
import argparse
import os
import shutil
@ -41,7 +22,7 @@ def start():
parser.add_argument('--symlink', help='Set if create direct symlink to paramecio in new site', action='store_true')
#parser.add_argument('--tests', help='Create a symlink to tests for check into paramecio site', action='store_true')
parser.add_argument('--tests', help='Create a symlink to tests for check into paramecio site', action='store_true')
# Options for deploy
@ -49,9 +30,9 @@ def start():
parser.add_argument('--folder', help='If you deploy in a subdirectory, set it, without beggining and ending slashes', required=False)
#parser.add_argument('--host', help='The host ip or domain where the app is binded', required=False)
parser.add_argument('--host', help='The host ip or domain where the app is binded', required=False)
#parser.add_argument('--port', help='Change the default port 8080 to other number. Use 80 is not recommended, use 80 for the proxy server how nginx or apache', required=False)
parser.add_argument('--port', help='Change the default port 8080 to other number. Use 80 is not recommended, use 80 for the proxy server how nginx or apache', required=False)
args=parser.parse_args()
@ -107,14 +88,12 @@ def start():
except:
print('Error: cannot symlink paramecio in new site')
"""
if args.tests==True:
try:
os.symlink(workdir, args.path+'/paramecio2/', True)
except:
print('Error: cannot symlink paramecio2 in new site')
"""
with open(path_settings+'/config.py', 'r') as f:
conf=f.read()
@ -372,7 +351,7 @@ def start():
models_files=os.listdir(models_path)
m=re.compile(r".*\.py$")
m=re.compile(".*\.py$")
underscore=re.compile("^__.*")

View file

@ -1,25 +1,3 @@
"""
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/>.
"""
from paramecio2.app import start_app
app=start_app()
application=app

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.app import start_app
app=start_app()

View file

@ -1,18 +0,0 @@
"""
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/>.
"""

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
import argparse
import os, sys
@ -28,7 +9,6 @@ import re
from pathlib import Path
from importlib import import_module
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.slugify import slugify
try:
from settings import config
except:
@ -39,18 +19,12 @@ except:
"""Command line utility for extract I18n.lang strings from html templates and .py files
"""
pattern=re.compile(r'^\w+\.(py|html|phtml|js)$')
pattern=re.compile('^\w+\.(py|html|phtml|js)$')
ignored=re.compile(r'^[__|\.].*$')
ignored=re.compile('^[__|\.].*$')
lang_p=re.compile(r"I18n\.lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)")
lang_t=re.compile(r"\${lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)\}")
lang_s=re.compile(r"i18n\.slang\('(.*?)',\s+'(.*?)'\)")
lang_ts=re.compile(r"\${slang\('(.*?)',\s+'(.*?)'\)\}")
lang_tl=re.compile(r"i18n\.tlang\('(.*?)'\)")
lang_ttl=re.compile(r"\${tlang\('(.*?)'\)\}")
lang_p=re.compile("I18n\.lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)")
lang_t=re.compile("\${lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)\}")
tmp_lang={}
@ -60,16 +34,12 @@ def start():
global lang_p
global lang_t
global lang_s
global lang_tl
global lang_ts
global lang_ttl
# Module to search a file where save the file.
parser = argparse.ArgumentParser(description='A tool for create python language files')
parser.add_argument('--module', help='The module where search lang files', required=True)
parser.add_argument('--module', help='The module where search lang files', required=False)
args = parser.parse_args()
@ -87,9 +57,9 @@ def start():
module_base=os.path.basename(args.module)
lang_p=re.compile(r"I18n\.lang\('("+module_base+r"?)',\s+'(.*?)',\s+'(.*?)'\)")
lang_p=re.compile("I18n\.lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)")
#lang_t=re.compile("\${lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)\}")
lang_t=re.compile(r"lang\('("+module_base+r"?)',\s+'(.*?)',\s+'(.*?)'\)")
lang_t=re.compile("lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)")
if not os.path.isdir(path):
@ -97,7 +67,7 @@ def start():
exit(1)
scandir(path, path_save, module_base)
scandir(path, path_save)
#Save the files
@ -166,7 +136,7 @@ def start():
pass
def scandir(path, module_search='', module_base=None):
def scandir(path, module_search=''):
list=os.listdir(path)
@ -188,19 +158,6 @@ def scandir(path, module_search='', module_base=None):
match_p=lang_p.findall(line)
match_t=lang_t.findall(line)
match_s=lang_s.findall(line)
match_ts=lang_ts.findall(line)
match_tl=lang_tl.findall(line)
match_ttl=lang_ttl.findall(line)
"""
global lang_s
global lang_tl
global lang_ts
global lang_ttl
"""
if match_p!=None:
for m in match_p:
@ -232,44 +189,6 @@ def scandir(path, module_search='', module_base=None):
tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default)
if module_base:
if match_s!=None:
for m in match_s:
module=base_module
symbol=m[0]
text_default=m[1]
tmp_lang[module]=tmp_lang.get(module, {})
tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default)
for m in match_ts:
module=base_module
symbol=m[0]
text_default=m[1]
tmp_lang[module]=tmp_lang.get(module, {})
tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default)
for m in match_tl:
module=base_module
symbol=slugify(m[0])
text_default=m[0]
tmp_lang[module]=tmp_lang.get(module, {})
tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default)
for m in match_ttl:
module=base_module
symbol=slugify(m[0][:40])
text_default=m[0]
tmp_lang[module]=tmp_lang.get(module, {})
tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default)
f.close()

View file

@ -1,21 +1,3 @@
"""
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

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
import time
from datetime import date, datetime, tzinfo
import arrow

View file

@ -1,27 +1,7 @@
"""
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/>.
"""
from paramecio2.libraries.db.webmodel import PhangoField
from paramecio2.libraries.db import coreforms
from paramecio2.libraries.i18n import I18n
#from bs4 import BeautifulSoup
import bleach
from bs4 import BeautifulSoup
class IntegerField(PhangoField):
@ -38,12 +18,6 @@ class IntegerField(PhangoField):
super(IntegerField, self).__init__(name, size, required)
self.default_value=0
self.type_sql='int({})'.format(self.size)
self.default_value=0
self.jtype='integer'
self.jformat='int64'
self.jexample='1'
def check(self, value):
@ -88,11 +62,6 @@ class BigIntegerField(IntegerField):
"""
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='bigint({})'.format(self.size)
def get_type_sql(self):
"""Method for return the sql code for this type
@ -118,10 +87,6 @@ class FloatField(PhangoField):
self.error_default="The value is zero"
self.default_value=0
self.type_sql='float'.format(self.size)
self.jtype='number'
self.jformat='float'
def check(self, value):
@ -162,13 +127,6 @@ class FloatField(PhangoField):
class DecimalField(FloatField):
"""PhangoField field for Decimals fields."""
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='decimal(20,2)'
self.jtype='number'
def get_type_sql(self):
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
@ -176,14 +134,6 @@ class DecimalField(FloatField):
class DoubleField(FloatField):
"""PhangoField field for Double fields."""
def __init__(self, name, size=11, required=False):
super().__init__(name, size, required)
self.type_sql='double'
self.jtype='number'
self.jformat='double'
def get_type_sql(self):
return 'DOUBLE NOT NULL DEFAULT "0"'
@ -212,8 +162,6 @@ class TextField(PhangoField):
super().__init__(name, 11, required)
self.type_sql='text'
self.set_default='NOT NULL'
def get_type_sql(self):
@ -230,20 +178,6 @@ class LongTextField(TextField):
Class used for text fields, use LONGTEXT sql type for the this field.
"""
def __init__(self, name, required=False):
"""Init TextField class different to standard PhangoField
Args:
name (str): The name of new field
required (bool): Boolean for define if field is required or not
Attributes:
set_default (str): Set if the value es NOT NULL or not
"""
super().__init__(name, required)
self.type_sql='longtext'
def get_type_sql(self):
"""Method for return the sql code for this type
@ -270,7 +204,7 @@ class HTMLField(TextField):
"""
super().__init__(name, required)
self.trusted_tags=['b', 'strong']
self.trusted_tags=[]
def check(self, value):
"""Check method for html values
@ -278,12 +212,9 @@ class HTMLField(TextField):
This check method use beautifulsoap for clean and format html code
"""
# leach.clean('<p>"trial"</p><script></script>', tags=('p'))
"""
soup=BeautifulSoup(value, features='html.parser')
for tag in soup.findAll(True):
if tag.name not in self.trusted_tags:
tag.hidden=True
@ -296,17 +227,6 @@ class HTMLField(TextField):
return value
"""
value=bleach.clean(value, tags=self.trusted_tags)
if self.escape:
return value.replace('"', '&quot;')
else:
return value
class ForeignKeyField(IntegerField):
"""Subclass of IntegerField for create Foreign keys
@ -385,12 +305,6 @@ class BooleanField(IntegerField):
self.default_error="Need 0 or 1 value"
self.default_value=0
self.type_sql='tinyint(1)'
self.jtype='boolean'
#self.jformat='0'
self.jexample='0'
def check(self, value):
self.error=False

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from collections import OrderedDict
from html import escape
@ -263,8 +244,7 @@ class SelectModelForm(SelectForm):
arr_son[arr_value[self.field_parent]]=[]
if arr_value[self.field_value]!=self.model.model_id:
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])])
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], arr_value[self.field_name]])
self.create_son(0, arr_son)

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
import argparse
import os,traceback
import sys, inspect
@ -130,32 +111,6 @@ def start():
new_tables=[x for x in tables if x not in table_exists]
# Get foreignkeys
# SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA='catalogdev_db' AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'FOREIGN KEY' ;
foreignkey_fields={}
#| CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | TABLE_SCHEMA | TABLE_NAME | CONSTRAINT_TYPE |
#+--------------------+-------------------+-----------------------------------------+---------------+-------------------+-----------------+
#| def | catalogdev_db | product_id_attributesIDX | catalogdev_db | attributes | FOREIGN KEY |
#WebModel.connections
db_name=WebModel.connections['default']['db']
with connection.query('SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA=%s AND information_schema.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = %s', [db_name, 'FOREIGN KEY']) as cursor:
for row in cursor:
if not row['TABLE_NAME'] in foreignkey_fields:
foreignkey_fields[row['TABLE_NAME']]=[]
foreignkey_fields[row['TABLE_NAME']]=row['CONSTRAINT_NAME'].replace('_{}IDX'.format(row['TABLE_NAME']), '')
pass
#If don't want order
#new_tables=set(tables)-set(table_exists)
@ -213,23 +168,6 @@ def start():
print(Style.BRIGHT+"Checking old versions of model for find changes...")
for table in tables:
#print(table)
table_fields={table: {}}
# Field | Type | Null | Key | Default | Extra |
#| id | int(11) | NO | PRI | NULL | auto_increment |
with connection.query('describe %s' % table) as cursor:
#all_fields=cursor.fetchall()
#print(all_fields)
for row in cursor:
table_fields[table][row['Field']]={'type': row['Type'], 'key': row['Key']}
pass
#print(table_fields)
#connection.query("")
#Check if new table
@ -252,8 +190,7 @@ def start():
for f, v in WebModel.model[table].fields.items():
#if not f in WebModel.model[old_table].fields:
if not f in table_fields[table]:
if not f in WebModel.model[old_table].fields:
fields_to_add.append(f)
@ -297,15 +234,13 @@ def start():
#Add index
#if v.indexed==True and v_old.indexed==False:
if v.indexed==True and table_fields[table][f]['key']!='MUL':
if v.indexed==True and v_old.indexed==False:
fields_to_add_index.append(f)
changes+=1
#if v.indexed==False and v_old.indexed==True:
if v.indexed==False and table_fields[table][f]['key']=='MUL' and v.foreignkey==False:
if v.indexed==False and v_old.indexed==True:
fields_to_delete_index.append(f)
@ -313,15 +248,13 @@ def start():
#Add unique
#if v.unique==True and v_old.unique==False:
if v.unique==True and table_fields[table][f]['key']!='UNI':
if v.unique==True and v_old.unique==False:
fields_to_add_unique.append(f)
changes+=1
#if v.unique==False and v_old.unique==True:
if v.unique==False and table_fields[table][f]['key']=='UNI':
if v.unique==False and v_old.unique==True:
fields_to_delete_unique.append(f)
@ -329,43 +262,29 @@ def start():
#Add constraint
#if v.foreignkey==True and v_old.foreignkey==False:
if v.foreignkey==True and table_fields[table][f]['key']!='MUL':
if v.foreignkey==True and v_old.foreignkey==False:
fields_to_add_constraint.append(f)
changes+=1
#if v.foreignkey==False and v_old.foreignkey==True:
if v.foreignkey==False and table_fields[table][f]['key']=='MUL':
if v.foreignkey==False and v_old.foreignkey==True:
if table in foreignkey_fields:
fields_to_delete_constraint.append(f)
if f in foreignkey_fields[table]:
changes+=1
fields_to_delete_constraint.append(f)
changes+=1
# Clean fields
#for f, v in WebModel.model[old_table].fields.items():
for f, v in table_fields[table].items():
for f, v in WebModel.model[old_table].fields.items():
if not f in WebModel.model[table].fields:
#Add constraint
#if v.foreignkey==True:
if v.foreignkey==True:
if table in foreignkey_fields:
fields_to_delete_constraint.append(f)
if f in foreignkey_fields[table]:
fields_to_delete_constraint.append(f)
changes+=1
changes+=1
fields_to_delete.append(f)

View file

@ -1,18 +0,0 @@
"""
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/>.
"""

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.webmodel import PhangoField,WebModel
import json
@ -39,12 +20,6 @@ class ArrayField(PhangoField):
self.set_default='NOT NULL'
self.type_sql='text'
self.jtype='array'
self.default_value='[]'
def check(self, value):
if type(value).__name__=='str':

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import IntegerField
try:
from paramecio2.libraries.db.extraforms.colorform import ColorForm
@ -31,8 +12,6 @@ class ColorField(IntegerField):
super().__init__(name, size, required)
self.name_form=ColorForm
self.jtype='string'
self.jexample='#f0f0f0'
def check(self, value):

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import PhangoField
from paramecio2.libraries import datetime
try:
@ -39,9 +20,6 @@ class DateField(PhangoField):
self.error_default='Error: Date format invalid'
self.jtype='string'
self.jformat='date-time'
def check(self, value):
if self.utc:

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import PhangoField
from paramecio2.libraries import datetime
try:
@ -38,12 +19,6 @@ class DateTimeField(PhangoField):
self.error_default='Error: Date format invalid'
self.type_sql='datetime'
self.jformat='date-time'
self.jtype='string'
self.jexample='2022-12-01 12:24:11'
def check(self, value):
if self.utc:
@ -77,7 +52,6 @@ class DateTimeField(PhangoField):
def show_formatted(self, value):
# Convert to paramecio value
value=str(value)
value=value.replace('-', '').replace(':', '').replace(' ', '')

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.webmodel import WebModel, PhangoField
import json
@ -39,12 +20,6 @@ class DictField(PhangoField):
self.set_default='NOT NULL'
self.type_sql='longtext'
self.jtype='object'
self.default_value='{}'
def check(self, value):
if type(value).__name__=='str':
@ -79,7 +54,7 @@ class DictField(PhangoField):
def get_type_sql(self):
return 'JSON '+self.set_default
return 'TEXT '+self.set_default
def show_formatted(self, value):

View file

@ -1,26 +1,7 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import CharField
import re
mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+")
mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+")
class EmailField(CharField):
"""Field for save and check email addreses"""
@ -31,8 +12,6 @@ class EmailField(CharField):
self.error_default='Error: No valid format'
self.jformat='email'
def check(self, value):
value=super().check(value)

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
import os
import sys
from pathlib import Path

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
import json
from paramecio2.libraries.db.webmodel import PhangoField
from paramecio2.libraries.db.coreforms import BaseForm
@ -51,8 +32,6 @@ class I18nField(PhangoField):
arr_i18n={i:'' for i in I18n.dict_i18n}
self.default_value=json.dumps(arr_i18n)
self.type_sql='longtext'
def change_form(self, form):
self.extra_parameters=[form]
@ -111,7 +90,7 @@ class I18nField(PhangoField):
def get_type_sql(self):
return 'JSON NOT NULL'
return 'TEXT NOT NULL'
def obtain_lang_value(self, lang, value):

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
import os
import sys
from pathlib import Path
@ -132,14 +113,9 @@ class ImageField(CharField):
return self.save_folder+'/'+value
else:
self.txt_error='Field is empty'
self.error=True
value=os.path.basename(value)
return ''
#value=os.path.basename(value)
#return self.save_folder+'/'+value
return self.save_folder+'/'+value
# Load image file
@ -204,7 +180,7 @@ class ImageField(CharField):
if format_image!='JPEG' and format_image!='GIF' and format_image!='PNG':
self.error=True
self.txt_error='Format is wrong. Requires GIF, JPEG or PNG formats'
self.txt_error='Format is wrong. Requires JPEG or PNG formats'
im.close()
return ""
@ -222,6 +198,24 @@ class ImageField(CharField):
save_file=self.save_folder+'/'+filename
if self.yes_thumbnail:
for name, width_t in self.thumbnail.items():
im_thumb=im.copy()
ratio=(real_width/width_t)
height_t=round(real_height/ratio)
size=(width_t, height_t)
save_file_thumb=self.save_folder+'/'+name+filename
im_thumb.thumbnail(size, Image.ANTIALIAS)
im_thumb.save(save_file_thumb, "JPEG", quality=self.default_quality_thumb)
im_thumb.close()
# Save file
try:
@ -251,26 +245,6 @@ class ImageField(CharField):
os.remove(save_file)
# Save thumbnails
if self.yes_thumbnail:
for name, width_t in self.thumbnail.items():
im_thumb=im.copy()
ratio=(real_width/width_t)
height_t=round(real_height/ratio)
size=(width_t, height_t)
save_file_thumb=self.save_folder+'/'+name+filename
im_thumb.thumbnail(size, Image.LANCZOS)
im_thumb.save(save_file_thumb, "JPEG", quality=self.default_quality_thumb)
im_thumb.close()
im.save(save_file)
# Delete old files
@ -295,23 +269,10 @@ class ImageField(CharField):
os.remove(arr_image[self.name])
if self.yes_thumbnail:
for tname, width_t in self.thumbnail.items():
old_dir_name=os.path.dirname(arr_image[self.name])
old_base_name=os.path.basename(arr_image[self.name])
old_thumb_name=old_dir_name+'/'+tname+old_base_name
if os.path.isfile(old_thumb_name):
os.remove(old_thumb_name)
self.model.yes_reset_conditions=old_reset
#self.model.conditions=old_conditions
#self.model.conditions=old_conditions
im.close()

View file

@ -1,34 +1,9 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import CharField
import ipaddress
class IpField(CharField):
"""Field for save ip internet address values in db"""
def __init__(self, name, size=1024, required=False):
super().__init__(name, size, required)
self.jformat='ipV4'
def check(self, value):
try:

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.webmodel import WebModel, PhangoField
import sys
try:
@ -44,10 +25,6 @@ class JsonField(PhangoField):
self.set_default='NOT NULL'
self.type_sql='longtext'
self.jtype='object'
def check(self, value):
if type(value).__name__=='str':
@ -79,7 +56,7 @@ class JsonField(PhangoField):
def get_type_sql(self):
return 'JSON '+self.set_default
return 'LONGTEXT '+self.set_default
def show_formatted(self, value):
@ -104,12 +81,10 @@ class JsonValueField(PhangoField):
return 'JSON'
def check(self, value):
#print(value)
try:
final_value=json.dumps(value)
final_value=value
except json.JSONDecodeError:
final_value='{}'

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.corefields import CharField
from paramecio2.libraries.db import coreforms
from paramecio2.libraries.i18n import I18n

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import DecimalField
from decimal import Decimal, getcontext
from locale import format_string

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
#from paramecio2.libraries.db.webmodel import PhangoField
from paramecio2.libraries.db.corefields import IntegerField
from paramecio2.libraries.db.coreforms import SelectModelForm

View file

@ -1,32 +1,7 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import PhangoField
from paramecio2.libraries.db.coreforms import PasswordForm
from hmac import compare_digest as compare_hash
#try:
# import crypt
#except:
# pass
#import bcrypt
from argon2 import PasswordHasher
import crypt
class PasswordField(PhangoField):
"""Field for check and save passwords"""
@ -38,7 +13,6 @@ class PasswordField(PhangoField):
self.name_form=PasswordForm
self.default_value=''
self.encrypt_password=True
self.jformat='password'
def check(self, value):
@ -70,10 +44,7 @@ class PasswordField(PhangoField):
#salt=crypt.mksalt(crypt.METHOD_SHA512)
if self.encrypt_password:
#value=crypt.crypt(value)
ph=PasswordHasher()
final_value=ph.hash(value)
return final_value
value=crypt.crypt(value)
"""
else:
@ -89,12 +60,7 @@ class PasswordField(PhangoField):
def verify( password, h):
"""Static method used for verify a password save using PasswordField"""
#return bcrypt_sha256.verify(password, h)
#return compare_hash(h, crypt.crypt(password, h))
ph=PasswordHasher()
try:
return ph.verify(h, password)
except:
return False
return compare_hash(h, crypt.crypt(password, h))
# Old function bcrypt

View file

@ -1,25 +1,6 @@
"""
Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
from paramecio2.libraries.db.corefields import IntegerField
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 paramecio2.libraries.db.corefields import DecimalField
class PercentField(DecimalField):
class PercentField(IntegerField):
"""Field used for save percent values from 0 to 100."""
def __init__(self, name, required=False):

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.corefields import CharField
from slugify import slugify
from paramecio2.libraries.db.coreforms import HiddenForm

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import CharField
import re
import ipaddress
@ -32,11 +13,6 @@ check_url = re.compile(
class UrlField(CharField):
"""Field for check and save strings in url format"""
def __init__(self, name, size=1024, required=False):
super().__init__(name, size, required)
self.jformat='url'
def check(self, value):
self.error=False
@ -50,7 +26,7 @@ class UrlField(CharField):
return value
check_domain=re.compile(r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$')
check_domain=re.compile('^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$')
class DomainField(CharField):
"""Field for check and save strings in domain internet format"""
@ -77,7 +53,7 @@ class DomainField(CharField):
#^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[0-9]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$
check_git_url=re.compile(r'^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[a-zA-Z0-9\-_]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$')
check_git_url=re.compile('^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[a-zA-Z0-9\-_]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$')
class GitUrlField(CharField):

View file

@ -1,26 +1,7 @@
"""
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/>.
"""
from paramecio2.libraries.db.corefields import PhangoField
from paramecio2.libraries.db.coreforms import PasswordForm
from hmac import compare_digest as compare_hash
#import crypt
import crypt
import re
class UserNameField(PhangoField):

View file

@ -1,18 +0,0 @@
"""
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/>.
"""

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
class CheckForm(BaseForm):

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.mtemplates import standard_t

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.mtemplates import standard_t
from paramecio2.libraries.datetime import format_timedata
@ -30,41 +11,28 @@ class DateForm(BaseForm):
super().__init__(name, value)
self.yes_time=True
self.yes_time=False
self.t=standard_t
def form(self):
if type(self.default_value).__name__!='datetime':
y=''
m=''
d=''
h=''
min=''
s=''
min_time=''
y=''
m=''
d=''
h=''
min=''
s=''
min_time=''
time=format_timedata(self.default_value)
time=format_timedata(self.default_value)
if time[0]:
y=int(time[0])
m=int(time[1])
d=int(time[2])
h=int(time[3])
min_time=int(time[4])
s='00' #int(time[5])
else:
y=self.default_value.year #"{:>10}".format(s)
m="{:02d}".format(self.default_value.month)
d="{:02d}".format(self.default_value.day)
h="{:02d}".format(self.default_value.hour)
min_time="{:02d}".format(self.default_value.minute)
s='00'
pass
if time[0]:
y=int(time[0])
m=int(time[1])
d=int(time[2])
h=int(time[3])
min_time=int(time[4])
s=int(time[5])
return self.t.load_template('forms/dateform.phtml', yes_time=self.yes_time, form=self.name, y=y, m=m, d=d, h=h, min=min_time, s=s)

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.mtemplates import env_theme, PTemplate

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.mtemplates import standard_t

View file

@ -1,21 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.mtemplates import env_theme, PTemplate

View file

@ -1,21 +1,3 @@
"""
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/>.
"""
from paramecio2.libraries.db.coreforms import BaseForm
from paramecio2.libraries.mtemplates import env_theme, PTemplate

View file

@ -1,44 +0,0 @@
# A more simple set for make queries
def insert(model, db, dict_values):
final_values={}
for k in model.fields.keys():
final_values[k]=model.fields[k].check(dict_values.get(k, ''))
del final_values[model.name_field_id]
str_fields="`"+"`, `".join(final_values.keys())+"`"
str_query='insert into {} ({}) VALUES ({})'.format(model.name, str_fields, ", ".join(['%s']*len(final_values)))
success=False
with db.query(str_query, list(final_values.values())) as cursor:
if cursor.rowcount>0:
model.last_id=cursor.lastrowid
success=True
return success
def select(model, db, dict_fields=[], where_sql='', limit='', dict_values=[]):
if len(dict_fields)==0:
dict_fields=['`'+field+'`' for field in model.fields.keys()]
str_fields=", ".join(dict_fields)
str_query='select {} from {} {} limit 1'.format(str_fields, model.name, where_sql)
arr_result=[]
with db.query(str_query, dict_values) as cursor:
arr_result=cursor.fetchall()
return arr_result

View file

@ -46,7 +46,6 @@ class SqlClass:
self.conn=None
self.connected=False
self.pool_recycle=3600
self.last_query=''
self.connect()
@ -74,9 +73,6 @@ class SqlClass:
engine=create_engine("mysql+%s://%s:%s@%s/%s?charset=utf8mb4" % (self.connection['db_type'], self.connection['user'], self.connection['password'], self.connection['host'], self.connection['db']), pool_recycle=self.pool_recycle, echo_pool=True, pool_size=self.pool_size, pool_pre_ping=True)
#Postgre
#engine = create_engine("postgresql+psycopg2://scott:tiger@localhost:5432/mydatabase")
except:
e = sys.exc_info()[0]
v = sys.exc_info()[1]
@ -174,20 +170,16 @@ class SqlClass:
cursor.execute(sql_query, arguments)
self.conn.commit()
#if hasattr(cursor, '_executed'):
# self.last_query=cursor._executed
return cursor
except:
e = sys.exc_info()[0]
v = sys.exc_info()[1]
#if hasattr(cursor, '_executed'):
# self.last_query=cursor._executed
if hasattr(cursor, '_last_executed'):
sql_query=cursor._last_executed
self.error_connection="Error in query ||%s||Values: %s" % (self.last_query, str(arguments))
self.error_connection="Error in query ||%s||Values: %s" % (sql_query, str(arguments))
self.conn.close()

View file

@ -3,7 +3,7 @@
from paramecio2.libraries.db.webmodel import WebModel
from paramecio2.libraries.db.coreforms import PasswordForm
from paramecio2.libraries.i18n import I18n
from flask import request, session
from flask import request
class UserModel(WebModel):
"""Model used with basic things for users login and signup
@ -46,7 +46,7 @@ class UserModel(WebModel):
repeat_password.required=1
repeat_password.label=_('Repeat Password')
repeat_password.label=I18n.lang('common', 'repeat_password', 'Repeat Password')
repeat_password.field=self.fields[self.password_field]
@ -99,7 +99,7 @@ class UserModel(WebModel):
if dict_values['repeat_password']!=dict_values[self.password_field]:
self.fields[self.password_field].error=True
self.fields[self.password_field].txt_error=_('Error: passwords doesn\'t match')
self.fields[self.password_field].txt_error=I18n.lang('common', 'error_passwords_no_match', 'Error: passwords doesn\'t match')
error+=1

View file

@ -25,7 +25,6 @@ class PhangoField:
Attributes:
name (str): The name of the field
jtype(Python type): The type of value in python
label (str): A label or generic name for use in text labels used for representate the field
required (bool): If the field is required or not.
size (int): The size of sql field.
@ -55,10 +54,6 @@ class PhangoField:
self.name=name
# The type of the field in javascript. Util for api documentation
self.jtype='string'
# The label for the Field
self.label=name.replace('_', ' ').title()
@ -151,10 +146,6 @@ class PhangoField:
self.help=''
self.type_sql='varchar({})'.format(self.size)
def get_type_sql(self):
"""This method is used for describe the new field in a sql language format."""
@ -231,7 +222,6 @@ class PrimaryKeyField(PhangoField):
self.name_form=HiddenForm
self.required=False
self.error_default="The value is zero"
self.type_sql='int({})'.format(self.size)
def check(self, value):
@ -362,7 +352,6 @@ class WebModel:
sqlclass (SqlClass): A sql_class used for connect to db.
show_formatted (bool): If True, by default all fields are showed with formatted value using show_formatted method of PhangoField classes and children in select method. If False, raw value is showed.
enctype (bool): If True, forms generated using this model are prepared for enctype=multipart/form-data A.K.A. upload files.
model_id (int): Variable where the actual row from model selected can be saved for different things.
"""
self.cached=WebModel.global_cached
@ -467,8 +456,6 @@ class WebModel:
self.enctype=False
self.model_id=0
self.dummy=0
# A method for add the connection
@ -1140,9 +1127,7 @@ class WebModel:
#Need delete rows from other related tables save in self.related_models_deleted
#+' '+self.order_by+' '+self.limit
sql=("delete from `"+self.name+"` "+self.conditions[0]).strip()
sql=("delete from `"+self.name+"` "+self.conditions[0]+' '+self.order_by+' '+self.limit).strip()
result=self.query(sql, self.conditions[1], self.connection_id)
@ -1305,7 +1290,7 @@ class WebModel:
self.arr_sql_set_index[self.name][field]='ALTER TABLE `'+self.name+'` ADD CONSTRAINT `'+field+'_'+self.name+'IDX` FOREIGN KEY ( `'+field+'` ) REFERENCES `'+table_related+'` (`'+id_table_related+'`) ON DELETE CASCADE ON UPDATE CASCADE;'
return "create table `"+self.name+"` (\n"+",\n".join(table_fields)+"\n) DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;";
return "create table `"+self.name+"` (\n"+",\n".join(table_fields)+"\n) DEFAULT CHARSET=utf8;";
def update_table(self, fields_to_add, fields_to_modify, fields_to_add_index, fields_to_add_constraint, fields_to_add_unique, fields_to_delete_index, fields_to_delete_unique, fields_to_delete_constraint, fields_to_delete):

View file

@ -1,48 +1,10 @@
#!/usr/bin/env python3
"""
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/>.
"""
from paramecio2.libraries.db import corefields
from paramecio2.libraries.db.coreforms import PasswordForm
from paramecio2.libraries.i18n import I18n
from flask import session, request, abort
from paramecio2.libraries.keyutils import create_key_encrypt
from paramecio2.libraries.i18n import I18n, PGetText
# For tests outing the web framework
try:
from settings import config
except:
class config:
pass
framework='flask'
if hasattr(config, 'framework'):
framework=config.framework
if framework=='flask':
from flask import session, request, abort
elif framework=='fastapi':
from parameciofast.libraries.sessions import get_session
pass
# Need unittest
"""Functions and classes for process forms"""
@ -189,24 +151,13 @@ def show_form(post, arr_form, t, yes_error=True, pass_values=True, modelform_tpl
# Create csrf_token in session
#generate_csrf()
generate_csrf()
if pass_values==True:
pass_values_to_form(post, arr_form, yes_error, pass_values)
return t.load_template(modelform_tpl, forms=arr_form)
def extract_post(post, fields):
"""Helper function for create a simple array from other using fields list for filter
Args:
post (dict): A dict with keys and values to filter.
fields (list): A list with keys to validate.
"""
return {k:v for k,v in post.items() if k in fields}
#Simple Function for add repeat_password form to user model
def set_extra_forms_user(user_admin):
@ -227,7 +178,7 @@ def set_extra_forms_user(user_admin):
user_admin.forms['repeat_password'].required=True
user_admin.forms['repeat_password'].label=_('Repeat Password')
user_admin.forms['repeat_password'].label=I18n.lang('common', 'repeat_password', 'Repeat Password')
def csrf_token(token_id='csrf_token'):
@ -265,6 +216,6 @@ def check_csrf(name_csrf_token='csrf_token'):
csrf_token=session.get('csrf_token', '')
if csrf_token=='' or csrf_token!=request.form.get(name_csrf_token):
abort(403)
abort(404)

View file

@ -1,44 +1,16 @@
"""
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/>.
"""
from paramecio2.libraries.lists import SimpleList
from flask import request, redirect, flash
from paramecio2.libraries.urls import add_get_parameters
#from paramecio.citoplasma.mtemplates import set_flash_message
from paramecio2.libraries.formsutils import show_form
from paramecio2.libraries.mtemplates import env_theme, PTemplate
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.formsutils import check_csrf
from paramecio2.libraries.i18n import I18n
from collections import OrderedDict
pgettext=PGetText(__file__)
_=pgettext.gettext
#env=env_theme(__file__)
#t=PTemplate(env)
class GenerateAdminClass:
"""Class for insert, update and list items of a model
"""
def __init__(self, model, url, t=None, query_args=None, request_post=None):
def __init__(self, model, url, t):
"""A class for generate forms, insert and update items from a database model
For an easy and fast access to database data, you can use this class for get a simple database model of paramecio and get list of items, add forms, edit forms and more.
@ -66,20 +38,9 @@ class GenerateAdminClass:
self.model=model
if t:
self.t=t
self.t=t
else:
env=env_theme(__file__)
self.t=PTemplate(env)
#self.t.l=pgettext
#self.t.add_filter(self.t._)
self.list=SimpleList(model, url, self.t)
self.list=SimpleList(model, url, t)
self.arr_fields_edit=list(model.fields.keys())
@ -115,11 +76,7 @@ class GenerateAdminClass:
self.post_update=None
self.text_home=_('Home')
self.query_args=None
self.request_post=None
self.text_home=I18n.lang('common', 'home', 'Home')
def show(self):
""" Method for show the admin model
@ -130,13 +87,9 @@ class GenerateAdminClass:
html (str): The html content of the admin page, can be, items list, forms for create items, etc...
"""
if not self.query_args:
op_admin=request.args.get('op_admin', '0')
item_id=request.args.get('id', '0')
op_admin=request.args.get('op_admin', '0')
else:
op_admin=self.query_args.get('op_admin', '0')
item_id=self.query_args.get('id', '0')
item_id=request.args.get('id', '0')
if len(self.model.forms)==0:
@ -153,26 +106,21 @@ class GenerateAdminClass:
post=None
title_edit=_('Add new item')
title_edit=I18n.lang('common', 'add_new_item', 'Add new item')
pass_value=False
if item_id!='0':
post=self.model.select_a_row(item_id, [], True)
title_edit=_('Edit item')
title_edit=I18n.lang('common', 'edit_new_item', 'Edit item')
pass_value=True
if post==None or post==False:
if item_id=='0':
self.model.model_id=0
post={}
else:
return ""
else:
self.model.model_id=int(item_id)
url_action=add_get_parameters(self.url, op_admin=2, id=item_id)
@ -182,8 +130,6 @@ class GenerateAdminClass:
elif op_admin=='2':
check_csrf()
self.model.reset_conditions()
insert_row=self.model.insert
@ -197,34 +143,26 @@ class GenerateAdminClass:
try:
if not self.query_args:
item_id=str(int(request.args.get('id', '0')))
else:
item_id=str(int(self.query_args.get('id', '0')))
item_id=str(int(request.args.get('id', '0')))
except:
item_id='0'
title_edit=_('Add new item')
title_edit=I18n.lang('common', 'add_new_item', 'Add new item')
if item_id!='0':
insert_row=self.model.update
title_edit=_('Edit item')
title_edit=I18n.lang('common', 'edit_new_item', 'Edit item')
self.model.conditions=['WHERE `'+self.model.name+'`.`'+self.model.name_field_id+'`=%s', [item_id]]
if not self.request_post:
post=dict(request.form)
post=dict(request.form)
else:
post=self.request_post
print(post)
if pre_update_ret:
if insert_row(post):
flash(_('Task successful'))
flash(I18n.lang('common', 'task_successful', 'Task successful'))
if self.post_update:
if item_id=='0':
@ -237,10 +175,7 @@ class GenerateAdminClass:
url_action=add_get_parameters(self.url, op_admin=2, id=item_id)
if not self.request_post:
post=dict(request.form)
else:
post=self.request_post
post=dict(request.form)
form=show_form(post, edit_forms, self.t, True)
@ -250,7 +185,7 @@ class GenerateAdminClass:
else:
url_action=add_get_parameters(self.url, op_admin=2, id=item_id)
#post=dict(request.form)
post=dict(request.form)
form=show_form(post, edit_forms, self.t, True)
@ -261,20 +196,14 @@ class GenerateAdminClass:
elif op_admin=='3':
if not self.query_args:
verified=request.args.get('verified', '0')
else:
verified=self.query_args.get('verified', '0')
verified=request.args.get('verified', '0')
if verified=='1':
if item_id!='0':
self.model.conditions=['WHERE `'+self.model.name+'`.`'+self.model.name_field_id+'`=%s', [item_id]]
self.model.delete()
flash(_('Task successful'))
flash(I18n.lang('common', 'task_successful', 'Task successful'))
return redirect(self.url_redirect)
else:
@ -353,11 +282,7 @@ class GenerateConfigClass:
self.post_update=None
self.text_home=_('Home')
self.query_args=None
self.request_post=None
self.text_home=I18n.lang('common', 'home', 'Home')
def show(self):
@ -367,17 +292,13 @@ class GenerateConfigClass:
"""
#op_config=request.args.get('op_config', '0')
if not self.query_args:
op_config=request.args.get('op_config', '0')
else:
op_config=self.query_args.get('op_config', '0')
op_config=request.args.get('op_config', '0')
if len(self.model.forms)==0:
self.model.create_forms()
title_edit=_('Edit')+' '+self.title_name
title_edit=I18n.lang('common', 'edit', 'Edit')+' '+self.title_name
edit_forms=OrderedDict()
@ -399,32 +320,21 @@ class GenerateConfigClass:
if c:
insert_model=self.model.update
#post=dict(request.form)
if not self.request_post:
post=dict(request.form)
else:
post=self.request_post
post=dict(request.form)
if insert_model(post):
flash(_('Task successful'))
set_flash_message(I18n.lang('common', 'task_successful', 'Task successful'))
self.model.yes_reset_conditions=True
if self.post_update:
self.post_update(self)
return redirect(self.url_redirect)
redirect(self.url_redirect)
else:
#post=dict(request.form)
if not self.request_post:
post=dict(request.form)
else:
post=self.request_post
post=dict(request.form)
form=show_form(post, edit_forms, self.t, True)
self.model.yes_reset_conditions=True

View file

@ -1,21 +1,3 @@
"""
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/>.
"""
from flask import request

View file

@ -1,145 +0,0 @@
#/usr/bin/env python3
#from paramecio.citoplasma.urls import add_get_parameters
from paramecio2.libraries.urls import add_get_parameters
class HierarchyLinks:
def __init__(arr_links, t=None):
self.arr_links=arr_links
self.arr_indexes={}
def update_links(self, link_father, link_son, text):
self.arr_links[link_father][link_son]=text
def calculate_indexes():
#oreach(self.arr_links as $father_link => $arr_child_links)
for father_link, arr_child_links in self.arr_links.items():
#foreach($arr_child_links as $link => $text)
for link, text in self.arr_child_links.items():
self.arr_indexes[link]=father_link
def result(last_link, arr_result=[], yes_last_link=0):
self.calculate_indexes()
if last_link in self.arr_indexes:
father=self.arr_indexes[last_link]
arr_last_link[0]=self.no_link
arr_last_link[1]=self.yes_link
yes_link_func=arr_last_link[yes_last_link]
if father!='':
arr_result.append(self.yes_link_func(last_link, self.arr_links[father][last_link]))
yes_last_link=1
arr_result=self.result(father, arr_result, yes_last_link)
return arr_result
else:
arr_result.append(self.yes_link_func(last_link, self.arr_links[father][last_link]))
return arr_result
return arr_result
def show(link, separator='&gt;&gt;', class_link=''):
arr_result=self.result(link)
arr_result=array_reverse(arr_result)
return ' '+separator+' '.join(arr_result)
def yes_link(link, text):
return '<a href="'+link+'">'+text+'</a>'
def no_link(link, text):
return text
class HierarchyModelLinks:
def __init__(self, model, first_element_title, field_name, field_parent, base_url):
self.model=model
self.field_parent=field_parent
self.field_name=field_name
self.base_url=base_url
self.arr_parent={}
self.arr_son=[]
self.first_element_title=first_element_title
def prepare(self):
conditions=self.model.conditions
with self.model.set_conditions('', []).select([self.model.name_field_id, self.field_name, self.field_parent]) as cur:
for arr_model in cur:
if self.field_parent not in self.arr_parent:
self.arr_parent[arr_model[self.model.name_field_id]]=[]
self.arr_parent[arr_model[self.model.name_field_id]]=[self.model.fields[self.field_name].show_formatted(arr_model[self.field_name]), arr_model[self.field_parent]]
self.model.conditions=conditions
def parents(self, son_id, url_func):
if son_id not in self.arr_parent or son_id==0:
return
self.arr_son.insert(0, url_func(son_id, self.arr_parent[son_id][0]))
self.parents(self.arr_parent[son_id][1], self.url)
def no_url(self, son_id, title):
return title
def url(self, son_id, title):
args={}
args[self.field_parent]=str(son_id)
return '<a href="%s">%s</a>' % (add_get_parameters(self.base_url, **args), title)
def show(self, son_id, separator=' &gt;&gt; '):
try:
son_id=int(son_id)
except:
son_id=0
self.prepare()
self.parents(son_id, self.no_url)
self.arr_son.insert(0, self.url(0, self.first_element_title))
return separator.join(self.arr_son)

View file

@ -1,30 +1,9 @@
#!/usr/bin/env python3
"""
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/>.
"""
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
yes_session=False
@ -49,44 +28,6 @@ def load_lang(*args):
# 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
@ -105,64 +46,9 @@ class I18n:
l={}
def __init__(self, module):
self.module=module
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:
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, lang)
#@staticmethod
#def set_lang(code_lang):
# if default_lang
def __init__(self, module):
self.module=module
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)
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:
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
@ -244,4 +130,3 @@ class I18n:
return json.dumps(arr_final)
common_pgettext=PGetText(__file__)

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from hashlib import sha512, sha256
from base64 import b64encode
from os import urandom

View file

@ -1,59 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-18 23:51+0100\n"
"PO-Revision-Date: 2023-12-19 00:01+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.1\n"
#: formsutils.py:183
msgid "Repeat Password"
msgstr "Repetir contraseña"
#: lists.py:113
msgid "Options"
msgstr "Opciones"
#: lists.py:222 generate_admin_class.py:304
msgid "Edit"
msgstr "Editar"
#: lists.py:223
msgid "Delete"
msgstr "Borrar"
#: lists.py:395 templates/utils/list.phtml:133
msgid "Pages"
msgstr "Páginas"
#: generate_admin_class.py:82 generate_admin_class.py:288
msgid "Home"
msgstr "Inicio"
#: generate_admin_class.py:112 generate_admin_class.py:155
msgid "Add new item"
msgstr "Añadir nuevo elemento"
#: generate_admin_class.py:118 generate_admin_class.py:160
msgid "Edit item"
msgstr "Editar elemento"
#: generate_admin_class.py:168 generate_admin_class.py:209
#: generate_admin_class.py:329
msgid "Task successful"
msgstr "Tarea exitosa"
#: templates/utils/list.phtml:14
msgid "Search"
msgstr "Buscar"

View file

@ -1,59 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-18 23:51+0100\n"
"PO-Revision-Date: 2023-12-19 00:01+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.1\n"
#: formsutils.py:183
msgid "Repeat Password"
msgstr "Repetir contraseña"
#: lists.py:113
msgid "Options"
msgstr "Opciones"
#: lists.py:222 generate_admin_class.py:304
msgid "Edit"
msgstr "Editar"
#: lists.py:223
msgid "Delete"
msgstr "Borrar"
#: lists.py:395 templates/utils/list.phtml:133
msgid "Pages"
msgstr "Páginas"
#: generate_admin_class.py:82 generate_admin_class.py:288
msgid "Home"
msgstr "Inicio"
#: generate_admin_class.py:112 generate_admin_class.py:155
msgid "Add new item"
msgstr "Añadir nuevo elemento"
#: generate_admin_class.py:118 generate_admin_class.py:160
msgid "Edit item"
msgstr "Editar elemento"
#: generate_admin_class.py:168 generate_admin_class.py:209
#: generate_admin_class.py:329
msgid "Task successful"
msgstr "Tarea exitosa"
#: templates/utils/list.phtml:14
msgid "Search"
msgstr "Buscar"

View file

@ -1,35 +1,13 @@
"""
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/>.
"""
#By default id is not showed
from paramecio2.libraries.pages import Pages
from paramecio2.libraries.urls import add_get_parameters
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.i18n import I18n
#from flask import request, session
from paramecio2.libraries.get_data import get_query_args
import sys
import re
pgettext=PGetText(__file__)
_=pgettext.gettext
class SimpleList:
"""Class for create item list from a model table
"""
@ -129,7 +107,7 @@ class SimpleList:
#self.yes_options=True
self.arr_extra_fields=[_('Options')]
self.arr_extra_fields=[I18n.lang('common', 'options', 'Options')]
self.arr_extra_options=[SimpleList.standard_options]
@ -238,8 +216,8 @@ class SimpleList:
options (list): Return a list of basic options for items row
"""
options=[]
options.append('<a href="'+add_get_parameters(url, op_admin=1, id=id)+'">'+_('Edit')+'</a>')
options.append('<a href="'+add_get_parameters(url, op_admin=3, id=id)+'">'+_('Delete')+'</a>')
options.append('<a href="'+add_get_parameters(url, op_admin=1, id=id)+'">'+I18n.lang('common', 'edit', 'Edit')+'</a>')
options.append('<a href="'+add_get_parameters(url, op_admin=3, id=id)+'">'+I18n.lang('common', 'delete', 'Delete')+'</a>')
return options
def show(self):
@ -308,7 +286,7 @@ class AjaxList(SimpleList):
"""Class for make a list from a table based in Ajax
"""
# Fields example: [['Hostname', True], ['IP', True], ['Options', False]]
# Fields example: [[I18n.lang('cuchulu', 'hostname', 'Hostname'), True], ['IP', True], [I18n.lang('cuchulu', 'options', 'Options'), False]]
# arr_order_fields=['server.hostname', 'server.ip']
@ -411,7 +389,7 @@ class AjaxList(SimpleList):
pages=Pages()
html_pages=_('Pages')+': '+pages.show( begin_page, total_elements, limit, '#' ,initial_num_pages=self.initial_num_pages, variable='begin_page', label='', func_jscript='')
html_pages=I18n.lang('cuchulu', 'pages', 'Pages')+': '+pages.show( begin_page, total_elements, limit, '#' ,initial_num_pages=self.initial_num_pages, variable='begin_page', label='', func_jscript='')
with self.db.query(str_query, params) as cursor:
for row in cursor:
@ -433,7 +411,5 @@ class AjaxList(SimpleList):
return {'fields': self.fields, 'rows': rows, 'html_pages': html_pages}
class SimpleAjaxList():
pass

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
import csv
import os

View file

@ -1,27 +1,8 @@
"""
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/>.
"""
# Template frontend from mako.
#import gettext
import gettext
from mako.template import Template
from flask import url_for as url_for_flask
from flask import session, url_for
from mako.lookup import TemplateLookup
from os import path
try:
@ -31,24 +12,12 @@ except:
theme='default'
reloader=False
#import gettext
import gettext
import sys
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.urls import make_url, make_media_url, add_get_parameters
from paramecio2.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
@ -74,8 +43,6 @@ def env_theme(module, cache_enabled=True, cache_impl='', cache_args={}, module_d
standard_templates=path.dirname(__file__)+'/templates'
standard_languages=path.dirname(__file__)+'/languages'
module_directory+='/'+module
module_templates=module+'/templates'
@ -97,7 +64,7 @@ class PTemplate:
templates_loaded={}
def __init__(self, environment, url_for_function=None):
def __init__(self, environment):
"""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.
@ -123,22 +90,15 @@ class PTemplate:
self.add_filter(I18n.lang)
self.add_filter(make_url)
_=gettext.gettext
self.add_filter(_)
#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)
@ -148,30 +108,6 @@ class PTemplate:
self.add_filter(self.load_js)
# Loading language domain for gettext in templates
base_name=path.dirname(path.realpath(__file__))
module_env=self.env.directories[1].replace('/templates', '')
#print(path.basename(module_env)+' '+base_name+'/languages/')
self.l=PGetText(module_env+'/app.py')
self.i18n=I18n(module_env)
self.add_filter(self._)
self.i18n=I18n(base_name)
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

View file

@ -1,24 +1,5 @@
#!/usr/bin/env python3
"""
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/>.
"""
from math import ceil, floor
from paramecio2.libraries.urls import add_get_parameters
from paramecio2.libraries.i18n import I18n

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from flask import g, session, redirect, url_for
from functools import wraps
from paramecio2.libraries.db.webmodel import WebModel
@ -32,7 +13,7 @@ def db(f):
"""Wrapper function for add db connection to your flask function
Wrapper function for add db connection to your flask function. Also close the connection if error or function exception is finished.
Wrapper function for add db connection to your flask function. Also close the connection if error or function exection is finished.
Args:
*args (mixed): The args of function
@ -42,8 +23,7 @@ def db(f):
wrapper (function): Return the wrapper.
"""
if not hasattr(g, 'connection'):
g.connection=WebModel.connection()
g.connection=WebModel.connection()
try:

View file

@ -1,47 +0,0 @@
from paramecio2.libraries.db import corefields
from paramecio2.libraries.db.extrafields.jsonfield import JsonField
from paramecio2.libraries.db.extrafields.dictfield import DictField
try:
import ujson as json
except:
import json
"""A class for list objects with data fields
"""
class ListItem:
pass
"""Typical item """
class Items(ListItem):
name=corefields.CharField('name')
class StandardResponse:
error=corefields.BooleanField('error')
message=corefields.CharField('message')
code_error=corefields.IntegerField('code_error')
error_form=DictField('error_form', corefields.CharField('error_form'))
#items=JsonField('items', corefields.CharField)
def __init__(self, error=0, message='', code_error=0):
self.error=error
self.message=message
self.code_error=code_error
def toDict(self):
return self.__dict__
def toJSON(self):
return json.dumps( self, default=lambda o: o.__dict__, sort_keys=True, indent=4 )
class ResponseItems(StandardResponse):
pass

View file

@ -1,24 +1,4 @@
#!/usr/bin/env python3
"""
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/>.
"""
import os
import smtplib
import mimetypes
@ -29,7 +9,6 @@ from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr, formatdate
import ssl as ssl_module
import sys
@ -130,7 +109,7 @@ class SendMail:
return True
def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[], reply_to=()):
def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[]):
""" Method that send email
With this method you can send email to multiple address, html, and add attachments to email
@ -148,10 +127,6 @@ class SendMail:
if not self.connect():
return False
if len(reply_to)==0:
reply_to=(from_address, from_address)
COMMASPACE=', '
if len(attachments)==0:
@ -159,9 +134,6 @@ class SendMail:
msg=MIMEText(message, content_type)
msg['Subject']=subject
msg['Reply-To']=formataddr(reply_to)
msg['From']=from_address
msg['To']=COMMASPACE.join(to_address)
@ -177,15 +149,10 @@ class SendMail:
outer=MIMEMultipart()
outer['Subject']=subject
msg['Reply-To']=formataddr(reply_to)
outer['From']=from_address
outer['To']=COMMASPACE.join(to_address)
#outer['Date']=formatdate()
# Attach message text
msg=MIMEText(message, content_type)

View file

@ -1,22 +1,3 @@
"""
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/>.
"""
from slugify import slugify as slugify_func
def slugify(slug, *args, **wargs):

View file

@ -1,54 +1,38 @@
${add_js('jquery.min.js', 'admin')}
<!--<input type="number" min="1" max="31" name="${form}_day" id="time_${form}_day" class="form_day" value="${d}" size="2" maxlength="2"/>
<input type="number" min="1" max="31" name="${form}_day" id="time_${form}_day" class="form_day" value="${d}" size="2" maxlength="2"/>
<input type="number" min="1" max="12" name="${form}_month" id="time_${form}_month" class="form_month" value="${m}" size="2" maxlength="2"/>
<input type="number" name="${form}_year" id="time_${form}_year" class="form_year" value="${y}" size="4" maxlength="4"/>-->
<%
date=''
if d!='':
date='-'.join((str(y), str(m), str(d)))
time=''
if h!='':
time=':'.join((str(h), str(min)))
%>
<input type="date" value="${date}" name="${form}_date" id="${form}_date" />
<input type="number" name="${form}_year" id="time_${form}_year" class="form_year" value="${y}" size="4" maxlength="4"/>
% if yes_time==True:
<!--<input type="number" min="0" max="23" name="${form}_hour" id="time_${form}_hour" class="form_hour" value="${h}" size="2" maxlength="2"/>
<input type="number" min="0" max="60" name="${form}_minute" id="time_${form}_minute" class="form_minute" value="${min}" size="2" maxlength="2"/>
<input type="number" min="0" max="60" name="${form}_second" id="time_${form}_second" class="form_second" value="${s}" size="2" maxlength="2"/>-->
<input type="time" value="${time}" name="${form}_time" id="${form}_time" />
<input type="text" min="0" max="23" name="${form}_hour" id="time_${form}_hour" class="form_hour" value="${h}" size="2" maxlength="2"/>
<input type="text" min="0" max="60" name="${form}_minute" id="time_${form}_minute" class="form_minute" value="${min}" size="2" maxlength="2"/>
<input type="text" min="0" max="60" name="${form}_second" id="time_${form}_second" class="form_second" value="${s}" size="2" maxlength="2"/>
% endif
<input type="hidden" name="${form}" id="time_${form}" value="" />
<script language="javascript">
$(document).submit(function () {
/*year=$("#time_${form}_year").val().toString();
year=$("#time_${form}_year").val().toString();
month=$("#time_${form}_month").val().toString();
day=$("#time_${form}_day").val().toString();
year=add_extra_length(year, 4);
month=add_extra_length(month, 2);
day=add_extra_length(day, 2); */
day=add_extra_length(day, 2);
//final_time=year+month+day
final_time=$('#${form}_date').val().replace(/-/g, '');
final_time=year+month+day
% if yes_time==True:
/*hour=$("#time_${form}_hour").val().toString();
hour=$("#time_${form}_hour").val().toString();
minute=$("#time_${form}_minute").val().toString();
second=$("#time_${form}_second").val().toString();
hour=add_extra_length(hour, 2);
minute=add_extra_length(minute, 2);
second=add_extra_length(second, 2); */
second=add_extra_length(second, 2);
final_time+=$('#${form}_time').val().replace(':', '')+'00';
final_time+=final_time;
% else:

View file

@ -3,7 +3,7 @@
choose=''
%>
<span id="${name_form}_languages_form" style="display:inline-block;">
<div id="languages_form">
<%def name="select_lang(i18n, lang_selected)">
% if i18n==lang_selected:
<%
@ -28,23 +28,10 @@ choose=''
<%
form.default_value=default_value[i18n]
%>
<span id="${name_form}_${i18n}_switch" class="${name_form}_i18n_form">${form.form()|n}</span> <a class="choose_flag ${name_form}_i18n_flag lang_form" id="${name_form}_${i18n}_element" href="#"><img src="${make_media_url('images/languages/'+i18n+'.png', 'admin')}" alt="${name_form}_${i18n}"></a>
<span id="${name_form}_${i18n}_switch" class="${name_form}_i18n_form">${form.form()|n}</span> <a class="choose_flag ${name_form}_i18n_flag lang_form" id="${name_form}_${i18n}_element" href="#"><img src="${make_media_url('images/languages/'+i18n+'.png', 'admin')}" alt="${name_form}_${i18n}"/></a>
% endfor
<i class="fa fa-globe all_languages tooltip" aria-hidden="true" style="display:none;cursor:pointer;" data-tooltip-content="#tooltip_${name_form}_content"></i>
<div class="tooltip_templates" style="display:none;"><div id="tooltip_${name_form}_content">${_('All lenguages activated, if you change to multilanguage, the default language will be ')}${lang_selected}</div></div>
% endif
% if len(arr_i18n)==1:
<script>
$("#${name_form}_languages_form").find('.${name_form}_i18n_flag').hide();
$("#${name_form}_languages_form").find('.all_languages').show();
</script>
% endif
</span>
</div>
<script>

View file

@ -1,3 +1,3 @@
<!--<h1>${admin.title}</h1>-->
<p><a href="${add_get_parameters(admin.url, op_admin='1')}">${_('Add new item')}</a></p>
<p><a href="${add_get_parameters(admin.url, op_admin='1')}">${lang('common', 'add_item', 'Add new item')}</a></p>
${admin.list.show()|n}

View file

@ -11,13 +11,13 @@
% if simplelist.yes_search:
<div class="form">
<form method="get" action="${simplelist.url}">
${_('Search')}: <input type="text" name="search_text" value="${simplelist.search_text|n}">
${lang('common','search', 'Search')}: <input type="text" name="search_text" value="${simplelist.search_text|n}">
<select name="search_field">
% for field in simplelist.search_fields:
<option value="${simplelist.model.fields[field].name}" ${select_field(field)}>${simplelist.model.fields[field].label}</option>
% endfor
</select>
<input type="submit" value="${_('Search')}" />
<input type="submit" value="${lang('common', 'search', 'Search')}" />
</form>
</div>
% endif
@ -130,6 +130,6 @@ size_td=round(100/(len(simplelist.fields_showed)+len(simplelist.arr_extra_option
% endif
<p>
% if pages!='':
${_('Pages')}: ${pages|n}
${lang('common', 'pages', 'Pages')}: ${pages|n}
% endif
</p>

View file

@ -3,6 +3,6 @@
<!-- <input type="hidden" name="id" value="${item_id}">
<input type="hidden" name="op_admin" value="${op_admin}">
<input type="hidden" name="verified" value="${verified}">-->
<input type="submit" value="${_('Are you sure for delete this item?')}" />
<input type="submit" value="${lang('common', 'delete_item_you_sure', 'Are you sure for delete this item?')}" />
</div>
</form>

View file

@ -1,22 +1,3 @@
"""
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:

View file

@ -4,11 +4,10 @@ try:
admin_app=Blueprint('admin_app', __name__, static_folder='static')
env=env_theme(__file__)
t=PTemplate(env)
wsgi_module=True
except:
pass

View file

@ -1,17 +1,13 @@
from settings import config
from flask import g, url_for, session
from flask import g, url_for
from paramecio2.modules.admin.models.admin import UserAdmin
from paramecio2.libraries.generate_admin_class import GenerateAdminClass
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.db.coreforms import SelectForm
from paramecio2.libraries.db.coreforms import HiddenForm
import copy
from paramecio2.modules.admin import admin_app, t as admin_t
pgettext=PGetText(__file__+'/../')
_=pgettext.gettext
t=copy.copy(admin_t)
@admin_app.route('/admin/ausers/', methods=['GET', 'POST'])
@ -31,47 +27,37 @@ def ausers():
user_admin.fields['dark_theme'].name_form=SelectForm
user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'dark_theme', 'disabled', 'double_auth', 'last_login', 'privileges'])
user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'dark_theme', 'disabled', 'double_auth', 'last_login'])
user_admin.forms['privileges'].arr_select={0: _('Without privileges'), 1: _('Selected privileges'), 2: _('Administrator')}
user_admin.forms['privileges'].arr_select={0: I18n.lang('admin', 'without_privileges', 'Without privileges'), 1: I18n.lang('admin', 'selected_privileges', 'Selected privileges'), 2: I18n.lang('admin', 'administrator', 'Administrator')}
user_admin.forms['disabled'].arr_select={0: _('User enabled'), 1: _('User disabled')}
user_admin.forms['disabled'].arr_select={0: I18n.lang('admin', 'user_enabled', 'User enabled'), 1: I18n.lang('admin', 'user_disabled', 'User disabled')}
user_admin.forms['double_auth'].arr_select={0: _('No'), 1: _('Yes')}
user_admin.forms['double_auth'].arr_select={0: I18n.lang('admin', 'no', 'No'), 1: I18n.lang('admin', 'yes', 'Yes')}
user_admin.fields['password'].protected=False
user_admin.forms['dark_theme'].arr_select={0: _('Light theme'), 1: _('Dark theme')}
user_admin.forms['dark_theme'].arr_select={0: I18n.lang('admin', 'light_theme', 'Light theme'), 1: I18n.lang('admin', 'dark_theme', 'Dark theme')}
user_admin.check_user=False
user_admin.check_email=False
url=url_for('admin_app.ausers')
admin=GenerateAdminClass(user_admin, url)
admin=GenerateAdminClass(user_admin, url, t)
admin.list.fields_showed=['username']
admin.list.search_fields=['username']
admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'lang', 'dark_theme', 'double_auth', 'disabled', 'last_login', 'privileges']
admin.post_update=update_lang
admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'lang', 'dark_theme', 'double_auth', 'disabled', 'last_login']
form_admin=admin.show()
if type(form_admin).__name__=='str':
return t.load_template('content.phtml', title=_('Users edit'), contents=form_admin, path_module='admin_app.ausers')
return t.load_template('content.phtml', title=I18n.lang('admin', 'users_edit', 'Users edit'), contents=form_admin, path_module='admin_app.ausers')
else:
return form_admin
def update_lang(admin, item_id):
arr_row=admin.model.select_a_row(item_id)
session['lang']=arr_row.get('lang', I18n.get_default_lang())
return True

View file

@ -1,6 +1,6 @@
from flask import Blueprint, redirect, session, url_for, request, g, make_response, abort
from settings import config
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.datetime import now, format_local_strtime, timestamp_to_datetime, obtain_timestamp
from paramecio2.libraries.formsutils import show_form, generate_csrf, set_extra_forms_user, pass_values_to_form
from paramecio2.libraries.db.webmodel import WebModel
@ -15,27 +15,6 @@ from os import path
from paramecio2.modules.admin import admin_app, t
from paramecio2.libraries.sendmail import SendMail
from paramecio2.libraries.formsutils import check_csrf
from hmac import compare_digest as compare_hash
from paramecio2.modules.admin.libraries.admin_auth import admin_prepare, admin_finished, modules_access
try:
import ujson as json
except:
import json
try:
import crypt
crypt_pass=True
except:
crypt_pass=False
login_tries=5
if hasattr(config, 'login_tries'):
login_tries=config.login_tries
gtext=PGetText(__file__)
_=gtext.gettext
yes_recovery_login=False
email_address='localhost'
@ -47,9 +26,62 @@ if hasattr(config, 'email_address'):
email_address=config.email_address
admin_prepare=admin_app.before_request(admin_prepare)
#admin_app=Blueprint('admin_app', __name__, static_folder='static')
admin_finished=admin_app.after_request(admin_finished)
@admin_app.before_request
def admin_prepare():
g.connection=WebModel.connection()
if request.endpoint!='admin_app.login' and request.endpoint!='admin_app.signup' and request.endpoint!='admin_app.need_auth' and request.endpoint!='admin_app.auth_check':
if 'login_admin' not in session:
if 'remember_login_admin' in request.cookies:
with g.connection.query('select count(id) as count_id from useradmin where token_login=%s', [request.cookies['remember_login_admin']]) as cursor:
arr_count=cursor.fetchone()
if arr_count['count_id']==0:
url_redirect=config.domain_url+url_for('admin_app.login', _external=False)
return redirect(url_redirect)
else:
session['login_admin']=True
else:
url_redirect=config.domain_url+url_for('admin_app.login', _external=False)
return redirect(url_redirect)
else:
#print(session['verify_auth'])
if request.endpoint!='admin_app.logout':
if not session.get('verify_auth', True):
url_redirect=config.domain_url+url_for('admin_app.need_auth', _external=False)
return redirect(url_redirect)
"""
if request.method=='POST':
check_csrf()
"""
@admin_app.after_request
def admin_finished(response):
#print('pepe')
g.connection.close()
return response
# Load modules from admin
@ -64,24 +96,67 @@ for app in config.apps:
a=import_module(config_path)
arr_modules_admin={}
for app_load in config_admin:
#print(app)
if len(app_load)==3:
arr_modules_admin[app_load[2]+'/']=import_module(app_load[1])
#arr_modules_admin[app_load[2]+'/'].admin=admin_app.route(arr_modules_admin[app_load[2]+'/'])(arr_modules_admin[app_load[2]+'/'].admin)
#print(app_load[1])
elif len(app_load)==4:
arr_modules_admin[app_load[2]+'/'+app_load[3]]=import_module(app_load[1])
#print(app_load[1])
@admin_app.route('/admin/')
def admin():
return t.load_template('home.phtml', title=_('Admin'))
return t.load_template('home.phtml', title=I18n.lang('admin', 'admin', 'Admin'))
"""
@admin_app.route('/admin/')
@admin_app.route('/admin/<module>', methods=['GET', 'POST'])
@admin_app.route('/admin/<module>/<submodule>', methods=['GET', 'POST'])
def admin(module='', submodule=''):
if module=='':
return t.load_template('home.phtml', title=I18n.lang('admin', 'paramecio_admin', 'Paramecio admin'))
else:
path_module=module+'/'+submodule
if path_module in arr_modules_admin:
t_mod=copy.copy(t)
templates_path=path.dirname(arr_modules_admin[module+'/'+submodule].__file__).replace('/admin', '')+'/templates'
try:
index_value = t_mod.env.directories.index(templates_path)
except ValueError:
t_mod.env.directories.insert(0, templates_path)
content=arr_modules_admin[path_module].admin(t=t)
if type(content).__name__=='str':
return t.load_template('content.phtml', title=I18n.lang('admin', 'paramecio_admin', 'Paramecio admin'), contents=content, path_module=path_module)
else:
return content
else:
abort(404)
"""
@admin_app.route('/admin/logout/')
def logout():
@ -102,7 +177,7 @@ def logout():
@admin_app.route('/admin/login/', methods=['GET', 'POST'])
def login():
new_crypt=False
#connection=WebModel.connection()
user_admin=UserAdmin(g.connection)
@ -129,25 +204,9 @@ def login():
arr_user=user_admin.set_conditions('WHERE username=%s', [username]).select_a_row_where()
if arr_user and not check_login_tries():
if arr_user:
# Layer compatibility with old crypt password
check_pass=user_admin.fields['password'].verify(password, arr_user['password'])
if not check_pass:
try:
check_pass=compare_hash(arr_user['password'], crypt.crypt(password, arr_user['password']))
new_crypt=True
except:
print('Warning: python developers deleting unix crypt module support, you cannot use sha512 passwords.')
check_pass=False
pass
if check_pass:
if user_admin.fields['password'].verify(password, arr_user['password']):
if not arr_user['disabled']:
@ -172,15 +231,15 @@ def login():
timestamp=int(time())+315360000
user_admin.fields['token_login'].protected=False
resp.set_cookie('remember_login_admin', value=json.dumps((arr_user['id'], remember_key)), max_age=315360000, expires=timestamp, path=config.application_root)
resp.set_cookie('remember_login_admin', value=remember_key, max_age=315360000, expires=timestamp, path=config.application_root)
if arr_user['double_auth']:
token_auth=create_key(8)
session['verify_auth']=False
#user_admin.set_conditions('WHERE id=%s', [arr_user['id']]).update({'token_auth': token_auth})
user_admin.fields['token_auth'].protected=False
arr_update['token_auth']=token_auth
@ -189,7 +248,9 @@ def login():
sendmail=SendMail(ssl=True)
sendmail.send(config.portal_email, [arr_user['email']], _('Code for complete login'), _('We send to you a code for activate your account using double authentication:')+"\n"+token_auth, content_type='plain', attachments=[])
# def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[]):
sendmail.send(config.portal_email, [arr_user['email']], I18n.lang('admin', 'code_for_complete_login', 'Code for complete login'), I18n.lang('admin', 'code_for_complete_login_explain', 'We send to you a code for activate your account using double authentication:')+"\n"+token_auth, content_type='plain', attachments=[])
if arr_user['dark_theme']:
session['theme']='1'
@ -198,17 +259,8 @@ def login():
arr_update['last_login']=now()
session['lang']=arr_user.get('lang', I18n.default_lang)
if len(arr_update)>0:
if new_crypt:
print('Changing password for %s to argon2' % arr_user['username'])
user_admin.fields['password'].protected=False
arr_update['password']=password
user_admin.set_conditions('WHERE id=%s', [arr_user['id']]).update(arr_update)
return resp
@ -231,6 +283,8 @@ def login():
return {'error': 1, 'you_cannot_login': you_cannot_login}
#if
else:
forms=show_form(post, user_admin.forms, t, yes_error=False)
@ -348,19 +402,21 @@ def auth_check():
@admin_app.route('/admin/change_theme/')
def change_theme():
db=g.connection
theme_selected=str(request.args.get('theme', '0'))
session['theme']=theme_selected
if not db.query('update useradmin set dark_theme=%s WHERE id=%s', [session['theme'], session['user_id']]):
error=1
error=0
return {'error': error}
"""
@admin_app.route('/admin/recovery_password/')
def recovery_password():
return ""
"""
def check_login_tries():
logintries=LoginTries(g.connection)
@ -385,7 +441,7 @@ def check_login_tries():
if arr_try:
if arr_try['num_tries']<login_tries:
if arr_try['num_tries']<5:
logintries.query('update logintries set num_tries=num_tries+1, last_login=%s WHERE ip=%s', [date_now, ip])

View file

@ -1,132 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-19 15:30+0100\n"
"PO-Revision-Date: 2023-12-19 15:32+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.1\n"
#: admin/modules.py:51
msgid "Error: git url wrong"
msgstr "Error: Url git equivocada"
#: admin/modules.py:58
msgid "Error: cannot install the module, view the error in wsgi server log"
msgstr "Error: no puedo instalar el módulo, ver el error en el servidor wsgi"
#: admin/ausers.py:36
msgid "Without privileges"
msgstr "Sin privilegios"
#: admin/ausers.py:36
msgid "Selected privileges"
msgstr "Privilegios seleccionados"
#: admin/ausers.py:36
msgid "Administrator"
msgstr "Administrador"
#: admin/ausers.py:38
msgid "User enabled"
msgstr "Usuario activado"
#: admin/ausers.py:38
msgid "User disabled"
msgstr "Usuario desactivado"
#: admin/ausers.py:40
msgid "No"
msgstr "No"
#: admin/ausers.py:40
msgid "Yes"
msgstr "Sí"
#: admin/ausers.py:44
msgid "Light theme"
msgstr "Tema claro"
#: admin/ausers.py:44 templates/dashboard.phtml:110
msgid "Dark theme"
msgstr "Tema oscuro"
#: admin/ausers.py:63 settings/config_admin.py:11
msgid "Users edit"
msgstr "Editar usuarios"
#: app.py:126
msgid "Admin"
msgstr "Admin"
#: app.py:260
msgid "Code for complete login"
msgstr "Código para completar login"
#: app.py:260
msgid ""
"We send to you a code for activate your account using double authentication:"
msgstr ""
"Te enviaremos un código para activr tu cuenta usando doble autenticación"
#: settings/config_admin.py:9
msgid "Users"
msgstr "Usuarios"
#: templates/modules.phtml:3
msgid "Add new module"
msgstr "Añadir nuevo módulo"
#: templates/home.phtml:4 templates/users.phtml:4
msgid "Welcome to Paramecio Admin"
msgstr "Bienvenido a Paramecio Admin"
#: templates/home.phtml:7 templates/users.phtml:7
msgid "From here you can admin your site"
msgstr "Desde aquí puedes administrar tu sitio"
#: templates/login.phtml:4 templates/login.phtml:99 templates/need_auth.phtml:2
#: templates/need_auth.phtml:6
msgid "Paramecio Login"
msgstr "Paramecio login"
#: templates/login.phtml:103
msgid "Remember login?"
msgstr "¿Recordar login?"
#: templates/login.phtml:110
msgid "Recovery password?"
msgstr "¿Recuperar contraseña?"
#: templates/login.phtml:112
msgid "Remember that only have 3 attempts"
msgstr "Recuera que sólo tienes 3 intentos"
#: templates/register.phtml:51 templates/register.phtml:55
msgid "Paramecio Sign up"
msgstr "Paramecio Registro"
#: templates/dashboard.phtml:66
msgid "Applications"
msgstr "Aplicaciones"
#: templates/need_auth.phtml:9
msgid ""
"Check your email for get instructions for complete login with double auth or"
msgstr ""
"Mira tu email para obtener instrucciones para completar el login con doble "
"autenticación o"
#: templates/need_auth.phtml:10
msgid "Code"
msgstr "Código"

View file

@ -1,89 +0,0 @@
from flask import g, request, redirect, session, url_for
from paramecio2.libraries.db.webmodel import WebModel
from settings import config
from paramecio2.libraries.db.extrafields.passwordfield import PasswordField
from paramecio2.libraries.i18n import I18n, PGetText
try:
import ujson as json
except:
import json
modules_access=[]
def admin_prepare():
g.connection=WebModel.connection()
if request.endpoint!='admin_app.login' and request.endpoint!='admin_app.signup' and request.endpoint!='admin_app.need_auth' and request.endpoint!='admin_app.auth_check':
if 'login_admin' not in session:
if 'remember_login_admin' in request.cookies:
try:
arr_cookie=json.loads(request.cookies['remember_login_admin'])
except:
arr_cookie=(0, '')
#print(arr_cookie)
#with g.connection.query('select count(id) as count_id from useradmin where token_login=%s', [request.cookies['remember_login_admin']]) as cursor:
with g.connection.query('select id, token_login, dark_theme from useradmin where id=%s', [arr_cookie[0]]) as cursor:
arr_user=cursor.fetchone()
if arr_user:
passfield=PasswordField('token_login')
if passfield.verify(arr_cookie[1], arr_user['token_login']):
session['login_admin']=True
session['user_id']=arr_user['id']
if arr_user['dark_theme']:
session['theme']='1'
else:
session['theme']='0'
session['lang']=arr_user.get('lang', I18n.default_lang)
else:
session.clear()
url_redirect=config.domain_url+url_for('admin_app.login', _external=False)
return redirect(url_redirect)
else:
session.clear()
url_redirect=config.domain_url+url_for('admin_app.login', _external=False)
return redirect(url_redirect)
else:
url_redirect=config.domain_url+url_for('admin_app.login', _external=False)
return redirect(url_redirect)
else:
if request.endpoint!='admin_app.logout':
if not session.get('verify_auth', True):
url_redirect=config.domain_url+url_for('admin_app.need_auth', _external=False)
return redirect(url_redirect)
def admin_finished(response):
g.connection.close()
return response

View file

@ -1,75 +0,0 @@
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.mtemplates import env_theme, PTemplate
from paramecio2.modules.admin.models.admin import UserAdmin, LoginTries
from paramecio2.libraries.db.webmodel import WebModel
from paramecio2.libraries.db import simplequery
from settings import config
from paramecio2.libraries.datetime import now, format_local_strtime, timestamp_to_datetime, obtain_timestamp
from paramecio2.libraries.keyutils import create_key_encrypt, create_key
from time import time
#from paramecio2.wsgiapp import app
#from paramecio2.modules.admin2 import admin_app
#from bottle import request, redirect, Bottle, response
from flask import request, redirect
#from paramecio2.modules.admin.libraries.loginplugin import check_login
#from paramecio2.libraries.sessionplugin import SessionPlugin
#from paramecio2.libraries.httputils import GetPostFiles
from paramecio2.libraries.formsutils import check_form, csrf_token
from paramecio2.libraries.db.coreforms import PasswordForm
from paramecio2.libraries.sendmail import SendMail
#from paramecio2.modules.admin.libraries.config import modules_admin
login_tries=5
if hasattr(config, 'login_tries'):
login_tries=config.login_tries
seconds_login=300
if hasattr(config, 'seconds_login'):
seconds_login=config.seconds_login
def check_login_tries(request, db):
logintries=LoginTries(db)
logintries.safe_query()
ip=request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
"""
if 'x-real-ip' in request.headers:
ip=request.headers['x-real-ip']
elif 'x-forwarded-for' in request.headers:
ip=request.headers['x-forwarded-for']
else:
ip=request.client.host
"""
you_cannot_login=0
now_str=now()
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
date_check=format_local_strtime('YYYY-MM-DD HH:mm:ss', timestamp_to_datetime(obtain_timestamp(now_str)-seconds_login))
logintries.query('delete from logintries where last_login<%s', [date_check])
arr_try=logintries.set_conditions('WHERE ip=%s', [ip]).select_a_row_where()
if arr_try:
if arr_try['num_tries']<login_tries:
logintries.query('update logintries set num_tries=num_tries+1, last_login=%s WHERE ip=%s', [date_now, ip])
else:
you_cannot_login=1
else:
logintries.query('insert into logintries (`ip`, `num_tries`, `last_login`) VALUES (%s, %s, %s)', [ip, 1, date_now])
return you_cannot_login

View file

@ -11,7 +11,7 @@ body
margin:0px;
background-color:#f4f6f9;
font-family: sans, sans-serif, serif;
font-family: "Roboto", sans, sans-serif, serif;
font-size: 16px;
/*-webkit-transition: all 0.5s ease-in-out;
transition: all 0.5s ease-in-out;
@ -409,13 +409,6 @@ p {
}
.dark .form textarea {
background-color: #1e202a;
color: #fbfbfb;
}
.form label {
display: block;
/*width: 150px;*/
@ -1002,7 +995,7 @@ a.form_button_tab:hover
#layer_loading {
z-index:99999;
z-index:50000;
/*background-color:rgba(0,0,0,0.4);*/
/*opacity:0.5;*/
position:absolute;
@ -1036,7 +1029,6 @@ a.form_button_tab:hover
box-sizing: border-box;
/* border: solid #fff 1px;*/
float:left;
overflow:hidden;
}
@ -1046,7 +1038,6 @@ a.form_button_tab:hover
box-sizing: border-box;
/* border: solid #fff 1px;*/
float:left;
overflow:hidden;
}
@ -1057,7 +1048,6 @@ a.form_button_tab:hover
box-sizing: border-box;
/*border: solid #f00 1px;*/
float:left;
overflow:hidden;
}
@ -1067,7 +1057,6 @@ a.form_button_tab:hover
box-sizing: border-box;
/*border: solid #f00 1px;*/
float:left;
overflow:hidden;
}

View file

@ -2,7 +2,7 @@ body {
margin:0px;
background: #fbfbfb;
font-family: sans , sans-serif;
font-family: 'Open Sans', sans-serif;
}

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
Subproject commit 353a80d8437aaf449ecdd7e459c1fcf7575f41bc

View file

@ -82,7 +82,7 @@ class UserAdmin(UserModel):
self.register(corefields.CharField('token_recovery'))
self.register(PasswordField('token_login'))
self.register(corefields.CharField('token_login'))
self.register(PasswordField('token_auth'))

View file

@ -1,23 +1,8 @@
from paramecio2.libraries.config_admin import config_admin
from paramecio2.libraries.i18n import I18n, PGetText
from paramecio2.libraries.i18n import I18n
#modules_admin=[[I18n.lang('admin', 'users_admin', 'User\'s Admin'), 'paramecio.modules.admin.admin.ausers', 'ausers']]
pgettext=PGetText(__file__+'/../')
_=pgettext.gettext
config_admin.append([I18n.lang('admin', 'users', 'Users')])
def users_text():
return _('Backend Users')
def users_edit():
return _('Backend users edit')
config_admin.append([users_text])
config_admin.append([users_edit, 'paramecio2.modules.admin.admin.ausers', 'admin_app.ausers', '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="24" style="fill: currentColor;display: inline-block;vertical-align: -.130em;position:relative;left:-6px;top:2px;"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320 312C386.3 312 440 258.3 440 192C440 125.7 386.3 72 320 72C253.7 72 200 125.7 200 192C200 258.3 253.7 312 320 312zM290.3 368C191.8 368 112 447.8 112 546.3C112 562.7 125.3 576 141.7 576L498.3 576C514.7 576 528 562.7 528 546.3C528 447.8 448.2 368 349.7 368L290.3 368z"/></svg>'])
#config_admin.append([I18n.lang('admin', 'modules', 'Modules')])
#config_admin.append([I18n.lang('admin', 'edit_module', 'Edit modules'), 'paramecio2.modules.admin.admin.modules', 'admin_app.amodules', 'fa-dropbox'])
config_admin.append([I18n.lang('admin', 'users_edit', 'Users edit'), 'paramecio2.modules.admin.admin.ausers', 'admin_app.ausers', 'fa-user'])

View file

@ -17,11 +17,11 @@ if session.get('theme', '0')=='1':
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>${title}</title>
<link href="${make_media_url('css/font-awesome.min.css', 'admin')}" rel="stylesheet" />
<link href="${make_media_url('css/admin.css', 'admin')}" rel="stylesheet" />
<link href="${make_media_url('css/font-awesome.min.css', 'admin')}" rel="stylesheet" />
<link href="${make_media_url('css/responsive-nav.css', 'admin')}" rel="stylesheet" />
<link href="${make_media_url('css/tooltipster.bundle.min.css', 'admin')}" rel="stylesheet" />
<!--<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> -->
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<%block name="extra_css">
</%block>
<script language="Javascript" src="${make_media_url('js/jquery.min.js', 'admin')}"></script>
@ -34,11 +34,11 @@ ${load_js()|n}
</%block>
</head>
<body class="${dark_css}">
<div id="layer_loading"><div id="container_loading"><div class="lds-dual-ring"></div></div></div>
<div id="layer_loading" style="display:none;"><div id="container_loading"><div class="lds-dual-ring"></div></div></div>
<div id="languages_general">
</div>
<div id="logout">
<%block name="logout"><a href="${url_for('admin_app.logout')}"><i class="fa fa-power-off" aria-hidden="true"></i> Logout</a></%block>
<%block name="logout"><a href="${url_for('.logout')}"><i class="fa fa-power-off" aria-hidden="true"></i> Logout</a></%block>
</div>
<div id="center_body">
@ -63,9 +63,7 @@ ${load_js()|n}
<nav id="menu" class="nav-collapse">
<ul>
<li class="menu_title"><%block name="applications"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="22" style="fill: currentColor;display: inline-block;vertical-align: -.130em;position:relative;left:-3px;top:2px;">
<!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
<path d="M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z"/></svg>${_('Applications')}</li></%block>
<li class="menu_title"><%block name="applications"><i class="fa fa-gear" aria-hidden="true"></i>${lang('admin', 'applications', 'Applications')}</li></%block>
<%block name="menu_list">
<%
@ -77,7 +75,6 @@ ${load_js()|n}
<%
class_selected=''
link_text=''
%>
@ -92,33 +89,13 @@ ${load_js()|n}
if len(admin)>3:
icon_module=admin[3]
if type(admin[0]).__name__=='function':
link_text=admin[0]()
else:
link_text=admin[0]
%>
<li><a href="${url_for(admin[2])}" class="${class_selected}">&nbsp;
% if icon_module.startswith('fa-'):
<i class="fa ${icon_module}" aria-hidden="true"></i>
%else:
${icon_module|n}
% endif
${link_text}
</a>
</li>
<li><a href="${url_for(admin[2])}" class="${class_selected}">&nbsp;<i class="fa ${icon_module}" aria-hidden="true"></i>${admin[0]}</a></li>
% elif len(admin)==1:
<%
if type(admin[0]).__name__=='function':
link_text=admin[0]()
else:
link_text=admin[0]
%>
<li><div class="father_admin">${link_text}</div></li>
<li><div class="father_admin">${admin[0]}</div></li>
% endif
@ -130,7 +107,7 @@ ${load_js()|n}
<h1>${title}</h1>
<div class="switch-btn">
<div class="switch-text">
${_('Dark theme')}
Dark Mode
</div>
<div class="switch-slider">
<label class="switch">
@ -219,12 +196,6 @@ ${load_js()|n}
});
$(document).ready(function () {
$('#layer_loading').hide();
});
</script>
<%block name="jscript_block">
</%block>

View file

@ -1,9 +1,9 @@
<%inherit file="dashboard.phtml"/>
<%block name="content">
<div class="title">
${_('Welcome to Paramecio Admin')}
Welcome to Paramecio Admin
</div>
<div class="cont">
${_('From here you can admin your site')}
From here you can admin your site
</div>
</%block>

View file

@ -1,9 +1,9 @@
<%inherit file="home.html"/>
<%block name="content">
<div class="title">
${_('Welcome to Admin dashboard')}
${lang('admin', 'welcome_to_admin_dashboard', 'Welcome to Admin dashboard')}
</div>
<div class="cont">
${_('From here you can configure your site')}.
${lang('admin', 'from_here_you_can_configure_your_site', 'From here you can configure your site')}.
</div>
</%block>

View file

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title><%block name="title">${_('Paramecio Login')}</%block></title>
<title><%block name="title">${lang('admin', 'login', 'Paramecio Login')}</%block></title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!--<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>-->
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link href="${make_media_url('css/login.css', 'admin')}" rel='stylesheet' type='text/css'>
<link href="${make_media_url('css/font-awesome.min.css', 'admin')}" rel='stylesheet' type='text/css'>
<%block name="extra_css">
@ -58,25 +58,25 @@
if(data.hasOwnProperty('disable')) {
$('#username_error').html("${_('Error, your user is disabled, you need support of web administration')}");
$('#username_error').html("${lang('common', 'error_disabled', 'Error, your user is disabled, you need support of web administration')}");
} if(data.hasOwnProperty('you_cannot_login')) {
if(data.you_cannot_login) {
$('#username_error').html("${_('Error, excessive tries, wait some minutes for login again')}");
$('#username_error').html("${lang('common', 'error_tries_disabled', 'Error, excessive tries, wait some minutes for login again')}");
}
else {
$('#username_error').html("${_('Error, wrong username or password')}");
$('#username_error').html("${lang('common', 'error_login', 'Error, wrong username or password')}");
}
}
else {
$('#username_error').html("${_('Error, wrong username or password')}");
$('#username_error').html("${lang('common', 'error_login', 'Error, wrong username or password')}");
}
@ -96,21 +96,20 @@
<%block name="content">
<form id="login">
<div id="title">
${_('Paramecio Login')}
${lang('admin', 'login', 'Paramecio Login')}
</div>
${forms|n}
${csrf_token()|n}
<div class="form">
${_('Remember login?')} <input type="checkbox" id="remember_login" name="remember_login" value="1">
${lang('admin', 'remember_login', 'Remember login?')} <input type="checkbox" id="remember_login" name="remember_login" value="1">
</div>
<div id="submit_block">
<input type="submit" value="${_('Login')}" class="submit" id="login_submit"/>
<input type="submit" value="${lang('common', 'login', 'Login')}" class="submit" id="login_submit"/>
<span id="loading">&nbsp;</span>
</div>
% if yes_recovery_login:
<div class="form"><a href="${url_for('.recovery_password')}">${_('Recovery password?')}</a></div>
<div class="form"><a href="${url_for('.recovery_password')}">${lang('admin', 'recovery_password', 'Recovery password?')}</a></div>
% endif
<!--<div class="form">${_('Remember that only have 3 attempts')}</div>-->
<div class="form">${lang('admin', 'remember_tries', 'Remember that only have 3 attempts')}</div>
</form>
</%block>
</body>

View file

@ -1,17 +1,17 @@
<%inherit file="login.phtml"/>
<%block name="title">${_('Paramecio Login')}</%block>
<%block name="title">${lang('admin', 'login', 'Paramecio Login')}</%block>
<%block name="content">
<form id="login">
<div id="title">
${_('Paramecio Login')}
${lang('admin', 'login', 'Paramecio Login')}
</div>
<div class="form">
<p align="center">${_('Check your email for get instructions for complete login with double auth or')} <a href="${url_for('.logout')}">logout</a> and login again with other user</p>
<p><label>${_('Code')} *</label><input type="text" class="" name="code" id="code_form" value="" /> <span class="error" id="code_error"></span></p>
<p align="center">${lang('admin', 'check_your_email', 'Check your email for get instructions for complete login with double auth or')} <a href="${url_for('.logout')}">logout</a> and login again with other user</p>
<p><label>${lang('admin', 'code', 'Code')} *</label><input type="text" class="" name="code" id="code_form" value="" /> <span class="error" id="code_error"></span></p>
${csrf_token()|n}
</div>
<div id="submit_block">
<input type="submit" value="${_('Send code')}" class="submit" id="code_submit"/>
<input type="submit" value="${lang('common', 'send_code', 'Send code')}" class="submit" id="code_submit"/>
<span id="loading">&nbsp;</span>
</div>
</form>
@ -60,17 +60,17 @@
if(data.hasOwnProperty('disable')) {
$('#code_error').html("${_('Error, your user is disabled, you need support of web administration')}");
$('#code_error').html("${lang('common', 'error_disabled', 'Error, your user is disabled, you need support of web administration')}");
} else {
$('#code_error').html("${_('Error, wrong code')}");
$('#code_error').html("${lang('common', 'error_wrong_code', 'Error, wrong code')}");
}
if(data.you_cannot_login) {
$('#code_error').html("${_('Error, excessive tries, wait some minutes for login again')}");
$('#code_error').html("${lang('common', 'error_tries_disabled', 'Error, excessive tries, wait some minutes for login again')}");
}

View file

@ -48,17 +48,16 @@
});
</script>
</%block>
<%block name="title">${_('Paramecio Sign up')}</%block>
<%block name="title">${lang('admin', 'sign_up', 'Paramecio Sign up')}</%block>
<%block name="content">
<form id="login">
<div id="title">
${_('Paramecio Sign up')}
${lang('admin', 'sign_up', 'Paramecio Sign up')}
</div>
${forms|n}
${csrf_token()|n}
<div id="result_register"></div>
<div id="submit_block">
<input type="submit" value="${_('Sign up')}" class="submit" id="register_submit"/>
<input type="submit" value="${lang('common', 'sign_up', 'Sign up')}" class="submit" id="register_submit"/>
<span id="loading">&nbsp;</span>
</div>
</form>

View file

@ -1,9 +1,9 @@
<%inherit file="dashboard.phtml"/>
<%block name="content">
<div class="title">
${_('Welcome to Paramecio Admin')}
Welcome to Paramecio Admin
</div>
<div class="cont">
${_('From here you can admin your site')}
From here you can admin your site
</div>
</%block>

View file

@ -2,11 +2,6 @@
from settings import config
from paramecio2.libraries.mtemplates import PTemplate, env_theme
from paramecio2.modules.welcome import welcome_app
from paramecio2.libraries.i18n import PGetText
pgettext=PGetText(__file__)
_=pgettext.gettext
env=env_theme(__file__)
@ -15,7 +10,7 @@ t=PTemplate(env)
@welcome_app.route('/welcome')
def home():
return t.load_template('welcome.phtml', title=_("Welcome"), content=_('Welcome to the real world'))
return t.load_template('welcome.phtml', title="Welcome", content='Welcome to the real world')
#return render_template('welcome.html', title="Welcome")
"""

View file

@ -1,30 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-12-18 22:13+0100\n"
"PO-Revision-Date: 2023-12-18 22:13+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.1\n"
#: app.py:13
msgid "Welcome"
msgstr "Bienvenido"
#: app.py:13
msgid "Welcome to the real world"
msgstr "Bienvenido al mundo real"
#: templates/welcome.phtml:75
msgid "Paramecio, a system created for create webapps"
msgstr "Paramecio, un sistema creado para crear webapps"

View file

@ -72,7 +72,7 @@
${content}
</div>
<div class="footer">${_('Paramecio, a system created for create webapps')}</div>
<div class="footer">Paramecio, a system created for create webapps</div>
</div>
</body>

View file

@ -1,151 +0,0 @@
#!/usr/bin/env python3
import traceback
import argparse
import os,sys
import shutil
import getpass
from pathlib import Path
from importlib import import_module
from paramecio2.libraries.slugify import slugify
import json
sys.path.insert(0, os.path.realpath('.'))
try:
from settings import config
except:
pass
def start():
"""Module for create new modules for paramecio
"""
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
parser.add_argument('--folder', help='The folder where the new paramecio module is located', required=True)
args=parser.parse_args()
workdir=os.path.dirname(os.path.abspath(__file__))
# Create directory
real_path=os.path.basename(slugify(args.folder))
path=Path('modules/'+real_path)
path.mkdir(0o755, True)
#open('modules/'+args.path+'/__init__.py', 'a').close()
with open('modules/'+real_path+'/__init__.py', 'a') as f:
f.write("from flask import Blueprint\n\n")
f.write("{}_app=Blueprint('{}_app', __name__)\n\n".format(real_path, real_path))
f.write("@{}_app.route('/{}')\n".format(real_path, real_path))
f.write("def {}_home():\n".format(real_path))
f.write(" return {'hello': 'world'}")
modules_json={}
if os.path.isfile('settings/modules.json'):
with open('settings/modules.json') as f:
#apps={'monit2': ['modules.monit2', 'monit2_app', '/'], 'welcome': ['paramecio2.modules.welcome', 'welcome_app', '/'], 'pastafari2': ['modules.pastafari2', 'pastafari_app', '/'], 'apache': ['modules.apache', 'apache_app', '/'], 'mariadb': ['modules.mariadb', 'mariadb_app', '/'], 'apiv1': ['modules.apiv1', 'apiv1_app', '/'], 'admin': ['paramecio2.modules.admin', 'admin_app', '/']}
json_text=f.read()
if json_text!='':
modules_json=json.loads(json_text)
with open('settings/modules.json', 'w+') as f:
modules_json[real_path]=['modules.'+real_path, real_path+'_app', '/']
f.write(json.dumps(modules_json, indent=4))
print('Created the new module. Please, reload the WSGI server and go to url /'+real_path)
"""
except:
print('Error: cannot create the directory. Check if exists and if you have permissions')
exit(1)
"""
#Create base controller file
#f=open('modules/'+args.path+'/index.py', 'w')
"""
try:
shutil.copy(workdir+'/examples/app.py', 'modules/'+args.path+'/__init__.py')
except:
print('Error: cannot copy controller example. Check if you have permissions')
exit(1)
"""
# Regenerate modules
#regenerate_modules_config()
"""
def regenerate_modules_config():
print("Regenerating modules configuration...")
modules=[]
modules.append("#!/usr/bin/env python3\n\n")
modules.append("list_modules=[]\n\n")
for module in config.modules:
try:
controller_path=import_module(module)
controller_base=os.path.dirname(controller_path.__file__)
base_module=module.split('.')[-1]
dir_controllers=os.listdir(controller_base)
modules.append('from '+module+' import ')
arr_controllers=[]
for controller in dir_controllers:
if controller.find('.py')!=-1 and controller.find('__init__')==-1:
controller_py=controller.replace('.py', '')
arr_controllers.append(controller_py)
#load(module+'.'+controller_py)
modules.append(", ".join(arr_controllers))
modules.append("\n\n")
#add_func_static_module(controller_base)
except:
print("Exception in user code:")
print("-"*60)
traceback.print_exc(file=sys.stdout)
print("-"*60)
exit(1)
with open('./settings/modules.py', 'w') as f:
f.write("".join(modules))
"""
if __name__=="__main__":
start()

View file

@ -1,115 +0,0 @@
#!/usr/bin/env python3
import traceback
import argparse
import os,sys
import shutil
import getpass
from pathlib import Path
from importlib import import_module
from paramecio2.libraries.slugify import slugify
import json
import subprocess
#from colorama import init, Fore, Back, Style
import colorama
sys.path.insert(0, os.path.realpath('.'))
try:
from settings import config
except:
pass
def start():
"""Module for create new modules for paramecio
"""
parser=argparse.ArgumentParser(description='A tool for add modules for paramecio from git url')
parser.add_argument('--git_url', help='The git url for clone the module.', required=True)
args=parser.parse_args()
colorama.init()
workdir='.'
os.chdir('./modules/')
if subprocess.call("git clone {}".format(args.git_url), shell=True) > 0:
print('Error, cannot install the git module. Do you have installed git?. Is a correct url?')
exit(1)
real_path=''
with os.scandir('.') as d:
for e in d:
#print(e.name)
if not e.name in config.apps:
#print(e.name)
if args.git_url.find(e.name)!=-1:
real_path=os.path.basename(e.name)
# Add dependencies using pip
os.chdir('../')
dependencies_file='./modules/{}/dependencies.json'.format(real_path)
if os.path.isfile(dependencies_file):
dep_json=[]
with open(dependencies_file) as f:
content=f.read()
try:
dep_json=json.loads(content)
except:
dep_json=[]
print('Error: cannot install dependencies for the package. Malformed json {}'.format(content))
if len(dep_json)>0:
if 'packages' in dep_json:
print('Install dependencies for the module...')
arr_packages=" ".join(dep_json['packages'])
if subprocess.call("pip install {}".format(arr_packages), shell=True) > 0:
print('Error, cannot install the pip libraries. Do you have installed pip?.')
if 'models' in dep_json:
print('Creating database tables from models in module...')
for model in dep_json['models']:
if subprocess.call("paramecio2db --model modules/{}/models/{}.py".format(real_path, model), shell=True) > 0:
print('Error, cannot install the model {}.'.format(model))
# Add to json configuration.
if real_path!='':
if os.path.isfile('settings/modules.json'):
modules_json={}
with open('settings/modules.json') as f:
print('Inserting new module in modules.json...')
json_text=f.read()
if json_text!='':
modules_json=json.loads(json_text)
with open('settings/modules.json', 'w+') as f:
modules_json[real_path]=['modules.'+real_path, real_path+'_app', '/']
f.write(json.dumps(modules_json, indent=4))
print('Please, reload your wsgi application for access to your new module')
if __name__=="__main__":
start()

Some files were not shown because too many files have changed in this diff Show more