Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afdb028070 | ||
|
|
80d4615f32 | ||
|
|
5a12a7fb31 | ||
|
|
17c10b8a45 | ||
|
|
f8e2c8e319 | ||
|
|
9ea82fdaaf | ||
|
|
3d01a83c85 | ||
|
|
13f8b1a10c | ||
|
|
f78e172eda | ||
|
|
bd452f90b2 | ||
|
|
2b1f150f02 | ||
|
|
e7a9457249 | ||
|
|
d3ab2e73d5 | ||
|
|
860f40ca63 | ||
|
|
59a7db56a4 | ||
|
|
01e5107a96 | ||
|
|
8765dbbe0f | ||
|
|
d125b0b95b | ||
|
|
1d2fc4b8f5 | ||
|
|
146ea115bf | ||
|
|
4f44188997 | ||
|
|
ac5fb303eb | ||
|
|
31a14997f8 | ||
|
|
4cd4ff2c5e | ||
|
|
26ef508966 | ||
|
|
28b4badb9a | ||
|
|
8e2e2420b0 | ||
|
|
67349627f4 | ||
|
|
cfe825f94a | ||
|
|
53e79915fa | ||
|
|
9fb5f688ed | ||
|
|
c2cdae0615 | ||
|
|
2516bb59a1 | ||
|
|
13c78fae58 | ||
|
|
91c0a31800 | ||
|
|
277137c8e6 | ||
|
|
4556a355a2 | ||
|
|
7dafb07438 | ||
|
|
3c8e7a1364 | ||
|
|
921cd46c10 | ||
|
|
b0f6dc4e16 | ||
|
|
1499b19575 | ||
|
|
877b867cef | ||
|
|
837808bd77 | ||
|
|
10a69f4338 | ||
|
|
7bc603f380 | ||
|
|
6c1cc6fc96 |
@@ -1,4 +1,4 @@
|
||||
## 2.6.1
|
||||
## 2.6.1 - 2.6.4
|
||||
|
||||
Attention all users: For pre 2.6 applications to work with web2py >=2.6, you must copy static/js/web2py.js, controllers/appadmin.py, and views/appadmin.html from the welcome app to your own apps (all of them).
|
||||
|
||||
@@ -38,6 +38,8 @@ Attention MySQL users: The length of string fields changed from 255 to 512 bytes
|
||||
- speedup for define_table, thanks Michele
|
||||
- settings.cfg to admin, thanks Paolo
|
||||
- many bugs fixed, thanks Niphlod, Michele, Roberto, Jonathan, and many others
|
||||
- 2.6,3 specifically fixed a possible DoS vulnerability
|
||||
- 2.6.4 specifically fixes major problem introduced in 2.6.1 with session logic
|
||||
|
||||
## 2.5.2
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -30,7 +30,7 @@ update:
|
||||
echo "remember that pymysql was tweaked"
|
||||
src:
|
||||
### Use semantic versioning
|
||||
echo 'Version 2.6.1-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
|
||||
echo 'Version 2.6.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
|
||||
### rm -f all junk files
|
||||
make clean
|
||||
### clean up baisc apps
|
||||
|
||||
2
VERSION
2
VERSION
@@ -1 +1 @@
|
||||
Version 2.6.1-stable+timestamp.2013.09.12.17.08.08
|
||||
Version 2.6.4-stable+timestamp.2013.09.21.20.41.28
|
||||
|
||||
@@ -50,7 +50,7 @@ if request.function == 'manage':
|
||||
auth.requires_membership(manager_role)(lambda: None)()
|
||||
menu = False
|
||||
elif (request.application == 'admin' and not session.authorized) or \
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
redirect(URL('admin', 'default', 'index',
|
||||
vars=dict(send=URL(args=request.args, vars=request.vars))))
|
||||
else:
|
||||
@@ -540,30 +540,30 @@ def table_template(table):
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -580,14 +580,14 @@ def bg_graph_model():
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
def manage():
|
||||
|
||||
@@ -212,7 +212,7 @@ def toggle_breakpoint():
|
||||
except Exception, e:
|
||||
session.flash = str(e)
|
||||
return response.json({'ok': ok, 'lineno': lineno})
|
||||
|
||||
|
||||
def list_breakpoints():
|
||||
"Return a list of linenumbers for current breakpoints"
|
||||
|
||||
@@ -234,4 +234,3 @@ def list_breakpoints():
|
||||
session.flash = str(e)
|
||||
ok = False
|
||||
return response.json({'ok': ok, 'breakpoints': breakpoints})
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ def safe_write(a, value, b='w'):
|
||||
|
||||
def get_app(name=None):
|
||||
app = name or request.args(0)
|
||||
if (app and os.path.exists(apath(app, r=request)) and
|
||||
if (app and os.path.exists(apath(app, r=request)) and
|
||||
(not MULTI_USER_MODE or is_manager() or
|
||||
db(db.app.name == app)(db.app.owner == auth.user.id).count())):
|
||||
return app
|
||||
@@ -108,7 +108,7 @@ def index():
|
||||
if session.authorized:
|
||||
redirect(send)
|
||||
elif request.vars.password:
|
||||
if verify_password(request.vars.password):
|
||||
if verify_password(request.vars.password[:1024]):
|
||||
session.authorized = True
|
||||
login_record(True)
|
||||
|
||||
@@ -583,11 +583,11 @@ def edit():
|
||||
|
||||
# show settings tab and save prefernces
|
||||
if 'settings' in request.vars:
|
||||
if request.post_vars: #save new preferences
|
||||
if request.post_vars: #save new preferences
|
||||
if config.save(request.post_vars.items()):
|
||||
response.headers["web2py-component-flash"] = T('Preferences saved correctly')
|
||||
else:
|
||||
response.headers["web2py-component-flash"] = T('Preferences saved on session only')
|
||||
response.headers["web2py-component-flash"] = T('Preferences saved on session only')
|
||||
response.headers["web2py-component-command"] = "update_theme('%s'); jQuery('a[href=#editor_settings] button.close').click();" % config.read()['theme']
|
||||
return
|
||||
else:
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
'%s %%{row} updated': '%s filas actualizadas',
|
||||
'%Y-%m-%d': '%Y-%m-%d',
|
||||
'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S',
|
||||
'(requires internet access, experimental)': '(requires internet access, experimental)',
|
||||
'(something like "it-it")': '(algo como "it-it")',
|
||||
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)',
|
||||
'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files',
|
||||
@@ -32,6 +33,7 @@
|
||||
'application "%s" uninstalled': 'aplicación "%s" desinstalada',
|
||||
'application compiled': 'aplicación compilada',
|
||||
'application is compiled and cannot be designed': 'la aplicación está compilada y no puede ser modificada',
|
||||
'Application name:': 'Application name:',
|
||||
'are not used': 'are not used',
|
||||
'are not used yet': 'are not used yet',
|
||||
'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?',
|
||||
@@ -47,6 +49,7 @@
|
||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.',
|
||||
'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que se ejecuta!',
|
||||
'Autocomplete': 'Autocomplete',
|
||||
'Autocomplete Python Code': 'Autocomplete Python Code',
|
||||
'Available databases and tables': 'Bases de datos y tablas disponibles',
|
||||
'back': 'atrás',
|
||||
'breakpoint': 'breakpoint',
|
||||
@@ -54,14 +57,17 @@
|
||||
'browse': 'buscar',
|
||||
'cache': 'cache',
|
||||
'cache, errors and sessions cleaned': 'cache, errores y sesiones eliminados',
|
||||
'can be a git repo': 'can be a git repo',
|
||||
'Cannot be empty': 'No puede estar vacío',
|
||||
'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.',
|
||||
'Cannot compile: there are errors in your app:': 'No se puede compilar: hay errores en su aplicación:',
|
||||
'cannot create file': 'no es posible crear archivo',
|
||||
'cannot upload file "%(filename)s"': 'no es posible subir archivo "%(filename)s"',
|
||||
'Change admin password': 'cambie contraseña admin',
|
||||
'change editor settings': 'change editor settings',
|
||||
'Change Password': 'Cambie Contraseña',
|
||||
'check all': 'marcar todos',
|
||||
'Check for upgrades': 'Check for upgrades',
|
||||
'Check to delete': 'Marque para eliminar',
|
||||
'Checking for upgrades...': 'Buscando actulizaciones...',
|
||||
'Clean': 'limpiar',
|
||||
@@ -91,6 +97,7 @@
|
||||
'Current request': 'Solicitud en curso',
|
||||
'Current response': 'Respuesta en curso',
|
||||
'Current session': 'Sesión en curso',
|
||||
'currently running': 'currently running',
|
||||
'currently saved or': 'actualmente guardado o',
|
||||
'customize me!': 'Adaptame!',
|
||||
'data uploaded': 'datos subidos',
|
||||
@@ -107,14 +114,21 @@
|
||||
'delete plugin': 'eliminar plugin',
|
||||
'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)',
|
||||
'Delete:': 'Elimine:',
|
||||
'Deploy': 'Deploy',
|
||||
'Deploy on Google App Engine': 'Instale en Google App Engine',
|
||||
'Deploy to OpenShift': 'Deploy to OpenShift',
|
||||
'Description': 'Descripción',
|
||||
'design': 'modificar',
|
||||
'DESIGN': 'DISEÑO',
|
||||
'Design for': 'Diseño para',
|
||||
'Detailed traceback description': 'Detailed traceback description',
|
||||
'details': 'details',
|
||||
'direction: ltr': 'direction: ltr',
|
||||
'Disable': 'Disable',
|
||||
'docs': 'docs',
|
||||
'done!': 'listo!',
|
||||
'Download': 'Download',
|
||||
'download files via http:': 'download files via http:',
|
||||
'download layouts': 'download layouts',
|
||||
'download plugins': 'download plugins',
|
||||
'E-mail': 'Correo electrónico',
|
||||
@@ -122,17 +136,25 @@
|
||||
'Edit': 'editar',
|
||||
'Edit application': 'Editar aplicación',
|
||||
'edit controller': 'editar controlador',
|
||||
'edit controller:': 'edit controller:',
|
||||
'Edit current record': 'Edite el registro actual',
|
||||
'Edit Profile': 'Editar Perfil',
|
||||
'edit views:': 'editar vistas:',
|
||||
'Editing file': 'Editando archivo',
|
||||
'Editing file "%s"': 'Editando archivo "%s"',
|
||||
'Editing Language file': 'Editando archivo de lenguaje',
|
||||
'Editing myclientapi': 'Editing myclientapi',
|
||||
'Editing myemail': 'Editing myemail',
|
||||
'Editing rbare': 'Editing rbare',
|
||||
'Editing ul': 'Editing ul',
|
||||
'Enterprise Web Framework': 'Armazón Empresarial para Internet',
|
||||
'Error': 'Error',
|
||||
'Error logs for "%(app)s"': 'Bitácora de errores en "%(app)s"',
|
||||
'Error snapshot': 'Error snapshot',
|
||||
'Error ticket': 'Error ticket',
|
||||
'Errors': 'errores',
|
||||
'Exception instance attributes': 'Atributos de la instancia de Excepción',
|
||||
'Expand Abbreviation': 'Expand Abbreviation',
|
||||
'export as csv file': 'exportar como archivo CSV',
|
||||
'exposes': 'expone',
|
||||
'exposes:': 'exposes:',
|
||||
@@ -154,6 +176,7 @@
|
||||
'Find Next': 'Find Next',
|
||||
'Find Previous': 'Find Previous',
|
||||
'First name': 'Nombre',
|
||||
'Frames': 'Frames',
|
||||
'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].',
|
||||
'Globals##debug': 'Globals',
|
||||
'graph model': 'graph model',
|
||||
@@ -166,6 +189,7 @@
|
||||
'includes': 'incluye',
|
||||
'insert new': 'inserte nuevo',
|
||||
'insert new %s': 'inserte nuevo %s',
|
||||
'inspect attributes': 'inspect attributes',
|
||||
'Install': 'instalar',
|
||||
'Installed applications': 'Aplicaciones instaladas',
|
||||
'Interaction at %s line %s': 'Interaction at %s line %s',
|
||||
@@ -175,10 +199,12 @@
|
||||
'Invalid action': 'Acción inválida',
|
||||
'Invalid email': 'Correo inválido',
|
||||
'invalid password': 'contraseña inválida',
|
||||
'invalid password.': 'invalid password.',
|
||||
'Invalid Query': 'Consulta inválida',
|
||||
'invalid request': 'solicitud inválida',
|
||||
'invalid ticket': 'tiquete inválido',
|
||||
'Key bindings': 'Key bindings',
|
||||
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
|
||||
'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado',
|
||||
'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados',
|
||||
'languages': 'lenguajes',
|
||||
@@ -188,6 +214,7 @@
|
||||
'Last saved on:': 'Guardado en:',
|
||||
'License for': 'Licencia para',
|
||||
'loading...': 'cargando...',
|
||||
'locals': 'locals',
|
||||
'Locals##debug': 'Locals',
|
||||
'Login': 'Inicio de sesión',
|
||||
'login': 'inicio de sesión',
|
||||
@@ -195,6 +222,7 @@
|
||||
'Logout': 'fin de sesión',
|
||||
'Lost Password': 'Contraseña perdida',
|
||||
'manage': 'manage',
|
||||
'Manage': 'Manage',
|
||||
'merge': 'combinar',
|
||||
'Models': 'Modelos',
|
||||
'models': 'modelos',
|
||||
@@ -202,16 +230,22 @@
|
||||
'modules': 'módulos',
|
||||
'Name': 'Nombre',
|
||||
'new application "%s" created': 'nueva aplicación "%s" creada',
|
||||
'New application wizard': 'New application wizard',
|
||||
'new plugin installed': 'nuevo plugin instalado',
|
||||
'New Record': 'Registro nuevo',
|
||||
'new record inserted': 'nuevo registro insertado',
|
||||
'New simple application': 'New simple application',
|
||||
'next': 'next',
|
||||
'next 100 rows': '100 filas siguientes',
|
||||
'NO': 'NO',
|
||||
'No databases in this application': 'No hay bases de datos en esta aplicación',
|
||||
'No Interaction yet': 'No Interaction yet',
|
||||
'no match': 'no encontrado',
|
||||
'no package selected': 'no package selected',
|
||||
'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder',
|
||||
'online designer': 'online designer',
|
||||
'or alternatively': 'or alternatively',
|
||||
'Or Get from URL:': 'Or Get from URL:',
|
||||
'or import from csv file': 'o importar desde archivo CSV',
|
||||
'or provide app url:': 'o provea URL de la aplicación:',
|
||||
'or provide application url:': 'o provea URL de la aplicación:',
|
||||
@@ -220,6 +254,7 @@
|
||||
'Overwrite installed app': 'sobreescriba aplicación instalada',
|
||||
'Pack all': 'empaquetar todo',
|
||||
'Pack compiled': 'empaquete compiladas',
|
||||
'Pack custom': 'Pack custom',
|
||||
'pack plugin': 'empaquetar plugin',
|
||||
'PAM authenticated user, cannot change password here': 'usuario autenticado por PAM, no puede cambiar la contraseña aquí',
|
||||
'Password': 'Contraseña',
|
||||
@@ -236,6 +271,7 @@
|
||||
'Private files': 'Private files',
|
||||
'private files': 'private files',
|
||||
'Query:': 'Consulta:',
|
||||
'Rapid Search': 'Rapid Search',
|
||||
'record': 'registro',
|
||||
'record does not exist': 'el registro no existe',
|
||||
'record id': 'id de registro',
|
||||
@@ -244,11 +280,14 @@
|
||||
'Register': 'Registrese',
|
||||
'Registration key': 'Contraseña de Registro',
|
||||
'reload': 'reload',
|
||||
'Reload routes': 'Reload routes',
|
||||
'Remove compiled': 'eliminar compiladas',
|
||||
'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s',
|
||||
'Replace': 'Replace',
|
||||
'Replace All': 'Replace All',
|
||||
'request': 'request',
|
||||
'Resolve Conflict file': 'archivo Resolución de Conflicto',
|
||||
'response': 'response',
|
||||
'restore': 'restaurar',
|
||||
'return': 'return',
|
||||
'revert': 'revertir',
|
||||
@@ -256,26 +295,32 @@
|
||||
'Rows in table': 'Filas en la tabla',
|
||||
'Rows selected': 'Filas seleccionadas',
|
||||
'rules are not defined': 'rules are not defined',
|
||||
'Run tests in this file': 'Run tests in this file',
|
||||
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')",
|
||||
'Running on %s': 'Running on %s',
|
||||
'Save': 'Save',
|
||||
'save': 'guardar',
|
||||
'Save file:': 'Save file:',
|
||||
'Save file: %s': 'Save file: %s',
|
||||
'Save via Ajax': 'Save via Ajax',
|
||||
'Saved file hash:': 'Hash del archivo guardado:',
|
||||
'selected': 'seleccionado(s)',
|
||||
'session': 'session',
|
||||
'session expired': 'sesión expirada',
|
||||
'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s',
|
||||
'shell': 'shell',
|
||||
'Site': 'sitio',
|
||||
'some files could not be removed': 'algunos archivos no pudieron ser removidos',
|
||||
'Start searching': 'Start searching',
|
||||
'Start wizard': 'Start wizard',
|
||||
'state': 'estado',
|
||||
'static': 'estáticos',
|
||||
'Static': 'Static',
|
||||
'static': 'estáticos',
|
||||
'Static files': 'Archivos estáticos',
|
||||
'step': 'step',
|
||||
'stop': 'stop',
|
||||
'submit': 'enviar',
|
||||
'Submit': 'Submit',
|
||||
'successful': 'successful',
|
||||
'Sure you want to delete this object?': '¿Está seguro que desea eliminar este objeto?',
|
||||
'table': 'tabla',
|
||||
@@ -303,6 +348,7 @@
|
||||
'This is the %(filename)s template': 'Esta es la plantilla %(filename)s',
|
||||
'this page to see if a breakpoint was hit and debug interaction is required.': 'this page to see if a breakpoint was hit and debug interaction is required.',
|
||||
'Ticket': 'Tiquete',
|
||||
'Ticket ID': 'Ticket ID',
|
||||
'Timestamp': 'Timestamp',
|
||||
'TM': 'MR',
|
||||
'to previous version.': 'a la versión previa.',
|
||||
@@ -311,10 +357,13 @@
|
||||
'to use the debugger!': 'to use the debugger!',
|
||||
'toggle breakpoint': 'toggle breakpoint',
|
||||
'Toggle Fullscreen': 'Toggle Fullscreen',
|
||||
'Traceback': 'Traceback',
|
||||
'translation strings for the application': 'cadenas de caracteres de traducción para la aplicación',
|
||||
'Translation strings for the application': 'Translation strings for the application',
|
||||
'try': 'intente',
|
||||
'try something like': 'intente algo como',
|
||||
'Try the mobile interface': 'Try the mobile interface',
|
||||
'try view': 'try view',
|
||||
'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.',
|
||||
'Unable to check for upgrades': 'No es posible verificar la existencia de actualizaciones',
|
||||
'unable to create application "%s"': 'no es posible crear la aplicación "%s"',
|
||||
@@ -332,9 +381,12 @@
|
||||
'update': 'actualizar',
|
||||
'update all languages': 'actualizar todos los lenguajes',
|
||||
'Update:': 'Actualice:',
|
||||
'upgrade now to %s': 'upgrade now to %s',
|
||||
'upgrade web2py now': 'actualize web2py ahora',
|
||||
'Upload': 'Upload',
|
||||
'Upload & install packed application': 'Suba e instale aplicación empaquetada',
|
||||
'Upload a package:': 'Upload a package:',
|
||||
'Upload and install packed application': 'Upload and install packed application',
|
||||
'upload application:': 'subir aplicación:',
|
||||
'Upload existing application': 'Suba esta aplicación',
|
||||
'upload file:': 'suba archivo:',
|
||||
@@ -348,6 +400,7 @@
|
||||
'view': 'vista',
|
||||
'Views': 'Vistas',
|
||||
'views': 'vistas',
|
||||
'Web Framework': 'Web Framework',
|
||||
'web2py is up to date': 'web2py está actualizado',
|
||||
'web2py online debugger': 'web2py online debugger',
|
||||
'web2py Recent Tweets': 'Tweets Recientes de web2py',
|
||||
|
||||
@@ -49,7 +49,7 @@ def verify_password(password):
|
||||
session.pam_user = None
|
||||
if DEMO_MODE:
|
||||
return True
|
||||
elif not 'password' in _config:
|
||||
elif not _config.get('password'):
|
||||
return False
|
||||
elif _config['password'].startswith('pam_user:'):
|
||||
session.pam_user = _config['password'][9:].strip()
|
||||
|
||||
6
applications/admin/settings.cfg
Normal file
6
applications/admin/settings.cfg
Normal file
@@ -0,0 +1,6 @@
|
||||
[DEFAULT]
|
||||
theme = web2py
|
||||
|
||||
[editor]
|
||||
theme = web2py
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
position: absolute ! important;
|
||||
top: 0; left: 0;
|
||||
width: 100% ! important;
|
||||
z-index: 9999;
|
||||
z-index: 1028;
|
||||
}
|
||||
|
||||
/* BREAKPOINTS */
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
});
|
||||
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
|
||||
doc.on('click', "input.datetime", function () {
|
||||
var tformat = $(this).data('w2p_datetime_format')
|
||||
var tformat = $(this).data('w2p_datetime_format');
|
||||
var active = $(this).data('w2p_datetime');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
|
||||
if(active === undefined) {
|
||||
@@ -196,7 +196,7 @@
|
||||
});
|
||||
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
|
||||
doc.on('click', "input.date", function () {
|
||||
var tformat = $(this).data('w2p_date_format')
|
||||
var tformat = $(this).data('w2p_date_format');
|
||||
var active = $(this).data('w2p_date');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : date_format;
|
||||
if(active === undefined) {
|
||||
@@ -277,6 +277,8 @@
|
||||
ajax_page: function (method, action, data, target, element) {
|
||||
/* element is a new parameter, but should be put be put in front */
|
||||
if(element == undefined) element = $(document);
|
||||
/* if target is not there, fill it with something that there isn't in the page*/
|
||||
if(target == undefined || target == '') target = 'w2p_none';
|
||||
if(web2py.fire(element, 'ajax:before', null, target )) { /*test a usecase, should stop here if returns false */
|
||||
$.ajax({
|
||||
'type': method,
|
||||
@@ -303,12 +305,7 @@
|
||||
},
|
||||
'complete': function (xhr, status) {
|
||||
web2py.fire(element, 'ajax:complete', [xhr, status], target);
|
||||
var html = xhr.responseText;
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
var t = $('#' + target);
|
||||
if(content == 'prepend') t.prepend(html);
|
||||
else if(content == 'append') t.append(html);
|
||||
else if(content != 'hide') t.html(html);
|
||||
web2py.updatePage(xhr, target); /* Parse and load the html received */
|
||||
web2py.trap_form(action, target);
|
||||
web2py.trap_link(target);
|
||||
web2py.ajax_init('#' + target);
|
||||
@@ -378,6 +375,20 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
updatePage: function (xhr, target) {
|
||||
var t = $('#' + target);
|
||||
var html = $.parseHTML(xhr.responseText, document, true);
|
||||
var title_elements = $(html).filter('title').add($(html).find('title'));
|
||||
var title = title_elements.last().text();
|
||||
if (title) {
|
||||
title_elements.remove(); /* Remove any title elements from the response */
|
||||
document.title = $.trim(title); /* Set the new document title */
|
||||
}
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
if(content == 'prepend') t.prepend(xhr.responseText);
|
||||
else if(content == 'append') t.append(xhr.responseText);
|
||||
else if(content != 'hide') t.html(html);
|
||||
},
|
||||
calc_entropy: function (mystring) {
|
||||
/* calculate a simple entropy for a given string */
|
||||
var csets = new Array(
|
||||
@@ -569,9 +580,9 @@
|
||||
}
|
||||
if(target == undefined) {
|
||||
if(method == 'GET') {
|
||||
web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('get', action, [], '', el);
|
||||
} else if(method == 'POST') {
|
||||
web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('post', action, [], '', el);
|
||||
}
|
||||
} else {
|
||||
if(method == 'GET') {
|
||||
@@ -689,3 +700,4 @@ web2py_trap_link = jQuery.web2py.trap_link;
|
||||
web2py_calc_entropy = jQuery.web2py.calc_entropy;
|
||||
*/
|
||||
/* compatibility code - end*/
|
||||
|
||||
|
||||
@@ -30,6 +30,4 @@ jQuery(function(){
|
||||
hoverMenu(); // first page load
|
||||
jQuery(window).resize(hoverMenu); // on resize event
|
||||
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
|
||||
// make all buttons bootstrap buttons
|
||||
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn');
|
||||
});
|
||||
@@ -79,8 +79,8 @@
|
||||
return marker;
|
||||
}
|
||||
|
||||
{{if filetype=='html':}}
|
||||
// must be here or break emmet/zencoding for html
|
||||
{{if filetype in ('html', 'js', 'css'):}}
|
||||
// must be here or break emmet/zencoding
|
||||
CodeMirror.defaults.extraKeys["Ctrl-S"] =
|
||||
function(instance) {
|
||||
doClickSave();};
|
||||
|
||||
@@ -50,7 +50,7 @@ if request.function == 'manage':
|
||||
auth.requires_membership(manager_role)(lambda: None)()
|
||||
menu = False
|
||||
elif (request.application == 'admin' and not session.authorized) or \
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
redirect(URL('admin', 'default', 'index',
|
||||
vars=dict(send=URL(args=request.args, vars=request.vars))))
|
||||
else:
|
||||
@@ -540,30 +540,30 @@ def table_template(table):
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -580,14 +580,14 @@ def bg_graph_model():
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
def manage():
|
||||
|
||||
@@ -5,12 +5,12 @@ def get(args):
|
||||
return None
|
||||
try:
|
||||
obj = globals(),get(args[0])
|
||||
for k in range(1,len(args)):
|
||||
for k in range(1,len(args)):
|
||||
obj = getattr(obj,args[k])
|
||||
return obj
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def vars():
|
||||
"""the running controller function!"""
|
||||
title = '.'.join(request.args)
|
||||
@@ -27,7 +27,7 @@ def vars():
|
||||
d = getattr(obj,'__bases__',None)
|
||||
|
||||
for key in keys:
|
||||
a = getattr(obj,key,None)
|
||||
a = getattr(obj,key,None)
|
||||
if a and not isinstance(a,DAL):
|
||||
doc1 = getattr(a, '__doc__', '')
|
||||
t1 = type(a)
|
||||
@@ -38,7 +38,7 @@ def vars():
|
||||
else:
|
||||
doc = 'Unkown'
|
||||
keys = []
|
||||
t = c = d = None
|
||||
t = c = d = None
|
||||
else:
|
||||
raise HTTP(400)
|
||||
return dict(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
def hello1():
|
||||
""" simple page without template """
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
});
|
||||
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
|
||||
doc.on('click', "input.datetime", function () {
|
||||
var tformat = $(this).data('w2p_datetime_format')
|
||||
var tformat = $(this).data('w2p_datetime_format');
|
||||
var active = $(this).data('w2p_datetime');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
|
||||
if(active === undefined) {
|
||||
@@ -196,7 +196,7 @@
|
||||
});
|
||||
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
|
||||
doc.on('click', "input.date", function () {
|
||||
var tformat = $(this).data('w2p_date_format')
|
||||
var tformat = $(this).data('w2p_date_format');
|
||||
var active = $(this).data('w2p_date');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : date_format;
|
||||
if(active === undefined) {
|
||||
@@ -277,6 +277,8 @@
|
||||
ajax_page: function (method, action, data, target, element) {
|
||||
/* element is a new parameter, but should be put be put in front */
|
||||
if(element == undefined) element = $(document);
|
||||
/* if target is not there, fill it with something that there isn't in the page*/
|
||||
if(target == undefined || target == '') target = 'w2p_none';
|
||||
if(web2py.fire(element, 'ajax:before', null, target )) { /*test a usecase, should stop here if returns false */
|
||||
$.ajax({
|
||||
'type': method,
|
||||
@@ -303,12 +305,7 @@
|
||||
},
|
||||
'complete': function (xhr, status) {
|
||||
web2py.fire(element, 'ajax:complete', [xhr, status], target);
|
||||
var html = xhr.responseText;
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
var t = $('#' + target);
|
||||
if(content == 'prepend') t.prepend(html);
|
||||
else if(content == 'append') t.append(html);
|
||||
else if(content != 'hide') t.html(html);
|
||||
web2py.updatePage(xhr, target); /* Parse and load the html received */
|
||||
web2py.trap_form(action, target);
|
||||
web2py.trap_link(target);
|
||||
web2py.ajax_init('#' + target);
|
||||
@@ -378,6 +375,20 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
updatePage: function (xhr, target) {
|
||||
var t = $('#' + target);
|
||||
var html = $.parseHTML(xhr.responseText, document, true);
|
||||
var title_elements = $(html).filter('title').add($(html).find('title'));
|
||||
var title = title_elements.last().text();
|
||||
if (title) {
|
||||
title_elements.remove(); /* Remove any title elements from the response */
|
||||
document.title = $.trim(title); /* Set the new document title */
|
||||
}
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
if(content == 'prepend') t.prepend(xhr.responseText);
|
||||
else if(content == 'append') t.append(xhr.responseText);
|
||||
else if(content != 'hide') t.html(html);
|
||||
},
|
||||
calc_entropy: function (mystring) {
|
||||
/* calculate a simple entropy for a given string */
|
||||
var csets = new Array(
|
||||
@@ -569,9 +580,9 @@
|
||||
}
|
||||
if(target == undefined) {
|
||||
if(method == 'GET') {
|
||||
web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('get', action, [], '', el);
|
||||
} else if(method == 'POST') {
|
||||
web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('post', action, [], '', el);
|
||||
}
|
||||
} else {
|
||||
if(method == 'GET') {
|
||||
@@ -689,3 +700,4 @@ web2py_trap_link = jQuery.web2py.trap_link;
|
||||
web2py_calc_entropy = jQuery.web2py.calc_entropy;
|
||||
*/
|
||||
/* compatibility code - end*/
|
||||
|
||||
|
||||
@@ -30,6 +30,4 @@ jQuery(function(){
|
||||
hoverMenu(); // first page load
|
||||
jQuery(window).resize(hoverMenu); // on resize event
|
||||
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
|
||||
// make all buttons bootstrap buttons
|
||||
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn');
|
||||
});
|
||||
@@ -50,7 +50,7 @@ if request.function == 'manage':
|
||||
auth.requires_membership(manager_role)(lambda: None)()
|
||||
menu = False
|
||||
elif (request.application == 'admin' and not session.authorized) or \
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
|
||||
redirect(URL('admin', 'default', 'index',
|
||||
vars=dict(send=URL(args=request.args, vars=request.vars))))
|
||||
else:
|
||||
@@ -540,30 +540,30 @@ def table_template(table):
|
||||
|
||||
def bg_graph_model():
|
||||
graph = pgv.AGraph(layout='dot', directed=True, strict=False, rankdir='LR')
|
||||
|
||||
subgraphs = dict()
|
||||
|
||||
subgraphs = dict()
|
||||
for tablename in db.tables:
|
||||
if hasattr(db[tablename],'_meta_graphmodel'):
|
||||
meta_graphmodel = db[tablename]._meta_graphmodel
|
||||
else:
|
||||
meta_graphmodel = dict(group='Undefined', color='#ECECEC')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
|
||||
group = meta_graphmodel['group'].replace(' ', '')
|
||||
if not subgraphs.has_key(group):
|
||||
subgraphs[group] = dict(meta=meta_graphmodel, tables=[])
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
else:
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
subgraphs[group]['tables'].append(tablename)
|
||||
|
||||
graph.add_node(tablename, name=tablename, shape='plaintext',
|
||||
label=table_template(tablename))
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
|
||||
for n, key in enumerate(subgraphs.iterkeys()):
|
||||
graph.subgraph(nbunch=subgraphs[key]['tables'],
|
||||
name='cluster%d' % n,
|
||||
style='filled',
|
||||
color=subgraphs[key]['meta']['color'],
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
label=subgraphs[key]['meta']['group'])
|
||||
|
||||
for tablename in db.tables:
|
||||
for field in db[tablename]:
|
||||
@@ -580,14 +580,14 @@ def bg_graph_model():
|
||||
#return graph.draw(format='png', prog='dot')
|
||||
if not request.args:
|
||||
return graph.draw(format='png', prog='dot')
|
||||
else:
|
||||
else:
|
||||
response.headers['Content-Disposition']='attachment;filename=graph.%s'%request.args(0)
|
||||
if request.args(0) == 'dot':
|
||||
if request.args(0) == 'dot':
|
||||
return graph.string()
|
||||
else:
|
||||
return graph.draw(format=request.args(0), prog='dot')
|
||||
|
||||
def graph_model():
|
||||
def graph_model():
|
||||
return dict(databases=databases, pgv=pgv)
|
||||
|
||||
def manage():
|
||||
|
||||
@@ -31,7 +31,7 @@ def user():
|
||||
http://..../[app]/default/user/profile
|
||||
http://..../[app]/default/user/retrieve_password
|
||||
http://..../[app]/default/user/change_password
|
||||
http://..../[app]/default/user/manage_users (requires membership in
|
||||
http://..../[app]/default/user/manage_users (requires membership in
|
||||
use @auth.requires_login()
|
||||
@auth.requires_membership('group name')
|
||||
@auth.requires_permission('read','table name',record_id)
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
});
|
||||
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
|
||||
doc.on('click', "input.datetime", function () {
|
||||
var tformat = $(this).data('w2p_datetime_format')
|
||||
var tformat = $(this).data('w2p_datetime_format');
|
||||
var active = $(this).data('w2p_datetime');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : datetime_format;
|
||||
if(active === undefined) {
|
||||
@@ -196,7 +196,7 @@
|
||||
});
|
||||
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
|
||||
doc.on('click', "input.date", function () {
|
||||
var tformat = $(this).data('w2p_date_format')
|
||||
var tformat = $(this).data('w2p_date_format');
|
||||
var active = $(this).data('w2p_date');
|
||||
var format = (typeof tformat != 'undefined') ? tformat : date_format;
|
||||
if(active === undefined) {
|
||||
@@ -277,6 +277,8 @@
|
||||
ajax_page: function (method, action, data, target, element) {
|
||||
/* element is a new parameter, but should be put be put in front */
|
||||
if(element == undefined) element = $(document);
|
||||
/* if target is not there, fill it with something that there isn't in the page*/
|
||||
if(target == undefined || target == '') target = 'w2p_none';
|
||||
if(web2py.fire(element, 'ajax:before', null, target )) { /*test a usecase, should stop here if returns false */
|
||||
$.ajax({
|
||||
'type': method,
|
||||
@@ -303,12 +305,7 @@
|
||||
},
|
||||
'complete': function (xhr, status) {
|
||||
web2py.fire(element, 'ajax:complete', [xhr, status], target);
|
||||
var html = xhr.responseText;
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
var t = $('#' + target);
|
||||
if(content == 'prepend') t.prepend(html);
|
||||
else if(content == 'append') t.append(html);
|
||||
else if(content != 'hide') t.html(html);
|
||||
web2py.updatePage(xhr, target); /* Parse and load the html received */
|
||||
web2py.trap_form(action, target);
|
||||
web2py.trap_link(target);
|
||||
web2py.ajax_init('#' + target);
|
||||
@@ -378,6 +375,20 @@
|
||||
}
|
||||
});
|
||||
},
|
||||
updatePage: function (xhr, target) {
|
||||
var t = $('#' + target);
|
||||
var html = $.parseHTML(xhr.responseText, document, true);
|
||||
var title_elements = $(html).filter('title').add($(html).find('title'));
|
||||
var title = title_elements.last().text();
|
||||
if (title) {
|
||||
title_elements.remove(); /* Remove any title elements from the response */
|
||||
document.title = $.trim(title); /* Set the new document title */
|
||||
}
|
||||
var content = xhr.getResponseHeader('web2py-component-content');
|
||||
if(content == 'prepend') t.prepend(xhr.responseText);
|
||||
else if(content == 'append') t.append(xhr.responseText);
|
||||
else if(content != 'hide') t.html(html);
|
||||
},
|
||||
calc_entropy: function (mystring) {
|
||||
/* calculate a simple entropy for a given string */
|
||||
var csets = new Array(
|
||||
@@ -569,9 +580,9 @@
|
||||
}
|
||||
if(target == undefined) {
|
||||
if(method == 'GET') {
|
||||
web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('get', action, [], '', el);
|
||||
} else if(method == 'POST') {
|
||||
web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
|
||||
web2py.ajax_page('post', action, [], '', el);
|
||||
}
|
||||
} else {
|
||||
if(method == 'GET') {
|
||||
@@ -689,3 +700,4 @@ web2py_trap_link = jQuery.web2py.trap_link;
|
||||
web2py_calc_entropy = jQuery.web2py.calc_entropy;
|
||||
*/
|
||||
/* compatibility code - end*/
|
||||
|
||||
|
||||
@@ -30,6 +30,4 @@ jQuery(function(){
|
||||
hoverMenu(); // first page load
|
||||
jQuery(window).resize(hoverMenu); // on resize event
|
||||
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
|
||||
// make all buttons bootstrap buttons
|
||||
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn');
|
||||
});
|
||||
@@ -5,7 +5,7 @@
|
||||
Usage:
|
||||
Install py2exe: http://sourceforge.net/projects/py2exe/files/
|
||||
Copy script to the web2py directory
|
||||
c:\bin\python26\python build_windows_exe.py py2exe
|
||||
c:\bin\python26\python setup_exe.py py2exe
|
||||
|
||||
Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py
|
||||
"""
|
||||
@@ -42,18 +42,18 @@ remove_build_files = Config.getboolean("Setup", "remove_build_files")
|
||||
|
||||
|
||||
# Python base version
|
||||
python_version = sys.version[:3]
|
||||
python_version = sys.version_info[:3]
|
||||
|
||||
# List of modules deprecated in python2.6 that are in the above set
|
||||
py26_deprecated = ['mhlib', 'multifile', 'mimify', 'sets', 'MimeWriter']
|
||||
|
||||
if python_version == '2.6':
|
||||
base_modules += ['json', 'multiprocessing']
|
||||
if python_version > (2,5):
|
||||
base_modules += ['json', 'multiprocessing', 'ldap']
|
||||
base_modules = list(set(base_modules).difference(set(py26_deprecated)))
|
||||
|
||||
|
||||
#I don't know if this is even necessary
|
||||
if python_version == '2.6':
|
||||
if python_version > (2,5):
|
||||
# Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52
|
||||
try:
|
||||
shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/')
|
||||
@@ -62,8 +62,11 @@ if python_version == '2.6':
|
||||
|
||||
|
||||
setup(
|
||||
console=['web2py.py'],
|
||||
console=[{'script':'web2py.py',
|
||||
'icon_resources': [(0, 'extras/icons/web2py.ico')]
|
||||
}],
|
||||
windows=[{'script':'web2py.py',
|
||||
'icon_resources': [(1, 'extras/icons/web2py.ico')],
|
||||
'dest_base':'web2py_no_console' # MUST NOT be just 'web2py' otherwise it overrides the standard web2py.exe
|
||||
}],
|
||||
name="web2py",
|
||||
@@ -72,14 +75,9 @@ setup(
|
||||
author="Massimo DiPierro",
|
||||
license="LGPL v3",
|
||||
data_files=[
|
||||
'ABOUT',
|
||||
'LICENSE',
|
||||
'VERSION',
|
||||
'splashlogo.gif',
|
||||
'logging.example.conf',
|
||||
'options_std.py',
|
||||
'app.example.yaml',
|
||||
'queue.example.yaml'
|
||||
'ABOUT',
|
||||
'LICENSE',
|
||||
'VERSION'
|
||||
],
|
||||
options={'py2exe': {
|
||||
'packages': contributed_modules,
|
||||
@@ -126,6 +124,10 @@ else:
|
||||
copy_folders('applications/examples', 'applications/examples')
|
||||
print "Only web2py's admin, examples & welcome applications have been added"
|
||||
|
||||
copy_folders('extras', 'extras')
|
||||
copy_folders('examples', 'examples')
|
||||
copy_folders('handlers', 'handlers')
|
||||
|
||||
|
||||
#should we copy project's site-packages into dist/site-packages
|
||||
if copy_site_packages:
|
||||
|
||||
@@ -10,7 +10,7 @@ Web2Py framework modules
|
||||
========================
|
||||
"""
|
||||
|
||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
|
||||
|
||||
from globals import current
|
||||
from html import *
|
||||
|
||||
@@ -47,7 +47,7 @@ class MemcacheClient(object):
|
||||
value = obj[1] + value
|
||||
self.client.set(key, (time.time(), value))
|
||||
return value
|
||||
|
||||
|
||||
def incr(self, key, value=1):
|
||||
return self.increment(key, value)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
Note: This module is intended as a plugin replacement of pbkdf2.py
|
||||
by Armin Ronacher.
|
||||
|
||||
Git repository:
|
||||
Git repository:
|
||||
$ git clone https://github.com/michele-comitini/pbkdf2_ctypes.git
|
||||
|
||||
:copyright: Copyright (c) 2013: Michele Comitini <mcm@glisco.it>
|
||||
@@ -79,7 +79,7 @@ def openssl_hashlib_to_crypto_map_get(hashfunc):
|
||||
crypto_hashfunc.restype = ctypes.c_void_p
|
||||
return crypto_hashfunc()
|
||||
|
||||
|
||||
|
||||
def openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
"""OpenSSL compatibile wrapper
|
||||
"""
|
||||
@@ -92,7 +92,7 @@ def openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
c_iter = ctypes.c_int(iterations)
|
||||
c_keylen = ctypes.c_int(keylen)
|
||||
c_buff = ctypes.create_string_buffer(keylen)
|
||||
|
||||
|
||||
# PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
|
||||
# const unsigned char *salt, int saltlen, int iter,
|
||||
# const EVP_MD *digest,
|
||||
@@ -102,7 +102,7 @@ def openssl_pbkdf2(data, salt, iterations, digest, keylen):
|
||||
ctypes.c_char_p, ctypes.c_int,
|
||||
ctypes.c_int, ctypes.c_void_p,
|
||||
ctypes.c_int, ctypes.c_char_p]
|
||||
|
||||
|
||||
crypto.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int
|
||||
err = crypto.PKCS5_PBKDF2_HMAC(c_pass, c_passlen,
|
||||
c_salt, c_saltlen,
|
||||
|
||||
@@ -46,7 +46,7 @@ class PDF(object):
|
||||
canvas.setLineWidth(4)
|
||||
canvas.line(0, -1.25 * cm, 21.7 * cm, -1.25 * cm)
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
canvas.saveState()
|
||||
notes = listify(invoice.get('notes',''))
|
||||
textobject = canvas.beginText(1 * cm, -25 * cm)
|
||||
@@ -57,7 +57,7 @@ class PDF(object):
|
||||
textobject.textLine('Pag.%s/%s' % (page+1,pages))
|
||||
canvas.drawText(textobject)
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
canvas.saveState()
|
||||
business_details = listify(invoice.get('from','FROM:'))
|
||||
canvas.setFont(self.font_face, 9)
|
||||
@@ -66,13 +66,13 @@ class PDF(object):
|
||||
textobject.textLine(line)
|
||||
canvas.drawText(textobject)
|
||||
canvas.restoreState()
|
||||
|
||||
|
||||
canvas.saveState()
|
||||
client_info = listify(invoice.get('to','TO:'))
|
||||
textobject = canvas.beginText(1.5 * cm, -2.5 * cm)
|
||||
for line in client_info:
|
||||
textobject.textLine(line)
|
||||
canvas.drawText(textobject)
|
||||
canvas.drawText(textobject)
|
||||
canvas.restoreState()
|
||||
|
||||
textobject = canvas.beginText(1.5 * cm, -6.75 * cm)
|
||||
@@ -86,7 +86,7 @@ class PDF(object):
|
||||
data = [invoice_items[0]]
|
||||
for item in items:
|
||||
data.append([
|
||||
self.format_currency(x)
|
||||
self.format_currency(x)
|
||||
if isinstance(x,float) else x
|
||||
for x in item])
|
||||
righta = [k for k,v in enumerate(items[0])
|
||||
@@ -116,10 +116,10 @@ class PDF(object):
|
||||
data = [invoice['totals'][0]]
|
||||
for item in items:
|
||||
data.append([
|
||||
self.format_currency(x)
|
||||
self.format_currency(x)
|
||||
if isinstance(x,float) else x
|
||||
for x in item])
|
||||
righta = [k for k,v in enumerate(items[0])
|
||||
righta = [k for k,v in enumerate(items[0])
|
||||
if isinstance(v,(int,float,Decimal))]
|
||||
total = self.format_currency(invoice['total'])
|
||||
data.append(['']*(len(items[0])-1)+[total])
|
||||
|
||||
@@ -90,13 +90,13 @@ def populate(table, n=None, default=True, compute=False, contents={}):
|
||||
can be used in two ways:
|
||||
|
||||
>>> populate(db.tablename, n=100)
|
||||
|
||||
or
|
||||
|
||||
or
|
||||
|
||||
>>> for k,row in enumerate(populate(db.tablename)): print row
|
||||
"""
|
||||
|
||||
generator = populate_generator(table, default=default,
|
||||
generator = populate_generator(table, default=default,
|
||||
compute=compute, contents=contents)
|
||||
if n is not None:
|
||||
for k,record in enumerate(generator):
|
||||
@@ -246,8 +246,8 @@ def populate_generator(table, default=True, compute=False, contents={}):
|
||||
record[fieldname] = random.choice(LAST_NAMES)
|
||||
elif fieldname.find('username')>=0:
|
||||
record[fieldname] = random.choice(FIRST_NAMES).lower()+str(random.randint(1000,9999))
|
||||
else:
|
||||
record[fieldname] = random.choice(FIRST_NAMES)+' '+random.choice(LAST_NAMES)
|
||||
else:
|
||||
record[fieldname] = random.choice(FIRST_NAMES)+' '+random.choice(LAST_NAMES)
|
||||
elif fieldname.find('phone')>=0:
|
||||
record[fieldname] = '(%s%s%s) %s%s%s-%s%s%s%s' % (
|
||||
random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'),random.choice('1234567890'))
|
||||
|
||||
@@ -244,7 +244,7 @@ def run(history, statement, env={}):
|
||||
for name, val in statement_module.__dict__.items():
|
||||
if name not in old_globals or represent(val) != old_globals[name]:
|
||||
new_globals[name] = val
|
||||
|
||||
|
||||
if True in [isinstance(val, tuple(UNPICKLABLE_TYPES))
|
||||
for val in new_globals.values()]:
|
||||
# this statement added an unpicklable global. store the statement and
|
||||
|
||||
@@ -65,7 +65,7 @@ class Stripe:
|
||||
|
||||
def refund(self, charge_id):
|
||||
params = urllib.urlencode({})
|
||||
u = urllib.urlopen(self.URL_REFUND % (self.key, charge_id),
|
||||
u = urllib.urlopen(self.URL_REFUND % (self.key, charge_id),
|
||||
params)
|
||||
return simplejson.loads(u.read())
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ class WebClient(object):
|
||||
else:
|
||||
self.method = 'GET' if method=='auto' else method
|
||||
data = None
|
||||
t0 = time.time()
|
||||
t0 = time.time()
|
||||
self.response = opener.open(self.url, data)
|
||||
self.time = time.time() - t0
|
||||
except urllib2.HTTPError, error:
|
||||
|
||||
96
gluon/dal.py
96
gluon/dal.py
@@ -234,12 +234,6 @@ except ImportError:
|
||||
except ImportError:
|
||||
simplejson = None
|
||||
|
||||
try:
|
||||
from gluon import validators
|
||||
have_validators = True
|
||||
except (ImportError, SyntaxError):
|
||||
have_validators = False
|
||||
|
||||
LOGGER = logging.getLogger("web2py.dal")
|
||||
DEFAULT = lambda:0
|
||||
|
||||
@@ -316,6 +310,11 @@ if not 'google' in DRIVERS:
|
||||
except ImportError:
|
||||
LOGGER.debug('no MySQL driver MySQLDB')
|
||||
|
||||
try:
|
||||
import mysql.connector as mysqlconnector
|
||||
DRIVERS.append("MySQL(mysqlconnector)")
|
||||
except ImportError:
|
||||
LOGGER.debug("no driver mysql.connector")
|
||||
|
||||
try:
|
||||
import psycopg2
|
||||
@@ -1085,7 +1084,7 @@ class BaseAdapter(ConnectionPool):
|
||||
elif not key in sql_fields:
|
||||
del sql_fields_current[key]
|
||||
ftype = sql_fields_old[key]['type']
|
||||
if (self.dbengine in ('postgres',) and
|
||||
if (self.dbengine in ('postgres',) and
|
||||
ftype.startswith('geometry')):
|
||||
geotype, parms = ftype[:-1].split('(')
|
||||
schema = parms.split(',')[0]
|
||||
@@ -1095,7 +1094,7 @@ class BaseAdapter(ConnectionPool):
|
||||
elif self.dbengine in ('firebird',):
|
||||
query = ['ALTER TABLE %s DROP %s;' % (tablename, key)]
|
||||
else:
|
||||
query = ['ALTER TABLE %s DROP COLUMN %s;' %
|
||||
query = ['ALTER TABLE %s DROP COLUMN %s;' %
|
||||
(tablename, key)]
|
||||
metadata_change = True
|
||||
elif sql_fields[key]['sql'] != sql_fields_old[key]['sql'] \
|
||||
@@ -1135,9 +1134,9 @@ class BaseAdapter(ConnectionPool):
|
||||
self.log('faked!\n', table)
|
||||
else:
|
||||
self.execute(sub_query)
|
||||
# Caveat: mysql, oracle and firebird
|
||||
# Caveat: mysql, oracle and firebird
|
||||
# do not allow multiple alter table
|
||||
# in one transaction so we must commit
|
||||
# in one transaction so we must commit
|
||||
# partial transactions and
|
||||
# update table._dbt after alter table.
|
||||
if db._adapter.commit_on_alter_table:
|
||||
@@ -1790,7 +1789,7 @@ class BaseAdapter(ConnectionPool):
|
||||
return self.connection.rollback()
|
||||
|
||||
def close_connection(self):
|
||||
if self.connection:
|
||||
if self.connection:
|
||||
r = self.connection.close()
|
||||
self.connection = None
|
||||
return r
|
||||
@@ -2156,9 +2155,15 @@ class BaseAdapter(ConnectionPool):
|
||||
for row in rowsobj.records:
|
||||
box = row[tablename]
|
||||
for f,v in fields_virtual:
|
||||
box[f] = v.f(row)
|
||||
try:
|
||||
box[f] = v.f(row)
|
||||
except AttributeError:
|
||||
pass # not enough fields to define virtual field
|
||||
for f,v in fields_lazy:
|
||||
box[f] = (v.handler or VirtualCommand)(v.f,row)
|
||||
try:
|
||||
box[f] = (v.handler or VirtualCommand)(v.f,row)
|
||||
except AttributeError:
|
||||
pass # not enough fields to define virtual field
|
||||
|
||||
### old style virtual fields
|
||||
for item in table.virtualfields:
|
||||
@@ -2445,7 +2450,7 @@ class JDBCSQLiteAdapter(SQLiteAdapter):
|
||||
|
||||
|
||||
class MySQLAdapter(BaseAdapter):
|
||||
drivers = ('MySQLdb','pymysql')
|
||||
drivers = ('MySQLdb','pymysql', 'mysqlconnector')
|
||||
|
||||
commit_on_alter_table = True
|
||||
support_distributed_transaction = True
|
||||
@@ -4418,7 +4423,7 @@ class GoogleSQLAdapter(UseDatabaseStoredFile,MySQLAdapter):
|
||||
|
||||
def find_driver(self,adapter_args,uri=None):
|
||||
self.adapter_args = adapter_args
|
||||
self.driver = "google"
|
||||
self.driver = "google"
|
||||
|
||||
class NoSQLAdapter(BaseAdapter):
|
||||
can_select_for_update = False
|
||||
@@ -5372,7 +5377,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
d = datetime.date(2000, 1, 1)
|
||||
# mongodb doesn't has a time object and so it must datetime,
|
||||
# string or integer
|
||||
return datetime.datetime.combine(d, value)
|
||||
return datetime.datetime.combine(d, value)
|
||||
elif fieldtype == "blob":
|
||||
from bson import Binary
|
||||
if not isinstance(value, Binary):
|
||||
@@ -6504,9 +6509,10 @@ class IMAPAdapter(NoSQLAdapter):
|
||||
def _insert(self, table, fields):
|
||||
def add_payload(message, obj):
|
||||
payload = Message()
|
||||
charset = obj.get("encoding", "utf-8")
|
||||
payload.set_type(obj.get("mime", None))
|
||||
payload.set_charset(charset)
|
||||
payload.set_charset(obj.get("encoding", "utf-8"))
|
||||
mime = obj.get("mime", None)
|
||||
if mime:
|
||||
payload.set_type(mime)
|
||||
if "text" in obj:
|
||||
payload.set_payload(obj["text"])
|
||||
elif "payload" in obj:
|
||||
@@ -6544,7 +6550,9 @@ class IMAPAdapter(NoSQLAdapter):
|
||||
else:
|
||||
message[item] = ";".join([i for i in
|
||||
value])
|
||||
if not message.is_multipart():
|
||||
if (not message.is_multipart() and
|
||||
(not message.get_content_type().startswith(
|
||||
"multipart"))):
|
||||
if isinstance(content, basestring):
|
||||
message.set_payload(content)
|
||||
elif len(content) > 0:
|
||||
@@ -6872,7 +6880,9 @@ def sqlhtml_validators(field):
|
||||
fieldtype
|
||||
"""
|
||||
db = field.db
|
||||
if not have_validators:
|
||||
try:
|
||||
from gluon import validators
|
||||
except ImportError:
|
||||
return []
|
||||
field_type, field_length = field.type, field.length
|
||||
if isinstance(field_type, SQLCustomType):
|
||||
@@ -6938,14 +6948,14 @@ def sqlhtml_validators(field):
|
||||
refs = reduce(lambda a,b:a&b, [count(ids[i:i+30]) for i in rx])
|
||||
else:
|
||||
refs = db(id.belongs(ids)).select(id)
|
||||
return (refs and ', '.join(f(r,x.id) for x in refs) or '')
|
||||
return (refs and ', '.join(f(r,x.id) for x in refs) or '')
|
||||
field.represent = field.represent or list_ref_repr
|
||||
if hasattr(referenced, '_format') and referenced._format:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
referenced._format,multiple=True)
|
||||
else:
|
||||
requires = validators.IS_IN_DB(db,referenced._id,
|
||||
multiple=True)
|
||||
multiple=True)
|
||||
if field.unique:
|
||||
requires._and = validators.IS_NOT_IN_DB(db,field)
|
||||
if not field.notnull:
|
||||
@@ -8349,10 +8359,10 @@ class Table(object):
|
||||
self._id = field
|
||||
for field in fields:
|
||||
if isinstance(field, (FieldMethod, FieldVirtual)):
|
||||
virtual_fields.append(field)
|
||||
virtual_fields.append(field)
|
||||
elif isinstance(field, Field) and not field.name in fieldnames:
|
||||
if field.db is not None:
|
||||
field = copy.copy(field)
|
||||
field = copy.copy(field)
|
||||
include_new(field)
|
||||
elif isinstance(field, dict) and not field['fieldname'] in fieldnames:
|
||||
include_new(Field(**field))
|
||||
@@ -8447,8 +8457,10 @@ class Table(object):
|
||||
clones.append(field.clone(
|
||||
unique=False, type=field.type if nfk else 'bigint'))
|
||||
archive_db.define_table(
|
||||
archive_name, Field(current_record,field_type,
|
||||
label=current_record_label), *clones)
|
||||
archive_name,
|
||||
Field(current_record,field_type,label=current_record_label),
|
||||
*clones,**dict(format=self._format))
|
||||
|
||||
self._before_update.append(
|
||||
lambda qset,fs,db=archive_db,an=archive_name,cn=current_record:
|
||||
archive_record(qset,fs,db[an],cn))
|
||||
@@ -8914,7 +8926,7 @@ class Table(object):
|
||||
except ValueError:
|
||||
raise RuntimeError("Unable to parse line:%s field:%s value:'%s'"
|
||||
% (lineno+1,field,line[i]))
|
||||
|
||||
|
||||
if not (id_map or cid is None or id_offset is None or unique_idx):
|
||||
csv_id = long(line[cid])
|
||||
curr_id = self.insert(**dict(items))
|
||||
@@ -9207,13 +9219,13 @@ class Expression(object):
|
||||
|
||||
def startswith(self, value):
|
||||
db = self.db
|
||||
if not self.type in ('string', 'text', 'json'):
|
||||
if not self.type in ('string', 'text', 'json', 'upload'):
|
||||
raise SyntaxError("startswith used with incompatible field type")
|
||||
return Query(db, db._adapter.STARTSWITH, self, value)
|
||||
|
||||
def endswith(self, value):
|
||||
db = self.db
|
||||
if not self.type in ('string', 'text', 'json'):
|
||||
if not self.type in ('string', 'text', 'json', 'upload'):
|
||||
raise SyntaxError("endswith used with incompatible field type")
|
||||
return Query(db, db._adapter.ENDSWITH, self, value)
|
||||
|
||||
@@ -9231,7 +9243,7 @@ class Expression(object):
|
||||
return self.contains('')
|
||||
else:
|
||||
return reduce(all and AND or OR,subqueries)
|
||||
if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'):
|
||||
if not self.type in ('string', 'text', 'json', 'upload') and not self.type.startswith('list:'):
|
||||
raise SyntaxError("contains used with incompatible field type")
|
||||
return Query(db, db._adapter.CONTAINS, self, value, case_sensitive=case_sensitive)
|
||||
|
||||
@@ -9613,7 +9625,7 @@ class Field(Expression):
|
||||
else:
|
||||
filename = name
|
||||
# ## if file is in DB
|
||||
if isinstance(self_uploadfield, (str, Field)):
|
||||
if isinstance(self_uploadfield, (str, Field)):
|
||||
return dict(path=None,filename=filename)
|
||||
# ## if file is on filesystem
|
||||
if not path:
|
||||
@@ -10422,24 +10434,24 @@ class Rows(object):
|
||||
one_result = False
|
||||
if 'one_result' in args:
|
||||
one_result = args['one_result']
|
||||
|
||||
|
||||
def build_fields_struct(row, fields, num, groups):
|
||||
''' helper function:
|
||||
''' helper function:
|
||||
'''
|
||||
if num > len(fields)-1:
|
||||
if one_result:
|
||||
return row
|
||||
else:
|
||||
return [row]
|
||||
|
||||
|
||||
key = fields[num]
|
||||
value = row[key]
|
||||
|
||||
|
||||
if value not in groups:
|
||||
groups[value] = build_fields_struct(row, fields, num+1, {})
|
||||
else:
|
||||
struct = build_fields_struct(row, fields, num+1, groups[ value ])
|
||||
|
||||
|
||||
# still have more grouping to do
|
||||
if type(struct) == type(dict()):
|
||||
groups[value].update()
|
||||
@@ -10449,22 +10461,22 @@ class Rows(object):
|
||||
# no more grouping, first only on
|
||||
else:
|
||||
groups[value] = struct
|
||||
|
||||
|
||||
return groups
|
||||
|
||||
|
||||
if len(fields) == 0:
|
||||
return self
|
||||
|
||||
|
||||
# if select returned no results
|
||||
if not self.records:
|
||||
return {}
|
||||
|
||||
|
||||
grouped_row_group = dict()
|
||||
|
||||
# build the struct
|
||||
for row in self:
|
||||
build_fields_struct(row, fields, 0, grouped_row_group)
|
||||
|
||||
|
||||
return grouped_row_group
|
||||
|
||||
def render(self, i=None, fields=None):
|
||||
|
||||
120
gluon/globals.py
120
gluon/globals.py
@@ -211,12 +211,12 @@ class Request(Storage):
|
||||
body.seek(0)
|
||||
|
||||
# parse POST variables on POST, PUT, BOTH only in post_vars
|
||||
if (body and
|
||||
env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH') and
|
||||
not is_json):
|
||||
query_string = env.pop('QUERY_STRING') if 'QUERY_STRING' in env else None
|
||||
if (body and not is_json
|
||||
and env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH')):
|
||||
query_string = env.pop('QUERY_STRING',None)
|
||||
dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
|
||||
post_vars.update(dpost)
|
||||
if len(dpost):
|
||||
post_vars.update(dpost)
|
||||
if query_string is not None:
|
||||
env['QUERY_STRING'] = query_string
|
||||
# The same detection used by FieldStorage to detect multipart POSTs
|
||||
@@ -232,19 +232,13 @@ class Request(Storage):
|
||||
if key is None:
|
||||
continue # not sure why cgi.FieldStorage returns None key
|
||||
dpk = dpost[key]
|
||||
# if an element is not a file replace it with its value else leave it alone
|
||||
if isinstance(dpk, list):
|
||||
value = []
|
||||
for _dpk in dpk:
|
||||
if not _dpk.filename:
|
||||
value.append(_dpk.value)
|
||||
else:
|
||||
value.append(_dpk)
|
||||
elif not dpk.filename:
|
||||
value = dpk.value
|
||||
else:
|
||||
value = dpk
|
||||
pvalue = listify(value)
|
||||
# if an element is not a file replace it with
|
||||
# its value else leave it alone
|
||||
|
||||
pvalue = listify([(_dpk if _dpk.filename else _dpk.value)
|
||||
for _dpk in dpk]
|
||||
if isinstance(dpk, list) else
|
||||
(dpk if dpk.filename else dpk.value))
|
||||
if len(pvalue):
|
||||
post_vars[key] = (len(pvalue) > 1 and pvalue) or pvalue[0]
|
||||
|
||||
@@ -333,13 +327,12 @@ class Request(Storage):
|
||||
_self.args[-1], _, self.extension = self.args[-1].rpartition('.')
|
||||
current.response.headers['Content-Type'] = \
|
||||
contenttype('.' + _self.extension.lower())
|
||||
if not method in ['GET', 'POST', 'DELETE', 'PUT']:
|
||||
raise HTTP(400, "invalid method")
|
||||
rest_action = _action().get(method, None)
|
||||
if not rest_action:
|
||||
if not (rest_action and method==method.upper()
|
||||
and callable(rest_action)):
|
||||
raise HTTP(400, "method not supported")
|
||||
try:
|
||||
return rest_action(*_self.args, **_self.vars)
|
||||
return rest_action(*_self.args, **getattr(_self,'vars',{}))
|
||||
except TypeError, e:
|
||||
exc_type, exc_value, exc_traceback = sys.exc_info()
|
||||
if len(traceback.extract_tb(exc_traceback)) == 1:
|
||||
@@ -742,10 +735,11 @@ class Session(Storage):
|
||||
response.session_cookie_compression_level = compression_level
|
||||
|
||||
# check if there is a session_id in cookies
|
||||
try:
|
||||
response.session_id = cookies[response.session_id_name].value
|
||||
try:
|
||||
old_session_id = cookies[response.session_id_name].value
|
||||
except KeyError:
|
||||
response.session_id = None
|
||||
old_session_id = None
|
||||
response.session_id = old_session_id
|
||||
|
||||
# if we are supposed to use cookie based session data
|
||||
if cookie_key:
|
||||
@@ -865,12 +859,30 @@ class Session(Storage):
|
||||
else:
|
||||
response.session_id = None
|
||||
response.session_new = True
|
||||
# if there is no session id yet, we'll need to create a
|
||||
# new session
|
||||
else:
|
||||
response.session_new = True
|
||||
|
||||
# set the cookie now if you know the session_id so user can set
|
||||
# cookie attributes in controllers/models
|
||||
# cookie will be reset later
|
||||
# yet cookie may be reset later
|
||||
# Removed comparison between old and new session ids - should send
|
||||
# the cookie all the time
|
||||
if isinstance(response.session_id,str):
|
||||
response.cookies[response.session_id_name] = response.session_id
|
||||
response.cookies[response.session_id_name]['path'] = '/'
|
||||
if cookie_expires:
|
||||
response.cookies[response.session_id_name]['expires'] = \
|
||||
cookie_expires.strftime(FMT)
|
||||
|
||||
session_pickled = cPickle.dumps(self)
|
||||
response.session_hash = hashlib.md5(session_pickled).hexdigest()
|
||||
|
||||
if self.flash:
|
||||
(response.flash, self.flash) = (self.flash, None)
|
||||
|
||||
session_pickled = cPickle.dumps(self)
|
||||
response.session_hash = hashlib.md5(session_pickled).hexdigest()
|
||||
|
||||
def renew(self, clear_session=False):
|
||||
|
||||
@@ -929,6 +941,30 @@ class Session(Storage):
|
||||
else:
|
||||
response.session_new = True
|
||||
|
||||
def _fixup_before_save(self):
|
||||
response = current.response
|
||||
rcookies = response.cookies
|
||||
if self._forget and response.session_id_name in rcookies:
|
||||
del rcookies[response.session_id_name]
|
||||
elif self._secure and response.session_id_name in rcookies:
|
||||
rcookies[response.session_id_name]['secure'] = True
|
||||
|
||||
def clear_session_cookies(sefl):
|
||||
request = current.request
|
||||
response = current.response
|
||||
session = response.session
|
||||
masterapp = response.session_masterapp
|
||||
cookies = request.cookies
|
||||
rcookies = response.cookies
|
||||
# if not cookie_key, but session_data_name in cookies
|
||||
# expire session_data_name from cookies
|
||||
if response.session_data_name in cookies:
|
||||
rcookies[response.session_data_name] = 'expired'
|
||||
rcookies[response.session_data_name]['path'] = '/'
|
||||
rcookies[response.session_data_name]['expires'] = PAST
|
||||
if response.session_id_name in rcookies:
|
||||
del rcookies[response.session_id_name]
|
||||
|
||||
def save_session_id_cookie(self):
|
||||
request = current.request
|
||||
response = current.response
|
||||
@@ -946,12 +982,11 @@ class Session(Storage):
|
||||
if response.session_id:
|
||||
rcookies[response.session_id_name] = response.session_id
|
||||
rcookies[response.session_id_name]['path'] = '/'
|
||||
if isinstance(response.session_cookie_expires,datetime.datetime):
|
||||
rcookies[response.session_id_name]['expires'] = \
|
||||
response.session_cookie_expires.strftime(FMT)
|
||||
elif isinstance(response.session_cookie_expires,str):
|
||||
rcookies[response.session_id_name]['expires'] = \
|
||||
response.session_cookie_expires
|
||||
expires = response.session_cookie_expires
|
||||
if isinstance(expires,datetime.datetime):
|
||||
expires = expires.strftime(FMT)
|
||||
if expires:
|
||||
rcookies[response.session_id_name]['expires'] = expires
|
||||
|
||||
def clear(self):
|
||||
Storage.clear(self)
|
||||
@@ -981,6 +1016,7 @@ class Session(Storage):
|
||||
|
||||
def _try_store_in_cookie(self, request, response):
|
||||
if self._forget or self._unchanged(response):
|
||||
# self.clear_session_cookies()
|
||||
self.save_session_id_cookie()
|
||||
return False
|
||||
name = response.session_data_name
|
||||
@@ -988,13 +1024,15 @@ class Session(Storage):
|
||||
value = secure_dumps(dict(self),
|
||||
response.session_cookie_key,
|
||||
compression_level=compression_level)
|
||||
expires = response.session_cookie_expires
|
||||
rcookies = response.cookies
|
||||
rcookies.pop(name, None)
|
||||
rcookies[name] = value
|
||||
rcookies[name]['path'] = '/'
|
||||
expires = response.session_cookie_expires
|
||||
if isinstance(expires,datetime.datetime):
|
||||
expires = expires.strftime(FMT)
|
||||
if expires:
|
||||
rcookies[name]['expires'] = expires.strftime(FMT)
|
||||
rcookies[name]['expires'] = expires
|
||||
return True
|
||||
|
||||
def _unchanged(self,response):
|
||||
@@ -1006,13 +1044,15 @@ class Session(Storage):
|
||||
def _try_store_in_db(self, request, response):
|
||||
# don't save if file-based sessions,
|
||||
# no session id, or session being forgotten
|
||||
# or no changes to session
|
||||
|
||||
if not response.session_db_table or self._forget or self._unchanged(response):
|
||||
# or no changes to session (Unless the session is new)
|
||||
if (not response.session_db_table or
|
||||
self._forget or
|
||||
(self._unchanged(response) and not response.session_new)):
|
||||
if (not response.session_db_table and
|
||||
global_settings.db_sessions is not True and
|
||||
response.session_masterapp in global_settings.db_sessions):
|
||||
global_settings.db_sessions.remove(response.session_masterapp)
|
||||
# self.clear_session_cookies()
|
||||
self.save_session_id_cookie()
|
||||
return False
|
||||
|
||||
@@ -1050,7 +1090,9 @@ class Session(Storage):
|
||||
|
||||
def _try_store_in_file(self, request, response):
|
||||
try:
|
||||
if not response.session_id or self._forget or self._unchanged(response):
|
||||
if (not response.session_id or self._forget
|
||||
or self._unchanged(response)):
|
||||
# self.clear_session_cookies()
|
||||
self.save_session_id_cookie()
|
||||
return False
|
||||
if response.session_new or not response.session_file:
|
||||
|
||||
@@ -1484,7 +1484,7 @@ class A(DIV):
|
||||
if not self['_disable_with']:
|
||||
self['_data-w2p_disable_with'] = 'default'
|
||||
if self['callback'] and not self['_id']:
|
||||
self['_id'] = web2py_uuid()
|
||||
self['_id'] = web2py_uuid()
|
||||
if self['delete']:
|
||||
self['_data-w2p_remove'] = self['delete']
|
||||
if self['target']:
|
||||
|
||||
@@ -67,8 +67,8 @@ regex_param = re.compile(r'{(?P<s>.+?)}')
|
||||
|
||||
# pattern for a valid accept_language
|
||||
regex_language = \
|
||||
re.compile('([a-z]{2}(?:\-[a-z]{2})?(?:\-[a-z]{2})?)(?:[,;]|$)')
|
||||
regex_langfile = re.compile('^[a-z]{2}(-[a-z]{2})?\.py$')
|
||||
re.compile('([a-z]{2,3}(?:\-[a-z]{2})?(?:\-[a-z]{2})?)(?:[,;]|$)')
|
||||
regex_langfile = re.compile('^[a-z]{2,3}(-[a-z]{2})?\.py$')
|
||||
regex_backslash = re.compile(r"\\([\\{}%])")
|
||||
regex_plural = re.compile('%({.+?})')
|
||||
regex_plural_dict = re.compile('^{(?P<w>[^()[\]][^()[\]]*?)\((?P<n>[^()\[\]]+)\)}$') # %%{word(varname or number)}
|
||||
|
||||
@@ -453,7 +453,7 @@ def wsgibase(environ, responder):
|
||||
serve_controller(request, response, session)
|
||||
|
||||
except HTTP, http_response:
|
||||
|
||||
|
||||
if static_file:
|
||||
return http_response.to(responder, env=env)
|
||||
|
||||
@@ -470,7 +470,7 @@ def wsgibase(environ, responder):
|
||||
# ##################################################
|
||||
# on success, commit database
|
||||
# ##################################################
|
||||
|
||||
|
||||
if response.do_not_commit is True:
|
||||
BaseAdapter.close_all_instances(None)
|
||||
elif response.custom_commit:
|
||||
@@ -489,7 +489,7 @@ def wsgibase(environ, responder):
|
||||
if request.cid:
|
||||
http_response.headers.setdefault(
|
||||
'web2py-component-content', 'replace')
|
||||
|
||||
|
||||
if request.ajax:
|
||||
if response.flash:
|
||||
http_response.headers['web2py-component-flash'] = \
|
||||
@@ -498,17 +498,13 @@ def wsgibase(environ, responder):
|
||||
if response.js:
|
||||
http_response.headers['web2py-component-command'] = \
|
||||
urllib2.quote(response.js.replace('\n',''))
|
||||
|
||||
|
||||
# ##################################################
|
||||
# store cookies in headers
|
||||
# ##################################################
|
||||
|
||||
rcookies = response.cookies
|
||||
if session._forget and response.session_id_name in rcookies:
|
||||
del rcookies[response.session_id_name]
|
||||
elif session._secure:
|
||||
rcookies[response.session_id_name]['secure'] = True
|
||||
http_response.cookies2headers(rcookies)
|
||||
session._fixup_before_save()
|
||||
http_response.cookies2headers(response.cookies)
|
||||
|
||||
ticket = None
|
||||
|
||||
@@ -612,7 +608,8 @@ def save_password(password, port):
|
||||
|
||||
def appfactory(wsgiapp=wsgibase,
|
||||
logfilename='httpserver.log',
|
||||
profiler_dir=None):
|
||||
profiler_dir=None,
|
||||
profilerfilename=None):
|
||||
"""
|
||||
generates a wsgi application that does logging and profiling and calls
|
||||
wsgibase
|
||||
@@ -623,7 +620,8 @@ def appfactory(wsgiapp=wsgibase,
|
||||
[, profilerfilename='profiler.log']]])
|
||||
|
||||
"""
|
||||
|
||||
if profilerfilename is not None:
|
||||
raise BaseException("Deprecated API")
|
||||
if profiler_dir:
|
||||
profiler_dir = abspath(profiler_dir)
|
||||
logger.warn('profiler is on. will use dir %s', profiler_dir)
|
||||
@@ -631,14 +629,14 @@ def appfactory(wsgiapp=wsgibase,
|
||||
try:
|
||||
os.makedirs(profiler_dir)
|
||||
except:
|
||||
raise BaseException, "Can't create dir %s" % profiler_dir
|
||||
raise BaseException("Can't create dir %s" % profiler_dir)
|
||||
filepath = pjoin(profiler_dir, 'wtest')
|
||||
try:
|
||||
filehandle = open( filepath, 'w' )
|
||||
filehandle.close()
|
||||
os.unlink(filepath)
|
||||
except IOError:
|
||||
raise BaseException, "Unable to write to dir %s" % profiler_dir
|
||||
raise BaseException("Unable to write to dir %s" % profiler_dir)
|
||||
|
||||
def app_with_logging(environ, responder):
|
||||
"""
|
||||
|
||||
@@ -48,7 +48,7 @@ class TicketStorage(Storage):
|
||||
self._store_on_disk(request, ticket_id, ticket_data)
|
||||
|
||||
def _store_in_db(self, request, ticket_id, ticket_data):
|
||||
self.db._adapter.reconnect()
|
||||
self.db._adapter.reconnect()
|
||||
try:
|
||||
table = self._get_table(self.db, self.tablename, request.application)
|
||||
id = table.insert(ticket_id=ticket_id,
|
||||
|
||||
@@ -244,15 +244,15 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
||||
url = path_info + '?' + query_string
|
||||
message = 'You are being redirected <a href="%s">here</a>'
|
||||
return HTTP(303, message % url, Location=url), environ
|
||||
elif not environ.get('__ROUTES_ONERROR__', False):
|
||||
# wsgibase will be called recursively with
|
||||
elif not environ.get('__ROUTES_ONERROR__', False):
|
||||
# wsgibase will be called recursively with
|
||||
# the routes_onerror path.
|
||||
environ['__ROUTES_ONERROR__'] = True # limit recursion
|
||||
path_info = '/' + path_info.lstrip('/')
|
||||
environ['PATH_INFO'] = path_info
|
||||
environ['QUERY_STRING'] = query_string
|
||||
path_info = '/' + path_info.lstrip('/')
|
||||
environ['PATH_INFO'] = path_info
|
||||
environ['QUERY_STRING'] = query_string
|
||||
environ['WEB2PY_STATUS_CODE'] = status
|
||||
return None, environ
|
||||
return None, environ
|
||||
# do nothing!
|
||||
return http_response, environ
|
||||
|
||||
|
||||
@@ -137,12 +137,12 @@ def env(
|
||||
|
||||
for k, v in extra_request.items():
|
||||
request[k] = v
|
||||
|
||||
|
||||
path_info = '/%s/%s/%s' % (a, c, f)
|
||||
if request.args:
|
||||
path_info = '%s/%s' % (path_info, '/'.join(request.args))
|
||||
if request.vars:
|
||||
vars = ['%s=%s' % (k,v) if v else '%s' % k
|
||||
vars = ['%s=%s' % (k,v) if v else '%s' % k
|
||||
for (k,v) in request.vars.iteritems()]
|
||||
path_info = '%s?%s' % (path_info, '&'.join(vars))
|
||||
request.env.path_info = path_info
|
||||
@@ -238,7 +238,7 @@ def run(
|
||||
pyfile = os.path.join('applications', a, 'controllers', c + '.py')
|
||||
pycfile = os.path.join('applications', a, 'compiled',
|
||||
"controllers_%s_%s.pyc" % (c, f))
|
||||
if ((cronjob and os.path.isfile(pycfile))
|
||||
if ((cronjob and os.path.isfile(pycfile))
|
||||
or not os.path.isfile(pyfile)):
|
||||
exec read_pyc(pycfile) in _env
|
||||
elif os.path.isfile(pyfile):
|
||||
@@ -425,7 +425,7 @@ def execute_from_command_line(argv=None):
|
||||
parser = optparse.OptionParser(usage=get_usage())
|
||||
|
||||
parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME',
|
||||
help='run web2py in interactive shell ' +
|
||||
help='run web2py in interactive shell ' +
|
||||
'or IPython(if installed) with specified appname')
|
||||
msg = 'run web2py in interactive shell or bpython (if installed) with'
|
||||
msg += ' specified appname (if app does not exist it will be created).'
|
||||
|
||||
@@ -292,6 +292,7 @@ class ListWidget(StringWidget):
|
||||
_class = 'string'
|
||||
requires = field.requires if isinstance(
|
||||
field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None
|
||||
if isinstance(value,str): value = [value]
|
||||
nvalue = value or ['']
|
||||
items = [LI(INPUT(_id=_id, _class=_class, _name=_name,
|
||||
value=v, hideerror=k < len(nvalue) - 1,
|
||||
@@ -1356,8 +1357,6 @@ class SQLFORM(FORM):
|
||||
value = self.record[fieldname]
|
||||
else:
|
||||
value = self.table[fieldname].default
|
||||
if field.type.startswith('list:') and isinstance(value, str):
|
||||
value = [value]
|
||||
row_id = '%s_%s%s' % (
|
||||
self.table, fieldname, SQLFORM.ID_ROW_SUFFIX)
|
||||
widget = field.widget(field, value)
|
||||
|
||||
@@ -434,7 +434,7 @@ class TemplateParser(object):
|
||||
# Get the filename; filename looks like ``"template.html"``.
|
||||
# We need to eval to remove the quotes and get the string type.
|
||||
filename = eval(filename, context)
|
||||
|
||||
|
||||
# Allow empty filename for conditional extend and include directives.
|
||||
if not filename:
|
||||
return ''
|
||||
|
||||
@@ -114,7 +114,7 @@ class TestRoutes(unittest.TestCase):
|
||||
except AttributeError:
|
||||
pass
|
||||
"""
|
||||
# outgoing
|
||||
# outgoing
|
||||
self.assertEqual(filter_url('http://domain.com/init/default/index',
|
||||
out=True), '/init/default/index')
|
||||
self.assertEqual(filter_url('http://domain.com/init/default/index/arg1', out=True), '/init/default/index/arg1')
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestWeb(LiveTest):
|
||||
|
||||
# check registration and login were successful
|
||||
client.get('index')
|
||||
|
||||
|
||||
# COMMENTED BECAUSE FAILS BUT WHY?
|
||||
self.assertTrue('Welcome Homer' in client.text)
|
||||
|
||||
|
||||
@@ -921,7 +921,7 @@ class Auth(object):
|
||||
username_case_sensitive=True,
|
||||
update_fields = ['email'],
|
||||
ondelete="CASCADE",
|
||||
client_side = True,
|
||||
client_side = True,
|
||||
renew_session_onlogin=True,
|
||||
renew_session_onlogout=True,
|
||||
keep_session_onlogin=True,
|
||||
@@ -1509,7 +1509,7 @@ class Auth(object):
|
||||
self.bar = SPAN(s1, Anr(items[0]['name'],
|
||||
_href=items[0]['href']), s3,
|
||||
_class='auth_navbar')
|
||||
for item in items:
|
||||
for item in items[1:]:
|
||||
self.bar.insert(-1, s2)
|
||||
self.bar.insert(-1, Anr(item['name'], _href=item['href']))
|
||||
|
||||
@@ -1560,8 +1560,8 @@ class Auth(object):
|
||||
current_record.replace('_',' ').title())
|
||||
for table in tables:
|
||||
fieldnames = table.fields()
|
||||
if ('id' in fieldnames and
|
||||
'modified_on' in fieldnames and
|
||||
if ('id' in fieldnames and
|
||||
'modified_on' in fieldnames and
|
||||
not current_record in fieldnames):
|
||||
table._enable_record_versioning(
|
||||
archive_db=archive_db,
|
||||
@@ -2167,17 +2167,17 @@ class Auth(object):
|
||||
elif 'username' in table_user.fields:
|
||||
username = 'username'
|
||||
else:
|
||||
username = 'email'
|
||||
username = 'email'
|
||||
settings = self.settings
|
||||
if 'username' in table_user.fields or \
|
||||
not settings.login_email_validate:
|
||||
tmpvalidator = IS_NOT_EMPTY(error_message=self.messages.is_empty)
|
||||
if not settings.username_case_sensitive:
|
||||
tmpvalidator = [IS_LOWER(), tmpvalidator]
|
||||
tmpvalidator = [IS_LOWER(), tmpvalidator]
|
||||
else:
|
||||
tmpvalidator = IS_EMAIL(error_message=self.messages.invalid_email)
|
||||
if not settings.email_case_sensitive:
|
||||
tmpvalidator = [IS_LOWER(), tmpvalidator]
|
||||
tmpvalidator = [IS_LOWER(), tmpvalidator]
|
||||
old_requires = table_user[username].requires
|
||||
table_user[username].requires = tmpvalidator
|
||||
|
||||
@@ -2191,7 +2191,7 @@ class Auth(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
### use session for federated login
|
||||
### use session for federated login
|
||||
snext = self.get_vars_next()
|
||||
if snext:
|
||||
session._auth_next = snext
|
||||
@@ -2414,9 +2414,9 @@ class Auth(object):
|
||||
next = cas.logout_url(next)
|
||||
|
||||
current.session.auth = None
|
||||
current.session.flash = self.messages.logged_out
|
||||
if self.settings.renew_session_onlogout:
|
||||
current.session.renew(clear_session=not self.settings.keep_session_onlogout)
|
||||
current.session.flash = self.messages.logged_out
|
||||
if not next is None:
|
||||
redirect(next)
|
||||
|
||||
@@ -2515,9 +2515,9 @@ class Auth(object):
|
||||
captcha.comment, self.settings.formstyle, 'captcha__row')
|
||||
|
||||
table_user.registration_key.default = key = web2py_uuid()
|
||||
if form.accepts(request, session if self.csrf_prevention else None,
|
||||
if form.accepts(request, session if self.csrf_prevention else None,
|
||||
formname='register',
|
||||
onvalidation=onvalidation,
|
||||
onvalidation=onvalidation,
|
||||
hideerror=self.settings.hideerror):
|
||||
description = self.messages.group_description % form.vars
|
||||
if self.settings.create_user_groups:
|
||||
@@ -5858,7 +5858,7 @@ class Wiki(object):
|
||||
class Config(object):
|
||||
def __init__(
|
||||
self,
|
||||
filename,
|
||||
filename,
|
||||
section,
|
||||
default_values={}
|
||||
):
|
||||
@@ -5879,11 +5879,11 @@ class Config(object):
|
||||
def save(self, options):
|
||||
for option, value in options:
|
||||
self.config.set(self.section, option, value)
|
||||
try:
|
||||
try:
|
||||
self.config.write(open(self.filename, 'w'))
|
||||
result = True
|
||||
except:
|
||||
current.session['settings_%s' % self.section] = dict(self.config.items(self.section))
|
||||
current.session['settings_%s' % self.section] = dict(self.config.items(self.section))
|
||||
result = False
|
||||
return result
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@ def is_loopback_ip_address(ip=None, addrinfo=None):
|
||||
if not isinstance(ip, basestring):
|
||||
return False
|
||||
# IPv4 or IPv6-embedded IPv4 or IPv4-compatible IPv6
|
||||
if ip.count('.') == 3:
|
||||
if ip.count('.') == 3:
|
||||
return ip.lower().startswith(('127', '::127', '0:0:0:0:0:0:127',
|
||||
'::ffff:127', '0:0:0:0:0:ffff:127'))
|
||||
return ip == '::1' or ip == '0:0:0:0:0:0:0:1' # IPv6 loopback
|
||||
@@ -322,7 +322,7 @@ def getipaddrinfo(host):
|
||||
"""
|
||||
try:
|
||||
return [addrinfo for addrinfo in socket.getaddrinfo(host, None)
|
||||
if (addrinfo[0] == socket.AF_INET or
|
||||
if (addrinfo[0] == socket.AF_INET or
|
||||
addrinfo[0] == socket.AF_INET6)
|
||||
and isinstance(addrinfo[4][0], basestring)]
|
||||
except socket.error:
|
||||
|
||||
@@ -42,6 +42,7 @@ __all__ = [
|
||||
'IS_DATETIME',
|
||||
'IS_DECIMAL_IN_RANGE',
|
||||
'IS_EMAIL',
|
||||
'IS_LIST_OF_EMAILS',
|
||||
'IS_EMPTY_OR',
|
||||
'IS_EXPR',
|
||||
'IS_FLOAT_IN_RANGE',
|
||||
@@ -1178,6 +1179,39 @@ class IS_EMAIL(Validator):
|
||||
return (value, None)
|
||||
return (value, translate(self.error_message))
|
||||
|
||||
class IS_LIST_OF_EMAILS(object):
|
||||
"""
|
||||
use as follows:
|
||||
Field('emails','list:string',
|
||||
widget=SQLFORM.widgets.text.widget,
|
||||
requires=IS_LIST_OF_EMAILS(),
|
||||
represent=lambda v,r: \
|
||||
SPAN(*[A(x,_href='mailto:'+x) for x in (v or [])])
|
||||
)
|
||||
"""
|
||||
split_emails = re.compile('[^,;\s]+')
|
||||
def __init__(self, error_message = 'Invalid emails: %s'):
|
||||
self.error_message = error_message
|
||||
|
||||
def __call__(self, value):
|
||||
bad_emails = []
|
||||
emails = []
|
||||
f = IS_EMAIL()
|
||||
for email in self.split_emails.findall(value):
|
||||
if not email in emails:
|
||||
emails.append(email)
|
||||
error = f(email)[1]
|
||||
if error and not email in bad_emails:
|
||||
bad_emails.append(email)
|
||||
if not bad_emails:
|
||||
return (value, None)
|
||||
else:
|
||||
return (value,
|
||||
translate(self.error_message) % ', '.join(bad_emails))
|
||||
|
||||
def formatter(self,value,row=None):
|
||||
return ', '.join(value or [])
|
||||
|
||||
|
||||
# URL scheme source:
|
||||
# <http://en.wikipedia.org/wiki/URI_scheme> obtained on 2008-Nov-10
|
||||
@@ -2890,7 +2924,8 @@ class CRYPT(object):
|
||||
key=None,
|
||||
digest_alg='pbkdf2(1000,20,sha512)',
|
||||
min_length=0,
|
||||
error_message='too short', salt=True):
|
||||
error_message='too short', salt=True,
|
||||
max_length=1024):
|
||||
"""
|
||||
important, digest_alg='md5' is not the default hashing algorithm for
|
||||
web2py. This is only an example of usage of this function.
|
||||
@@ -2901,10 +2936,12 @@ class CRYPT(object):
|
||||
self.key = key
|
||||
self.digest_alg = digest_alg
|
||||
self.min_length = min_length
|
||||
self.max_length = max_length
|
||||
self.error_message = error_message
|
||||
self.salt = salt
|
||||
|
||||
def __call__(self, value):
|
||||
value = value and value[:self.max_length]
|
||||
if len(value) < self.min_length:
|
||||
return ('', translate(self.error_message))
|
||||
return (LazyCrypt(self, value), None)
|
||||
@@ -3495,7 +3532,7 @@ class IS_IPV6(Validator):
|
||||
('2001::8ffa:fe22:b3af', None)
|
||||
>>> IS_IPV6(subnets='invalidsubnet')('2001::8ffa:fe22:b3af')
|
||||
('2001::8ffa:fe22:b3af', 'invalid subnet provided')
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -176,7 +176,7 @@ class Web2pyCronService(Web2pyService):
|
||||
from gluon.settings import global_settings
|
||||
from gluon.fileutils import abspath
|
||||
from os.path import exists, join
|
||||
|
||||
|
||||
self.log('web2py Cron service starting')
|
||||
if not self.chdir():
|
||||
return
|
||||
@@ -186,7 +186,7 @@ class Web2pyCronService(Web2pyService):
|
||||
opt_mod = self._exe_args_
|
||||
options = __import__(opt_mod, [], [], '')
|
||||
logpath = abspath(join(options.folder, "logging.conf"))
|
||||
|
||||
|
||||
if exists(logpath):
|
||||
logging.config.fileConfig(logpath)
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user