modern #2
|
|
@ -8,7 +8,7 @@ import re
|
|||
from pathlib import Path
|
||||
from base64 import b64encode
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.modules.admin.models.admin import UserAdmin
|
||||
from paramecio.modules.admin2.models.admin import UserAdmin2
|
||||
from subprocess import call
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
|
@ -265,7 +265,7 @@ def start():
|
|||
|
||||
f.close()
|
||||
|
||||
config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin', 'paramecio.modules.lang']")
|
||||
config_text=config_text.replace("modules=['paramecio.modules.welcome']", "modules=['paramecio.modules.welcome', 'paramecio.modules.admin2', 'paramecio.modules.lang']")
|
||||
|
||||
with open(path_settings+'/config.py', 'w') as f:
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ class DateTimeField(PhangoField):
|
|||
self.error_default='Error: Date format invalid in %s' % self.name
|
||||
|
||||
self.type_sql='datetime'
|
||||
|
||||
self.default_value='0000-00-00 00:00:00'
|
||||
|
||||
def check(self, value):
|
||||
|
||||
|
|
@ -68,6 +70,9 @@ class DateTimeField(PhangoField):
|
|||
|
||||
value=datetime.format_local_strtime('YYYY-MM-DD HH:mm:ss', value)
|
||||
|
||||
if value=='':
|
||||
return '0000-00-00 00:00:00'
|
||||
|
||||
return value
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from mako.template import Template
|
|||
from mako.lookup import TemplateLookup
|
||||
from paramecio.libraries.urls import make_url, make_url_domain, make_media_url, make_media_url_module, add_get_parameters
|
||||
from paramecio.libraries.i18n import I18n, PGetText
|
||||
from paramecio.libraries.sessions import get_session
|
||||
from paramecio.libraries.sessionplugin import get_session
|
||||
from paramecio.libraries.adminutils import make_admin_url
|
||||
from paramecio.libraries.db.formsutils import csrf_token
|
||||
from paramecio.libraries.js import make_js_url
|
||||
|
|
@ -448,7 +448,7 @@ class HeaderHTML:
|
|||
|
||||
s['flash']=''
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
return message
|
||||
|
||||
|
|
@ -458,7 +458,7 @@ def set_flash_message(message):
|
|||
|
||||
s['flash']=message
|
||||
|
||||
s.save()
|
||||
#s.save()
|
||||
|
||||
def qf(text):
|
||||
|
||||
|
|
|
|||
304
paramecio/modules/admin2/app.py
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
|
||||
#from paramecio import wsgi_app
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.mtemplates import env_theme, PTemplate
|
||||
from paramecio.modules.admin2.models.admin import UserAdmin2, LoginTries2
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.libraries.db import simplequery
|
||||
from settings import config
|
||||
from paramecio.libraries.datetime import now, format_local_strtime, timestamp_to_datetime, obtain_timestamp
|
||||
from paramecio.libraries.keyutils import create_key_encrypt, create_key
|
||||
from time import time
|
||||
from paramecio.wsgiapp import app
|
||||
#from paramecio.modules.admin2 import admin_app
|
||||
from bottle import request, redirect, Bottle
|
||||
from paramecio.modules.admin2.libraries.loginplugin import check_login
|
||||
from paramecio.libraries.sessionplugin import SessionPlugin
|
||||
from paramecio.libraries.httputils import GetPostFiles
|
||||
from paramecio.libraries.db.formsutils import check_form, csrf_token
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
|
||||
admin_app=Bottle()
|
||||
admin_app.install(SessionPlugin())
|
||||
admin_app.install(check_login)
|
||||
|
||||
env=env_theme(__file__)
|
||||
t=PTemplate(env)
|
||||
|
||||
usermodel=UserAdmin2()
|
||||
|
||||
usermodel.create_forms()
|
||||
|
||||
login_tries=5
|
||||
|
||||
if hasattr(config, 'login_tries'):
|
||||
login_tries=config.login_tries
|
||||
|
||||
seconds_login=300
|
||||
|
||||
if hasattr(config, 'seconds_login'):
|
||||
seconds_login=config.seconds_login
|
||||
|
||||
if hasattr(config, 'cookie_name'):
|
||||
cookie_name=config.cookie_name
|
||||
|
||||
@admin_app.get('/test', skip=[check_login])
|
||||
def test_session(session={}):
|
||||
|
||||
return session
|
||||
|
||||
|
||||
@admin_app.get('/admin', name="admin_app.home_admin")
|
||||
def home_admin(session={}):
|
||||
|
||||
#s=get_session()
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
return t.load_template('layout.phtml', title=i18n.tlang('Admin'), module_selected='home_admin', session=session)
|
||||
#return {}
|
||||
|
||||
|
||||
@admin_app.get('/admin/login', name="admin_app.login_admin", skip=[check_login])
|
||||
def login_admin(session={}):
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
if cookie_name+'_remember' in request.cookies:
|
||||
|
||||
arr_user=simplequery.select(usermodel, db, dict_fields=['id', 'username'], where_sql='WHERE token_login=%s', dict_values=[request.cookies[cookie_name+'_remember']])
|
||||
|
||||
if len(arr_user)>0:
|
||||
now_str=now()
|
||||
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
|
||||
|
||||
db.query('update useradmin2 set last_login=%s WHERE id=%s', [date_now, arr_user[0]['id']])
|
||||
|
||||
request.session['login_admin']=True
|
||||
|
||||
db.close()
|
||||
|
||||
redirect(app.get_url('app_admin.home_admin'))
|
||||
|
||||
with db.query('select count(id) as num_users from useradmin2', []) as cursor:
|
||||
num_users=cursor.fetchone()['num_users']
|
||||
|
||||
if not num_users:
|
||||
redirect(app.get_url('admin_app.signup_admin'))
|
||||
|
||||
db.close()
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
return t.load_template('login.phtml', title=i18n.tlang('Login'))
|
||||
|
||||
@admin_app.get('/admin/signup', skip=[check_login], name='admin_app.signup_admin')
|
||||
def signup_admin(session={}):
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
with db.query('select count(id) as num_users from useradmin2', []) as cursor:
|
||||
num_users=cursor.fetchone()['num_users']
|
||||
|
||||
if num_users>0:
|
||||
redirect(app.url_path_for('login_admin'))
|
||||
|
||||
db.close()
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
return t.load_template('signup.phtml', title=i18n.tlang('Signup'))
|
||||
|
||||
@admin_app.post('/admin/login', skip=[check_login], name='admin_app.check_login_admin')
|
||||
def check_login_admin(session={}):
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
error=1
|
||||
|
||||
message=i18n.tlang('Invalid user and password')
|
||||
|
||||
no_login=check_login_tries(request, db)
|
||||
|
||||
#username=request.json.get('username', '')
|
||||
#password=request.json.get('password', '')
|
||||
#remember_login=request.json.get('remember_login', '')
|
||||
getpost=GetPostFiles()
|
||||
|
||||
getpost.obtain_post()
|
||||
|
||||
username=getpost.post.get('username')
|
||||
password=getpost.post.get('password')
|
||||
remember_login=getpost.post.get('remember_login')
|
||||
|
||||
if username!='' and password!='' and not no_login:
|
||||
|
||||
with db.query('select * from useradmin2 WHERE username=%s', [username]) as cursor:
|
||||
result=cursor.fetchone()
|
||||
|
||||
if result:
|
||||
|
||||
if usermodel.fields['password'].verify(password, result['password']):
|
||||
|
||||
remember_key=''
|
||||
|
||||
if remember_login==True:
|
||||
# Send cookies
|
||||
|
||||
remember_key=create_key_encrypt()
|
||||
|
||||
timestamp=int(time())+315360000
|
||||
|
||||
response.set_cookie(key=cookie_name+'_remember', value=remember_key, expires=timestamp, max_age=315360000, httponly=True, path=config.application_root)
|
||||
|
||||
now_str=now()
|
||||
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
|
||||
|
||||
db.query('update useradmin2 set token_login=%s, last_login=%s WHERE id=%s', [remember_key, date_now, result['id']])
|
||||
|
||||
session['login_admin']=True
|
||||
session['user_id']=result['id']
|
||||
session['theme']=result['dark_theme']
|
||||
error=0
|
||||
message=''
|
||||
|
||||
db.close()
|
||||
|
||||
return {'error': error, 'message': message, 'no_login': no_login}
|
||||
|
||||
@admin_app.post('/admin/signup', skip=[check_login], name='admin_app.signup_insert_admin')
|
||||
def signup_insert_admin(session={}):
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
error=1
|
||||
|
||||
message=''
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
arr_form={'username': usermodel.forms['username'], 'password': usermodel.forms['password'], 'repeat_password': PasswordForm('repeat_password', ''), 'email': usermodel.forms['email']}
|
||||
|
||||
getpost=GetPostFiles()
|
||||
|
||||
getpost.obtain_post()
|
||||
|
||||
#Only can exist and user
|
||||
|
||||
final_password=getpost.post.get('password', '')
|
||||
|
||||
(error, error_form, post, arr_form)=check_form(getpost.post, arr_form, sufix_form='_error')
|
||||
|
||||
post['password']=final_password
|
||||
|
||||
with db.query('select count(id) as num_users from useradmin2', []) as cursor:
|
||||
num_users=cursor.fetchone()['num_users']
|
||||
|
||||
if num_users:
|
||||
message="You cannot add new users from here"
|
||||
else:
|
||||
error=0
|
||||
|
||||
if not error:
|
||||
|
||||
if simplequery.insert(usermodel, db, dict(post)):
|
||||
#if 1:
|
||||
error=0
|
||||
message="User added!"
|
||||
else:
|
||||
message=i18n.tlang('Sorry, cannot create the new user')
|
||||
|
||||
db.close()
|
||||
|
||||
return {'error': error, 'message': message}
|
||||
|
||||
@admin_app.get('/admin/logout', name="admin_app.logout_admin", skip=[check_login])
|
||||
def logout_admin(session={}):
|
||||
|
||||
if login_admin in session:
|
||||
del session['login_admin']
|
||||
|
||||
if cookie_name+'_remember' in request.cookies:
|
||||
response.delete_cookie(cookie_name+'_remember', path=session_opts['session.path'])
|
||||
|
||||
redirect(app.get_url('admin_app.login_admin'))
|
||||
|
||||
@admin_app.get('/change_lang')
|
||||
def change_lang():
|
||||
|
||||
error=0
|
||||
|
||||
message=''
|
||||
|
||||
return {'error': 0, 'message': ''}
|
||||
|
||||
@admin_app.get('/admin/change_theme', name="admin_app.change_theme")
|
||||
def change_theme(session={}):
|
||||
|
||||
try:
|
||||
theme=int(request.query.get('theme', 0))
|
||||
except:
|
||||
theme=0
|
||||
|
||||
error=1
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
session['theme']=theme
|
||||
|
||||
db.query('update useradmin2 set dark_theme=%s WHERE id=%s', [theme, session['user_id']])
|
||||
|
||||
error=0
|
||||
|
||||
db.close()
|
||||
|
||||
return {'error': error, 'message': ''}
|
||||
|
||||
|
||||
def check_login_tries(request, db):
|
||||
|
||||
logintries=LoginTries2(db)
|
||||
|
||||
logintries.safe_query()
|
||||
|
||||
ip=request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
|
||||
|
||||
"""
|
||||
if 'x-real-ip' in request.headers:
|
||||
ip=request.headers['x-real-ip']
|
||||
elif 'x-forwarded-for' in request.headers:
|
||||
ip=request.headers['x-forwarded-for']
|
||||
else:
|
||||
ip=request.client.host
|
||||
"""
|
||||
|
||||
you_cannot_login=0
|
||||
|
||||
now_str=now()
|
||||
date_now=format_local_strtime('YYYY-MM-DD HH:mm:ss', now_str)
|
||||
|
||||
date_check=format_local_strtime('YYYY-MM-DD HH:mm:ss', timestamp_to_datetime(obtain_timestamp(now_str)-seconds_login))
|
||||
|
||||
logintries.query('delete from logintries2 where last_login<%s', [date_check])
|
||||
|
||||
arr_try=logintries.set_conditions('WHERE ip=%s', [ip]).select_a_row_where()
|
||||
|
||||
if arr_try:
|
||||
|
||||
if arr_try['num_tries']<login_tries:
|
||||
|
||||
logintries.query('update logintries2 set num_tries=num_tries+1, last_login=%s WHERE ip=%s', [date_now, ip])
|
||||
|
||||
else:
|
||||
|
||||
you_cannot_login=1
|
||||
|
||||
else:
|
||||
|
||||
logintries.query('insert into logintries2 (`ip`, `num_tries`, `last_login`) VALUES (%s, %s, %s)', [ip, 1, date_now])
|
||||
|
||||
return you_cannot_login
|
||||
|
||||
app.merge(admin_app)
|
||||
78
paramecio/modules/admin2/ausers.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
from paramecio.modules.admin2.models.admin import UserAdmin2
|
||||
from paramecio.modules.admin2.app import admin_app
|
||||
from paramecio.modules.admin2.libraries.config import modules_admin, modules_admin_icons
|
||||
from paramecio.libraries.mtemplates import PTemplate, env_theme
|
||||
from paramecio.libraries.i18n import I18n
|
||||
import paramecio.modules.admin2.libraries.i18n
|
||||
import os
|
||||
#from paramecio.libraries.lists import SimpleList
|
||||
from paramecio.libraries.generate_admin_class import GenerateAdminClass
|
||||
from paramecio.libraries.db.formsutils import show_form
|
||||
from paramecio.libraries.db.coreforms import SelectForm
|
||||
from paramecio.libraries.db.coreforms import PasswordForm
|
||||
from paramecio.wsgiapp import app
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
from paramecio.modules.admin2.models.admin import UserAdmin2
|
||||
|
||||
env=env_theme(__file__)
|
||||
|
||||
t=PTemplate(env)
|
||||
|
||||
#t.env.directories=admin_t.env.directories
|
||||
"""
|
||||
tpl_path=os.path.dirname(__file__).replace('/admin', '')+'/templates/admin'
|
||||
|
||||
if t.env.directories[1]!=tpl_path:
|
||||
t.env.directories.insert(1, tpl_path)
|
||||
"""
|
||||
#modules_admin.append(['menu_users', 'people-circle', True])
|
||||
|
||||
modules_admin.append(['admin_app.admin_users', 'people-circle'])
|
||||
|
||||
modules_admin_icons.append('<symbol id="people-circle" viewBox="0 0 16 16"><path d="M11 6a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/><path fill-rule="evenodd" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm8-7a7 7 0 0 0-5.468 11.37C3.242 11.226 4.805 10 8 10s4.757 1.225 5.468 2.37A7 7 0 0 0 8 1z"/></symbol>')
|
||||
|
||||
@admin_app.get('/admin/ausers', name="admin_app.admin_users")
|
||||
@admin_app.post('/admin/ausers', name="admin_app.admin_users")
|
||||
def admin_users(session={}):
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
db=WebModel.connection()
|
||||
|
||||
user_admin=UserAdmin2(db)
|
||||
|
||||
user_admin.fields['privileges'].name_form=SelectForm
|
||||
|
||||
user_admin.fields['dark_theme'].name_form=SelectForm
|
||||
|
||||
user_admin.create_forms(['username', 'password', 'email', 'privileges', 'lang', 'dark_theme'])
|
||||
|
||||
user_admin.forms['privileges'].arr_select={0: I18n.lang('admin', 'without_privileges', 'Without privileges'), 1: I18n.lang('admin', 'selected_privileges', 'Selected privileges'), 2: I18n.lang('admin', 'administrator', 'Administrator')}
|
||||
|
||||
user_admin.forms['dark_theme'].arr_select={0: _('Light theme'), 1: _('Dark theme')}
|
||||
|
||||
user_admin.fields['password'].protected=False
|
||||
|
||||
url=app.get_url('admin_app.admin_users')
|
||||
|
||||
admin=GenerateAdminClass(user_admin, url, t)
|
||||
|
||||
admin.list.fields_showed=['username', 'email', 'double_auth', 'privileges', 'last_login']
|
||||
|
||||
admin.list.search_fields=['username']
|
||||
|
||||
admin.arr_fields_edit=['username', 'password', 'repeat_password', 'email', 'privileges', 'lang', 'dark_theme']
|
||||
|
||||
#slist=SimpleList(users, url, t)
|
||||
|
||||
#slist.fields_showed=['username', 'email', 'double_auth', 'last_login']
|
||||
|
||||
#slist=slist.show()
|
||||
|
||||
slist=admin.show()
|
||||
|
||||
db.close()
|
||||
|
||||
return t.load_template('users.phtml', title=i18n.tlang('Admin users'), tlang=i18n.tlang, module_selected='admin_app.admin_users', slist=slist, session=session)
|
||||
|
||||
app.merge(admin_app)
|
||||
10
paramecio/modules/admin2/libraries/config.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Every element
|
||||
|
||||
# ['Name of module admin', 'name_function_for_url_for', 'xml-icon']
|
||||
|
||||
modules_admin=[]
|
||||
|
||||
modules_admin_icons=[]
|
||||
|
||||
19
paramecio/modules/admin2/libraries/i18n.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from parameciofast.libraries.i18n import I18n
|
||||
|
||||
"""
|
||||
I18n.l['en-US']=I18n.l.get('en-US', {})
|
||||
|
||||
I18n.l['en-US']['fastadmin']=I18n.l['en-US'].get('fastadmin', {})
|
||||
|
||||
I18n.l['en-US']['fastadmin']['fastadmin_ausers']='Admin users'
|
||||
|
||||
I18n.l['es-ES']=I18n.l.get('es-ES', {})
|
||||
|
||||
I18n.l['es-ES']['fastadmin']=I18n.l['en-US'].get('fastadmin', {})
|
||||
|
||||
I18n.l['es-ES']['fastadmin']['fastadmin_ausers']='Usuarios admin'
|
||||
"""
|
||||
|
||||
I18n.add_lang('en-US', 'admin2', 'admin_app.admin_users', 'Admin users')
|
||||
I18n.add_lang('es-ES', 'admin2', 'admin_app.admin_users', 'Usuarios admin')
|
||||
|
||||
66
paramecio/modules/admin2/libraries/loginplugin.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
from bottle import request, response, redirect
|
||||
from settings import config
|
||||
import inspect
|
||||
from paramecio.wsgiapp import app
|
||||
|
||||
def check_login(callback):
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
if 'session' in request.environ:
|
||||
|
||||
if request.environ['session'].get('login_admin', 0):
|
||||
|
||||
result = callback(*args, **kwargs)
|
||||
|
||||
return result
|
||||
|
||||
redirect(app.get_url('admin_app.login_admin'))
|
||||
|
||||
return wrapper
|
||||
"""
|
||||
class CheckLoginPlugin(object):
|
||||
|
||||
name = 'checklogin'
|
||||
api = 2
|
||||
|
||||
def __init__(self, keyword='session'):
|
||||
|
||||
self.keyword=keyword
|
||||
|
||||
|
||||
def setup(self, app):
|
||||
''' Make sure that other installed plugins don't affect the same keyword argument.'''
|
||||
for other in app.plugins:
|
||||
if not isinstance(other, SessionPlugin): continue
|
||||
if other.keyword == self.keyword:
|
||||
raise PluginError("Found another login plugin with "\
|
||||
"conflicting settings (non-unique keyword).")
|
||||
|
||||
def apply(self, callback, context):
|
||||
|
||||
# Test if the original callback accepts a 'session' keyword.
|
||||
# Ignore it if it does not need a login handle.
|
||||
|
||||
conf = context.config.get('checklogin') or {}
|
||||
|
||||
keyword = conf.get('keyword', self.keyword)
|
||||
|
||||
args = inspect.getfullargspec(context.callback)[0]
|
||||
|
||||
if keyword not in args:
|
||||
return callback
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
|
||||
if 'session' in request.environ:
|
||||
|
||||
if request.environ['session'].get('login_admin', 1):
|
||||
|
||||
rv=callback(*args, **kwargs)
|
||||
|
||||
return rv
|
||||
|
||||
redirect(app.get_url('admin_app.home_admin'))
|
||||
|
||||
return wrapper
|
||||
"""
|
||||
1441
paramecio/modules/admin2/media/css/admin.css
Normal file
126
paramecio/modules/admin2/media/css/layout.css
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
|
||||
body {
|
||||
|
||||
background: #121c27;
|
||||
min-height: 100vh;
|
||||
min-height: -webkit-fill-available;
|
||||
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
height: 100vh;
|
||||
height: -webkit-fill-available;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
/*overflow-y: hidden;*/
|
||||
}
|
||||
|
||||
|
||||
html {
|
||||
height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
|
||||
#header_dashboard {
|
||||
|
||||
background: #1d2939;
|
||||
-webkit-box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.75);
|
||||
-moz-box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.75);
|
||||
box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.75);
|
||||
z-index:99999;
|
||||
|
||||
}
|
||||
|
||||
.bi {
|
||||
vertical-align: -.125em;
|
||||
pointer-events: none;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.hands {
|
||||
|
||||
vertical-align: -.140em !important;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Loader page */
|
||||
|
||||
.loader-div {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index:999999;
|
||||
/*background: url('../images/caledonian.png') no-repeat center 40%;
|
||||
background-color: #ec1c24;*/
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.loader {
|
||||
position: relative;
|
||||
width: 10vw;
|
||||
height: 5vw;
|
||||
padding: 1.5vw;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.loader span {
|
||||
position: absolute;
|
||||
height: 0.8vw;
|
||||
width: 0.8vw;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.loader span:nth-child(1) {
|
||||
animation: loading-dotsA 0.5s infinite linear;
|
||||
}
|
||||
|
||||
.loader span:nth-child(2) {
|
||||
animation: loading-dotsB 0.5s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes loading-dotsA {
|
||||
0% {
|
||||
transform: none;
|
||||
}
|
||||
25% {
|
||||
transform: translateX(2vw);
|
||||
}
|
||||
50% {
|
||||
transform: none;
|
||||
}
|
||||
75% {
|
||||
transform: translateY(2vw);
|
||||
}
|
||||
100% {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes loading-dotsB {
|
||||
0% {
|
||||
transform: none;
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-2vw);
|
||||
}
|
||||
50% {
|
||||
transform: none;
|
||||
}
|
||||
75% {
|
||||
transform: translateY(-2vw);
|
||||
}
|
||||
100% {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
49
paramecio/modules/admin2/media/css/responsive-nav.css
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*! responsive-nav.js 1.0.39 by @viljamis */
|
||||
|
||||
.nav-collapse ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
display: block;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-collapse li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.js .nav-collapse {
|
||||
clip: rect(0 0 0 0);
|
||||
max-height: 0;
|
||||
position: absolute;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.nav-collapse.opened {
|
||||
max-height: 9999px;
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 40em) {
|
||||
.js .nav-collapse {
|
||||
position: relative;
|
||||
}
|
||||
.js .nav-collapse.closed {
|
||||
max-height: none;
|
||||
}
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
1
paramecio/modules/admin2/media/css/tooltipster.bundle.min.css
vendored
Normal file
BIN
paramecio/modules/admin2/media/images/ajax-loader.gif
Normal file
|
After Width: | Height: | Size: 500 B |
BIN
paramecio/modules/admin2/media/images/background.png
Normal file
|
After Width: | Height: | Size: 332 B |
BIN
paramecio/modules/admin2/media/images/background_title.png
Normal file
|
After Width: | Height: | Size: 476 B |
BIN
paramecio/modules/admin2/media/images/background_title_login.png
Normal file
|
After Width: | Height: | Size: 5 KiB |
BIN
paramecio/modules/admin2/media/images/languages/en-US.png
Normal file
|
After Width: | Height: | Size: 562 B |
BIN
paramecio/modules/admin2/media/images/languages/es-ES.png
Normal file
|
After Width: | Height: | Size: 539 B |
BIN
paramecio/modules/admin2/media/images/logo.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
2
paramecio/modules/admin2/media/js/jquery-3.7.1.min.js
vendored
Normal file
1
paramecio/modules/admin2/media/js/responsive-nav.min.js
vendored
Normal file
2
paramecio/modules/admin2/media/js/tooltipster.bundle.min.js
vendored
Normal file
0
paramecio/modules/admin2/models/__init__.py
Normal file
76
paramecio/modules/admin2/models/admin.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from paramecio.libraries.i18n import I18n
|
||||
from paramecio.libraries.db.webmodel import WebModel
|
||||
#from paramecio.libraries.db.usermodel import UserModel
|
||||
from paramecio.libraries.db import corefields
|
||||
from paramecio.libraries.db.extrafields.emailfield import EmailField
|
||||
from paramecio.libraries.db.extrafields.passwordfield import PasswordField
|
||||
from paramecio.libraries.db.extrafields.langfield import LangField
|
||||
from paramecio.libraries.db.extrafields.ipfield import IpField
|
||||
from paramecio.libraries.db.extrafields.datetimefield import DateTimeField
|
||||
from paramecio.libraries.db.usermodel import UserModel
|
||||
|
||||
class PrivilegesField2(corefields.IntegerField):
|
||||
|
||||
def show_formatted(self, value):
|
||||
|
||||
value=int(value)
|
||||
|
||||
if value==0:
|
||||
return I18n.lang('admin', 'without_privileges', 'Without privileges')
|
||||
elif value==1:
|
||||
return I18n.lang('admin', 'selected_privileges', 'Selected privileges')
|
||||
elif value==2:
|
||||
return I18n.lang('admin', 'administrator', 'Administrator')
|
||||
|
||||
class UserAdmin2(UserModel):
|
||||
|
||||
#def create_fields(self):
|
||||
def __init__(self, connection=None):
|
||||
|
||||
super().__init__(connection)
|
||||
|
||||
# I can change other fields here, how the name.
|
||||
|
||||
self.register(corefields.CharField('username'))
|
||||
|
||||
self.fields['username'].required=True
|
||||
|
||||
self.register(PasswordField('password'))
|
||||
|
||||
self.fields['password'].required=True
|
||||
|
||||
self.register(EmailField('email'))
|
||||
|
||||
self.fields['email'].required=True
|
||||
|
||||
self.register(corefields.CharField('token_recovery'))
|
||||
|
||||
self.register(corefields.CharField('token_login'))
|
||||
|
||||
#self.register(PasswordField('token_auth'))
|
||||
|
||||
self.register(PrivilegesField2('privileges'))
|
||||
|
||||
self.register(LangField('lang', 20))
|
||||
|
||||
self.register(corefields.BooleanField('disabled'))
|
||||
|
||||
self.register(corefields.BooleanField('double_auth'))
|
||||
|
||||
self.register(corefields.BooleanField('dark_theme'))
|
||||
|
||||
#self.register(corefields.IntegerField('num_tries', 1))
|
||||
|
||||
self.register(DateTimeField('last_login'))
|
||||
|
||||
class LoginTries2(WebModel):
|
||||
|
||||
def __init__(self, connection=None):
|
||||
|
||||
super().__init__(connection)
|
||||
self.register(IpField('ip'))
|
||||
self.register(corefields.IntegerField('num_tries', 1))
|
||||
self.register(DateTimeField('last_login'))
|
||||
|
||||
176
paramecio/modules/admin2/templates/layout.phtml
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
<%
|
||||
|
||||
from paramecio.modules.admin2.libraries.config import modules_admin, modules_admin_icons
|
||||
from parameciofast.libraries.i18n import I18n
|
||||
|
||||
i18n=I18n('admin2')
|
||||
|
||||
dark_checked=""
|
||||
dark_css=""
|
||||
|
||||
if session.get('theme', '0')==True:
|
||||
dark_checked='checked'
|
||||
dark_css='dark'
|
||||
|
||||
|
||||
%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||
<title>${title}</title>
|
||||
<link href="${make_media_url('css/admin.css', 'admin2')}" rel="stylesheet" />
|
||||
<link href="${make_media_url('css/responsive-nav.css', 'admin2')}" rel="stylesheet" />
|
||||
<link href="${make_media_url('css/tooltipster.bundle.min.css', 'admin2')}" rel="stylesheet" />
|
||||
<!--<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"> -->
|
||||
<%block name="extra_css">
|
||||
</%block>
|
||||
<script language="Javascript" src="${make_media_url('js/jquery-3.7.1.min.js', 'admin2')}"></script>
|
||||
<script language="Javascript" src="${make_media_url('js/responsive-nav.min.js', 'admin2')}"></script>
|
||||
<script language="Javascript" src="${make_media_url('js/tooltipster.bundle.min.js', 'admin2')}"></script>
|
||||
<%block name="extra_js">
|
||||
</%block>
|
||||
<%block name="extra_header">
|
||||
</%block>
|
||||
</head>
|
||||
<body class="${dark_css}">
|
||||
<div id="layer_loading"><div id="container_loading"><div class="lds-dual-ring"></div></div></div>
|
||||
<div id="languages_general">
|
||||
</div>
|
||||
<div id="logout">
|
||||
<%block name="logout"><a href="${url_for('admin_app.logout_admin')}"><i class="fa fa-power-off" aria-hidden="true"></i> Logout</a></%block>
|
||||
</div>
|
||||
|
||||
<div id="center_body">
|
||||
<div id="header">
|
||||
<a href="#nav" id="toggle"><i class="fa fa-bars" aria-hidden="true"></i><span>Menu</span></a>
|
||||
<%block name="title_admin"><span id="title_phango">Admin</span> <span id="title_framework">site!</span>
|
||||
</%block>
|
||||
<%block name="languages">
|
||||
<div id="languages_general">
|
||||
<%def name="select_lang(i18n, lang_selected)">${'choose_flag' if i18n==lang_selected else 'no_choose_flag'}</%def>
|
||||
</%block>
|
||||
% if lang_selected!=None:
|
||||
% for i18n_lang in i18n.dict_i18n:
|
||||
<a class="${select_lang(i18n_lang, lang_selected)}" href=""><img src="${make_media_url('images/languages/'+i18n_lang+'.png', 'admin2')}" alt="${i18n_lang}"/></a>
|
||||
% endfor
|
||||
% endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="content_admin">
|
||||
<nav id="menu" class="nav-collapse">
|
||||
<ul>
|
||||
<li class="menu_title"><%block name="applications"><i class="fa fa-gear" aria-hidden="true"></i>${tlang('Applications')}</li></%block>
|
||||
% for module in modules_admin:
|
||||
<li>
|
||||
% if len(module)>2:
|
||||
<div class="father_admin">
|
||||
<svg class="bi me-2" width="16" height="16"><use xlink:href="#${module[1]}"></use></svg>
|
||||
${i18n.clang('admin2', module[0], module[0])}
|
||||
</div>
|
||||
% else:
|
||||
<a href="${url_for(module[0])}" class="${'selected_menu' if module[0]==module_selected else ''}">
|
||||
<svg class="bi me-2" width="16" height="16"><use xlink:href="#${module[1]}"></use></svg>
|
||||
${i18n.clang('admin2', module[0], module[0])}
|
||||
</a>
|
||||
% endif
|
||||
</li>
|
||||
% endfor
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="contents">
|
||||
<h1>${title}</h1>
|
||||
<div class="switch-btn">
|
||||
<div class="switch-text">
|
||||
${_('Dark theme')}
|
||||
</div>
|
||||
<div class="switch-slider">
|
||||
<label class="switch">
|
||||
<input type="checkbox" name="theme" value="1" id="theme" ${dark_checked}/>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
|
||||
<%block name="content">
|
||||
</%block>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading_ajax">
|
||||
</div>
|
||||
<script>
|
||||
var navigation = responsiveNav(".nav-collapse", {customToggle: "#toggle"});
|
||||
|
||||
$('.tooltip').tooltipster({
|
||||
animation: 'fade',
|
||||
delay: 100,
|
||||
trigger: 'click'
|
||||
});
|
||||
|
||||
const slider = document.querySelector('input[name="theme"]');
|
||||
|
||||
slider.addEventListener("change", function () {
|
||||
|
||||
//Block button while send to ajax.
|
||||
|
||||
$(this).prop("disabled",true);
|
||||
|
||||
var dark='';
|
||||
|
||||
if (this.checked) {
|
||||
document.body.classList.add("dark");
|
||||
|
||||
dark='1';
|
||||
|
||||
} else {
|
||||
document.body.classList.remove("dark");
|
||||
|
||||
dark='0';
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "${url_for('admin_app.change_theme')}?theme="+dark,
|
||||
type: 'GET',
|
||||
data: {},
|
||||
success: function (data) {
|
||||
|
||||
if(!data.error) {
|
||||
|
||||
console.log('Changed to dark in all pages');
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
console.log('Cannot set dark theme in all pages!');
|
||||
|
||||
}
|
||||
|
||||
$(slider).prop("disabled",false);
|
||||
|
||||
},
|
||||
error: function (data) {
|
||||
|
||||
alert('Error: '+data.status+' '+data.statusText);
|
||||
|
||||
$(slider).prop("disabled", false);
|
||||
|
||||
},
|
||||
dataType: 'json'
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#layer_loading').hide();
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
<%block name="jscript_block">
|
||||
</%block>
|
||||
</body>
|
||||
</html>
|
||||
123
paramecio/modules/admin2/templates/layout_bs.phtml
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<%
|
||||
|
||||
from parameciofast.modules.fastadmin.libraries.config import modules_admin, modules_admin_icons
|
||||
from parameciofast.libraries.i18n import I18n
|
||||
|
||||
i18n=I18n('fastadmin')
|
||||
|
||||
%>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${title}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="${make_media_url('css/layout.css', 'fastadmin')}" type="text/css" media="all" />
|
||||
<%block name="css">
|
||||
</%block>
|
||||
<%block name="header_js">
|
||||
</%block>
|
||||
</head>
|
||||
<body data-bs-theme="dark">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||||
<symbol id="home" viewBox="0 0 16 16">
|
||||
<path d="M8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4.5a.5.5 0 0 0 .5-.5v-4h2v4a.5.5 0 0 0 .5.5H14a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146zM2.5 14V7.707l5.5-5.5 5.5 5.5V14H10v-4a.5.5 0 0 0-.5-.5h-3a.5.5 0 0 0-.5.5v4H2.5z"/>
|
||||
</symbol>
|
||||
%for module_icon in modules_admin_icons:
|
||||
|
||||
${module_icon|n}
|
||||
|
||||
%endfor
|
||||
</svg>
|
||||
<div class="loader-div" id="loader-div" style="display:none;">
|
||||
<span class="loader">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
<main>
|
||||
<div class="container-fluid">
|
||||
<div id="header_dashboard" class="row pt-3 pb-3">
|
||||
<div class="col-sm-6 m-0 d-flex align-content-center flex-wrap align-self-center">
|
||||
<h2 class="text-left ms-4 mt-0 mt-0 p-0" id="form_title">${tlang('Dashboard')}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 p-3 m-0 text-white bg-dark" style="height:91vh;">
|
||||
<!--<a href="/" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
|
||||
<svg class="bi me-2" width="40" height="32"><use xlink:href="#bootstrap"></use></svg>
|
||||
<span class="fs-4">Sidebar</span>
|
||||
</a>-->
|
||||
<hr>
|
||||
<!--<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li>
|
||||
<a href="#" class="nav-link active text-white">
|
||||
<svg class="bi me-2" width="16" height="16"><use xlink:href="#speedometer2"></use></svg>
|
||||
${tlang('Dashboard')}
|
||||
</a>
|
||||
</li>
|
||||
</ul>-->
|
||||
% for module in modules_admin:
|
||||
<ul class="nav nav-pills flex-column mb-auto">
|
||||
<li>
|
||||
<a href="${url_for(module[0])}" class="nav-link text-white menu_item ${'active' if module[0]==module_selected else ''}" id="menu_${module[0]}">
|
||||
<svg class="bi me-2" width="16" height="16"><use xlink:href="#${module[1]}"></use></svg>
|
||||
${i18n.clang('fastadmin', module[0], module[0])}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
% endfor
|
||||
<hr>
|
||||
|
||||
</div>
|
||||
<div class="col-sm m-2 pt-3" style="">
|
||||
<%block name="content">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary bg-gradient">
|
||||
${tlang('Welcome to admin')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>${tlang('This is the admin section of your site.')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<footer class="footer m-0 p-0">
|
||||
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.documentElement.setAttribute('data-bs-theme','dark');
|
||||
|
||||
setTimeout(function () {
|
||||
|
||||
$('#loader-div').fadeOut(2000);
|
||||
|
||||
|
||||
}, 1000);
|
||||
|
||||
$('.menu_item').hover( function (e) {
|
||||
|
||||
if($(this).hasClass('active')) {
|
||||
|
||||
$(this).removeClass('active');
|
||||
|
||||
} else {
|
||||
|
||||
$(this).addClass('active');
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
<%block name="js">
|
||||
</%block>
|
||||
</body>
|
||||
</html>
|
||||
171
paramecio/modules/admin2/templates/login.phtml
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>${title}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<link href="${make_media_url('css/layout.css', 'admin2')}" rel="stylesheet" />
|
||||
<%block name="css">
|
||||
</%block>
|
||||
<%block name="header_js">
|
||||
</%block>
|
||||
</head>
|
||||
<body data-bs-theme="dark">
|
||||
<div class="loader-div" id="loader-div" style="display:none;">
|
||||
<span class="loader">
|
||||
<span></span>
|
||||
<span></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="container-fluid m-0 p-0">
|
||||
<div class="d-flex align-items-center" style="height:100vh;">
|
||||
<div class="col-3"></div>
|
||||
<div class="col">
|
||||
<%block name="content">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary">
|
||||
${tlang('Login')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="${url_for('admin_app.check_login_admin')}" id="login_form" class="needs-validation" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="username_form" class="form-label">${tlang('Username')}</label>
|
||||
<input type="text" class="form-control form-control-lg has-validation" id="username_form" name="password" aria-describedby="username" autocomplete="off" required>
|
||||
<div class="invalid-feedback">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password_form" class="form-label">Password</label>
|
||||
<input type="password" class="form-control form-control-lg has-validation" id="password_form" name="password" autocomplete="off" required>
|
||||
<div class="invalid-feedback" id="login_invalid">
|
||||
${tlang('Error: username or password invalid')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="remember_login_form" name="remember_login" value="1">
|
||||
<label class="form-check-label" for="autologin">${tlang('Remember login')}</label>
|
||||
</div>
|
||||
<button type="submit" id="login_submit" class="btn btn-primary">Submit</button>
|
||||
${csrf_token()|n}
|
||||
</form>
|
||||
</div>
|
||||
</%block>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js" integrity="sha384-0pUGZvbkm6XF6gxjEnlmuGrJXVbNuzT9qBBavbLwCsOGabYfZo0T0to5eqruptLy" crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
document.documentElement.setAttribute('data-bs-theme','dark');
|
||||
</script>
|
||||
<%block name="jscript">
|
||||
<script>
|
||||
|
||||
/*setTimeout(function () {
|
||||
|
||||
$('#loader-div').fadeOut(2000);
|
||||
|
||||
|
||||
}, 1000);*/
|
||||
|
||||
$(document).ready( function () {
|
||||
|
||||
$('#login_form').submit( function (event) {
|
||||
|
||||
$('#username_form').get(0).setCustomValidity("");
|
||||
$('#password_form').get(0).setCustomValidity("");
|
||||
|
||||
form=document.getElementById('login_form');
|
||||
|
||||
error=false;
|
||||
|
||||
if (!form.checkValidity()) {
|
||||
|
||||
error=true;
|
||||
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
form.classList.add('was-validated');
|
||||
|
||||
if(!error) {
|
||||
|
||||
$('#loader-div').show();
|
||||
|
||||
$('#login_submit').prop('disabled', true);
|
||||
|
||||
const is_checked=document.getElementById('remember_login_form').checked;
|
||||
|
||||
data_form={'username': $('#username_form').val(), 'password': $('#password_form').val(), 'csrf_token': $("#csrf_token").val(), 'remember_login': is_checked};
|
||||
|
||||
$.ajax({
|
||||
url: "${url_for('admin_app.check_login_admin')}",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
contentType : 'application/json',
|
||||
data: JSON.stringify(data_form),
|
||||
error: function (data) {
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
$('#loader-div').hide();
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
alert('${tlang("Error: please, try again later")}');
|
||||
|
||||
},
|
||||
}).done(function(data) {
|
||||
|
||||
if(data.error==0)
|
||||
{
|
||||
|
||||
//location.reload()
|
||||
location.href="${url_for('admin_app.home_admin')}";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
$('#username_form').get(0).setCustomValidity("${tlang('Error: username or password invalid')}");
|
||||
$('#password_form').get(0).setCustomValidity("${tlang('Error: username or password invalid')}");
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
|
||||
if(data.no_login) {
|
||||
|
||||
$('#login_invalid').html("${tlang('Error: you try login excessive times, please wait some minutes for try again')}");
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$('#login_invalid').html("${tlang('Error: username or password invalid')}");
|
||||
|
||||
}
|
||||
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
$('#loader-div').hide();
|
||||
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</%block>
|
||||
</body>
|
||||
</html>
|
||||
186
paramecio/modules/admin2/templates/signup.phtml
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
<%inherit file="login.phtml"/>
|
||||
<%block name="content">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary">
|
||||
${tlang('Signup')}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="${url_for('admin_app.signup_insert_admin')}" id="login_form" class="needs-validation" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="username_form" class="form-label">${tlang('Username')}*</label>
|
||||
<input type="text" class="form-control form-control-lg has-validation" id="username_form" name="password" aria-describedby="username" autocomplete="off" minlength="4" pattern="\w{4,32}" required>
|
||||
<div class="invalid-feedback">
|
||||
${tlang('You need a valid username')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email_form" class="form-label">${tlang('Email')}*</label>
|
||||
<input type="email" class="form-control form-control-lg has-validation" id="email_form" name="email" autocomplete="off" minlength="4" required>
|
||||
<div class="invalid-feedback">
|
||||
${tlang('You need an email')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password_form" class="form-label">Password*</label>
|
||||
<input type="password" class="form-control form-control-lg has-validation" id="password_form" name="password" autocomplete="off" minlength="2" required>
|
||||
<div class="invalid-feedback">
|
||||
${tlang('You need a password')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="repeat_password_form" class="form-label">${tlang('Repeat password')}*</label>
|
||||
<input type="password" class="form-control form-control-lg has-validation" id="repeat_password_form" name="repeat_password" autocomplete="off" minlength="2" required>
|
||||
<div class="invalid-feedback">
|
||||
${tlang('You need the same password in this field and not empty')}
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" id="login_submit" class="btn btn-primary">${tlang('Create user')}</button>
|
||||
${csrf_token()|n}
|
||||
</form>
|
||||
</div>
|
||||
</%block>
|
||||
<%block name="jscript">
|
||||
<script>
|
||||
$(document).ready( function () {
|
||||
|
||||
$('#repeat_password_form').on('input',function(e){
|
||||
|
||||
if($('#repeat_password_form').val()!=$('#password_form').val()) {
|
||||
|
||||
console.log('No match');
|
||||
|
||||
$('#repeat_password_form').get(0).setCustomValidity("${tlang('Passwords doesn\'t match')}");
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
$('#repeat_password_form').get(0).setCustomValidity("");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Fetch all the forms we want to apply custom Bootstrap validation styles to
|
||||
var forms = document.querySelectorAll('.needs-validation');
|
||||
var error=false;
|
||||
|
||||
// Loop over them and prevent submission
|
||||
Array.prototype.slice.call(forms)
|
||||
.forEach(function (form) {
|
||||
form.addEventListener('submit', function (event) {
|
||||
|
||||
error=false;
|
||||
|
||||
if (!form.checkValidity()) {
|
||||
|
||||
error=true;
|
||||
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
form.classList.add('was-validated');
|
||||
|
||||
var form_data=new FormData(form);
|
||||
|
||||
if(!error) {
|
||||
|
||||
console.log('send submit');
|
||||
|
||||
$('#loader-div').show();
|
||||
|
||||
$('#login_submit').prop('disabled', true);
|
||||
|
||||
data_form={'username': $('#username_form').val(), 'email': $('#email_form').val(), 'password': $('#password_form').val(), 'repeat_password': $('#repeat_password_form').val(), 'csrf_token': $("#csrf_token").val()};
|
||||
|
||||
$.ajax({
|
||||
url: "${url_for('admin_app.signup_insert_admin')}",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
contentType : 'application/json',
|
||||
data: JSON.stringify(data_form),
|
||||
error: function (data) {
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
$('#loader-div').hide();
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
alert('${tlang("Error: please, try again later")}');
|
||||
|
||||
},
|
||||
}).done(function(data) {
|
||||
|
||||
if(data.error==0)
|
||||
{
|
||||
|
||||
//location.reload()
|
||||
location.href="${url_for('admin_app.login_admin')}";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
$('#loader-div').hide();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
)
|
||||
|
||||
});
|
||||
|
||||
/*$('#login_form').submit( function () {
|
||||
|
||||
$('#loader-div').show();
|
||||
|
||||
$('#login_submit').prop('disabled', true);
|
||||
|
||||
data_form={'username': $('#username_form').val(), 'email': $('#email_form').val(), 'password': $('#password_form').val(), 'repeat_password': $('#repeat_password_form').val(), 'csrf_token': $("#csrf_token").val()};
|
||||
|
||||
$.ajax({
|
||||
url: "${url_for('admin_app.signup_insert_admin')}",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
contentType : 'application/json',
|
||||
data: JSON.stringify(data_form),
|
||||
error: function (data) {
|
||||
|
||||
console.log(JSON.stringify(data));
|
||||
$('#loader-div').hide();
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
},
|
||||
}).done(function(data) {
|
||||
|
||||
if(data.error==0)
|
||||
{
|
||||
|
||||
//location.reload()
|
||||
location.href="${url_for('admin_app.home_admin')}";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
$('#login_submit').prop('disabled', false);
|
||||
|
||||
$('#loader-div').hide();
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return false;
|
||||
|
||||
});*/
|
||||
|
||||
});
|
||||
</script>
|
||||
</%block>
|
||||
4
paramecio/modules/admin2/templates/users.phtml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<%inherit file="layout.phtml"/>
|
||||
<%block name="content">
|
||||
${slist|n}
|
||||
</%block>
|
||||
|
|
@ -4,7 +4,7 @@ list_modules=[]
|
|||
|
||||
from paramecio.modules.welcome import index
|
||||
|
||||
from paramecio.modules.admin import index
|
||||
from paramecio.modules.admin2 import index
|
||||
|
||||
from paramecio.modules.lang import index
|
||||
|
||||
|
|
|
|||