diff --git a/paramecio2/app.py b/paramecio2/app.py
index 5bad0da..7a2e7b2 100644
--- a/paramecio2/app.py
+++ b/paramecio2/app.py
@@ -1,4 +1,4 @@
-from flask import Flask, session, url_for, escape, request, send_file, abort
+from flask import Flask, session, url_for, request, send_file, abort
from settings import config
from importlib import import_module
import os
@@ -24,11 +24,15 @@ def start_app():
application_root='/'
if hasattr(config, 'application_root'):
+ #print(config.application_root)
application_root=config.application_root
+
app.config.update(
APPLICATION_ROOT=application_root
)
+
+ #app.config['APPLICATION_ROOT']=application_root
if hasattr(config, 'json_sort_keys'):
app.config.update(
diff --git a/paramecio2/console.py b/paramecio2/console.py
index 6793e62..ea3c9b1 100755
--- a/paramecio2/console.py
+++ b/paramecio2/console.py
@@ -8,7 +8,7 @@ import re
from pathlib import Path
from base64 import b64encode
from paramecio2.libraries.db.webmodel import WebModel
-from paramecio2.modules.admin.models.admin import UserAdmin
+from paramecio2.modules.admin.models.admin import UserAdmin, LoginTries
from subprocess import call
from urllib.parse import urlparse
@@ -80,49 +80,7 @@ def start():
print('Error: cannot copy the file index.py to app.py. Check if exists and if you have permissions for this task')
- """
- try:
-
- shutil.copy(workdir+'/frontend/padmin.py', args.path+'/padmin.py')
-
- except:
-
- print('Error: cannot copy the file padmin.py. Check if exists and if you have permissions for this task')
-
- try:
-
- shutil.copy(workdir+'/frontend/i18nadmin.py', args.path+'/i18nadmin.py')
-
- except:
-
- print('Error: cannot copy the file i18nadmin.py. Check if exists and if you have permissions for this task')
-
- try:
-
- shutil.copy(workdir+'/frontend/regenerate.py', args.path+'/regenerate.py')
-
- except:
-
- print('Error: cannot copy the file regenerate.py. Check if exists and if you have permissions for this task')
-
- try:
-
- shutil.copy(workdir+'/frontend/create_module.py', args.path+'/create_module.py')
-
- except:
-
- print('Error: cannot copy the file create_module.py. Check if exists and if you have permissions for this task')
-
-
- try:
-
- shutil.copy(workdir+'/settings/modules.py', path_settings+'/modules.py')
-
- except:
-
- print('Error: cannot copy the file modules.py. Check if exists and if you have permissions for this task')
-
- """
+
if args.symlink==True:
try:
os.symlink(workdir, args.path+'/paramecio2', True)
@@ -178,6 +136,15 @@ def start():
conf=conf.replace("domain_url='http://localhost:5000'", "domain_url='"+domain_url+"'")
+ # Question about email
+
+ e_q=input('Email for site: ')
+
+ conf=conf.replace("no-reply@example.com", e_q)
+
+ #if e_q=='':
+
+
#domain_url='http://localhost:8080'
with open(path_settings+'/config.py', 'w') as f:
@@ -225,6 +192,7 @@ def start():
conn=WebModel.connection()
useradmin=UserAdmin(conn)
+ logintries=LoginTries(conn)
# Check if db exists
@@ -261,6 +229,9 @@ def start():
print('Error: cannot create table admin, you can create this table with padmin.py')
else:
+ sql_login=logintries.create_table()
+ logintries.query(sql_login)
+
# Add admin module to config
with open(path_settings+'/config.py', 'r') as f:
diff --git a/paramecio2/libraries/datetime.py b/paramecio2/libraries/datetime.py
index 9ad9703..e1d6d6f 100644
--- a/paramecio2/libraries/datetime.py
+++ b/paramecio2/libraries/datetime.py
@@ -664,6 +664,15 @@ class TimeClass:
"""
return self.t.format(self.format_date_full)
+
+ def format_datetime(self):
+ """Method for get datetime formatted using format_time attribute
+
+ Returns:
+ Datetime formatted with format_time attribute
+ """
+
+ return self.t.format(self.format_time)
def local_to_utc(self):
"""Method for convert datetime from actual timezone to UTC"""
diff --git a/paramecio2/libraries/db/corefields.py b/paramecio2/libraries/db/corefields.py
index 43c61ed..16db01f 100644
--- a/paramecio2/libraries/db/corefields.py
+++ b/paramecio2/libraries/db/corefields.py
@@ -172,6 +172,20 @@ class TextField(PhangoField):
return 'TEXT '+self.set_default
+class LongTextField(TextField):
+ """Class used for long text fields (32 bits size, 4G)
+
+ Class used for text fields, use LONGTEXT sql type for the this field.
+ """
+
+ def get_type_sql(self):
+
+ """Method for return the sql code for this type
+
+ """
+
+ return 'LONGTEXT '+self.set_default
+
class HTMLField(TextField):
"""Class used for html fields
diff --git a/paramecio2/libraries/db/coreforms.py b/paramecio2/libraries/db/coreforms.py
index ebbbb05..caed634 100644
--- a/paramecio2/libraries/db/coreforms.py
+++ b/paramecio2/libraries/db/coreforms.py
@@ -42,12 +42,13 @@ class BaseForm:
self.error=False
self.name_field_id=self.name+'_form'
self.help=''
+ self.placeholder=''
def form(self):
"""Method for returm the html code of the form
"""
- return ''
+ return ''
def show_formatted(self, value):
"""Method for show the value of form formatted
diff --git a/paramecio2/libraries/db/extrafields/datetimefield.py b/paramecio2/libraries/db/extrafields/datetimefield.py
index 10511d6..c68d028 100644
--- a/paramecio2/libraries/db/extrafields/datetimefield.py
+++ b/paramecio2/libraries/db/extrafields/datetimefield.py
@@ -26,16 +26,17 @@ class DateTimeField(PhangoField):
value=datetime.local_to_gmt(value)
elif not datetime.obtain_timestamp(value):
-
+
self.error=True
self.txt_error=self.error_default
- return ''
+
+ return '0000-00-00 00:00:00'
if value==False:
self.error=True
self.txt_error=self.error_default
- return ''
+ return '0000-00-00 00:00:00'
else:
"""
diff --git a/paramecio2/libraries/db/extrafields/imagefield.py b/paramecio2/libraries/db/extrafields/imagefield.py
index f8c58cd..4728b0f 100644
--- a/paramecio2/libraries/db/extrafields/imagefield.py
+++ b/paramecio2/libraries/db/extrafields/imagefield.py
@@ -5,8 +5,10 @@ from paramecio2.libraries.db.corefields import CharField
from paramecio2.libraries.db.extraforms.fileform import FileForm
from paramecio2.libraries.keyutils import create_key
import traceback
+from flask import request
+from werkzeug.utils import secure_filename
-from bottle import request
+# from bottle import request
try:
from PIL import Image
except:
@@ -63,61 +65,67 @@ class ImageField(CharField):
pass
def check(self, value):
-
+
files_uploaded=request.files
field_file=self.name+'_file'
#if not change
- if not field_file in files_uploaded:
-
- if value=='':
+ if field_file in files_uploaded:
+
+ if files_uploaded[field_file].filename=='':
- if self.model:
+ if value=='':
- if self.model.updated:
+ if self.model:
- old_reset=self.model.yes_reset_conditions
-
- self.model.yes_reset_conditions=False
-
- with self.model.select([self.name]) as cur:
-
- for arr_image in cur:
-
- if arr_image[self.name]!='':
- try:
- os.remove(arr_image[self.name])
- except:
- pass
-
- #if arr_image[self.name]!=save_file and arr_image[self.name]!='':
-
- #value=arr_image[self.name]
-
- self.model.yes_reset_conditions=old_reset
- self.txt_error='Field is empty'
- self.error=True
-
- return ''
+ if self.model.updated:
+
+ old_reset=self.model.yes_reset_conditions
+
+ self.model.yes_reset_conditions=False
+
+ with self.model.select([self.name]) as cur:
+
+ for arr_image in cur:
+
+ if arr_image[self.name]!='':
+ try:
+ os.remove(arr_image[self.name])
+ except:
+ pass
+
+ #if arr_image[self.name]!=save_file and arr_image[self.name]!='':
+
+ #value=arr_image[self.name]
+
+ self.model.yes_reset_conditions=old_reset
+ self.txt_error='Field is empty'
+ self.error=True
+
+ return ''
- else:
-
- value=os.path.basename(value)
-
- return self.save_folder+'/'+value
+ else:
+
+ value=os.path.basename(value)
+
+ return self.save_folder+'/'+value
+ else:
+ value=os.path.basename(value)
+
+ return self.save_folder+'/'+value
# Load image file
- file_bytecode=files_uploaded[field_file].file
+ #file_bytecode=files_uploaded[field_file].file
- filename=files_uploaded[field_file].filename
+ filename=secure_filename(files_uploaded[field_file].filename)
try:
- im=Image.open(file_bytecode)
+ im=Image.open(files_uploaded[field_file])
except IOError:
diff --git a/paramecio2/libraries/db/extrafields/jsonfield.py b/paramecio2/libraries/db/extrafields/jsonfield.py
index e90221d..80897ff 100644
--- a/paramecio2/libraries/db/extrafields/jsonfield.py
+++ b/paramecio2/libraries/db/extrafields/jsonfield.py
@@ -72,6 +72,7 @@ class JsonValueField(PhangoField):
super().__init__(name, required)
self.error_default='Sorry, the json dict is invalid'
+ self.default_value={}
#self.set_default='NOT NULL'
@@ -86,7 +87,7 @@ class JsonValueField(PhangoField):
except json.JSONDecodeError:
- final_value={}
+ final_value='{}'
self.error=True
self.txt_error=self.error_default
diff --git a/paramecio2/libraries/generate_admin_class.py b/paramecio2/libraries/generate_admin_class.py
index 23101d7..3497132 100644
--- a/paramecio2/libraries/generate_admin_class.py
+++ b/paramecio2/libraries/generate_admin_class.py
@@ -72,6 +72,8 @@ class GenerateAdminClass:
self.url_redirect=self.url
+ self.pre_update=None
+
self.post_update=None
self.text_home=I18n.lang('common', 'home', 'Home')
@@ -131,6 +133,13 @@ class GenerateAdminClass:
insert_row=self.model.insert
+ pre_update_ret=False
+
+ if not self.pre_update:
+ pre_update_ret=True
+ else:
+ pre_update_ret=self.pre_update(self)
+
try:
item_id=str(int(request.args.get('id', '0')))
@@ -149,21 +158,36 @@ class GenerateAdminClass:
post=dict(request.form)
- if insert_row(post):
- flash(I18n.lang('common', 'task_successful', 'Task successful'))
-
- if self.post_update:
- if item_id=='0':
- item_id=self.model.insert_id()
- self.post_update(self, item_id)
-
- return redirect(self.url_redirect)
+ if pre_update_ret:
+
+ if insert_row(post):
+ flash(I18n.lang('common', 'task_successful', 'Task successful'))
+
+ if self.post_update:
+ if item_id=='0':
+ item_id=self.model.insert_id()
+ self.post_update(self, item_id)
+
+ return redirect(self.url_redirect)
+
+ else:
+
+ url_action=add_get_parameters(self.url, op_admin=2, id=item_id)
+
+ post=dict(request.form)
+
+ form=show_form(post, edit_forms, self.t, True)
+
+ return self.t.load_template(self.template_insert, admin=self, title_edit=title_edit, form=form, model=self.model, id=item_id, url_action=url_action, enctype=self.model.enctype)
+
+
else:
url_action=add_get_parameters(self.url, op_admin=2, id=item_id)
post=dict(request.form)
form=show_form(post, edit_forms, self.t, True)
+
return self.t.load_template(self.template_insert, admin=self, title_edit=title_edit, form=form, model=self.model, id=item_id, url_action=url_action, enctype=self.model.enctype)
diff --git a/paramecio2/libraries/plugins.py b/paramecio2/libraries/plugins.py
index 1438355..81d4dd0 100644
--- a/paramecio2/libraries/plugins.py
+++ b/paramecio2/libraries/plugins.py
@@ -1,4 +1,4 @@
-from flask import g
+from flask import g, session, redirect, url_for
from functools import wraps
from paramecio2.libraries.db.webmodel import WebModel
diff --git a/paramecio2/modules/admin/admin/ausers.py b/paramecio2/modules/admin/admin/ausers.py
index 644e640..23274da 100644
--- a/paramecio2/modules/admin/admin/ausers.py
+++ b/paramecio2/modules/admin/admin/ausers.py
@@ -4,6 +4,7 @@ from paramecio2.modules.admin.models.admin import UserAdmin
from paramecio2.libraries.generate_admin_class import GenerateAdminClass
from paramecio2.libraries.i18n import I18n
from paramecio2.libraries.db.coreforms import SelectForm
+from paramecio2.libraries.db.coreforms import HiddenForm
import copy
from paramecio2.modules.admin import admin_app, t as admin_t
@@ -22,7 +23,9 @@ def ausers():
user_admin.fields['double_auth'].name_form=SelectForm
- user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'disabled', 'double_auth'])
+ user_admin.fields['last_login'].name_form=HiddenForm
+
+ user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'disabled', 'double_auth', 'last_login'])
user_admin.forms['privileges'].arr_select={0: I18n.lang('admin', 'without_privileges', 'Without privileges'), 1: I18n.lang('admin', 'selected_privileges', 'Selected privileges'), 2: I18n.lang('admin', 'administrator', 'Administrator')}
@@ -43,7 +46,7 @@ def ausers():
admin.list.search_fields=['username']
- admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'lang', 'double_auth', 'disabled']
+ admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'lang', 'double_auth', 'disabled', 'last_login']
form_admin=admin.show()
diff --git a/paramecio2/modules/admin/app.py b/paramecio2/modules/admin/app.py
index 042d3dc..a4755f2 100644
--- a/paramecio2/modules/admin/app.py
+++ b/paramecio2/modules/admin/app.py
@@ -239,6 +239,8 @@ def login():
#user_admin.set_conditions('WHERE id=%s', [arr_user['id']]).update({'token_auth': token_auth})
+ user_admin.fields['token_auth'].protected=False
+
arr_update['token_auth']=token_auth
# Send email
@@ -303,7 +305,9 @@ def signup():
forms['privileges']=2
- user_admin.valid_fields=['username', 'email', 'password', 'privileges']
+ forms['last_login']=now()
+
+ user_admin.valid_fields=['username', 'email', 'password', 'privileges', 'last_login']
user_admin.create_forms()
@@ -357,6 +361,8 @@ def auth_check():
check_csrf()
+ you_cannot_login=0
+
if 'login_admin' in session:
code=request.form.get('code', '')
@@ -365,23 +371,35 @@ def auth_check():
user_admin.check_user=False
- c=user_admin.set_conditions('WHERE id=%s AND token_auth=%s', [session['user_id'], code]).select_count()
+ arr_user=user_admin.set_conditions('WHERE id=%s', [session.get('user_id', 0)]).select_a_row_where()
- if c==1:
+ if arr_user:
- user_admin.safe_query()
+ if user_admin.fields['token_auth'].verify(code, arr_user['token_auth']):
- user_admin.set_conditions('WHERE id=%s', [session['user_id']]).update({'token_auth': ''})
+ user_admin.safe_query()
+
+ user_admin.set_conditions('WHERE id=%s', [session['user_id']]).update({'token_auth': ''})
+
+ session['verify_auth']=True
+ error=0
+
+ else:
- session['verify_auth']=True
- error=0
+ you_cannot_login=check_login_tries()
+
+ else:
+
+ you_cannot_login=check_login_tries()
- return {'error': error}
+ return {'error': error, 'you_cannot_login': you_cannot_login}
+"""
@admin_app.route('/admin/recovery_password/')
def recovery_password():
return ""
+"""
def check_login_tries():
diff --git a/paramecio2/modules/admin/models/admin.py b/paramecio2/modules/admin/models/admin.py
index 6b39303..9433fd7 100644
--- a/paramecio2/modules/admin/models/admin.py
+++ b/paramecio2/modules/admin/models/admin.py
@@ -84,7 +84,7 @@ class UserAdmin(UserModel):
self.register(corefields.CharField('token_login'))
- self.register(corefields.CharField('token_auth'))
+ self.register(PasswordField('token_auth'))
self.register(PrivilegesField('privileges'))
diff --git a/paramecio2/modules/admin/templates/dashboard.phtml b/paramecio2/modules/admin/templates/dashboard.phtml
index 2cd227d..eb3b224 100644
--- a/paramecio2/modules/admin/templates/dashboard.phtml
+++ b/paramecio2/modules/admin/templates/dashboard.phtml
@@ -38,7 +38,8 @@ ${load_js()|n}
portal_admin_name_set=('Paramecio', 'Framework!')
- #if hasattr(config, 'portal_admin_name_set'):
+ if hasattr(config, 'portal_admin_name_set'):
+ portal_admin_name_set=('Paramecio', 'Framework!')
%>
${portal_admin_name_set[0]} ${portal_admin_name_set[1]}
diff --git a/paramecio2/modules/admin/templates/need_auth.phtml b/paramecio2/modules/admin/templates/need_auth.phtml
index 897643d..aedd37b 100644
--- a/paramecio2/modules/admin/templates/need_auth.phtml
+++ b/paramecio2/modules/admin/templates/need_auth.phtml
@@ -50,7 +50,7 @@
}
else
{
- $('#code_submit').prop('disabled', true);
+ $('#code_submit').prop('disabled', false);
// Firefox have a horrible and stupid bug and you need attr for set de new csrf_token
@@ -68,6 +68,12 @@
}
+ if(data.you_cannot_login) {
+
+ $('#code_error').html("${lang('common', 'error_tries_disabled', 'Error, excessive tries, wait some minutes for login again')}");
+
+ }
+
}
});
diff --git a/setup.py b/setup.py
index f6254b5..ecbec2f 100644
--- a/setup.py
+++ b/setup.py
@@ -5,15 +5,15 @@ import os
from setuptools import setup, find_packages
-if sys.version_info < (3, 6):
- raise NotImplementedError("Sorry, you need at least Python 3.6 for use paramecio2.")
+if sys.version_info < (3, 8):
+ raise NotImplementedError("Sorry, you need at least Python 3.8 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.21',
+ version='2.0.27',
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',
@@ -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.5',
- 'Programming Language :: Python :: 3.6',
- 'Programming Language :: Python :: 3.7',
- 'Programming Language :: Python :: 3.6'
+ 'Programming Language :: Python :: 3.8'
+ 'Programming Language :: Python :: 3.9'
+ 'Programming Language :: Python :: 3.10'
+ 'Programming Language :: Python :: 3.11'
],
)