Merge github.com:web2py/web2py

This commit is contained in:
Michele Comitini
2012-09-04 22:37:20 +02:00
17 changed files with 244 additions and 151 deletions
+3 -3
View File
@@ -29,14 +29,14 @@ update:
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
echo "remember that pymysql was tweaked"
src:
echo 'Version 2.0.6 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION
echo 'Version 2.0.7 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION
### rm -f all junk files
make clean
### clean up baisc apps
rm -f routes.py
rm -f applications/*/sessions/*
rm -f applications/*/sessions/*
rm -f applications/*/errors/* | echo 'too many files'
rm -f applications/*/cache/*
rm -f applications/*/cache/*
rm -f applications/admin/databases/*
rm -f applications/welcome/databases/*
rm -f applications/examples/databases/*
+1 -1
View File
@@ -1 +1 @@
Version 2.0.6 (2012-09-02 12:26:12) stable
Version 2.0.7 (2012-09-04 15:32:23) stable
+5 -1
View File
@@ -244,11 +244,15 @@ def site():
DIV(T('Unable to download app because:'),PRE(str(e)))
redirect(URL(r=request))
fname = form_update.vars.url
elif form_update.accepted and form_update.vars.file:
fname = request.vars.file.filename
f = request.vars.file.file
else:
session.flash = 'No file uploaded and no URL specified'
redirect(URL(r=request))
if f:
appname = cleanpath(form_update.vars.name)
installed = app_install(appname, f,
+4
View File
@@ -16,6 +16,8 @@
'are not used yet': 'are not used yet',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
'arguments': 'arguments',
'at char %s': 'at char %s',
'at line %s': 'at line %s',
'back': '<<back',
'can be a git repo': 'can be a git repo',
'Change admin password': 'Change admin password',
@@ -58,6 +60,7 @@
'exposes': 'exposes',
'exposes:': 'exposes:',
'extends': 'extends',
'failed to compile file because:': 'failed to compile file because:',
'file does not exist': 'file does not exist',
'file saved on %s': 'file saved on %s',
'filter': 'filter',
@@ -87,6 +90,7 @@
'modules': 'modules',
'New application wizard': 'New application wizard',
'New simple application': 'New simple application',
'online designer': 'online designer',
'Overwrite installed app': 'Overwrite installed app',
'Pack all': 'Pack all',
'Peeking at file': 'Peeking at file',
+5 -5
View File
@@ -55,7 +55,7 @@ function doClickSave() {
prepareDataForSave('saved_on', jQuery("input[name='saved_on']").val()),
prepareDataForSave('saved_on', jQuery("input[name='saved_on']").val()),
prepareDataForSave('from_ajax','true')));
// console.info(area.textarea.value);
// console.info(area.textarea.value);
jQuery("input[name='saved_on']").attr('style','background-color:yellow');
jQuery("input[name='saved_on']").val('saving now...')
jQuery.ajax({
@@ -65,13 +65,13 @@ function doClickSave() {
dataType: "json",
data: dataForPost[0],
timeout: 5000,
beforeSend: function(xhr) {
beforeSend: function(xhr) {
xhr.setRequestHeader('web2py-component-location',document.location);
xhr.setRequestHeader('web2py-component-element','doClickSave');},
success: function(json,text,xhr){
success: function(json,text,xhr){
// show flash message (if any)
var flash=xhr.getResponseHeader('web2py-component-flash');
// show flash message (if any)
var flash=xhr.getResponseHeader('web2py-component-flash');
if (flash) jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
else jQuery('.flash').hide();
@@ -2,7 +2,9 @@
<h2>web2py&trade; {{=T('Web Framework')}}</h2>
<h3>{{=T('Login to the Administrative Interface')}}</h3>
<div>
{{if request.is_https or request.is_local:}}
<div class="form">
<form action="{{=URL(r=request)}}" method="post">
<div><input type="hidden" name="send" value="{{=send}}"/></div>
<table>
@@ -11,4 +13,6 @@
</table>
</form>
</div>
{{else:}}
<p class="help">{{=T('ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.')}}</p>
{{pass}}
File diff suppressed because one or more lines are too long
+5
View File
@@ -60,6 +60,7 @@
'edit profile': 'modifica profilo',
'Edit This App': 'Modifica questa applicazione',
'Email and SMS': 'Email and SMS',
'Email non valida': 'Email non valida',
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g',
'Errors': 'Errors',
'export as csv file': 'esporta come file CSV',
@@ -95,6 +96,7 @@
'Layouts': 'Layouts',
'Live Chat': 'Live Chat',
'Logged in': 'Logged in',
'Logged out': 'Logged out',
'login': 'accesso',
'Login': 'Login',
'logout': 'uscita',
@@ -113,6 +115,7 @@
'new record inserted': 'nuovo record inserito',
'next 100 rows': 'prossime 100 righe',
'No databases in this application': 'Nessun database presente in questa applicazione',
'Non può essere vuoto': 'Non può essere vuoto',
'not authorized': 'non autorizzato',
'Object or table name': 'Object or table name',
'Online examples': 'Vedere gli esempi',
@@ -166,12 +169,14 @@
'This App': 'This App',
'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)",
'Timestamp': 'Ora (timestamp)',
'too short': 'too short',
'Twitter': 'Twitter',
'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).',
'User %(id)s Logged-in': 'User %(id)s Logged-in',
'User %(id)s Logged-out': 'User %(id)s Logged-out',
'User %(id)s Registered': 'User %(id)s Registered',
'User ID': 'ID Utente',
'Verify Password': 'Verify Password',
+2 -9
View File
@@ -15,9 +15,9 @@ td,th {text-align:left; padding:2px 5px 2px 5px}
th {vertical-align:middle; border-right:1px solid white}
td {vertical-align:top}
form table tr td label {text-align:left}
p,table,ol,ul {padding:0; margin: 0.5em 0}
p,table,ol,ul {padding:0; margin: 0.75em 0}
p {text-align:justify}
ol, ul {list-style-position:inside}
ol, ul {list-style-position:outside; margin-left:2em}
li {margin-bottom:0.5em}
span,input,select,textarea,button,label,a {display:inline}
img {border:0}
@@ -39,13 +39,6 @@ input[type=text],input[type=password],select{width:300px; margin-right:5px}
/* Sticky footer begin */
.wrapper {
min-height:100%;
height:auto !important;
height:100%;
margin:0 auto -8em; /* set last value to footer height plus footer vertical padding */
}
.main {
padding:20px 0 50px 0;
}
+1 -1
View File
@@ -129,7 +129,7 @@
</section><!--/main-->
<!-- Footer ================================================== -->
<footer class="footer">
<footer class="footer" id="footer">
{{block footer}} <!-- this is default footer -->
<div class="footer-content">
<div class="copyright pull-left">{{=T('Copyright')}} &#169; {{=request.now.year}}</div>
+3
View File
@@ -92,11 +92,14 @@ _TEST()
"""
CACHED_REGEXES = {}
CACHED_REGEXES_MAX_SIZE = 1000
def re_compile(regex):
try:
return CACHED_REGEXES[regex]
except KeyError:
if len(CACHED_REGEXES) >= CACHED_REGEXES_MAX_SIZE:
CACHED_REGEXES.clear()
compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
return compiled_regex
+19 -16
View File
@@ -544,6 +544,7 @@ regex_list=re.compile('^(?:(?:(#{1,6})|(?:(\.+|\++|\-+)(\.)?))\s+)?(.*)$')
regex_bq_headline=re.compile('^(?:(\.+|\++|\-+)(\.)?\s+)?(-{3}-*)$')
regex_tq=re.compile('^(-{3}-*)(?::(?P<c>[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P<p>[a-zA-Z][_a-zA-Z\-\d]*)\])?)?$')
regex_proto = re.compile(r'(?<!["\w>/=])(?P<p>\w+):(?P<k>\w+://[\w\d\-+=?%&/:.]+)', re.M)
regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[^\s\'\"\]\}\)]+)',re.M)
regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=?%&/:.]+)',re.M)
regex_link=re.compile(r'('+LINK+r')|\[\[(?P<s>.+?)\]\]')
regex_link_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+\[(?P<a>.+?)\])?(?:\s+(?P<k>\S+))?(?:\s+(?P<p>popup))?\s*$')
@@ -620,7 +621,7 @@ def render(text,
- class_prefix is a prefix for ALL classes in markmin text. E.g. if class_prefix='my_'
then for ``test``:cls class will be changed to "my_cls" (default value is '')
- id_prefix is prefix for ALL ids in markmin text (default value is 'markmin_'). E.g.:
-- [[id]] will be converted to <a name="markmin_id"></a>
-- [[id]] will be converted to <div class="anchor" id="markmin_id"></div>
-- [[link #id]] will be converted to <a href="#markmin_id">link</a>
-- ``test``:cls[id] will be converted to <code class="cls" id="markmin_id">test</code>
@@ -832,22 +833,11 @@ def render(text,
# this is experimental @{function/args}
# turns into a digitally signed URL
def u1(match,URL=URL):
a,c,f,args = match.group('a','c','f','args')
a,c,f,args = match.group('a','c','f','args')
return URL(a=a or None,c=c or None,f = f or None,
args=args.split('/'), scheme=True, host=True)
text = regex_URL.sub(u1,text)
if environment:
def u2(match, environment=environment):
f = environment.get(match.group('a'), match.group(0))
if callable(f):
try:
f = f(match.group('b'))
except Exception, e:
f = 'ERROR: %s' % e
return str(f)
text = regex_env.sub(u2, text)
if latex == 'google':
text = regex_dd.sub('``\g<latex>``:latex ', text)
@@ -1314,12 +1304,24 @@ def render(text,
return '<code%s%s>%s</code>' % (cls, id, escape(code[beg:end]))
text = regex_expand_meta.sub(expand_meta, text)
text = text.translate(ttab_out)
if environment:
def u2(match, environment=environment):
f = environment.get(match.group('a'), match.group(0))
if callable(f):
try:
f = f(match.group('b'))
except Exception, e:
f = 'ERROR: %s' % e
return str(f)
text = regex_env.sub(u2, text)
return text
def markmin2html(text, extra={}, allowed={}, sep='p',
def markmin2html(text, extra={}, allowed={}, sep='p',
autolinks='default', protolinks='default',
class_prefix='', id_prefix='markmin_', pretty_print=False):
return render(text, extra, allowed, sep,
class_prefix='', id_prefix='markmin_', pretty_print=False):
return render(text, extra, allowed, sep,
autolinks=autolinks, protolinks=protolinks,
class_prefix=class_prefix, id_prefix=id_prefix,
pretty_print=pretty_print)
@@ -1403,3 +1405,4 @@ if __name__ == '__main__':
print " file.markmin [file.css] - process file.markmin + built in file.css (optional)"
print " file.markmin [@path_to/css] - process file.markmin + link path_to/css (optional)"
run_doctests()
+36 -19
View File
@@ -2379,9 +2379,9 @@ class PostgreSQLAdapter(BaseAdapter):
if self.driver_name == 'psycopg2':
return psycopg2_adapt(obj).getquoted()
elif self.driver_name == 'pg8000':
return str(obj).replace("%","%%").replace("'","''")
return "'%s'" % str(obj).replace("%","%%").replace("'","''")
else:
return str(obj).replace("'","''")
return "'%s'" % str(obj).replace("'","''")
def sequence_name(self,table):
return '%s_id_Seq' % table
@@ -6460,7 +6460,7 @@ class Row(object):
return '<Row %s>' % self.__dict__
def __int__(self):
return dict.__getitem__(self,'id')
return object.__getattribute__(self,'id')
def __eq__(self,other):
try:
@@ -7198,21 +7198,34 @@ def index():
[{field1: value1, field2: value2}, {field1: value1b, field2: value2b}]
Added 2012-08-24 "fields" optional argument. If not None, the
results cursor returned by the DB driver will be converted to a
DAL Rows object using the db._adapter.parse() method. This requires
specifying the "fields" argument as a list of DAL Field objects
that match the fields returned from the DB. The Field objects should
be part of one or more Table objects defined on the DAL object.
The "fields" list can include one or more DAL Table objects in addition
to or instead of including Field objects, or it can be just a single
table (not in a list). In that case, the Field objects will be
extracted from the table(s).
Added 2012-08-24 "fields" and "colnames" optional arguments. If either
is provided, the results cursor returned by the DB driver will be
converted to a DAL Rows object using the db._adapter.parse() method.
The "fields" argument is a list of DAL Field objects that match the
fields returned from the DB. The Field objects should be part of one or
more Table objects defined on the DAL object. The "fields" list can
include one or more DAL Table objects in addition to or instead of
including Field objects, or it can be just a single table (not in a
list). In that case, the Field objects will be extracted from the
table(s).
The field names will be extracted from the Field objects, or optionally,
a list of field names can be provided (in tablename.fieldname format)
via the "colnames" argument. Note, the fields and colnames must be in
the same order as the fields in the results cursor returned from the DB.
Instead of specifying the "fields" argument, the "colnames" argument
can be specified as a list of field names in tablename.fieldname format.
Again, these should represent tables and fields defined on the DAL
object.
It is also possible to specify both "fields" and the associated
"colnames". In that case, "fields" can also include DAL Expression
objects in addition to Field objects. For Field objects in "fields",
the associated "colnames" must still be in tablename.fieldname format.
For Expression objects in "fields", the associated "colnames" can
be any arbitrary labels.
Note, the DAL Table objects referred to by "fields" or "colnames" can
be dummy tables and do not have to represent any real tables in the
database. Also, note that the "fields" and "colnames" must be in the
same order as the fields in the results cursor returned from the DB.
"""
adapter = self._adapter
if placeholders:
@@ -7233,8 +7246,12 @@ def index():
# convert the list for each row into a dictionary so it's
# easier to work with. row['field_name'] rather than row[0]
return [dict(zip(fields,row)) for row in data]
data = adapter.cursor.fetchall()
if fields:
try:
data = adapter.cursor.fetchall()
except:
return None
if fields or colnames:
fields = [] if fields is None else fields
if not isinstance(fields, list):
fields = [fields]
extracted_fields = []
+17 -34
View File
@@ -91,7 +91,8 @@ from validators import CRYPT
from cache import Cache
from html import URL, xmlescape
from utils import is_valid_ip_address
from rewrite import load, url_in, thread as rwthread, try_rewrite_on_error
from rewrite import load, url_in, thread as rwthread, \
try_rewrite_on_error, fixup_missing_path_info
import newcron
__all__ = ['wsgibase', 'save_password', 'appfactory', 'HttpServer']
@@ -148,10 +149,11 @@ def copystream_progress(request, chunk_size= 10**5):
and stores progress upload status in cache.ram
X-Progress-ID:length and X-Progress-ID:uploaded
"""
if not request.env.content_length:
env = request.env
if not env.content_length:
return cStringIO.StringIO()
source = request.env.wsgi_input
size = int(request.env.content_length)
source = env.wsgi_input
size = int(env.content_length)
dest = tempfile.TemporaryFile()
if not 'X-Progress-ID' in request.vars:
copystream(source, dest, size, chunk_size)
@@ -274,7 +276,8 @@ def environ_aux(environ,request):
def parse_get_post_vars(request, environ):
# always parse variables in URL for GET, POST, PUT, DELETE, etc. in get_vars
dget = cgi.parse_qsl(request.env.query_string or '', keep_blank_values=1)
env = request.env
dget = cgi.parse_qsl(env.query_string or '', keep_blank_values=1)
for (key, value) in dget:
if key in request.get_vars:
if isinstance(request.get_vars[key], list):
@@ -290,7 +293,7 @@ def parse_get_post_vars(request, environ):
request.body = body = copystream_progress(request)
except IOError:
raise HTTP(400,"Bad Request - HTTP body is incomplete")
if (body and request.env.request_method in ('POST', 'PUT', 'BOTH')):
if (body and env.request_method in ('POST', 'PUT', 'BOTH')):
dpost = cgi.FieldStorage(fp=body,environ=environ,keep_blank_values=1)
# The same detection used by FieldStorage to detect multipart POSTs
is_multipart = dpost.type[:10] == 'multipart/'
@@ -382,26 +385,7 @@ def wsgibase(environ, responder):
# serve file if static
# ##################################################
eget = environ.get
if not eget('PATH_INFO') and eget('REQUEST_URI'):
# for fcgi, get path_info and
# query_string from request_uri
items = environ['REQUEST_URI'].split('?')
environ['PATH_INFO'] = items[0]
if len(items) > 1:
environ['QUERY_STRING'] = items[1]
else:
environ['QUERY_STRING'] = ''
elif not eget('REQUEST_URI'):
if eget('QUERY_STRING'):
environ['REQUEST_URI'] = eget('PATH_INFO') + '?' + eget('QUERY_STRING')
else:
environ['REQUEST_URI'] = eget('PATH_INFO')
if not eget('HTTP_HOST'):
environ['HTTP_HOST'] = \
eget('SERVER_NAME') + ':' + eget('SERVER_PORT')
fixup_missing_path_info(environ)
(static_file, environ) = url_in(request, environ)
if static_file:
@@ -438,6 +422,7 @@ def wsgibase(environ, responder):
is_https = env.wsgi_url_scheme \
in ['https', 'HTTPS'] or env.https=='on')
request.uuid = request.compute_uuid() # requires client
request.url = environ['PATH_INFO']
# ##################################################
# access the requested application
@@ -460,9 +445,6 @@ def wsgibase(environ, responder):
elif not request.is_local and \
exists(pjoin(request.folder,'DISABLED')):
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
request.url = URL(r=request,
args=request.args,
extension=request.raw_extension)
# ##################################################
# build missing folders
@@ -492,9 +474,9 @@ def wsgibase(environ, responder):
# load cookies
# ##################################################
if request.env.http_cookie:
if env.http_cookie:
try:
request.cookies.load(request.env.http_cookie)
request.cookies.load(env.http_cookie)
except Cookie.CookieError, e:
pass # invalid cookies
@@ -502,7 +484,8 @@ def wsgibase(environ, responder):
# try load session or create new session file
# ##################################################
session.connect(request, response)
if not env.web2py_disable_session:
session.connect(request, response)
# ##################################################
# set no-cache headers
@@ -558,7 +541,7 @@ def wsgibase(environ, responder):
# if session not in db try store session on filesystem
# this must be done after trying to commit database!
# ##################################################
session._try_store_on_disk(request, response)
# ##################################################
@@ -566,7 +549,7 @@ def wsgibase(environ, responder):
# ##################################################
if request.cid:
rheaders = response.headers
rheaders = http_response.headers
if response.flash and \
not 'web2py-component-flash' in rheaders:
rheaders['web2py-component-flash'] = \
+28 -6
View File
@@ -135,6 +135,27 @@ ROUTER_BASE_KEYS = set(
# filter_err: helper for doctest & unittest
# regex_filter_out: doctest
def fixup_missing_path_info(environ):
eget = environ.get
path_info = eget('PATH_INFO')
request_uri = eget('REQUEST_URI')
if not path_info and request_uri:
# for fcgi, get path_info and
# query_string from request_uri
items = request_uri.split('?')
path_info = environ['PATH_INFO'] = items[0]
environ['QUERY_STRING'] = items[1] if len(items) > 1 else ''
elif not request_uri:
query_string = eget('QUERY_STRING')
if query_string:
environ['REQUEST_URI'] = '%s?%s' % (path_info,query_string)
else:
environ['REQUEST_URI'] = path_info
if not eget('HTTP_HOST'):
environ['HTTP_HOST'] = \
'%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT'))
def url_in(request, environ):
"parse and rewrite incoming URL"
if routers:
@@ -287,8 +308,8 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
for sym in ('routes_app', 'routes_in', 'routes_out'):
if sym in symbols:
for (k, v) in symbols[sym]:
p[sym].append(compile_regex(k, v))
for items in symbols[sym]:
p[sym].append(compile_regex(*items))
for sym in ('routes_onerror', 'routes_apps_raw',
'error_handler','error_message', 'error_message_ticket',
'default_application','default_controller', 'default_function',
@@ -350,7 +371,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
log_rewrite('URL rewrite is on. configuration in %s' % path)
def compile_regex(k, v):
def compile_regex(k, v, env=None):
"""
Preprocess and compile the regular expressions in routes_app/in/out
The resulting regex will match a pattern of the form:
@@ -384,7 +405,7 @@ def compile_regex(k, v):
# same for replacement pattern, but with \g
for item in regex_at.findall(v):
v = v.replace(item, r'\g<%s>' % item[1:])
return (re.compile(k, re.DOTALL), v)
return (re.compile(k, re.DOTALL), v, env or {})
def load_routers(all_apps):
"load-time post-processing of routers"
@@ -498,8 +519,9 @@ def regex_uri(e, regexes, tag, default=None):
(e.get('REMOTE_ADDR','localhost'),
e.get('wsgi.url_scheme', 'http').lower(), host,
e.get('REQUEST_METHOD', 'get').lower(), path)
for (regex, value) in regexes:
for (regex, value, custom_env) in regexes:
if regex.match(key):
e.update(custom_env)
rewritten = regex.sub(value, key)
log_rewrite('%s: [%s] [%s] -> %s' % (tag, key, value, rewritten))
return rewritten
@@ -687,7 +709,7 @@ def regex_filter_out(url, e=None):
e.get('request_method', 'get').lower(), items[0])
else:
items[0] = ':http://localhost:get %s' % items[0]
for (regex, value) in thread.routes.routes_out:
for (regex, value, tmp) in thread.routes.routes_out:
if regex.match(items[0]):
rewritten = '?'.join([regex.sub(value, items[0])] + items[1:])
log_rewrite('routes_out: [%s] -> %s' % (url, rewritten))
+8 -7
View File
@@ -702,17 +702,17 @@ def formstyle_bootstrap(form, fields):
_submit = False
if isinstance(controls, INPUT):
controls['_class'] = 'input-xlarge'
controls.add_class('input-xlarge')
if controls['_type'] == 'submit':
# flag submit button
_submit = True
controls['_class'] = 'btn btn-primary'
if isinstance(controls, SELECT):
controls['_class'] = 'input-xlarge'
controls.add_class('input-xlarge')
if isinstance(controls, TEXTAREA):
controls['_class'] = 'input-xlarge'
controls.add_class('input-xlarge')
if isinstance(label, LABEL):
label['_class'] = 'control-label'
@@ -1376,8 +1376,8 @@ class SQLFORM(FORM):
# this should never happen but seems to happen to some
del fields['delete_this_record']
for field in self.table:
if not field.name in fields and field.writable==False \
and field.update is None:
if not field.name in fields and field.writable is False \
and field.update is None and field.compute is None:
if record_id and self.record:
fields[field.name] = self.record[field.name]
elif not self.table[field.name].default is None:
@@ -1663,8 +1663,9 @@ class SQLFORM(FORM):
session.flash = T('not authorized')
redirect(referrer)
def gridbutton(buttonclass='buttonadd',buttontext='Add',
buttonurl=url(args=[]),callback=None,delete=None,trap=True):
def gridbutton(buttonclass='buttonadd', buttontext='Add',
buttonurl=url(args=[]), callback=None,
delete=None, trap=True):
if showbuttontext:
if callback:
return A(SPAN(_class=ui.get(buttonclass)),
+14 -11
View File
@@ -839,6 +839,7 @@ class Auth(object):
table_event = None,
table_cas = None,
showid = False,
use_username = False,
login_email_validate = True,
login_userfield = None,
logout_onlogout = None,
@@ -1060,7 +1061,6 @@ class Auth(object):
request = current.request
session = current.session
auth = session.auth
self.use_username = None # None means postpone detection
self.user_groups = auth and auth.user_groups or {}
if auth and auth.last_visit and auth.last_visit + \
datetime.timedelta(days=0, seconds=auth.expiration) > request.now:
@@ -1251,21 +1251,21 @@ class Auth(object):
else:
login = A(T('Login'), _href=href('login'))
register = A(T('Register'), _href=href('register'))
retrieve_username = A(T('Forgot username?'), _href=href('retrieve_username'))
lost_password = A(T('Lost password?'), _href=href('request_reset_password'))
retrieve_username = A(
T('Forgot username?'), _href=href('retrieve_username'))
lost_password = A(
T('Lost password?'), _href=href('request_reset_password'))
bar = SPAN(s1, login, s3, _class='auth_navbar')
if not 'register' in self.settings.actions_disabled:
bar.insert(-1, s2)
bar.insert(-1, register)
if self.use_username is None:
# should always be false if auth.define_tables() is called
self.use_username = 'username' in self.table_user().fields
if self.use_username and \
not 'retrieve_username' in self.settings.actions_disabled:
if self.settings.use_username and not 'retrieve_username' \
in self.settings.actions_disabled:
bar.insert(-1, s2)
bar.insert(-1, retrieve_username)
if not 'request_reset_password' in self.settings.actions_disabled:
if not 'request_reset_password' \
in self.settings.actions_disabled:
bar.insert(-1, s2)
bar.insert(-1, lost_password)
return bar
@@ -1357,7 +1357,7 @@ class Auth(object):
writable=False,readable=False,
label=T('Modified By')))
def define_tables(self, username=False, signature=None,
def define_tables(self, username=None, signature=None,
migrate=True, fake_migrate=False):
"""
to be called unless tables are defined manually
@@ -1375,7 +1375,10 @@ class Auth(object):
db = self.db
settings = self.settings
self.use_username = username
if username is None:
username = settings.use_username
else:
settings.use_username = username
if not self.signature:
self.define_signature()
if signature==True: