Merge github.com:web2py/web2py

This commit is contained in:
Michele Comitini
2012-10-31 11:20:32 +01:00
11 changed files with 160 additions and 81 deletions
+1 -1
View File
@@ -1 +1 @@
Version 2.2.1 (2012-10-27 14:37:32) stable Version 2.2.1 (2012-10-30 11:09:19) stable
+2
View File
@@ -147,6 +147,7 @@ class CacheInRam(CacheAbstract):
def __init__(self, request=None): def __init__(self, request=None):
self.initialized = False self.initialized = False
self.request = request self.request = request
self.storage = {}
def initialize(self): def initialize(self):
if self.initialized: if self.initialized:
@@ -303,6 +304,7 @@ class CacheOnDisk(CacheAbstract):
self.initialized = False self.initialized = False
self.request = request self.request = request
self.folder = folder self.folder = folder
self.storage = {}
def initialize(self): def initialize(self):
if self.initialized: if self.initialized:
@@ -99,13 +99,21 @@ class DropboxAccount(object):
redirect('https://www.dropbox.com/logout') redirect('https://www.dropbox.com/logout')
return next return next
def get_client(self):
access_token = current.session.dropbox_access_token
self.sess.set_token(access_token[0], access_token[1])
self.client = client.DropboxClient(self.sess)
def put(self, filename, file): def put(self, filename, file):
if not hasattr(self,'client'): self.get_client()
return json.loads(self.client.put_file(filename, file))['bytes'] return json.loads(self.client.put_file(filename, file))['bytes']
def get(self, filename, file): def get(self, filename, file):
if not hasattr(self,'client'): self.get_client()
return self.client.get_file(filename) return self.client.get_file(filename)
def dir(self, path): def dir(self, path):
if not hasattr(self,'client'): self.get_client()
return json.loads(self.client.metadata(path)) return json.loads(self.client.metadata(path))
+31
View File
@@ -26,6 +26,7 @@ including:
- DB2 - DB2
- Interbase - Interbase
- Ingres - Ingres
- Informix
- SapDB (experimental) - SapDB (experimental)
- Cubrid (experimental) - Cubrid (experimental)
- CouchDB (experimental) - CouchDB (experimental)
@@ -255,6 +256,7 @@ REGEX_STORE_PATTERN = re.compile('\.(?P<e>\w{1,5})$')
REGEX_QUOTES = re.compile("'[^']*'") REGEX_QUOTES = re.compile("'[^']*'")
REGEX_ALPHANUMERIC = re.compile('^[0-9a-zA-Z]\w*$') REGEX_ALPHANUMERIC = re.compile('^[0-9a-zA-Z]\w*$')
REGEX_PASSWORD = re.compile('\://([^:@]*)\:') REGEX_PASSWORD = re.compile('\://([^:@]*)\:')
REGEX_NOPASSWD = re.compile('(?<=\:)([^:@/]+)(?=@.+)')
# list of drivers will be built on the fly # list of drivers will be built on the fly
# and lists only what is available # and lists only what is available
@@ -6660,6 +6662,35 @@ class DAL(object):
""" """
BaseAdapter.set_folder(folder) BaseAdapter.set_folder(folder)
@staticmethod
def get_instances():
"""
Returns a dictionary with uri as key with timings and defined tables
{'sqlite://storage.sqlite': {
'dbstats': [(select auth_user.email from auth_user, 0.02009)],
'dbtables': {
'defined': ['auth_cas', 'auth_event', 'auth_group',
'auth_membership', 'auth_permission', 'auth_user'],
'lazy': '[]'
}
}
}
"""
dbs = getattr(THREAD_LOCAL,'db_instances',{}).items()
infos = {}
for db_uid, db_group in dbs:
for db in db_group:
if not db._uri:
continue
k = REGEX_NOPASSWD.sub('******',db._uri)
infos[k] = dict(dbstats = [(row[0], row[1]) for row in db._timings],
dbtables = {'defined':
sorted(list(set(db.tables) -
set(db._LAZY_TABLES.keys()))),
'lazy': sorted(db._LAZY_TABLES.keys())}
)
return infos
@staticmethod @staticmethod
def distributed_transaction_begin(*instances): def distributed_transaction_begin(*instances):
if not instances: if not instances:
+15 -19
View File
@@ -48,7 +48,6 @@ except ImportError:
have_minify = False have_minify = False
regex_session_id = re.compile('^([\w\-]+/)?[\w\-\.]+$') regex_session_id = re.compile('^([\w\-]+/)?[\w\-\.]+$')
regex_nopasswd = re.compile('(?<=\:)([^:@/]+)(?=@.+)')
__all__ = ['Request', 'Response', 'Session'] __all__ = ['Request', 'Response', 'Session']
@@ -127,7 +126,10 @@ class Request(Storage):
If request comes in over HTTP, redirect it to HTTPS If request comes in over HTTP, redirect it to HTTPS
and secure the session. and secure the session.
""" """
if not global_settings.cronjob and not self.is_https: cmd_opts = global_settings.cmd_options
#checking if this is called within the scheduler or within the shell
#in addition to checking if it's not a cronjob
if not cmd_opts.shell and not cmd_opts.scheduler and not global_settings.cronjob and not self.is_https:
current.session.forget() current.session.forget()
redirect(URL(scheme='https', args=self.args, vars=self.vars)) redirect(URL(scheme='https', args=self.args, vars=self.vars))
@@ -434,22 +436,16 @@ class Response(Storage):
BUTTON = TAG.button BUTTON = TAG.button
admin = URL("admin", "default", "design", admin = URL("admin", "default", "design",
args=current.request.application) args=current.request.application)
from gluon.dal import THREAD_LOCAL from gluon.dal import DAL
if hasattr(THREAD_LOCAL, 'instances'): dbstats = []
dbstats = [TABLE(*[TR(PRE(row[0]), '%.2fms' % (row[1] * 1000)) dbtables = {}
for row in i.db._timings]) infos = DAL.get_instances()
for i in THREAD_LOCAL.instances] for k,v in infos.iteritems():
dbtables = dict([(regex_nopasswd.sub('******', i.uri), dbstats.append(TABLE(*[TR(PRE(row[0]),'%.2fms' %
{'defined': (row[1]*1000))
sorted(list(set(i.db.tables) - for row in v['dbstats']]))
set(i.db._LAZY_TABLES.keys()))) or dbtables[k] = dict(defined=v['dbtables']['defined'] or '[no defined tables]',
'[no defined tables]', lazy=v['dbtables']['lazy'] or '[no lazy tables]')
'lazy': sorted(i.db._LAZY_TABLES.keys()) or
'[no lazy tables]'})
for i in THREAD_LOCAL.instances])
else:
dbstats = [] # if no db or on GAE
dbtables = {}
u = web2py_uuid() u = web2py_uuid()
backtotop = A('Back to top', _href="#totop-%s" % u) backtotop = A('Back to top', _href="#totop-%s" % u)
return DIV( return DIV(
@@ -507,7 +503,7 @@ class Session(Storage):
request = current.request request = current.request
if response is None: if response is None:
response = current.response response = current.response
if separate == True: if separate is True:
separate = lambda session_name: session_name[-2:] separate = lambda session_name: session_name[-2:]
self._unlock(response) self._unlock(response)
if not masterapp: if not masterapp:
+6 -1
View File
@@ -140,7 +140,7 @@ class HTTP(BaseException):
return self.message return self.message
def redirect(location, how=303, client_side=False): def redirect(location='', how=303, client_side=False):
if location: if location:
from gluon import current from gluon import current
loc = location.replace('\r', '%0D').replace('\n', '%0A') loc = location.replace('\r', '%0D').replace('\n', '%0A')
@@ -150,3 +150,8 @@ def redirect(location, how=303, client_side=False):
raise HTTP(how, raise HTTP(how,
'You are being redirected <a href="%s">here</a>' % loc, 'You are being redirected <a href="%s">here</a>' % loc,
Location=loc) Location=loc)
else:
from gluon import current
if client_side and current.request.ajax:
raise HTTP(200, **{'web2py-component-command': 'window.location.reload(true)'})
+43 -22
View File
@@ -1365,13 +1365,13 @@ class Auth(object):
archive_names='%(tablename)s_archive', archive_names='%(tablename)s_archive',
current_record='current_record'): current_record='current_record'):
""" """
to enable full record vernionioning (including auth tables): to enable full record versioning (including auth tables):
auth = Auth(db) auth = Auth(db)
auth.define_tables(signature=True) auth.define_tables(signature=True)
# define our own tables # define our own tables
db.define_table('mything',Field('name'),auth.signature) db.define_table('mything',Field('name'),auth.signature)
auth.enable_record_vernining(tables=db) auth.enable_record_versioning(tables=db)
tables can be the db (all table) or a list of tables. tables can be the db (all table) or a list of tables.
only tables with modified_by and modified_on fiels (as created only tables with modified_by and modified_on fiels (as created
@@ -1386,7 +1386,7 @@ class Auth(object):
Important: If you use auth.enable_record_versioning, Important: If you use auth.enable_record_versioning,
do not use auth.archive or you will end up with duplicates. do not use auth.archive or you will end up with duplicates.
auth.archive does explicitely what enable_record_versioning auth.archive does explicitly what enable_record_versioning
does automatically. does automatically.
""" """
@@ -1489,7 +1489,7 @@ class Auth(object):
IS_NOT_IN_DB(db, '%s.username' % settings.table_user_name)] IS_NOT_IN_DB(db, '%s.username' % settings.table_user_name)]
if not settings.username_case_sensitive: if not settings.username_case_sensitive:
is_unique_username.insert(1, IS_LOWER()) is_unique_username.insert(1, IS_LOWER())
table = db.define_table( db.define_table(
settings.table_user_name, settings.table_user_name,
Field('first_name', length=128, default='', Field('first_name', length=128, default='',
label=self.messages.label_first_name, label=self.messages.label_first_name,
@@ -1522,7 +1522,7 @@ class Auth(object):
fake_migrate=fake_migrate, fake_migrate=fake_migrate,
format='%(username)s')) format='%(username)s'))
else: else:
table = db.define_table( db.define_table(
settings.table_user_name, settings.table_user_name,
Field('first_name', length=128, default='', Field('first_name', length=128, default='',
label=self.messages.label_first_name, label=self.messages.label_first_name,
@@ -1555,7 +1555,7 @@ class Auth(object):
if not settings.table_group_name in db.tables: if not settings.table_group_name in db.tables:
extra_fields = settings.extra_fields.get( extra_fields = settings.extra_fields.get(
settings.table_group_name, []) + signature_list settings.table_group_name, []) + signature_list
table = db.define_table( db.define_table(
settings.table_group_name, settings.table_group_name,
Field('role', length=512, default='', Field('role', length=512, default='',
label=self.messages.label_role, label=self.messages.label_role,
@@ -1573,7 +1573,7 @@ class Auth(object):
if not settings.table_membership_name in db.tables: if not settings.table_membership_name in db.tables:
extra_fields = settings.extra_fields.get( extra_fields = settings.extra_fields.get(
settings.table_membership_name, []) + signature_list settings.table_membership_name, []) + signature_list
table = db.define_table( db.define_table(
settings.table_membership_name, settings.table_membership_name,
Field('user_id', reference_table_user, Field('user_id', reference_table_user,
label=self.messages.label_user_id), label=self.messages.label_user_id),
@@ -1587,7 +1587,7 @@ class Auth(object):
if not settings.table_permission_name in db.tables: if not settings.table_permission_name in db.tables:
extra_fields = settings.extra_fields.get( extra_fields = settings.extra_fields.get(
settings.table_permission_name, []) + signature_list settings.table_permission_name, []) + signature_list
table = db.define_table( db.define_table(
settings.table_permission_name, settings.table_permission_name,
Field('group_id', reference_table_group, Field('group_id', reference_table_group,
label=self.messages.label_group_id), label=self.messages.label_group_id),
@@ -1605,7 +1605,7 @@ class Auth(object):
settings.table_permission_name, migrate), settings.table_permission_name, migrate),
fake_migrate=fake_migrate)) fake_migrate=fake_migrate))
if not settings.table_event_name in db.tables: if not settings.table_event_name in db.tables:
table = db.define_table( db.define_table(
settings.table_event_name, settings.table_event_name,
Field('time_stamp', 'datetime', Field('time_stamp', 'datetime',
default=current.request.now, default=current.request.now,
@@ -1629,7 +1629,7 @@ class Auth(object):
now = current.request.now now = current.request.now
if settings.cas_domains: if settings.cas_domains:
if not settings.table_cas_name in db.tables: if not settings.table_cas_name in db.tables:
table = db.define_table( db.define_table(
settings.table_cas_name, settings.table_cas_name,
Field('user_id', reference_table_user, default=None, Field('user_id', reference_table_user, default=None,
label=self.messages.label_user_id), label=self.messages.label_user_id),
@@ -1773,8 +1773,6 @@ class Auth(object):
""" """
logins user as specified by usernname (or email) and password logins user as specified by usernname (or email) and password
""" """
request = current.request
session = current.session
table_user = self.table_user() table_user = self.table_user()
if self.settings.login_userfield: if self.settings.login_userfield:
userfield = self.settings.login_userfield userfield = self.settings.login_userfield
@@ -2669,7 +2667,6 @@ class Auth(object):
redirect(self.settings.login_url) redirect(self.settings.login_url)
db = self.db db = self.db
table_user = self.table_user() table_user = self.table_user()
usern = self.settings.table_user_name
s = db(table_user.id == self.user.id) s = db(table_user.id == self.user.id)
request = current.request request = current.request
@@ -2717,7 +2714,7 @@ class Auth(object):
next = self.url(args=request.args) next = self.url(args=request.args)
else: else:
next = replace_id(next, form) next = replace_id(next, form)
redirect(next) redirect(next)
return form return form
def profile( def profile(
@@ -2990,6 +2987,8 @@ class Auth(object):
return self.id_group(self.user_group_role(user_id)) return self.id_group(self.user_group_role(user_id))
def user_group_role(self, user_id=None): def user_group_role(self, user_id=None):
if not self.settings.create_user_groups:
return None
if user_id: if user_id:
user = self.table_user()[user_id] user = self.table_user()[user_id]
else: else:
@@ -4139,12 +4138,13 @@ class Service(object):
return '<NULL>' return '<NULL>'
return value return value
if args and args[0] in self.run_procedures: if args and args[0] in self.run_procedures:
import types
r = universal_caller(self.run_procedures[args[0]], r = universal_caller(self.run_procedures[args[0]],
*args[1:], **dict(request.vars)) *args[1:], **dict(request.vars))
s = cStringIO.StringIO() s = cStringIO.StringIO()
if hasattr(r, 'export_to_csv_file'): if hasattr(r, 'export_to_csv_file'):
r.export_to_csv_file(s) r.export_to_csv_file(s)
elif r and isinstance(r[0], (dict, Storage)): elif r and not isinstance(r, types.GeneratorType) and isinstance(r[0], (dict, Storage)):
import csv import csv
writer = csv.writer(s) writer = csv.writer(s)
writer.writerow(r[0].keys()) writer.writerow(r[0].keys())
@@ -4656,7 +4656,7 @@ class Wiki(object):
html = page.body html = page.body
# @///function -> http://..../function # @///function -> http://..../function
html = replace_at_urls(html, URL) html = replace_at_urls(html, URL)
# http://...jpg -> <img src="http://...jpg/> or oembed # http://...jpg -> <img src="http://...jpg/> or embed
html = replace_autolinks(html, lambda link: expand_one(link, {})) html = replace_autolinks(html, lambda link: expand_one(link, {}))
# @{component:name} -> <script>embed component name</script> # @{component:name} -> <script>embed component name</script>
html = replace_components(html, self.env) html = replace_components(html, self.env)
@@ -4898,14 +4898,15 @@ class Wiki(object):
% self.force_prefix % self.force_prefix
redirect(URL(args=('_edit', self.force_prefix + slug))) redirect(URL(args=('_edit', self.force_prefix + slug)))
db.wiki_page.can_read.default = [Wiki.everybody] db.wiki_page.can_read.default = [Wiki.everybody]
db.wiki_page.can_edit.default = [auth.user_group_role()] user_group_role = auth.user_group_role()
db.wiki_page.can_edit.default = [user_group_role]
db.wiki_page.title.default = title_guess db.wiki_page.title.default = title_guess
db.wiki_page.slug.default = slug db.wiki_page.slug.default = slug
if slug == 'wiki-menu': if slug == 'wiki-menu':
db.wiki_page.body.default = \ db.wiki_page.body.default = \
'- Menu Item > @////index\n- - Submenu > http://web2py.com' '- Menu Item > @////index\n- - Submenu > http://web2py.com'
else: else:
db.wiki_page.body.default = '## %s\n\npage content' % title_guess db.wiki_page.body.default = '## %s\n\npage content\n\n[[new page @////new_page]]\n' % title_guess
vars = current.request.post_vars vars = current.request.post_vars
if vars.body: if vars.body:
vars.body = vars.body.replace('://%s' % self.host, '://HOSTNAME') vars.body = vars.body.replace('://%s' % self.host, '://HOSTNAME')
@@ -4924,12 +4925,20 @@ class Wiki(object):
pagecontent.css('font-family', pagecontent.css('font-family',
'Monaco,Menlo,Consolas,"Courier New",monospace'); 'Monaco,Menlo,Consolas,"Courier New",monospace');
var prevbutton = $('<button class="btn nopreview">Preview</button>'); var prevbutton = $('<button class="btn nopreview">Preview</button>');
var mediabutton = $('<button class="btn nopreview">Media</button>');
var preview = $('<div id="preview"></div>').hide(); var preview = $('<div id="preview"></div>').hide();
var previewmedia = $('<div id="previewmedia"></div>');
var table = $('form'); var table = $('form');
prevbutton.insertBefore(table);
preview.insertBefore(table); preview.insertBefore(table);
prevbutton.on('click', function(e) { prevbutton.insertBefore(table);
e.preventDefault(); mediabutton.insertBefore(table);
previewmedia.insertBefore(table);
mediabutton.toggle(function() {
web2py_component('%(urlmedia)s', 'previewmedia');
}, function() {
previewmedia.empty();
});
prevbutton.click(function() {
if (prevbutton.hasClass('nopreview')) { if (prevbutton.hasClass('nopreview')) {
prevbutton.addClass('preview').removeClass( prevbutton.addClass('preview').removeClass(
'nopreview').html('Edit Source'); 'nopreview').html('Edit Source');
@@ -4942,7 +4951,7 @@ class Wiki(object):
} }
}) })
}) })
""" % dict(url=URL(args=('_preview'))) """ % dict(url=URL(args=('_preview')), urlmedia=URL(extension='load',args=('_editmedia'),vars=dict(embedded=1)))
return dict(content=TAG[''](form, SCRIPT(script))) return dict(content=TAG[''](form, SCRIPT(script)))
def editmedia(self, slug): def editmedia(self, slug):
@@ -4958,9 +4967,21 @@ class Wiki(object):
row.filename.split('.')[-1])) row.filename.split('.')[-1]))
self.auth.db.wiki_media.wiki_page.default = page.id self.auth.db.wiki_media.wiki_page.default = page.id
self.auth.db.wiki_media.wiki_page.writable = False self.auth.db.wiki_media.wiki_page.writable = False
links = []
csv = True
if current.request.vars.embedded:
script = "var c = $('#wiki_page_body'); c.val(c.val() + $('%s').text()); return false;"
fragment = self.auth.db.wiki_media.id.represent
csv = False
links=[
lambda row:
A('copy into source', _href='#', _onclick=script % (fragment(row.id, row)))
]
content = SQLFORM.grid( content = SQLFORM.grid(
self.auth.db.wiki_media.wiki_page == page.id, self.auth.db.wiki_media.wiki_page == page.id,
orderby=self.auth.db.wiki_media.title, orderby=self.auth.db.wiki_media.title,
links = links,
csv = csv,
args=['_editmedia', slug], args=['_editmedia', slug],
user_signature=False) user_signature=False)
return dict(content=content) return dict(content=content)
+1 -1
View File
@@ -37,7 +37,7 @@ try:
except ImportError: except ImportError:
try: try:
from .aes import AES from .aes import AES
except ImportError: except (ImportError, ValueError):
from contrib.aes import AES from contrib.aes import AES
try: try:
+8
View File
@@ -778,6 +778,8 @@ class IS_FLOAT_IN_RANGE(Validator):
return (value, self.error_message) return (value, self.error_message)
def formatter(self, value): def formatter(self, value):
if value is None:
return None
return str2dec(value).replace('.', self.dot) return str2dec(value).replace('.', self.dot)
@@ -882,6 +884,8 @@ class IS_DECIMAL_IN_RANGE(Validator):
return (value, self.error_message) return (value, self.error_message)
def formatter(self, value): def formatter(self, value):
if value is None:
return None
return str2dec(value).replace('.', self.dot) return str2dec(value).replace('.', self.dot)
@@ -2170,6 +2174,8 @@ class IS_DATE(Validator):
return (value, translate(self.error_message) % self.extremes) return (value, translate(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None:
return None
format = self.format format = self.format
year = value.year year = value.year
y = '%.4i' % year y = '%.4i' % year
@@ -2228,6 +2234,8 @@ class IS_DATETIME(Validator):
return (value, translate(self.error_message) % self.extremes) return (value, translate(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None:
return None
format = self.format format = self.format
year = value.year year = value.year
y = '%.4i' % year y = '%.4i' % year
+3 -2
View File
@@ -95,8 +95,9 @@ class SessionSetDb(SessionSet):
"""Return list of SessionDb instances for existing sessions.""" """Return list of SessionDb instances for existing sessions."""
sessions = [] sessions = []
table = current.response.session_db_table table = current.response.session_db_table
for row in table._db(table.id > 0).select(): if table:
sessions.append(SessionDb(row)) for row in table._db(table.id > 0).select():
sessions.append(SessionDb(row))
return sessions return sessions
+38 -31
View File
@@ -7,14 +7,10 @@ import sys
import shutil import shutil
import os import os
from gluon.languages import findT, utf8_repr from gluon.languages import findT
sys.path.insert(0, '.') sys.path.insert(0, '.')
file = sys.argv[1]
apps = sys.argv[2:]
def sync_language(d, data): def sync_language(d, data):
''' this function makes sure a translated string will be prefered over an untranslated ''' this function makes sure a translated string will be prefered over an untranslated
string when syncing languages between apps. when both are translated, it prefers the string when syncing languages between apps. when both are translated, it prefers the
@@ -37,35 +33,46 @@ def sync_language(d, data):
return d return d
d = {} def sync_main(file, apps):
for app in apps: d = {}
path = 'applications/%s/' % app for app in apps:
findT(path, file) path = 'applications/%s/' % app
langfile = open(os.path.join(path, 'languages', '%s.py' % file)) findT(path, file)
langfile = open(os.path.join(path, 'languages', '%s.py' % file))
try:
data = eval(langfile.read())
finally:
langfile.close()
d = sync_language(d, data)
path = 'applications/%s/' % apps[-1]
file1 = os.path.join(path, 'languages', '%s.py' % file)
f = open(file1, 'w')
try: try:
data = eval(langfile.read()) f.write('# coding: utf8\n')
f.write('{\n')
keys = d.keys()
keys.sort()
for key in keys:
f.write("'''%s''':'''%s''',\n" % (key.replace("'", "\\'"), str(d[key].replace("'", "\\'"))))
f.write('}\n')
finally: finally:
langfile.close() f.close()
d = sync_language(d, data) oapps = reversed(apps[:-1])
for app in oapps:
path2 = 'applications/%s/' % app
file2 = os.path.join(path2, 'languages', '%s.py' % file)
if file1 != file2:
shutil.copyfile(file1, file2)
path = 'applications/%s/' % apps[-1] if __name__ == "__main__":
file1 = os.path.join(path, 'languages', '%s.py' % file)
f = open(file1, 'w') file = sys.argv[1]
try: apps = sys.argv[2:]
f.write('# coding: utf8\n')
f.write('{\n') sync_main(file, apps)
keys = d.keys()
keys.sort()
for key in keys:
f.write('%s:%s,\n' % (utf8_repr(key), utf8_repr(str(d[key]))))
f.write('}\n')
finally:
f.close()
oapps = reversed(apps[:-1])
for app in oapps:
path2 = 'applications/%s/' % app
file2 = os.path.join(path2, 'languages', '%s.py' % file)
shutil.copyfile(file1, file2)