diff --git a/LICENSE.txt b/LICENSE.txt index 10926e8..be3f7b2 100644 --- a/LICENSE.txt +++ b/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. + Copyright (C) 2007 Free Software Foundation, Inc. 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) 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 . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . 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: - - Copyright (C) - 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 -. - - 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 -. - +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md index f8f7473..55574f9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Also, you need the next software installed in your os: ### Python 3.10 or later. -Paramecio should work fine in 3.5-3.9 but is tested in 3.10 and 3.11 python 3 versions actually. +Paramecio should work fine in 3.5-3.9 but is tested in 3.10, 3.11 and 3.12 python 3 versions actually. In Debian and Ubuntu you can install Python 3 using the next command: `apt-get install python3`. @@ -20,7 +20,7 @@ In Fedora and other Red Hat derived distros you can use `yum install python3`. I ### MySQL or MariaDB database servers. -MariaDB 10.6 and later are recommended but is compatible with MariaDB 10. +MariaDB 10.6 and later are recommended but is compatible with previous versions. In Debian and Ubuntu you can install MariaDB using the next command: `apt-get install mariadb-server`. @@ -53,7 +53,7 @@ You can install the framework using the next command in your server: or if you want development version: -`pip3 install git+https://git.cuchulu.com/absurdo/paramecio2fm.git` +`pip3 install git+https://git.cuchulu.com/paramecio/paramecio2fm.git` This command will install in your server paramecio framework with its dependencies. @@ -67,3 +67,6 @@ If you get an error in your installation of dependencies how MarkupSafe or SqlAl `apt-get install python3-mako python3-sqlalchemy` and try pip3 command again. +## Documentation + +You can find paramecio2 documentation in this [site](https://docs.cuchulu.com/paramecio2/) diff --git a/README.rst b/README.rst index 325f3e4..8583253 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ ========================================================== -Paramecio is a simple framework based in bottle and mako. +Paramecio is a simple framework based in flask and mako. ========================================================== diff --git a/paramecio2/__init__.py b/paramecio2/__init__.py new file mode 100644 index 0000000..9c5d19f --- /dev/null +++ b/paramecio2/__init__.py @@ -0,0 +1,19 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2025 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + diff --git a/paramecio2/app.py b/paramecio2/app.py index 21b3648..51affe3 100644 --- a/paramecio2/app.py +++ b/paramecio2/app.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from flask import Flask, session, url_for, request, send_file, abort from settings import config from importlib import import_module @@ -5,6 +24,28 @@ import os import sys import inspect from paramecio2.libraries.datetime import set_timezone +try: + import ujson as json +except: + import json + +flask_session=False + +if hasattr(config, 'flask_session'): + + flask_session=config.flask_session + + if flask_session: + + from flask_session import Session + from redis import Redis + SESSION_TYPE='redis' + SESSION_REDIS=Redis(host='localhost', port=6379) + +swagger_app=False + +if hasattr(config, 'swagger_app'): + swagger_app=config.swagger_app def start_app(): @@ -46,11 +87,17 @@ def start_app(): workdir=os.getcwd() arr_module_path={} - - # Load blueprints + + # Load blueprints from json file (for installed modules using paramecio2 utilities) + + if os.path.isfile('./settings/modules.json'): + + with open('./settings/modules.json') as f: + json_apps=json.loads(f.read()) + config.apps=json_apps | config.apps for key_app, added_app in config.apps.items(): - + controller_path=import_module(added_app[0]) controller_base=os.path.dirname(controller_path.__file__) @@ -59,9 +106,14 @@ def start_app(): app_name=getattr(controller_path, added_app[1]) + #print(os.path.dirname(sys.modules[added_app[0]].__file__)) + #print(key_app) + + arr_module_path[key_app]=os.path.dirname(sys.modules[added_app[0]].__file__) + for controller in dir_controllers: - if controller.find('.py')!=-1 and controller.find('__init__')==-1: + if controller.find('.py')!=-1 and not controller.startswith('__'): controller_py=controller.replace('.py', '') @@ -69,7 +121,8 @@ def start_app(): a=import_module(module_app) - arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__) + #arr_module_path[key_app]=os.path.dirname(sys.modules[module_app].__file__) + if config.application_root=='/': app.register_blueprint(app_name, url_prefix=added_app[2]) else: @@ -95,11 +148,23 @@ def start_app(): if not os.path.isfile(file_path): #file_path=workdir+'/modules/'+module+'/media/'+media_file + + if not module in arr_module_path: + abort(404) + file_path=arr_module_path[module]+'/media/'+media_file - + if not os.path.isfile(file_path): abort(404) return send_file(file_path) + if swagger_app: + from flasgger import Swagger + swagger=Swagger(app) + + if flask_session: + app.config.from_object(__name__) + Session(app) + return app diff --git a/paramecio2/console.py b/paramecio2/console.py index 92e4877..ded9755 100755 --- a/paramecio2/console.py +++ b/paramecio2/console.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import argparse import os import shutil @@ -22,7 +41,7 @@ def start(): parser.add_argument('--symlink', help='Set if create direct symlink to paramecio in new site', action='store_true') - parser.add_argument('--tests', help='Create a symlink to tests for check into paramecio site', action='store_true') + #parser.add_argument('--tests', help='Create a symlink to tests for check into paramecio site', action='store_true') # Options for deploy @@ -30,9 +49,9 @@ def start(): parser.add_argument('--folder', help='If you deploy in a subdirectory, set it, without beggining and ending slashes', required=False) - parser.add_argument('--host', help='The host ip or domain where the app is binded', required=False) + #parser.add_argument('--host', help='The host ip or domain where the app is binded', required=False) - parser.add_argument('--port', help='Change the default port 8080 to other number. Use 80 is not recommended, use 80 for the proxy server how nginx or apache', required=False) + #parser.add_argument('--port', help='Change the default port 8080 to other number. Use 80 is not recommended, use 80 for the proxy server how nginx or apache', required=False) args=parser.parse_args() @@ -87,13 +106,15 @@ def start(): except: print('Error: cannot symlink paramecio in new site') - + + """ if args.tests==True: try: os.symlink(workdir, args.path+'/paramecio2/', True) except: print('Error: cannot symlink paramecio2 in new site') + """ with open(path_settings+'/config.py', 'r') as f: conf=f.read() @@ -351,7 +372,7 @@ def start(): models_files=os.listdir(models_path) - m=re.compile(".*\.py$") + m=re.compile(r".*\.py$") underscore=re.compile("^__.*") diff --git a/paramecio2/frontend/app.py b/paramecio2/frontend/app.py index a967bba..6860051 100644 --- a/paramecio2/frontend/app.py +++ b/paramecio2/frontend/app.py @@ -1,3 +1,25 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.app import start_app app=start_app() + +application=app + diff --git a/paramecio2/index.py b/paramecio2/index.py index a967bba..d3f111c 100644 --- a/paramecio2/index.py +++ b/paramecio2/index.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.app import start_app app=start_app() diff --git a/paramecio2/libraries/__init__.py b/paramecio2/libraries/__init__.py index e69de29..2616e97 100644 --- a/paramecio2/libraries/__init__.py +++ b/paramecio2/libraries/__init__.py @@ -0,0 +1,18 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" diff --git a/paramecio2/libraries/check_i18n.py b/paramecio2/libraries/check_i18n.py index c384d90..2082e51 100644 --- a/paramecio2/libraries/check_i18n.py +++ b/paramecio2/libraries/check_i18n.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import argparse import os, sys @@ -9,6 +28,7 @@ import re from pathlib import Path from importlib import import_module from paramecio2.libraries.i18n import I18n +from paramecio2.libraries.slugify import slugify try: from settings import config except: @@ -19,12 +39,18 @@ except: """Command line utility for extract I18n.lang strings from html templates and .py files """ -pattern=re.compile('^\w+\.(py|html|phtml|js)$') +pattern=re.compile(r'^\w+\.(py|html|phtml|js)$') -ignored=re.compile('^[__|\.].*$') +ignored=re.compile(r'^[__|\.].*$') -lang_p=re.compile("I18n\.lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)") -lang_t=re.compile("\${lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)\}") +lang_p=re.compile(r"I18n\.lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)") +lang_t=re.compile(r"\${lang\('(.*?)',\s+'(.*?)',\s+'(.*?)'\)\}") + +lang_s=re.compile(r"i18n\.slang\('(.*?)',\s+'(.*?)'\)") +lang_ts=re.compile(r"\${slang\('(.*?)',\s+'(.*?)'\)\}") + +lang_tl=re.compile(r"i18n\.tlang\('(.*?)'\)") +lang_ttl=re.compile(r"\${tlang\('(.*?)'\)\}") tmp_lang={} @@ -34,12 +60,16 @@ def start(): global lang_p global lang_t + global lang_s + global lang_tl + global lang_ts + global lang_ttl # Module to search a file where save the file. parser = argparse.ArgumentParser(description='A tool for create python language files') - parser.add_argument('--module', help='The module where search lang files', required=False) + parser.add_argument('--module', help='The module where search lang files', required=True) args = parser.parse_args() @@ -57,9 +87,9 @@ def start(): module_base=os.path.basename(args.module) - lang_p=re.compile("I18n\.lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)") + lang_p=re.compile(r"I18n\.lang\('("+module_base+r"?)',\s+'(.*?)',\s+'(.*?)'\)") #lang_t=re.compile("\${lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)\}") - lang_t=re.compile("lang\('("+module_base+"?)',\s+'(.*?)',\s+'(.*?)'\)") + lang_t=re.compile(r"lang\('("+module_base+r"?)',\s+'(.*?)',\s+'(.*?)'\)") if not os.path.isdir(path): @@ -67,7 +97,7 @@ def start(): exit(1) - scandir(path, path_save) + scandir(path, path_save, module_base) #Save the files @@ -136,7 +166,7 @@ def start(): pass -def scandir(path, module_search=''): +def scandir(path, module_search='', module_base=None): list=os.listdir(path) @@ -158,6 +188,19 @@ def scandir(path, module_search=''): match_p=lang_p.findall(line) match_t=lang_t.findall(line) + match_s=lang_s.findall(line) + match_ts=lang_ts.findall(line) + + match_tl=lang_tl.findall(line) + match_ttl=lang_ttl.findall(line) + + """ + global lang_s + global lang_tl + global lang_ts + global lang_ttl + """ + if match_p!=None: for m in match_p: @@ -188,7 +231,45 @@ def scandir(path, module_search=''): tmp_lang[module]=tmp_lang.get(module, {}) tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default) - + + if module_base: + + if match_s!=None: + + for m in match_s: + module=base_module + symbol=m[0] + text_default=m[1] + + tmp_lang[module]=tmp_lang.get(module, {}) + tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default) + + for m in match_ts: + module=base_module + symbol=m[0] + text_default=m[1] + + tmp_lang[module]=tmp_lang.get(module, {}) + tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default) + + + for m in match_tl: + module=base_module + symbol=slugify(m[0]) + text_default=m[0] + + tmp_lang[module]=tmp_lang.get(module, {}) + tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default) + + for m in match_ttl: + module=base_module + symbol=slugify(m[0][:40]) + text_default=m[0] + + tmp_lang[module]=tmp_lang.get(module, {}) + tmp_lang[module][symbol]=tmp_lang[module].get(symbol, text_default) + + f.close() diff --git a/paramecio2/libraries/config_admin.py b/paramecio2/libraries/config_admin.py index b022c53..a688564 100644 --- a/paramecio2/libraries/config_admin.py +++ b/paramecio2/libraries/config_admin.py @@ -1,3 +1,21 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" # Variable base for admin modules diff --git a/paramecio2/libraries/datetime.py b/paramecio2/libraries/datetime.py index c33d451..d761ba3 100644 --- a/paramecio2/libraries/datetime.py +++ b/paramecio2/libraries/datetime.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import time from datetime import date, datetime, tzinfo import arrow diff --git a/paramecio2/libraries/db/corefields.py b/paramecio2/libraries/db/corefields.py index 5d41e55..80f838f 100644 --- a/paramecio2/libraries/db/corefields.py +++ b/paramecio2/libraries/db/corefields.py @@ -1,7 +1,27 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.webmodel import PhangoField from paramecio2.libraries.db import coreforms from paramecio2.libraries.i18n import I18n -from bs4 import BeautifulSoup +#from bs4 import BeautifulSoup +import bleach class IntegerField(PhangoField): @@ -18,6 +38,12 @@ class IntegerField(PhangoField): super(IntegerField, self).__init__(name, size, required) self.default_value=0 + self.type_sql='int({})'.format(self.size) + + self.default_value=0 + self.jtype='integer' + self.jformat='int64' + self.jexample='1' def check(self, value): @@ -62,6 +88,11 @@ class BigIntegerField(IntegerField): """ + def __init__(self, name, size=11, required=False): + + super().__init__(name, size, required) + self.type_sql='bigint({})'.format(self.size) + def get_type_sql(self): """Method for return the sql code for this type @@ -87,6 +118,10 @@ class FloatField(PhangoField): self.error_default="The value is zero" self.default_value=0 + self.type_sql='float'.format(self.size) + + self.jtype='number' + self.jformat='float' def check(self, value): @@ -127,6 +162,13 @@ class FloatField(PhangoField): class DecimalField(FloatField): """PhangoField field for Decimals fields.""" + def __init__(self, name, size=11, required=False): + + super().__init__(name, size, required) + self.type_sql='decimal(20,2)' + + self.jtype='number' + def get_type_sql(self): return 'DECIMAL(20, 2) NOT NULL DEFAULT "0"' @@ -134,6 +176,14 @@ class DecimalField(FloatField): class DoubleField(FloatField): """PhangoField field for Double fields.""" + def __init__(self, name, size=11, required=False): + + super().__init__(name, size, required) + self.type_sql='double' + + self.jtype='number' + self.jformat='double' + def get_type_sql(self): return 'DOUBLE NOT NULL DEFAULT "0"' @@ -162,6 +212,8 @@ class TextField(PhangoField): super().__init__(name, 11, required) + self.type_sql='text' + self.set_default='NOT NULL' def get_type_sql(self): @@ -177,7 +229,21 @@ class LongTextField(TextField): Class used for text fields, use LONGTEXT sql type for the this field. """ + + def __init__(self, name, required=False): + """Init TextField class different to standard PhangoField + Args: + name (str): The name of new field + required (bool): Boolean for define if field is required or not + + Attributes: + set_default (str): Set if the value es NOT NULL or not + """ + + super().__init__(name, required) + self.type_sql='longtext' + def get_type_sql(self): """Method for return the sql code for this type @@ -204,7 +270,7 @@ class HTMLField(TextField): """ super().__init__(name, required) - self.trusted_tags=[] + self.trusted_tags=['b', 'strong'] def check(self, value): """Check method for html values @@ -212,9 +278,12 @@ class HTMLField(TextField): This check method use beautifulsoap for clean and format html code """ + # leach.clean('

