From 692fe985555f63930c03b929c1bfbe023a54c9a5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 21 Jul 2012 13:29:23 -0500 Subject: [PATCH 01/28] admin design shows private files, thanks Alan --- VERSION | 2 +- applications/admin/controllers/default.py | 17 ++++++- applications/admin/views/default/design.html | 52 ++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 45616fdb..eee4796c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-20 17:37:48) dev +Version 2.00.0 (2012-07-21 13:29:17) dev diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 711f0330..2ae19382 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -854,6 +854,11 @@ def design(): modules = modules=[x.replace('\\','/') for x in modules] modules.sort() + # Get all private files + privates = listdir(apath('%s/private/' % app, r=request), '[^\.#].*') + privates = [x.replace('\\','/') for x in privates] + privates.sort() + # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') statics = [x.replace('\\','/') for x in statics] @@ -908,6 +913,7 @@ def design(): modules=filter_plugins(modules,plugins), extend=extend, include=include, + privates=filter_plugins(privates,plugins), statics=filter_plugins(statics,plugins), languages=languages, plurals=plurals, @@ -924,7 +930,7 @@ def delete_plugin(): redirect(URL('design', args=app, anchor=request.vars.id)) elif 'delete' in request.vars: try: - for folder in ['models','views','controllers','static','modules']: + for folder in ['models','views','controllers','static','modules', 'private']: path=os.path.join(apath(app,r=request),folder) for item in os.listdir(path): if item.rsplit('.',1)[0] == plugin_name: @@ -994,6 +1000,11 @@ def plugin(): modules = modules=[x.replace('\\','/') for x in modules] modules.sort() + # Get all private files + privates = listdir(apath('%s/private/' % app, r=request), '[^\.#].*') + privates = [x.replace('\\','/') for x in privates] + privates.sort() + # Get all static files statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') statics = [x.replace('\\','/') for x in statics] @@ -1023,6 +1034,7 @@ def plugin(): modules=filter_plugins(modules), extend=extend, include=include, + privates=filter_plugins(privates), statics=filter_plugins(statics), languages=languages, crontab=crontab) @@ -1142,10 +1154,11 @@ def create_file(): # coding: utf8 from gluon import *\n""")[1:] - elif path[-8:] == '/static/': + elif (path[-8:] == '/static/') or (path[-9:] == '/private/'): if request.vars.plugin and not filename.startswith('plugin_%s/' % request.vars.plugin): filename = 'plugin_%s/%s' % (request.vars.plugin, filename) text = '' + else: redirect(request.vars.sender+anchor) diff --git a/applications/admin/views/default/design.html b/applications/admin/views/default/design.html index 3664edbf..e5b92ab8 100644 --- a/applications/admin/views/default/design.html +++ b/applications/admin/views/default/design.html @@ -57,6 +57,7 @@ def deletefile(arglist, vars={}): {{=button('#languages', T("languages"))}} {{=button('#static', T("static"))}} {{=button('#modules', T("modules"))}} + {{=button('#private', T("private files"))}} {{=button('#plugins', T("plugins"))}}

@@ -318,6 +319,57 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi {{=file_upload_form('%s/modules/' % app, 'modules')}} + + +

+ {{=T("Private files")}} + {{=helpicon()}} {{=T("These files are not served, they are only available from within your app")}} +

+
+
+
+ {{if not privates:}}

{{=T("There are no private files")}}

{{pass}} + +
{{=file_create_form('%s/private/' % app, 'private')}} + {{=file_upload_form('%s/private/' % app, 'private')}}
+
+ +

From c0c23b8eb78e6a7c0672417e61d3136b1564295b Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 21 Jul 2012 16:12:45 -0500 Subject: [PATCH 02/28] experimental git support (needs a little more work), thanks Andrew Replogle --- VERSION | 2 +- applications/admin/controllers/default.py | 101 +++++++++++++++++- .../admin/views/default/git_pull.html | 9 ++ .../admin/views/default/git_push.html | 6 ++ applications/admin/views/default/site.html | 16 ++- 5 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 applications/admin/views/default/git_pull.html create mode 100644 applications/admin/views/default/git_push.html diff --git a/VERSION b/VERSION index eee4796c..15ca9c12 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-21 13:29:17) dev +Version 2.00.0 (2012-07-21 16:12:40) dev diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 2ae19382..e39e02e8 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -14,15 +14,24 @@ from gluon.fileutils import abspath, read_file, write_file from glob import glob import shutil import platform +try: + from git import * + have_git = True +except ImportError: + have_git = False + GIT_MISSING = 'requires python-git module, but not installed' + from gluon.languages import (regex_language, read_possible_languages, read_possible_plurals, lang_sampling, 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']: + +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']: session.flash = T('disabled in demo mode') redirect(URL('site')) + if not is_manager() and request.function in ['change_password','upgrade_web2py']: session.flash = T('disabled in multi user mode') redirect(URL('site')) @@ -188,6 +197,23 @@ def site(): msg = 'you must specify a name for the uploaded application' response.flash = T(msg) + elif (request.vars.appurl or '').endswith('.git') and request.vars.filename: + if not have_git: + session.flash = GIT_MISSING + elif request.vars.filename: + target = os.path.join(apath(r=request),request.vars.filename) + if os.path.exists(target): + session.flash = 'Application by that name already exists.' + else: + try: + new_repo = Repo.clone_from(request.vars.appurl,target) + session.flash = T('new application "%s" imported',request.vars.filename) + except GitCommandError, err: + session.flash = T('Invalid git repository specified.') + else: + session.flash = 'Application Name required for git import.' + redirect(URL(r=request)) + elif file_or_appurl and request.vars.filename: # fetch an application via URL or file upload f = None @@ -1558,3 +1584,76 @@ def bulk_register(): redirect(URL('site')) return locals() +### Begin experimental stuff need fixes: +# 1) should run in its own process - cannot os.chdir +# 2) should not prompt user at console +# 3) should give option to force commit and not reuqire manual merge + +def git_pull(): + """ Git Pull handler """ + app = get_app() + if not have_git: + session.flash = GIT_MISSING + redirect(URL('site')) + if 'pull' in request.vars: + try: + repo = Repo(os.path.join(apath(r=request),app)) + origin = repo.remotes.origin + origin.fetch() + origin.pull() + session.flash = T("Application updated via git pull") + redirect(URL('site')) + except CheckoutError, message: + logging.error(message) + session.flash = T("Pull failed, certain files could not be checked out. Check logs for details.") + redirect(URL('site')) + except UnmergedEntriesError: + session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") + redirect(URL('site')) + except AssertionError: + session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") + redirect(URL('site')) + except GitCommandError, status: + logging.error(str(status)) + session.flash = T("Pull failed, git exited abnormally. See logs for details.") + redirect(URL('site')) + except Exception,e: + logging.error("Unexpected error:", sys.exc_info()[0]) + session.flash = T("Pull failed, git exited abnormally. See logs for details.") + redirect(URL('site')) + elif 'cancel' in request.vars: + redirect(URL('site')) + return dict(app=app) + + +def git_push(): + """ Git Push handler """ + app = get_app() + if not have_git: + session.flash = GIT_MISSING + redirect(URL('site')) + form = SQLFORM.factory(Field('changelog',requires=IS_NOT_EMPTY())) + form.element('input[type=submit]')['_value']=T('Push') + form.add_button(T('Cancel'),URL('site')) + form.process() + if form.accepted: + try: + repo = Repo(os.path.join(apath(r=request),app)) + index = repo.index + os.chdir(os.path.join(apath(r=request),app)) + index.add('*') + new_commit = index.commit(form.vars.changelog) + origin = repo.remotes.origin + origin.push() + session.flash = T("Git repo updated with latest application changes.") + redirect(URL('site')) + except UnmergedEntriesError: + session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.") + redirect(URL('site')) + except Exception, e: + logging.error("Unexpected error:", sys.exc_info()[0]) + session.flash = T("Push failed, git exited abnormally. See logs for details.") + redirect(URL('site')) + os.chdir(apath(r=request)) + return dict(app=app,form=form) + diff --git a/applications/admin/views/default/git_pull.html b/applications/admin/views/default/git_pull.html new file mode 100644 index 00000000..68623136 --- /dev/null +++ b/applications/admin/views/default/git_pull.html @@ -0,0 +1,9 @@ +{{extend 'layout.html'}} + +
+

{{=T('This will pull changes from the remote repo for application "%s"?', app)}}

+ + + +
{{=FORM(INPUT(_type='submit',_name='cancel',_value=T('Cancel')))}}{{=FORM(INPUT(_type='submit',_name='pull',_value=T('Pull')))}}
+
diff --git a/applications/admin/views/default/git_push.html b/applications/admin/views/default/git_push.html new file mode 100644 index 00000000..03db083f --- /dev/null +++ b/applications/admin/views/default/git_push.html @@ -0,0 +1,6 @@ +{{extend 'layout.html'}} + +

{{=T('This will push changes to the remote repo for application "%s".', app)}}

+
+{{=form}} +
diff --git a/applications/admin/views/default/site.html b/applications/admin/views/default/site.html index 6a54cb5a..0f73cefc 100644 --- a/applications/admin/views/default/site.html +++ b/applications/admin/views/default/site.html @@ -34,6 +34,10 @@ {{=button(URL('remove_compiled_app',args=a), T("Remove compiled"))}} {{pass}} {{pass}} + {{if os.path.exists(os.path.join(apath(r=request),a,'.git')): }} + {{=button(URL('git_pull',args=a), T("Git Pull"))}} + {{=button(URL('git_push',args=a), T("Git Push"))}} + {{pass}} {{if a!=request.application:}} {{=button(URL('uninstall',args=a), T("Uninstall"))}} {{=button_enable(URL('enable',args=a), a)}} @@ -117,16 +121,18 @@ - {{=LABEL(T("Get from URL:"), _for='upload_url')}} + {{=LABEL(T("Get from URL:"), _for='upload_url')}} - +
- - - + + ({{=T('can be a git repo')}}) + + + {{=LABEL(T("Overwrite installed app"), _for='upload_overwrite')}} From 349457057c4b7395f3009cdf2e867cbcdaf43e87 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 23 Jul 2012 16:19:54 -0500 Subject: [PATCH 03/28] solved custom import issue? --- VERSION | 2 +- applications/admin/controllers/openshift.py | 2 +- gluon/winservice.py | 11 +++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index 15ca9c12..f750a6e2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-21 16:12:40) dev +Version 2.00.0 (2012-07-23 16:19:51) dev diff --git a/applications/admin/controllers/openshift.py b/applications/admin/controllers/openshift.py index 52889b5e..88945f74 100644 --- a/applications/admin/controllers/openshift.py +++ b/applications/admin/controllers/openshift.py @@ -34,7 +34,7 @@ def deploy(): assert repo.bare == False for i in form.vars.applications: - appsrc = os.path.join(os.getcwd(),'applications',i) + appsrc = os.path.join(apath(r=request),i) appdest = os.path.join(osrepo,'wsgi',osname,'applications',i) dir_util.copy_tree(appsrc,appdest) #shutil.copytree(appsrc,appdest) diff --git a/gluon/winservice.py b/gluon/winservice.py index bbda61ca..d4355111 100644 --- a/gluon/winservice.py +++ b/gluon/winservice.py @@ -90,7 +90,7 @@ class Web2pyService(Service): cls = _winreg.QueryValue(h, 'PythonClass') finally: _winreg.CloseKey(h) - dir = os.path.dirname(cls) + dir = os.path.dirname(cls) os.chdir(dir) return True except: @@ -149,8 +149,12 @@ class Web2pyService(Service): def web2py_windows_service_handler(argv=None, opt_file='options'): path = os.path.dirname(__file__) - classstring = os.path.normpath(os.path.join(up(path), - 'gluon.winservice.Web2pyService')) + web2py_path = iup(path) + os.chdir(web2py_path) + global_settings.gluon_parent = web2py_path + custom_import_install(web2py_path) + classstring = os.path.normpath( + os.path.join(web2py_path,'gluon.winservice.Web2pyService')) if opt_file: Web2pyService._exe_args_ = opt_file win32serviceutil.HandleCommandLine(Web2pyService, @@ -158,7 +162,6 @@ def web2py_windows_service_handler(argv=None, opt_file='options'): win32serviceutil.HandleCommandLine(Web2pyService, serviceClassString=classstring, argv=argv) - if __name__ == '__main__': web2py_windows_service_handler() From d1de9794cd6ccada107242d22c9e2e7d1556f325 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 23 Jul 2012 17:23:27 -0500 Subject: [PATCH 04/28] another attempt to fix winservice issue --- VERSION | 2 +- gluon/winservice.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index f750a6e2..f964822b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-23 16:19:51) dev +Version 2.00.0 (2012-07-23 17:23:22) dev diff --git a/gluon/winservice.py b/gluon/winservice.py index d4355111..4a818a80 100644 --- a/gluon/winservice.py +++ b/gluon/winservice.py @@ -92,6 +92,8 @@ class Web2pyService(Service): _winreg.CloseKey(h) dir = os.path.dirname(cls) os.chdir(dir) + global_settings.gluon_parent = dir + custom_import_install(dir) return True except: self.log("Can't change to web2py working path; server is stopped") @@ -151,8 +153,6 @@ def web2py_windows_service_handler(argv=None, opt_file='options'): path = os.path.dirname(__file__) web2py_path = iup(path) os.chdir(web2py_path) - global_settings.gluon_parent = web2py_path - custom_import_install(web2py_path) classstring = os.path.normpath( os.path.join(web2py_path,'gluon.winservice.Web2pyService')) if opt_file: From 3b42bc3e602c3783c7245bf74caa2f5bc1db0e72 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 24 Jul 2012 06:53:08 -0500 Subject: [PATCH 05/28] backward compatiblity fix for auth sinature --- VERSION | 2 +- gluon/tools.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index f964822b..07f9e596 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-23 17:23:22) dev +Version 2.00.0 (2012-07-24 06:53:03) dev diff --git a/gluon/tools.py b/gluon/tools.py index 24467443..923194b1 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -881,7 +881,8 @@ class Auth(object): return URL(args=current.request.args,vars=current.request.vars) def __init__(self, environment=None, db=None, mailer=True, - hmac_key=None, controller='default', function='user', cas_provider=None): + hmac_key=None, controller='default', function='user', + cas_provider=None, signature=True): """ auth=Auth(db) @@ -1133,7 +1134,10 @@ class Auth(object): # when user wants to be logged in for longer response.cookies[response.session_id_name]["expires"] = \ auth.expiration - + if signature: + self.define_signature() + else: + self.signature = None def _get_user_id(self): "accessor for auth.user_id" @@ -1332,7 +1336,8 @@ class Auth(object): db = self.db settings = self.settings - self.define_signature() + if not self.signature: + self.define_signature() if signature==True: signature_list = [self.signature] elif not signature: From d295a479c6ffa700886f4dac0146a8659d9ffdfc Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 24 Jul 2012 21:11:35 -0500 Subject: [PATCH 06/28] crud.select(fields=[..]), fields can now be string, issue 901 --- VERSION | 2 +- gluon/sqlhtml.py | 3 ++- gluon/tools.py | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 07f9e596..d490530f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-24 06:53:03) dev +Version 2.00.0 (2012-07-24 21:11:30) dev diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 0c7cae23..bef67d18 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1241,7 +1241,8 @@ class SQLFORM(FORM): ### do not know why this happens, it should not (source_file, original_filename) = \ (cStringIO.StringIO(f), 'file.txt') - newfilename = field.store(source_file, original_filename, field.uploadfolder) + newfilename = field.store(source_file, original_filename, + field.uploadfolder) # this line is for backward compatibility only self.vars['%s_newfilename' % fieldname] = newfilename fields[fieldname] = newfilename diff --git a/gluon/tools.py b/gluon/tools.py index 923194b1..48943cb4 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -3397,6 +3397,8 @@ class Crud(object): query = table.id > 0 if not fields: fields = [field for field in table if field.readable] + else: + fields = [table[f] if isinstance(f,str) else f for f in fields] rows = self.db(query).select(*fields,**dict(orderby=orderby, limitby=limitby)) return rows From d0078857aec0f3cd0a79e3d82d89a9ae5d02b5b0 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 24 Jul 2012 21:19:58 -0500 Subject: [PATCH 07/28] fixed list hideerror, thanks Howesc and Niphold, issue 886 --- VERSION | 2 +- gluon/sqlhtml.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index d490530f..adcf56dd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-24 21:11:30) dev +Version 2.00.0 (2012-07-24 21:19:55) dev diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index bef67d18..84dd7519 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1173,8 +1173,7 @@ class SQLFORM(FORM): row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX) widget = field.widget(field, value) self.field_parent[row_id].components = [ widget ] - if not field.type.startswith('list:'): - self.field_parent[row_id]._traverse(False, hideerror) + self.field_parent[row_id]._traverse(False, hideerror) self.custom.widget[ fieldname ] = widget self.accepted = ret return ret From fd87b0922137c6ec65e539721f194481452e0d96 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 24 Jul 2012 21:23:48 -0500 Subject: [PATCH 08/28] correction to issue 886, thanks Niphlod --- VERSION | 2 +- gluon/sqlhtml.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index adcf56dd..5850ee1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-24 21:19:55) dev +Version 2.00.0 (2012-07-24 21:23:46) dev diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 84dd7519..f77e35e5 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -410,8 +410,11 @@ class CheckboxesWidget(OptionsWidget): LABEL(v,_for='%s%s' % (field.name,k)))) opts.append(child(tds)) + if opts: - opts[-1][0][0]['hideerror'] = False + opts.append(INPUT(_class="hidden", requires=attr.get('requires', None), + _disabled="disabled", _name=field.name, + hideerror=False)) return parent(*opts, **attr) From 709c387901ad87d1689ceb2511c12ef34fd67e9c Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 24 Jul 2012 22:06:09 -0500 Subject: [PATCH 09/28] possible solution to issue 887 --- VERSION | 2 +- gluon/dal.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5850ee1f..796e78e8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-24 21:23:46) dev +Version 2.00.0 (2012-07-24 22:06:05) dev diff --git a/gluon/dal.py b/gluon/dal.py index 42d357e1..7aadc901 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -3785,6 +3785,9 @@ class GoogleSQLAdapter(UseDatabaseStoredFile,MySQLAdapter): self.execute("SET FOREIGN_KEY_CHECKS=1;") self.execute("SET sql_mode='NO_BACKSLASH_ESCAPES';") + def execute(self,a): + return self.log_execute(a.decode('utf8')) + class NoSQLAdapter(BaseAdapter): can_select_for_update = False From 5b31ede4b9ffbded293fae319617a793fe771db6 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 16:21:34 -0500 Subject: [PATCH 10/28] BR()*5 --- VERSION | 2 +- gluon/html.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 796e78e8..0d781764 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-24 22:06:05) dev +Version 2.00.0 (2012-07-25 16:21:28) dev diff --git a/gluon/html.py b/gluon/html.py index aed30ae2..40cf5a17 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -472,7 +472,8 @@ class XmlComponent(object): def xml(self): raise NotImplementedError - + def __mul__(self,n): + return CAT(*[self for i in range(n)]) class XML(XmlComponent): """ From 46dcb379a8b129adf34c00771175ce373762df58 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 16:53:21 -0500 Subject: [PATCH 11/28] lazy_lazy_cache->lazy_cache, thanks Anthony --- VERSION | 2 +- gluon/cache.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 0d781764..d0e265df 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 16:21:28) dev +Version 2.00.0 (2012-07-25 16:53:17) dev diff --git a/gluon/cache.py b/gluon/cache.py index 14006087..18b51714 100644 --- a/gluon/cache.py +++ b/gluon/cache.py @@ -35,7 +35,7 @@ except ImportError: logger = logging.getLogger("web2py.cache") -__all__ = ['Cache', 'lazy_lazy_cache'] +__all__ = ['Cache', 'lazy_cache'] DEFAULT_TIME_EXPIRE = 300 @@ -478,7 +478,7 @@ class Cache(object): return CacheAction(func,key,time_expire,self,cache_model) return tmp -def lazy_lazy_cache(key=None,time_expire=None,cache_model='ram'): +def lazy_cache(key=None,time_expire=None,cache_model='ram'): def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model): key = key or repr(f) def g(*c,**d): From b32f3aae162d8650ddb1850b6f389173146c0c45 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 16:57:17 -0500 Subject: [PATCH 12/28] lazy_cache docstring --- VERSION | 2 +- gluon/cache.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d0e265df..93cedd85 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 16:53:17) dev +Version 2.00.0 (2012-07-25 16:57:13) dev diff --git a/gluon/cache.py b/gluon/cache.py index 18b51714..437e140e 100644 --- a/gluon/cache.py +++ b/gluon/cache.py @@ -479,6 +479,13 @@ class Cache(object): return tmp def lazy_cache(key=None,time_expire=None,cache_model='ram'): + """ + can be used to cache any function including in modules, + as long as the cached function is only called within a web2py request + if a key is not provided, one is generated from the function name + the time_expire defaults to None (no cache expiration) + if cache_model is "ram" then the model is current.cache.ram, etc. + """ def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model): key = key or repr(f) def g(*c,**d): From 46829e95ad164dfa77ecef2657a6b976bca2e337 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 18:34:53 -0500 Subject: [PATCH 13/28] fixed winservice, thanks Marin Prajic --- VERSION | 2 +- applications/welcome/languages/it.py | 75 +++---- applications/welcome/languages/ro.py | 286 ++++++++++++++------------- gluon/winservice.py | 4 +- 4 files changed, 187 insertions(+), 180 deletions(-) diff --git a/VERSION b/VERSION index 93cedd85..27e8cae4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 16:57:13) dev +Version 2.00.0 (2012-07-25 18:34:46) dev diff --git a/applications/welcome/languages/it.py b/applications/welcome/languages/it.py index 8039c9db..afa3109a 100644 --- a/applications/welcome/languages/it.py +++ b/applications/welcome/languages/it.py @@ -3,14 +3,18 @@ '!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', +'%d seconds ago': '%d seconds ago', '%s %%{row} deleted': '%s righe ("record") cancellate', '%s %%{row} updated': '%s righe ("record") modificate', '%s selected': '%s selezionato', +'%Y-%m-%d': '%d/%m/%Y', +'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S', 'Administrative interface': 'Interfaccia amministrativa', +'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', 'Available databases and tables': 'Database e tabelle disponibili', +'cache': 'cache', 'Cannot be empty': 'Non può essere vuoto', +'change password': 'Cambia password', 'Check to delete': 'Seleziona per cancellare', 'Client IP': 'Client IP', 'Controller': 'Controller', @@ -18,50 +22,79 @@ 'Current request': 'Richiesta (request) corrente', 'Current response': 'Risposta (response) corrente', 'Current session': 'Sessione (session) corrente', -'DB Model': 'Modello di DB', +'customize me!': 'Personalizzami!', +'data uploaded': 'dati caricati', 'Database': 'Database', +'database': 'database', +'database %s select': 'database %s select', +'db': 'db', +'DB Model': 'Modello di DB', 'Delete': 'Delete', 'Delete:': 'Cancella:', 'Description': 'Descrizione', +'design': 'progetta', 'Documentation': 'Documentazione', +'done!': 'fatto!', 'E-mail': 'E-mail', 'Edit': 'Modifica', -'Edit This App': 'Modifica questa applicazione', 'Edit current record': 'Modifica record corrente', +'edit profile': 'modifica profilo', +'Edit This App': 'Modifica questa applicazione', +'export as csv file': 'esporta come file CSV', 'First name': 'Nome', 'Group ID': 'ID Gruppo', +'hello': 'hello', +'hello world': 'salve mondo', 'Hello World': 'Salve Mondo', 'Hello World in a flash!': 'Salve Mondo in un flash!', 'Import/Export': 'Importa/Esporta', 'Index': 'Indice', +'insert new': 'inserisci nuovo', +'insert new %s': 'inserisci nuovo %s', 'Internal State': 'Stato interno', -'Invalid Query': 'Richiesta (query) non valida', 'Invalid email': 'Email non valida', +'Invalid Query': 'Richiesta (query) non valida', +'invalid request': 'richiesta non valida', 'Last name': 'Cognome', 'Layout': 'Layout', +'login': 'accesso', +'logout': 'uscita', +'lost password?': 'dimenticato la password?', 'Main Menu': 'Menu principale', 'Menu Model': 'Menu Modelli', 'Name': 'Nome', 'New Record': 'Nuovo elemento (record)', +'new record inserted': 'nuovo record inserito', +'next 100 rows': 'prossime 100 righe', 'No databases in this application': 'Nessun database presente in questa applicazione', +'not authorized': 'non autorizzato', 'Online examples': 'Vedere gli esempi', +'or import from csv file': 'oppure importa da file CSV', 'Origin': 'Origine', 'Password': 'Password', 'Powered by': 'Powered by', +'previous 100 rows': '100 righe precedenti', 'Query:': 'Richiesta (query):', +'record': 'record', +'record does not exist': 'il record non esiste', +'record id': 'record id', 'Record ID': 'Record ID', +'register': 'registrazione', 'Registration key': 'Chiave di Registazione', 'Reset Password key': 'Resetta chiave Password ', 'Role': 'Ruolo', 'Rows in table': 'Righe nella tabella', 'Rows selected': 'Righe selezionate', +'state': 'stato', 'Stylesheet': 'Foglio di stile (stylesheet)', 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', +'table': 'tabella', 'Table name': 'Nome tabella', '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 output of the file is a dictionary that was rendered by the view %s': 'L\'output del file è un "dictionary" che è stato visualizzato dalla vista %s', 'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", 'Timestamp': 'Ora (timestamp)', +'unable to parse csv file': 'non riesco a decodificare questo file CSV', 'Update': 'Update', 'Update:': 'Aggiorna:', '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).', @@ -73,36 +106,4 @@ 'You are successfully running web2py': 'Stai eseguendo web2py con successo', 'You can modify this application and adapt it to your needs': 'Puoi modificare questa applicazione adattandola alle tue necessità', 'You visited the url %s': "Hai visitato l'URL %s", -'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', -'cache': 'cache', -'change password': 'Cambia password', -'customize me!': 'Personalizzami!', -'data uploaded': 'dati caricati', -'database': 'database', -'database %s select': 'database %s select', -'db': 'db', -'design': 'progetta', -'done!': 'fatto!', -'edit profile': 'modifica profilo', -'export as csv file': 'esporta come file CSV', -'hello': 'hello', -'hello world': 'salve mondo', -'insert new': 'inserisci nuovo', -'insert new %s': 'inserisci nuovo %s', -'invalid request': 'richiesta non valida', -'login': 'accesso', -'logout': 'uscita', -'lost password?': 'dimenticato la password?', -'new record inserted': 'nuovo record inserito', -'next 100 rows': 'prossime 100 righe', -'not authorized': 'non autorizzato', -'or import from csv file': 'oppure importa da file CSV', -'previous 100 rows': '100 righe precedenti', -'record': 'record', -'record does not exist': 'il record non esiste', -'record id': 'record id', -'register': 'registrazione', -'state': 'stato', -'table': 'tabella', -'unable to parse csv file': 'non riesco a decodificare questo file CSV', } diff --git a/applications/welcome/languages/ro.py b/applications/welcome/languages/ro.py index b48c1a5c..e2f0a31a 100644 --- a/applications/welcome/languages/ro.py +++ b/applications/welcome/languages/ro.py @@ -5,12 +5,16 @@ '!langname!': 'Română', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" (actualizează) este o expresie opțională precum "câmp1=\'valoare_nouă\'". Nu puteți actualiza sau șterge rezultatele unui JOIN', '%(nrows)s records found': '%(nrows)s înregistrări găsite', -'%Y-%m-%d': '%Y-%m-%d', -'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', +'%d days ago': '%d days ago', +'%d weeks ago': '%d weeks ago', '%s %%{row} deleted': '%s linii șterse', '%s %%{row} updated': '%s linii actualizate', '%s selected': '%s selectat(e)', +'%Y-%m-%d': '%Y-%m-%d', +'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', '(something like "it-it")': '(ceva ce seamănă cu "it-it")', +'1 day ago': '1 day ago', +'1 week ago': '1 week ago', '<': '<', '<=': '<=', '=': '=', @@ -18,13 +22,15 @@ '>=': '>=', 'A new version of web2py is available': 'O nouă versiune de web2py este disponibilă', 'A new version of web2py is available: %s': 'O nouă versiune de web2py este disponibilă: %s', -'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENȚIE: Nu vă puteți conecta decât utilizând o conexiune securizată (HTTPS) sau rulând aplicația pe computerul local.', -'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENȚIE: Nu puteți efectua mai multe teste o dată deoarece lansarea în execuție a mai multor subpocese nu este sigură.', -'ATTENTION: you cannot edit the running application!': 'ATENȚIE: nu puteți edita o aplicație în curs de execuție!', 'About': 'Despre', +'about': 'despre', 'About application': 'Despre aplicație', 'Access Control': 'Control acces', 'Add': 'Adaugă', +'additional code for your application': 'cod suplimentar pentru aplicația dvs.', +'admin disabled because no admin password': 'administrare dezactivată deoarece parola de administrator nu a fost furnizată', +'admin disabled because not supported on google app engine': 'administrare dezactivată deoarece funcționalitatea nu e suportat pe Google App Engine', +'admin disabled because unable to access password file': 'administrare dezactivată deoarece nu există acces la fișierul cu parole', 'Admin is disabled because insecure channel': 'Adminstrarea este dezactivată deoarece conexiunea nu este sigură', 'Admin is disabled because unsecure channel': 'Administrarea este dezactivată deoarece conexiunea nu este securizată', 'Administration': 'Administrare', @@ -32,62 +38,118 @@ 'Administrator Password:': 'Parolă administrator:', 'Ajax Recipes': 'Rețete Ajax', 'And': 'Și', +'and rename it (required):': 'și renumiți (obligatoriu):', +'and rename it:': ' și renumiți:', +'appadmin': 'appadmin', +'appadmin is disabled because insecure channel': 'appadmin dezactivat deoarece conexiunea nu e sigură', +'application "%s" uninstalled': 'aplicația "%s" a fost dezinstalată', +'application compiled': 'aplicația a fost compilată', +'application is compiled and cannot be designed': 'aplicația este compilată și nu poate fi editată', 'Are you sure you want to delete file "%s"?': 'Sigur ștergeți fișierul "%s"?', 'Are you sure you want to delete this object?': 'Sigur ștergeți acest obiect?', 'Are you sure you want to uninstall application "%s"': 'Sigur dezinstalați aplicația "%s"', 'Are you sure you want to uninstall application "%s"?': 'Sigur dezinstalați aplicația "%s"?', +'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENȚIE: Nu vă puteți conecta decât utilizând o conexiune securizată (HTTPS) sau rulând aplicația pe computerul local.', +'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENȚIE: Nu puteți efectua mai multe teste o dată deoarece lansarea în execuție a mai multor subpocese nu este sigură.', +'ATTENTION: you cannot edit the running application!': 'ATENȚIE: nu puteți edita o aplicație în curs de execuție!', 'Authentication': 'Autentificare', 'Available databases and tables': 'Baze de date și tabele disponibile', 'Back': 'Înapoi', 'Buy this book': 'Cumpără această carte', +'cache': 'cache', 'Cache Keys': 'Chei cache', +'cache, errors and sessions cleaned': 'cache, erori și sesiuni golite', 'Cannot be empty': 'Nu poate fi vid', 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'Compilare imposibilă: aplicația conține erori. Debogați aplicația și încercați din nou.', +'cannot create file': 'fișier imposibil de creat', +'cannot upload file "%(filename)s"': 'imposibil de încărcat fișierul "%(filename)s"', 'Change Password': 'Schimbare parolă', 'Change password': 'Schimbare parolă', +'change password': 'schimbare parolă', +'check all': 'coșați tot', 'Check to delete': 'Coșați pentru a șterge', +'clean': 'golire', 'Clear': 'Golește', +'click to check for upgrades': 'Clic pentru a verifica dacă există upgrade-uri', 'Client IP': 'IP client', 'Community': 'Comunitate', +'compile': 'compilare', +'compiled application removed': 'aplicația compilată a fost ștearsă', 'Components and Plugins': 'Componente și plugin-uri', +'contains': 'conține', 'Controller': 'Controlor', 'Controllers': 'Controlori', +'controllers': 'controlori', 'Copyright': 'Drepturi de autor', +'create file with filename:': 'crează fișier cu numele:', 'Create new application': 'Creați aplicație nouă', +'create new application:': 'crează aplicație nouă:', +'crontab': 'crontab', 'Current request': 'Cerere curentă', 'Current response': 'Răspuns curent', 'Current session': 'Sesiune curentă', -'DB Model': 'Model bază de date', -'DESIGN': 'DESIGN', +'currently saved or': 'în prezent salvat sau', +'customize me!': 'Personalizează-mă!', +'data uploaded': 'date încărcate', 'Database': 'Baza de date', +'database': 'bază de date', +'database %s select': 'selectare bază de date %s', +'database administration': 'administrare bază de date', 'Date and Time': 'Data și ora', +'db': 'db', +'DB Model': 'Model bază de date', +'defines tables': 'definire tabele', 'Delete': 'Șterge', +'delete': 'șterge', +'delete all checked': 'șterge tot ce e coșat', 'Delete:': 'Șterge:', 'Demo': 'Demo', 'Deploy on Google App Engine': 'Instalare pe Google App Engine', 'Deployment Recipes': 'Rețete de instalare', 'Description': 'Descriere', +'design': 'design', +'DESIGN': 'DESIGN', 'Design for': 'Design pentru', 'Disk Cache Keys': 'Chei cache de disc', 'Documentation': 'Documentație', "Don't know what to do?": 'Nu știți ce să faceți?', +'done!': 'gata!', 'Download': 'Descărcare', 'E-mail': 'E-mail', 'E-mail invalid': 'E-mail invalid', +'edit': 'editare', 'EDIT': 'EDITARE', 'Edit': 'Editare', -'Edit Profile': 'Editare profil', -'Edit This App': 'Editați această aplicație', 'Edit application': 'Editare aplicație', +'edit controller': 'editare controlor', 'Edit current record': 'Editare înregistrare curentă', +'Edit Profile': 'Editare profil', +'edit profile': 'editare profil', +'Edit This App': 'Editați această aplicație', 'Editing file': 'Editare fișier', 'Editing file "%s"': 'Editare fișier "%s"', 'Email and SMS': 'E-mail și SMS', +'enter a number between %(min)g and %(max)g': 'introduceți un număr între %(min)g și %(max)g', +'enter an integer between %(min)g and %(max)g': 'introduceți un întreg între %(min)g și %(max)g', 'Error logs for "%(app)s"': 'Log erori pentru "%(app)s"', +'errors': 'erori', 'Errors': 'Erori', 'Export': 'Export', -'FAQ': 'Întrebări frecvente', +'export as csv file': 'exportă ca fișier csv', +'exposes': 'expune', +'extends': 'extinde', +'failed to reload module': 'reîncarcare modul nereușită', 'False': 'Neadevărat', +'FAQ': 'Întrebări frecvente', +'file "%(filename)s" created': 'fișier "%(filename)s" creat', +'file "%(filename)s" deleted': 'fișier "%(filename)s" șters', +'file "%(filename)s" uploaded': 'fișier "%(filename)s" încărcat', +'file "%(filename)s" was not deleted': 'fișierul "%(filename)s" n-a fost șters', +'file "%s" of %s restored': 'fișier "%s" de %s restaurat', +'file changed on disk': 'fișier modificat pe disc', +'file does not exist': 'fișier inexistent', +'file saved on %(time)s': 'fișier salvat %(time)s', +'file saved on %s': 'fișier salvat pe %s', 'First name': 'Prenume', 'Forbidden': 'Interzis', 'Forms and Validators': 'Formulare și validatori', @@ -98,19 +160,31 @@ 'Group uniquely assigned to user %(id)s': 'Grup asociat în mod unic utilizatorului %(id)s', 'Groups': 'Grupuri', 'Hello World': 'Salutare lume', +'help': 'ajutor', 'Home': 'Acasă', 'How did you get here?': 'Cum ați ajuns aici?', +'htmledit': 'editare html', 'Import/Export': 'Import/Export', +'includes': 'include', 'Index': 'Index', +'insert new': 'adaugă nou', +'insert new %s': 'adaugă nou %s', 'Installed applications': 'Aplicații instalate', +'internal error': 'eroare internă', 'Internal State': 'Stare internă', 'Introduction': 'Introducere', -'Invalid Query': 'Interogare invalidă', 'Invalid action': 'Acțiune invalidă', 'Invalid email': 'E-mail invalid', +'invalid password': 'parolă invalidă', 'Invalid password': 'Parolă invalidă', +'Invalid Query': 'Interogare invalidă', +'invalid request': 'cerere invalidă', +'invalid ticket': 'tichet invalid', +'language file "%(filename)s" created/updated': 'fișier de limbă "%(filename)s" creat/actualizat', 'Language files (static strings) updated': 'Fișierele de limbă (șirurile statice de caractere) actualizate', +'languages': 'limbi', 'Languages': 'Limbi', +'languages updated': 'limbi actualizate', 'Last name': 'Nume', 'Last saved on:': 'Ultima salvare:', 'Layout': 'Șablon', @@ -118,39 +192,54 @@ 'Layouts': 'Șabloane', 'License for': 'Licență pentru', 'Live Chat': 'Chat live', +'loading...': 'încarc...', 'Logged in': 'Logat', 'Logged out': 'Delogat', 'Login': 'Autentificare', +'login': 'autentificare', 'Login to the Administrative Interface': 'Logare interfață de administrare', +'logout': 'ieșire', 'Logout': 'Ieșire', 'Lost Password': 'Parolă pierdută', 'Lost password?': 'Parolă pierdută?', 'Main Menu': 'Meniu principal', 'Menu Model': 'Model meniu', +'merge': 'unește', 'Models': 'Modele', +'models': 'modele', 'Modules': 'Module', +'modules': 'module', 'My Sites': 'Site-urile mele', -'NO': 'NU', 'Name': 'Nume', 'New': 'Nou', -'New Record': 'Înregistrare nouă', +'new application "%s" created': 'aplicația nouă "%s" a fost creată', 'New password': 'Parola nouă', +'New Record': 'Înregistrare nouă', +'new record inserted': 'înregistrare nouă adăugată', +'next 100 rows': 'următoarele 100 de linii', +'NO': 'NU', 'No databases in this application': 'Aplicație fără bază de date', 'Object or table name': 'Obiect sau nume de tabel', 'Old password': 'Parola veche', 'Online examples': 'Exemple online', 'Or': 'Sau', +'or import from csv file': 'sau importă din fișier csv', +'or provide application url:': 'sau furnizează adresă url:', 'Origin': 'Origine', 'Original/Translation': 'Original/Traducere', 'Other Plugins': 'Alte plugin-uri', 'Other Recipes': 'Alte rețete', 'Overview': 'Prezentare de ansamblu', +'pack all': 'împachetează toate', +'pack compiled': 'pachet compilat', 'Password': 'Parola', "Password fields don't match": 'Câmpurile de parolă nu se potrivesc', 'Peeking at file': 'Vizualizare fișier', +'please input your password again': 'introduceți parola din nou', 'Plugins': 'Plugin-uri', 'Powered by': 'Pus în mișcare de', 'Preface': 'Prefață', +'previous 100 rows': '100 de linii anterioare', 'Profile': 'Profil', 'Python': 'Python', 'Query': 'Interogare', @@ -158,52 +247,88 @@ 'Quick Examples': 'Exemple rapide', 'RAM Cache Keys': 'Chei cache RAM', 'Recipes': 'Rețete', +'record': 'înregistrare', +'record does not exist': 'înregistrare inexistentă', +'record id': 'id înregistrare', 'Record ID': 'ID înregistrare', +'register': 'înregistrare', 'Register': 'Înregistrare', 'Registration identifier': 'Identificator de autentificare', 'Registration key': 'Cheie înregistrare', 'Registration successful': 'Autentificare reușită', 'Remember me (for 30 days)': 'Ține-mă minte (timp de 30 de zile)', +'remove compiled': 'șterge compilate', 'Request reset password': 'Cerere resetare parolă', 'Reset Password key': 'Cheie restare parolă', 'Resolve Conflict file': 'Fișier rezolvare conflict', +'restore': 'restaurare', +'revert': 'revenire', 'Role': 'Rol', 'Rows in table': 'Linii în tabel', 'Rows selected': 'Linii selectate', +'save': 'salvare', 'Save profile': 'Salvează profil', 'Saved file hash:': 'Hash fișier salvat:', 'Search': 'Căutare', 'Semantic': 'Semantică', 'Services': 'Servicii', +'session expired': 'sesiune expirată', +'shell': 'line de commandă', +'site': 'site', +'some files could not be removed': 'anumite fișiere n-au putut fi șterse', +'starts with': 'începe cu', +'state': 'stare', +'static': 'static', 'Static files': 'Fișiere statice', 'Stylesheet': 'Foaie de stiluri', 'Submit': 'Înregistrează', 'Support': 'Suport', 'Sure you want to delete this object?': 'Sigur ștergeți acest obiect?', +'table': 'tabel', 'Table name': 'Nume tabel', +'test': 'test', 'Testing application': 'Testare aplicație', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Interogarea (query)" este o condiție de tipul "db.tabel1.câmp1==\'valoare\'". Ceva de genul "db.tabel1.câmp1==db.tabel2.câmp2" generează un JOIN SQL.', +'the application logic, each URL path is mapped in one exposed function in the controller': 'logica aplicației, fiecare rută URL este mapată într-o funcție expusă de controlor', 'The Core': 'Nucleul', -'The Views': 'Vederile', +'the data representation, define database tables and sets': 'reprezentarea datelor, definește tabelele bazei de date și seturile (de date)', 'The output of the file is a dictionary that was rendered by the view %s': 'Fișierul produce un dicționar care a fost prelucrat de vederea %s', +'the presentations layer, views are also known as templates': 'nivelul de prezentare, vederile sunt de asemenea numite și șabloane', +'The Views': 'Vederile', 'There are no controllers': 'Nu există controlori', 'There are no models': 'Nu există modele', 'There are no modules': 'Nu există module', 'There are no static files': 'Nu există fișiere statice', 'There are no translators, only default language is supported': 'Nu există traduceri, doar limba implicită este suportată', 'There are no views': 'Nu există vederi', +'these files are served without processing, your images go here': 'aceste fișiere sunt servite fără procesare, imaginea se plasează acolo', 'This App': 'Această aplicație', 'This is a copy of the scaffolding application': 'Aceasta este o copie a aplicației schelet', 'This is the %(filename)s template': 'Aceasta este șablonul fișierului %(filename)s', 'Ticket': 'Tichet', 'Timestamp': 'Moment în timp (timestamp)', +'to previous version.': 'la versiunea anterioară.', +'too short': 'prea scurt', +'translation strings for the application': 'șiruri de caractere folosite la traducerea aplicației', 'True': 'Adevărat', +'try': 'încearcă', +'try something like': 'încearcă ceva de genul', 'Twitter': 'Twitter', 'Unable to check for upgrades': 'Imposibil de verificat dacă există actualizări', +'unable to create application "%s"': 'imposibil de creat aplicația "%s"', +'unable to delete file "%(filename)s"': 'imposibil de șters fișierul "%(filename)s"', 'Unable to download': 'Imposibil de descărcat', 'Unable to download app': 'Imposibil de descărcat aplicația', +'unable to parse csv file': 'imposibil de analizat fișierul csv', +'unable to uninstall "%s"': 'imposibil de dezinstalat "%s"', +'uncheck all': 'decoșează tot', +'uninstall': 'dezinstalează', +'update': 'actualizează', +'update all languages': 'actualizează toate limbile', 'Update:': 'Actualizare:', +'upload application:': 'incarcă aplicația:', 'Upload existing application': 'Încarcă aplicația existentă', +'upload file:': 'încarcă fișier:', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Folosiți (...)&(...) pentru AND, (...)|(...) pentru OR, și ~(...) pentru NOT, pentru a crea interogări complexe.', 'User %(id)s Logged-in': 'Utilizator %(id)s autentificat', 'User %(id)s Logged-out': 'Utilizator %(id)s delogat', @@ -212,10 +337,16 @@ 'User %(id)s Profile updated': 'Profil utilizator %(id)s actualizat', 'User %(id)s Registered': 'Utilizator %(id)s înregistrat', 'User ID': 'ID utilizator', +'value already in database or empty': 'Valoare existentă în baza de date sau vidă', 'Verify Password': 'Verifică parola', +'versioning': 'versiuni', 'Videos': 'Video-uri', 'View': 'Vedere', +'view': 'vedere', 'Views': 'Vederi', +'views': 'vederi', +'web2py is up to date': 'web2py este la zi', +'web2py Recent Tweets': 'Ultimele tweet-uri web2py', 'Welcome': 'Bine ați venit', 'Welcome %s': 'Bine ați venit %s', 'Welcome to web2py': 'Bun venit la web2py', @@ -225,131 +356,4 @@ 'You are successfully running web2py': 'Rulați cu succes web2py', 'You can modify this application and adapt it to your needs': 'Puteți modifica și adapta aplicația nevoilor dvs.', 'You visited the url %s': 'Ați vizitat adresa %s', -'about': 'despre', -'additional code for your application': 'cod suplimentar pentru aplicația dvs.', -'admin disabled because no admin password': 'administrare dezactivată deoarece parola de administrator nu a fost furnizată', -'admin disabled because not supported on google app engine': 'administrare dezactivată deoarece funcționalitatea nu e suportat pe Google App Engine', -'admin disabled because unable to access password file': 'administrare dezactivată deoarece nu există acces la fișierul cu parole', -'and rename it (required):': 'și renumiți (obligatoriu):', -'and rename it:': ' și renumiți:', -'appadmin': 'appadmin', -'appadmin is disabled because insecure channel': 'appadmin dezactivat deoarece conexiunea nu e sigură', -'application "%s" uninstalled': 'aplicația "%s" a fost dezinstalată', -'application compiled': 'aplicația a fost compilată', -'application is compiled and cannot be designed': 'aplicația este compilată și nu poate fi editată', -'cache': 'cache', -'cache, errors and sessions cleaned': 'cache, erori și sesiuni golite', -'cannot create file': 'fișier imposibil de creat', -'cannot upload file "%(filename)s"': 'imposibil de încărcat fișierul "%(filename)s"', -'change password': 'schimbare parolă', -'check all': 'coșați tot', -'clean': 'golire', -'click to check for upgrades': 'Clic pentru a verifica dacă există upgrade-uri', -'compile': 'compilare', -'compiled application removed': 'aplicația compilată a fost ștearsă', -'contains': 'conține', -'controllers': 'controlori', -'create file with filename:': 'crează fișier cu numele:', -'create new application:': 'crează aplicație nouă:', -'crontab': 'crontab', -'currently saved or': 'în prezent salvat sau', -'customize me!': 'Personalizează-mă!', -'data uploaded': 'date încărcate', -'database': 'bază de date', -'database %s select': 'selectare bază de date %s', -'database administration': 'administrare bază de date', -'db': 'db', -'defines tables': 'definire tabele', -'delete': 'șterge', -'delete all checked': 'șterge tot ce e coșat', -'design': 'design', -'done!': 'gata!', -'edit': 'editare', -'edit controller': 'editare controlor', -'edit profile': 'editare profil', -'enter a number between %(min)g and %(max)g': 'introduceți un număr între %(min)g și %(max)g', -'enter an integer between %(min)g and %(max)g': 'introduceți un întreg între %(min)g și %(max)g', -'errors': 'erori', -'export as csv file': 'exportă ca fișier csv', -'exposes': 'expune', -'extends': 'extinde', -'failed to reload module': 'reîncarcare modul nereușită', -'file "%(filename)s" created': 'fișier "%(filename)s" creat', -'file "%(filename)s" deleted': 'fișier "%(filename)s" șters', -'file "%(filename)s" uploaded': 'fișier "%(filename)s" încărcat', -'file "%(filename)s" was not deleted': 'fișierul "%(filename)s" n-a fost șters', -'file "%s" of %s restored': 'fișier "%s" de %s restaurat', -'file changed on disk': 'fișier modificat pe disc', -'file does not exist': 'fișier inexistent', -'file saved on %(time)s': 'fișier salvat %(time)s', -'file saved on %s': 'fișier salvat pe %s', -'help': 'ajutor', -'htmledit': 'editare html', -'includes': 'include', -'insert new': 'adaugă nou', -'insert new %s': 'adaugă nou %s', -'internal error': 'eroare internă', -'invalid password': 'parolă invalidă', -'invalid request': 'cerere invalidă', -'invalid ticket': 'tichet invalid', -'language file "%(filename)s" created/updated': 'fișier de limbă "%(filename)s" creat/actualizat', -'languages': 'limbi', -'languages updated': 'limbi actualizate', -'loading...': 'încarc...', -'login': 'autentificare', -'logout': 'ieșire', -'merge': 'unește', -'models': 'modele', -'modules': 'module', -'new application "%s" created': 'aplicația nouă "%s" a fost creată', -'new record inserted': 'înregistrare nouă adăugată', -'next 100 rows': 'următoarele 100 de linii', -'or import from csv file': 'sau importă din fișier csv', -'or provide application url:': 'sau furnizează adresă url:', -'pack all': 'împachetează toate', -'pack compiled': 'pachet compilat', -'please input your password again': 'introduceți parola din nou', -'previous 100 rows': '100 de linii anterioare', -'record': 'înregistrare', -'record does not exist': 'înregistrare inexistentă', -'record id': 'id înregistrare', -'register': 'înregistrare', -'remove compiled': 'șterge compilate', -'restore': 'restaurare', -'revert': 'revenire', -'save': 'salvare', -'session expired': 'sesiune expirată', -'shell': 'line de commandă', -'site': 'site', -'some files could not be removed': 'anumite fișiere n-au putut fi șterse', -'starts with': 'începe cu', -'state': 'stare', -'static': 'static', -'table': 'tabel', -'test': 'test', -'the application logic, each URL path is mapped in one exposed function in the controller': 'logica aplicației, fiecare rută URL este mapată într-o funcție expusă de controlor', -'the data representation, define database tables and sets': 'reprezentarea datelor, definește tabelele bazei de date și seturile (de date)', -'the presentations layer, views are also known as templates': 'nivelul de prezentare, vederile sunt de asemenea numite și șabloane', -'these files are served without processing, your images go here': 'aceste fișiere sunt servite fără procesare, imaginea se plasează acolo', -'to previous version.': 'la versiunea anterioară.', -'too short': 'prea scurt', -'translation strings for the application': 'șiruri de caractere folosite la traducerea aplicației', -'try': 'încearcă', -'try something like': 'încearcă ceva de genul', -'unable to create application "%s"': 'imposibil de creat aplicația "%s"', -'unable to delete file "%(filename)s"': 'imposibil de șters fișierul "%(filename)s"', -'unable to parse csv file': 'imposibil de analizat fișierul csv', -'unable to uninstall "%s"': 'imposibil de dezinstalat "%s"', -'uncheck all': 'decoșează tot', -'uninstall': 'dezinstalează', -'update': 'actualizează', -'update all languages': 'actualizează toate limbile', -'upload application:': 'incarcă aplicația:', -'upload file:': 'încarcă fișier:', -'value already in database or empty': 'Valoare existentă în baza de date sau vidă', -'versioning': 'versiuni', -'view': 'vedere', -'views': 'vederi', -'web2py Recent Tweets': 'Ultimele tweet-uri web2py', -'web2py is up to date': 'web2py este la zi', } diff --git a/gluon/winservice.py b/gluon/winservice.py index 4a818a80..78c28d1a 100644 --- a/gluon/winservice.py +++ b/gluon/winservice.py @@ -92,7 +92,9 @@ class Web2pyService(Service): _winreg.CloseKey(h) dir = os.path.dirname(cls) os.chdir(dir) + from gluon.settings import global_settings global_settings.gluon_parent = dir + from gluon.custom_import import custom_import_install custom_import_install(dir) return True except: @@ -151,7 +153,7 @@ class Web2pyService(Service): def web2py_windows_service_handler(argv=None, opt_file='options'): path = os.path.dirname(__file__) - web2py_path = iup(path) + web2py_path = up(path) os.chdir(web2py_path) classstring = os.path.normpath( os.path.join(web2py_path,'gluon.winservice.Web2pyService')) From 6acc6e68e0d75c5df3d25bcf442535e799bbcf2c Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 21:04:56 -0500 Subject: [PATCH 14/28] possibly fixed issue 908 --- VERSION | 2 +- gluon/dal.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 27e8cae4..a5d94812 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 18:34:46) dev +Version 2.00.0 (2012-07-25 21:04:51) dev diff --git a/gluon/dal.py b/gluon/dal.py index 7aadc901..50d52717 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7112,6 +7112,9 @@ class Table(dict): "primarykey must be a list of fields from table '%s'" \ % tablename self._primarykey = primarykey + if len(primarykey)==1: + self._id = [f for f in fields if isinstance(f,Field) \ + and f.name==primarykey[0]][0] elif not [f for f in fields if isinstance(f,Field) and f.type=='id']: field = Field('id', 'id') newfields.append(field) From 4aa54bd49cb05ca6d19d24aa9d510d12ab47d333 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 25 Jul 2012 21:12:34 -0500 Subject: [PATCH 15/28] fixed issue 907 --- VERSION | 2 +- gluon/cfs.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index a5d94812..c6610881 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 21:04:51) dev +Version 2.00.0 (2012-07-25 21:12:30) dev diff --git a/gluon/cfs.py b/gluon/cfs.py index 8dd69cc2..a99f9c06 100644 --- a/gluon/cfs.py +++ b/gluon/cfs.py @@ -33,7 +33,10 @@ def getcfs(key, filename, filter=None): This is used on Google App Engine since pyc files cannot be saved. """ - t = os.stat(filename).st_mtime + try: + t = os.stat(filename).st_mtime + except OSError: + return filter() cfs_lock.acquire() item = cfs.get(key, None) cfs_lock.release() From 8e54ab740ebaf8b4e8d4f56b6968fec946e5fe71 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 05:43:46 -0500 Subject: [PATCH 16/28] make win uses 2.7 --- Makefile | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index ee7ecd03..a9136fa9 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ app: mv ../web2py_osx/web2py_osx.zip . win: echo 'did you uncomment import_all in gluon/main.py?' - python2.5 -c 'import compileall; compileall.compile_dir("gluon/")' + python2.7 -c 'import compileall; compileall.compile_dir("gluon/")' find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \; cd ../web2py_win/library/; unzip ../library.zip cd ../web2py_win/library/; zip -r ../library.zip * diff --git a/VERSION b/VERSION index c6610881..ad37fc5c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-25 21:12:30) dev +Version 2.00.0 (2012-07-26 05:43:41) dev From e7f9c88871a9658f4495fb58f5725f4735f4c4d6 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 05:56:54 -0500 Subject: [PATCH 17/28] fixed incompatibility with spreadsheet.py --- VERSION | 2 +- gluon/contrib/spreadsheet.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index ad37fc5c..06fedb4e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 05:43:41) dev +Version 2.00.0 (2012-07-26 05:56:51) dev diff --git a/gluon/contrib/spreadsheet.py b/gluon/contrib/spreadsheet.py index 09a00e34..e91dc733 100644 --- a/gluon/contrib/spreadsheet.py +++ b/gluon/contrib/spreadsheet.py @@ -855,9 +855,9 @@ class Sheet: sorted_headers = [TH(),] + \ [TH(header[1]) for header in sorted(unsorted_headers)] - table.insert(0, TR(*sorted_headers, _class="%s_fieldnames" % \ - attributes["_class"])) - + table.insert(0, TR(*sorted_headers, + **{_class:"%s_fieldnames" % \ + attributes["_class"]})) else: data = SCRIPT(""" // web2py Spreadsheets: no db data.""") From beb82cc6880a7602ae759f50894058edc710fe23 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 06:06:13 -0500 Subject: [PATCH 18/28] fixed order of unzipping --- Makefile | 4 ++-- VERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a9136fa9..65b350ca 100644 --- a/Makefile +++ b/Makefile @@ -62,8 +62,8 @@ app: echo 'did you uncomment import_all in gluon/main.py?' python2.5 -c 'import compileall; compileall.compile_dir("gluon/")' #python web2py.py -S welcome -R __exit__.py - find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \; cd ../web2py_osx/site-packages/; unzip ../site-packages.zip + find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \; cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip * mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5 cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources @@ -86,8 +86,8 @@ app: win: echo 'did you uncomment import_all in gluon/main.py?' python2.7 -c 'import compileall; compileall.compile_dir("gluon/")' - find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \; cd ../web2py_win/library/; unzip ../library.zip + find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \; cd ../web2py_win/library/; zip -r ../library.zip * mv ../web2py_win/library.zip ../web2py_win/web2py cp README.markdown ../web2py_win/web2py/ diff --git a/VERSION b/VERSION index 06fedb4e..d602efe8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 05:56:51) dev +Version 2.00.0 (2012-07-26 06:06:10) dev From c767003331520c700b48c003bfc626d5a0826385 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 06:42:28 -0500 Subject: [PATCH 19/28] radio widget lists ips --- VERSION | 2 +- gluon/widget.py | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/VERSION b/VERSION index d602efe8..f6551085 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 06:06:10) dev +Version 2.00.0 (2012-07-26 06:42:24) dev diff --git a/gluon/widget.py b/gluon/widget.py index 96ef1d6e..122cf75f 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -220,44 +220,48 @@ class web2pyDialog(object): justify=Tkinter.LEFT).grid(row=0, column=0, sticky=sticky) - self.ip = Tkinter.Entry(self.root) - self.ip.insert(Tkinter.END, self.options.ip) - self.ip.grid(row=0, column=1, sticky=sticky) - + self.ips = {} + self.selected_ip = Tkinter.StringVar() + for row,ip in enumerate(self.options.ips): + self.ips[ip] = Tkinter.Radiobutton( + self.root,text=ip, variable=self.selected_ip, value=ip) + self.ips[ip].grid(row=row, column=1, sticky=sticky) + if row==0: self.ips[ip].select() + shift = len(self.options.ips) # Port Tkinter.Label(self.root, text='Server Port:', - justify=Tkinter.LEFT).grid(row=1, + justify=Tkinter.LEFT).grid(row=shift, column=0, sticky=sticky) self.port_number = Tkinter.Entry(self.root) self.port_number.insert(Tkinter.END, self.options.port) - self.port_number.grid(row=1, column=1, sticky=sticky) + self.port_number.grid(row=shift, column=1, sticky=sticky) # Password Tkinter.Label(self.root, text='Choose Password:', - justify=Tkinter.LEFT).grid(row=2, + justify=Tkinter.LEFT).grid(row=shift+1, column=0, sticky=sticky) self.password = Tkinter.Entry(self.root, show='*') self.password.bind('', lambda e: self.start()) self.password.focus_force() - self.password.grid(row=2, column=1, sticky=sticky) + self.password.grid(row=shift+1, column=1, sticky=sticky) # Prepare the canvas self.canvas = Tkinter.Canvas(self.root, width=300, height=100, bg='black') - self.canvas.grid(row=3, column=0, columnspan=2) + self.canvas.grid(row=shift+2, column=0, columnspan=2) self.canvas.after(1000, self.update_canvas) # Prepare the frame frame = Tkinter.Frame(self.root) - frame.grid(row=4, column=0, columnspan=2) + frame.grid(row=shift+3, column=0, columnspan=2) # Start button self.button_start = Tkinter.Button(frame, @@ -359,7 +363,7 @@ class web2pyDialog(object): if not password: self.error('no password, no web admin interface') - ip = self.ip.get() + ip = self.selected_ip.get() regexp = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' if ip and not re.compile(regexp).match(ip): @@ -790,6 +794,9 @@ def console(): global_settings.cmd_options = options global_settings.cmd_args = args + # detect all ips: + options.ips = ['127.0.0.1']+[ip for ip in socket.gethostbyname_ex('')[2] if not ip.startswith("127.")]+['0.0.0.0'] + if options.run_system_tests: run_system_tests() From cf91d0ad71e2af863c8a98b79cfffe3d6b13eb40 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 06:57:27 -0500 Subject: [PATCH 20/28] do not assume library.zip --- Makefile | 4 ++-- VERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 65b350ca..cc599250 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ app: echo 'did you uncomment import_all in gluon/main.py?' python2.5 -c 'import compileall; compileall.compile_dir("gluon/")' #python web2py.py -S welcome -R __exit__.py - cd ../web2py_osx/site-packages/; unzip ../site-packages.zip + #cd ../web2py_osx/site-packages/; unzip ../site-packages.zip find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \; cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip * mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5 @@ -86,7 +86,7 @@ app: win: echo 'did you uncomment import_all in gluon/main.py?' python2.7 -c 'import compileall; compileall.compile_dir("gluon/")' - cd ../web2py_win/library/; unzip ../library.zip + #cd ../web2py_win/library/; unzip ../library.zip find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \; cd ../web2py_win/library/; zip -r ../library.zip * mv ../web2py_win/library.zip ../web2py_win/web2py diff --git a/VERSION b/VERSION index f6551085..93629d0e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 06:42:24) dev +Version 2.00.0 (2012-07-26 06:57:23) dev From f29e670cc76be536b96b34ba1764785a12380350 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 07:37:31 -0500 Subject: [PATCH 21/28] Local/Public --- VERSION | 2 +- gluon/widget.py | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index 93629d0e..f20e0772 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 06:57:23) dev +Version 2.00.0 (2012-07-26 07:37:27) dev diff --git a/gluon/widget.py b/gluon/widget.py index 122cf75f..03e67eda 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -222,12 +222,15 @@ class web2pyDialog(object): sticky=sticky) self.ips = {} self.selected_ip = Tkinter.StringVar() - for row,ip in enumerate(self.options.ips): + row=0 + for ip,legend in (('127.0.0.1','Local'),('0.0.0.0','Public')): self.ips[ip] = Tkinter.Radiobutton( - self.root,text=ip, variable=self.selected_ip, value=ip) - self.ips[ip].grid(row=row, column=1, sticky=sticky) + self.root,text='%s (%s)' % (legend,ip), + variable=self.selected_ip, value=ip) + self.ips[ip].grid(row=row, column=1, sticky=sticky) if row==0: self.ips[ip].select() - shift = len(self.options.ips) + row+=1 + shift = row # Port Tkinter.Label(self.root, text='Server Port:', @@ -420,7 +423,7 @@ class web2pyDialog(object): thread.start_new_thread(start_browser, (proto, ip, port)) self.password.configure(state='readonly') - self.ip.configure(state='readonly') + [ip.configure(state='disabled') for ip in self.ips.values()] self.port_number.configure(state='readonly') if self.tb: @@ -439,7 +442,7 @@ class web2pyDialog(object): self.button_start.configure(state='normal') self.button_stop.configure(state='disabled') self.password.configure(state='normal') - self.ip.configure(state='normal') + [ip.configure(state='normal') for ip in self.ips.values()] self.port_number.configure(state='normal') self.server.stop() @@ -794,9 +797,6 @@ def console(): global_settings.cmd_options = options global_settings.cmd_args = args - # detect all ips: - options.ips = ['127.0.0.1']+[ip for ip in socket.gethostbyname_ex('')[2] if not ip.startswith("127.")]+['0.0.0.0'] - if options.run_system_tests: run_system_tests() From 5a16a35ef2fa759eefdd20e67d2d41103dc48b10 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 07:49:12 -0500 Subject: [PATCH 22/28] validate request.client --- VERSION | 2 +- gluon/main.py | 3 +++ gluon/utils.py | 22 +++++++++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index f20e0772..930ddc4b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 07:37:27) dev +Version 2.00.0 (2012-07-26 07:49:09) dev diff --git a/gluon/main.py b/gluon/main.py index cff04b3c..a3779359 100644 --- a/gluon/main.py +++ b/gluon/main.py @@ -87,6 +87,7 @@ from settings import global_settings from validators import CRYPT from cache import Cache from html import URL as Url +from utils import is_valid_ip_address import newcron import rewrite @@ -402,6 +403,8 @@ def wsgibase(environ, responder): try: local_hosts.append(socket.gethostbyname(http_host)) except socket.gaierror: pass request.client = get_client(request.env) + if not is_valid_ip_address(request.client): + raise HTTP(400,"Bad Request") request.folder = abspath('applications', request.application) + os.sep x_req_with = str(request.env.http_x_requested_with).lower() diff --git a/gluon/utils.py b/gluon/utils.py index d429b100..911182fb 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -16,6 +16,7 @@ import random import time import os import logging +import socket from contrib.pbkdf2 import pbkdf2_hex logger = logging.getLogger("web2py") @@ -69,7 +70,7 @@ def get_digest(value): elif value == "sha512": return hashlib.sha512 else: - raise ValueError("Invalid digest algorithm: %s" % value) + raise ValueError("Invalid digest algorithm: %s" % value) DIGEST_ALG_BY_SIZE = { 128/4: 'md5', @@ -146,6 +147,25 @@ def web2py_uuid(): bytes = ''.join(chr(c ^ ctokens[i]) for i,c in enumerate(bytes)) return str(uuid.UUID(bytes=bytes, version=4)) +def is_valid_ip_address(address): + """ + >>> is_valid_ip_address('127.0') + False + >>> is_valid_ip_address('127.0.0.1') + True + >>> is_valid_ip_address('2001:660::1') + True + """ + try: + if address.count('.')==3: + addr = socket.inet_aton(address) + else: + addr = socket.inet_pton(socket.AF_INET6, address) + except AttributeError: # no socket.inet_pton + return False + except socket.error: + return False + return True From 8cb1fb2564e3b05eb2df7435a649e29570043777 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 08:12:29 -0500 Subject: [PATCH 23/28] consolidated buttons on site --- VERSION | 2 +- applications/admin/views/default/site.html | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 930ddc4b..42a2f86f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 07:49:09) dev +Version 2.00.0 (2012-07-26 08:12:26) dev diff --git a/applications/admin/views/default/site.html b/applications/admin/views/default/site.html index 0f73cefc..fd809416 100644 --- a/applications/admin/views/default/site.html +++ b/applications/admin/views/default/site.html @@ -148,13 +148,11 @@
-

{{=T("Deploy on Google App Engine")}}

-

{{=button(URL('gae','deploy'), T('Deploy'))}}

-

- -
-

{{=T("Deploy to OpenShift")}}

-

{{=button(URL('openshift','deploy'),T('Deploy'))}}

+

{{=T("Deploy")}}

+

+ {{=button(URL('gae','deploy'), T('Deploy on Google App Engine'))}} + {{=button(URL('openshift','deploy'),T('Deploy to OpenShift'))}} +


{{if TWITTER_HASH:}}
From a13ba1bc63d42bd64b32382c8e2228e63d42736c Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 09:41:39 -0500 Subject: [PATCH 24/28] FORM.dialog --- VERSION | 2 +- applications/admin/controllers/default.py | 26 ++++++++++++++++++++ applications/admin/views/default/delete.html | 2 +- gluon/html.py | 22 ++++++++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 42a2f86f..cee3f88c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 08:12:26) dev +Version 2.00.0 (2012-07-26 09:41:33) dev diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index e39e02e8..341f1ddc 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -403,6 +403,32 @@ def delete(): redirect(URL(sender, anchor=request.vars.id2)) return dict(filename=filename, sender=sender) +def delete(): + """ Object delete handler """ + app = get_app() + filename = '/'.join(request.args) + sender = request.vars.sender + + if isinstance(sender, list): # ## fix a problem with Vista + sender = sender[0] + + dialog = FORM.dialog(T('Delete'), + {T('Cancel'):URL(sender, anchor=request.vars.id)}) + + if dialog.accepted: + try: + full_path = apath(filename, r=request) + lineno = count_lines(open(full_path,'r').read()) + os.unlink(full_path) + log_progress(app,'DELETE',filename,progress=-lineno) + session.flash = T('file "%(filename)s" deleted', + dict(filename=filename)) + except Exception: + session.flash = T('unable to delete file "%(filename)s"', + dict(filename=filename)) + redirect(URL(sender, anchor=request.vars.id2)) + return dict(dialog=dialog,filename=filename) + def enable(): app = get_app() filename = os.path.join(apath(app, r=request),'DISABLED') diff --git a/applications/admin/views/default/delete.html b/applications/admin/views/default/delete.html index 2e53ab7e..d0f86f44 100644 --- a/applications/admin/views/default/delete.html +++ b/applications/admin/views/default/delete.html @@ -4,6 +4,6 @@

