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}}
+
+ {{
+ path=[]
+ for file in privates+['']:
+ items=file.split('/')
+ file_path=items[:-1]
+ filename=items[-1]
+ while path!=file_path:
+ if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
+ path.append(file_path[len(path)])
+ thispath='private__'+'__'.join(path)
+ }}
+
+ {{=path[-1]}}/
+ {{
+ else:
+ path = path[:-1]
+ }}
+
+ {{
+ pass
+ pass
+ if filename:
+ }}
+
+ {{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}}
+
+
+ {{=filename}}
+
+ {{
+ pass
+ pass
+ }}
+ {{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()