Merge pull request 'modern' (#2) from modern into master
Reviewed-on: #2
This commit is contained in:
commit
b4af33d438
163 changed files with 6611 additions and 1387 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "paramecio/modules/admin/media/js/jsutils"]
|
||||
path = paramecio/modules/admin/media/js/jsutils
|
||||
url = git@git.cuchulu.com:paramecio/jsutils.git
|
||||
148
LICENSE.txt
148
LICENSE.txt
|
|
@ -1,23 +1,21 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
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 licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
our General Public Licenses are 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. 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.
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
|
|
@ -26,44 +24,34 @@ 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
|
@ -72,7 +60,7 @@ modification follow.
|
|||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
|
@ -549,35 +537,45 @@ 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. Use with the GNU Affero General Public License.
|
||||
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.
|
||||
|
||||
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 Affero General Public License into a single
|
||||
under version 3 of the GNU 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 special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
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
|
||||
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
|
||||
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 General
|
||||
Program specifies that a certain numbered version of the GNU Affero 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 General Public License, you may choose any version ever published
|
||||
GNU Affero 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 General Public License can be used, that proxy's
|
||||
versions of the GNU Affero 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.
|
||||
|
||||
|
|
@ -635,41 +633,29 @@ 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 General Public License as published by
|
||||
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 General Public License for more details.
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
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/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
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".
|
||||
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.
|
||||
|
||||
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 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>.
|
||||
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ include paramecio/*.py
|
|||
include README.md
|
||||
include LICENSE
|
||||
include REQUIREMENTS
|
||||
recursive-include paramecio/citoplasma *
|
||||
recursive-include paramecio/libraries *
|
||||
recursive-include paramecio/frontend *
|
||||
recursive-include paramecio/cromosoma *
|
||||
recursive-include paramecio/settings *
|
||||
recursive-include paramecio/modules *
|
||||
17
README.md
17
README.md
|
|
@ -12,22 +12,19 @@ Also, you need the next software installed in your os:
|
|||
|
||||
### Python 3.4 or later.
|
||||
|
||||
Paramecio should work fine in 3.3 but is tested in 3.4 and 3.5 python 3 versions.
|
||||
Paramecio should work fine since python 3.6 but is tested in 3.12 and 3.13 python 3 versions.
|
||||
|
||||
In Debian and Ubuntu you can install Python 3 using the next command: `apt-get install python3`.
|
||||
|
||||
In Fedora and other Red Hat derived distros you can use `yum install python3`. In RedHat/Centos 6 or 7 you need install [Ius Repos](https://ius.io/GettingStarted/) for get sane versions of python3. In new Fedora versions you can use dnf, a drop-in replacement for yum: `dnf install python3`.
|
||||
In Fedora and other Red Hat derived distros you can use `dnf install python3`.
|
||||
|
||||
### MySQL or MariaDB database servers.
|
||||
|
||||
MariaDB 10.0 and later are recommended.
|
||||
MariaDB 10.6 and later are recommended.
|
||||
|
||||
In Debian and Ubuntu you can install MariaDB using the next command: `apt-get install mariadb-server`.
|
||||
|
||||
In Fedora and other Red Hat derived distros you can use `yum install mariadb-server`.
|
||||
In RedHat/Centos 6 probably you need install adittional repositories for get latest versions of mariadb, but with MySQL 5.5, Paramecio should work fine.
|
||||
|
||||
When you will install the mysql server, you should create a new user and database for Paramecio.
|
||||
In Fedora and other Red Hat derived distros you can use `dnf install mariadb-server`.
|
||||
|
||||
### Pip
|
||||
|
||||
|
|
@ -35,7 +32,7 @@ Pip is the package manager of python. You can use the package manager of your os
|
|||
|
||||
In Debian and Ubuntu you can install pip using the next command: `apt-get install python3-pip`.
|
||||
|
||||
In Fedora and other Red Hat derived distros you can use `yum install python3-pip`. Of course, the command can change if you use Centos 6/7 with **Ius repos**.
|
||||
In Fedora and other Red Hat derived distros you can use `dnf install python3-pip`.
|
||||
|
||||
### Git
|
||||
|
||||
|
|
@ -43,7 +40,7 @@ In Fedora and other Red Hat derived distros you can use `yum install python3-pip
|
|||
|
||||
In Debian and Ubuntu you can install git using the next command: `apt-get install git`.
|
||||
|
||||
In Fedora and other Red Hat derived distros you can use `yum install git` or `dnf install git` in last fedora versions.
|
||||
In Fedora and other Red Hat derived distros you can use `dnf install git`.
|
||||
|
||||
## Install Paramecio Framework
|
||||
|
||||
|
|
@ -59,8 +56,6 @@ This command will install in your server paramecio framework with its dependenci
|
|||
|
||||
When Paramecio finish the installing, you can create your first paramecio site with `paramecio` command.
|
||||
|
||||
> If you install passlib and bcrypt python modules, your paramecio install will use bcrypt algorithm for crypt system passwords. If not, default system implementation crypt algorithm (normally the more strong algorithm available in the system) will be used.
|
||||
|
||||
### Tipical errors
|
||||
|
||||
If you get an error in your installation of dependencies how MarkupSafe or SqlAlchemy, please install gcc or install manually mako and sqlalchemy with your system package manager. For example, for debian and ubuntu:
|
||||
|
|
|
|||
|
|
@ -4,18 +4,18 @@ from bottle import route, get, post, run, default_app, abort, request, response,
|
|||
from settings import config
|
||||
#from beaker.middleware import SessionMiddleware
|
||||
from mimetypes import guess_type
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.citoplasma.datetime import set_timezone
|
||||
from itsdangerous import JSONWebSignatureSerializer
|
||||
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.libraries.datetime import set_timezone
|
||||
#from itsdangerous import JSONWebSignatureSerializer
|
||||
from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
from paramecio.wsgiapp import app
|
||||
#from paramecio.citoplasma.sessions import after_session
|
||||
#from paramecio.libraries.sessions import after_session
|
||||
|
||||
modules_pass=False
|
||||
|
||||
#app.reset()
|
||||
|
||||
#from paramecio.citoplasma.sessions import generate_session
|
||||
#from paramecio.libraries.sessions import generate_session
|
||||
|
||||
#Prepare links for static.
|
||||
#WARNING: only use this feature in development, not in production.
|
||||
|
|
@ -44,6 +44,10 @@ if hasattr(config, 'error_reporting'):
|
|||
|
||||
def prepare_app():
|
||||
|
||||
# In mod_wsgi, make strange thing with reloading.
|
||||
|
||||
app.reset()
|
||||
|
||||
def print_memory():
|
||||
print(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
|
||||
|
||||
|
|
@ -52,7 +56,7 @@ def prepare_app():
|
|||
module_loaded=None
|
||||
|
||||
#Getting paths for loaded modules for use in media load files
|
||||
|
||||
"""
|
||||
for module in config.modules:
|
||||
|
||||
#controller_path=sys.modules[module]
|
||||
|
|
@ -62,6 +66,7 @@ def prepare_app():
|
|||
base_module=module.split('.')[-1]
|
||||
|
||||
arr_module_path[base_module]=controller_base
|
||||
"""
|
||||
|
||||
#app.add_hook('before_request', print_memory)
|
||||
"""
|
||||
|
|
@ -84,7 +89,7 @@ def prepare_app():
|
|||
controller_path=import_module(added_app[0])
|
||||
|
||||
controller_base=os.path.dirname(controller_path.__file__)
|
||||
|
||||
print(controller_base)
|
||||
dir_controllers=os.listdir(controller_base)
|
||||
|
||||
for controller in dir_controllers:
|
||||
|
|
@ -97,23 +102,25 @@ def prepare_app():
|
|||
|
||||
a=import_module(module_app)
|
||||
|
||||
app_name=getattr(a, added_app[1])
|
||||
if added_app[1]!='':
|
||||
|
||||
#app.register_blueprint(app_name, url_prefix=added_app[2])
|
||||
app_name=getattr(a, added_app[1])
|
||||
|
||||
#app.mount(added_app[2], app_name)
|
||||
app_mounts[added_app[2]]=app_name
|
||||
app_mounts[added_app[2]]=app_name
|
||||
|
||||
arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__)
|
||||
|
||||
for k_app,v_app in app_mounts.items():
|
||||
|
||||
app.mount(k_app, v_app)
|
||||
#print(k_app)
|
||||
if k_app!='/':
|
||||
app.mount(k_app, v_app)
|
||||
elif k_app!='':
|
||||
app.merge(v_app)
|
||||
|
||||
set_timezone()
|
||||
|
||||
if error_reporting:
|
||||
from paramecio.citoplasma.error_reporting import ErrorReportingPlugin
|
||||
from paramecio.libraries.error_reporting import ErrorReportingPlugin
|
||||
|
||||
app.install(ErrorReportingPlugin())
|
||||
|
||||
|
|
@ -144,20 +151,21 @@ application=app
|
|||
|
||||
try:
|
||||
|
||||
from settings import modules
|
||||
|
||||
#from settings import modules
|
||||
prepare_app()
|
||||
|
||||
except:
|
||||
|
||||
app.reset()
|
||||
|
||||
@app.route('/')
|
||||
def catch_errors(all='/'):
|
||||
try:
|
||||
from pathlib import Path
|
||||
from settings import modules
|
||||
#from settings import modules
|
||||
import time
|
||||
prepare_app()
|
||||
p=Path('index.py')
|
||||
p=Path('app.py')
|
||||
p.touch()
|
||||
time.sleep(1)
|
||||
except:
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from importlib import import_module
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
import json
|
||||
from bottle import request
|
||||
|
||||
yes_session=False
|
||||
|
||||
i18n_module={}
|
||||
|
||||
def load_lang(*args):
|
||||
|
||||
for module in args:
|
||||
|
||||
lang_path=module[0]+'.i18n.'+module[1]
|
||||
|
||||
try:
|
||||
|
||||
i18n_module[lang_path]=import_module(lang_path)
|
||||
|
||||
pass
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
# here load the language
|
||||
|
||||
|
||||
class I18n:
|
||||
|
||||
default_lang='en-US'
|
||||
|
||||
dict_i18n=['en-US', 'es-ES']
|
||||
|
||||
l={}
|
||||
|
||||
#@staticmethod
|
||||
#def set_lang(code_lang):
|
||||
# if default_lang
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_default_lang():
|
||||
|
||||
lang=I18n.default_lang
|
||||
|
||||
s=get_session()
|
||||
|
||||
lang=s.get('lang', lang)
|
||||
|
||||
return lang
|
||||
|
||||
@staticmethod
|
||||
def lang(module, symbol, text_default, lang=None):
|
||||
|
||||
if not lang:
|
||||
lang=I18n.get_default_lang()
|
||||
|
||||
I18n.l[lang]=I18n.l.get(lang, {})
|
||||
|
||||
I18n.l[lang][module]=I18n.l[lang].get(module, {})
|
||||
|
||||
I18n.l[lang][module][symbol]=I18n.l[lang][module].get(symbol, text_default)
|
||||
|
||||
return I18n.l[lang][module][symbol]
|
||||
|
||||
@staticmethod
|
||||
def extract_value(value):
|
||||
|
||||
value=json.loads(value)
|
||||
|
||||
lang=I18n.get_default_lang()
|
||||
|
||||
if value[lang]!='':
|
||||
|
||||
return value[lang]
|
||||
|
||||
return value[I18n.default_lang]
|
||||
|
||||
@staticmethod
|
||||
def get_browser_lang():
|
||||
|
||||
return request.headers.get('Accept-Language', 'en-US')
|
||||
|
||||
@staticmethod
|
||||
def lang_json(module, symbol, text_default):
|
||||
|
||||
arr_final={}
|
||||
|
||||
for l in I18n.dict_i18n:
|
||||
arr_final[l]=I18n.lang(module, symbol, text_default, l)
|
||||
|
||||
return json.dumps(arr_final)
|
||||
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from hashlib import sha512, sha256
|
||||
from base64 import b64encode
|
||||
from os import urandom
|
||||
|
||||
# Functions for create random strings usando urandom
|
||||
|
||||
def create_key_encrypt(n=10):
|
||||
|
||||
return sha512(urandom(n)).hexdigest()
|
||||
|
||||
def create_key_encrypt_256(n=10):
|
||||
|
||||
return sha256(urandom(n)).hexdigest()
|
||||
|
||||
def create_key(n=10):
|
||||
|
||||
rand_bytes=urandom(n)
|
||||
|
||||
return b64encode(rand_bytes).decode('utf-8')[0:-2]
|
||||
|
|
@ -7,8 +7,8 @@ import getpass
|
|||
import re
|
||||
from pathlib import Path
|
||||
from base64 import b64encode
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.modules.admin.models.admin import UserAdmin
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.modules.admin2.models.admin import UserAdmin2, LoginTries2
|
||||
from subprocess import call
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ def start():
|
|||
|
||||
print('Error: cannot create the directory. Check if exists and if you have permissions')
|
||||
exit()
|
||||
# Create folder settings and copy index.py, admin.py
|
||||
# Create folder settings and copy app.py, admin.py
|
||||
|
||||
path_settings=args.path+'/settings'
|
||||
|
||||
|
|
@ -74,11 +74,11 @@ def start():
|
|||
|
||||
try:
|
||||
|
||||
shutil.copy(workdir+'/frontend/index.py', args.path+'/index.py')
|
||||
shutil.copy(workdir+'/frontend/app.py', args.path+'/app.py')
|
||||
|
||||
except:
|
||||
|
||||
print('Error: cannot copy the file index.py. Check if exists and if you have permissions for this task')
|
||||
print('Error: cannot copy the file app.py. Check if exists and if you have permissions for this task')
|
||||
|
||||
try:
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ def start():
|
|||
|
||||
print('Error: cannot copy the file create_module.py. Check if exists and if you have permissions for this task')
|
||||
|
||||
|
||||
"""
|
||||
try:
|
||||
|
||||
shutil.copy(workdir+'/settings/modules.py', path_settings+'/modules.py')
|
||||
|
|
@ -120,6 +120,7 @@ def start():
|
|||
except:
|
||||
|
||||
print('Error: cannot copy the file modules.py. Check if exists and if you have permissions for this task')
|
||||
"""
|
||||
|
||||
if args.symlink==True:
|
||||
try:
|
||||
|
|
@ -221,7 +222,9 @@ def start():
|
|||
|
||||
conn=WebModel.connection()
|
||||
|
||||
useradmin=UserAdmin(conn)
|
||||
useradmin=UserAdmin2(conn)
|
||||
|
||||
logintries=LoginTries2(conn)
|
||||
|
||||
# Check if db exists
|
||||
|
||||
|
|
@ -248,13 +251,15 @@ def start():
|
|||
|
||||
try:
|
||||
|
||||
shutil.copy(workdir+'/settings/modules.py.admin', path_settings+'/modules.py')
|
||||
#shutil.copy(workdir+'/settings/modules.py.admin', path_settings+'/modules.py')
|
||||
|
||||
shutil.copy(workdir+'/settings/config_admin.py.sample', path_settings+'/config_admin.py')
|
||||
#shutil.copy(workdir+'/settings/config_admin.py.sample', path_settings+'/config_admin.py')
|
||||
|
||||
sql=useradmin.create_table()
|
||||
|
||||
if not useradmin.query(sql):
|
||||
tries_sql=logintries.create_table()
|
||||
|
||||
if not useradmin.query(sql) or not useradmin.query(tries_sql) :
|
||||
print('Error: cannot create table admin, you can create this table with padmin.py')
|
||||
else:
|
||||
|
||||
|
|
@ -265,7 +270,8 @@ def start():
|
|||
|
||||
f.close()
|
||||
|
||||
config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin', 'paramecio.modules.lang']")
|
||||
#config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.lang']")
|
||||
config_text=config_text.replace("apps={'welcome': ['paramecio.modules.welcome', 'welcome_app', '/'], 'lang': ['paramecio.modules.lang', '', '']}", "apps={'welcome': ['paramecio.modules.welcome', 'welcome_app', '/'], 'lang': ['paramecio.modules.lang', '', ''], 'admin2': ['paramecio.modules.admin2', 'admin_app', '/admin/']}")
|
||||
|
||||
with open(path_settings+'/config.py', 'w') as f:
|
||||
|
||||
|
|
@ -360,7 +366,7 @@ def start():
|
|||
os.chdir(args.path)
|
||||
|
||||
#Regenerating modules.py
|
||||
|
||||
"""
|
||||
regenerate='regenerate.py'
|
||||
|
||||
os.chmod(regenerate, 0o755)
|
||||
|
|
@ -370,7 +376,7 @@ def start():
|
|||
exit(1)
|
||||
else:
|
||||
print('Regeneration of modules.py finished')
|
||||
|
||||
"""
|
||||
# Installing models
|
||||
|
||||
padmin='padmin.py'
|
||||
|
|
@ -385,7 +391,7 @@ def start():
|
|||
|
||||
models_files=os.listdir(models_path)
|
||||
|
||||
m=re.compile(".*\.py$")
|
||||
m=re.compile(r".*\.py$")
|
||||
|
||||
underscore=re.compile("^__.*")
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,11 @@ import getpass
|
|||
from pathlib import Path
|
||||
from settings import config
|
||||
from importlib import import_module
|
||||
import re
|
||||
|
||||
def start():
|
||||
"""Module for create new modules for paramecio
|
||||
"""
|
||||
|
||||
parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio')
|
||||
|
||||
|
|
@ -37,17 +40,68 @@ def start():
|
|||
|
||||
#f=open('modules/'+args.path+'/index.py', 'w')
|
||||
|
||||
name_module=os.path.basename(args.path)
|
||||
|
||||
try:
|
||||
shutil.copy(workdir+'/examples/index.py', 'modules/'+args.path)
|
||||
shutil.copy(workdir+'/examples/app.py', 'modules/'+args.path)
|
||||
|
||||
with open('modules/'+args.path+'/app.py') as f:
|
||||
|
||||
app_file=f.read()
|
||||
|
||||
app_file=app_file.replace('/example', '/'+name_module)
|
||||
|
||||
with open('modules/'+args.path+'/app.py', 'w') as f:
|
||||
|
||||
f.write(app_file)
|
||||
|
||||
pass
|
||||
|
||||
except:
|
||||
|
||||
print('Error: cannot copy controller example. Check if you have permissions')
|
||||
exit(1)
|
||||
|
||||
# Edit config.py
|
||||
|
||||
#module_final='modules.'+name_module
|
||||
module_final=f"'{name_module}': ['modules.{name_module}', '', '']"
|
||||
|
||||
try:
|
||||
|
||||
with open('./settings/config.py') as f:
|
||||
|
||||
#modules_final='\''+'\', \''.join(final_modules)+'\''
|
||||
|
||||
p=re.compile(r"^apps=\{(.*)\}$")
|
||||
|
||||
#config_file=p.sub(r"modules=[\1, "+modules_final+"]", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin', 'paramecio.modules.lang', 'modules.pastafari', 'modules.monit', 'modules.example']")
|
||||
|
||||
final_config=''
|
||||
|
||||
for line in f:
|
||||
if p.match(line):
|
||||
line=p.sub(r"apps={\1, "+module_final+"}", line)
|
||||
final_config+=line
|
||||
|
||||
with open('./settings/config.py', 'w') as f:
|
||||
|
||||
f.write(final_config)
|
||||
|
||||
print('Updated configuration for add new modules...')
|
||||
|
||||
|
||||
except:
|
||||
|
||||
print('Cannot update configuration, you need add the new module by hand')
|
||||
|
||||
# Reload config
|
||||
|
||||
#config.modules.append(module_final)
|
||||
|
||||
# Regenerate modules
|
||||
|
||||
regenerate_modules_config()
|
||||
#regenerate_modules_config()
|
||||
|
||||
def regenerate_modules_config():
|
||||
|
||||
|
|
@ -76,7 +130,7 @@ def regenerate_modules_config():
|
|||
|
||||
for controller in dir_controllers:
|
||||
|
||||
if controller.find('.py')!=-1 and controller.find('__init__')==-1:
|
||||
if controller.find('.py')!=-1 and controller.find('_')==-1:
|
||||
|
||||
controller_py=controller.replace('.py', '')
|
||||
|
||||
|
|
@ -99,11 +153,8 @@ def regenerate_modules_config():
|
|||
print("-"*60)
|
||||
exit(1)
|
||||
|
||||
f=open('./settings/modules.py', 'w')
|
||||
|
||||
f.write("".join(modules))
|
||||
|
||||
f.close()
|
||||
with open('./settings/modules.py', 'w') as f:
|
||||
f.write("".join(modules))
|
||||
|
||||
if __name__=="__main__":
|
||||
start()
|
||||
|
|
|
|||
|
|
@ -1,262 +0,0 @@
|
|||
from paramecio.cromosoma.webmodel import PhangoField
|
||||
from paramecio.cromosoma import coreforms
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
|
||||
class IntegerField(PhangoField):
|
||||
|
||||
"""Class that figure an integer sql type field.
|
||||
|
||||
Args:
|
||||
name (str): The name of new field
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
required (bool): Boolean for define if
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super(IntegerField, self).__init__(name, size, required)
|
||||
self.default_value=0
|
||||
|
||||
def check(self, value):
|
||||
|
||||
"""Method for check if value is integer
|
||||
|
||||
Args:
|
||||
value (int): The value to check
|
||||
|
||||
"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=str(int(value))
|
||||
|
||||
if value=="0" and self.required==True:
|
||||
self.txt_error="The value is zero"
|
||||
self.error=True
|
||||
except:
|
||||
|
||||
value="0"
|
||||
self.txt_error="The value is zero"
|
||||
self.error=True
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'INT('+str(self.size)+') NOT NULL DEFAULT "0"'
|
||||
|
||||
class BigIntegerField(IntegerField):
|
||||
|
||||
"""Class that figure an big integer sql type field.
|
||||
|
||||
Only change the sql type with respect to IntegerField
|
||||
|
||||
"""
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'BIGINT('+str(self.size)+') NOT NULL DEFAULT "0"'
|
||||
|
||||
|
||||
class FloatField(PhangoField):
|
||||
|
||||
"""Class that figure an float sql type field.
|
||||
|
||||
Args:
|
||||
name (str): The name of new field
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
required (bool): Boolean for define if
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super(FloatField, self).__init__(name, size, required)
|
||||
|
||||
self.error_default="The value is zero"
|
||||
self.default_value=0
|
||||
|
||||
def check(self, value):
|
||||
|
||||
"""Method for check if value is integer
|
||||
|
||||
Args:
|
||||
value (float): The value to check
|
||||
|
||||
"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=str(value)
|
||||
|
||||
if value.find(',')!=-1:
|
||||
value=value.replace(',', '.')
|
||||
|
||||
value=str(float(value))
|
||||
|
||||
if value==0 and self.required==True:
|
||||
self.txt_error=self.error_default
|
||||
self.error=True
|
||||
except:
|
||||
|
||||
value="0"
|
||||
self.txt_error=self.error_default
|
||||
self.error=True
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'FLOAT NOT NULL DEFAULT "0"'
|
||||
|
||||
class DecimalField(FloatField):
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"'
|
||||
|
||||
class DoubleField(FloatField):
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'DOUBLE NOT NULL DEFAULT "0"'
|
||||
|
||||
class CharField(PhangoField):
|
||||
|
||||
pass
|
||||
|
||||
class TextField(PhangoField):
|
||||
|
||||
def __init__(self, name, required=False):
|
||||
super().__init__(name, 11, required)
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'TEXT '+self.set_default
|
||||
|
||||
class HTMLField(TextField):
|
||||
|
||||
def __init__(self, name, required=False):
|
||||
super().__init__(name, required)
|
||||
|
||||
def check(self, value):
|
||||
|
||||
return re.sub('<.*?script?>', '', value)
|
||||
|
||||
|
||||
class ForeignKeyField(IntegerField):
|
||||
|
||||
def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
|
||||
|
||||
super(ForeignKeyField, self).__init__(name, size, required)
|
||||
|
||||
self.table_id=related_table.name_field_id
|
||||
|
||||
self.table_name=related_table.name
|
||||
|
||||
self.related_model=related_table
|
||||
|
||||
self.identifier_field=identifier_field
|
||||
|
||||
self.named_field=named_field
|
||||
|
||||
self.select_fields=select_fields
|
||||
|
||||
self.foreignkey=True
|
||||
|
||||
self.change_form(coreforms.SelectModelForm, [related_table, self.named_field, self.identifier_field])
|
||||
|
||||
self.default_value=None
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=super().check(value)
|
||||
|
||||
if value=='0' or value==0:
|
||||
value='NULL'
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'INT NULL'
|
||||
|
||||
|
||||
class BooleanField(IntegerField):
|
||||
|
||||
def __init__(self, name, size=1):
|
||||
|
||||
required=False
|
||||
|
||||
self.yes_text=I18n.lang('common', 'yes', 'Yes')
|
||||
self.no_text=I18n.lang('common', 'no', 'No')
|
||||
|
||||
super(IntegerField, self).__init__(name, size, required)
|
||||
|
||||
self.default_error="Need 0 or 1 value"
|
||||
self.default_value=0
|
||||
|
||||
def check(self, value):
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=int(value)
|
||||
|
||||
if value<0 or value>1:
|
||||
self.txt_error=self.default_error
|
||||
self.error=True
|
||||
|
||||
except:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.default_error
|
||||
value=0
|
||||
|
||||
value=str(value)
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'BOOLEAN NOT NULL DEFAULT "0"'
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
value=int(value)
|
||||
|
||||
if value==0:
|
||||
value=self.no_text
|
||||
else:
|
||||
value=self.yes_text
|
||||
|
||||
return str(value)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
from paramecio.cromosoma.webmodel import PhangoField,WebModel
|
||||
import json
|
||||
|
||||
class ArrayField(PhangoField):
|
||||
|
||||
def __init__(self, name, field_type, required=False):
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.field_type=field_type
|
||||
|
||||
self.error_default='Sorry, the json array is invalid'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if type(value).__name__=='str':
|
||||
try:
|
||||
value=json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
|
||||
value=[]
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
elif type(value).__name__!='list':
|
||||
|
||||
value=[]
|
||||
self.error=True
|
||||
self.txt_error='Sorry, the json array is invalid'
|
||||
|
||||
if type(self.field_type).__name__!='ArrayField':
|
||||
for k,v in enumerate(value):
|
||||
|
||||
value[k]=self.field_type.check(v)
|
||||
|
||||
final_value=json.dumps(value)
|
||||
|
||||
final_value=WebModel.escape_sql(final_value)
|
||||
|
||||
return final_value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'TEXT '+self.set_default
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return ", ".join(value)
|
||||
|
||||
def loads(self, value):
|
||||
|
||||
try:
|
||||
|
||||
return json.loads(value)
|
||||
except:
|
||||
|
||||
return False
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
from paramecio.cromosoma.corefields import IntegerField
|
||||
from paramecio.cromosoma.extraforms.colorform import ColorForm
|
||||
|
||||
class ColorField(IntegerField):
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=ColorForm
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=str(value).replace('#', '0x')
|
||||
|
||||
value=int(value, 16)
|
||||
|
||||
if value<0 or value>0xffffff:
|
||||
value=0
|
||||
|
||||
return value
|
||||
def get_hex_color(self, value):
|
||||
|
||||
value=str(hex(int(value))).replace('0x', '')
|
||||
|
||||
c=len(value)
|
||||
|
||||
if(c<6):
|
||||
repeat=6-c
|
||||
value=('0'*repeat)+value
|
||||
|
||||
value='#'+value
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
value=str(hex(int(value))).replace('0x', '')
|
||||
|
||||
c=len(value)
|
||||
|
||||
if(c<6):
|
||||
repeat=6-c
|
||||
value=('0'*repeat)+value
|
||||
|
||||
value='#'+value
|
||||
|
||||
return '<div style="width:50px;height:50px;background-color:%s;"></div>' % value;
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
from paramecio.cromosoma.corefields import PhangoField
|
||||
from paramecio.citoplasma import datetime
|
||||
from paramecio.cromosoma.extraforms.dateform import DateForm
|
||||
|
||||
class DateField(PhangoField):
|
||||
|
||||
def __init__(self, name, size=255, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=DateForm
|
||||
|
||||
self.utc=True
|
||||
|
||||
self.error_default='Error: Date format invalid'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if self.utc:
|
||||
|
||||
value=datetime.local_to_gmt(value)
|
||||
|
||||
elif not datetime.obtain_timestamp(value):
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return ''
|
||||
|
||||
if value==False:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return ''
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return datetime.format_date(value)
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
from paramecio.cromosoma.corefields import PhangoField
|
||||
from paramecio.citoplasma import datetime
|
||||
from paramecio.cromosoma.extraforms.dateform import DateForm
|
||||
|
||||
class DateTimeField(PhangoField):
|
||||
|
||||
def __init__(self, name, size=255, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=DateForm
|
||||
|
||||
self.utc=False
|
||||
|
||||
self.error_default='Error: Date format invalid'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if self.utc:
|
||||
|
||||
value=datetime.local_to_gmt(value)
|
||||
|
||||
elif not datetime.obtain_timestamp(value):
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return None
|
||||
|
||||
if value==False:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return None
|
||||
else:
|
||||
|
||||
"""
|
||||
format_date_txt="YYYY/MM/DD"
|
||||
|
||||
format_time_txt="HH:mm:ss"
|
||||
"""
|
||||
|
||||
value=datetime.format_local_strtime('YYYY-MM-DD HH:mm:ss', value)
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
# Convert to paramecio value
|
||||
if value!=None:
|
||||
value=str(value)
|
||||
value=value.replace('-', '').replace(':', '').replace(' ', '')
|
||||
|
||||
return datetime.format_date(value)
|
||||
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'DATETIME NULL'
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
from paramecio.cromosoma.webmodel import WebModel, PhangoField
|
||||
|
||||
try:
|
||||
import ujson as json
|
||||
except:
|
||||
import json
|
||||
|
||||
class DictField(PhangoField):
|
||||
|
||||
def __init__(self, name, field_type, required=False):
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.field_type=field_type
|
||||
|
||||
self.error_default='Sorry, the json dict is invalid'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if type(value).__name__=='str':
|
||||
try:
|
||||
value=json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
elif type(value).__name__!='dict':
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
for k,v in value.items():
|
||||
|
||||
value[k]=self.field_type.check(v)
|
||||
|
||||
final_value=json.dumps(value)
|
||||
|
||||
#final_value=WebModel.escape_sql(final_value)
|
||||
|
||||
return final_value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'TEXT '+self.set_default
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return ", ".join(value)
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
from paramecio.cromosoma.corefields import CharField
|
||||
import re
|
||||
|
||||
mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
||||
|
||||
class EmailField(CharField):
|
||||
|
||||
def __init__(self, name, size=1024, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.error_default='Error: No valid format'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=super().check(value)
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
if not mail_pattern.match(value):
|
||||
|
||||
self.error=True
|
||||
value=""
|
||||
self.txt_error=self.error_default
|
||||
|
||||
return value
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.mtemplates import standard_t
|
||||
|
||||
class ColorForm(BaseForm):
|
||||
|
||||
def __init__(self, name, value):
|
||||
|
||||
super().__init__(name, value)
|
||||
|
||||
self.t=standard_t
|
||||
|
||||
def form(self):
|
||||
|
||||
|
||||
return self.t.load_template('forms/colorform.phtml', name_form=self.name_field_id, default_value=self.default_value, form=self)
|
||||
14
paramecio/examples/app.py
Normal file
14
paramecio/examples/app.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from paramecio.libraries.mtemplates import env_theme, PTemplate
|
||||
from paramecio.libraries.urls import make_url
|
||||
from bottle import request
|
||||
from settings import config
|
||||
from paramecio.wsgiapp import app
|
||||
|
||||
env=env_theme(__file__)
|
||||
t=PTemplate(env)
|
||||
|
||||
@app.route('/example')
|
||||
def home():
|
||||
|
||||
return "Hello World!!"
|
||||
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
from paramecio.citoplasma.mtemplates import env_theme, PTemplate
|
||||
from paramecio.citoplasma.urls import make_url
|
||||
from bottle import route, request
|
||||
from settings import config
|
||||
|
||||
env=env_theme(__file__)
|
||||
|
||||
@route('/example')
|
||||
def home():
|
||||
|
||||
t=PTemplate(env)
|
||||
|
||||
return "Hello World!!"
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.wsgiapp import app
|
||||
from paramecio.index import run_app
|
||||
from paramecio.app import run_app
|
||||
|
||||
application=app
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.check_i18n import start
|
||||
from paramecio.libraries.check_i18n import start
|
||||
|
||||
start()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.dbadmin import start
|
||||
from paramecio.libraries.db.dbadmin import start
|
||||
|
||||
start()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.i18n import I18n
|
||||
|
||||
I18n.l['en-US']=I18n.l.get('en-US', {})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.i18n import I18n
|
||||
|
||||
I18n.l['en-US']=I18n.l.get('en-US', {})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from collections import OrderedDict
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.urls import make_url
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
from paramecio.cromosoma.formsutils import generate_csrf
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.urls import make_url
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
from paramecio.libraries.db.formsutils import generate_csrf
|
||||
from bottle import response,request
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from time import time
|
||||
|
||||
try:
|
||||
|
|
@ -98,7 +98,8 @@ def get_menu(modules_admin):
|
|||
|
||||
if len(menu[mod[2]])<4:
|
||||
menu[mod[2]].append('<i class="fa fa-circle-o" aria-hidden="true"></i>')
|
||||
|
||||
else:
|
||||
menu[mod[2]][3]='<i class="fa {}" aria-hidden="true"></i>'.format(menu[mod[2]][3])
|
||||
else:
|
||||
|
||||
menu[mod[2]]=mod[0]
|
||||
|
|
@ -114,6 +115,8 @@ def get_menu(modules_admin):
|
|||
|
||||
if len(menu[submod[2]])<4:
|
||||
menu[submod[2]].append('<i class="fa fa-circle-o" aria-hidden="true"></i>')
|
||||
else:
|
||||
menu[submod[2]][3]='<i class="fa {}" aria-hidden="true"></i>'.format(menu[submod[2]][3])
|
||||
|
||||
return menu
|
||||
|
||||
|
|
@ -158,7 +161,7 @@ def login_model(ModelLogin, session='', enable_tries=False):
|
|||
|
||||
user_admin.conditions=['WHERE username=%s', [username]]
|
||||
|
||||
arr_user=user_admin.select_a_row_where(['id', 'username', 'password', 'privileges', 'lang', 'num_tries', 'email'])
|
||||
arr_user=user_admin.select_a_row_where(['id', 'username', 'password', 'privileges', 'lang', 'num_tries', 'email', 'theme'])
|
||||
|
||||
if arr_user==False:
|
||||
|
||||
|
|
@ -180,6 +183,7 @@ def login_model(ModelLogin, session='', enable_tries=False):
|
|||
s[session+'lang']=arr_user['lang']
|
||||
s[session+'email']=arr_user['email']
|
||||
s[session+'username']=arr_user['username']
|
||||
s[session+'theme']=str(arr_user['theme'])
|
||||
|
||||
if s['lang']=='':
|
||||
s['lang']=I18n.default_lang
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.mtemplates import PTemplate
|
||||
from paramecio.citoplasma.adminutils import check_login, get_language, get_menu
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.mtemplates import PTemplate
|
||||
from paramecio.libraries.adminutils import check_login, get_language, get_menu
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.i18n import I18n
|
||||
|
||||
try:
|
||||
|
||||
|
|
@ -5,7 +5,7 @@ import os
|
|||
import re
|
||||
from pathlib import Path
|
||||
from importlib import import_module
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from settings import config
|
||||
|
||||
pattern=re.compile('^\w+\.(py|html|phtml|js)$')
|
||||
|
|
@ -48,6 +48,8 @@ def start():
|
|||
#lang_t=re.compile("\${lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)\}")
|
||||
lang_t=re.compile("lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)")
|
||||
|
||||
lang_s=re.compile("slang\('(.*?)',\s+'(.*?)'\)"
|
||||
|
||||
if not os.path.isdir(path):
|
||||
|
||||
print("Error: directory to scan doesn't exists")
|
||||
|
|
@ -91,7 +93,7 @@ def start():
|
|||
|
||||
file_lang="#!/usr/bin/env python3\n\n"
|
||||
|
||||
file_lang+="from paramecio.citoplasma.i18n import I18n\n\n"
|
||||
file_lang+="from paramecio.libraries.i18n import I18n\n\n"
|
||||
|
||||
for lang in I18n.dict_i18n:
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ try:
|
|||
from settings import config
|
||||
except:
|
||||
config={}
|
||||
#from paramecio.citoplasma.sessions import get_session
|
||||
#from paramecio.libraries.sessions import get_session
|
||||
from os import environ
|
||||
|
||||
"""Simple hook for timedate functions from Arrow datetime module. Maybe in the future use native python datetime functions or other libraries. Is simply an abstraction for not depend of particular library.
|
||||
426
paramecio/libraries/db/corefields.py
Normal file
426
paramecio/libraries/db/corefields.py
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.webmodel import PhangoField
|
||||
from paramecio.libraries.db import coreforms
|
||||
from paramecio.libraries.i18n import I18n
|
||||
#from bs4 import BeautifulSoup
|
||||
import bleach
|
||||
|
||||
class IntegerField(PhangoField):
|
||||
|
||||
"""Class that figure an integer sql type field.
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of field
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
required (bool): Boolean for define if field is required or not
|
||||
"""
|
||||
|
||||
super(IntegerField, self).__init__(name, size, required)
|
||||
self.default_value=0
|
||||
self.jtype='integer'
|
||||
self.jformat='int64'
|
||||
self.jexample='12345'
|
||||
|
||||
self.type_sql='int({})'.format(self.size)
|
||||
|
||||
def check(self, value):
|
||||
|
||||
"""Method for check if value is integer
|
||||
|
||||
Args:
|
||||
value (int): The value to check
|
||||
|
||||
"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=str(int(value))
|
||||
|
||||
if value=="0" and self.required==True:
|
||||
self.txt_error="The value is zero"
|
||||
self.error=True
|
||||
except:
|
||||
|
||||
value="0"
|
||||
self.txt_error="The value is zero"
|
||||
self.error=True
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'INT('+str(self.size)+') NOT NULL DEFAULT "0"'
|
||||
|
||||
class BigIntegerField(IntegerField):
|
||||
|
||||
"""Class that figure an big integer sql type field.
|
||||
|
||||
Only change the sql type with respect to 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
|
||||
|
||||
"""
|
||||
|
||||
return 'BIGINT('+str(self.size)+') NOT NULL DEFAULT "0"'
|
||||
|
||||
|
||||
class FloatField(PhangoField):
|
||||
|
||||
"""Class that figure an float sql type field.
|
||||
|
||||
Args:
|
||||
name (str): The name of new field
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
required (bool): Boolean for define if
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super(FloatField, self).__init__(name, size, required)
|
||||
|
||||
self.error_default="The value is zero"
|
||||
self.default_value=0
|
||||
self.type_sql='float'.format(self.size)
|
||||
|
||||
self.jtype='float'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
"""Method for check if value is integer
|
||||
|
||||
Args:
|
||||
value (float): The value to check
|
||||
|
||||
"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=str(value)
|
||||
|
||||
if value.find(',')!=-1:
|
||||
value=value.replace(',', '.')
|
||||
|
||||
value=str(float(value))
|
||||
|
||||
if value==0 and self.required==True:
|
||||
self.txt_error=self.error_default
|
||||
self.error=True
|
||||
except:
|
||||
|
||||
value="0"
|
||||
self.txt_error=self.error_default
|
||||
self.error=True
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'FLOAT NOT NULL DEFAULT "0"'
|
||||
|
||||
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"'
|
||||
|
||||
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='float'
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'DOUBLE NOT NULL DEFAULT "0"'
|
||||
|
||||
class CharField(PhangoField):
|
||||
"""Simple alias for PhangoField"""
|
||||
|
||||
pass
|
||||
|
||||
class TextField(PhangoField):
|
||||
"""Class used for text fields
|
||||
|
||||
Class used for text fields, use TEXT 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, 11, required)
|
||||
|
||||
self.type_sql='text'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'TEXT '+self.set_default
|
||||
|
||||
class LongTextField(TextField):
|
||||
"""Class used for long text fields (32 bits size, 4G)
|
||||
|
||||
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
|
||||
|
||||
"""
|
||||
|
||||
return 'LONGTEXT '+self.set_default
|
||||
|
||||
class HTMLField(TextField):
|
||||
"""Class used for html fields
|
||||
|
||||
Class used for html fields, use TEXT sql type for the this field because is children of TextField. In this method self.escape is used for convert " to "
|
||||
"""
|
||||
|
||||
def __init__(self, name, required=False):
|
||||
"""Init HTMLField 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:
|
||||
trusted_tags (list): A list with safe tags.
|
||||
"""
|
||||
|
||||
super().__init__(name, required)
|
||||
self.trusted_tags=[]
|
||||
|
||||
def check(self, value):
|
||||
"""Check method for html values
|
||||
|
||||
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
|
||||
|
||||
value=soup.renderContents().decode('utf-8')
|
||||
|
||||
if self.escape:
|
||||
|
||||
return value.replace('"', '"')
|
||||
else:
|
||||
|
||||
return value
|
||||
|
||||
"""
|
||||
|
||||
value=bleach.clean('<p>"trial"</p><script></script>', tags=self.trusted_tags)
|
||||
|
||||
if self.escape:
|
||||
|
||||
return value.replace('"', '"')
|
||||
else:
|
||||
|
||||
return value
|
||||
|
||||
|
||||
class ForeignKeyField(IntegerField):
|
||||
"""Subclass of IntegerField for create Foreign keys
|
||||
|
||||
A subclass of IntegerField used for create foreign keys in related tables.
|
||||
"""
|
||||
|
||||
def __init__(self, name, related_table, size=11, required=False, identifier_field='id', named_field="id", select_fields=[]):
|
||||
"""
|
||||
Args:
|
||||
name (str): Name of field
|
||||
related_table (WebModel): The table-model related with this foreign key
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
required (bool): Boolean for define if field is required or not
|
||||
identifier_field (str): The Id field name from related table
|
||||
named_field (str): The field from related table used for identify the row seleted from related table
|
||||
select_fields (list): A series of fields names from related
|
||||
"""
|
||||
|
||||
super(ForeignKeyField, self).__init__(name, size, required)
|
||||
|
||||
self.table_id=related_table.name_field_id
|
||||
|
||||
self.table_name=related_table.name
|
||||
|
||||
self.related_model=related_table
|
||||
|
||||
self.identifier_field=identifier_field
|
||||
|
||||
self.named_field=named_field
|
||||
|
||||
self.select_fields=select_fields
|
||||
|
||||
self.foreignkey=True
|
||||
|
||||
self.change_form(coreforms.SelectModelForm, [related_table, self.named_field, self.identifier_field])
|
||||
|
||||
self.default_value=None
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=super().check(value)
|
||||
|
||||
if value=='0' or value==0:
|
||||
value=None
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'INT NULL'
|
||||
|
||||
|
||||
class BooleanField(IntegerField):
|
||||
"""Field for boolean values
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=1):
|
||||
"""
|
||||
Args:
|
||||
name (str): Name of field
|
||||
size (int): The size of the new field in database. By default 11.
|
||||
"""
|
||||
|
||||
required=False
|
||||
|
||||
self.yes_text=I18n.lang('common', 'yes', 'Yes')
|
||||
self.no_text=I18n.lang('common', 'no', 'No')
|
||||
|
||||
super(IntegerField, self).__init__(name, size, required)
|
||||
|
||||
self.default_error="Need 0 or 1 value"
|
||||
self.default_value=0
|
||||
|
||||
self.type_sql='tinyint(1)'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
try:
|
||||
|
||||
value=int(value)
|
||||
|
||||
if value<0 or value>1:
|
||||
self.txt_error=self.default_error
|
||||
self.error=True
|
||||
value=0
|
||||
|
||||
except:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.default_error
|
||||
value=0
|
||||
|
||||
value=str(value)
|
||||
|
||||
return value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'BOOLEAN NOT NULL DEFAULT "0"'
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
value=int(value)
|
||||
|
||||
if value==0:
|
||||
value=self.no_text
|
||||
else:
|
||||
value=self.yes_text
|
||||
|
||||
return str(value)
|
||||
|
|
@ -1,13 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
from html import escape
|
||||
|
||||
#Forms para python3
|
||||
|
||||
class BaseForm:
|
||||
"""The class used by all forms classes
|
||||
|
||||
BaseForm is the base class used for all form classes.
|
||||
|
||||
A form class is used for generate simple html forms, how input type, text type, hidden type, etc. PhangoField classes use this forms for generate automatically forms using GenerateAdminClass and others.
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
"""
|
||||
Args:
|
||||
name (str): The html name for this form
|
||||
value (str): The default value of this html form.
|
||||
|
||||
Attributes:
|
||||
label (str): Label used in functions how show_form that generate a complete html form from a form class list
|
||||
name (str): Name of the html form.
|
||||
default_value (mixed): The default value of the form. Equal to value in typical html form.
|
||||
css (str): Used for add css classes to the html form
|
||||
type (str): Variable used for conventional html forms with html tag <input>
|
||||
field (PhangoField): Field related with this form. Used in PhangoField.
|
||||
required (boolean): If form is required or not, used in functions that generate forms.
|
||||
name_field_id (str): The html id for the html form. Used for html things.
|
||||
help (str): A string with help text, used in functions that generate forms.
|
||||
"""
|
||||
|
||||
self.label=name
|
||||
self.name=name
|
||||
|
|
@ -17,26 +59,45 @@ class BaseForm:
|
|||
self.field=None
|
||||
self.required=False
|
||||
self.txt_error=''
|
||||
self.error=False
|
||||
self.name_field_id=self.name+'_form'
|
||||
self.help=''
|
||||
self.placeholder=''
|
||||
|
||||
def form(self):
|
||||
"""Method for returm the html code of the form
|
||||
"""
|
||||
|
||||
return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" />'
|
||||
return '<input type="'+self.type+'" class="'+self.css+'" name="'+self.name+'" id="'+self.name_field_id+'" value="'+self.setform(self.default_value)+'" placeholder="'+self.placeholder+'" />'
|
||||
|
||||
def show_formatted(self, value):
|
||||
"""Method for show the value of form formatted
|
||||
|
||||
Args:
|
||||
value (mixed): The value of field form
|
||||
"""
|
||||
|
||||
return value
|
||||
|
||||
#Method for escape value for html input. DON'T CHANGE IF YOU DON'T KNOWN WHAT ARE YOU DOING
|
||||
|
||||
def setform(self, value):
|
||||
"""A method for set the value in the form for escape and other things
|
||||
|
||||
Args:
|
||||
value (mixed): The value of field form for set
|
||||
"""
|
||||
|
||||
value=str(value)
|
||||
|
||||
return value.replace('"', '"').replace("'", ''')
|
||||
|
||||
def change_name(self, new_name):
|
||||
"""A method for change the default form html name of the field form
|
||||
|
||||
Args:
|
||||
new_name (str): The new name of the form. Always is finished with _form suffix
|
||||
"""
|
||||
|
||||
self.name=new_name
|
||||
|
||||
|
|
@ -45,6 +106,8 @@ class BaseForm:
|
|||
return ""
|
||||
|
||||
class SimpleTextForm(BaseForm):
|
||||
"""Form for simple text
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
super().__init__(name, value)
|
||||
|
|
@ -56,6 +119,8 @@ class SimpleTextForm(BaseForm):
|
|||
return super().form()+' '+self.after_text
|
||||
|
||||
class TextForm(BaseForm):
|
||||
"""Form for simple text form
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
super(TextForm, self).__init__(name, value)
|
||||
|
|
@ -65,6 +130,8 @@ class TextForm(BaseForm):
|
|||
return '<textarea class="'+self.css+'" name="'+self.name+'" id="'+self.name+'_form">'+self.setform(self.default_value)+'</textarea>'
|
||||
|
||||
class PasswordForm(BaseForm):
|
||||
"""Form for password forms
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, show_password=False):
|
||||
super(PasswordForm, self).__init__(name, value)
|
||||
|
|
@ -79,6 +146,8 @@ class PasswordForm(BaseForm):
|
|||
return value
|
||||
|
||||
class HiddenForm(BaseForm):
|
||||
"""Form for hidden forms
|
||||
"""
|
||||
|
||||
def __init__(self, name, value):
|
||||
super(HiddenForm, self).__init__(name, value)
|
||||
|
|
@ -86,8 +155,16 @@ class HiddenForm(BaseForm):
|
|||
|
||||
|
||||
class SelectForm(BaseForm):
|
||||
"""Form for select html form
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, elements=OrderedDict()):
|
||||
"""
|
||||
Args:
|
||||
name (str): The html name for this form
|
||||
value (str): The default value of this html form
|
||||
elements (OrderedDict): An ordered dict with the keys(the form value) and text label. Example, if you have a OrderedDict how {'0': 'Value selected'} in a html select form you have the next result: <select name="name"><option value="0">Value selected</option></selected>
|
||||
"""
|
||||
super(SelectForm, self).__init__(name, value)
|
||||
self.arr_select=elements
|
||||
|
||||
|
|
@ -107,8 +184,19 @@ class SelectForm(BaseForm):
|
|||
return the_form
|
||||
|
||||
class SelectModelForm(SelectForm):
|
||||
"""Form for select html using a webmodel how base for get the data
|
||||
"""
|
||||
|
||||
def __init__(self, name, value, model, field_name, field_value, field_parent=None):
|
||||
"""
|
||||
Args:
|
||||
name (str): The html name for this form
|
||||
value (str): The default value of this html form.
|
||||
model (WebModel): The webmodel used for get the data for arr_select
|
||||
field_name (str): The field used for get the name of every option in select
|
||||
field_value (str): The field used for get the value of every option in select
|
||||
field_parent (int): If the model have parents or children, the value of this argument
|
||||
"""
|
||||
super(SelectModelForm, self).__init__(name, value)
|
||||
|
||||
try:
|
||||
|
|
@ -129,12 +217,16 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
|
||||
def normal_form(self):
|
||||
"""Method for prepare the form hierated from SelectForm class, without parents
|
||||
|
||||
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
|
||||
"""
|
||||
|
||||
self.arr_select['']=''
|
||||
|
||||
with self.model.select([self.field_name, self.field_value], True) as cur:
|
||||
for arr_value in cur:
|
||||
|
||||
#print(self.model.fields[self.field_name])
|
||||
self.arr_select[arr_value[self.field_value]]=self.model.fields[self.field_name].show_formatted(arr_value[self.field_name])
|
||||
|
||||
try:
|
||||
|
|
@ -147,6 +239,10 @@ class SelectModelForm(SelectForm):
|
|||
return super().form()
|
||||
|
||||
def parent_form(self):
|
||||
"""Method for prepare the form hierated from SelectForm class, with parents
|
||||
|
||||
Method for prepare the form hierated from SelectForm class getting data from database using model attribute.
|
||||
"""
|
||||
|
||||
self.arr_select['']=''
|
||||
|
||||
|
|
@ -168,7 +264,8 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
arr_son[arr_value[self.field_parent]]=[]
|
||||
|
||||
arr_son[arr_value[self.field_parent]].append([arr_value[self.field_value], arr_value[self.field_name]])
|
||||
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])])
|
||||
|
||||
self.create_son(0, arr_son)
|
||||
|
||||
|
|
@ -186,6 +283,8 @@ class SelectModelForm(SelectForm):
|
|||
|
||||
|
||||
def create_son(self, parent_id, arr_son, separator=''):
|
||||
"""Recursive method for generate parents and children dictionary
|
||||
"""
|
||||
|
||||
if parent_id in arr_son:
|
||||
for son in arr_son[parent_id]:
|
||||
|
|
@ -12,6 +12,14 @@ import traceback
|
|||
engine=None
|
||||
|
||||
class SqlClass:
|
||||
"""Class used how interface to sqlalchemy for connect to mysql engine
|
||||
|
||||
Attributes:
|
||||
cursors_connect (pymysql.cursors.DictCursor): Cursor dict connection to database
|
||||
disable_pool (boolean): If True then not exists mysql pool, if False, use sql pool for better connections.
|
||||
pymsql_install (boolean): If True, pymysql is used how mysqldb classic python module.
|
||||
pool_size (int): The size of the mysql pool.
|
||||
"""
|
||||
|
||||
cursors_connect=None
|
||||
disable_pool=False
|
||||
|
|
@ -19,8 +27,18 @@ class SqlClass:
|
|||
pool_size=15
|
||||
|
||||
def __init__(self, connection):
|
||||
"""
|
||||
Args:
|
||||
connection (dict): A dict with the configurations of SqlClass connection
|
||||
Attributes:
|
||||
error_connection (str): A string where errors are saved
|
||||
connection (dict): A dict with the configurations of SqlClass connection
|
||||
conn (MySQL Connection): A PyMySQL or Mysqldb connection
|
||||
connected (bool): Simple bool for check if was connected to mysql
|
||||
pool_recycle (int): Time limite for recycle the pool by inactivity
|
||||
"""
|
||||
|
||||
self.max_overflow=-1
|
||||
#self.max_overflow=-1
|
||||
self.error_connection=""
|
||||
# Data of connection
|
||||
self.connection=connection
|
||||
|
|
@ -28,11 +46,13 @@ class SqlClass:
|
|||
self.conn=None
|
||||
self.connected=False
|
||||
self.pool_recycle=3600
|
||||
self.connect()
|
||||
self.last_query=''
|
||||
self.connect()
|
||||
|
||||
|
||||
def connect(self):
|
||||
"""Method for connect to mysql db using pymysql or mysqldb
|
||||
"""
|
||||
|
||||
global engine
|
||||
|
||||
|
|
@ -45,7 +65,8 @@ class SqlClass:
|
|||
if self.connection['db_type']=='pymysql':
|
||||
|
||||
import pymysql.cursors
|
||||
|
||||
pymysql.install_as_MySQLdb
|
||||
SqlClass.pymysql_install=True
|
||||
SqlClass.cursors_connect=pymysql.cursors.DictCursor
|
||||
else:
|
||||
import MySQLdb.cursors
|
||||
|
|
@ -53,6 +74,9 @@ 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]
|
||||
|
|
@ -77,18 +101,22 @@ class SqlClass:
|
|||
pymysql.install_as_MySQLdb
|
||||
SqlClass.pymysql_install=True
|
||||
|
||||
self.conn=pymysql.connect(self.connection['host'],
|
||||
user=self.connection['user'],
|
||||
passwd=self.connection['password'],
|
||||
db=self.connection['db'],
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
"""
|
||||
connection = pymysql.connect(host='localhost',
|
||||
user='user',
|
||||
password='passwd',
|
||||
database='db',
|
||||
charset='utf8mb4',
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
"""
|
||||
|
||||
self.conn=pymysql.connect(host=self.connection['host'], user=self.connection['user'], passwd=self.connection['password'], db=self.connection['db'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
else:
|
||||
|
||||
import MySQLdb.cursors
|
||||
|
||||
self.conn=MySQLdb.connect(self.connection['host'],
|
||||
self.conn=MySQLdb.connect(host=self.connection['host'],
|
||||
user=self.connection['user'],
|
||||
passwd=self.connection['password'],
|
||||
db=self.connection['db'],
|
||||
|
|
@ -132,7 +160,13 @@ class SqlClass:
|
|||
#Make def query more simple if not debugging.
|
||||
|
||||
def query(self, sql_query, arguments=[], name_connection="default"):
|
||||
"""Method for send a sql query to mysql server
|
||||
|
||||
Args:
|
||||
sql_query (str): The sql sentence to execute. For data you should use %s character.
|
||||
arguments (list): The data used in sql sentence. This data substitute the %s characters.
|
||||
name_connection (str): The name of dict element with the configuration of connection, without used in this moment.
|
||||
"""
|
||||
|
||||
cursor=self.conn.cursor(SqlClass.cursors_connect)
|
||||
|
||||
|
|
@ -140,18 +174,20 @@ class SqlClass:
|
|||
|
||||
cursor.execute(sql_query, arguments)
|
||||
self.conn.commit()
|
||||
if hasattr(cursor, '_last_executed'):
|
||||
self.last_query=cursor._last_executed
|
||||
|
||||
#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, '_last_executed'):
|
||||
sql_query=cursor._last_executed
|
||||
#if hasattr(cursor, '_executed'):
|
||||
# self.last_query=cursor._executed
|
||||
|
||||
self.error_connection="Error in query ||%s||Values: %s" % (sql_query, str(arguments))
|
||||
self.error_connection="Error in query ||%s||Values: %s" % (self.last_query, str(arguments))
|
||||
|
||||
self.conn.close()
|
||||
|
||||
|
|
@ -196,12 +232,16 @@ class SqlClass:
|
|||
#return cursor.fetchone()
|
||||
|
||||
def __del__(self):
|
||||
"""Typical method used when class is deleted from memory. Close the connextion if exists.
|
||||
"""
|
||||
|
||||
if self.conn:
|
||||
|
||||
self.conn.close()
|
||||
|
||||
def close(self, name_connection="default"):
|
||||
"""Method used for close the connection if you want close connection and open other.
|
||||
"""
|
||||
|
||||
if self.conn:
|
||||
|
||||
|
|
@ -1,5 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
import argparse
|
||||
import os,traceback
|
||||
import sys, inspect
|
||||
|
|
@ -9,12 +29,19 @@ from datetime import date
|
|||
from pathlib import Path
|
||||
from colorama import init, Fore, Back, Style
|
||||
from importlib import import_module, reload
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from settings import config
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
sys.path.insert(0, os.path.realpath('.'))
|
||||
|
||||
#from models import books
|
||||
try:
|
||||
from settings import config
|
||||
except:
|
||||
#print('You need a settings directory with a paramecio2 configuration')
|
||||
#sys.exit(1)
|
||||
pass
|
||||
|
||||
def start():
|
||||
"""Function for create and update mysql tables using webmodel classes and fields how source.
|
||||
"""
|
||||
|
||||
connection=WebModel.connection()
|
||||
|
||||
|
|
@ -103,6 +130,32 @@ 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)
|
||||
|
||||
|
|
@ -125,23 +178,29 @@ def start():
|
|||
|
||||
print("--Adding indexes and constraints for the new table "+table)
|
||||
|
||||
for k_field, index in WebModel.arr_sql_index[table].items():
|
||||
print("---Added index to "+k_field)
|
||||
connection.query(index)
|
||||
if table in WebModel.arr_sql_index:
|
||||
|
||||
for k_set, index_set in WebModel.arr_sql_set_index[table].items():
|
||||
for k_field, index in WebModel.arr_sql_index[table].items():
|
||||
print("---Added index to "+k_field)
|
||||
connection.query(index)
|
||||
|
||||
if index_set!="":
|
||||
connection.query(index_set)
|
||||
print("---Added constraint to "+k_set)
|
||||
if table in WebModel.arr_sql_set_index:
|
||||
|
||||
for k_set, index_set in WebModel.arr_sql_set_index[table].items():
|
||||
|
||||
if index_set!="":
|
||||
connection.query(index_set)
|
||||
print("---Added constraint to "+k_set)
|
||||
|
||||
print("--Adding uniques elements for the new table")
|
||||
|
||||
for k_field, unique_set in WebModel.arr_sql_unique[table].items():
|
||||
if table in WebModel.arr_sql_unique:
|
||||
|
||||
if unique_set!="":
|
||||
connection.query(unique_set)
|
||||
print("---Added unique to "+unique_set)
|
||||
for k_field, unique_set in WebModel.arr_sql_unique[table].items():
|
||||
|
||||
if unique_set!="":
|
||||
connection.query(unique_set)
|
||||
print("---Added unique to "+unique_set)
|
||||
|
||||
#See if changes exists
|
||||
|
||||
|
|
@ -160,6 +219,23 @@ 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
|
||||
|
||||
|
|
@ -182,7 +258,8 @@ def start():
|
|||
|
||||
for f, v in WebModel.model[table].fields.items():
|
||||
|
||||
if not f in WebModel.model[old_table].fields:
|
||||
#if not f in WebModel.model[old_table].fields:
|
||||
if not f in table_fields[table]:
|
||||
|
||||
fields_to_add.append(f)
|
||||
|
||||
|
|
@ -226,13 +303,15 @@ def start():
|
|||
|
||||
#Add index
|
||||
|
||||
if v.indexed==True and v_old.indexed==False:
|
||||
#if v.indexed==True and v_old.indexed==False:
|
||||
if v.indexed==True and table_fields[table][f]['key']!='MUL':
|
||||
|
||||
fields_to_add_index.append(f)
|
||||
|
||||
changes+=1
|
||||
|
||||
if v.indexed==False and v_old.indexed==True:
|
||||
#if v.indexed==False and v_old.indexed==True:
|
||||
if v.indexed==False and table_fields[table][f]['key']=='MUL' and v.foreignkey==False:
|
||||
|
||||
fields_to_delete_index.append(f)
|
||||
|
||||
|
|
@ -240,13 +319,15 @@ def start():
|
|||
|
||||
#Add unique
|
||||
|
||||
if v.unique==True and v_old.unique==False:
|
||||
#if v.unique==True and v_old.unique==False:
|
||||
if v.unique==True and table_fields[table][f]['key']!='UNI':
|
||||
|
||||
fields_to_add_unique.append(f)
|
||||
|
||||
changes+=1
|
||||
|
||||
if v.unique==False and v_old.unique==True:
|
||||
#if v.unique==False and v_old.unique==True:
|
||||
if v.unique==False and table_fields[table][f]['key']=='UNI':
|
||||
|
||||
fields_to_delete_unique.append(f)
|
||||
|
||||
|
|
@ -254,29 +335,43 @@ def start():
|
|||
|
||||
#Add constraint
|
||||
|
||||
if v.foreignkey==True and v_old.foreignkey==False:
|
||||
#if v.foreignkey==True and v_old.foreignkey==False:
|
||||
if v.foreignkey==True and table_fields[table][f]['key']!='MUL':
|
||||
|
||||
fields_to_add_constraint.append(f)
|
||||
|
||||
changes+=1
|
||||
|
||||
if v.foreignkey==False and v_old.foreignkey==True:
|
||||
#if v.foreignkey==False and v_old.foreignkey==True:
|
||||
if v.foreignkey==False and table_fields[table][f]['key']=='MUL':
|
||||
|
||||
fields_to_delete_constraint.append(f)
|
||||
if table in foreignkey_fields:
|
||||
|
||||
changes+=1
|
||||
if f in foreignkey_fields[table]:
|
||||
|
||||
for f, v in WebModel.model[old_table].fields.items():
|
||||
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():
|
||||
|
||||
if not f in WebModel.model[table].fields:
|
||||
|
||||
#Add constraint
|
||||
|
||||
if v.foreignkey==True:
|
||||
#if v.foreignkey==True:
|
||||
|
||||
fields_to_delete_constraint.append(f)
|
||||
if table in foreignkey_fields:
|
||||
|
||||
changes+=1
|
||||
if f in foreignkey_fields[table]:
|
||||
|
||||
fields_to_delete_constraint.append(f)
|
||||
|
||||
changes+=1
|
||||
|
||||
fields_to_delete.append(f)
|
||||
|
||||
96
paramecio/libraries/db/extrafields/arrayfield.py
Normal file
96
paramecio/libraries/db/extrafields/arrayfield.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Flask, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.webmodel import PhangoField,WebModel
|
||||
import json
|
||||
|
||||
class ArrayField(PhangoField):
|
||||
"""Field for save json arrays with determined values"""
|
||||
|
||||
def __init__(self, name, field_type, required=False):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of new field
|
||||
field_type (PhangoField): The type of PhangoField for save in ArrayField
|
||||
required (bool): Boolean for define if field is required or not
|
||||
"""
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.field_type=field_type
|
||||
|
||||
self.error_default='Sorry, the json array is invalid'
|
||||
|
||||
self.set_default=''
|
||||
|
||||
self.type_sql='text'
|
||||
|
||||
self.jtype='array'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if type(value).__name__=='str':
|
||||
try:
|
||||
value=json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
|
||||
value=[]
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
elif type(value).__name__!='list':
|
||||
|
||||
value=[]
|
||||
self.error=True
|
||||
self.txt_error='Sorry, the json array is invalid'
|
||||
|
||||
error=0
|
||||
|
||||
if type(self.field_type).__name__!='ArrayField':
|
||||
for k,v in enumerate(value):
|
||||
|
||||
value[k]=self.field_type.check(v)
|
||||
if self.field_type.error:
|
||||
error+=1
|
||||
|
||||
if error>0:
|
||||
self.error=True
|
||||
|
||||
final_value=json.dumps(value)
|
||||
|
||||
final_value=WebModel.escape_sql(final_value)
|
||||
|
||||
return final_value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'JSON'
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return ", ".join(value)
|
||||
|
||||
def loads(self, value):
|
||||
|
||||
try:
|
||||
|
||||
return json.loads(value)
|
||||
except:
|
||||
|
||||
return False
|
||||
72
paramecio/libraries/db/extrafields/colorfield.py
Normal file
72
paramecio/libraries/db/extrafields/colorfield.py
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.corefields import IntegerField
|
||||
try:
|
||||
from paramecio.libraries.db.extraforms.colorform import ColorForm
|
||||
except:
|
||||
class ColorForm:
|
||||
pass
|
||||
|
||||
class ColorField(IntegerField):
|
||||
"""Simple field for save colors in hexadecimal format."""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=ColorForm
|
||||
self.jtype='string'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=str(value).replace('#', '0x')
|
||||
|
||||
value=int(value, 16)
|
||||
|
||||
if value<0 or value>0xffffff:
|
||||
value=0
|
||||
|
||||
return value
|
||||
def get_hex_color(self, value):
|
||||
|
||||
value=str(hex(int(value))).replace('0x', '')
|
||||
|
||||
c=len(value)
|
||||
|
||||
if(c<6):
|
||||
repeat=6-c
|
||||
value=('0'*repeat)+value
|
||||
|
||||
value='#'+value
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
value=str(hex(int(value))).replace('0x', '')
|
||||
|
||||
c=len(value)
|
||||
|
||||
if(c<6):
|
||||
repeat=6-c
|
||||
value=('0'*repeat)+value
|
||||
|
||||
value='#'+value
|
||||
|
||||
return '<div style="width:50px;height:50px;background-color:%s;"></div>' % value;
|
||||
67
paramecio/libraries/db/extrafields/datefield.py
Normal file
67
paramecio/libraries/db/extrafields/datefield.py
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.corefields import PhangoField
|
||||
from paramecio.libraries import datetime
|
||||
try:
|
||||
from paramecio.libraries.db.extraforms.dateform import DateForm
|
||||
except:
|
||||
|
||||
class DateForm:
|
||||
pass
|
||||
|
||||
class DateField(PhangoField):
|
||||
"""Field for use and save dates in YYYYMMDDHHSS format"""
|
||||
|
||||
def __init__(self, name, size=255, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=DateForm
|
||||
|
||||
self.utc=True
|
||||
|
||||
self.error_default='Error: Date format invalid'
|
||||
|
||||
self.jtype='string'
|
||||
self.jformat='date-time'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if self.utc:
|
||||
|
||||
value=datetime.local_to_gmt(value)
|
||||
|
||||
elif not datetime.obtain_timestamp(value):
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return ''
|
||||
|
||||
if value==False:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
return ''
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return datetime.format_date(value)
|
||||
96
paramecio/libraries/db/extrafields/datetimefield.py
Normal file
96
paramecio/libraries/db/extrafields/datetimefield.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.corefields import PhangoField
|
||||
from paramecio.libraries import datetime
|
||||
try:
|
||||
from paramecio.libraries.db.extraforms.dateform import DateForm
|
||||
except:
|
||||
class DateForm:
|
||||
pass
|
||||
|
||||
class DateTimeField(PhangoField):
|
||||
"""Field for use and save dates in MySQL date format"""
|
||||
|
||||
def __init__(self, name, size=255, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.name_form=DateForm
|
||||
|
||||
self.utc=False
|
||||
|
||||
self.error_default='Error: Date format invalid in %s' % self.name
|
||||
|
||||
self.type_sql='datetime'
|
||||
|
||||
self.default_value='0000-00-00 00:00:00'
|
||||
|
||||
self.jformat='date-time'
|
||||
self.jtype='string'
|
||||
self.jexample='2022-12-01 12:24:11'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if self.utc:
|
||||
|
||||
value=datetime.local_to_gmt(value)
|
||||
|
||||
elif not datetime.obtain_timestamp(value):
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default+' '+value
|
||||
|
||||
return '0000-00-00 00:00:00'
|
||||
|
||||
if value==False:
|
||||
|
||||
self.error=True
|
||||
self.txt_error=self.error_default+' '+value
|
||||
return '0000-00-00 00:00:00'
|
||||
else:
|
||||
|
||||
"""
|
||||
format_date_txt="YYYY/MM/DD"
|
||||
|
||||
format_time_txt="HH:mm:ss"
|
||||
"""
|
||||
|
||||
value=datetime.format_local_strtime('YYYY-MM-DD HH:mm:ss', value)
|
||||
|
||||
if value=='':
|
||||
return '0000-00-00 00:00:00'
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
# Convert to paramecio value
|
||||
value=str(value)
|
||||
value=value.replace('-', '').replace(':', '').replace(' ', '')
|
||||
|
||||
return datetime.format_date(value)
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
"""Method for return the sql code for this type
|
||||
|
||||
"""
|
||||
|
||||
return 'DATETIME NOT NULL'
|
||||
85
paramecio/libraries/db/extrafields/dictfield.py
Normal file
85
paramecio/libraries/db/extrafields/dictfield.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.webmodel import WebModel, PhangoField
|
||||
import json
|
||||
|
||||
class DictField(PhangoField):
|
||||
"""Field for save json dicts with determined values"""
|
||||
|
||||
def __init__(self, name, field_type, required=False):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of field
|
||||
field_type (PhangoField): The type of PhangoField for save in ArrayField
|
||||
required (bool): Boolean for define if field is required or not
|
||||
"""
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.field_type=field_type
|
||||
|
||||
self.error_default='Sorry, the json dict is invalid'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
self.type_sql='longtext'
|
||||
|
||||
self.jtype='object'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if type(value).__name__=='str':
|
||||
try:
|
||||
value=json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
elif type(value).__name__!='dict':
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
error=0
|
||||
for k,v in value.items():
|
||||
|
||||
value[k]=self.field_type.check(v)
|
||||
if self.field_type.error:
|
||||
error+=1
|
||||
|
||||
final_value=json.dumps(value)
|
||||
|
||||
if error>0:
|
||||
self.error=True
|
||||
|
||||
#final_value=WebModel.escape_sql(final_value)
|
||||
|
||||
return final_value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'JSON '+self.set_default
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return ", ".join(value)
|
||||
|
||||
49
paramecio/libraries/db/extrafields/emailfield.py
Normal file
49
paramecio/libraries/db/extrafields/emailfield.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
import re
|
||||
|
||||
mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+")
|
||||
|
||||
class EmailField(CharField):
|
||||
"""Field for save and check email addreses"""
|
||||
|
||||
def __init__(self, name, size=1024, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.error_default='Error: No valid format in '+self.name
|
||||
|
||||
self.jformat='email'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
value=super().check(value)
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
if not mail_pattern.match(value):
|
||||
|
||||
self.error=True
|
||||
value=""
|
||||
self.txt_error=self.error_default+' Email:'+value
|
||||
|
||||
return value
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.cromosoma.extraforms.fileform import FileForm
|
||||
from paramecio.citoplasma import httputils
|
||||
from paramecio.citoplasma.keyutils import create_key
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
from paramecio.libraries.db.extraforms.fileform import FileForm
|
||||
from paramecio.libraries import httputils
|
||||
from paramecio.libraries.keyutils import create_key
|
||||
import traceback
|
||||
|
||||
from bottle import request
|
||||
|
||||
|
||||
from uuid import uuid4
|
||||
#from paramecio.cromosoma.extraforms.fileform import FileForm
|
||||
#from paramecio.libraries.db.extraforms.fileform import FileForm
|
||||
|
||||
class FileField(CharField):
|
||||
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
from paramecio.cromosoma.webmodel import PhangoField
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.cromosoma.extraforms.i18nform import I18nForm
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
from paramecio.libraries.db.webmodel import PhangoField
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.db.extraforms.i18nform import I18nForm
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
import json
|
||||
import re
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.cromosoma.extraforms.fileform import FileForm
|
||||
from paramecio.citoplasma import httputils
|
||||
from paramecio.citoplasma.keyutils import create_key
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
from paramecio.libraries.db.extraforms.fileform import FileForm
|
||||
from paramecio.libraries import httputils
|
||||
from paramecio.libraries.keyutils import create_key
|
||||
import traceback
|
||||
|
||||
from bottle import request
|
||||
|
|
@ -16,7 +16,7 @@ except:
|
|||
|
||||
|
||||
from uuid import uuid4
|
||||
#from paramecio.cromosoma.extraforms.fileform import FileForm
|
||||
#from paramecio.libraries.db.extraforms.fileform import FileForm
|
||||
|
||||
class ImageField(CharField):
|
||||
|
||||
|
|
@ -1,8 +1,14 @@
|
|||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
import ipaddress
|
||||
|
||||
class IpField(CharField):
|
||||
|
||||
def __init__(self, name, size=1024, required=False):
|
||||
|
||||
super().__init__(name, size, required)
|
||||
|
||||
self.jformat='ipV4'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
try:
|
||||
117
paramecio/libraries/db/extrafields/jsonfield.py
Normal file
117
paramecio/libraries/db/extrafields/jsonfield.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for Bottle, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.webmodel import WebModel, PhangoField
|
||||
import sys
|
||||
try:
|
||||
|
||||
import ujson as json
|
||||
except:
|
||||
import json
|
||||
|
||||
class JsonField(PhangoField):
|
||||
"""Field for save json datatype values"""
|
||||
|
||||
def __init__(self, name, field_type, required=False):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of field
|
||||
field_type (PhangoField): The type of PhangoField for save in JsonField
|
||||
required (bool): Boolean for define if field is required or not
|
||||
"""
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.field_type=field_type
|
||||
|
||||
self.error_default='Sorry, the json dict is invalid'
|
||||
|
||||
self.set_default='NOT NULL'
|
||||
|
||||
self.type_sql='longtext'
|
||||
|
||||
self.jtype='object'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
if type(value).__name__=='str':
|
||||
try:
|
||||
|
||||
value=json.loads(value)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
elif type(value).__name__!='dict':
|
||||
|
||||
value={}
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
for k,v in value.items():
|
||||
|
||||
value[k]=self.field_type.check(v)
|
||||
|
||||
final_value=json.dumps(value)
|
||||
|
||||
#final_value=WebModel.escape_sql(final_value)
|
||||
|
||||
return final_value
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'JSON '+self.set_default
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
return ", ".join(value)
|
||||
|
||||
# You need check the values previously.
|
||||
|
||||
class JsonValueField(PhangoField):
|
||||
"""Field for save json mixed values. You need check the values previously, the field only check values for prevent sql injections."""
|
||||
|
||||
def __init__(self, name, required=False):
|
||||
|
||||
super().__init__(name, required)
|
||||
|
||||
self.error_default='Sorry, the json dict is invalid'
|
||||
self.default_value={}
|
||||
|
||||
#self.set_default='NOT NULL'
|
||||
|
||||
def get_type_sql(self):
|
||||
|
||||
return 'JSON'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
try:
|
||||
final_value=json.dumps(value)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
|
||||
final_value='{}'
|
||||
self.error=True
|
||||
self.txt_error=self.error_default
|
||||
|
||||
return final_value
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.cromosoma import coreforms
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
from paramecio.libraries.db import coreforms
|
||||
from paramecio.libraries.i18n import I18n
|
||||
|
||||
class LangField(CharField):
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from paramecio.cromosoma.corefields import DecimalField
|
||||
from paramecio.libraries.db.corefields import DecimalField
|
||||
from decimal import Decimal, getcontext
|
||||
from locale import format_string
|
||||
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
#from paramecio.cromosoma.webmodel import PhangoField
|
||||
from paramecio.cromosoma.corefields import IntegerField
|
||||
from paramecio.cromosoma.coreforms import SelectModelForm
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
#from paramecio.libraries.db.webmodel import PhangoField
|
||||
from paramecio.libraries.db.corefields import IntegerField
|
||||
from paramecio.libraries.db.coreforms import SelectModelForm
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
|
||||
class ParentField(IntegerField):
|
||||
|
||||
|
|
@ -1,9 +1,38 @@
|
|||
from paramecio.cromosoma.corefields import PhangoField
|
||||
from paramecio.cromosoma.coreforms import PasswordForm
|
||||
#!/usr/bin/python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from paramecio.libraries.db.corefields import PhangoField
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
from hmac import compare_digest as compare_hash
|
||||
import crypt
|
||||
from hmac import compare_digest as compare_hash
|
||||
#try:
|
||||
# import crypt
|
||||
#except:
|
||||
# pass
|
||||
|
||||
#import bcrypt
|
||||
from argon2 import PasswordHasher
|
||||
|
||||
class PasswordField(PhangoField):
|
||||
"""Field for check and save passwords"""
|
||||
|
||||
def __init__(self, name, size=1024, required=False):
|
||||
|
||||
|
|
@ -12,6 +41,7 @@ class PasswordField(PhangoField):
|
|||
self.name_form=PasswordForm
|
||||
self.default_value=''
|
||||
self.encrypt_password=True
|
||||
self.jformat='password'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
|
|
@ -43,7 +73,10 @@ class PasswordField(PhangoField):
|
|||
|
||||
#salt=crypt.mksalt(crypt.METHOD_SHA512)
|
||||
if self.encrypt_password:
|
||||
value=crypt.crypt(value)
|
||||
#value=crypt.crypt(value)
|
||||
ph=PasswordHasher()
|
||||
final_value=ph.hash(value)
|
||||
return final_value
|
||||
|
||||
"""
|
||||
else:
|
||||
|
|
@ -57,8 +90,14 @@ class PasswordField(PhangoField):
|
|||
|
||||
@staticmethod
|
||||
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))
|
||||
#return compare_hash(h, crypt.crypt(password, h))
|
||||
ph=PasswordHasher()
|
||||
try:
|
||||
return ph.verify(h, password)
|
||||
except:
|
||||
return False
|
||||
|
||||
# Old function bcrypt
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from paramecio.cromosoma.corefields import IntegerField
|
||||
from paramecio.libraries.db.corefields import IntegerField
|
||||
|
||||
class PercentField(IntegerField):
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.citoplasma.slugify import slugify
|
||||
from paramecio.cromosoma.coreforms import HiddenForm
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
from paramecio.libraries.slugify import slugify
|
||||
from paramecio.libraries.db.coreforms import HiddenForm
|
||||
|
||||
class SlugifyField(CharField):
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from paramecio.cromosoma.corefields import CharField
|
||||
from paramecio.libraries.db.corefields import CharField
|
||||
import re
|
||||
|
||||
check_url = re.compile(
|
||||
|
|
@ -15,6 +15,7 @@ class UrlField(CharField):
|
|||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
self.jformat='url'
|
||||
|
||||
if not check_url.match(value):
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from paramecio.cromosoma.corefields import PhangoField
|
||||
from paramecio.cromosoma.coreforms import PasswordForm
|
||||
from paramecio.libraries.db.corefields import PhangoField
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
from hmac import compare_digest as compare_hash
|
||||
import crypt
|
||||
import re
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
|
||||
class CheckForm(BaseForm):
|
||||
|
||||
38
paramecio/libraries/db/extraforms/colorform.py
Normal file
38
paramecio/libraries/db/extraforms/colorform.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for bottlepy, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.mtemplates import standard_t
|
||||
|
||||
class ColorForm(BaseForm):
|
||||
|
||||
def __init__(self, name, value):
|
||||
"""Form for get colors from a picker"""
|
||||
|
||||
super().__init__(name, value)
|
||||
|
||||
self.t=standard_t
|
||||
|
||||
def form(self):
|
||||
|
||||
|
||||
return self.t.load_template('forms/colorform.phtml', name_form=self.name_field_id, default_value=self.default_value, form=self)
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.mtemplates import standard_t
|
||||
from paramecio.citoplasma.datetime import format_timedata
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.mtemplates import standard_t
|
||||
from paramecio.libraries.datetime import format_timedata
|
||||
|
||||
class DateForm(BaseForm):
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.mtemplates import env_theme, PTemplate
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.mtemplates import env_theme, PTemplate
|
||||
|
||||
env=env_theme(__file__)
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.mtemplates import standard_t
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.mtemplates import standard_t
|
||||
import json
|
||||
|
||||
class I18nForm(BaseForm):
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.mtemplates import env_theme, PTemplate
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.mtemplates import env_theme, PTemplate
|
||||
|
||||
env=env_theme(__file__)
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
from paramecio.cromosoma.coreforms import BaseForm
|
||||
from paramecio.citoplasma.mtemplates import env_theme, PTemplate
|
||||
from paramecio.libraries.db.coreforms import BaseForm
|
||||
from paramecio.libraries.mtemplates import env_theme, PTemplate
|
||||
|
||||
env=env_theme(__file__)
|
||||
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma import corefields
|
||||
from paramecio.cromosoma.coreforms import PasswordForm
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.keyutils import create_key_encrypt
|
||||
from paramecio.libraries.db import corefields
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.sessionplugin import get_session
|
||||
from paramecio.libraries.keyutils import create_key_encrypt
|
||||
from bottle import request
|
||||
|
||||
# Need unittest
|
||||
|
|
@ -123,9 +123,10 @@ def csrf_token(token_id='csrf_token'):
|
|||
|
||||
s=get_session()
|
||||
|
||||
if not 'csrf_token' in s:
|
||||
s['csrf_token']=create_key_encrypt()
|
||||
s.save()
|
||||
#if not 'csrf_token' in s:
|
||||
s['csrf_token']=create_key_encrypt()
|
||||
|
||||
#s.save()
|
||||
|
||||
return '<input type="hidden" name="csrf_token" class="csrf_token" id="'+token_id+'" value="'+s['csrf_token']+'" />'
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ def generate_csrf():
|
|||
|
||||
if not 'csrf_token' in s:
|
||||
s['csrf_token']=create_key_encrypt()
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
return s['csrf_token']
|
||||
|
||||
44
paramecio/libraries/db/simplequery.py
Normal file
44
paramecio/libraries/db/simplequery.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
# 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
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.cromosoma.coreforms import PasswordForm
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
|
||||
class UserModel(WebModel):
|
||||
|
||||
|
|
@ -1,23 +1,64 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.libraries.db.databases.sqlalchemy import SqlClass
|
||||
from paramecio.libraries.db.coreforms import BaseForm, HiddenForm
|
||||
import sys
|
||||
import re
|
||||
import uuid
|
||||
from importlib import import_module, reload
|
||||
from collections import OrderedDict
|
||||
from paramecio.cromosoma.databases.sqlalchemy import SqlClass
|
||||
from paramecio.cromosoma.coreforms import BaseForm, HiddenForm
|
||||
import copy
|
||||
import traceback
|
||||
|
||||
class PhangoField:
|
||||
"""Base class for fields used in WebModel classes
|
||||
|
||||
PhangoField is a class with all elements and variables that you can imagine for a database mysql field in a table. You have many types similar to mysql field types.
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=255, required=False):
|
||||
"""
|
||||
Args:
|
||||
name (str): The name of the field
|
||||
size (int): The size of sql field.
|
||||
required (bool): If the field is required or not.
|
||||
|
||||
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.
|
||||
protected (bool): If the field can be updated or not in WebModel update method.
|
||||
quote_close (str): In old versions was used for get more protection for sql sentences
|
||||
error (bool): If error in query, set to True.
|
||||
txt_error (str): Variable where the basic text error is saved
|
||||
model (str): The model where this component or field live
|
||||
indexed (bool): Property used for set this field how indexed in the database table.
|
||||
unique (bool): Property used for set this field how unique value in the database table.
|
||||
foreignkey (bool): Simple property for make more easy identify foreignkeyfields.
|
||||
default_value (str): Property that define the default value for this field
|
||||
update (bool): Property that define if this field is in an update operation or insert operation
|
||||
check_blank (bool): Property used for check if this value cannot change if is in blank and is filled
|
||||
name_form(BaseForm): Define the form, when is created forms with create_forms you can change the properties of this class
|
||||
escape (bool): Property that define if make escape in show_formatted. This property control the html transformation of <>" characters in html entities.If false, convert.
|
||||
file_related (bool): File related: if the field have a file related, delete the file
|
||||
extra_parameters (list): Extra parameters for the form related with this field
|
||||
t (PTemplate): Template manager for the form if needed
|
||||
error_default (str): Error text by default
|
||||
show_formatted_value (bool): Show this value formatted
|
||||
help (str): Value used for help strings in tooltips in forms
|
||||
|
||||
"""
|
||||
|
||||
# The name of the field in database table
|
||||
|
||||
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()
|
||||
|
|
@ -50,7 +91,7 @@ class PhangoField:
|
|||
|
||||
self.txt_error=""
|
||||
|
||||
# Themodel where this component or field live
|
||||
# The model where this component or field live
|
||||
|
||||
self.model=None
|
||||
|
||||
|
|
@ -100,7 +141,7 @@ class PhangoField:
|
|||
|
||||
# Error by default
|
||||
|
||||
self.error_default='Error: field required'
|
||||
self.error_default='Error: '+self.name+' field required'
|
||||
|
||||
# Show this value formatted
|
||||
|
||||
|
|
@ -110,27 +151,28 @@ class PhangoField:
|
|||
|
||||
self.help=''
|
||||
|
||||
# This method is used for describe the new field in a sql language format.
|
||||
|
||||
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."""
|
||||
|
||||
return 'VARCHAR('+str(self.size)+') NOT NULL DEFAULT "'+self.default_value+'"'
|
||||
|
||||
def show_formatted(self, value):
|
||||
"""Method for format the value to show in html or others text outputs"""
|
||||
|
||||
return value
|
||||
|
||||
# This method for check the value
|
||||
|
||||
|
||||
def check(self, value):
|
||||
"""Method for check if value is valid for this type field"""
|
||||
|
||||
self.error=False
|
||||
self.txt_error=''
|
||||
|
||||
value=str(value).strip()
|
||||
|
||||
#Minimal escape for prevent basic js injection.
|
||||
|
||||
if self.escape==False:
|
||||
value=value.replace('<', '<')
|
||||
|
||||
|
|
@ -150,6 +192,7 @@ class PhangoField:
|
|||
pass
|
||||
|
||||
def create_form(self):
|
||||
"""Create a BaseForm object for use in forms functions and methods"""
|
||||
#self.name, self.default_value,
|
||||
|
||||
final_parameters=copy.copy(self.extra_parameters)
|
||||
|
|
@ -165,6 +208,7 @@ class PhangoField:
|
|||
return form
|
||||
|
||||
def change_form(self, new_form, parameters):
|
||||
"""Change the base form of the field and its parameters"""
|
||||
|
||||
self.name_form=new_form
|
||||
|
||||
|
|
@ -174,6 +218,10 @@ class PhangoField:
|
|||
pass
|
||||
|
||||
class PrimaryKeyField(PhangoField):
|
||||
"""Primary key field based in PhangoField.
|
||||
|
||||
This field is used for create a typical id field in a mariadb/mysql table
|
||||
"""
|
||||
|
||||
def __init__(self, name, size=11, required=False):
|
||||
super(PrimaryKeyField, self).__init__(name, size, required)
|
||||
|
|
@ -181,6 +229,7 @@ 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):
|
||||
|
||||
|
|
@ -217,6 +266,24 @@ class PrimaryKeyField(PhangoField):
|
|||
|
||||
|
||||
class WebModel:
|
||||
"""The most important class for the framework
|
||||
|
||||
Webmodel is a class for create objects that represent models. This models are a mirage of SQL tables. You can create fields, add indexes, foreign keys, and more.
|
||||
|
||||
Attributes:
|
||||
arr_sql_index (dict): Internal dict used for generate mysql index in fields
|
||||
arr_sql_set_index (dict): Internal dict used for generate mysql index in fields
|
||||
arr_sql_unique (dict): Internal dict used for generate mysql unique values in fields
|
||||
arr_sql_set_unique (dict): Internal dict used for generate mysql unique values in fields
|
||||
last_query (str): The last query execute by WebModel
|
||||
connection_pool (list): A list used in older versions, deprecated
|
||||
first_primary_key (PrimaryKeyField): Field used for primary field and create models in database.
|
||||
model (OrderedDict): Dict used for internal things and create tables.
|
||||
connections (dict): A dict with the configuration of the mysql connection. You can use this element in config.py. You set elements, normally "default" with typical elements how:
|
||||
host: Database host, user: The username of mysql db, password: The password of user, db: The name of the db, charset: The charset of database, normally utf8, db_type: The db_type, possible values are mysqldb or pymysql, by default, pymysql.
|
||||
connection_id (str): The id by default of the selected connection from connections.
|
||||
|
||||
"""
|
||||
|
||||
__slots__=('sqlclass', 'fields', 'forms')
|
||||
|
||||
|
|
@ -248,12 +315,54 @@ class WebModel:
|
|||
|
||||
@staticmethod
|
||||
def connection():
|
||||
"""Static method for make a connection using SqlClass
|
||||
|
||||
Returns: Return a SqlClass connection for mysql db.
|
||||
"""
|
||||
|
||||
return SqlClass(WebModel.connections['default'])
|
||||
|
||||
# Init the class
|
||||
|
||||
def __init__(self, sqlclass=None, name_field_id="id"):
|
||||
"""
|
||||
Args:
|
||||
sqlclass (SqlClass): The SqlClass connection used for the mysql db
|
||||
name_field_id (str): The name of field id of this model/mysql table
|
||||
|
||||
Attributes:
|
||||
name (str): The name of this model correspondient to the sql table name with lower string.
|
||||
label (str): Descriptive name, first is used self.name how default.
|
||||
label_general (str): Descriptive general name, first is used self.name how default.
|
||||
name_field_id (str): The name of field id of this model/mysql table
|
||||
fields (OrderedDict): A dict with the fields of model/table based in PhangoField objects.
|
||||
fields_error (OrderedDict): A dict where the errors when check data fields are saved
|
||||
related (list): A list where related fields are saved.
|
||||
forms (OrderedDict): A dict where forms related with fields using how base BaseForm class are saved if you use self.create_forms() method.
|
||||
errors (dict): A dict where generic errors are saved.
|
||||
num_errors (int): Number of errors generated by the model on query methods.
|
||||
query_error (str): If error in query, saved here.
|
||||
values_query (list): Where the values for a sql query for filtering are saved.
|
||||
conditions (list): A list used for define the sql conditions.
|
||||
First element is the sql condition, Example: 'WHERE id=%s', and second element is the variable to substitute %s, example [1]. Complete example: ['WHERE id=%s', 1]
|
||||
order_by (str): Internal variable used for set the sql order str. You don't shoud change this variable if yo don't know what are you doing.
|
||||
limit (str): Internal variable used for set the sql limit str.
|
||||
related_models_deleted (list): Internal variable used for delete tables from db.
|
||||
required_save (str): Internal variable used for required fields defined in self.fields
|
||||
primary_key (str): Default name of primary key field
|
||||
yes_reset_conditions (bool): If True, methods how select and update reset self.conditions. If False, self.conditions is used in next select and update executions.
|
||||
updated (bool): True if the model is used for update, False if the model is used for insert or other operations.
|
||||
valid_fields (list): List with the fields validated for insert or update
|
||||
last_id (int): The id of last inserted row.
|
||||
distinct (str): Add DISTINCT keyword to self.select method.
|
||||
post (dict): A simple dictionary where post values are saved for use of fields classes
|
||||
files_delete (dict): A simple dictionary that save the fields that have files related. If i delete the row in database i need delete the files related
|
||||
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.
|
||||
field_quote(str): The field delimiter. In Mariadb is `, in PostgreSQL in the future is \"
|
||||
"""
|
||||
|
||||
self.cached=WebModel.global_cached
|
||||
|
||||
|
|
@ -277,7 +386,7 @@ class WebModel:
|
|||
|
||||
# Errors of fields of the table, for safe thread reasons.
|
||||
|
||||
self.fields_error=OrderedDict()
|
||||
#self.fields_error=OrderedDict()
|
||||
|
||||
#The tables related with foreignkeyfield to this table
|
||||
|
||||
|
|
@ -357,12 +466,20 @@ class WebModel:
|
|||
|
||||
self.enctype=False
|
||||
|
||||
self.model_id=0
|
||||
|
||||
self.dummy=0
|
||||
|
||||
self.field_quote='`'
|
||||
|
||||
# A method for add the connection
|
||||
|
||||
def conn(self, sqlclass):
|
||||
""" Method for get the SqlClass object and prepare sql variables
|
||||
|
||||
Args:
|
||||
sqlclass (SqlClass): A SqlClass object that present the db connection
|
||||
"""
|
||||
self.sqlclass=sqlclass
|
||||
|
||||
# Reset conditions
|
||||
|
|
@ -371,21 +488,35 @@ class WebModel:
|
|||
|
||||
self.conditions=["WHERE 1=1", []]
|
||||
|
||||
self.order_by="ORDER BY `"+self.name+"`.`id` ASC"
|
||||
name_table=self.field_quote+self.name+self.field_quote
|
||||
|
||||
field_id=self.field_quote+"id"+self.field_quote
|
||||
|
||||
self.order_by="ORDER BY "+name_table+"."+field_id+" ASC"
|
||||
|
||||
self.limit=""
|
||||
|
||||
# A method for change the name of table
|
||||
|
||||
def change_name(self, name):
|
||||
""" A method for change the name of table
|
||||
|
||||
Args;
|
||||
name (str): The new name of table
|
||||
"""
|
||||
|
||||
self.name=name
|
||||
|
||||
self.order_by="ORDER BY `"+self.name+"`.`id` ASC"
|
||||
name_table=self.field_quote+self.name+self.field_quote
|
||||
|
||||
field_id=self.field_quote+"id"+self.field_quote
|
||||
|
||||
self.order_by="ORDER BY "+name_table+"."+field_id+" ASC"
|
||||
|
||||
# A method where create the new fields of this model
|
||||
|
||||
def create_fields(self):
|
||||
"""Dummy method for use in children classes for add fields"""
|
||||
|
||||
#print([i for i in dir(self.__class__) if i[:1] != '_'])
|
||||
#print(dir(self))
|
||||
|
|
@ -395,6 +526,14 @@ class WebModel:
|
|||
# A method for register the fields
|
||||
|
||||
def register(self, field_model, required=False):
|
||||
"""A method for register the fields in model class
|
||||
|
||||
With this method, your register your fields in the model, inside self.fields attribute. Fields are used for build the query for get the data from the sql table.
|
||||
|
||||
Args:
|
||||
field_model (PhangoField): PhangoField object for add to model
|
||||
required (bool): If True, the field is required when you insert or update a item row in table model. If False, the field is not required. If field is not required and checking fail, the model update/insert ignore it.
|
||||
"""
|
||||
|
||||
#self.fields_required[field_model]=field_model.required
|
||||
|
||||
|
|
@ -436,6 +575,13 @@ class WebModel:
|
|||
# Method for make queries
|
||||
|
||||
def query(self, str_query, args=[], connection_id='default'):
|
||||
"""Method for make typical sql query to db
|
||||
|
||||
Args:
|
||||
str_query (str): The str query. Use the typical format of sql python drivers, example: select * from my_table WHERE id=%s.
|
||||
args (list): The arguments to substitute %s characters of the strings. The list must sequential with %s characters in the string.
|
||||
connection_id (str): The connection data used for this connection, by default is "default".
|
||||
"""
|
||||
|
||||
self.connect_to_db()
|
||||
return self.sqlclass.query(str_query, args, connection_id)
|
||||
|
|
@ -443,6 +589,8 @@ class WebModel:
|
|||
# Method for clean fields
|
||||
|
||||
def clean_fields(self):
|
||||
"""Method for delete fields from self.fields dict"""
|
||||
|
||||
clean=self.fields_to_clean
|
||||
for field in self.fields_to_clean:
|
||||
del self.fields[field]
|
||||
|
|
@ -451,6 +599,14 @@ class WebModel:
|
|||
# External agent define if the update is in code or from external source, how a form.
|
||||
|
||||
def insert(self, dict_values, external_agent=True):
|
||||
"""Insert method, for insert a row in database using a dictionary
|
||||
|
||||
This method is a shortcut for typical sql insert sentence.
|
||||
|
||||
Args:
|
||||
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
|
||||
external_agent (bool): External agent define if the update is in code or from external source, how a form.
|
||||
"""
|
||||
|
||||
self.clean_fields()
|
||||
|
||||
|
|
@ -466,7 +622,6 @@ class WebModel:
|
|||
|
||||
try:
|
||||
|
||||
#fields, values, update_values=self.check_all_fields(dict_values, external_agent)
|
||||
arr_return=self.check_all_fields(dict_values, external_agent)
|
||||
|
||||
if arr_return:
|
||||
|
|
@ -476,17 +631,20 @@ class WebModel:
|
|||
else:
|
||||
return False
|
||||
|
||||
except:
|
||||
self.query_error='Cannot insert the new row'
|
||||
print(sys.exc_info()[0])
|
||||
raise
|
||||
#return False
|
||||
except Exception as e:
|
||||
self.query_error='Cannot insert the new row '+str(e)
|
||||
#print(sys.exc_info()[0])
|
||||
return False
|
||||
|
||||
c=len(values)
|
||||
|
||||
arr_str=['%s' for x in range(c)]
|
||||
|
||||
sql="insert into `"+self.name+"` (`"+"`, `".join(fields)+"`) VALUES ("+", ".join(arr_str)+")"
|
||||
join_fields="`, `".join(fields)
|
||||
|
||||
join_values=", ".join(arr_str)
|
||||
|
||||
sql="insert into `"+self.name+self.field_quote+" ("+self.field_quote+join_fields+self.field_quote+") VALUES ("+join_values+")"
|
||||
|
||||
cursor=self.query(sql, values, self.connection_id)
|
||||
|
||||
|
|
@ -509,6 +667,15 @@ class WebModel:
|
|||
# Update method. For update one or many rows.
|
||||
|
||||
def update(self, dict_values, external_agent=True):
|
||||
"""Upate method, for update a row in database using a dictionary
|
||||
|
||||
This method is a shortcut for typical sql update sentence.
|
||||
|
||||
Args:
|
||||
dict_values (dict): A dict with the name of the fields how defined in PhangoField for the keys, and values for the values designed for every field.
|
||||
external_agent (bool): External agent define if the update is in code or from external source, how a form.
|
||||
"""
|
||||
|
||||
|
||||
self.clean_fields()
|
||||
|
||||
|
|
@ -545,7 +712,9 @@ class WebModel:
|
|||
#print(traceback.format_exc())
|
||||
return False
|
||||
|
||||
sql="update `"+self.name+"` SET "+", ".join(update_values)+" "+self.conditions[0]
|
||||
field_name=self.field_quote+self.name+self.field_quote
|
||||
|
||||
sql="update "+field_name+" SET "+", ".join(update_values)+" "+self.conditions[0]
|
||||
|
||||
cursor=self.query(sql, values+self.conditions[1], self.connection_id)
|
||||
|
||||
|
|
@ -583,7 +752,7 @@ class WebModel:
|
|||
"""
|
||||
|
||||
def reset_conditions(self):
|
||||
|
||||
"""Method for reset self.conditions to default values"""
|
||||
self.conditions=["WHERE 1=1", []]
|
||||
self.limit=''
|
||||
|
||||
|
|
@ -591,6 +760,19 @@ class WebModel:
|
|||
#Type assoc can be assoc for return dictionaries
|
||||
|
||||
def select(self, arr_select=[], raw_query=False):
|
||||
"""A method for select fields from a table in db. Support for foreignkeys.
|
||||
|
||||
This method is a shortcut for typical sql select sentence. You can select multiple tables using ForeignKeyField class.
|
||||
|
||||
Args:
|
||||
arr_select (dict): A list with the name of the fields how defined in PhangoField.
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
false (bool): If return false, the db connection is down.
|
||||
sql_cursor (cursor): Return cursor db for get data using loops or other if operation is successful, if not, return False.
|
||||
|
||||
"""
|
||||
|
||||
self.clean_fields()
|
||||
|
||||
|
|
@ -608,7 +790,7 @@ class WebModel:
|
|||
|
||||
#First table selecction
|
||||
|
||||
tables_to_select=['`'+self.name+'`']
|
||||
tables_to_select=[self.field_quote+self.name+self.field_quote]
|
||||
|
||||
keys=list(self.fields.keys())
|
||||
|
||||
|
|
@ -641,21 +823,21 @@ class WebModel:
|
|||
else:
|
||||
arr_repeat_field[self.fields[field].table_name]=0
|
||||
|
||||
table_name=self.fields[field].table_name+'` as `'+self.fields[field].table_name+str(arr_repeat_field[self.fields[field].table_name])
|
||||
table_name=self.fields[field].table_name+self.field_quote+' as '+self.field_quote+self.fields[field].table_name+str(arr_repeat_field[self.fields[field].table_name])
|
||||
|
||||
final_table_name=self.fields[field].table_name+str(arr_repeat_field[self.fields[field].table_name])
|
||||
|
||||
# The name with its alias of this related table model
|
||||
|
||||
tables_to_select.append('`'+table_name+'`')
|
||||
tables_to_select.append(self.field_quote+table_name+self.field_quote)
|
||||
|
||||
# Add field from related table
|
||||
# as "+table_name+"_"+self.fields[field].named_field
|
||||
extra_fields.append("`"+final_table_name+"`.`"+self.fields[field].named_field+"` as "+field)
|
||||
extra_fields.append(self.field_quote+final_table_name+self.field_quote+"."+self.field_quote+self.fields[field].named_field+self.field_quote+" as "+field)
|
||||
|
||||
# Add a condition to sql query for join the two tables.
|
||||
|
||||
conditions[0]+=" AND `"+final_table_name+"`.`"+self.fields[field].identifier_field+"`=`"+self.name+"`.`"+field+"`"
|
||||
conditions[0]+=" AND "+self.field_quote+final_table_name+self.field_quote+"."+self.field_quote+self.fields[field].identifier_field+self.field_quote+"="+self.field_quote+self.name+self.field_quote+"."+self.field_quote+field+self.field_quote
|
||||
|
||||
# Add extra fields from related table from select_fields ForeignKeyField class member
|
||||
|
||||
|
|
@ -668,11 +850,11 @@ class WebModel:
|
|||
|
||||
# Check if extra_field is ForeignKeyField, if yes, call this function recursively.
|
||||
|
||||
extra_fields.append("`"+final_table_name+"`.`"+extra_field+"` as `"+field+"_"+extra_field+"`")
|
||||
extra_fields.append(self.field_quote+final_table_name+self.field_quote+"."+self.field_quote+extra_field+self.field_quote+" as "+self.field_quote+field+"_"+extra_field+self.field_quote)
|
||||
else:
|
||||
# Add normal field to sql query
|
||||
|
||||
final_fields.append("`"+self.name+"`.`"+field+"`")
|
||||
final_fields.append(self.field_quote+self.name+self.field_quote+"."+self.field_quote+field+self.field_quote)
|
||||
|
||||
#if len(new_fields)>0:
|
||||
#self.fields.update(new_fields)
|
||||
|
|
@ -706,16 +888,31 @@ class WebModel:
|
|||
# Show results in a dictionary
|
||||
|
||||
def fetch(self, cursor):
|
||||
""" Simple method for get a row from db using cursor
|
||||
|
||||
Args:
|
||||
cursor (Db cursor): A typical db cursor of python sql interface standard.
|
||||
|
||||
Returns:
|
||||
row (dict): Return a dictionary with the row selected.
|
||||
|
||||
"""
|
||||
|
||||
return cursor.fetchone()
|
||||
|
||||
def insert_id(self):
|
||||
"""Method for get the id from last row inserted in table"""
|
||||
|
||||
return self.last_id
|
||||
|
||||
def element_exists(self, id):
|
||||
"""Check if exist row with id in db
|
||||
|
||||
self.conditions=['WHERE `'+self.name_field_id+'`=%s', [id]]
|
||||
Args:
|
||||
id (int): The id of the row to search.
|
||||
"""
|
||||
|
||||
self.conditions=['WHERE '+self.field_quote+self.name_field_id+self.field_quote+'=%s', [id]]
|
||||
|
||||
count=self.select_count(self.name_field_id)
|
||||
|
||||
|
|
@ -731,8 +928,17 @@ class WebModel:
|
|||
pass
|
||||
|
||||
def select_a_row(self, id, fields_selected=[], raw_query=0):
|
||||
"""Shortcut for get a simple row from a query
|
||||
|
||||
self.conditions=['WHERE `'+self.name+'`.`'+self.name_field_id+'`=%s', [id]]
|
||||
Args:
|
||||
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
row (dict): Returns dict with the row values.
|
||||
"""
|
||||
|
||||
self.conditions=['WHERE '+self.field_quote+self.name+self.field_quote+'.'+self.field_quote+self.name_field_id+self.field_quote+'=%s', [id]]
|
||||
|
||||
self.limit="limit 1"
|
||||
|
||||
|
|
@ -752,6 +958,15 @@ class WebModel:
|
|||
return row
|
||||
|
||||
def select_a_row_where(self, fields_selected=[], raw_query=0, begin=0):
|
||||
"""Shortcut for get a simple row from a query using self.conditions
|
||||
|
||||
Args:
|
||||
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
row (dict): Returns dict with the row values.
|
||||
"""
|
||||
|
||||
self.limit="limit "+str(begin)+", 1"
|
||||
|
||||
|
|
@ -770,6 +985,16 @@ class WebModel:
|
|||
|
||||
|
||||
def select_to_array(self, fields_selected=[], raw_query=0):
|
||||
"""Shortcut for get a a list of rows from select sql query
|
||||
|
||||
Args:
|
||||
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
row_values (dict): Returns dict with the row values.
|
||||
"""
|
||||
|
||||
|
||||
if len(fields_selected)==0:
|
||||
fields_selected=list(self.fields.keys())
|
||||
|
|
@ -810,6 +1035,16 @@ class WebModel:
|
|||
|
||||
def select_to_dict(self, fields_selected=[], raw_query=0, integer=True):
|
||||
|
||||
"""Shortcut for get a dict of rows from select sql query
|
||||
|
||||
Args:
|
||||
fields_selected (dict): A list with the name of the fields how defined in PhangoField.
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
row (dict): Returns dict with the row values.
|
||||
"""
|
||||
|
||||
if integer:
|
||||
def check_index(index):
|
||||
return index
|
||||
|
|
@ -855,6 +1090,15 @@ class WebModel:
|
|||
# A method por count num rows affected for sql conditions
|
||||
|
||||
def select_count(self, field_to_count='id', raw_query=True):
|
||||
"""Method for get a typical sql count using conditions
|
||||
|
||||
Args:
|
||||
field_to_count (str): The field
|
||||
raw_query (bool): If True, if foreignkeyfields exists, are not selected. If False, foreignkeyfields are selected too if foreignkeyfield are in arr_select.
|
||||
|
||||
Returns:
|
||||
num_elecments (int): Returns the number of elements selected.
|
||||
"""
|
||||
|
||||
# Connect to db
|
||||
|
||||
|
|
@ -864,7 +1108,7 @@ class WebModel:
|
|||
|
||||
#First table selecction
|
||||
|
||||
tables_to_select=['`'+self.name+'`']
|
||||
tables_to_select=[self.field_quote+self.name+self.field_quote]
|
||||
|
||||
fields=list(self.fields.keys())
|
||||
|
||||
|
|
@ -878,13 +1122,13 @@ class WebModel:
|
|||
|
||||
table_name=self.fields[field].table_name
|
||||
|
||||
tables_to_select.append('`'+table_name+'`')
|
||||
tables_to_select.append(self.field_quote+table_name+self.field_quote)
|
||||
|
||||
# Add a condition to sql query for join the two tables.
|
||||
|
||||
conditions[0]+=" AND `"+table_name+"`.`"+self.fields[field].identifier_field+"`=`"+self.name+"`.`"+field+"`"
|
||||
conditions[0]+=" AND "+self.field_quote+table_name+self.field_quote+"."+self.field_quote+self.fields[field].identifier_field+self.field_quote+"="+self.field_quote+self.name+self.field_quote+"."+self.field_quote+field+self.field_quote
|
||||
|
||||
sql= "select count(`"+field_to_count+"`) from "+", ".join(tables_to_select)+' '+conditions[0]
|
||||
sql= "select count("+self.field_quote+field_to_count+self.field_quote+") from "+", ".join(tables_to_select)+' '+conditions[0]
|
||||
|
||||
count=0
|
||||
|
||||
|
|
@ -901,12 +1145,19 @@ class WebModel:
|
|||
# A method for delete rows using sql conditions
|
||||
|
||||
def delete(self):
|
||||
"""Method for delete a series of rows using conditions
|
||||
|
||||
Returns:
|
||||
bool (bool): If delete is successfully, return True, if not, return False.
|
||||
"""
|
||||
|
||||
#self.connect_to_db()
|
||||
|
||||
#Need delete rows from other related tables save in self.related_models_deleted
|
||||
|
||||
sql=("delete from `"+self.name+"` "+self.conditions[0]+' '+self.order_by+' '+self.limit).strip()
|
||||
#+' '+self.order_by+' '+self.limit
|
||||
|
||||
sql=("delete from "+self.field_quote+self.name+self.field_quote+" "+self.conditions[0]).strip()
|
||||
|
||||
result=self.query(sql, self.conditions[1], self.connection_id)
|
||||
|
||||
|
|
@ -923,6 +1174,15 @@ class WebModel:
|
|||
return False
|
||||
|
||||
def set_conditions(self, sql_text, values:list) -> object:
|
||||
"""Method for set conditions for a typical sql query
|
||||
|
||||
Args:
|
||||
sql_text (str): The sql text with the conditions, Example: WHERE id=%s
|
||||
values (list): A list with values for substitute %s characters for the real values filtered for not allow sql injections.
|
||||
|
||||
Returns:
|
||||
Return the same object with self.conditions modified.
|
||||
"""
|
||||
|
||||
self.conditions=[sql_text, values]
|
||||
|
||||
|
|
@ -930,6 +1190,15 @@ class WebModel:
|
|||
|
||||
@staticmethod
|
||||
def check_in_list(in_list):
|
||||
"""Method for convert values to int for use in IN (1,2,3) sql sentences.
|
||||
|
||||
Args:
|
||||
in_list (list): List with numbers items.
|
||||
|
||||
Returns:
|
||||
sql_filtered (str): with (1,2,3) sql sentence filtered.
|
||||
|
||||
"""
|
||||
|
||||
for x in range(0, len(in_list)):
|
||||
try:
|
||||
|
|
@ -939,6 +1208,15 @@ class WebModel:
|
|||
return '('+', '.join(in_list)+')'
|
||||
|
||||
def check_in_list_str(self, field, in_list):
|
||||
"""Method for convert values to int for use in IN (value1, value2, value3) sql sentences.
|
||||
|
||||
Args:
|
||||
field (PhangoField): The PhangoField used for check the values of in_list
|
||||
in_list (list): List with value items.
|
||||
|
||||
Returns:
|
||||
sql_filtered (str): (value1, value2, value3) sql sentence filtered.
|
||||
"""
|
||||
|
||||
for x in range(0, len(in_list)):
|
||||
in_list[x]=str(self.fields[field].check(in_list[x]))
|
||||
|
|
@ -946,6 +1224,15 @@ class WebModel:
|
|||
return '("'+'", "'.join(in_list)+'")'
|
||||
|
||||
def set_order(self, order:dict) -> object:
|
||||
""" Method for set and complete the query with "order by" sentences.
|
||||
|
||||
Args:
|
||||
order (dict): A dict with a field name how key, and 0 or 1 how values. 0 define order how ASC, 1 define order how DESC.
|
||||
|
||||
Returns:
|
||||
|
||||
Returns the same object for execute a query after set_order declaration.
|
||||
"""
|
||||
|
||||
arr_order=[]
|
||||
arr_order.append('ASC')
|
||||
|
|
@ -964,6 +1251,15 @@ class WebModel:
|
|||
return self
|
||||
|
||||
def set_limit(self, limit: tuple) -> None:
|
||||
""" Method for set and complete the query with "limit" sentences.
|
||||
|
||||
Args:
|
||||
limit (tuple): A tuple with one or two elements. If one element, example (1), the result is "LIMIT first_element", if two elements, example (1,2), the result is "LIMIT first_element, two_element"
|
||||
|
||||
Returns:
|
||||
|
||||
Returns the same object for execute a query after set_order declaration.
|
||||
"""
|
||||
|
||||
limit[0]=int(limit[0])
|
||||
|
||||
|
|
@ -979,6 +1275,7 @@ class WebModel:
|
|||
# Method for create sql tables
|
||||
|
||||
def create_table(self):
|
||||
"""Method for create a table from this model object"""
|
||||
|
||||
#self.connect_to_db()
|
||||
|
||||
|
|
@ -996,13 +1293,13 @@ class WebModel:
|
|||
fields=self.fields
|
||||
for field, data in fields.items():
|
||||
|
||||
table_fields.append('`'+field+'` '+data.get_type_sql())
|
||||
table_fields.append(self.field_quote+field+self.field_quote+' '+data.get_type_sql())
|
||||
|
||||
#Check if indexed
|
||||
|
||||
if fields[field].indexed==True:
|
||||
|
||||
self.arr_sql_index[self.name][field]='CREATE INDEX `index_'+self.name+'_'+field+'` ON '+self.name+'(`'+field+'`);'
|
||||
self.arr_sql_index[self.name][field]='CREATE INDEX '+self.field_quote+'index_'+self.name+'_'+field+self.field_quote+' ON '+self.name+'('+self.field_quote+field+self.field_quote+');'
|
||||
self.arr_sql_set_index[self.name][field]=""
|
||||
|
||||
|
||||
|
|
@ -1010,20 +1307,20 @@ class WebModel:
|
|||
|
||||
if fields[field].unique==True:
|
||||
|
||||
self.arr_sql_unique[self.name][field]='ALTER TABLE `'+self.name+'` ADD UNIQUE (`'+field+'`)'
|
||||
self.arr_sql_unique[self.name][field]='ALTER TABLE '+self.field_quote+self.name+self.field_quote+' ADD UNIQUE ('+self.field_quote+field+self.field_quote+')'
|
||||
self.arr_sql_set_unique[self.name][field]=""
|
||||
|
||||
if type(fields[field]).__name__=="ForeignKeyField":
|
||||
|
||||
self.arr_sql_index[self.name][field]='CREATE INDEX `index_'+self.name+'_'+field+'` ON '+self.name+'(`'+field+'`);'
|
||||
self.arr_sql_index[self.name][field]='CREATE INDEX '+self.field_quote+'index_'+self.name+'_'+field+self.field_quote+' ON '+self.name+'('+self.field_quote+field+self.field_quote+');'
|
||||
|
||||
table_related=fields[field].table_name
|
||||
|
||||
id_table_related=fields[field].table_id
|
||||
|
||||
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;'
|
||||
self.arr_sql_set_index[self.name][field]='ALTER TABLE '+self.field_quote+self.name+self.field_quote+' ADD CONSTRAINT '+self.field_quote+field+'_'+self.name+'IDX'+self.field_quote+' FOREIGN KEY ( '+self.field_quote+field+self.field_quote+' ) REFERENCES '+self.field_quote+table_related+self.field_quote+' ('+self.field_quote+id_table_related+self.field_quote+') ON DELETE CASCADE ON UPDATE CASCADE;'
|
||||
|
||||
return "create table `"+self.name+"` (\n"+",\n".join(table_fields)+"\n) DEFAULT CHARSET=utf8;";
|
||||
return "create table "+self.field_quote+self.name+self.field_quote+" (\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):
|
||||
|
||||
|
|
@ -1033,40 +1330,40 @@ class WebModel:
|
|||
|
||||
print("---Deleting index from "+field+" in "+self.name)
|
||||
|
||||
self.query('DROP INDEX `index_'+self.name+'_'+field+'` ON '+self.name, [], self.connection_id)
|
||||
self.query('DROP INDEX '+self.field_quote+'index_'+self.name+'_'+field+self.field_quote+' ON '+self.name, [], self.connection_id)
|
||||
|
||||
for field in fields_to_delete_unique:
|
||||
|
||||
print("---Deleting unique from "+field+" in "+self.name)
|
||||
|
||||
self.query('DROP INDEX `'+field+'` ON '+self.name, [], self.connection_id)
|
||||
self.query('DROP INDEX '+self.field_quote+field+self.field_quote+' ON '+self.field_quote+self.name+self.field_quote, [], self.connection_id)
|
||||
|
||||
for field in fields_to_delete_constraint:
|
||||
|
||||
print("---Deleting foreignkey from "+field+" in "+self.name)
|
||||
|
||||
self.query('ALTER TABLE `'+self.name+'` DROP FOREIGN KEY '+field+'_'+self.name+'IDX', [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' DROP FOREIGN KEY '+field+'_'+self.name+'IDX', [], self.connection_id)
|
||||
|
||||
for field in fields_to_delete:
|
||||
|
||||
print("---Deleting "+field+" from "+self.name)
|
||||
|
||||
self.query('ALTER TABLE `'+self.name+'` DROP `'+field+'`', [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' DROP '+self.field_quote+field+self.field_quote, [], self.connection_id)
|
||||
#Deleting indexes and constraints.
|
||||
|
||||
#Obtain new fields
|
||||
|
||||
for field in fields_to_modify:
|
||||
print("---Updating "+field+" in "+self.name)
|
||||
self.query('ALTER TABLE `'+self.name+'` MODIFY `'+field+'` '+self.fields[field].get_type_sql(), [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' MODIFY '+self.field_quote+field+self.field_quote+' '+self.fields[field].get_type_sql(), [], self.connection_id)
|
||||
|
||||
for field in fields_to_add:
|
||||
print("---Adding "+field+" in "+self.name)
|
||||
self.query('ALTER TABLE `'+self.name+'` ADD `'+field+'` '+self.fields[field].get_type_sql(), [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' ADD '+self.field_quote+field+self.field_quote+' '+self.fields[field].get_type_sql(), [], self.connection_id)
|
||||
|
||||
for field in fields_to_add_index:
|
||||
print("---Adding index to "+field+" in "+self.name)
|
||||
self.query('CREATE INDEX `index_'+self.name+'_'+field+'` ON '+self.name+' (`'+field+'`);', [], self.connection_id)
|
||||
self.query('CREATE INDEX '+self.field_quote+'index_'+self.name+'_'+field+self.field_quote+' ON '+self.name+' ('+self.field_quote+field+self.field_quote+');', [], self.connection_id)
|
||||
|
||||
for field in fields_to_add_constraint:
|
||||
|
||||
|
|
@ -1076,20 +1373,26 @@ class WebModel:
|
|||
|
||||
id_table_related=self.fields[field].table_id
|
||||
|
||||
self.query('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;', [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' ADD CONSTRAINT '+self.field_quote+field+'_'+self.name+'IDX'+self.field_quote+' FOREIGN KEY ( '+self.field_quote+field+self.field_quote+' ) REFERENCES '+self.field_quote+table_related+self.field_quote+' ('+self.field_quote+id_table_related+self.field_quote+') ON DELETE CASCADE ON UPDATE CASCADE;', [], self.connection_id)
|
||||
|
||||
for field in fields_to_add_unique:
|
||||
|
||||
print("---Adding unique to "+field+" in "+self.name)
|
||||
|
||||
self.query('ALTER TABLE `'+self.name+'` ADD UNIQUE (`'+field+'`)', [], self.connection_id)
|
||||
self.query('ALTER TABLE '+self.field_quote+self.name+self.field_quote+' ADD UNIQUE ('+self.field_quote+field+self.field_quote+')', [], self.connection_id)
|
||||
|
||||
|
||||
|
||||
# Method for drop sql tables and related
|
||||
|
||||
def drop(self):
|
||||
return self.query('DROP TABLE '+self.name, [], self.connection_id)
|
||||
"""Method for drop a table based in this model
|
||||
|
||||
Returns:
|
||||
sql_str (str): Return the sql query for drop the table represented by this model
|
||||
"""
|
||||
|
||||
return self.query('DROP TABLE '+self.field_quote+self.name+self.field_quote, [], self.connection_id)
|
||||
|
||||
#Return an array with all fields
|
||||
|
||||
|
|
@ -1099,6 +1402,20 @@ class WebModel:
|
|||
#Check of all fields in table.
|
||||
|
||||
def check_all_fields(self, dict_values, external_agent, yes_update=False, errors_set="insert"):
|
||||
"""Method for check all fields of a model for insert or update a row in table db.
|
||||
|
||||
Args:
|
||||
dict_values (dict): The dict of values to check
|
||||
external_agent (bool): If True, the query is considered manipulated by external agent and the checks are stricts, if not, checks are not stricts
|
||||
yes_update (bool): If True, the check need be done for update sql sentence, if False, the check is done for insert sql sentence
|
||||
errors_set (str): If insert value, the errors are set for insert sql statement, if update value, then the errors are set for update sql statement.
|
||||
|
||||
Returns:
|
||||
wrong (bool): Return False if checking is wrong. If not False returns a tuple with fields filtered, original values as values and values filtered how update_values
|
||||
fields (list): list with fields
|
||||
values (dict): dict with values
|
||||
update_values (dict): dict with updated values with checking
|
||||
"""
|
||||
|
||||
fields=[]
|
||||
values=[]
|
||||
|
|
@ -1115,7 +1432,7 @@ class WebModel:
|
|||
error=False
|
||||
|
||||
if yes_update==True:
|
||||
f_update=lambda field, value: "`"+field+"`=%s"
|
||||
f_update=lambda field, value: self.field_quote+field+self.field_quote+"=%s"
|
||||
else:
|
||||
f_update=lambda field, value: ""
|
||||
|
||||
|
|
@ -1227,6 +1544,7 @@ class WebModel:
|
|||
#Reset the require field in fields
|
||||
|
||||
def reset_require(self):
|
||||
"""Reset the require attribute in fields"""
|
||||
|
||||
for k, v in self.fields.items():
|
||||
|
||||
|
|
@ -1237,6 +1555,7 @@ class WebModel:
|
|||
#Reload the require field in fields
|
||||
|
||||
def reload_require(self):
|
||||
"""Reload the require field in fields"""
|
||||
|
||||
for k,r in self.fields.items():
|
||||
self.fields[k].required=r
|
||||
|
|
@ -1244,6 +1563,7 @@ class WebModel:
|
|||
#Choose all fields to updated
|
||||
|
||||
def set_valid_fields(self, fields={}):
|
||||
"""Choose all fields to updated"""
|
||||
|
||||
if len(fields)==0:
|
||||
fields=self.fields.keys()
|
||||
|
|
@ -1253,6 +1573,7 @@ class WebModel:
|
|||
#Create a form based in table.
|
||||
|
||||
def create_forms(self, arr_fields=[]):
|
||||
"""Create a form based in table."""
|
||||
|
||||
self.forms=OrderedDict()
|
||||
|
||||
|
|
@ -1270,6 +1591,12 @@ class WebModel:
|
|||
return arr_fields
|
||||
|
||||
def create_form_after(self, form_after, new_form):
|
||||
"""Create form after other form
|
||||
|
||||
Args:
|
||||
form_after (str): The name of the form where the new form is located next
|
||||
new_form (BaseForm): The BaseForm or derivated class used for create the new form.
|
||||
"""
|
||||
|
||||
new_dict=OrderedDict()
|
||||
|
||||
|
|
@ -1281,6 +1608,11 @@ class WebModel:
|
|||
self.forms=new_dict
|
||||
|
||||
def show_errors(self):
|
||||
"""Get all errors of model last operation.
|
||||
|
||||
Returns:
|
||||
error_txt (str): A string with all errors.
|
||||
"""
|
||||
|
||||
arr_error=[]
|
||||
error_txt=''
|
||||
|
|
@ -1301,6 +1633,11 @@ class WebModel:
|
|||
return error_txt
|
||||
|
||||
def collect_errors(self):
|
||||
"""Get all errors and save in dictionary
|
||||
|
||||
Returns:
|
||||
errors (dict): Return a dict where the key is the field where the error exists and value is the error text.
|
||||
"""
|
||||
|
||||
arr_error= {}
|
||||
error_txt=''
|
||||
|
|
@ -1318,12 +1655,15 @@ class WebModel:
|
|||
return arr_error
|
||||
|
||||
def safe_query(self):
|
||||
"""Method for reset require for fields.
|
||||
With this method you can make queries without real checks, except mysql injection safe variables."""
|
||||
|
||||
self.create_forms()
|
||||
self.reset_require()
|
||||
|
||||
|
||||
def close(self):
|
||||
"""Method for close sqlclass db connection"""
|
||||
|
||||
self.sqlclass.close()
|
||||
|
||||
|
|
@ -1341,6 +1681,7 @@ class WebModel:
|
|||
#del sqlclass.connection[key]
|
||||
@staticmethod
|
||||
def escape_sql(value):
|
||||
"""Manual escape for sql, you shouldn't use it"""
|
||||
|
||||
value=str(value)
|
||||
|
||||
|
|
@ -1354,6 +1695,11 @@ class WebModel:
|
|||
# Set post values from a post array
|
||||
|
||||
def set_post_values(self, post):
|
||||
"""Prepare a dict with values using fields keys how base
|
||||
|
||||
Returns:
|
||||
post (dict): Return a dict with values without checking anything.
|
||||
"""
|
||||
|
||||
for k in self.fields.keys():
|
||||
|
||||
|
|
@ -1373,6 +1719,6 @@ class QueryModel(WebModel):
|
|||
|
||||
self.label_general=self.name
|
||||
|
||||
self.order_by="ORDER BY `"+self.name+"`.`id` ASC"
|
||||
self.order_by="ORDER BY "+self.field_quote+self.name+self.field_quote+"."+self.field_quote+"id"+self.field_quote+" ASC"
|
||||
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
# A bottle plugin for send emails if
|
||||
|
||||
from settings import config
|
||||
from paramecio.citoplasma.sendmail import SendMail
|
||||
from paramecio.libraries.sendmail import SendMail
|
||||
import sys, traceback
|
||||
|
||||
email_failed=''
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
from paramecio.citoplasma.lists import SimpleList
|
||||
from paramecio.libraries.lists import SimpleList
|
||||
from bottle import request
|
||||
from paramecio.citoplasma.urls import add_get_parameters, redirect
|
||||
from paramecio.citoplasma.mtemplates import set_flash_message
|
||||
from paramecio.cromosoma.formsutils import show_form
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
from paramecio.libraries.urls import add_get_parameters, redirect
|
||||
from paramecio.libraries.mtemplates import set_flash_message
|
||||
from paramecio.libraries.db.formsutils import show_form
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
from collections import OrderedDict
|
||||
|
||||
class GenerateAdminClass:
|
||||
|
|
@ -49,6 +49,8 @@ class GenerateAdminClass:
|
|||
|
||||
self.url_redirect=self.url
|
||||
|
||||
self.pre_update=None
|
||||
|
||||
self.post_update=None
|
||||
|
||||
self.text_home=I18n.lang('common', 'home', 'Home')
|
||||
|
|
@ -126,6 +128,9 @@ class GenerateAdminClass:
|
|||
title_edit=I18n.lang('common', 'edit_new_item', 'Edit item')
|
||||
self.model.conditions=['WHERE `'+self.model.name+'`.`'+self.model.name_field_id+'`=%s', [getpostfiles.get['id']]]
|
||||
|
||||
if self.pre_update:
|
||||
getpostfiles.post=self.pre_update(self, getpostfiles.post)
|
||||
|
||||
if insert_row(getpostfiles.post):
|
||||
set_flash_message(I18n.lang('common', 'task_successful', 'Task successful'))
|
||||
|
||||
|
|
@ -209,6 +214,8 @@ class GenerateConfigClass:
|
|||
|
||||
self.template_insert='utils/insertform.phtml'
|
||||
|
||||
self.pre_update=None
|
||||
|
||||
self.post_update=None
|
||||
|
||||
self.text_home=I18n.lang('common', 'home', 'Home')
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.urls import add_get_parameters
|
||||
from paramecio.libraries.urls import add_get_parameters
|
||||
|
||||
class HierarchyLinks:
|
||||
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
import json, re
|
||||
from bottle import request, response
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.keyutils import create_key_encrypt
|
||||
from paramecio.libraries.sessionplugin import get_session
|
||||
from paramecio.libraries.keyutils import create_key_encrypt
|
||||
from bottle import HTTPResponse
|
||||
|
||||
no_csrf=False
|
||||
change_csrf=False
|
||||
change_csrf=True
|
||||
|
||||
try:
|
||||
|
||||
|
|
@ -84,15 +84,22 @@ class GetPostFiles:
|
|||
|
||||
self.post={}
|
||||
|
||||
try:
|
||||
if not request.json:
|
||||
|
||||
self.post=request.forms.decode('utf-8')
|
||||
try:
|
||||
|
||||
except:
|
||||
self.post=request.forms.decode('utf-8')
|
||||
|
||||
request.forms.recode_unicode=False
|
||||
self.post=request.forms.decode('utf-8')
|
||||
except:
|
||||
|
||||
request.forms.recode_unicode=False
|
||||
self.post=request.forms.decode('utf-8')
|
||||
|
||||
|
||||
else:
|
||||
self.post=request.json
|
||||
|
||||
#print(self.post.keys())
|
||||
if len(required_post)==0:
|
||||
required_post=self.post.keys()
|
||||
|
||||
|
|
@ -100,7 +107,7 @@ class GetPostFiles:
|
|||
self.post[post]=self.post.get(post, '')
|
||||
|
||||
s=get_session()
|
||||
|
||||
#print('s', s)
|
||||
if ignore_csrf_token==False and no_csrf==False:
|
||||
|
||||
if 'csrf_token' in s:
|
||||
|
|
@ -111,9 +118,9 @@ class GetPostFiles:
|
|||
|
||||
# Clean csrf_token
|
||||
|
||||
del s['csrf_token']
|
||||
#del s['csrf_token']
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
#raise NameError('Error: you need a valid csrf_token')
|
||||
raise HTTPResponse(body=json.dumps({'error_csrf': 1, 'error': 1, 'token_invalid': 1}), status=200, headers={'Content-type': 'application/json'})
|
||||
|
|
@ -124,7 +131,7 @@ class GetPostFiles:
|
|||
|
||||
del s['csrf_token']
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
|
||||
else:
|
||||
|
|
@ -160,8 +167,8 @@ def check_csrf(post):
|
|||
|
||||
del s['csrf_token']
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
else:
|
||||
#raise NameError('Error: you don\'t send any valid csrf_token')
|
||||
raise HTTPResponse(body=json.dumps({'error_csrf': 1, 'error': 1, 'token_invalid': 0}), status=200, headers={'Content-type': 'application/json'})
|
||||
raise HTTPResponse(body=json.dumps({'error_csrf': 1, 'error': 1, 'message': 'Error: csrf token invalid', 'token_invalid': 0}), status=200, headers={'Content-type': 'application/json'})
|
||||
212
paramecio/libraries/i18n.py
Normal file
212
paramecio/libraries/i18n.py
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from importlib import import_module
|
||||
from paramecio.libraries.sessionplugin import get_session
|
||||
import json
|
||||
from bottle import request
|
||||
import gettext
|
||||
import os
|
||||
|
||||
yes_session=False
|
||||
|
||||
i18n_module={}
|
||||
|
||||
def load_lang(*args):
|
||||
"""A function for load the lang module dinamically
|
||||
"""
|
||||
|
||||
for module in args:
|
||||
|
||||
lang_path=module[0]+'.i18n.'+module[1]
|
||||
|
||||
try:
|
||||
|
||||
i18n_module[lang_path]=import_module(lang_path)
|
||||
|
||||
pass
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
# here load the language
|
||||
|
||||
class PGetText:
|
||||
|
||||
# Dict where all gettext domain are saved -> domain=name, example, admin, libraries, pastafari2, etc...
|
||||
|
||||
l={}
|
||||
|
||||
def __init__(self, module_file):
|
||||
|
||||
module_dir=os.path.dirname(os.path.realpath(module_file))
|
||||
|
||||
module_name=os.path.basename(module_dir)
|
||||
|
||||
if module_name not in PGetText.l:
|
||||
|
||||
PGetText.l[module_name]={}
|
||||
|
||||
for i in I18n.dict_i18n:
|
||||
|
||||
if i not in PGetText.l[module_name]:
|
||||
|
||||
PGetText.l[module_name][i]=gettext.translation(module_name, module_dir+'/languages/', languages=[i], fallback=True)
|
||||
PGetText.l[module_name][i].install()
|
||||
|
||||
self.module=module_name
|
||||
|
||||
def gettext(self, text):
|
||||
|
||||
return PGetText.l[self.module][I18n.get_default_lang()].gettext(text)
|
||||
|
||||
def pgettext(module_file):
|
||||
|
||||
module=os.path.dirname(os.path.realpath(module_file))
|
||||
|
||||
base_name=os.path.dirname(os.path.realpath(module))
|
||||
|
||||
l=gettext.translation(os.path.basename(base_name), module+'/languages/', languages=I18n.get_default_lang(), fallback=True)
|
||||
|
||||
return l.gettext
|
||||
|
||||
class I18n:
|
||||
|
||||
"""Class for i18n tasks
|
||||
|
||||
Class for i18n tasks, how, strings for every language supported, for now are en-US and es-ES. You can add more languages adding
|
||||
|
||||
Attributes:
|
||||
default_lang (str): The default string lang used when get someone
|
||||
dict_i18n (list): The list with default languages. You can add more calling it static variable in settings/config.py
|
||||
|
||||
"""
|
||||
|
||||
default_lang='en-US'
|
||||
|
||||
dict_i18n=['en-US', 'es-ES']
|
||||
|
||||
l={}
|
||||
|
||||
def __init__(self, module):
|
||||
|
||||
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:
|
||||
symbol (str): The symbol used for identify the text string.
|
||||
text_default (str): The text default used. You have use how base for translations.
|
||||
"""
|
||||
|
||||
symbol=text_default[:60]
|
||||
|
||||
return I18n.lang(self.module, symbol, text_default, lang)
|
||||
|
||||
|
||||
#@staticmethod
|
||||
#def set_lang(code_lang):
|
||||
# if default_lang
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_default_lang():
|
||||
"""Static method for get the default lang"""
|
||||
|
||||
lang=I18n.default_lang
|
||||
|
||||
s=get_session()
|
||||
|
||||
lang=s.get('lang', lang)
|
||||
|
||||
return lang
|
||||
|
||||
@staticmethod
|
||||
def lang(module, symbol, text_default, lang=None):
|
||||
"""Static method for get a string from selected language
|
||||
|
||||
Static method used to get the string of the selected language. If there is no string in the selected language, it returns text_default.
|
||||
|
||||
Args:
|
||||
module (str): The module to which the translation string belongs
|
||||
symbol (str): Simple symbol that is useful for identify the string
|
||||
text_default (str): The text used by default when there are not translation in the selected language
|
||||
"""
|
||||
|
||||
if not lang:
|
||||
lang=I18n.get_default_lang()
|
||||
|
||||
I18n.l[lang]=I18n.l.get(lang, {})
|
||||
|
||||
I18n.l[lang][module]=I18n.l[lang].get(module, {})
|
||||
|
||||
I18n.l[lang][module][symbol]=I18n.l[lang][module].get(symbol, text_default)
|
||||
|
||||
return I18n.l[lang][module][symbol]
|
||||
|
||||
@staticmethod
|
||||
def extract_value(value):
|
||||
"""Static method for get values from json lang array
|
||||
|
||||
Args:
|
||||
value (json): Lang dict in json format
|
||||
"""
|
||||
|
||||
value=json.loads(value)
|
||||
|
||||
lang=I18n.get_default_lang()
|
||||
|
||||
if value[lang]!='':
|
||||
|
||||
return value[lang]
|
||||
|
||||
return value[I18n.default_lang]
|
||||
|
||||
@staticmethod
|
||||
def get_browser_lang():
|
||||
|
||||
return request.headers.get('Accept-Language', 'en-US')
|
||||
|
||||
@staticmethod
|
||||
def lang_json(module, symbol, text_default):
|
||||
|
||||
arr_final={}
|
||||
|
||||
for l in I18n.dict_i18n:
|
||||
arr_final[l]=I18n.lang(module, symbol, text_default, l)
|
||||
|
||||
return json.dumps(arr_final)
|
||||
|
||||
common_pgettext=PGetText(__file__)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from settings import config
|
||||
|
||||
def make_js_url(file_path, module):
|
||||
86
paramecio/libraries/keyutils.py
Normal file
86
paramecio/libraries/keyutils.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"""
|
||||
Parameciofm is a series of wrappers for bottlepy, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from hashlib import sha512, sha256
|
||||
from base64 import b64encode
|
||||
from os import urandom
|
||||
import string
|
||||
import secrets
|
||||
|
||||
# Functions for create random strings usando urandom
|
||||
|
||||
def create_key_encrypt(n=10):
|
||||
""" Simple function for create a random string
|
||||
|
||||
Simple function for create a random string based in sha512
|
||||
|
||||
Args:
|
||||
n (int): size of string random bytes (view urandom function in Python3 Help)
|
||||
"""
|
||||
|
||||
return sha512(urandom(n)).hexdigest()
|
||||
|
||||
def create_key_encrypt_256(n=10):
|
||||
|
||||
""" Simple function for create a random string
|
||||
|
||||
Simple function for create a random string based in sha256
|
||||
|
||||
Args:
|
||||
n (int): size of string random bytes (view urandom function in Python3 Help)
|
||||
"""
|
||||
|
||||
return sha256(urandom(n)).hexdigest()
|
||||
|
||||
def create_key(n=10):
|
||||
|
||||
""" Simple function for create a random string
|
||||
|
||||
Simple function for create a random string based in urandom function and base64 encoding
|
||||
|
||||
Args:
|
||||
n (int): size of string random bytes (view urandom function in Python3 Help)
|
||||
"""
|
||||
|
||||
rand_bytes=urandom(n)
|
||||
|
||||
return b64encode(rand_bytes).decode('utf-8')[0:-2]
|
||||
|
||||
def create_simple_password(n=14):
|
||||
|
||||
""" Based in python3 documentation for create passwords using secrets module
|
||||
|
||||
https://docs.python.org/3/library/secrets.html
|
||||
|
||||
Args:
|
||||
n (int): Number of random elements of the password
|
||||
|
||||
"""
|
||||
|
||||
password=''
|
||||
|
||||
alphabet=string.ascii_letters+string.digits+string.punctuation
|
||||
|
||||
while True:
|
||||
password=''.join(secrets.choice(alphabet) for i in range(n))
|
||||
if (any(c.islower() for c in password) and any(c.isupper() for c in password) and sum(c.isdigit() for c in password) >= 3):
|
||||
break
|
||||
|
||||
return password
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
#By default id is not showed
|
||||
|
||||
from paramecio.citoplasma.pages import Pages
|
||||
from paramecio.citoplasma.urls import add_get_parameters
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.httputils import GetPostFiles
|
||||
from paramecio.libraries.pages import Pages
|
||||
from paramecio.libraries.urls import add_get_parameters
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
from bottle import request
|
||||
import sys
|
||||
import re
|
||||
from paramecio.citoplasma.pages import Pages
|
||||
from paramecio.libraries.pages import Pages
|
||||
|
||||
class SimpleList:
|
||||
|
||||
|
|
@ -1,22 +1,58 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for Bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
from bottle import hook
|
||||
from mako.template import Template
|
||||
from mako.lookup import TemplateLookup
|
||||
from paramecio.citoplasma.urls import make_url, make_url_domain, make_media_url, make_media_url_module, add_get_parameters
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.adminutils import make_admin_url
|
||||
from paramecio.cromosoma.formsutils import csrf_token
|
||||
from paramecio.citoplasma.js import make_js_url
|
||||
from paramecio.libraries.urls import make_url, make_url_domain, make_media_url, make_media_url_module, add_get_parameters
|
||||
from paramecio.libraries.i18n import I18n, PGetText
|
||||
from paramecio.libraries.sessionplugin import get_session
|
||||
from paramecio.libraries.adminutils import make_admin_url
|
||||
from paramecio.libraries.db.formsutils import csrf_token
|
||||
from paramecio.libraries.js import make_js_url
|
||||
from settings import config
|
||||
from os import path
|
||||
from collections import OrderedDict
|
||||
from paramecio.wsgiapp import app
|
||||
|
||||
# Preparing envs for views of modules, and views of
|
||||
|
||||
def env_theme(module, cache_enabled=True, cache_impl='', cache_args={}, module_directory="./tmp/modules"):
|
||||
|
||||
"""Function for create an environment for mako templates
|
||||
|
||||
Function for create an environment for mako templates. Really is a shortcut for TemplateLookup mako function. You can define cache options and module_directory for templates compiled
|
||||
|
||||
Args:
|
||||
module (str): The module where the templates can be founded
|
||||
cache_enabled (boolean): If True then mako template cache is enabled, is False, mako template cache is disabled.
|
||||
cache_args (dict): Cache Args dict parameter for TemplateLookup function from Mako templates. View Mako Templates documentation.
|
||||
module_directory (str): Module directory parameter for TemplateLookup function from Mako templates. View Mako Templates documentation.
|
||||
|
||||
Returns:
|
||||
|
||||
template (TemplateLookup): Return TemplateLookup object
|
||||
|
||||
"""
|
||||
|
||||
ext=module[len(module)-3:]
|
||||
|
||||
if ext=='.py':
|
||||
|
|
@ -50,12 +86,33 @@ def preload_templates(template_files, env):
|
|||
|
||||
return templates
|
||||
|
||||
def url_for(name, **kwargs):
|
||||
|
||||
return app.get_url(name, **kwargs)
|
||||
|
||||
class PTemplate:
|
||||
"""A class used how shortcuts for Mako template functions.
|
||||
"""
|
||||
|
||||
templates_loaded={}
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
environment (TemplateLookup): A TemplateLookup object generated with env_theme function
|
||||
|
||||
Attributes:
|
||||
autoescape_ext (set): A set of extensions file where automatic autoescape is used
|
||||
environment (TemplateLookup): A TemplateLookup object generated with env_theme function
|
||||
filters (list): A list of functions used for add filters to your templates.
|
||||
js (list): A list of javascript sources for generate js html load tags.
|
||||
|
||||
"""
|
||||
|
||||
# A simple method used in internal things of paramecio
|
||||
|
||||
self.show_basic_template=True
|
||||
|
|
@ -130,6 +187,32 @@ class PTemplate:
|
|||
|
||||
self.env=environment
|
||||
|
||||
# Loading language domain for gettext in templates
|
||||
|
||||
base_name=path.dirname(path.realpath(__file__))
|
||||
|
||||
module_env=self.env.directories[1].replace('/templates', '')
|
||||
|
||||
self.l=PGetText(module_env+'/app.py')
|
||||
|
||||
self.add_filter(self._)
|
||||
|
||||
self.i18n=I18n(base_name)
|
||||
|
||||
self.add_filter(self.i18n.slang)
|
||||
|
||||
self.add_filter(self.i18n.tlang)
|
||||
|
||||
#self.url_for=lambda name: app.get_url(name)
|
||||
#x = lambda a : a + 10
|
||||
#print(self.url_for)
|
||||
|
||||
self.add_filter(url_for)
|
||||
|
||||
def _(self, text):
|
||||
|
||||
return self.l.gettext(text)
|
||||
|
||||
#self.auto_reload=True
|
||||
|
||||
# Clean HeaderHTML
|
||||
|
|
@ -144,6 +227,12 @@ class PTemplate:
|
|||
|
||||
def guess_autoescape(self, template_name):
|
||||
|
||||
"""Simple helper method for get an extension from filename
|
||||
|
||||
Args:
|
||||
template_name (str): The template name
|
||||
"""
|
||||
|
||||
if template_name is None or '.' not in template_name:
|
||||
return False
|
||||
|
||||
|
|
@ -180,6 +269,18 @@ class PTemplate:
|
|||
|
||||
def load_template(self, template_file, **arguments):
|
||||
|
||||
"""Load a mako template and return the result
|
||||
|
||||
Load a mako template and return the results with different arguments applied
|
||||
|
||||
Args:
|
||||
template_file (str): The name of template file. The template is searched using configuration defined in self.env
|
||||
**arguments (mixed): Extra arguments with variables passed to template
|
||||
|
||||
Returns:
|
||||
template (str): Return a template rendered using mako class from self.env
|
||||
"""
|
||||
|
||||
template = self.env.get_template(template_file)
|
||||
|
||||
arguments.update(self.filters)
|
||||
|
|
@ -190,6 +291,18 @@ class PTemplate:
|
|||
|
||||
def render_template(self, template_file, **arguments):
|
||||
|
||||
"""Experimental method for parse a template
|
||||
|
||||
Experimental method for parse a template, similar to load_template but try cache the template loaded
|
||||
|
||||
Args:
|
||||
template_file (str): The name of template file. The template is searched using configuration defined in self.env
|
||||
**arguments (mixed): Extra arguments with variables passed to template
|
||||
|
||||
Returns:
|
||||
dummy (str): Dummy return necessary because mako expect return something
|
||||
"""
|
||||
|
||||
if not str(self.env.directories)+'/'+template_file in PTemplate.templates_loaded:
|
||||
PTemplate.templates_loaded[str(self.env.directories)+'/'+template_file]=self.env.get_template(template_file)
|
||||
|
||||
|
|
@ -335,7 +448,7 @@ class HeaderHTML:
|
|||
|
||||
s['flash']=''
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
return message
|
||||
|
||||
|
|
@ -345,7 +458,7 @@ def set_flash_message(message):
|
|||
|
||||
s['flash']=message
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
def qf(text):
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from math import ceil, floor
|
||||
from paramecio.citoplasma.urls import add_get_parameters
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.libraries.urls import add_get_parameters
|
||||
from paramecio.libraries.i18n import I18n
|
||||
|
||||
class Pages:
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
from paramecio.modules.admin.models.admin import UserAdmin
|
||||
from bottle import request
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.citoplasma.urls import redirect, make_url
|
||||
from paramecio.cromosoma.webmodel import WebModel
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.urls import redirect, make_url
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
import inspect
|
||||
|
||||
class LoginPlugin(object):
|
||||
|
|
@ -1,4 +1,23 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Parameciofm is a series of wrappers for bottle.py, mako and others and construct a simple headless cms.
|
||||
|
||||
Copyright (C) 2024 Antonio de la Rosa Caballero
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
import os
|
||||
import smtplib
|
||||
import mimetypes
|
||||
|
|
@ -13,6 +32,8 @@ import ssl as ssl_module
|
|||
import sys
|
||||
|
||||
class SendMail:
|
||||
"""Class for send email
|
||||
"""
|
||||
|
||||
port=587
|
||||
|
||||
|
|
@ -25,6 +46,20 @@ class SendMail:
|
|||
#ssl=True
|
||||
|
||||
def __init__(self, ssl=True):
|
||||
"""Class for send email
|
||||
|
||||
Class for send email using standard python library
|
||||
|
||||
Attributes:
|
||||
port (int): The port used for send email, by default 587
|
||||
host (str): The hostname of mail server used for send the email
|
||||
username (str): The username for login in mail server
|
||||
password (str): The password for login in mail server
|
||||
smtp (smtplib.SMTP): The python SMTP object used for send emails
|
||||
txt_error: (str): If error, is saved in this attribute
|
||||
|
||||
"""
|
||||
|
||||
|
||||
self.smtp=None #smtplib.SMTP(host=self.host, port=self.port)
|
||||
self.txt_error=''
|
||||
|
|
@ -79,9 +114,9 @@ class SendMail:
|
|||
|
||||
return False
|
||||
|
||||
except smtplib.SMTPAuthenticationError:
|
||||
except smtplib.SMTPAuthenticationError as eauth:
|
||||
|
||||
self.txt_error='Error: cannot login. Wrong username or password'
|
||||
self.txt_error='Error: cannot login. Wrong username or password '+eauth.__str__()
|
||||
|
||||
return False
|
||||
|
||||
|
|
@ -94,6 +129,18 @@ class SendMail:
|
|||
return True
|
||||
|
||||
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
|
||||
|
||||
Args:
|
||||
from_address (str): The adress used for send the email
|
||||
to_address (list): A list of emails where the email will be sended.
|
||||
subject (str): The subject of the email
|
||||
message (str): The content of the message
|
||||
content_type (str): The type of mail content, can be plain or html.
|
||||
attachments (list): A list with a serie of file paths for attach to the email.
|
||||
"""
|
||||
|
||||
if self.smtp==None:
|
||||
if not self.connect():
|
||||
|
|
@ -174,12 +221,14 @@ class SendMail:
|
|||
return True
|
||||
|
||||
def quit(self):
|
||||
"""Function used when you need quit connection for any reason"""
|
||||
|
||||
if self.smtp!=None:
|
||||
self.smtp.quit()
|
||||
self.smtp=None
|
||||
|
||||
def __del__(self):
|
||||
"""Method for clean the connection when the object is closed"""
|
||||
|
||||
if self.smtp!=None:
|
||||
|
||||
159
paramecio/libraries/sessionplugin.py
Normal file
159
paramecio/libraries/sessionplugin.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
from bottle import request, response
|
||||
from itsdangerous.url_safe import URLSafeTimedSerializer
|
||||
from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
|
||||
try:
|
||||
|
||||
from settings import config
|
||||
|
||||
except:
|
||||
|
||||
class config:
|
||||
cookie_name='paramecio.session'
|
||||
key_encrypt=create_key_encrypt_256(30)
|
||||
session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'}
|
||||
|
||||
import inspect
|
||||
|
||||
|
||||
|
||||
class Session(dict):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
self.update(*args, **kwargs)
|
||||
|
||||
self.changed=False
|
||||
|
||||
def __setitem__(self, item, value):
|
||||
|
||||
super(Session, self).__setitem__(item, value)
|
||||
self.changed=True
|
||||
|
||||
def get_session():
|
||||
|
||||
return request.environ.get('session', {})
|
||||
|
||||
def session_plugin(callback):
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
cookie=request.get_cookie(config.cookie_name)
|
||||
|
||||
safe=None
|
||||
|
||||
if not cookie:
|
||||
session=Session()
|
||||
else:
|
||||
|
||||
safe=URLSafeTimedSerializer(config.key_encrypt)
|
||||
try:
|
||||
session=Session(safe.loads(cookie))
|
||||
|
||||
if type(session).__name__!='Session':
|
||||
session=Session()
|
||||
|
||||
except:
|
||||
session=Session()
|
||||
|
||||
if 'session' in kwargs:
|
||||
kwargs['session']=session
|
||||
|
||||
#For compatibility with old sessions server-side style.
|
||||
|
||||
request.environ['session']=session
|
||||
|
||||
rv=callback(*args, **kwargs)
|
||||
|
||||
if session.changed:
|
||||
#print('changed')
|
||||
if not safe:
|
||||
safe=URLSafeTimedSerializer(config.key_encrypt)
|
||||
|
||||
#if not max_age:
|
||||
response.set_cookie(config.cookie_name, safe.dumps(session), path=config.session_opts['session.path'], httponly=True)
|
||||
|
||||
return rv
|
||||
|
||||
return wrapper
|
||||
|
||||
class SessionPlugin(object):
|
||||
|
||||
name = 'session'
|
||||
api = 2
|
||||
|
||||
def __init__(self, keyword='session'):
|
||||
|
||||
self.keyword=keyword
|
||||
|
||||
|
||||
def setup(self, app):
|
||||
''' Make sure that other installed plugins don't affect the same keyword argument.'''
|
||||
for other in app.plugins:
|
||||
if not isinstance(other, SessionPlugin): continue
|
||||
if other.keyword == self.keyword:
|
||||
raise PluginError("Found another login plugin with "\
|
||||
"conflicting settings (non-unique keyword).")
|
||||
|
||||
def apply(self, callback, context):
|
||||
|
||||
# Test if the original callback accepts a 'session' keyword.
|
||||
# Ignore it if it does not need a login handle.
|
||||
|
||||
conf = context.config.get('session') or {}
|
||||
|
||||
keyword = conf.get('keyword', self.keyword)
|
||||
|
||||
args = inspect.getfullargspec(context.callback)[0]
|
||||
|
||||
"""
|
||||
if keyword not in args:
|
||||
return callback
|
||||
"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
cookie=request.get_cookie(config.cookie_name)
|
||||
|
||||
safe=None
|
||||
|
||||
if not cookie:
|
||||
session=Session()
|
||||
else:
|
||||
|
||||
safe=URLSafeTimedSerializer(config.key_encrypt)
|
||||
try:
|
||||
session=Session(safe.loads(cookie))
|
||||
|
||||
if type(session).__name__!='Session':
|
||||
session=Session()
|
||||
|
||||
except:
|
||||
session=Session()
|
||||
|
||||
#if 'session' in kwargs:
|
||||
kwargs['session']=session
|
||||
|
||||
#For compatibility with old sessions server-side style.
|
||||
|
||||
request.environ['session']=session
|
||||
|
||||
rv=callback(*args, **kwargs)
|
||||
|
||||
if session.changed:
|
||||
#print('changed')
|
||||
if not safe:
|
||||
safe=URLSafeTimedSerializer(config.key_encrypt)
|
||||
|
||||
#if not max_age:
|
||||
response.set_cookie(config.cookie_name, safe.dumps(session), path=config.session_opts['session.path'], httponly=True)
|
||||
|
||||
return rv
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.citoplasma.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
from oslo_concurrency import lockutils
|
||||
from paramecio.libraries.keyutils import create_key_encrypt, create_key_encrypt_256, create_key
|
||||
#from oslo_concurrency import lockutils
|
||||
|
||||
|
||||
try:
|
||||
|
|
@ -15,7 +15,7 @@ except:
|
|||
key_encrypt=create_key_encrypt_256(30)
|
||||
session_opts={'session.data_dir': 'sessions', 'session.type': 'file', 'session.path': 'paramecio'}
|
||||
|
||||
from itsdangerous import JSONWebSignatureSerializer
|
||||
#from itsdangerous import JSONWebSignatureSerializer
|
||||
from bottle import request, response
|
||||
import os
|
||||
try:
|
||||
|
|
@ -142,40 +142,8 @@ elif config.session_opts['session.type']=='redis':
|
|||
|
||||
import redis
|
||||
|
||||
def load_session(token):
|
||||
|
||||
s={}
|
||||
|
||||
r=redis.StrictRedis(host=config.session_opts['session.host'], port=config.session_opts['session.port'], db=config.session_opts['session.db'])
|
||||
|
||||
value=r.get(token)
|
||||
|
||||
if not value:
|
||||
s={'token': token}
|
||||
else:
|
||||
try:
|
||||
s=json.loads(value.decode('utf-8'))
|
||||
except:
|
||||
s={'token': token}
|
||||
return s
|
||||
|
||||
def save_session(token, session):
|
||||
|
||||
r=redis.StrictRedis(host=config.session_opts['session.host'], port=config.session_opts['session.port'], db=config.session_opts['session.db'])
|
||||
|
||||
r.set(token, json.dumps(session))
|
||||
|
||||
def after_session():
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
def generate_session(session={}, max_age=None):
|
||||
|
||||
#secret=URLSafeSerializer(config.key_encrypt)
|
||||
|
||||
#session=secret.dumps(session)
|
||||
|
||||
token=create_key(30).replace('/', '#')
|
||||
|
||||
s={'token': token}
|
||||
|
|
@ -183,9 +151,76 @@ else:
|
|||
# Bug in python 3.6, if you put max_age how None, is passed to header cookie.
|
||||
|
||||
if not max_age:
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'])
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], httponly=True)
|
||||
else:
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], max_age=max_age)
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], max_age=max_age, httponly=True)
|
||||
|
||||
request.environ['session']=s
|
||||
|
||||
save_session(token, s)
|
||||
|
||||
request.environ['session']=s
|
||||
|
||||
return s
|
||||
|
||||
def regenerate_session():
|
||||
|
||||
token=create_key(30).replace('/', '#')
|
||||
|
||||
s={'token': token}
|
||||
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], httponly=True)
|
||||
|
||||
save_session(token, s)
|
||||
|
||||
request.environ['session']=s
|
||||
|
||||
return ParamecioSession(s)
|
||||
|
||||
def load_session(token):
|
||||
|
||||
s={}
|
||||
|
||||
r=redis.Redis(host=config.session_opts['session.host'], port=config.session_opts['session.port'], decode_responses=True)
|
||||
|
||||
value=r.get(token)
|
||||
|
||||
r.close()
|
||||
|
||||
if not value:
|
||||
s={'token': token}
|
||||
else:
|
||||
try:
|
||||
s=json.loads(value)
|
||||
except:
|
||||
s={'token': token}
|
||||
return s
|
||||
|
||||
def save_session(token, session):
|
||||
|
||||
r=redis.Redis(host=config.session_opts['session.host'], port=config.session_opts['session.port'])
|
||||
|
||||
r.set(token, json.dumps(session))
|
||||
|
||||
r.close()
|
||||
|
||||
def after_session():
|
||||
pass
|
||||
|
||||
else:
|
||||
|
||||
def generate_session(session={}, max_age=None):
|
||||
|
||||
token=create_key(30).replace('/', '#')
|
||||
|
||||
s={'token': token}
|
||||
|
||||
# Bug in python 3.6, if you put max_age how None, is passed to header cookie.
|
||||
|
||||
if not max_age:
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], httponly=True)
|
||||
else:
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], max_age=max_age, httponly=True)
|
||||
#Set-Cookie: phango_session=n2ro4lghim75p8vjseb5v3lhap; path=/experiment2/
|
||||
#response.set_header('Set-Cookie', '%s=%s; path=%s' % (config.cookie_name, token, config.session_opts['session.path']) )
|
||||
|
||||
|
|
@ -205,7 +240,7 @@ else:
|
|||
|
||||
s={'token': token}
|
||||
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'])
|
||||
response.set_cookie(config.cookie_name, token, path=config.session_opts['session.path'], httponly=True)
|
||||
|
||||
file_session=config.session_opts['session.data_dir']+'/'+token+'_session'
|
||||
|
||||
|
|
@ -237,7 +272,7 @@ else:
|
|||
|
||||
return s
|
||||
|
||||
@lockutils.synchronized('not_thread_safe')
|
||||
#@lockutils.synchronized('not_thread_safe')
|
||||
def save_session(token, session, create_file=False):
|
||||
|
||||
file_session=config.session_opts['session.data_dir']+'/'+token+'_session'
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
from jinja2 import Template, Environment, FileSystemLoader
|
||||
from paramecio.citoplasma.urls import make_url, make_media_url, make_media_url_module, add_get_parameters
|
||||
from paramecio.citoplasma.i18n import I18n
|
||||
from paramecio.citoplasma.sessions import get_session
|
||||
from paramecio.libraries.urls import make_url, make_media_url, make_media_url_module, add_get_parameters
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from settings import config
|
||||
|
||||
# Preparing envs for views of modules, and views of
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
${add_js_home_local('jquery.min.js', 'admin')}
|
||||
${add_js_home_local('spectrum.js', 'admin')}
|
||||
${add_css_home_local('spectrum.css', 'admin')}
|
||||
<%
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue