diff --git a/.travis.yml b/.travis.yml index 4ee324e3..40a1af6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,17 +6,17 @@ python: - '2.7' - 'pypy' install: - - pip install -e . --use-mirrors + - pip install -e . env: - DB=sqlite:memory - DB=mysql://root:@localhost/test_w2p - DB=postgres://postgres:@localhost/test_w2p before_script: - - if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install --use-mirrors unittest2; fi - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors coverage; fi; - - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install --use-mirrors python-coveralls; fi - - if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi; - - if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi + - if [[ $TRAVIS_PYTHON_VERSION != '2.7' ]]; then pip install unittest2; fi + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install coverage; fi; + - if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then pip install python-coveralls; fi + - if [[ $DB == postgres* ]]; then pip install psycopg2; fi; + - if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install pysqlite; fi - if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi - if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi #Temporal solution to travis issue #155 diff --git a/Makefile b/Makefile index c25c4920..f1020b43 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ src: ### build web2py_src.zip echo '' > NEWINSTALL mv web2py_src.zip web2py_src_old.zip | echo 'no old' - cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/extras/* handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py + cd ..; zip -r web2py/web2py_src.zip web2py/web2py.py web2py/anyserver.py web2py/gluon/*.py web2py/gluon/contrib/* web2py/extras/* web2py/handlers/* web2py/examples/* web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/MANIFEST.in web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py mdp: make src diff --git a/VERSION b/VERSION index 46c0f5e5..612d20bb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.6.0-development+timestamp.2013.08.29.21.31.47 +Version 2.6.0-development+timestamp.2013.09.04.16.32.36 diff --git a/anyserver.py b/anyserver.py index 4ec14b28..cfca6585 100644 --- a/anyserver.py +++ b/anyserver.py @@ -189,7 +189,7 @@ def run(servername, ip, port, softcron=True, logging=False, profiler=None): if logging: application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase, logfilename='httpserver.log', - profilerfilename=profiler) + profiler_dir=profiler) else: application = gluon.main.wsgibase if softcron: @@ -319,8 +319,8 @@ def main(): parser.add_option('-P', '--profiler', default=False, - dest='profiler', - help='profiler filename') + dest='profiler_dir', + help='profiler dir') servers = ', '.join(x for x in dir(Servers) if not x[0] == '_') parser.add_option('-s', '--server', @@ -346,7 +346,7 @@ def main(): print 'starting %s on %s:%s...' % ( options.server, options.ip, options.port) run(options.server, options.ip, options.port, - logging=options.logging, profiler=options.profiler) + logging=options.logging, profiler=options.profiler_dir) if __name__ == '__main__': main() diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 1cdc3dd4..6aefe163 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -1016,8 +1016,9 @@ def design(): privates.sort() # Get all static files + MAXNFILES = 1000 statics = listdir(apath('%s/static/' % app, r=request), '[^\.#].*') - statics = [x.replace('\\', '/') for x in statics] + statics = [x.replace('\\', '/') for x in statics[:MAXNFILES]] statics.sort() # Get all languages diff --git a/applications/admin/languages/de.py b/applications/admin/languages/de.py index d5750418..79ad07db 100644 --- a/applications/admin/languages/de.py +++ b/applications/admin/languages/de.py @@ -248,6 +248,7 @@ 'Next Edit Point': 'nächster Bearbeitungsschritt', 'NO': 'NEIN', 'No databases in this application': 'Keine Datenbank in dieser Anwendung', +'no package selected': 'no package selected', 'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder', 'online designer': 'online designer', 'or alternatively': 'or alternatively', diff --git a/applications/admin/static/js/web2py.js b/applications/admin/static/js/web2py.js index de2a6e25..b38adbbf 100644 --- a/applications/admin/static/js/web2py.js +++ b/applications/admin/static/js/web2py.js @@ -74,28 +74,6 @@ * Ideally all events should be bound to the document, so we can avoid calling * this over and over... all will be bound to the document */ - var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; - var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; - $("input.date", target).each(function () { - $(this).attr('autocomplete', 'off'); - Calendar.setup({ - inputField: this, - ifFormat: date_format, - showsTime: false - }); - }); - $("input.datetime", target).each(function () { - $(this).attr('autocomplete', 'off'); - Calendar.setup({ - inputField: this, - ifFormat: datetime_format, - showsTime: true, - timeFormat: "24" - }); - }); - $("input.time", target).each(function () { - $(this).timeEntry().attr('autocomplete', 'off'); - }); /*adds btn class to buttons*/ $('button', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn'); @@ -181,18 +159,13 @@ /* * This is called once for page * Ideally it should bound all the things that are needed + * and require no dom manipulations */ var doc = $(document); doc.on('click', '.flash', function (e) { var t = $(this); if(t.css('top') == '0px') t.slideUp('slow'); else t.fadeOut(); - /* if I want to display a clickable something - * inside flash, I should not be prevented to follow it - * - * e.preventDefault(); - */ - }); doc.on('keyup', 'input.integer', function () { this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); @@ -205,7 +178,48 @@ if(this.checked) if(!web2py.confirm(confirm_message)) this.checked = false; }); - + var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; + doc.on('click', "input.datetime", function () { + var tformat = $(this).data('w2p_datetime_format') + var active = $(this).data('w2p_datetime'); + var format = (typeof tformat != 'undefined') ? tformat : datetime_format; + if(active === undefined) { + Calendar.setup({ + inputField: this, + ifFormat: format, + showsTime: true, + timeFormat: "24" + }); + $(this).attr('autocomplete', 'off'); + $(this).data('w2p_datetime', 1); + $(this).trigger('click'); + } + }); + var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; + doc.on('click', "input.date", function () { + var tformat = $(this).data('w2p_date_format') + var active = $(this).data('w2p_date'); + var format = (typeof tformat != 'undefined') ? tformat : date_format; + if(active === undefined) { + Calendar.setup({ + inputField: this, + ifFormat: format, + showsTime: false + }); + $(this).data('w2p_date', 1); + $(this).attr('autocomplete', 'off'); + $(this).trigger('click'); + } + }); + doc.on('focus', "input.time", function () { + var active = $(this).data('w2p_time'); + if(active === undefined) { + $(this).timeEntry({ + spinnerImage: '' + }).attr('autocomplete', 'off'); + $(this).data('w2p_time', 1); + } + }); doc.ajaxSuccess(function (e, xhr) { var redirect = xhr.getResponseHeader('web2py-redirect-location'); if(redirect !== null) { diff --git a/applications/examples/controllers/cache_examples.py b/applications/examples/controllers/cache_examples.py index 857833d5..33c9e447 100644 --- a/applications/examples/controllers/cache_examples.py +++ b/applications/examples/controllers/cache_examples.py @@ -1,5 +1,6 @@ import time +response.view = 'cache_examples/generic.html' def cache_in_ram(): """cache the output of the lambda function in ram""" diff --git a/applications/examples/static/js/web2py.js b/applications/examples/static/js/web2py.js index de2a6e25..b38adbbf 100644 --- a/applications/examples/static/js/web2py.js +++ b/applications/examples/static/js/web2py.js @@ -74,28 +74,6 @@ * Ideally all events should be bound to the document, so we can avoid calling * this over and over... all will be bound to the document */ - var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; - var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; - $("input.date", target).each(function () { - $(this).attr('autocomplete', 'off'); - Calendar.setup({ - inputField: this, - ifFormat: date_format, - showsTime: false - }); - }); - $("input.datetime", target).each(function () { - $(this).attr('autocomplete', 'off'); - Calendar.setup({ - inputField: this, - ifFormat: datetime_format, - showsTime: true, - timeFormat: "24" - }); - }); - $("input.time", target).each(function () { - $(this).timeEntry().attr('autocomplete', 'off'); - }); /*adds btn class to buttons*/ $('button', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn'); @@ -181,18 +159,13 @@ /* * This is called once for page * Ideally it should bound all the things that are needed + * and require no dom manipulations */ var doc = $(document); doc.on('click', '.flash', function (e) { var t = $(this); if(t.css('top') == '0px') t.slideUp('slow'); else t.fadeOut(); - /* if I want to display a clickable something - * inside flash, I should not be prevented to follow it - * - * e.preventDefault(); - */ - }); doc.on('keyup', 'input.integer', function () { this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); @@ -205,7 +178,48 @@ if(this.checked) if(!web2py.confirm(confirm_message)) this.checked = false; }); - + var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; + doc.on('click', "input.datetime", function () { + var tformat = $(this).data('w2p_datetime_format') + var active = $(this).data('w2p_datetime'); + var format = (typeof tformat != 'undefined') ? tformat : datetime_format; + if(active === undefined) { + Calendar.setup({ + inputField: this, + ifFormat: format, + showsTime: true, + timeFormat: "24" + }); + $(this).attr('autocomplete', 'off'); + $(this).data('w2p_datetime', 1); + $(this).trigger('click'); + } + }); + var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; + doc.on('click', "input.date", function () { + var tformat = $(this).data('w2p_date_format') + var active = $(this).data('w2p_date'); + var format = (typeof tformat != 'undefined') ? tformat : date_format; + if(active === undefined) { + Calendar.setup({ + inputField: this, + ifFormat: format, + showsTime: false + }); + $(this).data('w2p_date', 1); + $(this).attr('autocomplete', 'off'); + $(this).trigger('click'); + } + }); + doc.on('focus', "input.time", function () { + var active = $(this).data('w2p_time'); + if(active === undefined) { + $(this).timeEntry({ + spinnerImage: '' + }).attr('autocomplete', 'off'); + $(this).data('w2p_time', 1); + } + }); doc.ajaxSuccess(function (e, xhr) { var redirect = xhr.getResponseHeader('web2py-redirect-location'); if(redirect !== null) { diff --git a/applications/examples/views/cache_examples/generic.html b/applications/examples/views/cache_examples/generic.html new file mode 100644 index 00000000..22b41e70 --- /dev/null +++ b/applications/examples/views/cache_examples/generic.html @@ -0,0 +1,3 @@ +{{extend 'layout.html'}} +
[^\]]*)\])?)?)',re.S)
@@ -553,6 +559,53 @@ regex_markmin_escape = re.compile(r"(\\*)(['`:*~\\[\]{}@\$+\-.#\n])")
regex_backslash = re.compile(r"\\(['`:*~\\[\]{}@\$+\-.#\n])")
ttab_in = maketrans("'`:*~\\[]{}@$+-.#\n", '\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05')
ttab_out = maketrans('\x0b\x0c\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x05',"'`:*~\\[]{}@$+-.#\n")
+regex_quote = re.compile('(?P test 1 test 1 span messag in markmin ... link to id
@@ -2531,7 +2539,7 @@ def test():
>>> print form.xml()
>>> session={}
- >>> form=FORM(INPUT(value=\"Hello World\", _name=\"var\", requires=IS_MATCH('^\w+$')))
+ >>> form=FORM(INPUT(value="Hello World", _name="var", requires=IS_MATCH('^\w+$')))
>>> isinstance(form.as_dict(), dict)
True
>>> form.as_dict(flat=True).has_key("vars")
diff --git a/gluon/languages.py b/gluon/languages.py
index c20918b4..c8b746c6 100644
--- a/gluon/languages.py
+++ b/gluon/languages.py
@@ -15,7 +15,6 @@ import re
import sys
import pkgutil
import logging
-import marshal
from cgi import escape
from threading import RLock
diff --git a/gluon/main.py b/gluon/main.py
index 21669463..d3adf7a0 100644
--- a/gluon/main.py
+++ b/gluon/main.py
@@ -14,7 +14,6 @@ Contains:
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
import gc
-import cStringIO
import Cookie
import os
import re
diff --git a/gluon/newcron.py b/gluon/newcron.py
index bc8fb438..00d5e549 100644
--- a/gluon/newcron.py
+++ b/gluon/newcron.py
@@ -117,6 +117,10 @@ class Token(object):
if a cron job started before 60 seconds and did not stop,
a warning is issue "Stale cron.master detected"
"""
+ if sys.platform == 'win32':
+ locktime = 59.5
+ else:
+ locktime = 59.99
if portalocker.LOCK_EX is None:
logger.warning('WEB2PY CRON: Disabled because no file locking')
return None
@@ -128,7 +132,7 @@ class Token(object):
(start, stop) = cPickle.load(self.master)
except:
(start, stop) = (0, 1)
- if startup or self.now - start > 59.99:
+ if startup or self.now - start > locktime:
ret = self.now
if not stop:
# this happens if previous cron job longer than 1 minute
@@ -136,6 +140,7 @@ class Token(object):
logger.debug('WEB2PY CRON: Acquiring lock')
self.master.seek(0)
cPickle.dump((self.now, 0), self.master)
+ self.master.flush()
finally:
portalocker.unlock(self.master)
if not ret:
@@ -246,6 +251,7 @@ class cronlauncher(threading.Thread):
shell=self.shell)
_cron_subprocs.append(proc)
(stdoutdata, stderrdata) = proc.communicate()
+ _cron_subprocs.remove(proc)
if proc.returncode != 0:
logger.warning(
'WEB2PY CRON Call returned code %s:\n%s' %
@@ -303,7 +309,11 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
for task in tasks:
if _cron_stopping:
break
- commands = [sys.executable]
+ if sys.executable.lower().endswith('pythonservice.exe'):
+ _python_exe = os.path.join(sys.exec_prefix, 'python.exe')
+ else:
+ _python_exe = sys.executable
+ commands = [_python_exe]
w2p_path = fileutils.abspath('web2py.py', gluon=True)
if os.path.exists(w2p_path):
commands.append(w2p_path)
diff --git a/gluon/settings.py b/gluon/settings.py
index 92962570..0e87ef9d 100644
--- a/gluon/settings.py
+++ b/gluon/settings.py
@@ -6,7 +6,6 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
import os
import sys
-import socket
import platform
from storage import Storage
diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py
index e3399ac4..48171c9d 100644
--- a/gluon/sqlhtml.py
+++ b/gluon/sqlhtml.py
@@ -2073,14 +2073,13 @@ class SQLFORM(FORM):
order = request.vars.order or ''
if sortable:
if order and not order == 'None':
- if order[:1] == '~':
- sign, rorder = '~', order[1:]
+ otablename, ofieldname = order.split('~')[-1].split('.', 1)
+ sort_field = db[otablename][ofieldname]
+ exception = sort_field.type in ('date', 'datetime', 'time')
+ if exception:
+ orderby = (order[:1] == '~' and sort_field) or ~sort_field
else:
- sign, rorder = '', order
- tablename, fieldname = rorder.split('.', 1)
- orderby = db[tablename][fieldname]
- if sign == '~':
- orderby = ~orderby
+ orderby = (order[:1] == '~' and ~sort_field) or sort_field
expcolumns = [str(f) for f in columns]
if export_type.endswith('with_hidden_cols'):
@@ -2095,7 +2094,8 @@ class SQLFORM(FORM):
try:
dbset = dbset(SQLFORM.build_query(
fields, request.vars.get('keywords', '')))
- rows = dbset.select(cacheable=True, *expcolumns)
+ rows = dbset.select(left=left, orderby=orderby,
+ cacheable=True, *expcolumns)
except Exception, e:
response.flash = T('Internal Error')
rows = []
diff --git a/gluon/template.py b/gluon/template.py
index bd68b157..096d5247 100644
--- a/gluon/template.py
+++ b/gluon/template.py
@@ -845,9 +845,9 @@ def render(content="hello world",
'hello world'
>>> render(content='abc')
'abc'
- >>> render(content='abc\\'')
+ >>> render(content='abc\'')
"abc'"
- >>> render(content='a"\\'bc')
+ >>> render(content='a"\'bc')
'a"\\'bc'
>>> render(content='a\\nbc')
'a\\nbc'
diff --git a/gluon/tools.py b/gluon/tools.py
index 11f02c5e..1834b310 100644
--- a/gluon/tools.py
+++ b/gluon/tools.py
@@ -1304,8 +1304,9 @@ class Auth(object):
else:
raise HTTP(404)
- def navbar(self, mode='Default', action=None, prefix='Welcome',
- referrer_actions=DEFAULT, user_identifier=DEFAULT):
+ def navbar(self, prefix='Welcome', action=None,
+ separators=(' [ ', ' | ', ' ] '), user_identifier=DEFAULT,
+ referrer_actions=DEFAULT, mode='default'):
""" Navbar with support for more templates
This uses some code from the old navbar.
@@ -1490,21 +1491,21 @@ class Auth(object):
'bare': bare
} # Define custom modes.
- try:
+ if mode in options and callable(options[mode]):
options[mode]()
- except KeyError: # KeyError if mode is not in options (do Default)
+ else:
+ s1, s2, s3 = separators
if self.user_id:
- self.bar = SPAN(prefix, user_identifier, '[',
+ self.bar = SPAN(prefix, user_identifier, s1,
Anr(items[0]['name'],
- _href=items[0]['href']), ']',
+ _href=items[0]['href']), s3,
_class='auth_navbar')
else:
- self.bar = SPAN('[', Anr(items[0]['name'],
- _href=items[0]['href']), ']',
+ self.bar = SPAN(s1, Anr(items[0]['name'],
+ _href=items[0]['href']), s3,
_class='auth_navbar')
- del items[0]
for item in items:
- self.bar.insert(-1, ']')
+ self.bar.insert(-1, s2)
self.bar.insert(-1, Anr(item['name'], _href=item['href']))
return self.bar
@@ -1522,7 +1523,8 @@ class Auth(object):
tables,
archive_db=None,
archive_names='%(tablename)s_archive',
- current_record='current_record'):
+ current_record='current_record',
+ current_record_label=None):
"""
to enable full record versioning (including auth tables):
@@ -1549,6 +1551,8 @@ class Auth(object):
does automatically.
"""
+ current_record_label = current_record_label or current.T(
+ current_record.replace('_',' ').title())
for table in tables:
fieldnames = table.fields()
if ('id' in fieldnames and
@@ -1557,7 +1561,8 @@ class Auth(object):
table._enable_record_versioning(
archive_db=archive_db,
archive_name=archive_names,
- current_record=current_record)
+ current_record=current_record,
+ current_record_label=current_record_label)
def define_signature(self):
db = self.db
@@ -2157,12 +2162,17 @@ class Auth(object):
elif 'username' in table_user.fields:
username = 'username'
else:
- username = 'email'
+ username = 'email'
+ settings = self.settings
if 'username' in table_user.fields or \
- not self.settings.login_email_validate:
+ not settings.login_email_validate:
tmpvalidator = IS_NOT_EMPTY(error_message=self.messages.is_empty)
+ if not settings.username_case_sensitive:
+ tmpvalidator = [IS_LOWER(), tmpvalidator]
else:
tmpvalidator = IS_EMAIL(error_message=self.messages.invalid_email)
+ if not settings.email_case_sensitive:
+ tmpvalidator = [IS_LOWER(), tmpvalidator]
old_requires = table_user[username].requires
table_user[username].requires = tmpvalidator
@@ -2170,7 +2180,7 @@ class Auth(object):
response = current.response
session = current.session
- passfield = self.settings.password_field
+ passfield = settings.password_field
try:
table_user[passfield].requires[-1].min_length = 0
except:
@@ -2186,43 +2196,43 @@ class Auth(object):
if next is DEFAULT:
# important for security
- next = self.settings.login_next
+ next = settings.login_next
user_next = snext
if user_next:
external = user_next.split('://')
if external[0].lower() in ['http', 'https', 'ftp']:
host_next = user_next.split('//', 1)[-1].split('/')[0]
- if host_next in self.settings.cas_domains:
+ if host_next in settings.cas_domains:
next = user_next
else:
next = user_next
if onvalidation is DEFAULT:
- onvalidation = self.settings.login_onvalidation
+ onvalidation = settings.login_onvalidation
if onaccept is DEFAULT:
- onaccept = self.settings.login_onaccept
+ onaccept = settings.login_onaccept
if log is DEFAULT:
log = self.messages['login_log']
- onfail = self.settings.login_onfail
+ onfail = settings.login_onfail
user = None # default
# do we use our own login form, or from a central source?
- if self.settings.login_form == self:
+ if settings.login_form == self:
form = SQLFORM(
table_user,
fields=[username, passfield],
hidden=dict(_next=next),
- showid=self.settings.showid,
+ showid=settings.showid,
submit_button=self.messages.login_button,
delete_label=self.messages.delete_label,
- formstyle=self.settings.formstyle,
- separator=self.settings.label_separator
+ formstyle=settings.formstyle,
+ separator=settings.label_separator
)
- if self.settings.remember_me_form:
+ if settings.remember_me_form:
## adds a new input checkbox "remember me for longer"
- if self.settings.formstyle != 'bootstrap':
+ if settings.formstyle != 'bootstrap':
addrow(form, XML(" "),
DIV(XML(" "),
INPUT(_type='checkbox',
@@ -2235,9 +2245,9 @@ class Auth(object):
self.messages.label_remember_me,
_for="auth_user_remember",
)), "",
- self.settings.formstyle,
+ settings.formstyle,
'auth_user_remember__row')
- elif self.settings.formstyle == 'bootstrap':
+ elif settings.formstyle == 'bootstrap':
addrow(form,
"",
LABEL(
@@ -2247,20 +2257,20 @@ class Auth(object):
self.messages.label_remember_me,
_class="checkbox"),
"",
- self.settings.formstyle,
+ settings.formstyle,
'auth_user_remember__row')
- captcha = self.settings.login_captcha or \
- (self.settings.login_captcha != False and self.settings.captcha)
+ captcha = settings.login_captcha or \
+ (settings.login_captcha != False and settings.captcha)
if captcha:
addrow(form, captcha.label, captcha, captcha.comment,
- self.settings.formstyle, 'captcha__row')
+ settings.formstyle, 'captcha__row')
accepted_form = False
if form.accepts(request, session,
formname='login', dbio=False,
onvalidation=onvalidation,
- hideerror=self.settings.hideerror):
+ hideerror=settings.hideerror):
accepted_form = True
# check for username in db
@@ -2282,36 +2292,36 @@ class Auth(object):
# try alternate logins 1st as these have the
# current version of the password
user = None
- for login_method in self.settings.login_methods:
+ for login_method in settings.login_methods:
if login_method != self and \
login_method(request.vars[username],
request.vars[passfield]):
- if not self in self.settings.login_methods:
+ if not self in settings.login_methods:
# do not store password in db
form.vars[passfield] = None
user = self.get_or_create_user(
- form.vars, self.settings.update_fields)
+ form.vars, settings.update_fields)
break
if not user:
# alternates have failed, maybe because service inaccessible
- if self.settings.login_methods[0] == self:
+ if settings.login_methods[0] == self:
# try logging in locally using cached credentials
if form.vars.get(passfield, '') == temp_user[passfield]:
# success
user = temp_user
else:
# user not in db
- if not self.settings.alternate_requires_registration:
+ if not settings.alternate_requires_registration:
# we're allowed to auto-register users from external systems
- for login_method in self.settings.login_methods:
+ for login_method in settings.login_methods:
if login_method != self and \
login_method(request.vars[username],
request.vars[passfield]):
- if not self in self.settings.login_methods:
+ if not self in settings.login_methods:
# do not store password in db
form.vars[passfield] = None
user = self.get_or_create_user(
- form.vars, self.settings.update_fields)
+ form.vars, settings.update_fields)
break
if not user:
self.log_event(self.messages['login_failed_log'],
@@ -2321,25 +2331,25 @@ class Auth(object):
callback(onfail, None)
redirect(
self.url(args=request.args, vars=request.get_vars),
- client_side=self.settings.client_side)
+ client_side=settings.client_side)
else:
# use a central authentication server
- cas = self.settings.login_form
+ cas = settings.login_form
cas_user = cas.get_user()
if cas_user:
cas_user[passfield] = None
user = self.get_or_create_user(
table_user._filter_fields(cas_user),
- self.settings.update_fields)
+ settings.update_fields)
elif hasattr(cas, 'login_form'):
return cas.login_form()
else:
# we need to pass through login again before going on
- next = self.url(self.settings.function, args='login')
+ next = self.url(settings.function, args='login')
redirect(cas.login_url(next),
- client_side=self.settings.client_side)
+ client_side=settings.client_side)
# process authenticated users
if user:
@@ -2349,20 +2359,20 @@ class Auth(object):
self.login_user(user)
session.auth.expiration = \
request.vars.get('remember', False) and \
- self.settings.long_expiration or \
- self.settings.expiration
+ settings.long_expiration or \
+ settings.expiration
session.auth.remember = 'remember' in request.vars
self.log_event(log, user)
session.flash = self.messages.logged_in
# how to continue
- if self.settings.login_form == self:
+ if settings.login_form == self:
if accepted_form:
callback(onaccept, form)
if next == session._auth_next:
session._auth_next = None
next = replace_id(next, form)
- redirect(next, client_side=self.settings.client_side)
+ redirect(next, client_side=settings.client_side)
table_user[username].requires = old_requires
return form
@@ -2371,7 +2381,7 @@ class Auth(object):
if next == session._auth_next:
del session._auth_next
- redirect(next, client_side=self.settings.client_side)
+ redirect(next, client_side=settings.client_side)
def logout(self, next=DEFAULT, onlogout=DEFAULT, log=DEFAULT):
"""
@@ -3584,7 +3594,8 @@ class Auth(object):
templates=None,
migrate=True,
controller=None,
- function=None):
+ function=None,
+ force_render=False):
if controller and function: resolve = False
@@ -3608,7 +3619,7 @@ class Auth(object):
if resolve:
action = str(current.request.args(0)).startswith("_")
if slug and not action:
- wiki = self._wiki.read(slug)
+ wiki = self._wiki.read(slug,force_render)
if isinstance(wiki, dict) and wiki.has_key('content'):
# We don't want to return a dict object, just the wiki
wiki = wiki['content']
@@ -5189,13 +5200,18 @@ class Wiki(object):
controller, function, args = items[0], items[1], items[2:]
return LOAD(controller, function, args=args, ajax=True).xml()
- def get_render(self):
+ def get_renderer(self):
if isinstance(self.settings.render, basestring):
r = getattr(self, "%s_render" % self.settings.render)
elif callable(self.settings.render):
r = self.settings.render
+ elif isinstance(self.settings.render, dict):
+ return lambda page: self.settings.render.get(page.render,
+ getattr(self,
+ "%s_render" % (page.render or 'markmin')))(page)
else:
- raise ValueError("Invalid render type %s" % type(render))
+ raise ValueError(
+ "Invalid render type %s" % type(self.settings.render))
return r
def __init__(self, auth, env=None, render='markmin',
@@ -5206,7 +5222,24 @@ class Wiki(object):
settings = self.settings = auth.settings.wiki
- # render: "markmin", "html", ..., a b c d e f 1 2 3