"trial"

', 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 @@ -227,7 +296,18 @@ class HTMLField(TextField): return value - + """ + + value=bleach.clean(value, tags=self.trusted_tags) + + if self.escape: + + return value.replace('"', '"') + else: + + return value + + class ForeignKeyField(IntegerField): """Subclass of IntegerField for create Foreign keys @@ -305,6 +385,12 @@ class BooleanField(IntegerField): self.default_error="Need 0 or 1 value" self.default_value=0 + self.type_sql='tinyint(1)' + + self.jtype='boolean' + #self.jformat='0' + self.jexample='0' + def check(self, value): self.error=False diff --git a/paramecio2/libraries/db/coreforms.py b/paramecio2/libraries/db/coreforms.py index 6a37dd8..427090f 100644 --- a/paramecio2/libraries/db/coreforms.py +++ b/paramecio2/libraries/db/coreforms.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from collections import OrderedDict from html import escape @@ -244,7 +263,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) diff --git a/paramecio2/libraries/db/dbadmin.py b/paramecio2/libraries/db/dbadmin.py index 2151ec7..a2d39f2 100644 --- a/paramecio2/libraries/db/dbadmin.py +++ b/paramecio2/libraries/db/dbadmin.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import argparse import os,traceback import sys, inspect @@ -111,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) @@ -168,6 +213,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 @@ -190,7 +252,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) @@ -234,13 +297,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) @@ -248,13 +313,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) @@ -262,29 +329,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 - - for f, v in WebModel.model[old_table].fields.items(): + if f in foreignkey_fields[table]: + + 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: - - fields_to_delete_constraint.append(f) - - changes+=1 + #if v.foreignkey==True: + + if table in foreignkey_fields: + + if f in foreignkey_fields[table]: + + fields_to_delete_constraint.append(f) + + changes+=1 fields_to_delete.append(f) diff --git a/paramecio2/libraries/db/extrafields/__init__.py b/paramecio2/libraries/db/extrafields/__init__.py index e69de29..2616e97 100644 --- a/paramecio2/libraries/db/extrafields/__init__.py +++ b/paramecio2/libraries/db/extrafields/__init__.py @@ -0,0 +1,18 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" diff --git a/paramecio2/libraries/db/extrafields/arrayfield.py b/paramecio2/libraries/db/extrafields/arrayfield.py index ab18168..cf964da 100644 --- a/paramecio2/libraries/db/extrafields/arrayfield.py +++ b/paramecio2/libraries/db/extrafields/arrayfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.webmodel import PhangoField,WebModel import json @@ -19,6 +38,12 @@ class ArrayField(PhangoField): self.error_default='Sorry, the json array is invalid' self.set_default='NOT NULL' + + self.type_sql='text' + + self.jtype='array' + + self.default_value='[]' def check(self, value): diff --git a/paramecio2/libraries/db/extrafields/colorfield.py b/paramecio2/libraries/db/extrafields/colorfield.py index 25a756b..160a2ca 100644 --- a/paramecio2/libraries/db/extrafields/colorfield.py +++ b/paramecio2/libraries/db/extrafields/colorfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import IntegerField try: from paramecio2.libraries.db.extraforms.colorform import ColorForm @@ -12,6 +31,8 @@ class ColorField(IntegerField): super().__init__(name, size, required) self.name_form=ColorForm + self.jtype='string' + self.jexample='#f0f0f0' def check(self, value): diff --git a/paramecio2/libraries/db/extrafields/datefield.py b/paramecio2/libraries/db/extrafields/datefield.py index 1859b49..740b2b2 100644 --- a/paramecio2/libraries/db/extrafields/datefield.py +++ b/paramecio2/libraries/db/extrafields/datefield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import PhangoField from paramecio2.libraries import datetime try: @@ -19,6 +38,9 @@ class DateField(PhangoField): self.utc=True self.error_default='Error: Date format invalid' + + self.jtype='string' + self.jformat='date-time' def check(self, value): diff --git a/paramecio2/libraries/db/extrafields/datetimefield.py b/paramecio2/libraries/db/extrafields/datetimefield.py index c68d028..73cfd03 100644 --- a/paramecio2/libraries/db/extrafields/datetimefield.py +++ b/paramecio2/libraries/db/extrafields/datetimefield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import PhangoField from paramecio2.libraries import datetime try: @@ -18,6 +37,12 @@ class DateTimeField(PhangoField): self.utc=False self.error_default='Error: Date format invalid' + + self.type_sql='datetime' + + self.jformat='date-time' + self.jtype='string' + self.jexample='2022-12-01 12:24:11' def check(self, value): @@ -52,6 +77,7 @@ class DateTimeField(PhangoField): def show_formatted(self, value): # Convert to paramecio value + value=str(value) value=value.replace('-', '').replace(':', '').replace(' ', '') diff --git a/paramecio2/libraries/db/extrafields/dictfield.py b/paramecio2/libraries/db/extrafields/dictfield.py index ee8d6fb..51ad54b 100644 --- a/paramecio2/libraries/db/extrafields/dictfield.py +++ b/paramecio2/libraries/db/extrafields/dictfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.webmodel import WebModel, PhangoField import json @@ -19,6 +38,12 @@ class DictField(PhangoField): self.error_default='Sorry, the json dict is invalid' self.set_default='NOT NULL' + + self.type_sql='longtext' + + self.jtype='object' + + self.default_value='{}' def check(self, value): @@ -54,7 +79,7 @@ class DictField(PhangoField): def get_type_sql(self): - return 'TEXT '+self.set_default + return 'JSON '+self.set_default def show_formatted(self, value): diff --git a/paramecio2/libraries/db/extrafields/emailfield.py b/paramecio2/libraries/db/extrafields/emailfield.py index 88d1afd..99a6585 100644 --- a/paramecio2/libraries/db/extrafields/emailfield.py +++ b/paramecio2/libraries/db/extrafields/emailfield.py @@ -1,7 +1,26 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import CharField import re -mail_pattern=re.compile("\w[\w\.-]*@\w[\w\.-]+\.\w+") +mail_pattern=re.compile(r"\w[\w\.-]*@\w[\w\.-]+\.\w+") class EmailField(CharField): """Field for save and check email addreses""" @@ -10,7 +29,9 @@ class EmailField(CharField): super().__init__(name, size, required) - self.error_default='Error: No valid format' + self.error_default='Error: No valid format' + + self.jformat='email' def check(self, value): diff --git a/paramecio2/libraries/db/extrafields/filefield.py b/paramecio2/libraries/db/extrafields/filefield.py index 30cb849..b03f4c1 100644 --- a/paramecio2/libraries/db/extrafields/filefield.py +++ b/paramecio2/libraries/db/extrafields/filefield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import os import sys from pathlib import Path diff --git a/paramecio2/libraries/db/extrafields/i18nfield.py b/paramecio2/libraries/db/extrafields/i18nfield.py index 2b57c09..116db21 100644 --- a/paramecio2/libraries/db/extrafields/i18nfield.py +++ b/paramecio2/libraries/db/extrafields/i18nfield.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import json from paramecio2.libraries.db.webmodel import PhangoField from paramecio2.libraries.db.coreforms import BaseForm @@ -31,6 +50,8 @@ class I18nField(PhangoField): arr_i18n={i:'' for i in I18n.dict_i18n} self.default_value=json.dumps(arr_i18n) + + self.type_sql='longtext' def change_form(self, form): self.extra_parameters=[form] @@ -90,7 +111,7 @@ class I18nField(PhangoField): def get_type_sql(self): - return 'TEXT NOT NULL' + return 'JSON NOT NULL' def obtain_lang_value(self, lang, value): diff --git a/paramecio2/libraries/db/extrafields/imagefield.py b/paramecio2/libraries/db/extrafields/imagefield.py index 4728b0f..0798222 100644 --- a/paramecio2/libraries/db/extrafields/imagefield.py +++ b/paramecio2/libraries/db/extrafields/imagefield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import os import sys from pathlib import Path @@ -73,7 +92,7 @@ class ImageField(CharField): #if not change if field_file in files_uploaded: - + if files_uploaded[field_file].filename=='': if value=='': @@ -113,9 +132,14 @@ class ImageField(CharField): return self.save_folder+'/'+value else: - value=os.path.basename(value) + self.txt_error='Field is empty' + self.error=True + + return '' + + #value=os.path.basename(value) - return self.save_folder+'/'+value + #return self.save_folder+'/'+value # Load image file @@ -180,7 +204,7 @@ class ImageField(CharField): if format_image!='JPEG' and format_image!='GIF' and format_image!='PNG': self.error=True - self.txt_error='Format is wrong. Requires JPEG or PNG formats' + self.txt_error='Format is wrong. Requires GIF, JPEG or PNG formats' im.close() return "" @@ -197,24 +221,6 @@ class ImageField(CharField): filename=prefix+filename save_file=self.save_folder+'/'+filename - - if self.yes_thumbnail: - - for name, width_t in self.thumbnail.items(): - - im_thumb=im.copy() - - ratio=(real_width/width_t) - height_t=round(real_height/ratio) - - size=(width_t, height_t) - - save_file_thumb=self.save_folder+'/'+name+filename - - im_thumb.thumbnail(size, Image.ANTIALIAS) - im_thumb.save(save_file_thumb, "JPEG", quality=self.default_quality_thumb) - - im_thumb.close() # Save file @@ -245,6 +251,26 @@ class ImageField(CharField): os.remove(save_file) + # Save thumbnails + + if self.yes_thumbnail: + + for name, width_t in self.thumbnail.items(): + + im_thumb=im.copy() + + ratio=(real_width/width_t) + height_t=round(real_height/ratio) + + size=(width_t, height_t) + + save_file_thumb=self.save_folder+'/'+name+filename + + im_thumb.thumbnail(size, Image.LANCZOS) + im_thumb.save(save_file_thumb, "JPEG", quality=self.default_quality_thumb) + + im_thumb.close() + im.save(save_file) # Delete old files @@ -268,11 +294,24 @@ class ImageField(CharField): if os.path.isfile(arr_image[self.name]): os.remove(arr_image[self.name]) + + if self.yes_thumbnail: + + for tname, width_t in self.thumbnail.items(): + + old_dir_name=os.path.dirname(arr_image[self.name]) + old_base_name=os.path.basename(arr_image[self.name]) + + old_thumb_name=old_dir_name+'/'+tname+old_base_name + + if os.path.isfile(old_thumb_name): + os.remove(old_thumb_name) + self.model.yes_reset_conditions=old_reset - - #self.model.conditions=old_conditions + + #self.model.conditions=old_conditions im.close() diff --git a/paramecio2/libraries/db/extrafields/ipfield.py b/paramecio2/libraries/db/extrafields/ipfield.py index 7044798..6257774 100644 --- a/paramecio2/libraries/db/extrafields/ipfield.py +++ b/paramecio2/libraries/db/extrafields/ipfield.py @@ -1,9 +1,34 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import CharField import ipaddress class IpField(CharField): """Field for save ip internet address values in db""" + def __init__(self, name, size=1024, required=False): + + super().__init__(name, size, required) + + self.jformat='ipV4' + def check(self, value): try: diff --git a/paramecio2/libraries/db/extrafields/jsonfield.py b/paramecio2/libraries/db/extrafields/jsonfield.py index 80897ff..44a2d0d 100644 --- a/paramecio2/libraries/db/extrafields/jsonfield.py +++ b/paramecio2/libraries/db/extrafields/jsonfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.webmodel import WebModel, PhangoField import sys try: @@ -25,6 +44,10 @@ class JsonField(PhangoField): self.set_default='NOT NULL' + self.type_sql='longtext' + + self.jtype='object' + def check(self, value): if type(value).__name__=='str': @@ -56,7 +79,7 @@ class JsonField(PhangoField): def get_type_sql(self): - return 'LONGTEXT '+self.set_default + return 'JSON '+self.set_default def show_formatted(self, value): @@ -81,12 +104,14 @@ class JsonValueField(PhangoField): return 'JSON' def check(self, value): - + #print(value) try: final_value=json.dumps(value) - + + final_value=value + except json.JSONDecodeError: - + final_value='{}' self.error=True self.txt_error=self.error_default diff --git a/paramecio2/libraries/db/extrafields/langfield.py b/paramecio2/libraries/db/extrafields/langfield.py index a6cac37..8e9f013 100644 --- a/paramecio2/libraries/db/extrafields/langfield.py +++ b/paramecio2/libraries/db/extrafields/langfield.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import CharField from paramecio2.libraries.db import coreforms from paramecio2.libraries.i18n import I18n diff --git a/paramecio2/libraries/db/extrafields/moneyfield.py b/paramecio2/libraries/db/extrafields/moneyfield.py index 812db21..501ecde 100644 --- a/paramecio2/libraries/db/extrafields/moneyfield.py +++ b/paramecio2/libraries/db/extrafields/moneyfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import DecimalField from decimal import Decimal, getcontext from locale import format_string diff --git a/paramecio2/libraries/db/extrafields/parentfield.py b/paramecio2/libraries/db/extrafields/parentfield.py index 06f197a..6ef0718 100644 --- a/paramecio2/libraries/db/extrafields/parentfield.py +++ b/paramecio2/libraries/db/extrafields/parentfield.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + #from paramecio2.libraries.db.webmodel import PhangoField from paramecio2.libraries.db.corefields import IntegerField from paramecio2.libraries.db.coreforms import SelectModelForm diff --git a/paramecio2/libraries/db/extrafields/passwordfield.py b/paramecio2/libraries/db/extrafields/passwordfield.py index 08985f7..33e0b42 100644 --- a/paramecio2/libraries/db/extrafields/passwordfield.py +++ b/paramecio2/libraries/db/extrafields/passwordfield.py @@ -1,7 +1,32 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import PhangoField from paramecio2.libraries.db.coreforms import PasswordForm from hmac import compare_digest as compare_hash -import crypt +#try: +# import crypt +#except: +# pass + +#import bcrypt +from argon2 import PasswordHasher class PasswordField(PhangoField): """Field for check and save passwords""" @@ -13,6 +38,7 @@ class PasswordField(PhangoField): self.name_form=PasswordForm self.default_value='' self.encrypt_password=True + self.jformat='password' def check(self, value): @@ -44,7 +70,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: @@ -60,7 +89,12 @@ class PasswordField(PhangoField): def verify( password, h): """Static method used for verify a password save using PasswordField""" #return bcrypt_sha256.verify(password, h) - return compare_hash(h, crypt.crypt(password, h)) + #return compare_hash(h, crypt.crypt(password, h)) + ph=PasswordHasher() + try: + return ph.verify(h, password) + except: + return False # Old function bcrypt diff --git a/paramecio2/libraries/db/extrafields/percentfield.py b/paramecio2/libraries/db/extrafields/percentfield.py index fad839d..ff91967 100644 --- a/paramecio2/libraries/db/extrafields/percentfield.py +++ b/paramecio2/libraries/db/extrafields/percentfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import DecimalField class PercentField(DecimalField): diff --git a/paramecio2/libraries/db/extrafields/slugifyfield.py b/paramecio2/libraries/db/extrafields/slugifyfield.py index 2fc61c3..ab0f995 100644 --- a/paramecio2/libraries/db/extrafields/slugifyfield.py +++ b/paramecio2/libraries/db/extrafields/slugifyfield.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import CharField from slugify import slugify from paramecio2.libraries.db.coreforms import HiddenForm diff --git a/paramecio2/libraries/db/extrafields/urlfield.py b/paramecio2/libraries/db/extrafields/urlfield.py index 314b452..533083e 100644 --- a/paramecio2/libraries/db/extrafields/urlfield.py +++ b/paramecio2/libraries/db/extrafields/urlfield.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import CharField import re import ipaddress @@ -13,6 +32,11 @@ check_url = re.compile( class UrlField(CharField): """Field for check and save strings in url format""" + def __init__(self, name, size=1024, required=False): + + super().__init__(name, size, required) + self.jformat='url' + def check(self, value): self.error=False @@ -26,7 +50,7 @@ class UrlField(CharField): return value -check_domain=re.compile('^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$') +check_domain=re.compile(r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})$') class DomainField(CharField): """Field for check and save strings in domain internet format""" @@ -53,7 +77,7 @@ class DomainField(CharField): #^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[0-9]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$ -check_git_url=re.compile('^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[a-zA-Z0-9\-_]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$') +check_git_url=re.compile(r'^(https|ssh):\/\/([a-zA-Z0-9\-_]+@)?[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)*(:[a-zA-Z0-9\-_]+)?\/[a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*(\.git)?$') class GitUrlField(CharField): diff --git a/paramecio2/libraries/db/extrafields/usernamefield.py b/paramecio2/libraries/db/extrafields/usernamefield.py index 16c300f..1b79d9d 100644 --- a/paramecio2/libraries/db/extrafields/usernamefield.py +++ b/paramecio2/libraries/db/extrafields/usernamefield.py @@ -1,7 +1,26 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.corefields import PhangoField from paramecio2.libraries.db.coreforms import PasswordForm from hmac import compare_digest as compare_hash -import crypt +#import crypt import re class UserNameField(PhangoField): diff --git a/paramecio2/libraries/db/extraforms/__init__.py b/paramecio2/libraries/db/extraforms/__init__.py index e69de29..2616e97 100644 --- a/paramecio2/libraries/db/extraforms/__init__.py +++ b/paramecio2/libraries/db/extraforms/__init__.py @@ -0,0 +1,18 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" diff --git a/paramecio2/libraries/db/extraforms/checkform.py b/paramecio2/libraries/db/extraforms/checkform.py index 943ae34..a0658a0 100644 --- a/paramecio2/libraries/db/extraforms/checkform.py +++ b/paramecio2/libraries/db/extraforms/checkform.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.coreforms import BaseForm class CheckForm(BaseForm): diff --git a/paramecio2/libraries/db/extraforms/colorform.py b/paramecio2/libraries/db/extraforms/colorform.py index 4b6b62b..546609f 100644 --- a/paramecio2/libraries/db/extraforms/colorform.py +++ b/paramecio2/libraries/db/extraforms/colorform.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.mtemplates import standard_t diff --git a/paramecio2/libraries/db/extraforms/dateform.py b/paramecio2/libraries/db/extraforms/dateform.py index c888c6f..b1cb49a 100644 --- a/paramecio2/libraries/db/extraforms/dateform.py +++ b/paramecio2/libraries/db/extraforms/dateform.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.mtemplates import standard_t from paramecio2.libraries.datetime import format_timedata @@ -11,28 +30,41 @@ class DateForm(BaseForm): super().__init__(name, value) - self.yes_time=False + self.yes_time=True self.t=standard_t def form(self): - y='' - m='' - d='' - h='' - min='' - s='' - min_time='' + if type(self.default_value).__name__!='datetime': - time=format_timedata(self.default_value) + y='' + m='' + d='' + h='' + min='' + s='' + min_time='' + + time=format_timedata(self.default_value) + + if time[0]: + y=int(time[0]) + m=int(time[1]) + d=int(time[2]) + h=int(time[3]) + min_time=int(time[4]) + s='00' #int(time[5]) - if time[0]: - y=int(time[0]) - m=int(time[1]) - d=int(time[2]) - h=int(time[3]) - min_time=int(time[4]) - s=int(time[5]) + else: + + y=self.default_value.year #"{:>10}".format(s) + m="{:02d}".format(self.default_value.month) + d="{:02d}".format(self.default_value.day) + h="{:02d}".format(self.default_value.hour) + min_time="{:02d}".format(self.default_value.minute) + s='00' + + pass return self.t.load_template('forms/dateform.phtml', yes_time=self.yes_time, form=self.name, y=y, m=m, d=d, h=h, min=min_time, s=s) diff --git a/paramecio2/libraries/db/extraforms/fileform.py b/paramecio2/libraries/db/extraforms/fileform.py index ba64248..cc1d3a6 100644 --- a/paramecio2/libraries/db/extraforms/fileform.py +++ b/paramecio2/libraries/db/extraforms/fileform.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.mtemplates import env_theme, PTemplate diff --git a/paramecio2/libraries/db/extraforms/i18nform.py b/paramecio2/libraries/db/extraforms/i18nform.py index 410f103..6272dd1 100644 --- a/paramecio2/libraries/db/extraforms/i18nform.py +++ b/paramecio2/libraries/db/extraforms/i18nform.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.i18n import I18n from paramecio2.libraries.mtemplates import standard_t diff --git a/paramecio2/libraries/db/extraforms/texthtmlform.py b/paramecio2/libraries/db/extraforms/texthtmlform.py index 6ba9d73..3dbdf06 100644 --- a/paramecio2/libraries/db/extraforms/texthtmlform.py +++ b/paramecio2/libraries/db/extraforms/texthtmlform.py @@ -1,3 +1,21 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.mtemplates import env_theme, PTemplate diff --git a/paramecio2/libraries/db/extraforms/usernameform.py b/paramecio2/libraries/db/extraforms/usernameform.py index ffcef75..1a82527 100644 --- a/paramecio2/libraries/db/extraforms/usernameform.py +++ b/paramecio2/libraries/db/extraforms/usernameform.py @@ -1,3 +1,21 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" from paramecio2.libraries.db.coreforms import BaseForm from paramecio2.libraries.mtemplates import env_theme, PTemplate diff --git a/paramecio2/libraries/db/simplequery.py b/paramecio2/libraries/db/simplequery.py new file mode 100644 index 0000000..7c8aaad --- /dev/null +++ b/paramecio2/libraries/db/simplequery.py @@ -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 diff --git a/paramecio2/libraries/db/sqlalchemy.py b/paramecio2/libraries/db/sqlalchemy.py index c7da074..9dcf592 100644 --- a/paramecio2/libraries/db/sqlalchemy.py +++ b/paramecio2/libraries/db/sqlalchemy.py @@ -46,6 +46,7 @@ class SqlClass: self.conn=None self.connected=False self.pool_recycle=3600 + self.last_query='' self.connect() @@ -73,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] @@ -163,23 +167,27 @@ class SqlClass: 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) try: cursor.execute(sql_query, arguments) self.conn.commit() + + #if hasattr(cursor, '_executed'): + # self.last_query=cursor._executed + return cursor except: e = sys.exc_info()[0] v = sys.exc_info()[1] - if hasattr(cursor, '_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() diff --git a/paramecio2/libraries/db/webmodel.py b/paramecio2/libraries/db/webmodel.py index 1d7a29d..b908e05 100644 --- a/paramecio2/libraries/db/webmodel.py +++ b/paramecio2/libraries/db/webmodel.py @@ -25,6 +25,7 @@ class PhangoField: Attributes: name (str): The name of the field + jtype(Python type): The type of value in python label (str): A label or generic name for use in text labels used for representate the field required (bool): If the field is required or not. size (int): The size of sql field. @@ -54,6 +55,10 @@ class PhangoField: self.name=name + # The type of the field in javascript. Util for api documentation + + self.jtype='string' + # The label for the Field self.label=name.replace('_', ' ').title() @@ -145,6 +150,10 @@ class PhangoField: # Value used for help strings in tooltips in forms self.help='' + + self.type_sql='varchar({})'.format(self.size) + + def get_type_sql(self): """This method is used for describe the new field in a sql language format.""" @@ -222,6 +231,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): @@ -352,6 +362,7 @@ class WebModel: sqlclass (SqlClass): A sql_class used for connect to db. show_formatted (bool): If True, by default all fields are showed with formatted value using show_formatted method of PhangoField classes and children in select method. If False, raw value is showed. enctype (bool): If True, forms generated using this model are prepared for enctype=multipart/form-data A.K.A. upload files. + model_id (int): Variable where the actual row from model selected can be saved for different things. """ self.cached=WebModel.global_cached @@ -456,6 +467,8 @@ class WebModel: self.enctype=False + self.model_id=0 + self.dummy=0 # A method for add the connection @@ -1127,7 +1140,9 @@ class WebModel: #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.name+"` "+self.conditions[0]).strip() result=self.query(sql, self.conditions[1], self.connection_id) @@ -1290,7 +1305,7 @@ class WebModel: self.arr_sql_set_index[self.name][field]='ALTER TABLE `'+self.name+'` ADD CONSTRAINT `'+field+'_'+self.name+'IDX` FOREIGN KEY ( `'+field+'` ) REFERENCES `'+table_related+'` (`'+id_table_related+'`) ON DELETE CASCADE ON UPDATE CASCADE;' - return "create table `"+self.name+"` (\n"+",\n".join(table_fields)+"\n) DEFAULT CHARSET=utf8;"; + return "create table `"+self.name+"` (\n"+",\n".join(table_fields)+"\n) DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;"; 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): diff --git a/paramecio2/libraries/formsutils.py b/paramecio2/libraries/formsutils.py index 0f87a20..b6ee5c7 100644 --- a/paramecio2/libraries/formsutils.py +++ b/paramecio2/libraries/formsutils.py @@ -1,12 +1,48 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.db import corefields from paramecio2.libraries.db.coreforms import PasswordForm from paramecio2.libraries.i18n import I18n -from flask import session, request, abort from paramecio2.libraries.keyutils import create_key_encrypt from paramecio2.libraries.i18n import I18n, PGetText +# For tests outing the web framework + +try: + from settings import config +except: + class config: + pass + +framework='flask' + +if hasattr(config, 'framework'): + framework=config.framework + +if framework=='flask': + from flask import session, request, abort +elif framework=='fastapi': + from parameciofast.libraries.sessions import get_session + pass # Need unittest """Functions and classes for process forms""" @@ -153,13 +189,24 @@ def show_form(post, arr_form, t, yes_error=True, pass_values=True, modelform_tpl # Create csrf_token in session - generate_csrf() + #generate_csrf() if pass_values==True: pass_values_to_form(post, arr_form, yes_error, pass_values) return t.load_template(modelform_tpl, forms=arr_form) +def extract_post(post, fields): + + """Helper function for create a simple array from other using fields list for filter + + Args: + post (dict): A dict with keys and values to filter. + fields (list): A list with keys to validate. + """ + + return {k:v for k,v in post.items() if k in fields} + #Simple Function for add repeat_password form to user model def set_extra_forms_user(user_admin): @@ -218,6 +265,6 @@ def check_csrf(name_csrf_token='csrf_token'): csrf_token=session.get('csrf_token', '') if csrf_token=='' or csrf_token!=request.form.get(name_csrf_token): - abort(404) + abort(403) diff --git a/paramecio2/libraries/generate_admin_class.py b/paramecio2/libraries/generate_admin_class.py index 114458a..4c8dbc9 100644 --- a/paramecio2/libraries/generate_admin_class.py +++ b/paramecio2/libraries/generate_admin_class.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from paramecio2.libraries.lists import SimpleList from flask import request, redirect, flash from paramecio2.libraries.urls import add_get_parameters @@ -5,6 +24,7 @@ from paramecio2.libraries.urls import add_get_parameters from paramecio2.libraries.formsutils import show_form from paramecio2.libraries.mtemplates import env_theme, PTemplate from paramecio2.libraries.i18n import I18n, PGetText +from paramecio2.libraries.formsutils import check_csrf from collections import OrderedDict pgettext=PGetText(__file__) @@ -18,7 +38,7 @@ class GenerateAdminClass: """Class for insert, update and list items of a model """ - def __init__(self, model, url, t=None): + def __init__(self, model, url, t=None, query_args=None, request_post=None): """A class for generate forms, insert and update items from a database model For an easy and fast access to database data, you can use this class for get a simple database model of paramecio and get list of items, add forms, edit forms and more. @@ -96,6 +116,10 @@ class GenerateAdminClass: self.post_update=None self.text_home=_('Home') + + self.query_args=None + + self.request_post=None def show(self): """ Method for show the admin model @@ -106,9 +130,13 @@ class GenerateAdminClass: html (str): The html content of the admin page, can be, items list, forms for create items, etc... """ - op_admin=request.args.get('op_admin', '0') + if not self.query_args: + op_admin=request.args.get('op_admin', '0') + item_id=request.args.get('id', '0') - item_id=request.args.get('id', '0') + else: + op_admin=self.query_args.get('op_admin', '0') + item_id=self.query_args.get('id', '0') if len(self.model.forms)==0: @@ -137,9 +165,14 @@ class GenerateAdminClass: if post==None or post==False: if item_id=='0': + + self.model.model_id=0 + post={} else: return "" + else: + self.model.model_id=int(item_id) url_action=add_get_parameters(self.url, op_admin=2, id=item_id) @@ -149,6 +182,8 @@ class GenerateAdminClass: elif op_admin=='2': + check_csrf() + self.model.reset_conditions() insert_row=self.model.insert @@ -162,7 +197,11 @@ class GenerateAdminClass: try: - item_id=str(int(request.args.get('id', '0'))) + if not self.query_args: + + item_id=str(int(request.args.get('id', '0'))) + else: + item_id=str(int(self.query_args.get('id', '0'))) except: @@ -176,8 +215,12 @@ class GenerateAdminClass: title_edit=_('Edit item') self.model.conditions=['WHERE `'+self.model.name+'`.`'+self.model.name_field_id+'`=%s', [item_id]] - post=dict(request.form) + if not self.request_post: + post=dict(request.form) + else: + post=self.request_post + print(post) if pre_update_ret: if insert_row(post): @@ -194,7 +237,10 @@ class GenerateAdminClass: url_action=add_get_parameters(self.url, op_admin=2, id=item_id) - post=dict(request.form) + if not self.request_post: + post=dict(request.form) + else: + post=self.request_post form=show_form(post, edit_forms, self.t, True) @@ -204,7 +250,7 @@ class GenerateAdminClass: else: url_action=add_get_parameters(self.url, op_admin=2, id=item_id) - post=dict(request.form) + #post=dict(request.form) form=show_form(post, edit_forms, self.t, True) @@ -215,7 +261,13 @@ class GenerateAdminClass: elif op_admin=='3': - verified=request.args.get('verified', '0') + if not self.query_args: + + verified=request.args.get('verified', '0') + + else: + + verified=self.query_args.get('verified', '0') if verified=='1': @@ -302,6 +354,10 @@ class GenerateConfigClass: self.post_update=None self.text_home=_('Home') + + self.query_args=None + + self.request_post=None def show(self): @@ -311,7 +367,11 @@ class GenerateConfigClass: """ - op_config=request.args.get('op_config', '0') + #op_config=request.args.get('op_config', '0') + if not self.query_args: + op_config=request.args.get('op_config', '0') + else: + op_config=self.query_args.get('op_config', '0') if len(self.model.forms)==0: @@ -339,21 +399,32 @@ class GenerateConfigClass: if c: insert_model=self.model.update - post=dict(request.form) + #post=dict(request.form) + if not self.request_post: + + post=dict(request.form) + else: + post=self.request_post + if insert_model(post): - set_flash_message(_('Task successful')) + flash(_('Task successful')) self.model.yes_reset_conditions=True if self.post_update: self.post_update(self) - redirect(self.url_redirect) + return redirect(self.url_redirect) else: - post=dict(request.form) + #post=dict(request.form) + if not self.request_post: + post=dict(request.form) + else: + post=self.request_post + form=show_form(post, edit_forms, self.t, True) self.model.yes_reset_conditions=True diff --git a/paramecio2/libraries/get_data.py b/paramecio2/libraries/get_data.py index d995e95..e460f14 100644 --- a/paramecio2/libraries/get_data.py +++ b/paramecio2/libraries/get_data.py @@ -1,3 +1,21 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" from flask import request diff --git a/paramecio2/libraries/hierarchy_links.py b/paramecio2/libraries/hierarchy_links.py new file mode 100644 index 0000000..fe9cba5 --- /dev/null +++ b/paramecio2/libraries/hierarchy_links.py @@ -0,0 +1,145 @@ +#/usr/bin/env python3 + +#from paramecio.citoplasma.urls import add_get_parameters +from paramecio2.libraries.urls import add_get_parameters + +class HierarchyLinks: + + def __init__(arr_links, t=None): + + self.arr_links=arr_links + + self.arr_indexes={} + + def update_links(self, link_father, link_son, text): + + self.arr_links[link_father][link_son]=text + + + def calculate_indexes(): + + #oreach(self.arr_links as $father_link => $arr_child_links) + for father_link, arr_child_links in self.arr_links.items(): + + #foreach($arr_child_links as $link => $text) + for link, text in self.arr_child_links.items(): + + self.arr_indexes[link]=father_link + + + def result(last_link, arr_result=[], yes_last_link=0): + + self.calculate_indexes() + + if last_link in self.arr_indexes: + + father=self.arr_indexes[last_link] + + arr_last_link[0]=self.no_link + + arr_last_link[1]=self.yes_link + + yes_link_func=arr_last_link[yes_last_link] + + if father!='': + + arr_result.append(self.yes_link_func(last_link, self.arr_links[father][last_link])) + + yes_last_link=1 + + arr_result=self.result(father, arr_result, yes_last_link) + + return arr_result + + else: + + arr_result.append(self.yes_link_func(last_link, self.arr_links[father][last_link])) + + return arr_result + + return arr_result + + + def show(link, separator='>>', class_link=''): + + arr_result=self.result(link) + + arr_result=array_reverse(arr_result) + + return ' '+separator+' '.join(arr_result) + + def yes_link(link, text): + + return ''+text+'' + + + def no_link(link, text): + + return text + +class HierarchyModelLinks: + + def __init__(self, model, first_element_title, field_name, field_parent, base_url): + + self.model=model + self.field_parent=field_parent + self.field_name=field_name + self.base_url=base_url + self.arr_parent={} + self.arr_son=[] + self.first_element_title=first_element_title + + def prepare(self): + + conditions=self.model.conditions + + with self.model.set_conditions('', []).select([self.model.name_field_id, self.field_name, self.field_parent]) as cur: + for arr_model in cur: + if self.field_parent not in self.arr_parent: + self.arr_parent[arr_model[self.model.name_field_id]]=[] + + self.arr_parent[arr_model[self.model.name_field_id]]=[self.model.fields[self.field_name].show_formatted(arr_model[self.field_name]), arr_model[self.field_parent]] + + self.model.conditions=conditions + + def parents(self, son_id, url_func): + + if son_id not in self.arr_parent or son_id==0: + return + + self.arr_son.insert(0, url_func(son_id, self.arr_parent[son_id][0])) + + self.parents(self.arr_parent[son_id][1], self.url) + + + def no_url(self, son_id, title): + return title + + def url(self, son_id, title): + + args={} + + args[self.field_parent]=str(son_id) + + return '%s' % (add_get_parameters(self.base_url, **args), title) + + def show(self, son_id, separator=' >> '): + + try: + son_id=int(son_id) + except: + son_id=0 + + self.prepare() + + self.parents(son_id, self.no_url) + + self.arr_son.insert(0, self.url(0, self.first_element_title)) + + return separator.join(self.arr_son) + + + + + + diff --git a/paramecio2/libraries/i18n.py b/paramecio2/libraries/i18n.py index 752bc08..89f501c 100644 --- a/paramecio2/libraries/i18n.py +++ b/paramecio2/libraries/i18n.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from importlib import import_module import gettext #from paramecio.citoplasma.sessions import get_session @@ -86,10 +105,65 @@ class I18n: l={} + def __init__(self, module): + + self.module=module + + def slang(self, symbol, text_default, lang=None): + """Method for get a string from selected language but object oriented + + Method for get a string from selected language but object oriented + + Args: + symbol (str): The symbol used for identify the text string. + text_default (str): The text default used. You have use how base for translations. + """ + return I18n.lang(self.module, symbol, text_default, lang) + + def tlang(self, text_default, lang=None): + """Method for get a string from selected language but object oriented and using module and symbol by default + + Method for get a string from selected language but object oriented and using module and symbol by default + + Args: + text_default (str): The text default used. You have use how base for translations. + """ + + symbol=text_default[:60] + + return I18n.lang(self.module, symbol, text_default, lang) + #@staticmethod #def set_lang(code_lang): # if default_lang + def __init__(self, module): + self.module=module + + def slang(self, symbol, text_default, lang=None): + """Method for get a string from selected language but object oriented + + Method for get a string from selected language but object oriented + + Args: + symbol (str): The symbol used for identify the text string. + text_default (str): The text default used. You have use how base for translations. + """ + return I18n.lang(self.module, symbol, text_default) + + def tlang(self, text_default, lang=None): + """Method for get a string from selected language but object oriented and using module and symbol by default + + Method for get a string from selected language but object oriented and using module and symbol by default + + Args: + text_default (str): The text default used. You have use how base for translations. + """ + + symbol=text_default[:60] + + return I18n.lang(self.module, symbol, text_default) + @staticmethod def get_default_lang(): diff --git a/paramecio2/libraries/keyutils.py b/paramecio2/libraries/keyutils.py index fcbef1c..7b0abda 100644 --- a/paramecio2/libraries/keyutils.py +++ b/paramecio2/libraries/keyutils.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from hashlib import sha512, sha256 from base64 import b64encode from os import urandom diff --git a/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.mo b/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.mo new file mode 100644 index 0000000..63d66da Binary files /dev/null and b/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.mo differ diff --git a/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.po b/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.po new file mode 100644 index 0000000..cd10b26 --- /dev/null +++ b/paramecio2/libraries/languages/es-ES/LC_MESSAGES/libraries.po @@ -0,0 +1,59 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-12-18 23:51+0100\n" +"PO-Revision-Date: 2023-12-19 00:01+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.1\n" + +#: formsutils.py:183 +msgid "Repeat Password" +msgstr "Repetir contraseña" + +#: lists.py:113 +msgid "Options" +msgstr "Opciones" + +#: lists.py:222 generate_admin_class.py:304 +msgid "Edit" +msgstr "Editar" + +#: lists.py:223 +msgid "Delete" +msgstr "Borrar" + +#: lists.py:395 templates/utils/list.phtml:133 +msgid "Pages" +msgstr "Páginas" + +#: generate_admin_class.py:82 generate_admin_class.py:288 +msgid "Home" +msgstr "Inicio" + +#: generate_admin_class.py:112 generate_admin_class.py:155 +msgid "Add new item" +msgstr "Añadir nuevo elemento" + +#: generate_admin_class.py:118 generate_admin_class.py:160 +msgid "Edit item" +msgstr "Editar elemento" + +#: generate_admin_class.py:168 generate_admin_class.py:209 +#: generate_admin_class.py:329 +msgid "Task successful" +msgstr "Tarea exitosa" + +#: templates/utils/list.phtml:14 +msgid "Search" +msgstr "Buscar" diff --git a/paramecio2/libraries/languages/libraries.mo b/paramecio2/libraries/languages/libraries.mo new file mode 100644 index 0000000..63d66da Binary files /dev/null and b/paramecio2/libraries/languages/libraries.mo differ diff --git a/paramecio2/libraries/languages/libraries.po b/paramecio2/libraries/languages/libraries.po new file mode 100644 index 0000000..cd10b26 --- /dev/null +++ b/paramecio2/libraries/languages/libraries.po @@ -0,0 +1,59 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-12-18 23:51+0100\n" +"PO-Revision-Date: 2023-12-19 00:01+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.1\n" + +#: formsutils.py:183 +msgid "Repeat Password" +msgstr "Repetir contraseña" + +#: lists.py:113 +msgid "Options" +msgstr "Opciones" + +#: lists.py:222 generate_admin_class.py:304 +msgid "Edit" +msgstr "Editar" + +#: lists.py:223 +msgid "Delete" +msgstr "Borrar" + +#: lists.py:395 templates/utils/list.phtml:133 +msgid "Pages" +msgstr "Páginas" + +#: generate_admin_class.py:82 generate_admin_class.py:288 +msgid "Home" +msgstr "Inicio" + +#: generate_admin_class.py:112 generate_admin_class.py:155 +msgid "Add new item" +msgstr "Añadir nuevo elemento" + +#: generate_admin_class.py:118 generate_admin_class.py:160 +msgid "Edit item" +msgstr "Editar elemento" + +#: generate_admin_class.py:168 generate_admin_class.py:209 +#: generate_admin_class.py:329 +msgid "Task successful" +msgstr "Tarea exitosa" + +#: templates/utils/list.phtml:14 +msgid "Search" +msgstr "Buscar" diff --git a/paramecio2/libraries/lists.py b/paramecio2/libraries/lists.py index 0ff00f0..d785573 100644 --- a/paramecio2/libraries/lists.py +++ b/paramecio2/libraries/lists.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + #By default id is not showed from paramecio2.libraries.pages import Pages @@ -414,5 +433,7 @@ class AjaxList(SimpleList): return {'fields': self.fields, 'rows': rows, 'html_pages': html_pages} +class SimpleAjaxList(): + pass diff --git a/paramecio2/libraries/load_apps.py b/paramecio2/libraries/load_apps.py index 81ef7ad..936343d 100644 --- a/paramecio2/libraries/load_apps.py +++ b/paramecio2/libraries/load_apps.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import csv import os diff --git a/paramecio2/libraries/mtemplates.py b/paramecio2/libraries/mtemplates.py index 1cb8371..3e469b3 100644 --- a/paramecio2/libraries/mtemplates.py +++ b/paramecio2/libraries/mtemplates.py @@ -1,8 +1,27 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + # Template frontend from mako. #import gettext from mako.template import Template -from flask import session, url_for +from flask import url_for as url_for_flask from mako.lookup import TemplateLookup from os import path try: @@ -18,6 +37,12 @@ from paramecio2.libraries.i18n import I18n, PGetText from paramecio2.libraries.urls import make_url, make_media_url, add_get_parameters from paramecio2.libraries.formsutils import csrf_token +framework='flask' + +if hasattr(config, 'framework'): + framework=config.framework + + """ def _(text): @@ -72,7 +97,7 @@ class PTemplate: templates_loaded={} - def __init__(self, environment): + def __init__(self, environment, url_for_function=None): """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. @@ -98,11 +123,22 @@ class PTemplate: self.add_filter(I18n.lang) - #self.add_filter(make_url) + self.add_filter(make_url) self.add_filter(make_media_url) + + if not url_for_function: + + url_for=url_for_flask - self.add_filter(url_for) + self.add_filter(url_for) + + else: + + url_for=url_for_function + + self.add_filter(url_for) + self.add_filter(csrf_token) @@ -121,8 +157,16 @@ class PTemplate: #print(path.basename(module_env)+' '+base_name+'/languages/') self.l=PGetText(module_env+'/app.py') + + self.i18n=I18n(module_env) self.add_filter(self._) + + self.i18n=I18n(base_name) + + self.add_filter(self.i18n.slang) + + self.add_filter(self.i18n.tlang) def _(self, text): diff --git a/paramecio2/libraries/pages.py b/paramecio2/libraries/pages.py index d2c9b0e..3a7192c 100644 --- a/paramecio2/libraries/pages.py +++ b/paramecio2/libraries/pages.py @@ -1,5 +1,24 @@ #!/usr/bin/env python3 +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from math import ceil, floor from paramecio2.libraries.urls import add_get_parameters from paramecio2.libraries.i18n import I18n diff --git a/paramecio2/libraries/plugins.py b/paramecio2/libraries/plugins.py index 3594140..fa76c02 100644 --- a/paramecio2/libraries/plugins.py +++ b/paramecio2/libraries/plugins.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from flask import g, session, redirect, url_for from functools import wraps from paramecio2.libraries.db.webmodel import WebModel @@ -13,7 +32,7 @@ def db(f): """Wrapper function for add db connection to your flask function - Wrapper function for add db connection to your flask function. Also close the connection if error or function exection is finished. + Wrapper function for add db connection to your flask function. Also close the connection if error or function exception is finished. Args: *args (mixed): The args of function @@ -23,7 +42,8 @@ def db(f): wrapper (function): Return the wrapper. """ - g.connection=WebModel.connection() + if not hasattr(g, 'connection'): + g.connection=WebModel.connection() try: diff --git a/paramecio2/libraries/responsesapi.py b/paramecio2/libraries/responsesapi.py new file mode 100644 index 0000000..f707a2d --- /dev/null +++ b/paramecio2/libraries/responsesapi.py @@ -0,0 +1,47 @@ +from paramecio2.libraries.db import corefields +from paramecio2.libraries.db.extrafields.jsonfield import JsonField +from paramecio2.libraries.db.extrafields.dictfield import DictField +try: + import ujson as json +except: + import json + +"""A class for list objects with data fields +""" + +class ListItem: + pass + +"""Typical item """ + +class Items(ListItem): + + name=corefields.CharField('name') + + +class StandardResponse: + + error=corefields.BooleanField('error') + message=corefields.CharField('message') + code_error=corefields.IntegerField('code_error') + error_form=DictField('error_form', corefields.CharField('error_form')) + #items=JsonField('items', corefields.CharField) + + def __init__(self, error=0, message='', code_error=0): + + self.error=error + self.message=message + self.code_error=code_error + + def toDict(self): + + return self.__dict__ + + def toJSON(self): + return json.dumps( self, default=lambda o: o.__dict__, sort_keys=True, indent=4 ) + + + +class ResponseItems(StandardResponse): + pass + diff --git a/paramecio2/libraries/sendmail.py b/paramecio2/libraries/sendmail.py index a8ea42e..e567b88 100644 --- a/paramecio2/libraries/sendmail.py +++ b/paramecio2/libraries/sendmail.py @@ -1,4 +1,24 @@ #!/usr/bin/env python3 + +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + import os import smtplib import mimetypes @@ -9,6 +29,7 @@ from email.mime.base import MIMEBase from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText +from email.utils import formataddr, formatdate import ssl as ssl_module import sys @@ -109,7 +130,7 @@ class SendMail: return True - def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[]): + def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[], reply_to=()): """ Method that send email With this method you can send email to multiple address, html, and add attachments to email @@ -127,6 +148,10 @@ class SendMail: if not self.connect(): return False + if len(reply_to)==0: + reply_to=(from_address, from_address) + + COMMASPACE=', ' if len(attachments)==0: @@ -134,6 +159,9 @@ class SendMail: msg=MIMEText(message, content_type) msg['Subject']=subject + + msg['Reply-To']=formataddr(reply_to) + msg['From']=from_address msg['To']=COMMASPACE.join(to_address) @@ -149,10 +177,15 @@ class SendMail: outer=MIMEMultipart() outer['Subject']=subject + + msg['Reply-To']=formataddr(reply_to) + outer['From']=from_address outer['To']=COMMASPACE.join(to_address) + #outer['Date']=formatdate() + # Attach message text msg=MIMEText(message, content_type) diff --git a/paramecio2/libraries/slugify.py b/paramecio2/libraries/slugify.py index f2ad2b9..433758c 100644 --- a/paramecio2/libraries/slugify.py +++ b/paramecio2/libraries/slugify.py @@ -1,3 +1,22 @@ +""" +Paramecio2fm is a series of wrappers for Flask, mako and others and construct a simple headless cms. + +Copyright (C) 2023 Antonio de la Rosa Caballero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +""" + from slugify import slugify as slugify_func def slugify(slug, *args, **wargs): diff --git a/paramecio2/libraries/templates/forms/dateform.phtml b/paramecio2/libraries/templates/forms/dateform.phtml index 6b6d852..1a10980 100644 --- a/paramecio2/libraries/templates/forms/dateform.phtml +++ b/paramecio2/libraries/templates/forms/dateform.phtml @@ -1,38 +1,54 @@ ${add_js('jquery.min.js', 'admin')} - + +<% + +date='' + +if d!='': + date='-'.join((str(y), str(m), str(d))) + +time='' + +if h!='': + time=':'.join((str(h), str(min))) + +%> + % if yes_time==True: - - - + + % endif + +% endif + @@ -34,11 +34,11 @@ ${load_js()|n} - +
-<%block name="logout"> Logout +<%block name="logout"> Logout
@@ -63,7 +63,9 @@ ${load_js()|n}
${forms|n} + ${csrf_token()|n}
${_('Remember login?')}
@@ -109,7 +110,7 @@ % if yes_recovery_login: % endif -
${_('Remember that only have 3 attempts')}
+ diff --git a/paramecio2/modules/admin/templates/register.phtml b/paramecio2/modules/admin/templates/register.phtml index e42610a..83fa2e5 100644 --- a/paramecio2/modules/admin/templates/register.phtml +++ b/paramecio2/modules/admin/templates/register.phtml @@ -55,6 +55,7 @@ ${_('Paramecio Sign up')} ${forms|n} + ${csrf_token()|n}
diff --git a/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.mo b/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.mo new file mode 100644 index 0000000..01da54e Binary files /dev/null and b/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.mo differ diff --git a/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.po b/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.po new file mode 100644 index 0000000..7d50c0e --- /dev/null +++ b/paramecio2/modules/welcome/languages/es-ES/LC_MESSAGES/welcome.po @@ -0,0 +1,30 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-12-18 22:13+0100\n" +"PO-Revision-Date: 2023-12-18 22:13+0100\n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.4.1\n" + +#: app.py:13 +msgid "Welcome" +msgstr "Bienvenido" + +#: app.py:13 +msgid "Welcome to the real world" +msgstr "Bienvenido al mundo real" + +#: templates/welcome.phtml:75 +msgid "Paramecio, a system created for create webapps" +msgstr "Paramecio, un sistema creado para crear webapps" diff --git a/paramecio2/scripts/create_module.py b/paramecio2/scripts/create_module.py new file mode 100644 index 0000000..2c49b7d --- /dev/null +++ b/paramecio2/scripts/create_module.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 + +import traceback +import argparse +import os,sys +import shutil +import getpass +from pathlib import Path +from importlib import import_module +from paramecio2.libraries.slugify import slugify +import json + +sys.path.insert(0, os.path.realpath('.')) + +try: + + from settings import config + +except: + + pass + + +def start(): + """Module for create new modules for paramecio + """ + + parser=argparse.ArgumentParser(description='A tool for create new modules for paramecio') + + parser.add_argument('--folder', help='The folder where the new paramecio module is located', required=True) + + args=parser.parse_args() + + workdir=os.path.dirname(os.path.abspath(__file__)) + + # Create directory + + real_path=os.path.basename(slugify(args.folder)) + + path=Path('modules/'+real_path) + + + path.mkdir(0o755, True) + + #open('modules/'+args.path+'/__init__.py', 'a').close() + with open('modules/'+real_path+'/__init__.py', 'a') as f: + f.write("from flask import Blueprint\n\n") + f.write("{}_app=Blueprint('{}_app', __name__)\n\n".format(real_path, real_path)) + f.write("@{}_app.route('/{}')\n".format(real_path, real_path)) + f.write("def {}_home():\n".format(real_path)) + f.write(" return {'hello': 'world'}") + + modules_json={} + + if os.path.isfile('settings/modules.json'): + + with open('settings/modules.json') as f: + + #apps={'monit2': ['modules.monit2', 'monit2_app', '/'], 'welcome': ['paramecio2.modules.welcome', 'welcome_app', '/'], 'pastafari2': ['modules.pastafari2', 'pastafari_app', '/'], 'apache': ['modules.apache', 'apache_app', '/'], 'mariadb': ['modules.mariadb', 'mariadb_app', '/'], 'apiv1': ['modules.apiv1', 'apiv1_app', '/'], 'admin': ['paramecio2.modules.admin', 'admin_app', '/']} + + json_text=f.read() + + if json_text!='': + modules_json=json.loads(json_text) + + with open('settings/modules.json', 'w+') as f: + modules_json[real_path]=['modules.'+real_path, real_path+'_app', '/'] + + f.write(json.dumps(modules_json, indent=4)) + + print('Created the new module. Please, reload the WSGI server and go to url /'+real_path) + + """ + except: + + print('Error: cannot create the directory. Check if exists and if you have permissions') + exit(1) + """ + #Create base controller file + + #f=open('modules/'+args.path+'/index.py', 'w') + """ + try: + shutil.copy(workdir+'/examples/app.py', 'modules/'+args.path+'/__init__.py') + + except: + + print('Error: cannot copy controller example. Check if you have permissions') + exit(1) + """ + + # Regenerate modules + + #regenerate_modules_config() +""" +def regenerate_modules_config(): + + print("Regenerating modules configuration...") + + modules=[] + + modules.append("#!/usr/bin/env python3\n\n") + modules.append("list_modules=[]\n\n") + + for module in config.modules: + + try: + + controller_path=import_module(module) + + controller_base=os.path.dirname(controller_path.__file__) + + base_module=module.split('.')[-1] + + dir_controllers=os.listdir(controller_base) + + modules.append('from '+module+' import ') + + arr_controllers=[] + + for controller in dir_controllers: + + if controller.find('.py')!=-1 and controller.find('__init__')==-1: + + controller_py=controller.replace('.py', '') + + arr_controllers.append(controller_py) + + #load(module+'.'+controller_py) + + + modules.append(", ".join(arr_controllers)) + + modules.append("\n\n") + + #add_func_static_module(controller_base) + + except: + + print("Exception in user code:") + print("-"*60) + traceback.print_exc(file=sys.stdout) + print("-"*60) + exit(1) + + with open('./settings/modules.py', 'w') as f: + f.write("".join(modules)) +""" + +if __name__=="__main__": + start() diff --git a/paramecio2/scripts/install_module.py b/paramecio2/scripts/install_module.py new file mode 100644 index 0000000..291de32 --- /dev/null +++ b/paramecio2/scripts/install_module.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +import traceback +import argparse +import os,sys +import shutil +import getpass +from pathlib import Path +from importlib import import_module +from paramecio2.libraries.slugify import slugify +import json +import subprocess +#from colorama import init, Fore, Back, Style +import colorama + +sys.path.insert(0, os.path.realpath('.')) + +try: + + from settings import config + +except: + + pass + + +def start(): + """Module for create new modules for paramecio + """ + + parser=argparse.ArgumentParser(description='A tool for add modules for paramecio from git url') + + parser.add_argument('--git_url', help='The git url for clone the module.', required=True) + + args=parser.parse_args() + + colorama.init() + + workdir='.' + + os.chdir('./modules/') + + if subprocess.call("git clone {}".format(args.git_url), shell=True) > 0: + print('Error, cannot install the git module. Do you have installed git?. Is a correct url?') + exit(1) + + real_path='' + + with os.scandir('.') as d: + for e in d: + #print(e.name) + if not e.name in config.apps: + #print(e.name) + if args.git_url.find(e.name)!=-1: + real_path=os.path.basename(e.name) + + # Add dependencies using pip + + os.chdir('../') + + dependencies_file='./modules/{}/dependencies.json'.format(real_path) + + if os.path.isfile(dependencies_file): + + dep_json=[] + + with open(dependencies_file) as f: + + content=f.read() + + try: + dep_json=json.loads(content) + except: + dep_json=[] + print('Error: cannot install dependencies for the package. Malformed json {}'.format(content)) + + if len(dep_json)>0: + if 'packages' in dep_json: + print('Install dependencies for the module...') + arr_packages=" ".join(dep_json['packages']) + if subprocess.call("pip install {}".format(arr_packages), shell=True) > 0: + print('Error, cannot install the pip libraries. Do you have installed pip?.') + + if 'models' in dep_json: + print('Creating database tables from models in module...') + for model in dep_json['models']: + if subprocess.call("paramecio2db --model modules/{}/models/{}.py".format(real_path, model), shell=True) > 0: + + print('Error, cannot install the model {}.'.format(model)) + + # Add to json configuration. + + if real_path!='': + + if os.path.isfile('settings/modules.json'): + + modules_json={} + + with open('settings/modules.json') as f: + print('Inserting new module in modules.json...') + + json_text=f.read() + + if json_text!='': + modules_json=json.loads(json_text) + + with open('settings/modules.json', 'w+') as f: + modules_json[real_path]=['modules.'+real_path, real_path+'_app', '/'] + + f.write(json.dumps(modules_json, indent=4)) + + print('Please, reload your wsgi application for access to your new module') + +if __name__=="__main__": + start() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e6a6771 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,56 @@ +[build-system] +requires = ["flit_core >=3.2,<4"] +build-backend = "flit_core.buildapi" + +[project] +name = "paramecio2" +authors = [{name = "Antonio de la Rosa", email = "antonio.delarosa@salirdelhoyo.com"}] +readme = "README.md" +version = "2.0.40" +description = "A simple framework using flask and mako" +# dynamic = ["version", "description"] + +classifiers=['Development Status :: 4 - Beta', + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Affero General Public License v3", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", + "Topic :: Internet :: WWW/HTTP :: HTTP Servers", + "Topic :: Internet :: WWW/HTTP :: WSGI", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", + "Topic :: Software Development :: Libraries :: Application Frameworks", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] +requires-python = ">=3.9" +dependencies=[ + "flask", + "pymysql", + "sqlalchemy", + "colorama", + "python-slugify", + "mako", + "pillow", + "arrow", + "bleach", + "argon2-cffi" +] + +[project.urls] +Home = "https://git.cuchulu.com/paramecio/paramecio2fm/" +Documentation = "https://docs.cuchulu.com/paramecio2/" + +[project.scripts] +paramecio2 = "paramecio2.console:start" +paramecio2db = "paramecio2.libraries.db.dbadmin:start" +paramecio2cm = "paramecio2.scripts.create_module:start" +paramecio2dm = "paramecio2.scripts.install_module:start" + +[tool.pytest.ini_options] +testpaths = ["tests"] +filterwarnings = [ + "error", +] diff --git a/setup.py b/setup.py index 151c07b..d55a6b3 100644 --- a/setup.py +++ b/setup.py @@ -5,32 +5,32 @@ import os from setuptools import setup, find_packages -if sys.version_info < (3, 8): - raise NotImplementedError("Sorry, you need at least Python 3.8 for use paramecio2.") +if sys.version_info < (3, 9): + raise NotImplementedError("Sorry, you need at least Python 3.9 for use paramecio2.") #import paramecio # Pillow should be installed after if you need ImageField # If you install passlib and bcrypt, the password system will use bcrypt by default, if not, will use native crypt libc setup(name='paramecio2', - version='2.0.28', + version='2.0.40', description='Simple Web Framework based in flask and Mako.', long_description='This framework is a simple framework used for create web apps. Paramecio is modular and fast. By default have a module called admin that can be used for create admin sites', author='Antonio de la Rosa Caballero', - author_email='antonio.delarosa@coesinfo.com', - url='https://bitbucket.org/paramecio/paramecio2fm/', + author_email='antonio.delarosa@salirdelhoyo.com', + url='https://git.cuchulu.com/paramecio/paramecio2fm/', packages=['paramecio2'], include_package_data=True, - install_requires=['flask', 'pymysql', 'sqlalchemy', 'colorama', 'python-slugify', 'mako', 'pillow', 'arrow', 'beautifulsoup4'], + install_requires=['flask', 'pymysql', 'sqlalchemy', 'colorama', 'python-slugify', 'mako', 'pillow', 'arrow', 'bleach', 'argon2-cffi'], entry_points={'console_scripts': [ 'paramecio2 = paramecio2.console:start', 'paramecio2db = paramecio2.libraries.db.dbadmin:start', 'paramecio2lang = paramecio2.libraries.check_i18n:start', ]}, zip_safe=False, - license='GPLV3', + license='AGPLV3', platforms = 'any', - classifiers=['Development Status :: 1 - Beta', + classifiers=['Development Status :: 4 - Beta', 'Intended Audience :: Developers', - 'License :: OSI Approved :: GPLV3 License', + 'License :: OSI Approved :: GNU Affero General Public License v3', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries', 'Topic :: Internet :: WWW/HTTP :: HTTP Servers', 'Topic :: Internet :: WWW/HTTP :: WSGI', @@ -38,9 +38,9 @@ setup(name='paramecio2', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Server', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8' - 'Programming Language :: Python :: 3.9' - 'Programming Language :: Python :: 3.10' - 'Programming Language :: Python :: 3.11' + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12' ], ) diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/paramecio2/tests/conftest.py b/tests/conftest.py similarity index 95% rename from paramecio2/tests/conftest.py rename to tests/conftest.py index e464f79..6064dfc 100644 --- a/paramecio2/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ from paramecio2.libraries.db import corefields def pytest_addoption(parser): - parser.addoption("--mysql_host", action="store", default="host", help="MySQL host: default localhost") + parser.addoption("--mysql_host", action="store", default="localhost", help="MySQL host: default localhost") parser.addoption("--mysql_user", action="store", default="root", help="Mysql User for make the test: default root") parser.addoption("--mysql_password", action="store", default="", help="Mysql password for make the test: default empty value") parser.addoption("--mysql_db", action="store", default="test_paramecio_db", help="Mysql Database for execute the test: default test_paramecio_db") diff --git a/paramecio2/tests/fields_test.py b/tests/fields_test.py similarity index 83% rename from paramecio2/tests/fields_test.py rename to tests/fields_test.py index f44027e..c769914 100644 --- a/paramecio2/tests/fields_test.py +++ b/tests/fields_test.py @@ -1,6 +1,7 @@ import sys import os import pytest +import json sys.path.insert(0, os.path.realpath(os.path.dirname(__file__))+'/../../') @@ -13,6 +14,7 @@ from paramecio2.libraries.db.extrafields.datefield import DateField from paramecio2.libraries.db.extrafields.datetimefield import DateTimeField from paramecio2.libraries.db.extrafields.dictfield import DictField from paramecio2.libraries.db.extrafields.emailfield import EmailField +from paramecio2.libraries.db.extrafields.i18nfield import I18nField from paramecio2.libraries import datetime class ExampleModel(WebModel): @@ -59,22 +61,22 @@ def test_test_htmlfield(): field=corefields.HTMLField('html') - assert field.check('

