diff --git a/Makefile b/Makefile index 1ab8a03d..d764297d 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ update: wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py echo "remember that pymysql was tweaked" src: - echo 'Version 2.0.8 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION + echo 'Version 2.0.9 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION ### rm -f all junk files make clean ### clean up baisc apps diff --git a/VERSION b/VERSION index a2c32e82..7ba0fb63 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.0.8 (2012-09-10 11:34:33) stable +Version 2.0.9 (2012-09-18 13:39:12) stable diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 9b1dbedb..0cb2e8de 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -15,17 +15,15 @@ from gluon.utils import web2py_uuid from glob import glob import shutil import platform -try: - from git import * +try: + from git import * have_git = True -except ImportError: +except ImportError: have_git = False - GIT_MISSING = 'requires python-git module, but not installed or incompatible version' + GIT_MISSING = 'requires gitpython module, but not installed or incompatible version' -from gluon.languages import (regex_language, read_possible_languages, - lang_sampling, - read_dict, write_dict, read_plural_dict, - write_plural_dict, PLURAL_RULES) +from gluon.languages import (read_possible_languages, read_dict, write_dict, + read_plural_dict, write_plural_dict) if DEMO_MODE and request.function in ['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','update_languages','reload_routes','git_push','git_pull']: @@ -368,10 +366,15 @@ def uninstall(): else: session.flash = T('no permission to uninstall "%s"', app) redirect(URL('site')) - if app_uninstall(app, request): - session.flash = T('application "%s" uninstalled', app) - else: + try: + filename = app_pack(app, request, raise_ex=True) + except: session.flash = T('unable to uninstall "%s"', app) + else: + if app_uninstall(app, request): + session.flash = T('application "%s" uninstalled', app) + else: + session.flash = T('unable to uninstall "%s"', app) redirect(URL('site')) return dict(app=app, dialog=dialog) @@ -812,7 +815,6 @@ def edit_language(): def edit_plurals(): """ Edit plurals file """ - #import ipdb; ipdb.set_trace() app = get_app() filename = '/'.join(request.args) plurals = read_plural_dict(apath(filename, r=request)) # plural forms dictionary @@ -949,30 +951,11 @@ def design(): statics.sort() # Get all languages - all_languages=dict([(lang+'.py',info[0]) for lang,info - in read_possible_languages(apath(app, r=request)).iteritems() - if info[2]!=0]) # info[2] is langfile_mtime: - # get only existed files - languages = sorted(all_languages) - - plural_rules = {} - all_plurals = PLURAL_RULES - for langfile,lang in all_languages.iteritems(): - lang=lang.strip() - match_language = regex_language.match(lang) - if match_language: - match_language = tuple(part - for part in match_language.groups() - if part) - plang = lang_sampling(match_language, all_plurals.keys()) - if plang: - plural=all_plurals[plang] - plural_rules[langfile]=(plural[0],plang,plural[4],plural[3]) - else: - plural_rules[langfile]=(0,lang,'plural_rules-%s.py'%lang,'') - - plurals = listdir(apath('%s/languages/' % app, r=request), - '^plural-[\w-]+\.py$') + languages=dict([(lang,info) for lang,info + in read_possible_languages( + apath(app, r=request)).iteritems() + if info[2]!=0]) # info[2] is langfile_mtime: + # get only existed files #Get crontab cronfolder = apath('%s/cron' % app, r=request) @@ -1000,8 +983,6 @@ def design(): privates=filter_plugins(privates,plugins), statics=filter_plugins(statics,plugins), languages=languages, - plurals=plurals, - plural_rules=plural_rules, crontab=crontab, plugins=plugins) diff --git a/applications/admin/languages/it.py b/applications/admin/languages/it.py index d578056e..957fe0bf 100644 --- a/applications/admin/languages/it.py +++ b/applications/admin/languages/it.py @@ -3,143 +3,26 @@ '!langcode!': 'it', '!langname!': 'Italiano', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', -'%Y-%m-%d': '%d/%m/%Y', -'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S', '%s %%{row} deleted': '%s righe ("record") cancellate', '%s %%{row} updated': '%s righe ("record") modificate', +'%Y-%m-%d': '%d/%m/%Y', +'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S', '(requires internet access)': '(requires internet access)', '(something like "it-it")': '(qualcosa simile a "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', 'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s', -'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)", -'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")', -'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ", 'About': 'informazioni', 'About application': "Informazioni sull'applicazione", -'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura', -'Admin language': 'Admin language', -'Administrator Password:': 'Password Amministratore:', -'Application name:': 'Application name:', -'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?', -'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?', -'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?', -'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?', -'Available databases and tables': 'Database e tabelle disponibili', -'Cannot be empty': 'Non può essere vuoto', -'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.", -'Change admin password': 'change admin password', -'Check for upgrades': 'check for upgrades', -'Check to delete': 'Seleziona per cancellare', -'Checking for upgrades...': 'Controllo aggiornamenti in corso...', -'Clean': 'pulisci', -'Compile': 'compila', -'Controller': 'Controller', -'Controllers': 'Controllers', -'Copyright': 'Copyright', -'Create': 'crea', -'Create new simple application': 'Crea nuova applicazione', -'Current request': 'Richiesta (request) corrente', -'Current response': 'Risposta (response) corrente', -'Current session': 'Sessione (session) corrente', -'DB Model': 'Modello di DB', -'Database': 'Database', -'Date and Time': 'Data and Ora', -'Delete': 'Cancella', -'Delete:': 'Cancella:', -'Deploy': 'deploy', -'Deploy on Google App Engine': 'Installa su Google App Engine', -'EDIT': 'MODIFICA', -'Edit': 'modifica', -'Edit This App': 'Modifica questa applicazione', -'Edit application': 'Modifica applicazione', -'Edit current record': 'Modifica record corrente', -'Editing Language file': 'Modifica file linguaggio', -'Editing file "%s"': 'Modifica del file "%s"', -'Enterprise Web Framework': 'Enterprise Web Framework', -'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"', -'Errors': 'errori', -'Exception instance attributes': 'Exception instance attributes', -'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].', -'Hello World': 'Salve Mondo', -'Help': 'aiuto', -'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.', -'Import/Export': 'Importa/Esporta', -'Index': 'Indice', -'Install': 'installa', -'Installed applications': 'Applicazioni installate', -'Internal State': 'Stato interno', -'Invalid Query': 'Richiesta (query) non valida', -'Invalid action': 'Azione non valida', -'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati', -'Languages': 'Linguaggi', -'Last saved on:': 'Ultimo salvataggio:', -'Layout': 'Layout', -'License for': 'Licenza relativa a', -'Login': 'Accesso', -'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa", -'Logout': 'uscita', -'Main Menu': 'Menu principale', -'Menu Model': 'Menu Modelli', -'Models': 'Modelli', -'Modules': 'Moduli', -'NO': 'NO', -'New Record': 'Nuovo elemento (record)', -'New application wizard': 'New application wizard', -'New simple application': 'New simple application', -'No databases in this application': 'Nessun database presente in questa applicazione', -'Original/Translation': 'Originale/Traduzione', -'Overwrite installed app': 'sovrascrivi applicazione installata', -'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui', -'Pack all': 'crea pacchetto', -'Pack compiled': 'crea pacchetto del codice compilato', -'Peeking at file': 'Uno sguardo al file', -'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione', -'Plugins': 'I Plugins', -'Powered by': 'Powered by', -'Query:': 'Richiesta (query):', -'Remove compiled': 'rimozione codice compilato', -'Resolve Conflict file': 'File di risoluzione conflitto', -'Rows in table': 'Righe nella tabella', -'Rows selected': 'Righe selezionate', -'Saved file hash:': 'Hash del file salvato:', -'Site': 'sito', -'Start wizard': 'start wizard', -'Static files': 'Files statici', -'Stylesheet': 'Foglio di stile (stylesheet)', -'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', -'TM': 'TM', -'Testing application': 'Test applicazione in corsg', -'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.', -'There are no controllers': 'Non ci sono controller', -'There are no models': 'Non ci sono modelli', -'There are no modules': 'Non ci sono moduli', -'There are no static files': 'Non ci sono file statici', -'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base', -'There are no views': 'Non ci sono viste ("view")', -'This is the %(filename)s template': 'Questo è il template %(filename)s', -'Ticket': 'Ticket', -'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]', -'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti', -'Unable to download app because:': 'Impossibile scaricare applicazione perché', -'Unable to download because': 'Impossibile scaricare perché', -'Unable to download because:': 'Unable to download because:', -'Uninstall': 'disinstalla', -'Update:': 'Aggiorna:', -'Upload & install packed application': 'Carica ed installa pacchetto con applicazione', -'Upload a package:': 'Upload a package:', -'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', -'Use an url:': 'Use an url:', -'Version': 'Versione', -'View': 'Vista', -'Views': 'viste', -'Welcome %s': 'Benvenuto %s', -'Welcome to web2py': 'Benvenuto su web2py', -'YES': 'SI', 'additional code for your application': 'righe di codice aggiuntive per la tua applicazione', +'Additional code for your application': 'Additional code for your application', 'admin disabled because no admin password': 'amministrazione disabilitata per mancanza di password amministrativa', 'admin disabled because not supported on google app engine': 'amministrazione non supportata da Google Apps Engine', 'admin disabled because unable to access password file': 'amministrazione disabilitata per impossibilità di leggere il file delle password', +'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura', +'Admin language': 'Admin language', 'administrative interface': 'administrative interface', +'Administrator Password:': 'Password Amministratore:', 'and rename it (required):': 'e rinominala (obbligatorio):', 'and rename it:': 'e rinominala:', 'appadmin': 'appadmin ', @@ -147,44 +30,104 @@ 'application "%s" uninstalled': 'applicazione "%s" disinstallata', 'application compiled': 'applicazione compilata', 'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare", +'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"?': 'Confermi di voler cancellare il file "%s"?', +'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?', +'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', +'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?', +'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?', 'arguments': 'arguments', +'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)", +'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")', +'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ", +'Available databases and tables': 'Database e tabelle disponibili', 'back': 'indietro', 'cache': 'cache', 'cache, errors and sessions cleaned': 'pulitura cache, errori and sessioni ', +'can be a git repo': 'can be a git repo', +'Cannot be empty': 'Non può essere vuoto', +'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.", 'cannot create file': 'impossibile creare il file', 'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"', +'Change admin password': 'change admin password', 'change password': 'cambia password', 'check all': 'controlla tutto', +'Check for upgrades': 'check for upgrades', +'Check to delete': 'Seleziona per cancellare', +'Checking for upgrades...': 'Controllo aggiornamenti in corso...', +'Clean': 'pulisci', 'click here for online examples': 'clicca per vedere gli esempi', 'click here for the administrative interface': "clicca per l'interfaccia amministrativa", 'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti', 'code': 'code', +'collapse/expand all': 'collapse/expand all', +'Compile': 'compila', 'compiled application removed': "rimosso il codice compilato dell'applicazione", +'Controller': 'Controller', +'Controllers': 'Controllers', 'controllers': 'controllers', +'Copyright': 'Copyright', +'Create': 'crea', 'create file with filename:': 'crea un file col nome:', 'create new application:': 'create new application:', +'Create new simple application': 'Crea nuova applicazione', 'created by': 'creato da', 'crontab': 'crontab', +'Current request': 'Richiesta (request) corrente', +'Current response': 'Risposta (response) corrente', +'Current session': 'Sessione (session) corrente', 'currently running': 'currently running', 'currently saved or': 'attualmente salvato o', 'customize me!': 'Personalizzami!', 'data uploaded': 'dati caricati', +'Database': 'Database', 'database': 'database', 'database %s select': 'database %s select', 'database administration': 'amministrazione database', +'Date and Time': 'Data and Ora', 'db': 'db', +'DB Model': 'Modello di DB', +'Debug': 'Debug', 'defines tables': 'defininisce le tabelle', +'Delete': 'Cancella', 'delete': 'Cancella', 'delete all checked': 'cancella tutti i selezionati', 'delete plugin': 'cancella plugin', +'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)', +'Delete:': 'Cancella:', +'Deploy': 'deploy', +'Deploy on Google App Engine': 'Installa su Google App Engine', +'Deploy to OpenShift': 'Deploy to OpenShift', 'design': 'progetta', +'Detailed traceback description': 'Detailed traceback description', 'direction: ltr': 'direction: ltr', +'Disable': 'Disable', +'docs': 'docs', 'done!': 'fatto!', +'download layouts': 'download layouts', +'download plugins': 'download plugins', +'EDIT': 'MODIFICA', +'Edit': 'modifica', +'Edit application': 'Modifica applicazione', 'edit controller': 'modifica controller', +'Edit current record': 'Modifica record corrente', 'edit profile': 'modifica profilo', +'Edit This App': 'Modifica questa applicazione', 'edit views:': 'modifica viste (view):', +'Editing file "%s"': 'Modifica del file "%s"', +'Editing Language file': 'Modifica file linguaggio', +'Enterprise Web Framework': 'Enterprise Web Framework', +'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"', +'Error snapshot': 'Error snapshot', +'Error ticket': 'Error ticket', +'Errors': 'errori', +'Exception instance attributes': 'Exception instance attributes', +'Expand Abbreviation': 'Expand Abbreviation', 'export as csv file': 'esporta come file CSV', 'exposes': 'espone', +'exposes:': 'exposes:', 'extends': 'estende', 'failed to reload module because:': 'ricaricamento modulo fallito perché:', 'file "%(filename)s" created': 'creato il file "%(filename)s"', @@ -195,73 +138,188 @@ 'file does not exist': 'file inesistente', 'file saved on %(time)s': "file salvato nell'istante %(time)s", 'file saved on %s': 'file salvato: %s', +'filter': 'filter', +'Frames': 'Frames', +'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].', +'Get from URL:': 'Get from URL:', +'Git Pull': 'Git Pull', +'Git Push': 'Git Push', +'Hello World': 'Salve Mondo', +'Help': 'aiuto', 'htmledit': 'modifica come html', +'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.', +'Import/Export': 'Importa/Esporta', 'includes': 'include', +'Index': 'Indice', 'insert new': 'inserisci nuovo', 'insert new %s': 'inserisci nuovo %s', +'inspect attributes': 'inspect attributes', +'Install': 'installa', +'Installed applications': 'Applicazioni installate', 'internal error': 'errore interno', +'Internal State': 'Stato interno', +'Invalid action': 'Azione non valida', 'invalid password': 'password non valida', +'Invalid Query': 'Richiesta (query) non valida', 'invalid request': 'richiesta non valida', 'invalid ticket': 'ticket non valido', +'Key bindings': 'Key bindings', +'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin', 'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato', +'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati', 'languages': 'linguaggi', +'Languages': 'Linguaggi', +'Last saved on:': 'Ultimo salvataggio:', +'Layout': 'Layout', +'License for': 'Licenza relativa a', 'loading...': 'caricamento...', +'locals': 'locals', 'login': 'accesso', +'Login': 'Accesso', +'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa", +'Logout': 'uscita', +'Main Menu': 'Menu principale', +'Menu Model': 'Menu Modelli', 'merge': 'unisci', 'models': 'modelli', +'Models': 'Modelli', +'Modules': 'Moduli', 'modules': 'moduli', 'new application "%s" created': 'creata la nuova applicazione "%s"', +'New application wizard': 'New application wizard', 'new plugin installed': 'installato nuovo plugin', +'New Record': 'Nuovo elemento (record)', 'new record inserted': 'nuovo record inserito', +'New simple application': 'New simple application', 'next 100 rows': 'prossime 100 righe', +'NO': 'NO', +'No databases in this application': 'Nessun database presente in questa applicazione', 'no match': 'nessuna corrispondenza', 'or import from csv file': 'oppure importa da file CSV', 'or provide app url:': "oppure fornisci url dell'applicazione:", +'Original/Translation': 'Originale/Traduzione', +'Overwrite installed app': 'sovrascrivi applicazione installata', +'Pack all': 'crea pacchetto', +'Pack compiled': 'crea pacchetto del codice compilato', 'pack plugin': 'crea pacchetto del plugin', +'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui', 'password changed': 'password modificata', +'Peeking at file': 'Uno sguardo al file', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato', +'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione', +'plugins': 'plugins', +'Plugins': 'I Plugins', +'Plural-Forms:': 'Plural-Forms:', +'Powered by': 'Powered by', 'previous 100 rows': '100 righe precedenti', +'private files': 'private files', +'Private files': 'Private files', +'Query:': 'Richiesta (query):', 'record': 'record', 'record does not exist': 'il record non esiste', 'record id': 'ID del record', 'register': 'registrazione', +'Remove compiled': 'rimozione codice compilato', +'request': 'request', +'Resolve Conflict file': 'File di risoluzione conflitto', +'response': 'response', 'restore': 'ripristino', 'revert': 'versione precedente', +'Rows in table': 'Righe nella tabella', +'Rows selected': 'Righe selezionate', +'rules are not defined': 'rules are not defined', +"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 via Ajax': 'Save via Ajax', +'Saved file hash:': 'Hash del file salvato:', 'selected': 'selezionato', +'session': 'session', 'session expired': 'sessions scaduta', 'shell': 'shell', +'Site': 'sito', 'some files could not be removed': 'non è stato possibile rimuovere alcuni files', +'Start wizard': 'start wizard', 'state': 'stato', 'static': 'statico', +'Static files': 'Files statici', +'Stylesheet': 'Foglio di stile (stylesheet)', 'submit': 'invia', +'Submit': 'Submit', +'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 'table': 'tabella', 'test': 'test', +'Testing application': 'Test applicazione in corsg', +'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.', +'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller', 'the application logic, each URL path is mapped in one exposed function in the controller': 'logica dell\'applicazione, ogni percorso "URL" corrisponde ad una funzione esposta da un controller', +'The data representation, define database tables and sets': 'The data representation, define database tables and sets', 'the data representation, define database tables and sets': 'rappresentazione dei dati, definizione di tabelle di database e di "set" ', +'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates', 'the presentations layer, views are also known as templates': 'Presentazione dell\'applicazione, viste (views, chiamate anche "templates")', +'There are no controllers': 'Non ci sono controller', +'There are no models': 'Non ci sono modelli', +'There are no modules': 'Non ci sono moduli', +'There are no plugins': 'There are no plugins', +'There are no static files': 'Non ci sono file statici', +'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base', +'There are no views': 'Non ci sono viste ("view")', +'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', 'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui', +'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'This is the %(filename)s template': 'Questo è il template %(filename)s', +'Ticket': 'Ticket', +'Ticket ID': 'Ticket ID', +'TM': 'TM', 'to previous version.': 'torna a versione precedente', +'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]', +'toggle breakpoint': 'toggle breakpoint', +'Toggle Fullscreen': 'Toggle Fullscreen', +'Traceback': 'Traceback', 'translation strings for the application': "stringhe di traduzioni per l'applicazione", +'Translation strings for the application': 'Translation strings for the application', 'try': 'prova', 'try something like': 'prova qualcosa come', +'try view': 'try view', +'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti', 'unable to create application "%s"': 'impossibile creare applicazione "%s"', 'unable to delete file "%(filename)s"': 'impossibile rimuovere file "%(plugin)s"', 'unable to delete file plugin "%(plugin)s"': 'impossibile rimuovere file di plugin "%(plugin)s"', +'Unable to download app because:': 'Impossibile scaricare applicazione perché', +'Unable to download because': 'Impossibile scaricare perché', +'Unable to download because:': 'Unable to download because:', 'unable to parse csv file': 'non riesco a decodificare questo file CSV', 'unable to uninstall "%s"': 'impossibile disinstallare "%s"', 'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"', 'uncheck all': 'smarca tutti', +'Uninstall': 'disinstalla', 'update': 'aggiorna', 'update all languages': 'aggiorna tutti i linguaggi', +'Update:': 'Aggiorna:', 'upgrade web2py now': 'upgrade web2py now', +'upload': 'upload', +'Upload & install packed application': 'Carica ed installa pacchetto con applicazione', +'Upload a package:': 'Upload a package:', +'Upload and install packed application': 'Upload and install packed application', 'upload application:': 'carica applicazione:', 'upload file:': 'carica file:', 'upload plugin file:': 'carica file di plugin:', +'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', +'Use an url:': 'Use an url:', 'variables': 'variables', +'Version': 'Versione', +'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s', 'versioning': 'sistema di versioni', +'Versioning': 'Versioning', 'view': 'vista', +'View': 'Vista', +'Views': 'viste', 'views': 'viste', -'web2py Recent Tweets': 'Tweets recenti per web2py', +'Web Framework': 'Web Framework', 'web2py is up to date': 'web2py è aggiornato', +'web2py Recent Tweets': 'Tweets recenti per web2py', 'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo', +'Welcome %s': 'Benvenuto %s', +'Welcome to web2py': 'Benvenuto su web2py', +'YES': 'SI', } diff --git a/applications/admin/languages/uk.py b/applications/admin/languages/uk.py index 461a4370..b70f3a8c 100644 --- a/applications/admin/languages/uk.py +++ b/applications/admin/languages/uk.py @@ -14,6 +14,7 @@ '%Y-%m-%d %H:%M:%S': '%Y/%m/%d %H:%M:%S', '(requires internet access)': '(потрібно мати доступ в інтернет)', '(something like "it-it")': '(щось схоже на "uk-ua")', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(не існує файлу **gluon/contrib/plural_rules/%s.py**)', '@markmin\x01Searching: **%s** %%{file}': 'Знайдено: **%s** %%{файл}', 'Abort': 'Припинити', 'About': 'Про', @@ -405,6 +406,7 @@ 'ticket': 'позначка', 'Ticket': 'Позначка (Ticket)', 'Ticket ID': 'Ід.позначки (Ticket ID)', +'Ticket Missing': 'Позначка (ticket) відсутня', 'tickets': 'позначки (tickets)', 'Time in Cache (h:m:s)': 'Час в кеші (г:хв:сек)', 'to previous version.': 'до попередньої версії.', @@ -412,6 +414,7 @@ 'To emulate a breakpoint programatically, write:': 'Для встановлення точки зупинки програмним чином напишіть:', 'to use the debugger!': 'щоб активувати ладнач!', 'toggle breakpoint': '+/- точку зупинки', +'Toggle Fullscreen': 'Перемкнути на весь екран', 'Traceback': 'Стек викликів (Traceback)', 'Translation strings for the application': 'Пари рядків <оригінал>:<переклад> для вибраної мови', 'try something like': 'спробуйте щось схоже на', diff --git a/applications/admin/static/css/web2py.css b/applications/admin/static/css/web2py.css index 54b1cbce..ac04a0f0 100644 --- a/applications/admin/static/css/web2py.css +++ b/applications/admin/static/css/web2py.css @@ -10,14 +10,14 @@ h3 {font-size:2.00em} h4 {font-size:1.50em} h5 {font-size:1.25em} h6 {font-size:1.12em} -th,label {font-weight:bold; white-space:nowrap} +th,label {font-weight:bold; white-space:nowrap;} td,th {text-align:left; padding:2px 5px 2px 5px} th {vertical-align:middle; border-right:1px solid white} td {vertical-align:top} form table tr td label {text-align:left} -p,table,ol,ul {padding:0; margin: 0.5em 0} +p,table,ol,ul {padding:0; margin: 0.75em 0} p {text-align:justify} -ol, ul {list-style-position:inside} +ol, ul {list-style-position:outside; margin-left:2em} li {margin-bottom:0.5em} span,input,select,textarea,button,label,a {display:inline} img {border:0} @@ -39,13 +39,6 @@ input[type=text],input[type=password],select{width:300px; margin-right:5px} /* Sticky footer begin */ -.wrapper { - min-height:100%; - height:auto !important; - height:100%; - margin:0 auto -8em; /* set last value to footer height plus footer vertical padding */ -} - .main { padding:20px 0 50px 0; } @@ -76,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid} fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1} /* fix ie problem with menu */ -.ie-lte7 .topbar .container {z-index:2} td.w2p_fw {padding-bottom:1px} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} @@ -102,25 +94,42 @@ div.flash { top:48px; right:50px; min-width:280px; - opacity:0.85; + opacity:0.95; margin:0px 0px 10px 10px; - color:#fff; vertical-align:middle; cursor:pointer; - background:#000; + color:#fff; + background-color:#000; border:2px solid #fff; - border-radius:5px; - -moz-border-radius:5px; - -webkit-border-radius:5px; + border-radius:8px; + -o-border-radius: 8px; + -moz-border-radius:8px; + -webkit-border-radius:8px; + background-image: -webkit-linear-gradient(top,#222,#000); + background-image: -o-linear-gradient(top,#222,#000); + background-image: -moz-linear-gradient(90deg, #222, #000); + background-image: linear-gradient(top,#222,#000); + background-repeat: repeat-x; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + -webkit-font-smoothing: antialiased; z-index:2000; } +div.flash:hover { opacity:0.25; } + div.error_wrapper {display:block} div.error { - background-color:red; + width: 298px; + background:red; + border: 2px solid #d00; color:white; - padding:3px; - display:inline-block; + padding:5px; + display:inline-block; + background-image: -webkit-linear-gradient(left,#f00,#fdd); + background-image: -o-linear-gradient(left,#f00,#fdd); + background-image: -moz-linear-gradient(0deg, #f00, #fdd); + background-image: linear-gradient(left,#f00,#fdd); + background-repeat: repeat-y; } .topbar { @@ -166,19 +175,6 @@ div.error { /* #MEDIA QUERIES SECTION */ - /* All Mobile Sizes (devices and browser) */ - @media only screen and (max-width:767px) { -/* removed because of bootswatch - .topbar {text-align:center} - #navbar,#menu {float:none} - #navbar {font-size:1.2em; padding:.6em 0 1.2em} - #menu {padding:0 0 1.5em} - #menu select {font-size:1.2em; margin:0; padding:0} - - div.flash {top:110px; right:10px} -*/ - } - /* *Grid * @@ -190,10 +186,7 @@ div.error { .web2py_paginator {} .web2py_grid {width:100%} .web2py_grid table {width:100%} -.web2py_grid tbody td { - padding:2px 5px 2px 5px; - vertical-align:middle; -} +.web2py_grid tbody td {padding:2px 5px 2px 5px; vertical-align: middle;} .web2py_grid thead th,.web2py_grid tfoot td { background-color:#EAEAEA; @@ -230,7 +223,7 @@ div.error { margin:0; } -.web2py_search_actions{ +.web2py_search_actions { float:left; text-align:left; } @@ -254,13 +247,13 @@ div.error { .web2py_console input[type=button], .web2py_console button { line-height:20px; - margin-right:5px; display:inline-block; + margin-right:2px; display:inline-block; padding:3px 5px 3px 5px; } .web2py_counter { margin-top:5px; - margin-right:5px; + margin-right:2px; width:35%; float:right; text-align:right; @@ -298,10 +291,20 @@ li.w2p_grid_breadcrumb_elem { display:inline-block; } -.ie9 #query_panel {padding-bottom:2px} +.web2py_console form { vertical-align: middle; } +.web2py_console input, .web2py_console select, +.web2py_console a { margin: 2px; } + #wiki_page_body { width: 600px; height: auto; min-height: 400px; -} \ No newline at end of file +} + +/* fix some IE problems */ + +.ie-lte7 .topbar .container {z-index:2} +.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); } +.ie-lte8 div.flash:hover {filter:alpha(opacity=25);} +.ie9 #w2p_query_panel {padding-bottom:2px} diff --git a/applications/admin/views/appadmin.html b/applications/admin/views/appadmin.html index 541a18bb..72dfc00c 100644 --- a/applications/admin/views/appadmin.html +++ b/applications/admin/views/appadmin.html @@ -149,7 +149,7 @@
{{=T.M("Number of entries: **%s**", ram['entries'])}}
{{if ram['entries'] > 0:}} -{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", +
{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})", dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
diff --git a/applications/admin/views/default/design.html b/applications/admin/views/default/design.html index 1934fc1d..27f12512 100644 --- a/applications/admin/views/default/design.html +++ b/applications/admin/views/default/design.html @@ -187,8 +187,9 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi {{if not languages:}}
{{=T("There are no translators, only default language is supported")}}
{{pass}}| @@ -203,24 +204,17 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi ( {{=T("Plural-Forms:")}} - {{p=plural_rules[file]}} - {{if p[0] == 0:}} - {{=T("rules are not defined")}}, - - {{=button(URL('create_file', vars=dict(filename=p[2], location='gluon/contrib/rules/', sender=URL('design', args=app), id=id, app=app, token=session.token)), T('Create rules'))}} - + {{p=languages[lang][3:7]}} + {{if p[2] == 'default':}} + {{=T("rules are not defined")}} {{=T.M("(file **gluon/contrib/plural_rules/%s.py** is not found)",lang[:2])}} {{else:}} - {{if p[0] == 1:}} - {{if p[3] == 'ok':}} - {{=B(T("are not used"))}}, - {{else:}} - {{=B(T("rules parsed with errors"))}}, - {{pass}} + {{if p[3] == 1:}} + {{=B(T("are not used"))}} {{else:}} - {{pfile='plural-%s.py'%p[1]}} - {{if pfile in plurals:}} + {{pfile=p[0]}} + {{if p[1]!=0:}} - {{=editpluralsfile('languages',pfile,dict(nplurals=p[0]))}} + {{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}} {{=peekfile('languages',pfile,dict(id=id))}} @@ -235,6 +229,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi |
{{=T.M("Number of entries: **%s**", ram['entries'])}}
{{if ram['entries'] > 0:}} -{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", +
{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})", dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
diff --git a/applications/examples/views/default/index.html b/applications/examples/views/default/index.html index 4548ad8a..52012aa1 100644 --- a/applications/examples/views/default/index.html +++ b/applications/examples/views/default/index.html @@ -80,8 +80,7 @@ Current version: {{="%s.%s.%s (%s) %s" % request.env.web2py_version}}
{{=T.M("Number of entries: **%s**", ram['entries'])}}
{{if ram['entries'] > 0:}} -{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", +
{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})", dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
diff --git a/applications/welcome/views/default/user.html b/applications/welcome/views/default/user.html index c6c28aca..9679e1bb 100644 --- a/applications/welcome/views/default/user.html +++ b/applications/welcome/views/default/user.html @@ -4,10 +4,10 @@ {{ if request.args(0)=='login': if not 'register' in auth.settings.actions_disabled: - form.add_button(T('Register'),URL(args='register')) + form.add_button(T('Register'),URL(args='register'),_class='btn') pass if not 'request_reset_password' in auth.settings.actions_disabled: - form.add_button(T('Lost Password'),URL(args='request_reset_password')) + form.add_button(T('Lost Password'),URL(args='request_reset_password'),_class='btn') pass pass =form diff --git a/applications/welcome/views/layout.html b/applications/welcome/views/layout.html index 35532710..d0a2fd2f 100644 --- a/applications/welcome/views/layout.html +++ b/applications/welcome/views/layout.html @@ -1,177 +1,183 @@ - - - - - - - -
- - - - - -[a-zA-Z][_a-zA-Z\-\d]*)\])?)?$') regex_proto = re.compile(r'(?/=])(?P
\w+):(?P.mailbox
"""
@@ -5684,7 +5703,7 @@ class IMAPAdapter(NoSQLAdapter):
year = int(date_list[2])
month = months.index(date_list[1])
day = int(date_list[0])
- hms = [int(value) for value in date_list[3].split(":")]
+ hms = map(int, date_list[3].split(":"))
return datetime.datetime(year, month, day,
hms[0], hms[1], hms[2]) + add
elif isinstance(date, (datetime.datetime, datetime.date)):
@@ -5797,7 +5816,14 @@ class IMAPAdapter(NoSQLAdapter):
Field("attachments", "list:string", writable=False, readable=False),
)
- return self.connection.mailbox_names
+ # Set a special _mailbox attribute for storing
+ # native mailbox names
+ self.db[mailbox_name].mailbox = \
+ self.connection.mailbox_names[mailbox_name]
+
+ # Set the db instance mailbox collections
+ self.db.mailboxes = self.connection.mailbox_names
+ return self.db.mailboxes
def create_table(self, *args, **kwargs):
# not implemented
@@ -6210,7 +6236,11 @@ class IMAPAdapter(NoSQLAdapter):
raise Exception("Operation not supported")
return result
- def NE(self, first, second):
+ def NE(self, first, second=None):
+ if (second is None) and isinstance(first, Field):
+ # All records special table query
+ if first.type == "id":
+ return self.GE(first, 1)
result = self.NOT(self.EQ(first, second))
result = result.replace("NOT NOT", "").strip()
return result
@@ -6361,11 +6391,9 @@ def sqlhtml_validators(field):
refs = None
db, id = r._db, r._id
if isinstance(db._adapter, GoogleDatastoreAdapter):
- for i in xrange(0, len(ids), 30):
- if not refs:
- refs = db(id.belongs(ids[i:i+30])).select(id)
- else:
- refs = refs&db(id.belongs(ids[i:i+30])).select(id)
+ def count(values): return db(id.belongs(values)).select(id)
+ rx = range(0, len(ids), 30)
+ 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(str(f(r,x.id)) for x in refs) or '')
@@ -6455,7 +6483,7 @@ class Row(object):
def values(self):
return self.__dict__.values()
-
+
def __iter__(self):
return self.__dict__.__iter__()
@@ -6648,7 +6676,7 @@ class DAL(object):
db.define_table('tablename', Field('fieldname1'),
Field('fieldname2'))
"""
-
+
@staticmethod
def set_folder(folder):
"""
@@ -7133,7 +7161,11 @@ def index():
return table
def __contains__(self, tablename):
- return tablename in self.tables
+ try:
+ return tablename in self.tables
+ except AttributeError:
+ # The instance has no .tables attribute yet
+ return False
def get(self,key,default):
return self.__dict__.get(key,default)
@@ -7171,7 +7203,7 @@ def index():
def __call__(self, query=None, ignore_common_filters=None):
if isinstance(query,Table):
- query = query._id>0
+ query = query._id != None
elif isinstance(query,Field):
query = query!=None
return Set(self, query, ignore_common_filters=ignore_common_filters)
@@ -7211,7 +7243,7 @@ def index():
Added 2012-08-24 "fields" and "colnames" optional arguments. If either
is provided, the results cursor returned by the DB driver will be
converted to a DAL Rows object using the db._adapter.parse() method.
-
+
The "fields" argument is a list of DAL Field objects that match the
fields returned from the DB. The Field objects should be part of one or
more Table objects defined on the DAL object. The "fields" list can
@@ -7224,14 +7256,14 @@ def index():
can be specified as a list of field names in tablename.fieldname format.
Again, these should represent tables and fields defined on the DAL
object.
-
+
It is also possible to specify both "fields" and the associated
"colnames". In that case, "fields" can also include DAL Expression
objects in addition to Field objects. For Field objects in "fields",
the associated "colnames" must still be in tablename.fieldname format.
For Expression objects in "fields", the associated "colnames" can
be any arbitrary labels.
-
+
Note, the DAL Table objects referred to by "fields" or "colnames" can
be dummy tables and do not have to represent any real tables in the
database. Also, note that the "fields" and "colnames" must be in the
@@ -7390,7 +7422,7 @@ class Table(object):
db.users.insert(name='me') # print db.users._insert(...) to see SQL
db.users.drop()
"""
-
+
def __init__(
self,
db,
@@ -7472,13 +7504,15 @@ class Table(object):
fields = list(fields)
if db and db._adapter.uploads_in_blob==True:
+ uploadfields = [f.name for f in fields if f.type=='blob']
for field in fields:
+ fn = field.uploadfield
if isinstance(field, Field) and field.type == 'upload'\
- and field.uploadfield is True:
- tmp = field.uploadfield = '%s_blob' % field.name
- if isinstance(field.uploadfield,str) and \
- not [f for f in fields if f.name==field.uploadfield]:
- fields.append(Field(field.uploadfield,'blob',default=''))
+ and fn is True:
+ fn = field.uploadfield = '%s_blob' % field.name
+ if isinstance(fn,str) and not fn in uploadfields:
+ fields.append(Field(fn,'blob',default='',
+ writable=False,readable=False))
lower_fieldnames = set()
reserved = dir(Table) + ['fields']
@@ -8073,6 +8107,10 @@ class Expression(object):
db = self.db
return Expression(db, db._adapter.EXTRACT, self, 'second', 'integer')
+ def epoch(self):
+ db = self.db
+ return Expression(db, db._adapter.EPOCH, self, None, 'integer')
+
def __getslice__(self, start, stop):
db = self.db
if start < 0:
@@ -8213,7 +8251,7 @@ class Expression(object):
def st_asgeojson(self, precision=15, options=0, version=1):
return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self,
- dict(precision=precision, options=options,
+ dict(precision=precision, options=options,
version=version), 'dict')
def st_astext(self):
@@ -8462,7 +8500,7 @@ class Field(Expression):
elif not filename:
filename = file.name
filename = os.path.basename(filename.replace('/', os.sep)\
- .replace('\\', os.sep))
+ .replace('\\', os.sep))
m = REGEX_STORE_PATTERN.search(filename)
extension = m and m.group('e') or 'txt'
uuid_key = web2py_uuid().replace('-', '')[-16:]
@@ -8700,7 +8738,7 @@ class Set(object):
def __call__(self, query, ignore_common_filters=False):
if isinstance(query,Table):
- query = query._id>0
+ query = query._id != None
elif isinstance(query,str):
query = Expression(self.db,query)
elif isinstance(query,Field):
@@ -8717,7 +8755,12 @@ class Set(object):
def _select(self, *fields, **attributes):
adapter = self.db._adapter
- fields = adapter.expand_all(fields, adapter.tables(self.query))
+ tablenames = adapter.tables(self.query,
+ attributes.get('join',None),
+ attributes.get('left',None),
+ attributes.get('orderby',None),
+ attributes.get('groupby',None))
+ fields = adapter.expand_all(fields, tablenames)
return adapter._select(self.query,fields,attributes)
def _delete(self):
@@ -8745,14 +8788,17 @@ class Set(object):
key,
(lambda self=self,distinct=distinct: \
db._adapter.count(self.query,distinct)),
- time_expire)
+ time_expire)
return db._adapter.count(self.query,distinct)
def select(self, *fields, **attributes):
- if self.query is None:# and fields[0]._table._common_filter != None:
- return self(fields[0]._table).select(*fields,**attributes)
adapter = self.db._adapter
- fields = adapter.expand_all(fields, adapter.tables(self.query))
+ tablenames = adapter.tables(self.query,
+ attributes.get('join',None),
+ attributes.get('left',None),
+ attributes.get('orderby',None),
+ attributes.get('groupby',None))
+ fields = adapter.expand_all(fields, tablenames)
return adapter.select(self.query,fields,attributes)
def nested_select(self,*fields,**attributes):
@@ -8860,7 +8906,7 @@ class RecordUpdater(object):
self.colset, self.table, self.id = colset, table, id
def __call__(self, **fields):
- colset, table, id = self.colset, self.table, self.id
+ colset, table, id = self.colset, self.table, self.id
newfields = fields or dict(colset)
for fieldname in newfields.keys():
if not fieldname in table.fields or table[fieldname].type=='id':
@@ -9182,7 +9228,7 @@ class Rows(object):
def xml(self,strict=False,row_name='row',rows_name='rows'):
"""
serializes the table using sqlhtml.SQLTABLE (if present)
- """
+ """
if strict:
ncols = len(self.colnames)
def f(row,field,indent=' '):
diff --git a/gluon/globals.py b/gluon/globals.py
index 5e94b726..ccaab79f 100644
--- a/gluon/globals.py
+++ b/gluon/globals.py
@@ -51,6 +51,7 @@ except ImportError:
have_minify = False
regex_session_id = re.compile('^([\w\-]+/)?[\w\-\.]+$')
+regex_nopasswd = re.compile('(?<=\:)([^:@/]+)(?=@.+)')
__all__ = ['Request', 'Response', 'Session']
@@ -230,7 +231,6 @@ class Response(Storage):
def include_files(self):
-
"""
Caching method for writing out files.
By default, caches in ram for 5 minutes. To change,
@@ -244,8 +244,9 @@ class Response(Storage):
if not item in files: files.append(item)
if have_minify and (self.optimize_css or self.optimize_js):
# cache for 5 minutes by default
+ key = hashlib.md5(repr(files)).hexdigest()
cache = self.cache_includes or (current.cache.ram, 60*5)
- def call_minify():
+ def call_minify(files=files):
return minify.minify(files,
URL('static','temp'),
current.request.folder,
@@ -253,7 +254,7 @@ class Response(Storage):
self.optimize_js)
if cache:
cache_model, time_expire = cache
- files = cache_model('response.files.minified',
+ files = cache_model('response.files.minified/'+key,
call_minify,
time_expire)
else:
@@ -278,7 +279,7 @@ class Response(Storage):
chunk_size = DEFAULT_CHUNK_SIZE,
request=None,
attachment=False,
- filename=None
+ filename=None,
):
"""
if a controller function::
@@ -410,11 +411,13 @@ class Response(Storage):
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
for row in i.db._timings]) \
for i in thread.instances]
- dbtables = dict([(i.uri, {'defined': sorted(list(set(i.db.tables) -
- set(i.db._LAZY_TABLES.keys()))) or
- '[no defined tables]',
- 'lazy': sorted(i.db._LAZY_TABLES.keys()) or
- '[no lazy tables]'})
+ dbtables = dict([(regex_nopasswd.sub('******',i.uri),
+ {'defined':
+ sorted(list(set(i.db.tables) -
+ set(i.db._LAZY_TABLES.keys()))) or
+ '[no defined tables]',
+ 'lazy': sorted(i.db._LAZY_TABLES.keys()) or
+ '[no lazy tables]'})
for i in thread.instances])
else:
dbstats = [] # if no db or on GAE
diff --git a/gluon/html.py b/gluon/html.py
index 2d6b8131..471756f3 100644
--- a/gluon/html.py
+++ b/gluon/html.py
@@ -243,7 +243,8 @@ def URL(
elif a and c and not f: (c,f,a)=(a,c,f)
from globals import current
if hasattr(current,'request'):
- r = current.request
+ r = current.request
+
if r:
application = r.application
controller = r.controller
@@ -296,10 +297,10 @@ def URL(
if other.endswith('/'):
other += '/' # add trailing slash to make last trailing empty arg explicit
- if '_signature' in vars:
- vars.pop('_signature')
list_vars = []
for (key, vals) in sorted(vars.items()):
+ if key == '_signature':
+ continue
if not isinstance(vals, (list, tuple)):
vals = [vals]
for val in vals:
@@ -347,7 +348,8 @@ def URL(
if regex_crlf.search(join([application, controller, function, other])):
raise SyntaxError, 'CRLF Injection Detected'
- url = url_out(r, env, application, controller, function,
+
+ url = url_out(r,env, application, controller, function,
args, other, scheme, host, port)
return url
@@ -1743,7 +1745,7 @@ class INPUT(DIV):
elif not t == 'submit':
if value is None:
self['value'] = _value
- else:
+ elif not isinstance(value,list):
self['_value'] = value
def xml(self):
@@ -2109,8 +2111,10 @@ class FORM(DIV):
REDIRECT_JS = "window.location='%s';return false"
def add_button(self,value,url,_class=None):
- self[0][-1][1].append(INPUT(_type="button",_value=value,_class=_class,
- _onclick=self.REDIRECT_JS % url))
+ submit = self.element('input[type=submit]')
+ submit.parent.append(
+ INPUT(_type="button",_value=value,_class=_class,
+ _onclick=self.REDIRECT_JS % url))
@@ -2227,6 +2231,7 @@ class MENU(DIV):
def __init__(self, data, **args):
self.data = data
self.attributes = args
+ self.components = []
if not '_class' in self.attributes:
self['_class'] = 'web2py-menu web2py-menu-vertical'
if not 'ul_class' in self.attributes:
diff --git a/gluon/http.py b/gluon/http.py
index 05330a22..b42ec628 100644
--- a/gluon/http.py
+++ b/gluon/http.py
@@ -89,21 +89,21 @@ class HTTP(BaseException):
status = str(status)
if not regex_status.match(status):
status = '500 %s' % (defined_status[500])
- if not 'Content-Type' in headers:
- headers['Content-Type'] = 'text/html; charset=UTF-8'
+ headers.setdefault('Content-Type','text/html; charset=UTF-8')
body = self.body
if status[:1] == '4':
if not body:
body = status
if isinstance(body, str):
- if len(body)<512 and headers['Content-Type'].startswith('text/html'):
+ if len(body)<512 and \
+ headers['Content-Type'].startswith('text/html'):
body += '' % ('x'*512) ### trick IE
headers['Content-Length'] = len(body)
rheaders = []
for k, v in headers.iteritems():
if isinstance(v, list):
rheaders += [(k, str(item)) for item in v]
- else:
+ elif not v is None:
rheaders.append((k, str(v)))
responder(status, rheaders)
if env.get('request_method','')=='HEAD':
diff --git a/gluon/languages.py b/gluon/languages.py
index 95be43b6..d09e55bb 100644
--- a/gluon/languages.py
+++ b/gluon/languages.py
@@ -29,15 +29,16 @@ from string import maketrans
__all__ = ['translator', 'findT', 'update_all_languages']
-ospath = os.path
ostat = os.stat
-osep = os.sep
+oslistdir = os.listdir
pjoin = os.path.join
+pexists = os.path.exists
pdirname = os.path.dirname
isdir = os.path.isdir
is_gae = settings.global_settings.web2py_runtime_gae
DEFAULT_LANGUAGE = 'en'
+DEFAULT_LANGUAGE_NAME = 'English'
# DEFAULT PLURAL-FORMS RULES:
# language doesn't use plural forms
@@ -45,23 +46,7 @@ DEFAULT_NPLURALS = 1
# only one singular/plural form is used
DEFAULT_GET_PLURAL_ID = lambda n: 0
# word is unchangeable
-DEFAULT_CONSTRUCTOR_PLURAL_FORM = lambda word, plural_id: word
-
-def safe_eval(text):
- if text.strip():
- try:
- import ast
- return ast.literal_eval(text)
- except ImportError:
- return eval(text,{},{})
- return None
-
-# used as default filter in translator.M()
-def markmin_aux(m):
- return '{%s}' % markmin_escape(m.group('s'))
-def markmin(s):
- return render(regex_param.sub(markmin_aux,s),
- sep='br', autolinks=None, id_prefix='')
+DEFAULT_CONSTRUCT_PLURAL_FORM = lambda word, plural_id: word
NUMBERS = (int,long,float)
@@ -75,14 +60,30 @@ regex_translate = re.compile(PY_STRING_LITERAL_RE, re.DOTALL)
regex_param=re.compile(r'{(?P
.+?)}')
# pattern for a valid accept_language
-
regex_language = \
- re.compile('^([a-zA-Z]{2})(\-[a-zA-Z]{2})?(\-[a-zA-Z]+)?$')
-regex_langfile = re.compile('^[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
+ re.compile('([a-z]{2}(?:\-[a-z]{2})?(?:\-[a-z]{2})?)(?:[,;]|$)')
+regex_langfile = re.compile('^[a-z]{2}(-[a-z]{2})?\.py$')
regex_backslash = re.compile(r"\\([\\{}%])")
regex_plural = re.compile('%({.+?})')
regex_plural_dict = re.compile('^{(?P