{{=T('Are you sure you want to delete file "%s"?', filename)}}

-

{{=FORM(INPUT(_type='submit',_name='nodelete',_value=T('Abort')),INPUT(_type='hidden',_name='sender',_value=sender), _class="inline")}}{{=FORM(INPUT(_type='submit',_name='delete',_value=T('Delete')),INPUT(_type='hidden',_name='sender',_value=sender), _class="inline")}}

+

{{=dialog}}

diff --git a/gluon/html.py b/gluon/html.py index 40cf5a17..50b61cd0 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -2024,8 +2024,28 @@ class FORM(DIV): self.validate(**kwargs) return self + 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,_onclick="window.location='%s';return false" % url,_class=_class)) + self[0][-1][1].append(INPUT(_type="button",_value=value,_class=_class, + _onclick=self.REDIRECT_JS % url)) + + + @staticmethod + def dialog(text='OK',buttons=None,hidden=None): + if not buttons: buttons = {} + if not hidden: hidden={} + inputs = [INPUT(_type='button', + _value=name, + _onclick=FORM.REDIRECT_JS % link) \ + for name,link in buttons.items()] + inputs += [INPUT(_type='hidden', + _name=name, + _value=value) + for name,value in hidden.items()] + form = FORM(INPUT(_type='submit',_value=text),*inputs) + form.process() + return form class BEAUTIFY(DIV): From 80a635f5efe4fa9f47714c1aa26e313dc87f5946 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 17:37:51 -0500 Subject: [PATCH 25/28] detect ips --- VERSION | 2 +- gluon/widget.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index cee3f88c..f31afd5b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 09:41:33) dev +Version 2.00.0 (2012-07-26 17:37:45) dev diff --git a/gluon/widget.py b/gluon/widget.py index 03e67eda..ce204a53 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -223,7 +223,10 @@ class web2pyDialog(object): self.ips = {} self.selected_ip = Tkinter.StringVar() row=0 - for ip,legend in (('127.0.0.1','Local'),('0.0.0.0','Public')): + ips = [('127.0.0.1','Local')] + \ + [(ip,'Public') for ip in options.ips] + \ + [('0.0.0.0','Public')] + for ip,legend in ips: self.ips[ip] = Tkinter.Radiobutton( self.root,text='%s (%s)' % (legend,ip), variable=self.selected_ip, value=ip) @@ -797,6 +800,9 @@ def console(): global_settings.cmd_options = options global_settings.cmd_args = args + options.ips = [ip for ip in socket.gethostbyname_ex(socket.getfqdn())[2] + if ip!='127.0.0.1'] + if options.run_system_tests: run_system_tests() From 4665d8b9627e465cc61c8d1624a549a7c2c2709a Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 17:42:20 -0500 Subject: [PATCH 26/28] issue 909, secure login_bare, thanks szimszon --- VERSION | 2 +- gluon/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index f31afd5b..747c91ff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 17:37:45) dev +Version 2.00.0 (2012-07-26 17:42:17) dev diff --git a/gluon/tools.py b/gluon/tools.py index 48943cb4..28e7129b 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -1627,7 +1627,7 @@ class Auth(object): userfield = 'email' passfield = self.settings.password_field user = self.db(table_user[userfield] == username).select().first() - if user: + if user and user.get(passfield,False): password = table_user[passfield].validate(password)[0] if not user.registration_key and password == user[passfield]: user = Storage(table_user._filter_fields(user, id=True)) From c5b7c637320a48f11a0f91a6ae864126c1b20504 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 26 Jul 2012 17:53:35 -0500 Subject: [PATCH 27/28] BR()+BR() --- VERSION | 2 +- gluon/html.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 747c91ff..5d98ccce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 17:42:17) dev +Version 2.00.0 (2012-07-26 17:53:32) dev diff --git a/gluon/html.py b/gluon/html.py index 50b61cd0..3776f67b 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -474,6 +474,16 @@ class XmlComponent(object): raise NotImplementedError def __mul__(self,n): return CAT(*[self for i in range(n)]) + def __add__(self,other): + if isinstance(self,CAT): + components = self.components + else: + components = [self] + if isinstance(other,CAT): + components += other.components + else: + components += [other] + return CAT(*components) class XML(XmlComponent): """ From 390129e2033c05a531c6750eca36a00387d2fe5e Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 27 Jul 2012 09:32:36 -0500 Subject: [PATCH 28/28] captures error in widget gethostaddr, thanks Patrick --- VERSION | 2 +- gluon/widget.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 5d98ccce..3159a75d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.00.0 (2012-07-26 17:53:32) dev +Version 2.00.0 (2012-07-27 09:32:24) dev diff --git a/gluon/widget.py b/gluon/widget.py index ce204a53..916f3495 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -800,8 +800,12 @@ def console(): global_settings.cmd_options = options global_settings.cmd_args = args - options.ips = [ip for ip in socket.gethostbyname_ex(socket.getfqdn())[2] - if ip!='127.0.0.1'] + try: + options.ips = [ + ip for ip in socket.gethostbyname_ex(socket.getfqdn())[2] + if ip!='127.0.0.1'] + except socket.gaierror: + options.ips = [] if options.run_system_tests: run_system_tests()