Compare commits

..
41 Commits
Author SHA1 Message Date
mdipierro 2802e29945 2.9.5 2014-03-15 21:25:04 -05:00
mdipierro e9eb8cbfc7 Merge pull request #403 from pyner/master
Update default.py
2014-03-15 21:09:44 -05:00
mdipierro d343d8380c Merge pull request #401 from niphlod/docs/n-p
sphinx-compatible docstrings
2014-03-15 21:08:05 -05:00
mdipierro 02906fa39f Merge pull request #400 from timrichardson/issues/1898s
better error handling in jsonrpc requests
2014-03-15 21:07:31 -05:00
mdipierro c1e797fe24 removed gluon/contrib/translitcodec because of backward compatibility issues and because it breaks Pypy 2014-03-15 10:54:48 -05:00
Massimo d7a42d8445 fixed problem with groups 2014-03-14 09:00:19 -05:00
samuel bonilla 4f82bed52e Update default.py
new alert: it's a plugin is not an application
2014-03-13 20:04:00 -05:00
mdipierro 84365f6721 fixed problem with circular imputs on GAE? 2014-03-13 08:41:31 -05:00
niphlod e209dcc8d3 sphinx-compatible docstrings 2014-03-12 22:03:42 +01:00
Tim Richardson cd15a0f983 better error handling in jsonrpc requests 2014-03-13 07:29:42 +11:00
mdipierro eb435e785c Merge pull request #399 from timrichardson/issues/1898
jsonrpc2 requests need mandatory key/pair jsonrpc:'2.0'
2014-03-12 13:39:05 -05:00
mdipierro f03e521120 Merge pull request #398 from niphlod/docs/h-m
sphinx-compatible docstrings
2014-03-12 13:37:42 -05:00
mdipierro 883909af4b reverted s.encode('translit/long') which breaks Pypy 2014-03-12 13:31:09 -05:00
Tim Richardson 13d66433e7 jsonrpc2 requests need mandatory key/pair jsonrpc:'2.0' 2014-03-12 22:38:29 +11:00
niphlod c44cfcb27c sphinx-compatible docstrings 2014-03-11 22:18:33 +01:00
mdipierro c14e5cbae5 Merge pull request #396 from niphlod/docs/html
sphinx-compatible docstrings
2014-03-11 10:51:31 -05:00
niphlod f09e0b4205 sphinx-compatible docstrings 2014-03-10 22:21:33 +01:00
mdipierro ad72cea9a6 fixed issue 1891:Remove response.meta.description from menu.py in welcome app, thanks sourcingp 2014-03-09 17:29:41 -05:00
mdipierro c289bc4239 fixed issue 1893:backwards compatibility problem with grid parameter exportclasses, thanks mweissen 2014-03-09 17:26:40 -05:00
mdipierro 733fa01b7e fixed issue 1894:represent requires 2 arguments in dal.py, thanks mweissen 2014-03-09 17:22:39 -05:00
mdipierro 4d03460944 fixed startup message, thanks mweissen 2014-03-09 17:19:03 -05:00
mdipierro b3ba5d9eaa Merge pull request #393 from niphlod/fix/tests
fix travis.yml to fetch the latest release of gae
2014-03-09 09:11:03 -05:00
mdipierro 9e8ef3585e fixed problem with login bare when registration_key is None 2014-03-09 08:55:22 -05:00
niphlod 5c9cf44720 fix travis.yml to fetch the latest release of gae (needs manual update whenever a version changes) 2014-03-09 14:11:53 +01:00
mdipierro 05a0d19ee9 cleanup 2014-03-08 14:40:34 -06:00
mdipierro d38adbe6ca Merge pull request #392 from apa-1/master
Fix for session unique_key query
2014-03-08 14:32:27 -06:00
mdipierro 871981cec7 Merge pull request #391 from michele-comitini/oauth_content-type
oauth strip encoding part in content-type response from server.
2014-03-08 14:31:32 -06:00
mdipierro 8a54001e11 Merge pull request #390 from dokime7/master
Fix DAL ADD method
2014-03-08 14:30:42 -06:00
mdipierro 1e59f6e8be Merge pull request #389 from timrichardson/issues/1888
add Recaptcha(...ajax=True) to use ajax api, fix Recaptcha in LOAD issue/1888
2014-03-08 14:29:58 -06:00
mdipierro 0e1eb5b56e Merge branch 'master' of github.com:web2py/web2py 2014-03-08 14:28:49 -06:00
mdipierro e43df945b4 no more pypy test for today 2014-03-08 14:28:18 -06:00
mdipierro c23706b794 Merge pull request #388 from gitanoqevaporelmundoentero/master
dal.py: Table class: validate_and_update_or_insert method created, validate_and_update minor changes
2014-03-08 14:15:43 -06:00
alex 98cd10c6f5 Fix for session unique_key query 2014-03-08 14:37:15 -05:00
Michele Comitini 55b92e854c use mimemessage method gettype() to extract content-type 2014-03-07 18:01:41 +01:00
Michele Comitini 0c926d60b8 oauth strip encoding part in content-type response from server. 2014-03-07 17:31:20 +01:00
Oscar Rodriguez d4d91fd003 dal.py: Table class: validate_and_update_or_insert method created 2014-03-07 16:22:09 +01:00
Jeremie Dokime 89c4efbac3 Fix DAL ADD method
Fix regression of ADD method:
NameError: global name 'gluon' is not defined
2014-03-06 17:56:11 +01:00
Oscar Rodriguez 3a5a34da0a dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-06 12:06:57 +01:00
Tim Richardson cf3992545d add Recaptcha(...ajax=True) to use ajax api, fix Recaptcha in LOAD 2014-03-06 09:40:48 +11:00
Oscar Rodriguez 0b7f663d43 dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-05 19:46:19 +01:00
Oscar Rodriguez abd72f8df0 dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-05 17:44:56 +01:00
31 changed files with 691 additions and 3039 deletions
+3 -7
View File
@@ -22,13 +22,9 @@ before_script:
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; 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 - if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
# Install last sdk for app engine # Install last sdk for app engine (update only whenever a new release is available)
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/svn/trunk/python/VERSION -O ./GAEVERSION; fi - if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_1.8.9.zip -nv; fi
- if [[ $DB == google* ]]; then GAERELEASE=$(cat ./GAEVERSION | grep -i release); fi - if [[ $DB == google* ]]; then unzip -q google_appengine_1.8.9.zip; fi
- if [[ $DB == google* ]]; then GAERELEASE=${GAERELEASE#*\"}; fi
- if [[ $DB == google* ]]; then GAERELEASE=${GAERELEASE%\"}; fi
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_$GAERELEASE.zip -nv; fi
- if [[ $DB == google* ]]; then unzip -q google_appengine_$GAERELEASE.zip; fi
- if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi - if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi
- if [[ $DB == mongodb* ]]; then pip install pymongo; fi - if [[ $DB == mongodb* ]]; then pip install pymongo; fi
+2 -2
View File
@@ -1,5 +1,6 @@
## 2.9.1 - 2.9.3 ## 2.9.1 - 2.9.5
- many small but important bug fixes
- jquery 1.11 - jquery 1.11
- codemirror 3.21, thanks Paolo Valleri - codemirror 3.21, thanks Paolo Valleri
- fixed security issue with sessions in database, thanks Nathan Humphreys - fixed security issue with sessions in database, thanks Nathan Humphreys
@@ -20,7 +21,6 @@
- invalidate function in web2py.js, thanks Paolo - invalidate function in web2py.js, thanks Paolo
- DAL(...,adapter_args=dict(engine='MyISAM')) - DAL(...,adapter_args=dict(engine='MyISAM'))
- todolist panel in admin editor, thanks Paolo Valleri - todolist panel in admin editor, thanks Paolo Valleri
- enable killing processed in windows, thanks Yair
## 2.8.1 ## 2.8.1
+1 -1
View File
@@ -30,7 +30,7 @@ update:
echo "remember that pymysql was tweaked" echo "remember that pymysql was tweaked"
src: src:
### Use semantic versioning ### Use semantic versioning
echo 'Version 2.9.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION echo 'Version 2.9.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files ### rm -f all junk files
make clean make clean
### clean up baisc apps ### clean up baisc apps
+1 -1
View File
@@ -1 +1 @@
Version 2.9.4-stable+timestamp.2014.03.04.22.40.54 Version 2.9.5-stable+timestamp.2014.03.15.21.24.06
+1 -1
View File
@@ -1937,7 +1937,7 @@ def install_plugin():
session.flash = T('New plugin installed: %s', filename) session.flash = T('New plugin installed: %s', filename)
else: else:
session.flash = \ session.flash = \
T('unable to create application "%s"', filename) T('unable to install plugin "%s"', filename)
redirect(URL(f="plugins", args=[app,])) redirect(URL(f="plugins", args=[app,]))
return dict(form=form, app=app, plugin=plugin, source=source) return dict(form=form, app=app, plugin=plugin, source=source)
@@ -16,6 +16,7 @@
<ul> <ul>
<li><a target="_blank" href="http://www.metacryption.com">MetaCryption, LLC</a> (USA)</li> <li><a target="_blank" href="http://www.metacryption.com">MetaCryption, LLC</a> (USA)</li>
<li><a target="_blank" href="http://www.planethost.com">PlanetHost</a> (USA)</li>
<li><a target="_blank" href="http://www.secution.com">Secution, Inc</a> (USA)</li> <li><a target="_blank" href="http://www.secution.com">Secution, Inc</a> (USA)</li>
<li><a target="_blank" href="http://www.wadecybertech.com">Wade Cybertech</a> (Canada)</li> <li><a target="_blank" href="http://www.wadecybertech.com">Wade Cybertech</a> (Canada)</li>
<li><a target="_blank" href="http://www.formatics.nl">Formatics</a> (Netherlands)</li> <li><a target="_blank" href="http://www.formatics.nl">Formatics</a> (Netherlands)</li>
-1
View File
@@ -12,7 +12,6 @@ response.subtitle = ''
## read more at http://dev.w3.org/html5/markup/meta.name.html ## read more at http://dev.w3.org/html5/markup/meta.name.html
response.meta.author = 'Your Name <you@example.com>' response.meta.author = 'Your Name <you@example.com>'
response.meta.description = 'a cool new app'
response.meta.keywords = 'web2py, python, framework' response.meta.keywords = 'web2py, python, framework'
response.meta.generator = 'Web2py Web Framework' response.meta.generator = 'Web2py Web Framework'
+1 -1
View File
@@ -11,7 +11,7 @@ Web2Py framework modules
""" """
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64'] __all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
from globals import current from globals import current
from html import * from html import *
from validators import * from validators import *
@@ -172,9 +172,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
if open_url: if open_url:
try: try:
data = open_url.read() data = open_url.read()
resp_type = open_url.info().get('Content-Type') resp_type = open_url.info().gettype()
# try json style first # try json style first
if not resp_type or resp_type == 'application/json': if not resp_type or resp_type[:16] == 'application/json':
try: try:
tokendata = json.loads(data) tokendata = json.loads(data)
current.session.token = tokendata current.session.token = tokendata
+11 -4
View File
@@ -111,7 +111,7 @@ class MockTable(object):
self.session_expiry = session_expiry self.session_expiry = session_expiry
self.with_lock = with_lock self.with_lock = with_lock
def __call__(self, record_id): def __call__(self, record_id, unique_key=None):
# Support DAL shortcut query: table(record_id) # Support DAL shortcut query: table(record_id)
q = self.id # This will call the __getattr__ below q = self.id # This will call the __getattr__ below
@@ -120,6 +120,7 @@ class MockTable(object):
# Instructs MockQuery, to behave as db(table.id == record_id) # Instructs MockQuery, to behave as db(table.id == record_id)
q.op = 'eq' q.op = 'eq'
q.value = record_id q.value = record_id
q.unique_key = unique_key
row = q.select() row = q.select()
return row[0] if row else Storage() return row[0] if row else Storage()
@@ -129,7 +130,7 @@ class MockTable(object):
#return a fake query. We need to query it just by id for normal operations #return a fake query. We need to query it just by id for normal operations
self.query = MockQuery(field='id', db=self.r_server, self.query = MockQuery(field='id', db=self.r_server,
prefix=self.keyprefix, session_expiry=self.session_expiry, prefix=self.keyprefix, session_expiry=self.session_expiry,
with_lock=self.with_lock) with_lock=self.with_lock, unique_key=self.unique_key)
return self.query return self.query
elif key == '_db': elif key == '_db':
#needed because of the calls in sessions2trash.py and globals.py #needed because of the calls in sessions2trash.py and globals.py
@@ -162,7 +163,7 @@ class MockQuery(object):
and listing all keys. No other operation is supported and listing all keys. No other operation is supported
""" """
def __init__(self, field=None, db=None, prefix=None, session_expiry=False, def __init__(self, field=None, db=None, prefix=None, session_expiry=False,
with_lock=False): with_lock=False, unique_key=None):
self.field = field self.field = field
self.value = None self.value = None
self.db = db self.db = db
@@ -170,6 +171,7 @@ class MockQuery(object):
self.op = None self.op = None
self.session_expiry = session_expiry self.session_expiry = session_expiry
self.with_lock = with_lock self.with_lock = with_lock
self.unique_key = unique_key
def __eq__(self, value, op='eq'): def __eq__(self, value, op='eq'):
self.value = value self.value = value
@@ -187,7 +189,12 @@ class MockQuery(object):
acquire_lock(self.db, key + ':lock', self.value) acquire_lock(self.db, key + ':lock', self.value)
rtn = self.db.hgetall(key) rtn = self.db.hgetall(key)
if rtn: if rtn:
rtn['update_record'] = self.update # update record support if self.unique_key:
#make sure the id and unique_key are correct
if rtn['unique_key'] == self.unique_key:
rtn['update_record'] = self.update # update record support
else:
rtn = None
return [Storage(rtn)] if rtn else [] return [Storage(rtn)] if rtn else []
elif self.op == 'ge' and self.field == 'id' and self.value == 0: elif self.op == 'ge' and self.field == 'id' and self.value == 0:
#means that someone wants the complete list #means that someone wants the complete list
+6 -4
View File
@@ -81,12 +81,13 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
class ServerProxy(object): class ServerProxy(object):
"JSON RPC Simple Client Service Proxy" "JSON RPC Simple Client Service Proxy"
def __init__(self, uri, transport=None, encoding=None, verbose=0): def __init__(self, uri, transport=None, encoding=None, verbose=0,version=None):
self.location = uri # server location (url) self.location = uri # server location (url)
self.trace = verbose # show debug messages self.trace = verbose # show debug messages
self.exceptions = True # raise errors? (JSONRPCError) self.exceptions = True # raise errors? (JSONRPCError)
self.timeout = None self.timeout = None
self.json_request = self.json_response = '' self.json_request = self.json_response = ''
self.version = version # '2.0' for jsonrpc2
type, uri = urllib.splittype(uri) type, uri = urllib.splittype(uri)
if type not in ("http", "https"): if type not in ("http", "https"):
@@ -112,6 +113,8 @@ class ServerProxy(object):
# build data sent to the service # build data sent to the service
request_id = random.randint(0, sys.maxint) request_id = random.randint(0, sys.maxint)
data = {'id': request_id, 'method': method, 'params': args, } data = {'id': request_id, 'method': method, 'params': args, }
if self.version:
data['jsonrpc'] = self.version #mandatory key/value for jsonrpc2 validation else err -32600
request = json.dumps(data) request = json.dumps(data)
# make HTTP request (retry if connection is lost) # make HTTP request (retry if connection is lost)
@@ -130,14 +133,13 @@ class ServerProxy(object):
# {'version': '1.1', 'id': id, 'result': result, 'error': None} # {'version': '1.1', 'id': id, 'result': result, 'error': None}
response = json.loads(response) response = json.loads(response)
if response['id'] != request_id:
raise JSONRPCError(0, "JSON Request ID != Response ID")
self.error = response.get('error', {}) self.error = response.get('error', {})
if self.error and self.exceptions: if self.error and self.exceptions:
raise JSONRPCError(self.error.get('code', 0), raise JSONRPCError(self.error.get('code', 0),
self.error.get('message', ''), self.error.get('message', ''),
self.error.get('data', None)) self.error.get('data', None))
if response['id'] != request_id:
raise JSONRPCError(0, "JSON Request ID != Response ID")
return response.get('result') return response.get('result')
-9
View File
@@ -1,9 +0,0 @@
translitcodec was originally written by Jason Kirtland in 2008.
Contributors are:
- Jason Kirtland <jek@discorporate.us>
- Craig Dennis <craig@idealist.org>
The translitcodec source distribution includes the 'transtab' package
by Markus Kuhn <mkuhn@acm.org>.
-31
View File
@@ -1,31 +0,0 @@
=====================
translitcodec Changes
=====================
0.3
---
Released on February 14, 2011
- Fixes to the transtab table rebuilding tool.
- Added translitcodec.__version__
0.2
---
Released on January 27, 2011
- Resolves issue of "TypeError: character mapping must return integer,
None or unicode" when a blank value (eg: \N{ZERO WIDTH SPACE} \u200B)
was encoded. Unicode blanks are now returned.
- Characters in the ASCII range are no longer included in the translation
tables.
0.1
---
Released on December 28, 2008
- Initial packaged release.
-20
View File
@@ -1,20 +0,0 @@
Copyright (c) 2008 Jason Kirtland <jek at discorporate us>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-44
View File
@@ -1,44 +0,0 @@
-*- coding: utf-8 -*-
Unicode to 8-bit charset transliteration codec.
This package contains codecs for transliterating ISO 10646 texts into
best-effort representations using smaller coded character sets (ASCII,
ISO 8859, etc.). The translation tables used by the codecs are from
the ``transtab`` collection by Markus Kuhn.
Three types of transliterating codecs are provided:
"long", using as many characters as needed to make a natural
replacement. For example, \u00e4 LATIN SMALL LETTER A WITH
DIAERESIS ``ä`` will be replaced with ``ae``.
"short", using the minimum number of characters to make a
replacement. For example, \u00e4 LATIN SMALL LETTER A WITH
DIAERESIS ``ä`` will be replaced with ``a``.
"one", only performing single character replacements. Characters
that can not be transliterated with a single character are passed
through unchanged. For example, \u2639 WHITE FROWNING FACE ``☹``
will be passed through unchanged.
Using the codecs is simple::
>>> import translitcodec
>>> u'fácil € ☺'.encode('translit/long')
u'facil EUR :-)'
>>> u'fácil € ☺'.encode('translit/short')
u'facil E :-)'
The codecs return Unicode by default. To receive a bytestring back,
either chain the output of encode() to another codec, or append the
name of the desired byte encoding to the codec name::
>>> u'fácil € ☺'.encode('translit/one').encode('ascii', 'replace')
'facil E ?'
>>> u'fácil € ☺'.encode('translit/one/ascii', 'replace')
'facil E ?'
The package also supplies a 'transliterate' codec, an alias for
'translit/long'.
File diff suppressed because it is too large Load Diff
+44 -4
View File
@@ -1480,7 +1480,7 @@ class BaseAdapter(ConnectionPool):
return '(%s)' % ' || '.join(self.expand(x,'string') for x in items) return '(%s)' % ' || '.join(self.expand(x,'string') for x in items)
def ADD(self, first, second): def ADD(self, first, second):
if self.is_numerical_type(first.type) or isinstance(first.type, gluon.dal.Field): if self.is_numerical_type(first.type) or isinstance(first.type, Field):
return '(%s + %s)' % (self.expand(first), return '(%s + %s)' % (self.expand(first),
self.expand(second, first.type)) self.expand(second, first.type))
else: else:
@@ -9153,8 +9153,17 @@ class Table(object):
record = self(_key) record = self(_key)
if not response.errors and record: if not response.errors and record:
row = self._db(self._id ==_key) if '_id' in self:
response.id = row.update(**fields) myset = self._db(self._id == record[self._id.name])
else:
query = None
for key, value in _key.iteritems():
if query is None:
query = getattr(self, key) == value
else:
query = query & (getattr(self, key) == value)
myset = self._db(query)
response.id = myset.update(**fields)
else: else:
response.id = None response.id = None
return response return response
@@ -9173,6 +9182,37 @@ class Table(object):
newid = self.insert(**values) newid = self.insert(**values)
return newid return newid
def validate_and_update_or_insert(self, _key=DEFAULT, **fields):
if _key is DEFAULT or _key == '':
primary_keys = {}
for key, value in fields.iteritems():
if key in self._primarykey:
primary_keys[key] = value
if primary_keys != {}:
record = self(**primary_keys)
_key = primary_keys
else:
required_keys = {}
for key, value in fields.iteritems():
if getattr(self, key).required:
required_keys[key] = value
record = self(**required_keys)
_key = required_keys
elif isinstance(_key, dict):
record = self(**_key)
else:
record = self(_key)
if record:
response = self.validate_and_update(_key, **fields)
primary_keys = {}
for key in self._primarykey:
primary_keys[key] = getattr(record, key)
response.id = primary_keys
else:
response = self.validate_and_insert(**fields)
return response
def bulk_insert(self, items): def bulk_insert(self, items):
""" """
here items is a list of dictionaries here items is a list of dictionaries
@@ -11074,7 +11114,7 @@ class Rows(object):
if field.type=='blob' and not value is None: if field.type=='blob' and not value is None:
value = base64.b64encode(value) value = base64.b64encode(value)
elif represent and field.represent: elif represent and field.represent:
value = field.represent(value) value = field.represent(value,record)
row.append(none_exception(value)) row.append(none_exception(value))
writer.writerow(row) writer.writerow(row)
+303 -233
View File
@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Template helpers
--------------------------------------------
""" """
import cgi import cgi
@@ -109,10 +112,11 @@ __all__ = [
def xmlescape(data, quote=True): def xmlescape(data, quote=True):
""" """
returns an escaped string of the provided data Returns an escaped string of the provided data
:param data: the data to be escaped Args:
:param quote: optional (default False) data: the data to be escaped
quote: optional (default False)
""" """
# first try the xml function # first try the xml function
@@ -164,80 +168,92 @@ def URL(
language=None, language=None,
): ):
""" """
generate a URL
example::
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':1, 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&p=3&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(3,1), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=3&p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', anchor='1+2'))
'/a/c/f#1%2B2'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1', hmac_key='key'))
'/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z']))
'/a/c/f/w/x/y/z'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z'], encode_embedded_slash=True))
'/a/c/f/w%2Fx/y%2Fz'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=False))
'/a/c/f/%(id)d'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=True))
'/a/c/f/%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=False))
'/a/c/f?id=%(id)d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=True))
'/a/c/f?id=%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=False))
'/a/c/f#%(id)d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=True))
'/a/c/f#%25%28id%29d'
generates a url '/a/c/f' corresponding to application a, controller c generates a url '/a/c/f' corresponding to application a, controller c
and function f. If r=request is passed, a, c, f are set, respectively, and function f. If r=request is passed, a, c, f are set, respectively,
to r.application, r.controller, r.function. to r.application, r.controller, r.function.
The more typical usage is: The more typical usage is:
URL(r=request, f='index') that generates a url for the index function URL('index')
that generates a url for the index function
within the present application and controller. within the present application and controller.
:param a: application (default to current if r is given) Args:
:param c: controller (default to current if r is given) a: application (default to current if r is given)
:param f: function (default to current if r is given) c: controller (default to current if r is given)
:param r: request (optional) f: function (default to current if r is given)
:param args: any arguments (optional) r: request (optional)
:param vars: any variables (optional) args: any arguments (optional). Additional "path" elements
:param anchor: anchorname, without # (optional) vars: any variables (optional). Querystring elements
:param hmac_key: key to use when generating hmac signature (optional) anchor: anchorname, without # (optional)
:param hash_vars: which of the vars to include in our hmac signature extension: force an extension
True (default) - hash all vars, False - hash none of the vars, hmac_key: key to use when generating hmac signature (optional)
iterable - hash only the included vars ['key1','key2'] hash_vars: which of the vars to include in our hmac signature
:param scheme: URI scheme (True, 'http' or 'https', etc); forces absolute URL (optional) True (default) - hash all vars, False - hash none of the vars,
:param host: string to force absolute URL with host (True means http_host) iterable - hash only the included vars ['key1','key2']
:param port: optional port number (forces absolute URL) salt: salt hashing with this string
user_signature: signs automatically the URL in such way that only the
user can access the URL (use with `URL.verify` or
`auth.requires_signature()`)
scheme: URI scheme (True, 'http' or 'https', etc); forces absolute URL (optional)
host: string to force absolute URL with host (True means http_host)
port: optional port number (forces absolute URL)
encode_embedded_slash: encode slash characters included in args
url_encode: encode characters included in vars
Raises:
SyntaxError: when no application, controller or function is available
or when a CRLF is found in the generated url
Examples:
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':1, 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&p=3&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(3,1), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=3&p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', anchor='1+2'))
'/a/c/f#1%2B2'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1', hmac_key='key'))
'/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z']))
'/a/c/f/w/x/y/z'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z'], encode_embedded_slash=True))
'/a/c/f/w%2Fx/y%2Fz'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=False))
'/a/c/f/%(id)d'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=True))
'/a/c/f/%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=False))
'/a/c/f?id=%(id)d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=True))
'/a/c/f?id=%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=False))
'/a/c/f#%(id)d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=True))
'/a/c/f#%25%28id%29d'
:raises SyntaxError: when no application, controller or function is
available
:raises SyntaxError: when a CRLF is found in the generated url
""" """
from rewrite import url_out # done here in case used not-in web2py from rewrite import url_out # done here in case used not-in web2py
@@ -528,7 +544,7 @@ class XML(XmlComponent):
use it to wrap a string that contains XML/HTML so that it will not be use it to wrap a string that contains XML/HTML so that it will not be
escaped by the template escaped by the template
example: Examples:
>>> XML('<h1>Hello</h1>').xml() >>> XML('<h1>Hello</h1>').xml()
'<h1>Hello</h1>' '<h1>Hello</h1>'
@@ -564,19 +580,19 @@ class XML(XmlComponent):
}, },
): ):
""" """
:param text: the XML text Args:
:param sanitize: sanitize text using the permitted tags and allowed text: the XML text
attributes (default False) sanitize: sanitize text using the permitted tags and allowed
:param permitted_tags: list of permitted tags (default: simple list of attributes (default False)
tags) permitted_tags: list of permitted tags (default: simple list of
:param allowed_attributes: dictionary of allowed attributed (default tags)
for A, IMG and BlockQuote). allowed_attributes: dictionary of allowed attributed (default
The key is the tag; the value is a list of allowed attributes. for A, IMG and BlockQuote).
The key is the tag; the value is a list of allowed attributes.
""" """
if sanitize: if sanitize:
text = sanitizer.sanitize(text, permitted_tags, text = sanitizer.sanitize(text, permitted_tags, allowed_attributes)
allowed_attributes)
if isinstance(text, unicode): if isinstance(text, unicode):
text = text.encode('utf8', 'xmlcharrefreplace') text = text.encode('utf8', 'xmlcharrefreplace')
elif not isinstance(text, str): elif not isinstance(text, str):
@@ -620,7 +636,8 @@ class XML(XmlComponent):
def flatten(self, render=None): def flatten(self, render=None):
""" """
return the text stored by the XML object rendered by the render function returns the text stored by the XML object rendered
by the `render` function
""" """
if render: if render:
return render(self.text, None, {}) return render(self.text, None, {})
@@ -628,8 +645,9 @@ class XML(XmlComponent):
def elements(self, *args, **kargs): def elements(self, *args, **kargs):
""" """
to be considered experimental since the behavior of this method is questionable to be considered experimental since the behavior of this method
another options could be TAG(self.text).elements(*args,**kargs) is questionable
another option could be `TAG(self.text).elements(*args,**kwargs)`
""" """
return [] return []
@@ -653,15 +671,15 @@ class DIV(XmlComponent):
Behaves like a dictionary regarding updating of attributes. Behaves like a dictionary regarding updating of attributes.
Behaves like a list regarding inserting/appending components. Behaves like a list regarding inserting/appending components.
example:: Examples:
>>> DIV('hello', 'world', _style='color:red;').xml() >>> DIV('hello', 'world', _style='color:red;').xml()
'<div style=\"color:red;\">helloworld</div>' '<div style=\"color:red;\">helloworld</div>'
all other HTML helpers are derived from DIV. All other HTML helpers are derived from `DIV`.
_something=\"value\" attributes are transparently translated into `_something="value"` attributes are transparently translated into
something=\"value\" HTML attributes `something="value"` HTML attributes
""" """
# name of the tag, subclasses should update this # name of the tag, subclasses should update this
@@ -671,10 +689,12 @@ class DIV(XmlComponent):
def __init__(self, *components, **attributes): def __init__(self, *components, **attributes):
""" """
:param *components: any components that should be nested in this element Args:
:param **attributes: any attributes you want to give to this element components: any components that should be nested in this element
attributes: any attributes you want to give to this element
:raises SyntaxError: when a stand alone tag receives components Raises:
SyntaxError: when a stand alone tag receives components
""" """
if self.tag[-1:] == '/' and components: if self.tag[-1:] == '/' and components:
@@ -705,6 +725,8 @@ class DIV(XmlComponent):
""" """
list style appending of components list style appending of components
Examples:
>>> a=DIV() >>> a=DIV()
>>> a.append(SPAN('x')) >>> a.append(SPAN('x'))
>>> print a >>> print a
@@ -717,7 +739,9 @@ class DIV(XmlComponent):
def insert(self, i, value): def insert(self, i, value):
""" """
list style inserting of components List-style inserting of components
Examples:
>>> a=DIV() >>> a=DIV()
>>> a.insert(0,SPAN('x')) >>> a.insert(0,SPAN('x'))
@@ -731,12 +755,12 @@ class DIV(XmlComponent):
def __getitem__(self, i): def __getitem__(self, i):
""" """
gets attribute with name 'i' or component #i. Gets attribute with name 'i' or component #i.
If attribute 'i' is not found returns None If attribute 'i' is not found returns None
:param i: index Args:
if i is a string: the name of the attribute i: index. If i is a string: the name of the attribute
otherwise references to number of the component otherwise references to number of the component
""" """
if isinstance(i, str): if isinstance(i, str):
@@ -749,12 +773,12 @@ class DIV(XmlComponent):
def __setitem__(self, i, value): def __setitem__(self, i, value):
""" """
sets attribute with name 'i' or component #i. Sets attribute with name 'i' or component #i.
:param i: index Args:
if i is a string: the name of the attribute i: index. If i is a string: the name of the attribute
otherwise references to number of the component otherwise references to number of the component
:param value: the new value value: the new value
""" """
self._setnode(value) self._setnode(value)
if isinstance(i, (str, unicode)): if isinstance(i, (str, unicode)):
@@ -764,11 +788,11 @@ class DIV(XmlComponent):
def __delitem__(self, i): def __delitem__(self, i):
""" """
deletes attribute with name 'i' or component #i. Deletes attribute with name 'i' or component #i.
:param i: index Args:
if i is a string: the name of the attribute i: index. If i is a string: the name of the attribute
otherwise references to number of the component otherwise references to number of the component
""" """
if isinstance(i, str): if isinstance(i, str):
@@ -778,13 +802,13 @@ class DIV(XmlComponent):
def __len__(self): def __len__(self):
""" """
returns the number of included components Returns the number of included components
""" """
return len(self.components) return len(self.components)
def __nonzero__(self): def __nonzero__(self):
""" """
always return True Always returns True
""" """
return True return True
@@ -804,10 +828,11 @@ class DIV(XmlComponent):
helper for _fixup. Checks if a component is in allowed_parents, helper for _fixup. Checks if a component is in allowed_parents,
otherwise wraps it in wrap_parent otherwise wraps it in wrap_parent
:param allowed_parents: (tuple) classes that the component should be an Args:
instance of allowed_parents: (tuple) classes that the component should be an
:param wrap_parent: the class to wrap the component in, if needed instance of
:param wrap_lambda: lambda to use for wrapping, if needed wrap_parent: the class to wrap the component in, if needed
wrap_lambda: lambda to use for wrapping, if needed
""" """
components = [] components = []
@@ -875,7 +900,7 @@ class DIV(XmlComponent):
def _xml(self): def _xml(self):
""" """
helper for xml generation. Returns separately: Helper for xml generation. Returns separately:
- the component attributes - the component attributes
- the generated xml of the inner components - the generated xml of the inner components
@@ -883,7 +908,8 @@ class DIV(XmlComponent):
do not have a False or None value. The underscore is removed. do not have a False or None value. The underscore is removed.
A value of True is replaced with the attribute name. A value of True is replaced with the attribute name.
:returns: tuple: (attributes, components) Returns:
tuple: (attributes, components)
""" """
# get the attributes for this component # get the attributes for this component
@@ -932,16 +958,18 @@ class DIV(XmlComponent):
def __str__(self): def __str__(self):
""" """
str(COMPONENT) returns equals COMPONENT.xml() str(COMPONENT) returns COMPONENT.xml()
""" """
return self.xml() return self.xml()
def flatten(self, render=None): def flatten(self, render=None):
""" """
return the text stored by the DIV object rendered by the render function Returns the text stored by the DIV object rendered by the render function
the render function must take text, tagname, and attributes the render function must take text, tagname, and attributes
render=None is equivalent to render=lambda text, tag, attr: text `render=None` is equivalent to `render=lambda text, tag, attr: text`
Examples:
>>> markdown = lambda text,tag=None,attributes={}: \ >>> markdown = lambda text,tag=None,attributes={}: \
{None: re.sub('\s+',' ',text), \ {None: re.sub('\s+',' ',text), \
@@ -972,11 +1000,13 @@ class DIV(XmlComponent):
def elements(self, *args, **kargs): def elements(self, *args, **kargs):
""" """
find all component that match the supplied attribute dictionary, Find all components that match the supplied attribute dictionary,
or None if nothing could be found or None if nothing could be found
All components of the components are searched. All components of the components are searched.
Examples:
>>> a = DIV(DIV(SPAN('x'),3,DIV(SPAN('y')))) >>> a = DIV(DIV(SPAN('x'),3,DIV(SPAN('y'))))
>>> for c in a.elements('span',first_only=True): c[0]='z' >>> for c in a.elements('span',first_only=True): c[0]='z'
>>> print a >>> print a
@@ -987,6 +1017,8 @@ class DIV(XmlComponent):
It also supports a syntax compatible with jQuery It also supports a syntax compatible with jQuery
Examples:
>>> a=TAG('<div><span><a id="1-1" u:v=$>hello</a></span><p class="this is a test">world</p></div>') >>> a=TAG('<div><span><a id="1-1" u:v=$>hello</a></span><p class="this is a test">world</p></div>')
>>> for e in a.elements('div a#1-1, p.is'): print e.flatten() >>> for e in a.elements('div a#1-1, p.is'): print e.flatten()
hello hello
@@ -995,7 +1027,6 @@ class DIV(XmlComponent):
hello hello
>>> a.elements('a[u:v=$]')[0].xml() >>> a.elements('a[u:v=$]')[0].xml()
'<a id="1-1" u:v="$">hello</a>' '<a id="1-1" u:v="$">hello</a>'
>>> a=FORM( INPUT(_type='text'), SELECT(range(1)), TEXTAREA() ) >>> a=FORM( INPUT(_type='text'), SELECT(range(1)), TEXTAREA() )
>>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled' >>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled'
>>> a.xml() >>> a.xml()
@@ -1005,6 +1036,8 @@ class DIV(XmlComponent):
a "replace" argument (note, a list of the original matching elements a "replace" argument (note, a list of the original matching elements
is still returned as usual). is still returned as usual).
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=P('x', _class='xyz')) >>> b = a.elements('span.abc', replace=P('x', _class='xyz'))
>>> print a >>> print a
@@ -1013,6 +1046,8 @@ class DIV(XmlComponent):
"replace" can be a callable, which will be passed the original element and "replace" can be a callable, which will be passed the original element and
should return a new element to replace it. should return a new element to replace it.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz')) >>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz'))
>>> print a >>> print a
@@ -1020,6 +1055,8 @@ class DIV(XmlComponent):
If replace=None, matching elements will be removed completely. If replace=None, matching elements will be removed completely.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span', find='y', replace=None) >>> b = a.elements('span', find='y', replace=None)
>>> print a >>> print a
@@ -1030,6 +1067,8 @@ class DIV(XmlComponent):
replaced (find_text is ignored if "replace" is not also specified). replaced (find_text is ignored if "replace" is not also specified).
Like the "find" argument, "find_text" can be a string or a compiled regex. Like the "find" argument, "find_text" can be a string or a compiled regex.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc')))) >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements(find_text=re.compile('x|y|z'), replace='hello') >>> b = a.elements(find_text=re.compile('x|y|z'), replace='hello')
>>> print a >>> print a
@@ -1038,6 +1077,8 @@ class DIV(XmlComponent):
If other attributes are specified along with find_text, then only components If other attributes are specified along with find_text, then only components
that match the specified attributes will be searched for find_text. that match the specified attributes will be searched for find_text.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='efg'), SPAN('z', _class='abc')))) >>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='efg'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello') >>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello')
>>> print a >>> print a
@@ -1134,7 +1175,7 @@ class DIV(XmlComponent):
def element(self, *args, **kargs): def element(self, *args, **kargs):
""" """
find the first component that matches the supplied attribute dictionary, Finds the first component that matches the supplied attribute dictionary,
or None if nothing could be found or None if nothing could be found
Also the components of the components are searched. Also the components of the components are searched.
@@ -1148,7 +1189,7 @@ class DIV(XmlComponent):
def siblings(self, *args, **kargs): def siblings(self, *args, **kargs):
""" """
find all sibling components that match the supplied argument list Finds all sibling components that match the supplied argument list
and attribute dictionary, or None if nothing could be found and attribute dictionary, or None if nothing could be found
""" """
sibs = [s for s in self.parent.components if not s == self] sibs = [s for s in self.parent.components if not s == self]
@@ -1175,7 +1216,7 @@ class DIV(XmlComponent):
def sibling(self, *args, **kargs): def sibling(self, *args, **kargs):
""" """
find the first sibling component that match the supplied argument list Finds the first sibling component that match the supplied argument list
and attribute dictionary, or None if nothing could be found and attribute dictionary, or None if nothing could be found
""" """
kargs['first_only'] = True kargs['first_only'] = True
@@ -1211,10 +1252,12 @@ copy_reg.pickle(__tag_div__, TAG_pickler, TAG_unpickler)
class __TAG__(XmlComponent): class __TAG__(XmlComponent):
""" """
TAG factory example:: TAG factory
>>> print TAG.first(TAG.second('test'), _key = 3) Examples:
<first key=\"3\"><second>test</second></first>
>>> print TAG.first(TAG.second('test'), _key = 3)
<first key=\"3\"><second>test</second></first>
""" """
@@ -1239,16 +1282,16 @@ class HTML(DIV):
There are four predefined document type definitions. There are four predefined document type definitions.
They can be specified in the 'doctype' parameter: They can be specified in the 'doctype' parameter:
-'strict' enables strict doctype - 'strict' enables strict doctype
-'transitional' enables transitional doctype (default) - 'transitional' enables transitional doctype (default)
-'frameset' enables frameset doctype - 'frameset' enables frameset doctype
-'html5' enables HTML 5 doctype - 'html5' enables HTML 5 doctype
-any other string will be treated as user's own doctype - any other string will be treated as user's own doctype
'lang' parameter specifies the language of the document. 'lang' parameter specifies the language of the document.
Defaults to 'en'. Defaults to 'en'.
See also :class:`DIV` See also `DIV`
""" """
tag = 'html' tag = 'html'
@@ -1289,10 +1332,10 @@ class XHTML(DIV):
There are three predefined document type definitions. There are three predefined document type definitions.
They can be specified in the 'doctype' parameter: They can be specified in the 'doctype' parameter:
-'strict' enables strict doctype - 'strict' enables strict doctype
-'transitional' enables transitional doctype (default) - 'transitional' enables transitional doctype (default)
-'frameset' enables frameset doctype - 'frameset' enables frameset doctype
-any other string will be treated as user's own doctype - any other string will be treated as user's own doctype
'lang' parameter specifies the language of the document and the xml document. 'lang' parameter specifies the language of the document and the xml document.
Defaults to 'en'. Defaults to 'en'.
@@ -1300,7 +1343,7 @@ class XHTML(DIV):
'xmlns' parameter specifies the xml namespace. 'xmlns' parameter specifies the xml namespace.
Defaults to 'http://www.w3.org/1999/xhtml'. Defaults to 'http://www.w3.org/1999/xhtml'.
See also :class:`DIV` See also `DIV`
""" """
tag = 'html' tag = 'html'
@@ -1443,7 +1486,7 @@ class P(DIV):
""" """
Will replace ``\\n`` by ``<br />`` if the `cr2br` attribute is provided. Will replace ``\\n`` by ``<br />`` if the `cr2br` attribute is provided.
see also :class:`DIV` see also `DIV`
""" """
tag = 'p' tag = 'p'
@@ -1476,6 +1519,23 @@ class HR(DIV):
class A(DIV): class A(DIV):
"""
Generates an A() link.
A() in web2py is really important and with the included web2py.js
allows lots of Ajax interactions in the page
On top of "usual" `_attributes`, it takes
Args:
callback: an url to call but not redirect to
cid: if you want to load the _href into an element of the page (component)
pass its id (without the #) here
delete: element to delete after calling callback
target: same thing as cid
confirm: text to display upon a callback with a delete
noconfirm: don't display alert upon a callback with delete
"""
tag = 'a' tag = 'a'
@@ -1546,24 +1606,25 @@ class CENTER(DIV):
class CODE(DIV): class CODE(DIV):
""" """
displays code in HTML with syntax highlighting. Displays code in HTML with syntax highlighting.
:param attributes: optional attributes: Args:
language: indicates the language, otherwise PYTHON is assumed
link: can provide a link
styles: for styles
- language: indicates the language, otherwise PYTHON is assumed Examples:
- link: can provide a link
- styles: for styles
Example:: {{=CODE(\"print 'hello world'\", language='python', link=None,
counter=1, styles={}, highlight_line=None)}}
{{=CODE(\"print 'hello world'\", language='python', link=None,
counter=1, styles={}, highlight_line=None)}}
supported languages are \"python\", \"html_plain\", \"c\", \"cpp\", supported languages are
\"web2py\", \"html\".
The \"html\" language interprets {{ and }} tags as \"web2py\" code, "python", "html_plain", "c", "cpp", "web2py", "html"
\"html_plain\" doesn't.
The "html" language interprets {{ and }} tags as "web2py" code,
"html_plain" doesn't.
if a link='/examples/global/vars/' is provided web2py keywords are linked to if a link='/examples/global/vars/' is provided web2py keywords are linked to
the online docs. the online docs.
@@ -1607,7 +1668,6 @@ class UL(DIV):
If subcomponents are not LI-components they will be wrapped in a LI If subcomponents are not LI-components they will be wrapped in a LI
see also :class:`DIV`
""" """
tag = 'ul' tag = 'ul'
@@ -1637,7 +1697,6 @@ class TR(DIV):
If subcomponents are not TD/TH-components they will be wrapped in a TD If subcomponents are not TD/TH-components they will be wrapped in a TD
see also :class:`DIV`
""" """
tag = 'tr' tag = 'tr'
@@ -1652,7 +1711,6 @@ class __TRHEAD__(DIV):
If subcomponents are not TD/TH-components they will be wrapped in a TH If subcomponents are not TD/TH-components they will be wrapped in a TH
see also :class:`DIV`
""" """
tag = 'tr' tag = 'tr'
@@ -1702,7 +1760,6 @@ class TABLE(DIV):
If subcomponents are not TR/TBODY/THEAD/TFOOT-components If subcomponents are not TR/TBODY/THEAD/TFOOT-components
they will be wrapped in a TR they will be wrapped in a TR
see also :class:`DIV`
""" """
tag = 'table' tag = 'table'
@@ -1724,34 +1781,36 @@ class IFRAME(DIV):
class INPUT(DIV): class INPUT(DIV):
""" """
INPUT Component INPUT Component
examples:: Takes two special attributes value= and requires=.
>>> INPUT(_type='text', _name='name', value='Max').xml() Args:
'<input name=\"name\" type=\"text\" value=\"Max\" />' value: used to pass the initial value for the input field.
>>> INPUT(_type='checkbox', _name='checkbox', value='on').xml()
'<input checked=\"checked\" name=\"checkbox\" type=\"checkbox\" value=\"on\" />'
>>> INPUT(_type='radio', _name='radio', _value='yes', value='yes').xml()
'<input checked=\"checked\" name=\"radio\" type=\"radio\" value=\"yes\" />'
>>> INPUT(_type='radio', _name='radio', _value='no', value='yes').xml()
'<input name=\"radio\" type=\"radio\" value=\"no\" />'
the input helper takes two special attributes value= and requires=.
:param value: used to pass the initial value for the input field.
value differs from _value because it works for checkboxes, radio, value differs from _value because it works for checkboxes, radio,
textarea and select/option too. textarea and select/option too.
For a checkbox value should be '' or 'on'.
For a radio or select/option value should be the _value
of the checked/selected item.
- for a checkbox value should be '' or 'on'. requires: should be None, or a validator or a list of validators
- for a radio or select/option value should be the _value
of the checked/selected item.
:param requires: should be None, or a validator or a list of validators
for the value of the field. for the value of the field.
Examples:
>>> INPUT(_type='text', _name='name', value='Max').xml()
'<input name=\"name\" type=\"text\" value=\"Max\" />'
>>> INPUT(_type='checkbox', _name='checkbox', value='on').xml()
'<input checked=\"checked\" name=\"checkbox\" type=\"checkbox\" value=\"on\" />'
>>> INPUT(_type='radio', _name='radio', _value='yes', value='yes').xml()
'<input checked=\"checked\" name=\"radio\" type=\"radio\" value=\"yes\" />'
>>> INPUT(_type='radio', _name='radio', _value='no', value='yes').xml()
'<input name=\"radio\" type=\"radio\" value=\"no\" />'
""" """
tag = 'input/' tag = 'input/'
@@ -1847,11 +1906,12 @@ class INPUT(DIV):
class TEXTAREA(INPUT): class TEXTAREA(INPUT):
""" """
example:: Examples::
TEXTAREA(_name='sometext', value='blah '*100, requires=IS_NOT_EMPTY()) TEXTAREA(_name='sometext', value='blah ' * 100, requires=IS_NOT_EMPTY())
'blah blah blah ...' will be the content of the textarea field. 'blah blah blah ...' will be the content of the textarea field.
""" """
tag = 'textarea' tag = 'textarea'
@@ -1898,12 +1958,12 @@ class OPTGROUP(DIV):
class SELECT(INPUT): class SELECT(INPUT):
""" """
example:: Examples:
>>> from validators import IS_IN_SET >>> from validators import IS_IN_SET
>>> SELECT('yes', 'no', _name='selector', value='yes', >>> SELECT('yes', 'no', _name='selector', value='yes',
... requires=IS_IN_SET(['yes', 'no'])).xml() ... requires=IS_IN_SET(['yes', 'no'])).xml()
'<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>' '<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>'
""" """
@@ -1962,12 +2022,13 @@ class LEGEND(DIV):
class FORM(DIV): class FORM(DIV):
""" """
example:: Examples:
>>> from validators import IS_NOT_EMPTY
>>> form=FORM(INPUT(_name="test", requires=IS_NOT_EMPTY()))
>>> form.xml()
'<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>'
>>> from validators import IS_NOT_EMPTY
>>> form=FORM(INPUT(_name="test", requires=IS_NOT_EMPTY()))
>>> form.xml()
'<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>'
a FORM is container for INPUT, TEXTAREA, SELECT and other helpers a FORM is container for INPUT, TEXTAREA, SELECT and other helpers
@@ -2107,12 +2168,12 @@ class FORM(DIV):
you can use it instead of directly form.accepts. you can use it instead of directly form.accepts.
Usage: Usage:
In controller In controller::
def action(): def action():
form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY())) form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY()))
form.validate() #you can pass some args here - see below form.validate() #you can pass some args here - see below
return dict(form=form) return dict(form=form)
This can receive a bunch of arguments This can receive a bunch of arguments
@@ -2190,29 +2251,31 @@ class FORM(DIV):
""" """
Perform the .validate() method but returns the form Perform the .validate() method but returns the form
Usage in controllers: Usage in controllers::
# directly on return
def action():
#some code here
return dict(form=FORM(...).process(...))
You can use it with FORM, SQLFORM or FORM based plugins # directly on return
def action():
#some code here
return dict(form=FORM(...).process(...))
Examples: You can use it with FORM, SQLFORM or FORM based plugins::
#response.flash messages
def action(): # response.flash messages
form = SQLFORM(db.table).process(message_onsuccess='Sucess!') def action():
retutn dict(form=form) form = SQLFORM(db.table).process(message_onsuccess='Sucess!')
return dict(form=form)
# callback function
# callback receives True or False as first arg, and a list of args.
def my_callback(status, msg):
response.flash = "Success! "+msg if status else "Errors occured"
# after argument can be 'flash' to response.flash messages
# or a function name to use as callback or None to do nothing.
def action():
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
# callback function
# callback receives True or False as first arg, and a list of args.
def my_callback(status, msg):
response.flash = "Success! "+msg if status else "Errors occured"
# after argument can be 'flash' to response.flash messages
# or a function name to use as callback or None to do nothing.
def action():
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
""" """
kwargs['dbio'] = kwargs.get('dbio', True) kwargs['dbio'] = kwargs.get('dbio', True)
# necessary for SQLHTML forms # necessary for SQLHTML forms
@@ -2223,7 +2286,7 @@ class FORM(DIV):
def add_button(self, value, url, _class=None): def add_button(self, value, url, _class=None):
submit = self.element(_type='submit') submit = self.element(_type='submit')
_class = "%s w2p-form-button" % _class if _class else "w2p-form-button" _class = "%s w2p-form-button" % _class if _class else "w2p-form-button"
submit.parent.append( submit.parent.append(
TAG['button'](value, _class=_class, TAG['button'](value, _class=_class,
_onclick=url if url.startswith('javascript:') else _onclick=url if url.startswith('javascript:') else
@@ -2313,16 +2376,20 @@ class FORM(DIV):
class BEAUTIFY(DIV): class BEAUTIFY(DIV):
""" """
example:: Turns any list, dictionary, etc into decent looking html.
>>> BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml()
'<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
turns any list, dictionary, etc into decent looking html.
Two special attributes are Two special attributes are
:sorted: a function that takes the dict and returned sorted keys
:keyfilter: a funciton that takes a key and returns its representation - sorted: a function that takes the dict and returned sorted keys
or None if the key is to be skipped. By default key[:1]=='_' is skipped. - keyfilter: a function that takes a key and returns its representation or
None if the key is to be skipped.
By default key[:1]=='_' is skipped.
Examples:
>>> BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml()
'<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
""" """
tag = 'div' tag = 'div'
@@ -2394,16 +2461,18 @@ class MENU(DIV):
""" """
Used to build menus Used to build menus
Optional arguments Args:
_class: defaults to 'web2py-menu web2py-menu-vertical' _class: defaults to 'web2py-menu web2py-menu-vertical'
ul_class: defaults to 'web2py-menu-vertical' ul_class: defaults to 'web2py-menu-vertical'
li_class: defaults to 'web2py-menu-expand' li_class: defaults to 'web2py-menu-expand'
li_first: defaults to 'web2py-menu-first' li_first: defaults to 'web2py-menu-first'
li_last: defaults to 'web2py-menu-last' li_last: defaults to 'web2py-menu-last'
Use like::
Example:
menu = MENU([['name', False, URL(...), [submenu]], ...]) menu = MENU([['name', False, URL(...), [submenu]], ...])
{{=menu}} {{=menu}}
""" """
tag = 'ul' tag = 'ul'
@@ -2503,9 +2572,10 @@ def embed64(
""" """
helper to encode the provided (binary) data into base64. helper to encode the provided (binary) data into base64.
:param filename: if provided, opens and reads this file in 'rb' mode Args:
:param file: if provided, reads this file filename: if provided, opens and reads this file in 'rb' mode
:param data: if provided, uses the provided data file: if provided, reads this file
data: if provided, uses the provided data
""" """
if filename and os.path.exists(file): if filename and os.path.exists(file):
+26 -3
View File
@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
HTTP statuses helpers
--------------------------------------------
""" """
import re import re
@@ -58,6 +61,18 @@ defined_status = {
regex_status = re.compile('^\d{3} [0-9A-Z ]+$') regex_status = re.compile('^\d{3} [0-9A-Z ]+$')
class HTTP(Exception): class HTTP(Exception):
"""Raises an HTTP response
Args:
status: usually an integer. If it's a well known status code, the ERROR
message will be automatically added. A string can also be passed
as `510 Foo Bar` and in that case the status code and the error
message will be parsed accordingly
body: what to return as body. If left as is, will return the error code
and the status message in the body itself
cookies: pass cookies along (usually not needed)
headers: pass headers as usual dict mapping
"""
def __init__( def __init__(
self, self,
@@ -136,6 +151,14 @@ class HTTP(Exception):
def redirect(location='', how=303, client_side=False): def redirect(location='', how=303, client_side=False):
"""Raises a redirect (303)
Args:
location: the url where to redirect
how: what HTTP status code to use when redirecting
client_side: if set to True, it triggers a reload of the entire page
when the fragment has been loaded as a component
"""
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')
+120 -94
View File
@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| Plural subsystem is created by Vladyslav Kozlovskyy (Ukraine) <dbdevelop@gmail.com>
Plural subsystem is created by Vladyslav Kozlovskyy (Ukraine) Translation system
<dbdevelop@gmail.com> --------------------------------------------
""" """
import os import os
@@ -27,7 +28,6 @@ from gluon.portalocker import read_locked, LockedFile
from utf8 import Utf8 from utf8 import Utf8
from gluon.fileutils import listdir from gluon.fileutils import listdir
import gluon.settings as settings
from gluon.cfs import getcfs from gluon.cfs import getcfs
from gluon.html import XML, xmlescape from gluon.html import XML, xmlescape
from gluon.contrib.markmin.markmin2html import render, markmin_escape from gluon.contrib.markmin.markmin2html import render, markmin_escape
@@ -41,7 +41,6 @@ pjoin = os.path.join
pexists = os.path.exists pexists = os.path.exists
pdirname = os.path.dirname pdirname = os.path.dirname
isdir = os.path.isdir isdir = os.path.isdir
is_gae = False # settings.global_settings.web2py_runtime_gae
DEFAULT_LANGUAGE = 'en' DEFAULT_LANGUAGE = 'en'
DEFAULT_LANGUAGE_NAME = 'English' DEFAULT_LANGUAGE_NAME = 'English'
@@ -76,6 +75,10 @@ regex_plural_tuple = re.compile(
'^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word} '^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$') regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
def is_writable():
""" returns True if and only if the filesystem is writable """
from gluon.settings import global_settings
return not global_settings.web2py_runtime_gae
def safe_eval(text): def safe_eval(text):
if text.strip(): if text.strip():
@@ -165,7 +168,7 @@ def read_dict_aux(filename):
def read_dict(filename): def read_dict(filename):
""" return dictionary with translation messages """ Returns dictionary with translation messages
""" """
return getcfs('lang:' + filename, filename, return getcfs('lang:' + filename, filename,
lambda: read_dict_aux(filename)) lambda: read_dict_aux(filename))
@@ -173,8 +176,8 @@ def read_dict(filename):
def read_possible_plural_rules(): def read_possible_plural_rules():
""" """
create list of all possible plural rules files Creates list of all possible plural rules files
result is cached in PLURAL_RULES dictionary to increase speed The result is cached in PLURAL_RULES dictionary to increase speed
""" """
plurals = {} plurals = {}
try: try:
@@ -294,41 +297,49 @@ def read_plural_dict(filename):
def write_plural_dict(filename, contents): def write_plural_dict(filename, contents):
if '__corrupted__' in contents: if '__corrupted__' in contents:
return return
fp = None
try: try:
fp = LockedFile(filename, 'w') fp = LockedFile(filename, 'w')
fp.write('#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n') fp.write('#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())): for key in sorted(contents, sort_function):
forms = '[' + ','.join([repr(Utf8(form)) forms = '[' + ','.join([repr(Utf8(form))
for form in contents[key]]) + ']' for form in contents[key]]) + ']'
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms)) fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
fp.write('}\n') fp.write('}\n')
except (IOError, OSError): except (IOError, OSError):
if not is_gae: if is_writable():
logging.warning('Unable to write to file %s' % filename) logging.warning('Unable to write to file %s' % filename)
return return
finally: finally:
fp.close() if fp:
fp.close()
def sort_function(x,y):
return cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())
def write_dict(filename, contents): def write_dict(filename, contents):
if '__corrupted__' in contents: if '__corrupted__' in contents:
return return
fp = None
try: try:
fp = LockedFile(filename, 'w') fp = LockedFile(filename, 'w')
fp.write('# -*- coding: utf-8 -*-\n{\n')
for key in sorted(contents, sort_function):
fp.write('%s: %s,\n' % (repr(Utf8(key)),
repr(Utf8(contents[key]))))
fp.write('}\n')
except (IOError, OSError): except (IOError, OSError):
if not settings.global_settings.web2py_runtime_gae: if is_writable():
logging.warning('Unable to write to file %s' % filename) logging.warning('Unable to write to file %s' % filename)
return return
fp.write('# -*- coding: utf-8 -*-\n{\n') finally:
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())): if fp:
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key])))) fp.close()
fp.write('}\n')
fp.close()
class lazyT(object): class lazyT(object):
""" """
never to be called explicitly, returned by Never to be called explicitly, returned by
translator.__call__() or translator.M() translator.__call__() or translator.M()
""" """
m = s = T = f = t = None m = s = T = f = t = None
@@ -428,28 +439,27 @@ copy_reg.pickle(lazyT, pickle_lazyT)
class translator(object): class translator(object):
""" """
this class is instantiated by gluon.compileapp.build_environment This class is instantiated by gluon.compileapp.build_environment
as the T object as the T object
::
Example:
T.force(None) # turns off translation T.force(None) # turns off translation
T.force('fr, it') # forces web2py to translate using fr.py or it.py T.force('fr, it') # forces web2py to translate using fr.py or it.py
T(\"Hello World\") # translates \"Hello World\" using the selected file T("Hello World") # translates "Hello World" using the selected file
notice 1: there is no need to force since, by default, T uses Note:
http_accept_language to determine a translation file. - there is no need to force since, by default, T uses
notice 2: http_accept_language to determine a translation file.
en and en-en are considered different languages! - en and en-en are considered different languages!
notice 3: - if language xx-yy is not found force() probes other similar languages
if language xx-yy is not found force() probes other similar using such algorithm: `xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py`
languages using such algorithm:
xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
""" """
def __init__(self, langpath, http_accept_language): def __init__(self, langpath, http_accept_language):
self.langpath = langpath self.langpath = langpath
self.http_accept_language = http_accept_language self.http_accept_language = http_accept_language
self.is_writable = not is_gae
# filled in self.force(): # filled in self.force():
#------------------------ #------------------------
# self.cache # self.cache
@@ -473,25 +483,26 @@ class translator(object):
self.otherTs = {} self.otherTs = {}
self.filter = markmin self.filter = markmin
self.ftag = 'markmin' self.ftag = 'markmin'
self.ns = None self.ns = None
def get_possible_languages_info(self, lang=None): def get_possible_languages_info(self, lang=None):
""" """
return info for selected language or dictionary with all Returns info for selected language or dictionary with all
possible languages info from APP/languages/*.py possible languages info from `APP/languages/*.py`
args: It Returns:
*lang* (str): language
returns: - a tuple containing::
if *lang* is defined:
return tuple(langcode, langname, langfile_mtime, langcode, langname, langfile_mtime,
pluraldict_fname, pluraldict_mtime, pluraldict_fname, pluraldict_mtime,
prules_langcode, nplurals, prules_langcode, nplurals,
get_plural_id, construct_plural_form) get_plural_id, construct_plural_form
or None
or None
- if *lang* is NOT defined a dictionary with all possible
languages::
if *lang* is NOT defined:
returns dictionary with all possible languages:
{ langcode(from filename): { langcode(from filename):
( langcode, # language code from !langcode! ( langcode, # language code from !langcode!
langname, langname,
@@ -504,6 +515,10 @@ class translator(object):
get_plural_id, # get_plural_id() for current language get_plural_id, # get_plural_id() for current language
construct_plural_form) # construct_plural_form() for current language construct_plural_form) # construct_plural_form() for current language
} }
Args:
lang (str): language
""" """
info = read_possible_languages(self.langpath) info = read_possible_languages(self.langpath)
if lang: if lang:
@@ -511,19 +526,18 @@ class translator(object):
return info return info
def get_possible_languages(self): def get_possible_languages(self):
""" get list of all possible languages for current applications """ """ Gets list of all possible languages for current application """
return list(set(self.current_languages + return list(set(self.current_languages +
[lang for lang in read_possible_languages(self.langpath).iterkeys() [lang for lang in read_possible_languages(self.langpath).iterkeys()
if lang != 'default'])) if lang != 'default']))
def set_current_languages(self, *languages): def set_current_languages(self, *languages):
""" """
set current AKA "default" languages Sets current AKA "default" languages
setting one of this languages makes force() function Setting one of this languages makes the force() function to turn
turn translation off to use default language translation off
""" """
if len(languages) == 1 and isinstance( if len(languages) == 1 and isinstance(languages[0], (tuple, list)):
languages[0], (tuple, list)):
languages = languages[0] languages = languages[0]
if not languages or languages[0] is None: if not languages or languages[0] is None:
# set default language from default.py/DEFAULT_LANGUAGE # set default language from default.py/DEFAULT_LANGUAGE
@@ -543,17 +557,19 @@ class translator(object):
self.force(self.http_accept_language) self.force(self.http_accept_language)
def plural(self, word, n): def plural(self, word, n):
""" get plural form of word for number *n* """ Gets plural form of word for number *n*
NOTE: *word" MUST be defined in current language invoked from T()/T.M() in `%%{}` tag
(T.accepted_language)
invoked from T()/T.M() in %%{} tag Args:
args: word (str): word in singular
word (str): word in singular n (numeric): number plural form created for
n (numeric): number plural form created for
Returns:
word (str): word in appropriate singular/plural form
Note:
"word" MUST be defined in current language (T.accepted_language)
returns:
(str): word in appropriate singular/plural form
""" """
if int(n) == 1: if int(n) == 1:
return word return word
@@ -574,7 +590,7 @@ class translator(object):
form = self.construct_plural_form(word, id) form = self.construct_plural_form(word, id)
forms[id - 1] = form forms[id - 1] = form
self.plural_dict[word] = forms self.plural_dict[word] = forms
if self.is_writable and self.plural_file: if is_writable() and self.plural_file:
write_plural_dict(self.plural_file, write_plural_dict(self.plural_file,
self.plural_dict) self.plural_dict)
return form return form
@@ -582,11 +598,10 @@ class translator(object):
def force(self, *languages): def force(self, *languages):
""" """
Selects language(s) for translation
select language(s) for translation
if a list of languages is passed as a parameter, if a list of languages is passed as a parameter,
first language from this list that matches the ones the first language from this list that matches the ones
from the possible_languages dictionary will be from the possible_languages dictionary will be
selected selected
@@ -691,11 +706,11 @@ class translator(object):
self.ns = ns self.ns = ns
otherT = self.__get_otherT__(language, ns) otherT = self.__get_otherT__(language, ns)
return otherT(message, symbols, lazy=lazy) return otherT(message, symbols, lazy=lazy)
def __get_otherT__(self, language=None, namespace=None): def __get_otherT__(self, language=None, namespace=None):
if not language and not namespace: if not language and not namespace:
raise Exception('Incorrect parameters') raise Exception('Incorrect parameters')
if namespace: if namespace:
if language: if language:
index = '%s/%s' % (namespace, language) index = '%s/%s' % (namespace, language)
@@ -742,7 +757,7 @@ class translator(object):
def M(self, message, symbols={}, language=None, def M(self, message, symbols={}, language=None,
lazy=None, filter=None, ftag=None, ns=None): lazy=None, filter=None, ftag=None, ns=None):
""" """
get cached translated markmin-message with inserted parametes Gets cached translated markmin-message with inserted parametes
if lazy==True lazyT object is returned if lazy==True lazyT object is returned
""" """
if lazy is None: if lazy is None:
@@ -760,16 +775,16 @@ class translator(object):
def get_t(self, message, prefix=''): def get_t(self, message, prefix=''):
""" """
user ## to add a comment into a translation string Use ## to add a comment into a translation string
the comment can be useful do discriminate different possible the comment can be useful do discriminate different possible
translations for the same string (for example different locations) translations for the same string (for example different locations)::
T(' hello world ') -> ' hello world ' T(' hello world ') -> ' hello world '
T(' hello world ## token') -> ' hello world ' T(' hello world ## token') -> ' hello world '
T('hello ## world## token') -> 'hello ## world' T('hello ## world## token') -> 'hello ## world'
the ## notation is ignored in multiline strings and strings that the ## notation is ignored in multiline strings and strings that
start with ##. this is to allow markmin syntax to be translated start with ##. This is needed to allow markmin syntax to be translated
""" """
if isinstance(message, unicode): if isinstance(message, unicode):
message = message.encode('utf8') message = message.encode('utf8')
@@ -786,32 +801,38 @@ class translator(object):
# guess translation same as original # guess translation same as original
self.t[key] = mt = self.default_t.get(key, message) self.t[key] = mt = self.default_t.get(key, message)
# update language file for latter translation # update language file for latter translation
if self.is_writable and self.language_file != self.default_language_file: if is_writable() and \
self.language_file != self.default_language_file:
write_dict(self.language_file, self.t) write_dict(self.language_file, self.t)
return regex_backslash.sub( return regex_backslash.sub(
lambda m: m.group(1).translate(ttab_in), mt) lambda m: m.group(1).translate(ttab_in), mt)
def params_substitution(self, message, symbols): def params_substitution(self, message, symbols):
""" """
substitute parameters from symbols into message using %. Substitutes parameters from symbols into message using %.
also parse %%{} placeholders for plural-forms processing. also parse `%%{}` placeholders for plural-forms processing.
returns: string with parameters
NOTE: *symbols* MUST BE OR tuple OR dict of parameters! Returns:
string with parameters
Note:
*symbols* MUST BE OR tuple OR dict of parameters!
""" """
def sub_plural(m): def sub_plural(m):
"""string in %{} is transformed by this rules: """String in `%{}` is transformed by this rules:
If string starts with \\, ! or ? such transformations If string starts with `\\`, `!` or `?` such transformations
take place: take place::
"!string of words" -> "String of word" (Capitalize)
"!!string of words" -> "String Of Word" (Title)
"!!!string of words" -> "STRING OF WORD" (Upper)
"\\!string of words" -> "!string of word"
(remove \\ and disable transformations)
"?word?number" -> "word" (return word, if number == 1)
"?number" or "??number" -> "" (remove number,
if number == 1)
"?word?number" -> "number" (if number != 1)
"!string of words" -> "String of word" (Capitalize)
"!!string of words" -> "String Of Word" (Title)
"!!!string of words" -> "STRING OF WORD" (Upper)
"\\!string of words" -> "!string of word"
(remove \\ and disable transformations)
"?word?number" -> "word" (return word, if number == 1)
"?number" or "??number" -> "" (remove number,
if number == 1)
"?word?number" -> "number" (if number != 1)
""" """
def sub_tuple(m): def sub_tuple(m):
""" word[number], !word[number], !!word[number], !!!word[number] """ word[number], !word[number], !!word[number], !!!word[number]
@@ -894,7 +915,7 @@ class translator(object):
def translate(self, message, symbols): def translate(self, message, symbols):
""" """
get cached translated message with inserted parameters(symbols) Gets cached translated message with inserted parameters(symbols)
""" """
message = get_from_cache(self.cache, message, message = get_from_cache(self.cache, message,
lambda: self.get_t(message)) lambda: self.get_t(message))
@@ -917,7 +938,8 @@ class translator(object):
def findT(path, language=DEFAULT_LANGUAGE): def findT(path, language=DEFAULT_LANGUAGE):
""" """
must be run by the admin app Note:
Must be run by the admin app
""" """
lang_file = pjoin(path, 'languages', language + '.py') lang_file = pjoin(path, 'languages', language + '.py')
sentences = read_dict(lang_file) sentences = read_dict(lang_file)
@@ -954,6 +976,10 @@ def findT(path, language=DEFAULT_LANGUAGE):
write_dict(lang_file, sentences) write_dict(lang_file, sentences)
def update_all_languages(application_path): def update_all_languages(application_path):
"""
Note:
Must be run by the admin app
"""
path = pjoin(application_path, 'languages/') path = pjoin(application_path, 'languages/')
for language in oslistdir(path): for language in oslistdir(path):
if regex_langfile.match(language): if regex_langfile.match(language):
+19 -23
View File
@@ -2,14 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Contains:
- wsgibase: the gluon wsgi application
The gluon wsgi application
---------------------------
""" """
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
@@ -135,9 +133,9 @@ HTTPS_SCHEMES = set(('https', 'HTTPS'))
def get_client(env): def get_client(env):
""" """
guess the client address from the environment variables Guesses the client address from the environment variables
first tries 'http_x_forwarded_for', secondly 'remote_addr' First tries 'http_x_forwarded_for', secondly 'remote_addr'
if all fails, assume '127.0.0.1' or '::1' (running locally) if all fails, assume '127.0.0.1' or '::1' (running locally)
""" """
eget = env.get eget = env.get
@@ -160,7 +158,7 @@ def get_client(env):
def serve_controller(request, response, session): def serve_controller(request, response, session):
""" """
this function is used to generate a dynamic page. This function is used to generate a dynamic page.
It first runs all models, then runs the function in the controller, It first runs all models, then runs the function in the controller,
and then tries to render the output using a view/template. and then tries to render the output using a view/template.
this function must run from the [application] folder. this function must run from the [application] folder.
@@ -234,7 +232,7 @@ class LazyWSGI(object):
return self._environ return self._environ
def start_response(self,status='200', headers=[], exec_info=None): def start_response(self,status='200', headers=[], exec_info=None):
""" """
in controller you can use:: in controller you can use:
- request.wsgi.environ - request.wsgi.environ
- request.wsgi.start_response - request.wsgi.start_response
@@ -249,7 +247,7 @@ class LazyWSGI(object):
""" """
In you controller use:: In you controller use::
@request.wsgi.middleware(middleware1, middleware2, ...) @request.wsgi.middleware(middleware1, middleware2, ...)
to decorate actions with WSGI middleware. actions must return strings. to decorate actions with WSGI middleware. actions must return strings.
uses a simulated environment so it may have weird behavior in some cases uses a simulated environment so it may have weird behavior in some cases
@@ -271,9 +269,9 @@ class LazyWSGI(object):
def wsgibase(environ, responder): def wsgibase(environ, responder):
""" """
this is the gluon wsgi application. the first function called when a page The gluon wsgi application. The first function called when a page
is requested (static or dynamic). it can be called by paste.httpserver is requested (static or dynamic). It can be called by paste.httpserver
or by apache mod_wsgi. or by apache mod_wsgi (or any WSGI-compatible server).
- fills request with info - fills request with info
- the environment variables, replacing '.' with '_' - the environment variables, replacing '.' with '_'
@@ -290,13 +288,11 @@ def wsgibase(environ, responder):
2. for dynamic pages: 2. for dynamic pages:
- /<application>[/<controller>[/<function>[/<sub>]]][.<extension>] - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>]
- (sub may go several levels deep, currently 3 levels are supported:
sub1/sub2/sub3)
The naming conventions are: The naming conventions are:
- application, controller, function and extension may only contain - application, controller, function and extension may only contain
[a-zA-Z0-9_] `[a-zA-Z0-9_]`
- file and sub may also contain '-', '=', '.' and '/' - file and sub may also contain '-', '=', '.' and '/'
""" """
eget = environ.get eget = environ.get
@@ -567,7 +563,7 @@ def wsgibase(environ, responder):
def save_password(password, port): def save_password(password, port):
""" """
used by main() to save the password in the parameters_port.py file. Used by main() to save the password in the parameters_port.py file.
""" """
password_file = abspath('parameters_%i.py' % port) password_file = abspath('parameters_%i.py' % port)
@@ -607,10 +603,10 @@ def appfactory(wsgiapp=wsgibase,
generates a wsgi application that does logging and profiling and calls generates a wsgi application that does logging and profiling and calls
wsgibase wsgibase
.. function:: gluon.main.appfactory( Args:
[wsgiapp=wsgibase wsgiapp: the base application
[, logfilename='httpserver.log' logfilename: where to store apache-compatible requests log
[, profilerfilename='profiler.log']]]) profiler_dir: where to store profile files
""" """
if profilerfilename is not None: if profilerfilename is not None:
+6 -3
View File
@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Useful regexes
---------------
""" """
import re import re
+10 -7
View File
@@ -2,8 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created by Attila Csipa <web2py@csipa.in.rs> | This file is part of the web2py Web Framework
Modified by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Created by Attila Csipa <web2py@csipa.in.rs>
| Modified by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Cron-style interface
""" """
import sys import sys
@@ -27,7 +31,7 @@ _cron_subprocs = []
def absolute_path_link(path): def absolute_path_link(path):
""" """
Return an absolute path for the destination of a symlink Returns an absolute path for the destination of a symlink
""" """
if os.path.islink(path): if os.path.islink(path):
@@ -40,7 +44,7 @@ def absolute_path_link(path):
def stopcron(): def stopcron():
"graceful shutdown of cron" "Graceful shutdown of cron"
global _cron_stopping global _cron_stopping
_cron_stopping = True _cron_stopping = True
while _cron_subprocs: while _cron_subprocs:
@@ -107,7 +111,7 @@ class Token(object):
def acquire(self, startup=False): def acquire(self, startup=False):
""" """
returns the time when the lock is acquired or Returns the time when the lock is acquired or
None if cron already running None if cron already running
lock is implemented by writing a pickle (start, stop) in cron.master lock is implemented by writing a pickle (start, stop) in cron.master
@@ -150,8 +154,7 @@ class Token(object):
def release(self): def release(self):
""" """
this function writes into cron.master the time when cron job Writes into cron.master the time when cron job was completed
was completed
""" """
if not self.master.closed: if not self.master.closed:
portalocker.lock(self.master, portalocker.LOCK_EX) portalocker.lock(self.master, portalocker.LOCK_EX)
+5 -7
View File
@@ -1,13 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# portalocker.py
# Cross-platform (posix/nt) API for flock-style file locking.
# Requires python 1.5.2 or better.
""" """
Cross-platform (posix/nt) API for flock-style file locking. Cross-platform (posix/nt) API for flock-style file locking.
Synopsis: Synopsis::
import portalocker import portalocker
file = open(\"somefile\", \"r+\") file = open(\"somefile\", \"r+\")
@@ -16,18 +13,18 @@ Synopsis:
file.write(\"foo\") file.write(\"foo\")
file.close() file.close()
If you know what you're doing, you may choose to If you know what you're doing, you may choose to::
portalocker.unlock(file) portalocker.unlock(file)
before closing the file, but why? before closing the file, but why?
Methods: Methods::
lock( file, flags ) lock( file, flags )
unlock( file ) unlock( file )
Constants: Constants::
LOCK_EX LOCK_EX
LOCK_SH LOCK_SH
@@ -163,6 +160,7 @@ def write_locked(filename, data):
fp.close() fp.close()
if __name__ == '__main__': if __name__ == '__main__':
import sys
f = LockedFile('test.txt', mode='wb') f = LockedFile('test.txt', mode='wb')
f.write('test ok') f.write('test ok')
f.close() f.close()
+1
View File
@@ -22,6 +22,7 @@ __author__ = "Thadeus Burgess <thadeusb@thadeusb.com>"
# These are keywords that are common to all SQL dialects, and should # These are keywords that are common to all SQL dialects, and should
# never be used as a table or column. Even if you use one of these # never be used as a table or column. Even if you use one of these
# the cursor will throw an OperationalError for the SQL syntax. # the cursor will throw an OperationalError for the SQL syntax.
COMMON = set(( COMMON = set((
'SELECT', 'SELECT',
'INSERT', 'INSERT',
+17 -14
View File
@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Restricted environment to execute application's code
-----------------------------------------------------
""" """
import sys import sys
@@ -26,7 +29,7 @@ __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
class TicketStorage(Storage): class TicketStorage(Storage):
""" """
defines the ticket object and the default values of its members (None) Defines the ticket object and the default values of its members (None)
""" """
def __init__( def __init__(
@@ -40,7 +43,7 @@ class TicketStorage(Storage):
def store(self, request, ticket_id, ticket_data): def store(self, request, ticket_id, ticket_data):
""" """
stores the ticket. It will figure out if this must be on disk or in db Stores the ticket. It will figure out if this must be on disk or in db
""" """
if self.db: if self.db:
self._store_in_db(request, ticket_id, ticket_data) self._store_in_db(request, ticket_id, ticket_data)
@@ -51,12 +54,12 @@ class TicketStorage(Storage):
self.db._adapter.reconnect() self.db._adapter.reconnect()
try: try:
table = self._get_table(self.db, self.tablename, request.application) table = self._get_table(self.db, self.tablename, request.application)
id = table.insert(ticket_id=ticket_id, table.insert(ticket_id=ticket_id,
ticket_data=cPickle.dumps(ticket_data), ticket_data=cPickle.dumps(ticket_data),
created_datetime=request.now) created_datetime=request.now)
self.db.commit() self.db.commit()
message = 'In FILE: %(layer)s\n\n%(traceback)s\n' message = 'In FILE: %(layer)s\n\n%(traceback)s\n'
except Exception, e: except Exception:
self.db.rollback() self.db.rollback()
message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n' message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n'
self.db.close() self.db.close()
@@ -111,8 +114,8 @@ class TicketStorage(Storage):
class RestrictedError(Exception): class RestrictedError(Exception):
""" """
class used to wrap an exception that occurs in the restricted environment Class used to wrap an exception that occurs in the restricted environment
below. the traceback is used to log the exception and generate a ticket. below. The traceback is used to log the exception and generate a ticket.
""" """
def __init__( def __init__(
@@ -123,7 +126,7 @@ class RestrictedError(Exception):
environment=None, environment=None,
): ):
""" """
layer here is some description of where in the system the exception Layer here is some description of where in the system the exception
occurred. occurred.
""" """
if environment is None: if environment is None:
@@ -148,7 +151,7 @@ class RestrictedError(Exception):
def log(self, request): def log(self, request):
""" """
logs the exception. Logs the exception.
""" """
try: try:
@@ -168,7 +171,7 @@ class RestrictedError(Exception):
def load(self, request, app, ticket_id): def load(self, request, app, ticket_id):
""" """
loads a logged exception. Loads a logged exception.
""" """
ticket_storage = TicketStorage(db=request.tickets_db) ticket_storage = TicketStorage(db=request.tickets_db)
d = ticket_storage.load(request, app, ticket_id) d = ticket_storage.load(request, app, ticket_id)
@@ -201,8 +204,8 @@ def compile2(code, layer):
def restricted(code, environment=None, layer='Unknown'): def restricted(code, environment=None, layer='Unknown'):
""" """
runs code in environment and returns the output. if an exception occurs Runs code in environment and returns the output. If an exception occurs
in code it raises a RestrictedError containing the traceback. layer is in code it raises a RestrictedError containing the traceback. Layer is
passed to RestrictedError to identify where the error occurred. passed to RestrictedError to identify where the error occurred.
""" """
if environment is None: if environment is None:
+53 -54
View File
@@ -2,9 +2,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
This file is part of the web2py Web Framework | This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu> | Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
gluon.rewrite parses incoming URLs and formats outgoing URLs for gluon.html.URL. gluon.rewrite parses incoming URLs and formats outgoing URLs for gluon.html.URL.
@@ -57,7 +57,7 @@ regex_args = re.compile('[^\w/.@=-]')
def _router_default(): def _router_default():
"return new copy of default base router" "Returns new copy of default base router"
router = Storage( router = Storage(
default_application='init', default_application='init',
applications='ALL', applications='ALL',
@@ -85,7 +85,7 @@ def _router_default():
def _params_default(app=None): def _params_default(app=None):
"return new copy of default parameters" "Returns a new copy of default parameters"
p = Storage() p = Storage()
p.name = app or "BASE" p.name = app or "BASE"
p.default_application = app or "init" p.default_application = app or "init"
@@ -181,7 +181,7 @@ def fixup_missing_path_info(environ):
def url_in(request, environ): def url_in(request, environ):
"parse and rewrite incoming URL" "Parses and rewrites incoming URL"
if routers: if routers:
return map_url_in(request, environ) return map_url_in(request, environ)
return regex_url_in(request, environ) return regex_url_in(request, environ)
@@ -189,7 +189,7 @@ def url_in(request, environ):
def url_out(request, environ, application, controller, function, def url_out(request, environ, application, controller, function,
args, other, scheme, host, port, language=None): args, other, scheme, host, port, language=None):
"assemble and rewrite outgoing URL" "Assembles and rewrites outgoing URL"
if routers: if routers:
acf = map_url_out(request, environ, application, controller, acf = map_url_out(request, environ, application, controller,
function, args, other, scheme, host, port, language) function, args, other, scheme, host, port, language)
@@ -214,7 +214,7 @@ def url_out(request, environ, application, controller, function,
def try_rewrite_on_error(http_response, request, environ, ticket=None): def try_rewrite_on_error(http_response, request, environ, ticket=None):
""" """
called from main.wsgibase to rewrite the http response. Called from main.wsgibase to rewrite the http response.
""" """
status = int(str(http_response.status).split()[0]) status = int(str(http_response.status).split()[0])
if status >= 399 and THREAD_LOCAL.routes.routes_onerror: if status >= 399 and THREAD_LOCAL.routes.routes_onerror:
@@ -255,7 +255,7 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
def try_redirect_on_error(http_object, request, ticket=None): def try_redirect_on_error(http_object, request, ticket=None):
"called from main.wsgibase to rewrite the http response" "Called from main.wsgibase to rewrite the http response"
status = int(str(http_object.status).split()[0]) status = int(str(http_object.status).split()[0])
if status > 399 and THREAD_LOCAL.routes.routes_onerror: if status > 399 and THREAD_LOCAL.routes.routes_onerror:
keys = set(('%s/%s' % (request.application, status), keys = set(('%s/%s' % (request.application, status),
@@ -392,7 +392,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
def compile_regex(k, v, env=None): def compile_regex(k, v, env=None):
""" """
Preprocess and compile the regular expressions in routes_app/in/out Preprocess and compile the regular expressions in routes_app/in/out
The resulting regex will match a pattern of the form: The resulting regex will match a pattern of the form::
[remote address]:[protocol]://[host]:[method] [path] [remote address]:[protocol]://[host]:[method] [path]
@@ -427,7 +427,7 @@ def compile_regex(k, v, env=None):
def load_routers(all_apps): def load_routers(all_apps):
"load-time post-processing of routers" "Load-time post-processing of routers"
for app in routers: for app in routers:
# initialize apps with routers that aren't present, # initialize apps with routers that aren't present,
@@ -533,7 +533,7 @@ def load_routers(all_apps):
def regex_uri(e, regexes, tag, default=None): def regex_uri(e, regexes, tag, default=None):
"filter incoming URI against a list of regexes" "Filters incoming URI against a list of regexes"
path = e['PATH_INFO'] path = e['PATH_INFO']
host = e.get('HTTP_HOST', e.get('SERVER_NAME', 'localhost')).lower() host = e.get('HTTP_HOST', e.get('SERVER_NAME', 'localhost')).lower()
i = host.find(':') i = host.find(':')
@@ -555,7 +555,7 @@ def regex_uri(e, regexes, tag, default=None):
def regex_select(env=None, app=None, request=None): def regex_select(env=None, app=None, request=None):
""" """
select a set of regex rewrite params for the current request Selects a set of regex rewrite params for the current request
""" """
if app: if app:
THREAD_LOCAL.routes = params_apps.get(app, params) THREAD_LOCAL.routes = params_apps.get(app, params)
@@ -572,7 +572,7 @@ def regex_select(env=None, app=None, request=None):
def regex_filter_in(e): def regex_filter_in(e):
"regex rewrite incoming URL" "Regex rewrite incoming URL"
routes = THREAD_LOCAL.routes routes = THREAD_LOCAL.routes
query = e.get('QUERY_STRING', None) query = e.get('QUERY_STRING', None)
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '') e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
@@ -603,7 +603,7 @@ def invalid_url(routes):
web2py_error='invalid path') web2py_error='invalid path')
def regex_url_in(request, environ): def regex_url_in(request, environ):
"rewrite and parse incoming URL" "Rewrites and parses incoming URL"
# ################################################## # ##################################################
# select application # select application
@@ -667,7 +667,7 @@ def regex_url_in(request, environ):
def regex_filter_out(url, e=None): def regex_filter_out(url, e=None):
"regex rewrite outgoing URL" "Regex rewrite outgoing URL"
if not hasattr(THREAD_LOCAL, 'routes'): if not hasattr(THREAD_LOCAL, 'routes'):
regex_select() # ensure routes is set (for application threads) regex_select() # ensure routes is set (for application threads)
routes = THREAD_LOCAL.routes routes = THREAD_LOCAL.routes
@@ -810,10 +810,10 @@ def filter_err(status, application='app', ticket='tkt'):
class MapUrlIn(object): class MapUrlIn(object):
"logic for mapping incoming URLs" "Logic for mapping incoming URLs"
def __init__(self, request=None, env=None): def __init__(self, request=None, env=None):
"initialize a map-in object" "Initializes a map-in object"
self.request = request self.request = request
self.env = env self.env = env
@@ -861,7 +861,7 @@ class MapUrlIn(object):
self.port = '443' if self.scheme == 'https' else '80' self.port = '443' if self.scheme == 'https' else '80'
def map_prefix(self): def map_prefix(self):
"strip path prefix, if present in its entirety" "Strips path prefix, if present in its entirety"
prefix = routers.BASE.path_prefix prefix = routers.BASE.path_prefix
if prefix: if prefix:
prefixlen = len(prefix) prefixlen = len(prefix)
@@ -873,7 +873,7 @@ class MapUrlIn(object):
self.args = List(self.args[prefixlen:]) # strip the prefix self.args = List(self.args[prefixlen:]) # strip the prefix
def map_app(self): def map_app(self):
"determine application name" "Determines application name"
base = routers.BASE # base router base = routers.BASE # base router
self.domain_application = None self.domain_application = None
self.domain_controller = None self.domain_controller = None
@@ -935,12 +935,12 @@ class MapUrlIn(object):
self._args_match = self.router._args_match self._args_match = self.router._args_match
def map_root_static(self): def map_root_static(self):
''' """
handle root-static files (no hyphen mapping) Handles root-static files (no hyphen mapping)
a root-static file is one whose incoming URL expects it to be at the root, a root-static file is one whose incoming URL expects it to be at the root,
typically robots.txt & favicon.ico typically robots.txt & favicon.ico
''' """
if len(self.args) == 1 and self.arg0 in self.router.root_static: if len(self.args) == 1 and self.arg0 in self.router.root_static:
self.controller = self.request.controller = 'static' self.controller = self.request.controller = 'static'
@@ -952,7 +952,7 @@ class MapUrlIn(object):
return None, None return None, None
def map_language(self): def map_language(self):
"handle language (no hyphen mapping)" "Handles language (no hyphen mapping)"
arg0 = self.arg0 # no hyphen mapping arg0 = self.arg0 # no hyphen mapping
if arg0 and self.languages and arg0 in self.languages: if arg0 and self.languages and arg0 in self.languages:
self.language = arg0 self.language = arg0
@@ -964,7 +964,7 @@ class MapUrlIn(object):
arg0 = self.arg0 arg0 = self.arg0
def map_controller(self): def map_controller(self):
"identify controller" "Identifies controller"
# handle controller # handle controller
# #
arg0 = self.harg0 # map hyphens arg0 = self.harg0 # map hyphens
@@ -981,7 +981,7 @@ class MapUrlIn(object):
def map_static(self): def map_static(self):
''' '''
handle static files Handles static files
file_match but no hyphen mapping file_match but no hyphen mapping
''' '''
if self.controller != 'static': if self.controller != 'static':
@@ -1025,7 +1025,7 @@ class MapUrlIn(object):
return static_file, version return static_file, version
def map_function(self): def map_function(self):
"handle function.extension" "Handles function.extension"
arg0 = self.harg0 # map hyphens arg0 = self.harg0 # map hyphens
functions = self.functions.get(self.controller, set()) functions = self.functions.get(self.controller, set())
if isinstance(self.router.default_function, dict): if isinstance(self.router.default_function, dict):
@@ -1058,9 +1058,9 @@ class MapUrlIn(object):
web2py_error='invalid extension') web2py_error='invalid extension')
def validate_args(self): def validate_args(self):
''' """
check args against validation pattern Checks args against validation pattern
''' """
for arg in self.args: for arg in self.args:
if not self.router._args_match.match(arg): if not self.router._args_match.match(arg):
raise HTTP( raise HTTP(
@@ -1068,16 +1068,15 @@ class MapUrlIn(object):
web2py_error='invalid arg <%s>' % arg) web2py_error='invalid arg <%s>' % arg)
def sluggify(self): def sluggify(self):
""
self.request.env.update( self.request.env.update(
(k.lower().replace('.', '_'), v) for k, v in self.env.iteritems()) (k.lower().replace('.', '_'), v) for k, v in self.env.iteritems())
def update_request(self): def update_request(self):
''' """
update request from self Updates request from self
build env.request_uri Builds env.request_uri
make lower-case versions of http headers in env Makes lower-case versions of http headers in env
''' """
self.request.application = self.application self.request.application = self.application
self.request.controller = self.controller self.request.controller = self.controller
self.request.function = self.function self.request.function = self.function
@@ -1105,24 +1104,24 @@ class MapUrlIn(object):
@property @property
def arg0(self): def arg0(self):
"return first arg" "Returns first arg"
return self.args(0) return self.args(0)
@property @property
def harg0(self): def harg0(self):
"return first arg with optional hyphen mapping" "Returns first arg with optional hyphen mapping"
if self.map_hyphen and self.args(0): if self.map_hyphen and self.args(0):
return self.args(0).replace('-', '_') return self.args(0).replace('-', '_')
return self.args(0) return self.args(0)
def pop_arg_if(self, dopop): def pop_arg_if(self, dopop):
"conditionally remove first arg and return new first arg" "Conditionally removes first arg and returns new first arg"
if dopop: if dopop:
self.args.pop(0) self.args.pop(0)
class MapUrlOut(object): class MapUrlOut(object):
"logic for mapping outgoing URLs" "Logic for mapping outgoing URLs"
def __init__(self, request, env, application, controller, def __init__(self, request, env, application, controller,
function, args, other, scheme, host, port, language): function, args, other, scheme, host, port, language):
@@ -1179,13 +1178,13 @@ class MapUrlOut(object):
self.omit_function = False self.omit_function = False
def omit_lang(self): def omit_lang(self):
"omit language if possible" "Omits language if possible"
if not self.language or self.language == self.default_language: if not self.language or self.language == self.default_language:
self.omit_language = True self.omit_language = True
def omit_acf(self): def omit_acf(self):
"omit what we can of a/c/f" "Omits what we can of a/c/f"
router = self.router router = self.router
@@ -1254,7 +1253,7 @@ class MapUrlOut(object):
self.omit_function = False self.omit_function = False
def build_acf(self): def build_acf(self):
"build acf from components" "Builds a/c/f from components"
acf = '' acf = ''
if self.map_hyphen: if self.map_hyphen:
self.application = self.application.replace('_', '-') self.application = self.application.replace('_', '-')
@@ -1280,7 +1279,7 @@ class MapUrlOut(object):
return acf or '/' return acf or '/'
def acf(self): def acf(self):
"convert components to /app/lang/controller/function" "Converts components to /app/lang/controller/function"
if not routers: if not routers:
return None # use regex filter return None # use regex filter
@@ -1290,7 +1289,7 @@ class MapUrlOut(object):
def map_url_in(request, env, app=False): def map_url_in(request, env, app=False):
"route incoming URL" "Routes incoming URL"
# initialize router-url object # initialize router-url object
# #
@@ -1333,8 +1332,8 @@ def map_url_in(request, env, app=False):
def map_url_out(request, env, application, controller, def map_url_out(request, env, application, controller,
function, args, other, scheme, host, port, language=None): function, args, other, scheme, host, port, language=None):
''' """
supply /a/c/f (or /a/lang/c/f) portion of outgoing url Supply /a/c/f (or /a/lang/c/f) portion of outgoing url
The basic rule is that we can only make transformations The basic rule is that we can only make transformations
that map_url_in can reverse. that map_url_in can reverse.
@@ -1342,14 +1341,14 @@ def map_url_out(request, env, application, controller,
Suppose that the incoming arguments are a,c,f,args,lang Suppose that the incoming arguments are a,c,f,args,lang
and that the router defaults are da, dc, df, dl. and that the router defaults are da, dc, df, dl.
We can perform these transformations trivially if args=[] and lang=None or dl: We can perform these transformations trivially if args=[] and lang=None or dl::
/da/dc/df => / /da/dc/df => /
/a/dc/df => /a /a/dc/df => /a
/a/c/df => /a/c /a/c/df => /a/c
We would also like to be able to strip the default application or application/controller We would also like to be able to strip the default application or application/controller
from URLs with function/args present, thus: from URLs with function/args present, thus::
/da/c/f/args => /c/f/args /da/c/f/args => /c/f/args
/da/dc/f/args => /f/args /da/dc/f/args => /f/args
@@ -1357,14 +1356,14 @@ def map_url_out(request, env, application, controller,
We use [applications] and [controllers] and {functions} to suppress ambiguous omissions. We use [applications] and [controllers] and {functions} to suppress ambiguous omissions.
We assume that language names do not collide with a/c/f names. We assume that language names do not collide with a/c/f names.
''' """
map = MapUrlOut(request, env, application, controller, map = MapUrlOut(request, env, application, controller,
function, args, other, scheme, host, port, language) function, args, other, scheme, host, port, language)
return map.acf() return map.acf()
def get_effective_router(appname): def get_effective_router(appname):
"return a private copy of the effective router for the specified application" "Returns a private copy of the effective router for the specified application"
if not routers or appname not in routers: if not routers or appname not in routers:
return None return None
return Storage(routers[appname]) # return a copy return Storage(routers[appname]) # return a copy
+7 -3
View File
@@ -2599,8 +2599,11 @@ class SQLFORM(FORM):
for k, v in sorted(exportManager.items()): for k, v in sorted(exportManager.items()):
if not v: if not v:
continue continue
label = v[1] if hasattr(v, "__getitem__") else k if hasattr(v, "__getitem__"):
title = v[2] if hasattr(v, "__getitem__") else label label = v[1]
title = v[2] if len(v)>2 else label
else:
label = title = k
link = url2(vars=dict( link = url2(vars=dict(
order=request.vars.order or '', order=request.vars.order or '',
_export_type=k, _export_type=k,
@@ -2926,7 +2929,8 @@ class SQLTABLE(TABLE):
if not sqlrows: if not sqlrows:
return return
if not columns: if not columns:
columns = ['.'.join(sqlrows.db._adapter.REGEX_TABLE_DOT_FIELD.match(c).groups()) for c in sqlrows.colnames] REGEX_TABLE_DOT_FIELD = sqlrows.db._adapter.REGEX_TABLE_DOT_FIELD
columns = [c for c in sqlrows.colnames if REGEX_TABLE_DOT_FIELD.match(c)]
if headers == 'fieldname:capitalize': if headers == 'fieldname:capitalize':
headers = {} headers = {}
for c in columns: for c in columns:
+45 -17
View File
@@ -757,7 +757,8 @@ class Recaptcha(DIV):
error_message='invalid', error_message='invalid',
label='Verify:', label='Verify:',
options='', options='',
comment = '' comment = '',
ajax=False
): ):
self.request_vars = request and request.vars or current.request.vars self.request_vars = request and request.vars or current.request.vars
self.remote_addr = request.env.remote_addr self.remote_addr = request.env.remote_addr
@@ -772,6 +773,7 @@ class Recaptcha(DIV):
self.label = label self.label = label
self.options = options self.options = options
self.comment = comment self.comment = comment
self.ajax = ajax
def _validate(self): def _validate(self):
@@ -825,18 +827,43 @@ class Recaptcha(DIV):
server = self.API_SSL_SERVER server = self.API_SSL_SERVER
else: else:
server = self.API_SERVER server = self.API_SERVER
captcha = DIV( if not self.ajax:
SCRIPT("var RecaptchaOptions = {%s};" % self.options), captcha = DIV(
SCRIPT(_type="text/javascript", SCRIPT("var RecaptchaOptions = {%s};" % self.options),
_src="%s/challenge?k=%s%s" % (server, public_key, error_param)), SCRIPT(_type="text/javascript",
TAG.noscript( _src="%s/challenge?k=%s%s" % (server, public_key, error_param)),
IFRAME( TAG.noscript(
_src="%s/noscript?k=%s%s" % ( IFRAME(
server, public_key, error_param), _src="%s/noscript?k=%s%s" % (
_height="300", _width="500", _frameborder="0"), BR(), server, public_key, error_param),
INPUT( _height="300", _width="500", _frameborder="0"), BR(),
_type='hidden', _name='recaptcha_response_field', INPUT(
_value='manual_challenge')), _id='recaptcha') _type='hidden', _name='recaptcha_response_field',
_value='manual_challenge')), _id='recaptcha')
else: #use Google's ajax interface, needed for LOADed components
url_recaptcha_js = "%s/js/recaptcha_ajax.js" % server
RecaptchaOptions = "var RecaptchaOptions = {%s}" % self.options
script = """%(options)s;
jQuery.getScript('%(url)s',function() {
Recaptcha.create('%(public_key)s',
'recaptcha',jQuery.extend(RecaptchaOptions,{'callback':Recaptcha.focus_response_field}))
}) """ % ({'options':RecaptchaOptions,'url':url_recaptcha_js,'public_key':public_key})
captcha = DIV(
SCRIPT(
script,
_type="text/javascript",
),
TAG.noscript(
IFRAME(
_src="%s/noscript?k=%s%s" % (
server, public_key, error_param),
_height="300", _width="500", _frameborder="0"), BR(),
INPUT(
_type='hidden', _name='recaptcha_response_field',
_value='manual_challenge')), _id='recaptcha')
if not self.errors.captcha: if not self.errors.captcha:
return XML(captcha).xml() return XML(captcha).xml()
else: else:
@@ -2035,8 +2062,9 @@ class Auth(object):
if user and user.get(settings.passfield, False): if user and user.get(settings.passfield, False):
password = settings.table_user[ password = settings.table_user[
settings.passfield].validate(password)[0] settings.passfield].validate(password)[0]
if not user.registration_key.strip() and password == \ if ((user.registration_key is None or
user[settings.passfield]: not user.registration_key.strip()) and
password == user[settings.passfield]):
self.login_user(user) self.login_user(user)
return user return user
else: else:
@@ -2322,8 +2350,8 @@ class Auth(object):
elif temp_user.registration_key in ('disabled', 'blocked'): elif temp_user.registration_key in ('disabled', 'blocked'):
response.flash = self.messages.login_disabled response.flash = self.messages.login_disabled
return form return form
elif not temp_user.registration_key is None and \ elif (not temp_user.registration_key is None
temp_user.registration_key.strip(): and temp_user.registration_key.strip()):
response.flash = \ response.flash = \
self.messages.registration_verifying self.messages.registration_verifying
return form return form
+2 -2
View File
@@ -17,10 +17,10 @@ import cgi
import urllib import urllib
import struct import struct
import decimal import decimal
import unicodedata
from cStringIO import StringIO from cStringIO import StringIO
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
from gluon.dal import FieldVirtual, FieldMethod from gluon.dal import FieldVirtual, FieldMethod
from gluon.contrib import translitcodec
regex_isint = re.compile('^[+-]?\d+$') regex_isint = re.compile('^[+-]?\d+$')
@@ -2519,7 +2519,7 @@ def urlify(s, maxlen=80, keep_underscores=False):
if isinstance(s, str): if isinstance(s, str):
s = s.decode('utf-8') # to unicode s = s.decode('utf-8') # to unicode
s = s.lower() # to lowercase s = s.lower() # to lowercase
s = s.encode('translit/long') # replace special characters s = unicodedata.normalize('NFKD', s) # replace special characters
s = s.encode('ascii', 'ignore') # encode as ASCII s = s.encode('ascii', 'ignore') # encode as ASCII
s = re.sub('&\w+?;', '', s) # strip html entities s = re.sub('&\w+?;', '', s) # strip html entities
if keep_underscores: if keep_underscores:
+4 -4
View File
@@ -1222,12 +1222,12 @@ end tell
url = get_url(ip, proto=proto, port=port) url = get_url(ip, proto=proto, port=port)
if not options.nobanner: if not options.nobanner:
print 'please visit:' message = '\nplease visit:\n\t%s\n' % url
print '\t', url
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
print 'use "taskkill /f /pid %i" to shutdown the web2py server' % os.getpid() message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n' % os.getpid()
else: else:
print 'use "kill -SIGTERM %i" to shutdown the web2py server' % os.getpid() message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n' % os.getpid()
print message
# enhance linecache.getline (used by debugger) to look at the source file # enhance linecache.getline (used by debugger) to look at the source file
# if the line was not found (under py2exe & when file was modified) # if the line was not found (under py2exe & when file was modified)