"trial"

')=='"trial"' + assert field.check('

"trial"

')=='<p>"trial"</p><script></script>' field.escape=True - assert field.check('

"trial"

')=='"trial"' + assert field.check('

"trial"

')=='<p>"trial"</p><script></script>' field.trusted_tags=['p'] - assert field.check('

"trial"

')=='

"trial"

' + assert field.check('

"trial"

')=='

"trial"

<script></script>' #field. def test_test_foreignkeyfield(): field=corefields.ForeignKeyField('foreign', ExampleModel()) - assert field.check('dsd')=='NULL' + assert field.check('dsd')==None def test_test_booleanfield(): @@ -131,7 +133,7 @@ def test_test_datetimefield(): assert field.check('20201230121011')=='2020-12-30 12:10:11' - assert field.check('20201290121011')=='' + assert field.check('20201290121011')=='0000-00-00 00:00:00' def test_test_dictfield(): @@ -153,7 +155,11 @@ def test_test_emailfield(): def test_test_i18nfield(): + field=I18nField('i18nfield') + assert field.check(json.dumps({'en-US': "Hello", 'es-ES': "Hola"}))=='{"en-US": "Hello", "es-ES": "Hola"}' + + assert field.check(json.dumps({'en-US': "Hello"}))=='{"en-US": "Hello", "es-ES": ""}' pass diff --git a/tests/test.sh b/tests/test.sh new file mode 100644 index 0000000..890ee43 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +pytest --mysql_host=127.0.0.1 --mysql_user=root --mysql_password=password --mysql_type=pymysql + diff --git a/tests/test_sendmailtest.py b/tests/test_sendmailtest.py deleted file mode 100644 index 5c29c93..0000000 --- a/tests/test_sendmailtest.py +++ /dev/null @@ -1,25 +0,0 @@ -from settings import config -from paramecio2.libraries import sendmail -import time -import unittest - -class TestFieldMethods(unittest.TestCase): - - def test_sendmail(self): - - s=sendmail.SendMail() - - self.assertTrue( s.send(config.portal_email, [config.email_test], 'This is a test', 'A message for test a simple email method', content_type='plain', attachments=[]) ) - - s=sendmail.SendMail() - - self.assertTrue( s.send(config.portal_email, [config.email_test], 'This is a test', 'A message for test a simple email method in html', content_type='html', attachments=[]) ) - - s=sendmail.SendMail() - - self.assertTrue( s.send(config.portal_email, [config.email_test], 'This is a test', 'A message for test a simple email method in html and attachments', content_type='html', attachments=['tests/images/image.jpg']) ) - - #s.quit() - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_urlstest.py b/tests/test_urlstest.py deleted file mode 100644 index 7c45dad..0000000 --- a/tests/test_urlstest.py +++ /dev/null @@ -1,55 +0,0 @@ -from settings import config -from paramecio2.libraries.urls import make_url, make_media_url, make_external_url -import time -import unittest - -class TestUrlsMethods(unittest.TestCase): - - def test_urls(self): - - basic_url=make_url('welcome') - - self.assertEqual(basic_url, '/welcome') - - straigth_url=make_url('welcome', {'item1': 1, 'item2': 'accént'}) - - self.assertEqual(straigth_url, '/welcome?item1=1&item2=acc%C3%A9nt') - - straigth_url=make_url('welcome', {'item1': 1, 'item2': 'thing with space'}) - - self.assertEqual(straigth_url, '/welcome?item1=1&item2=thing+with+space') - - def test_external_urls(self): - - basic_url=make_external_url('http://coesinfo.com/welcome') - - self.assertEqual(basic_url, 'http://coesinfo.com/welcome') - - straigth_url=make_external_url('http://coesinfo.com/welcome', {'item1': 1, 'item2': 'accént'}) - - self.assertEqual(straigth_url, 'http://coesinfo.com/welcome?item1=1&item2=acc%C3%A9nt') - - straigth_url=make_external_url('http://coesinfo.com/welcome', {'item1': 1, 'item2': 'thing with space'}) - - self.assertEqual(straigth_url, 'http://coesinfo.com/welcome?item1=1&item2=thing+with+space') - - - def test_media_urls(self): - - if config.yes_static: - - media_url=make_media_url('images/image.jpg', 'paramecio') - - self.assertEqual(media_url, config.media_url+'mediafrom/paramecio/images/image.jpg') - - else: - - media_url=make_media_url('images/image.jpg', 'paramecio') - - self.assertEqual(media_url, config.media_url+'media/paramecio/images/image.jpg') - -if __name__ == '__main__': - unittest.main() - - - diff --git a/tests/test_webmodeltest.py b/tests/test_webmodeltest.py deleted file mode 100644 index cc3ff8a..0000000 --- a/tests/test_webmodeltest.py +++ /dev/null @@ -1,305 +0,0 @@ -from settings import config -from paramecio2.libraries.db.webmodel import WebModel -from paramecio2.libraries.db import corefields -import unittest -# Create TestWebModelMethods - -class ExampleModel(WebModel): - - def __init__(self, connection): - - super().__init__(connection) - - # I can change other fields here, how the name. - - self.register(corefields.CharField('title')) - self.register(corefields.CharField('content')) - -class ForeignKeyExampleModel(WebModel): - - def __init__(self, connection): - - super().__init__(connection) - - # I can change other fields here, how the name. - - self.register(corefields.CharField('name')) - self.register(corefields.ForeignKeyField('example_id', ExampleModel(connection), size=11, required=False, identifier_field='id', named_field="id", select_fields=[])) - - -class ExampleModel2(WebModel): - - def __init__(self, connection): - - super().__init__(connection) - - # I can change other fields here, how the name. - - self.register(corefields.CharField('title')) - self.register(corefields.CharField('content')) - -class TestWebModelMethods(unittest.TestCase): - - def test_test_table(self): - - connection=WebModel.connection() - model=ExampleModel(connection) - - - sql=model.create_table() - - print('Creating table') - - self.assertTrue(model.query(sql)) - - - post={'title': 'Example title', 'content': 'New content'} - - model.set_valid_fields() - - print('Insert row') - - self.assertTrue(model.insert(post)) - - print('Check new id') - - self.assertEqual(model.insert_id(), 1) - - post={'title': 'Example title Updated', 'content': 'New content Updated'} - - model.conditions=['WHERE id=%s', [1]] - - print('Updating row') - - self.assertTrue(model.update(post)) - - model.yes_reset_conditions=False - - model.conditions=['WHERE id=%s', [1]] - - print('Count rows') - - self.assertEqual(model.select_count(), 1) - - print('Select a row') - - self.assertEqual(model.select_a_row(1, ['title', 'inexistent_field']), {'title': 'Example title Updated'}) - - print('Select a row with different conditions to search id') - - self.assertEqual(model.select_a_row_where(['title']), {'title': 'Example title Updated'}) - - print('Select and save in an array') - - self.assertEqual(model.select_to_array(['title', 'content']), [{'id': 1, 'title': 'Example title Updated', 'content': 'New content Updated'}]) - - model.yes_reset_conditions=True - - model.reset_conditions() - - print('Reset conditions') - - self.assertEqual(model.conditions, ['WHERE 1=1', []]) - - print('Simple base select') - - cur=model.select() - - row=model.fetch(cur) - - self.assertEqual(row, {'id': 1, 'title': 'Example title Updated', 'content': 'New content Updated'}) - - print('Check element exists') - - self.assertTrue(model.element_exists(1)) - - model.conditions=['WHERE id=%s', [2]] - - print('Check delete row') - - self.assertFalse(model.delete()) - - self.assertTrue(model.delete()) - - print('Check delete table') - - self.assertTrue(model.drop()) - - connection.close() - - - def test_update_table(self): - - connection=WebModel.connection() - model=ExampleModel(connection) - - - print('Check modifications in table') - - sql=model.create_table() - - self.assertTrue(model.query(sql)) - - 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=[] - - model.register(corefields.CharField('description')) - - model.update_table(['description'], 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) - - model.register(corefields.IntegerField('description')) - - model.update_table([], ['description'], ['description'], [], ['description'], fields_to_delete_index, fields_to_delete_unique, fields_to_delete_constraint, fields_to_delete) - - model.update_table([], fields_to_modify, fields_to_add_index, fields_to_add_constraint, fields_to_add_unique, ['description'], ['description'], fields_to_delete_constraint, ['description']) - - self.assertTrue(model.drop()) - - connection.close() - - def test_conditions(self): - - print('Test conditions') - - connection=WebModel.connection() - model=ExampleModel(connection) - - sql=model.create_table() - - self.assertTrue(model.query(sql)) - - cur=model.set_conditions('where id=%s', [4]).select() - - self.assertTrue(cur) - - cur.close() - - self.assertTrue(model.drop()) - - connection.close() - - def test_functions(self): - - print('Test functions') - - connection=WebModel.connection() - model=ExampleModel(connection) - - sql=model.create_table() - - self.assertTrue(model.query(sql)) - - cur=model.set_conditions('where id=%s', [4]).select() - - self.assertTrue(cur) - - cur.close() - - self.assertTrue(model.drop()) - - connection.close() - - def test_zcheck_1_foreignkeys(self): - - connection=WebModel.connection() - model=ExampleModel(connection) - foreignkey=ForeignKeyExampleModel(connection) - - print('Checking ForeignKeys models...') - - sql=model.create_table() - sqlf=foreignkey.create_table() - - print('Creating foreignkey table...') - - self.assertTrue(model.query(sql)) - self.assertTrue(foreignkey.query(sqlf)) - - for k_field, index in WebModel.arr_sql_index['foreignkeyexamplemodel'].items(): - print("---Added index to "+k_field) - foreignkey.query(index) - - for k_set, index_set in WebModel.arr_sql_set_index['foreignkeyexamplemodel'].items(): - - if index_set!="": - connection.query(index_set) - print("---Added constraint to "+k_set) - - model.create_forms() - - self.assertTrue(model.insert({'title': 'Foreign title', 'content': 'Foreign content'})) - - my_id=model.insert_id() - - foreignkey.create_forms() - - self.assertTrue(foreignkey.insert({'example_id': my_id, 'name': 'Relationship'})) - - print('Checking insert...') - - foreignkey.set_conditions('where example_id=%s', [my_id]) - - self.assertEqual(foreignkey.select_count(), 1) - - model.set_conditions('where id=%s', [my_id]) - - self.assertTrue(model.delete()) - - foreignkey.set_conditions('where example_id=%s', [my_id]) - - print('Checking automatic delete...') - - self.assertEqual(foreignkey.select_count(), 0) - - print('Dropping foreignkey table...') - - self.assertTrue(foreignkey.drop()) - self.assertTrue(model.drop()) - - pass - - def test_zcheck_connections(self): - - print('Check connection of models...') - - connection=WebModel.connection() - model=ExampleModel(connection) - - model2=ExampleModel2(connection) - - sql=model.create_table() - sql2=model2.create_table() - #print(sql) - - self.assertTrue(model.query(sql)) - self.assertTrue(model2.query(sql2)) - - self.assertTrue(model.drop()) - self.assertTrue(model2.drop()) - - connection.close() - - pass - - def test_check_filter_list_str(self): - - print('Check string list filtering') - - connection=WebModel.connection() - model=ExampleModel(connection) - - str_filter=model.check_in_list_str('title', ['joan', 'piter', 'luiz"']) - - self.assertEqual(str_filter, '("joan", "piter", "luiz"")') - - connection.close() - -if __name__ == '__main__': - unittest.main() - diff --git a/paramecio2/tests/webmodel_test.py b/tests/webmodel_test.py similarity index 100% rename from paramecio2/tests/webmodel_test.py rename to tests/webmodel_test.py