Compare commits

...

47 Commits

Author SHA1 Message Date
mdipierro
afdb028070 R-2.6.4 2013-09-21 20:42:19 -05:00
mdipierro
80d4615f32 R-2.6.4 2013-09-21 20:24:31 -05:00
mdipierro
5a12a7fb31 manually committed pull 221, update vars only if FieldStorage holds actually something, thanks Niphlod 2013-09-21 18:15:27 -05:00
mdipierro
17c10b8a45 things got out of sync 2013-09-20 21:28:26 -05:00
mdipierro
f8e2c8e319 new validator IS_LIST_OF_EMAILS 2013-09-20 21:17:05 -05:00
mdipierro
9ea82fdaaf fixed issue 1686:admin editor files menu hidden when codemirror is in fullscreen mode, thanks Paolo Caruccio 2013-09-20 20:57:29 -05:00
mdipierro
3d01a83c85 Merge pull request #220 from bunnyhugdev/master
Edited cookie handling code when using a db to store sessions
2013-09-20 12:51:49 -07:00
Joel Rathgaber
13f8b1a10c Edited cookie handling code when using a db to store sessions
- Session id cookie is always sent back to client in response
- When no cookie is sent in as a request, a new session id is created
2013-09-20 13:29:50 -06:00
Massimo
f78e172eda fixed issue 1673, propagation for format in archive tables 2013-09-20 12:47:15 -05:00
mdipierro
bd452f90b2 session._fixup_before_save 2013-09-20 08:36:52 -05:00
mdipierro
2b1f150f02 relaxed REST conditions 2013-09-19 22:18:45 -05:00
mdipierro
e7a9457249 fixed problem with widget and list:string 2013-09-19 22:03:37 -05:00
mdipierro
d3ab2e73d5 support for driver mysql.connector, thank you Josiel Santos 2013-09-19 21:44:12 -05:00
mdipierro
860f40ca63 added support for 3letter language files, issue 1680, thanks Fran 2013-09-19 21:33:31 -05:00
mdipierro
59a7db56a4 fixed major problem with persistance of session.flash 2013-09-19 20:51:11 -05:00
mdipierro
01e5107a96 possible fix to issues 1682, REST and DELETE 2013-09-19 18:33:27 -05:00
mdipierro
8765dbbe0f an attempt to fix 1685 2013-09-19 18:26:35 -05:00
mdipierro
d125b0b95b expires can be str or datetime 2013-09-19 18:03:35 -05:00
mdipierro
1d2fc4b8f5 simpler logic in parse POST but not functional difference 2013-09-19 17:47:29 -05:00
mdipierro
146ea115bf Merge pull request #219 from niphlod/enhancement/ajax_page_notarget
fixed issue when target is none. removed useless file in admin/static/js
2013-09-19 14:44:03 -07:00
niphlod
4f44188997 fixed issue when target is not none. removed useless file in admin/static/js 2013-09-19 21:52:45 +02:00
mdipierro
ac5fb303eb Merge pull request #218 from mpranjic/masterfix
startswith, endswith and contains for upload fields
2013-09-19 12:10:07 -07:00
mdipierro
31a14997f8 Merge pull request #216 from ilvalle/ajax_title
Set document title when an ajax fragment has got a <title> element
2013-09-19 12:09:01 -07:00
mpranjic
4cd4ff2c5e startswith, endswith and contains for upload fields 2013-09-19 10:58:23 +02:00
Massimo
26ef508966 added some missing files, is admin/static/js/jquery-1.10.0.min.map correct? 2013-09-18 14:37:28 -05:00
Paolo
28b4badb9a concatenation of filter and find for a better <title> discovery 2013-09-18 17:33:54 +02:00
mdipierro
8e2e2420b0 fixed style of edit in admin 2013-09-18 10:18:31 -05:00
mdipierro
67349627f4 Merge pull request #214 from niphlod/fix/admin_emmet
fixes keyboard shortcuts in admin when modifying js or css files
2013-09-18 08:05:49 -07:00
mdipierro
cfe825f94a Merge pull request #213 from spametki/master
Fix imap insert multipart detection
2013-09-18 08:05:05 -07:00
mdipierro
53e79915fa skip virtual fields on AttributeError 2013-09-18 08:29:31 -05:00
ilvalle
9fb5f688ed Set document title if an ajax fragment has a <title> element 2013-09-18 09:15:06 +02:00
mdipierro
c2cdae0615 new setup_exe.py, thanks Niphlod 2013-09-17 08:31:01 -05:00
mdipierro
2516bb59a1 fixed issue 1679:auth.navbar() duplicates first link if mode is 'default', thanks wkl 2013-09-16 08:55:43 -05:00
mdipierro
13c78fae58 R-2.6.3 2013-09-15 12:00:13 -05:00
mdipierro
91c0a31800 R-2.6.3 2013-09-15 11:51:01 -05:00
mdipierro
277137c8e6 R-2.6.3 2013-09-15 11:48:32 -05:00
mdipierro
4556a355a2 fixed a problem with CRYPT password length 2013-09-15 11:46:46 -05:00
mdipierro
7dafb07438 added a extra level of protection for long passwords, even if IS_LENGTH validator is missing 2013-09-15 09:40:15 -05:00
niphlod
3c8e7a1364 fixes keyboard shortcuts in admin when modifying js or css files 2013-09-15 11:23:58 +02:00
spametki
921cd46c10 Fix imap insert multipart detection 2013-09-14 16:03:41 -03:00
mdipierro
b0f6dc4e16 R-2.6.2 2013-09-13 17:44:01 -05:00
mdipierro
1499b19575 R-2.6.2 2013-09-13 17:41:13 -05:00
mdipierro
877b867cef Merge pull request #212 from niphlod/fix/js_punctuation
never got to understand ASI. Let's behave and have a semicolon.
2013-09-13 15:27:46 -07:00
niphlod
837808bd77 never got to understand ASI. Let's behave and have a semicolon. 2013-09-14 00:22:53 +02:00
mdipierro
10a69f4338 removed circular import for validators 2013-09-13 17:14:47 -05:00
mdipierro
7bc603f380 fixed Pointless to set session.flash before session.renew, thanks nursix 2013-09-13 15:41:58 -05:00
mdipierro
6c1cc6fc96 tabs -> spaces 2013-09-13 15:39:39 -05:00
47 changed files with 443 additions and 264 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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():

View File

@@ -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})

View File

@@ -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:

View File

@@ -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',

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
[DEFAULT]
theme = web2py
[editor]
theme = web2py

View File

@@ -4,7 +4,7 @@
position: absolute ! important;
top: 0; left: 0;
width: 100% ! important;
z-index: 9999;
z-index: 1028;
}
/* BREAKPOINTS */

View File

@@ -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*/

View File

@@ -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');
});

View File

@@ -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();};

View File

@@ -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():

View File

@@ -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(

View File

@@ -1,4 +1,3 @@
def hello1():
""" simple page without template """

View File

@@ -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*/

View File

@@ -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');
});

View File

@@ -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():

View File

@@ -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)

View File

@@ -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*/

View File

@@ -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');
});

View File

@@ -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:

View File

@@ -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 *

View File

@@ -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)

View File

@@ -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,

View File

@@ -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])

View File

@@ -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'))

View File

@@ -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

View File

@@ -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())

View File

@@ -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:

View File

@@ -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):

View File

@@ -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:

View 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']:

View File

@@ -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)}

View File

@@ -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):
"""

View File

@@ -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,

View File

@@ -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

View File

@@ -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).'

View File

@@ -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)

View File

@@ -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 ''

View File

@@ -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')

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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__(

View File

@@ -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: