Compare commits

...

23 Commits

Author SHA1 Message Date
mdipierro
edcc2e44dc R-2.12.3 2015-08-18 19:15:01 -05:00
mdipierro
2cf9f26b0d fixed pydal tracking again 2015-08-18 15:13:57 -05:00
mdipierro
4b99b6fdd7 adding debounced ajax calls 2015-08-18 14:16:10 -05:00
mdipierro
622430583f Merge pull request #1047 from web2py/revert-1046-master
Revert "added default HttpOnly cookies"
2015-08-18 13:59:07 -05:00
mdipierro
89cc5a5f70 Revert "added default HttpOnly cookies" 2015-08-18 13:57:44 -05:00
mdipierro
6899154fcd reverted DAL to v15.07 2015-08-18 13:46:38 -05:00
mdipierro
47c0e461f1 Merge pull request #1046 from ShySec/master
added default HttpOnly cookies
2015-08-18 11:53:54 -05:00
mdipierro
93237837ed Merge pull request #1042 from cassiobotaro/master
Allow change quality when RESIZE
2015-08-18 11:52:52 -05:00
kelson
cf20ce5fae _js_cookies => not httponly_cookies 2015-08-18 12:25:13 -04:00
mdipierro
04c86f07ef Merge pull request #1032 from dsk7/allow_requires_login_to_be_determined_dynamically
Allow to specify a function for requires_login at auth decoration.
2015-08-18 11:03:37 -05:00
mdipierro
1a12c4011b Merge pull request #1031 from mmunz/master
Repair cache status page in appadmin for welcome, admin and examples …
2015-08-18 11:02:14 -05:00
mdipierro
85bbe15758 Merge pull request #1030 from josedesoto/issue/1028
fixed issue #1028, saml2 bug
2015-08-18 11:01:46 -05:00
mdipierro
5816481a44 Merge pull request #1029 from timnyborg/patch-3
simplejsonrpc: Fix TypeError when data is None
2015-08-18 11:01:19 -05:00
kelson
2675e9d229 added default HttpOnly cookies 2015-08-18 10:23:29 -04:00
mdipierro
41498917d5 Autocomplete(...at_beginning=False) 2015-08-16 15:22:45 -05:00
Cássio Botaro
8f7acd8154 Allow change quality when RESIZE 2015-08-13 02:12:49 -03:00
mdipierro
26865421b6 email change 2015-08-11 00:32:09 -05:00
mdipierro
b9ee4d4730 R-2.12.2 2015-08-09 09:27:42 -05:00
mdipierro
8fd7a27d5f fixed problem with pack all and missing cache folder 2015-08-09 09:26:57 -05:00
dsk7
f94bc250eb Allow to specify a function for requires_login at auth decoration. 2015-08-02 13:21:20 +02:00
Manuel Munz
8078d4b0f3 Repair cache status page in appadmin for welcome, admin and examples apps.
This fixes how values are read. For cache.disk, we need to use the second value in the dict.
For cache.ram everything is already there in the object, no need trying to get the stats (which are not there) in the loop.
Also small fix for buttons that did not open the detailed statistics when clicked (class hidden has !important in bootstrap3).
2015-08-01 00:29:34 +02:00
Tim Nyborg
5ee8c9c930 simplejsonrpc: Fix TypeError when data is none 2015-07-27 12:07:26 +01:00
Jose de Soto
6659bc0793 fixed issue #1028, saml2 bug 2015-07-27 13:05:36 +02:00
15 changed files with 170 additions and 101 deletions

View File

@@ -32,7 +32,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.12.1-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.12.3-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
### clean up baisc apps

View File

@@ -1 +1 @@
Version 2.12.1-stable+timestamp.2015.08.07.02.10.57
Version 2.12.3-stable+timestamp.2015.08.18.19.14.07

View File

@@ -445,30 +445,31 @@ def ccache():
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
# get ram stats directly from the cache object
ram_stats = cache.ram.stats[request.application]
ram['hits'] = ram_stats['hit_total'] - ram_stats['misses']
ram['misses'] = ram_stats['misses']
try:
ram['ratio'] = ram['hits'] * 100 / ram_stats['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
try:
ram['ratio'] = ram['hits'] * 100 / value['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
else:
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
for key in cache.disk.storage:
value = cache.disk.storage[key]
if isinstance(value, dict):
disk['hits'] = value['hit_total'] - value['misses']
disk['misses'] = value['misses']
if isinstance(value[1], dict):
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
disk['misses'] = value[1]['misses']
try:
disk['ratio'] = disk['hits'] * 100 / value['hit_total']
disk['ratio'] = disk['hits'] * 100 / value[1]['hit_total']
except (KeyError, ZeroDivisionError):
disk['ratio'] = 0
else:
@@ -485,7 +486,7 @@ def ccache():
ram_keys.remove('oldest')
for key in ram_keys:
total[key] = ram[key] + disk[key]
try:
total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])

View File

@@ -137,9 +137,9 @@
<h4>{{=T("Overview")}}</h4>
<p>{{=T.M("Number of entries: **%s**", total['entries'])}}</p>
{{if total['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses})",
dict(ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>
{{=T("Size of cache:")}}
{{if object_stats:}}
@@ -155,7 +155,7 @@
{{=T.M("Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=total['oldest'][0], min=total['oldest'][1], sec=total['oldest'][2]))}}
</p>
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle();')}}
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="all_keys">
{{=total['keys']}}
</div>
@@ -183,7 +183,7 @@
{{=T.M("RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=ram['oldest'][0], min=ram['oldest'][1], sec=ram['oldest'][2]))}}
</p>
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle();')}}
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="ram_keys">
{{=ram['keys']}}
</div>
@@ -212,7 +212,7 @@
{{=T.M("DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=disk['oldest'][0], min=disk['oldest'][1], sec=disk['oldest'][2]))}}
</p>
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle();')}}
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="disk_keys">
{{=disk['keys']}}
</div>

View File

@@ -445,30 +445,31 @@ def ccache():
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
# get ram stats directly from the cache object
ram_stats = cache.ram.stats[request.application]
ram['hits'] = ram_stats['hit_total'] - ram_stats['misses']
ram['misses'] = ram_stats['misses']
try:
ram['ratio'] = ram['hits'] * 100 / ram_stats['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
try:
ram['ratio'] = ram['hits'] * 100 / value['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
else:
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
for key in cache.disk.storage:
value = cache.disk.storage[key]
if isinstance(value, dict):
disk['hits'] = value['hit_total'] - value['misses']
disk['misses'] = value['misses']
if isinstance(value[1], dict):
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
disk['misses'] = value[1]['misses']
try:
disk['ratio'] = disk['hits'] * 100 / value['hit_total']
disk['ratio'] = disk['hits'] * 100 / value[1]['hit_total']
except (KeyError, ZeroDivisionError):
disk['ratio'] = 0
else:
@@ -485,7 +486,7 @@ def ccache():
ram_keys.remove('oldest')
for key in ram_keys:
total[key] = ram[key] + disk[key]
try:
total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])

View File

@@ -137,9 +137,9 @@
<h4>{{=T("Overview")}}</h4>
<p>{{=T.M("Number of entries: **%s**", total['entries'])}}</p>
{{if total['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses})",
dict(ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>
{{=T("Size of cache:")}}
{{if object_stats:}}
@@ -155,7 +155,7 @@
{{=T.M("Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=total['oldest'][0], min=total['oldest'][1], sec=total['oldest'][2]))}}
</p>
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle();')}}
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="all_keys">
{{=total['keys']}}
</div>
@@ -183,7 +183,7 @@
{{=T.M("RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=ram['oldest'][0], min=ram['oldest'][1], sec=ram['oldest'][2]))}}
</p>
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle();')}}
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="ram_keys">
{{=ram['keys']}}
</div>
@@ -212,7 +212,7 @@
{{=T.M("DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=disk['oldest'][0], min=disk['oldest'][1], sec=disk['oldest'][2]))}}
</p>
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle();')}}
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="disk_keys">
{{=disk['keys']}}
</div>

View File

@@ -445,30 +445,31 @@ def ccache():
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
# get ram stats directly from the cache object
ram_stats = cache.ram.stats[request.application]
ram['hits'] = ram_stats['hit_total'] - ram_stats['misses']
ram['misses'] = ram_stats['misses']
try:
ram['ratio'] = ram['hits'] * 100 / ram_stats['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
try:
ram['ratio'] = ram['hits'] * 100 / value['hit_total']
except (KeyError, ZeroDivisionError):
ram['ratio'] = 0
else:
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
if hp:
ram['bytes'] += hp.iso(value[1]).size
ram['objects'] += hp.iso(value[1]).count
ram['entries'] += 1
if value[0] < ram['oldest']:
ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
for key in cache.disk.storage:
value = cache.disk.storage[key]
if isinstance(value, dict):
disk['hits'] = value['hit_total'] - value['misses']
disk['misses'] = value['misses']
if isinstance(value[1], dict):
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
disk['misses'] = value[1]['misses']
try:
disk['ratio'] = disk['hits'] * 100 / value['hit_total']
disk['ratio'] = disk['hits'] * 100 / value[1]['hit_total']
except (KeyError, ZeroDivisionError):
disk['ratio'] = 0
else:
@@ -485,7 +486,7 @@ def ccache():
ram_keys.remove('oldest')
for key in ram_keys:
total[key] = ram[key] + disk[key]
try:
total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])

View File

@@ -137,9 +137,9 @@
<h4>{{=T("Overview")}}</h4>
<p>{{=T.M("Number of entries: **%s**", total['entries'])}}</p>
{{if total['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses})",
dict(ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=total['ratio'], hits=total['hits'], misses=total['misses']))}}
</p>
<p>
{{=T("Size of cache:")}}
{{if object_stats:}}
@@ -155,7 +155,7 @@
{{=T.M("Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=total['oldest'][0], min=total['oldest'][1], sec=total['oldest'][2]))}}
</p>
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle();')}}
{{=BUTTON(T('Cache Keys'), _onclick='jQuery("#all_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="all_keys">
{{=total['keys']}}
</div>
@@ -183,7 +183,7 @@
{{=T.M("RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=ram['oldest'][0], min=ram['oldest'][1], sec=ram['oldest'][2]))}}
</p>
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle();')}}
{{=BUTTON(T('RAM Cache Keys'), _onclick='jQuery("#ram_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="ram_keys">
{{=ram['keys']}}
</div>
@@ -212,7 +212,7 @@
{{=T.M("DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.",
dict(hours=disk['oldest'][0], min=disk['oldest'][1], sec=disk['oldest'][2]))}}
</p>
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle();')}}
{{=BUTTON(T('Disk Cache Keys'), _onclick='jQuery("#disk_keys").toggle().toggleClass( "hidden" );')}}
<div class="hidden" id="disk_keys">
{{=disk['keys']}}
</div>

View File

@@ -59,6 +59,8 @@ def app_pack(app, request, raise_ex=False, filenames=None):
w2p_pack(filename, apath(app, request), filenames=filenames)
return filename
except Exception, e:
import traceback
print traceback.format_exc()
if raise_ex:
raise
return False
@@ -118,10 +120,9 @@ def app_cleanup(app, request):
r = False
# Remove cache files
path = apath('%s/cache/' % app, request)
CacheOnDisk(folder=path).clear()
path = apath('%s/cache/' % app, request)
if os.path.exists(path):
CacheOnDisk(folder=path).clear()
for f in os.listdir(path):
try:
if f[:1] != '.': recursive_unlink(os.path.join(path, f))

View File

@@ -6,15 +6,17 @@
#
# Given the model
#
# db.define_table("table_name", Field("picture", "upload"), Field("thumbnail", "upload"))
# db.define_table("table_name", Field("picture", "upload"),
# Field("thumbnail", "upload"))
#
# # to resize the picture on upload
# to resize the picture on upload
#
# from images import RESIZE
#
# db.table_name.picture.requires = RESIZE(200, 200)
#
# # to store original image in picture and create a thumbnail in 'thumbnail' field
# to store original image in picture and create a thumbnail
# in 'thumbnail' field
#
# from images import THUMB
# db.table_name.thumbnail.compute = lambda row: THUMB(row.picture, 200, 200)
@@ -24,8 +26,11 @@ from gluon import current
class RESIZE(object):
def __init__(self, nx=160, ny=80, error_message=' image resize'):
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
def __init__(self, nx=160, ny=80, quality=100,
error_message=' image resize'):
(self.nx, self.ny, self.quality, self.error_message) = (
nx, ny, quality, error_message)
def __call__(self, value):
if isinstance(value, str) and len(value) == 0:
@@ -36,7 +41,7 @@ class RESIZE(object):
img = Image.open(value.file)
img.thumbnail((self.nx, self.ny), Image.ANTIALIAS)
s = cStringIO.StringIO()
img.save(s, 'JPEG', quality=100)
img.save(s, 'JPEG', quality=self.quality)
s.seek(0)
value.file = s
except:
@@ -51,7 +56,7 @@ def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
request = current.request
from PIL import Image
import os
img = Image.open(os.path.join(request.folder,'uploads',image))
img = Image.open(os.path.join(request.folder, 'uploads', image))
img.thumbnail((nx, ny), Image.ANTIALIAS)
root, ext = os.path.splitext(image)
thumb = '%s_%s%s' % (root, name, ext)

View File

@@ -13,6 +13,7 @@ Include in your model (eg db.py)::
auth.define_tables(username=True)
from gluon.contrib.login_methods.saml2_auth import Saml2Auth
import os
auth.settings.login_form=Saml2Auth(
config_file = os.path.join(request.folder,'private','sp_conf'),
maps=dict(
@@ -20,10 +21,59 @@ Include in your model (eg db.py)::
email=lambda v: v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0],
user_id=lambda v: v['http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn'][0]))
you must have private/sp_conf.py, the pysaml2 sp configuration file
you must have private/sp_conf.py, the pysaml2 sp configuration file. For example:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT
import os.path
import requests
import tempfile
BASEDIR = os.path.abspath(os.path.dirname(__file__))
# Web2py SP url and application name
HOST = 'http://127.0.0.1:8000'
APP = 'sp'
# To load the IDP metadata...
IDP_METADATA = 'http://127.0.0.1:8088/metadata'
def full_path(local_file):
return os.path.join(BASEDIR, local_file)
CONFIG = {
# your entity id, usually your subdomain plus the url to the metadata view.
'entityid': '%s/%s/default/metadata' % (HOST, APP),
'service': {
'sp' : {
'name': 'MYSP',
'endpoints': {
'assertion_consumer_service': [
('%s/%s/default/user/login' % (HOST, APP), BINDING_HTTP_REDIRECT),
('%s/%s/default/user/login' % (HOST, APP), BINDING_HTTP_POST),
],
},
},
},
# Your private and public key.
'key_file': full_path('pki/mykey.pem'),
'cert_file': full_path('pki/mycert.pem'),
# where the remote metadata is stored
'metadata': {
"remote": [{
"url": IDP_METADATA,
"cert":full_path('pki/mycert.pem')
}]
},
}
"""
from saml2 import BINDING_HTTP_REDIRECT
from saml2 import BINDING_HTTP_REDIRECT, BINDING_HTTP_POST
from saml2.client import Saml2Client
from gluon.utils import web2py_uuid
from gluon import current, redirect, URL
@@ -59,10 +109,13 @@ def saml2_handler(session, request, config_filename = None):
client = Saml2Client(config_file = config_filename)
idps = client.metadata.with_descriptor("idpsso")
entityid = idps.keys()[0]
bindings = [BINDING_HTTP_REDIRECT]
bindings = [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]
binding, destination = client.pick_binding(
"single_sign_on_service", bindings, "idpsso", entity_id=entityid)
binding = BINDING_HTTP_REDIRECT
if request.env.request_method == 'GET':
binding = BINDING_HTTP_REDIRECT
elif request.env.request_method == 'POST':
binding = BINDING_HTTP_POST
if not request.vars.SAMLResponse:
req_id, req = client.create_authn_request(destination, binding=binding)
relay_state = web2py_uuid().replace('-','')

View File

@@ -34,7 +34,7 @@ except ImportError:
class JSONRPCError(RuntimeError):
"Error object for remote procedure call fail"
def __init__(self, code, message, data=None):
value = "%s: %s\n%s" % (code, message, '\n'.join(data))
value = "%s: %s\n%s" % (code, message, '\n'.join(data or ''))
RuntimeError.__init__(self, value)
self.code = code
self.message = message

View File

@@ -646,13 +646,12 @@ class AutocompleteWidget(object):
def __init__(self, request, field, id_field=None, db=None,
orderby=None, limitby=(0, 10), distinct=False,
keyword='_autocomplete_%(tablename)s_%(fieldname)s',
min_length=2, help_fields=None, help_string=None):
min_length=2, help_fields=None, help_string=None, at_beginning = True):
self.help_fields = help_fields or []
self.help_string = help_string
if self.help_fields and not self.help_string:
self.help_string = ' '.join('%%(%s)s' % f.name
for f in self.help_fields)
self.help_string = ' '.join('%%(%s)s' % f.name for f in self.help_fields)
self.request = request
self.keyword = keyword % dict(tablename=field.tablename,
@@ -662,6 +661,7 @@ class AutocompleteWidget(object):
self.limitby = limitby
self.distinct = distinct
self.min_length = min_length
self.at_beginning = at_beginning
self.fields = [field]
if id_field:
self.is_reference = True
@@ -679,8 +679,10 @@ class AutocompleteWidget(object):
field = self.fields[0]
if settings and settings.global_settings.web2py_runtime_gae:
rows = self.db(field.__ge__(self.request.vars[self.keyword]) & field.__lt__(self.request.vars[self.keyword] + u'\ufffd')).select(orderby=self.orderby, limitby=self.limitby, *(self.fields+self.help_fields))
else:
elif self.at_beginning:
rows = self.db(field.like(self.request.vars[self.keyword] + '%', case_sensitive=False)).select(orderby=self.orderby, limitby=self.limitby, distinct=self.distinct, *(self.fields+self.help_fields))
else:
rows = self.db(field.contains(self.request.vars[self.keyword], case_sensitive=False)).select(orderby=self.orderby, limitby=self.limitby, distinct=self.distinct, *(self.fields+self.help_fields))
if rows:
if self.is_reference:
id_field = self.fields[1]

View File

@@ -3739,7 +3739,12 @@ class Auth(object):
basic_allowed, basic_accepted, user = self.basic()
user = user or self.user
if requires_login:
login_required = requires_login
if callable(login_required):
login_required = login_required()
if login_required:
if not user:
if current.request.ajax:
raise HTTP(401, self.messages.ajax_failed_authentication)