Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2802e29945 | ||
|
|
e9eb8cbfc7 | ||
|
|
d343d8380c | ||
|
|
02906fa39f | ||
|
|
c1e797fe24 | ||
|
|
d7a42d8445 | ||
|
|
4f82bed52e | ||
|
|
84365f6721 | ||
|
|
e209dcc8d3 | ||
|
|
cd15a0f983 | ||
|
|
eb435e785c | ||
|
|
f03e521120 | ||
|
|
883909af4b | ||
|
|
13d66433e7 | ||
|
|
c44cfcb27c | ||
|
|
c14e5cbae5 | ||
|
|
f09e0b4205 | ||
|
|
ad72cea9a6 | ||
|
|
c289bc4239 | ||
|
|
733fa01b7e | ||
|
|
4d03460944 | ||
|
|
b3ba5d9eaa | ||
|
|
9e8ef3585e | ||
|
|
5c9cf44720 | ||
|
|
05a0d19ee9 | ||
|
|
d38adbe6ca | ||
|
|
871981cec7 | ||
|
|
8a54001e11 | ||
|
|
1e59f6e8be | ||
|
|
0e1eb5b56e | ||
|
|
e43df945b4 | ||
|
|
c23706b794 | ||
|
|
98cd10c6f5 | ||
|
|
55b92e854c | ||
|
|
0c926d60b8 | ||
|
|
d4d91fd003 | ||
|
|
89c4efbac3 | ||
|
|
3a5a34da0a | ||
|
|
cf3992545d | ||
|
|
0b7f663d43 | ||
|
|
abd72f8df0 |
10
.travis.yml
10
.travis.yml
@@ -22,13 +22,9 @@ before_script:
|
||||
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
|
||||
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
|
||||
|
||||
# Install last sdk for app engine
|
||||
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/svn/trunk/python/VERSION -O ./GAEVERSION; fi
|
||||
- if [[ $DB == google* ]]; then GAERELEASE=$(cat ./GAEVERSION | grep -i release); 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
|
||||
# Install last sdk for app engine (update only whenever a new release is available)
|
||||
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_1.8.9.zip -nv; fi
|
||||
- if [[ $DB == google* ]]; then unzip -q google_appengine_1.8.9.zip; fi
|
||||
- if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi
|
||||
|
||||
- if [[ $DB == mongodb* ]]; then pip install pymongo; fi
|
||||
|
||||
@@ -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
|
||||
- codemirror 3.21, thanks Paolo Valleri
|
||||
- fixed security issue with sessions in database, thanks Nathan Humphreys
|
||||
@@ -20,7 +21,6 @@
|
||||
- invalidate function in web2py.js, thanks Paolo
|
||||
- DAL(...,adapter_args=dict(engine='MyISAM'))
|
||||
- todolist panel in admin editor, thanks Paolo Valleri
|
||||
- enable killing processed in windows, thanks Yair
|
||||
|
||||
## 2.8.1
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -30,7 +30,7 @@ update:
|
||||
echo "remember that pymysql was tweaked"
|
||||
src:
|
||||
### 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
|
||||
make clean
|
||||
### clean up baisc apps
|
||||
|
||||
2
VERSION
2
VERSION
@@ -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
|
||||
|
||||
@@ -1937,7 +1937,7 @@ def install_plugin():
|
||||
session.flash = T('New plugin installed: %s', filename)
|
||||
else:
|
||||
session.flash = \
|
||||
T('unable to create application "%s"', filename)
|
||||
T('unable to install plugin "%s"', filename)
|
||||
redirect(URL(f="plugins", args=[app,]))
|
||||
return dict(form=form, app=app, plugin=plugin, source=source)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<ul>
|
||||
<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.wadecybertech.com">Wade Cybertech</a> (Canada)</li>
|
||||
<li><a target="_blank" href="http://www.formatics.nl">Formatics</a> (Netherlands)</li>
|
||||
|
||||
@@ -12,7 +12,6 @@ response.subtitle = ''
|
||||
|
||||
## read more at http://dev.w3.org/html5/markup/meta.name.html
|
||||
response.meta.author = 'Your Name <you@example.com>'
|
||||
response.meta.description = 'a cool new app'
|
||||
response.meta.keywords = 'web2py, python, framework'
|
||||
response.meta.generator = 'Web2py Web Framework'
|
||||
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
from globals import current
|
||||
from html 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:
|
||||
try:
|
||||
data = open_url.read()
|
||||
resp_type = open_url.info().get('Content-Type')
|
||||
resp_type = open_url.info().gettype()
|
||||
# try json style first
|
||||
if not resp_type or resp_type == 'application/json':
|
||||
if not resp_type or resp_type[:16] == 'application/json':
|
||||
try:
|
||||
tokendata = json.loads(data)
|
||||
current.session.token = tokendata
|
||||
|
||||
@@ -111,7 +111,7 @@ class MockTable(object):
|
||||
self.session_expiry = session_expiry
|
||||
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)
|
||||
|
||||
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)
|
||||
q.op = 'eq'
|
||||
q.value = record_id
|
||||
q.unique_key = unique_key
|
||||
|
||||
row = q.select()
|
||||
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
|
||||
self.query = MockQuery(field='id', db=self.r_server,
|
||||
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
|
||||
elif key == '_db':
|
||||
#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
|
||||
"""
|
||||
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.value = None
|
||||
self.db = db
|
||||
@@ -170,6 +171,7 @@ class MockQuery(object):
|
||||
self.op = None
|
||||
self.session_expiry = session_expiry
|
||||
self.with_lock = with_lock
|
||||
self.unique_key = unique_key
|
||||
|
||||
def __eq__(self, value, op='eq'):
|
||||
self.value = value
|
||||
@@ -187,7 +189,12 @@ class MockQuery(object):
|
||||
acquire_lock(self.db, key + ':lock', self.value)
|
||||
rtn = self.db.hgetall(key)
|
||||
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 []
|
||||
elif self.op == 'ge' and self.field == 'id' and self.value == 0:
|
||||
#means that someone wants the complete list
|
||||
|
||||
@@ -81,12 +81,13 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
||||
class ServerProxy(object):
|
||||
"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.trace = verbose # show debug messages
|
||||
self.exceptions = True # raise errors? (JSONRPCError)
|
||||
self.timeout = None
|
||||
self.json_request = self.json_response = ''
|
||||
self.version = version # '2.0' for jsonrpc2
|
||||
|
||||
type, uri = urllib.splittype(uri)
|
||||
if type not in ("http", "https"):
|
||||
@@ -112,6 +113,8 @@ class ServerProxy(object):
|
||||
# build data sent to the service
|
||||
request_id = random.randint(0, sys.maxint)
|
||||
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)
|
||||
|
||||
# make HTTP request (retry if connection is lost)
|
||||
@@ -130,14 +133,13 @@ class ServerProxy(object):
|
||||
# {'version': '1.1', 'id': id, 'result': result, 'error': None}
|
||||
response = json.loads(response)
|
||||
|
||||
if response['id'] != request_id:
|
||||
raise JSONRPCError(0, "JSON Request ID != Response ID")
|
||||
|
||||
self.error = response.get('error', {})
|
||||
if self.error and self.exceptions:
|
||||
raise JSONRPCError(self.error.get('code', 0),
|
||||
self.error.get('message', ''),
|
||||
self.error.get('data', None))
|
||||
if response['id'] != request_id:
|
||||
raise JSONRPCError(0, "JSON Request ID != Response ID")
|
||||
|
||||
return response.get('result')
|
||||
|
||||
|
||||
@@ -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>.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
48
gluon/dal.py
48
gluon/dal.py
@@ -1480,7 +1480,7 @@ class BaseAdapter(ConnectionPool):
|
||||
return '(%s)' % ' || '.join(self.expand(x,'string') for x in items)
|
||||
|
||||
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),
|
||||
self.expand(second, first.type))
|
||||
else:
|
||||
@@ -9153,8 +9153,17 @@ class Table(object):
|
||||
record = self(_key)
|
||||
|
||||
if not response.errors and record:
|
||||
row = self._db(self._id ==_key)
|
||||
response.id = row.update(**fields)
|
||||
if '_id' in self:
|
||||
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:
|
||||
response.id = None
|
||||
return response
|
||||
@@ -9173,6 +9182,37 @@ class Table(object):
|
||||
newid = self.insert(**values)
|
||||
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):
|
||||
"""
|
||||
here items is a list of dictionaries
|
||||
@@ -11074,7 +11114,7 @@ class Rows(object):
|
||||
if field.type=='blob' and not value is None:
|
||||
value = base64.b64encode(value)
|
||||
elif represent and field.represent:
|
||||
value = field.represent(value)
|
||||
value = field.represent(value,record)
|
||||
row.append(none_exception(value))
|
||||
writer.writerow(row)
|
||||
|
||||
|
||||
536
gluon/html.py
536
gluon/html.py
@@ -2,9 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
Template helpers
|
||||
--------------------------------------------
|
||||
"""
|
||||
|
||||
import cgi
|
||||
@@ -109,10 +112,11 @@ __all__ = [
|
||||
|
||||
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
|
||||
:param quote: optional (default False)
|
||||
Args:
|
||||
data: the data to be escaped
|
||||
quote: optional (default False)
|
||||
"""
|
||||
|
||||
# first try the xml function
|
||||
@@ -164,80 +168,92 @@ def URL(
|
||||
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
|
||||
and function f. If r=request is passed, a, c, f are set, respectively,
|
||||
to r.application, r.controller, r.function.
|
||||
|
||||
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.
|
||||
|
||||
:param a: application (default to current if r is given)
|
||||
:param c: controller (default to current if r is given)
|
||||
:param f: function (default to current if r is given)
|
||||
:param r: request (optional)
|
||||
:param args: any arguments (optional)
|
||||
:param vars: any variables (optional)
|
||||
:param anchor: anchorname, without # (optional)
|
||||
:param hmac_key: key to use when generating hmac signature (optional)
|
||||
:param hash_vars: which of the vars to include in our hmac signature
|
||||
True (default) - hash all vars, False - hash none of the vars,
|
||||
iterable - hash only the included vars ['key1','key2']
|
||||
:param scheme: URI scheme (True, 'http' or 'https', etc); forces absolute URL (optional)
|
||||
:param host: string to force absolute URL with host (True means http_host)
|
||||
:param port: optional port number (forces absolute URL)
|
||||
Args:
|
||||
a: application (default to current if r is given)
|
||||
c: controller (default to current if r is given)
|
||||
f: function (default to current if r is given)
|
||||
r: request (optional)
|
||||
args: any arguments (optional). Additional "path" elements
|
||||
vars: any variables (optional). Querystring elements
|
||||
anchor: anchorname, without # (optional)
|
||||
extension: force an extension
|
||||
hmac_key: key to use when generating hmac signature (optional)
|
||||
hash_vars: which of the vars to include in our hmac signature
|
||||
True (default) - hash all vars, False - hash none of the vars,
|
||||
iterable - hash only the included vars ['key1','key2']
|
||||
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
|
||||
@@ -528,7 +544,7 @@ class XML(XmlComponent):
|
||||
use it to wrap a string that contains XML/HTML so that it will not be
|
||||
escaped by the template
|
||||
|
||||
example:
|
||||
Examples:
|
||||
|
||||
>>> XML('<h1>Hello</h1>').xml()
|
||||
'<h1>Hello</h1>'
|
||||
@@ -564,19 +580,19 @@ class XML(XmlComponent):
|
||||
},
|
||||
):
|
||||
"""
|
||||
:param text: the XML text
|
||||
:param sanitize: sanitize text using the permitted tags and allowed
|
||||
attributes (default False)
|
||||
:param permitted_tags: list of permitted tags (default: simple list of
|
||||
tags)
|
||||
:param allowed_attributes: dictionary of allowed attributed (default
|
||||
for A, IMG and BlockQuote).
|
||||
The key is the tag; the value is a list of allowed attributes.
|
||||
Args:
|
||||
text: the XML text
|
||||
sanitize: sanitize text using the permitted tags and allowed
|
||||
attributes (default False)
|
||||
permitted_tags: list of permitted tags (default: simple list of
|
||||
tags)
|
||||
allowed_attributes: dictionary of allowed attributed (default
|
||||
for A, IMG and BlockQuote).
|
||||
The key is the tag; the value is a list of allowed attributes.
|
||||
"""
|
||||
|
||||
if sanitize:
|
||||
text = sanitizer.sanitize(text, permitted_tags,
|
||||
allowed_attributes)
|
||||
text = sanitizer.sanitize(text, permitted_tags, allowed_attributes)
|
||||
if isinstance(text, unicode):
|
||||
text = text.encode('utf8', 'xmlcharrefreplace')
|
||||
elif not isinstance(text, str):
|
||||
@@ -620,7 +636,8 @@ class XML(XmlComponent):
|
||||
|
||||
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:
|
||||
return render(self.text, None, {})
|
||||
@@ -628,8 +645,9 @@ class XML(XmlComponent):
|
||||
|
||||
def elements(self, *args, **kargs):
|
||||
"""
|
||||
to be considered experimental since the behavior of this method is questionable
|
||||
another options could be TAG(self.text).elements(*args,**kargs)
|
||||
to be considered experimental since the behavior of this method
|
||||
is questionable
|
||||
another option could be `TAG(self.text).elements(*args,**kwargs)`
|
||||
"""
|
||||
return []
|
||||
|
||||
@@ -653,15 +671,15 @@ class DIV(XmlComponent):
|
||||
Behaves like a dictionary regarding updating of attributes.
|
||||
Behaves like a list regarding inserting/appending components.
|
||||
|
||||
example::
|
||||
Examples:
|
||||
|
||||
>>> DIV('hello', 'world', _style='color:red;').xml()
|
||||
'<div style=\"color:red;\">helloworld</div>'
|
||||
>>> DIV('hello', 'world', _style='color:red;').xml()
|
||||
'<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\" HTML attributes
|
||||
`_something="value"` attributes are transparently translated into
|
||||
`something="value"` HTML attributes
|
||||
"""
|
||||
|
||||
# name of the tag, subclasses should update this
|
||||
@@ -671,10 +689,12 @@ class DIV(XmlComponent):
|
||||
|
||||
def __init__(self, *components, **attributes):
|
||||
"""
|
||||
:param *components: any components that should be nested in this element
|
||||
:param **attributes: any attributes you want to give to this element
|
||||
Args:
|
||||
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:
|
||||
@@ -705,6 +725,8 @@ class DIV(XmlComponent):
|
||||
"""
|
||||
list style appending of components
|
||||
|
||||
Examples:
|
||||
|
||||
>>> a=DIV()
|
||||
>>> a.append(SPAN('x'))
|
||||
>>> print a
|
||||
@@ -717,7 +739,9 @@ class DIV(XmlComponent):
|
||||
|
||||
def insert(self, i, value):
|
||||
"""
|
||||
list style inserting of components
|
||||
List-style inserting of components
|
||||
|
||||
Examples:
|
||||
|
||||
>>> a=DIV()
|
||||
>>> a.insert(0,SPAN('x'))
|
||||
@@ -731,12 +755,12 @@ class DIV(XmlComponent):
|
||||
|
||||
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
|
||||
|
||||
:param i: index
|
||||
if i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
Args:
|
||||
i: index. If i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
"""
|
||||
|
||||
if isinstance(i, str):
|
||||
@@ -749,12 +773,12 @@ class DIV(XmlComponent):
|
||||
|
||||
def __setitem__(self, i, value):
|
||||
"""
|
||||
sets attribute with name 'i' or component #i.
|
||||
Sets attribute with name 'i' or component #i.
|
||||
|
||||
:param i: index
|
||||
if i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
:param value: the new value
|
||||
Args:
|
||||
i: index. If i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
value: the new value
|
||||
"""
|
||||
self._setnode(value)
|
||||
if isinstance(i, (str, unicode)):
|
||||
@@ -764,11 +788,11 @@ class DIV(XmlComponent):
|
||||
|
||||
def __delitem__(self, i):
|
||||
"""
|
||||
deletes attribute with name 'i' or component #i.
|
||||
Deletes attribute with name 'i' or component #i.
|
||||
|
||||
:param i: index
|
||||
if i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
Args:
|
||||
i: index. If i is a string: the name of the attribute
|
||||
otherwise references to number of the component
|
||||
"""
|
||||
|
||||
if isinstance(i, str):
|
||||
@@ -778,13 +802,13 @@ class DIV(XmlComponent):
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
returns the number of included components
|
||||
Returns the number of included components
|
||||
"""
|
||||
return len(self.components)
|
||||
|
||||
def __nonzero__(self):
|
||||
"""
|
||||
always return True
|
||||
Always returns True
|
||||
"""
|
||||
return True
|
||||
|
||||
@@ -804,10 +828,11 @@ class DIV(XmlComponent):
|
||||
helper for _fixup. Checks if a component is in allowed_parents,
|
||||
otherwise wraps it in wrap_parent
|
||||
|
||||
:param allowed_parents: (tuple) classes that the component should be an
|
||||
instance of
|
||||
:param wrap_parent: the class to wrap the component in, if needed
|
||||
:param wrap_lambda: lambda to use for wrapping, if needed
|
||||
Args:
|
||||
allowed_parents: (tuple) classes that the component should be an
|
||||
instance of
|
||||
wrap_parent: the class to wrap the component in, if needed
|
||||
wrap_lambda: lambda to use for wrapping, if needed
|
||||
|
||||
"""
|
||||
components = []
|
||||
@@ -875,7 +900,7 @@ class DIV(XmlComponent):
|
||||
|
||||
def _xml(self):
|
||||
"""
|
||||
helper for xml generation. Returns separately:
|
||||
Helper for xml generation. Returns separately:
|
||||
- the component attributes
|
||||
- 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.
|
||||
A value of True is replaced with the attribute name.
|
||||
|
||||
:returns: tuple: (attributes, components)
|
||||
Returns:
|
||||
tuple: (attributes, components)
|
||||
"""
|
||||
|
||||
# get the attributes for this component
|
||||
@@ -932,16 +958,18 @@ class DIV(XmlComponent):
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
str(COMPONENT) returns equals COMPONENT.xml()
|
||||
str(COMPONENT) returns COMPONENT.xml()
|
||||
"""
|
||||
|
||||
return self.xml()
|
||||
|
||||
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
|
||||
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={}: \
|
||||
{None: re.sub('\s+',' ',text), \
|
||||
@@ -972,11 +1000,13 @@ class DIV(XmlComponent):
|
||||
|
||||
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
|
||||
|
||||
All components of the components are searched.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> a = DIV(DIV(SPAN('x'),3,DIV(SPAN('y'))))
|
||||
>>> for c in a.elements('span',first_only=True): c[0]='z'
|
||||
>>> print a
|
||||
@@ -987,6 +1017,8 @@ class DIV(XmlComponent):
|
||||
|
||||
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>')
|
||||
>>> for e in a.elements('div a#1-1, p.is'): print e.flatten()
|
||||
hello
|
||||
@@ -995,7 +1027,6 @@ class DIV(XmlComponent):
|
||||
hello
|
||||
>>> a.elements('a[u:v=$]')[0].xml()
|
||||
'<a id="1-1" u:v="$">hello</a>'
|
||||
|
||||
>>> a=FORM( INPUT(_type='text'), SELECT(range(1)), TEXTAREA() )
|
||||
>>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled'
|
||||
>>> a.xml()
|
||||
@@ -1005,6 +1036,8 @@ class DIV(XmlComponent):
|
||||
a "replace" argument (note, a list of the original matching elements
|
||||
is still returned as usual).
|
||||
|
||||
Examples:
|
||||
|
||||
>>> 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'))
|
||||
>>> print a
|
||||
@@ -1013,6 +1046,8 @@ class DIV(XmlComponent):
|
||||
"replace" can be a callable, which will be passed the original element and
|
||||
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'))))
|
||||
>>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz'))
|
||||
>>> print a
|
||||
@@ -1020,6 +1055,8 @@ class DIV(XmlComponent):
|
||||
|
||||
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'))))
|
||||
>>> b = a.elements('span', find='y', replace=None)
|
||||
>>> print a
|
||||
@@ -1030,6 +1067,8 @@ class DIV(XmlComponent):
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
>>> 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')
|
||||
>>> print a
|
||||
@@ -1038,6 +1077,8 @@ class DIV(XmlComponent):
|
||||
If other attributes are specified along with find_text, then only components
|
||||
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'))))
|
||||
>>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello')
|
||||
>>> print a
|
||||
@@ -1134,7 +1175,7 @@ class DIV(XmlComponent):
|
||||
|
||||
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
|
||||
|
||||
Also the components of the components are searched.
|
||||
@@ -1148,7 +1189,7 @@ class DIV(XmlComponent):
|
||||
|
||||
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
|
||||
"""
|
||||
sibs = [s for s in self.parent.components if not s == self]
|
||||
@@ -1175,7 +1216,7 @@ class DIV(XmlComponent):
|
||||
|
||||
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
|
||||
"""
|
||||
kargs['first_only'] = True
|
||||
@@ -1211,10 +1252,12 @@ copy_reg.pickle(__tag_div__, TAG_pickler, TAG_unpickler)
|
||||
class __TAG__(XmlComponent):
|
||||
|
||||
"""
|
||||
TAG factory example::
|
||||
TAG factory
|
||||
|
||||
>>> print TAG.first(TAG.second('test'), _key = 3)
|
||||
<first key=\"3\"><second>test</second></first>
|
||||
Examples:
|
||||
|
||||
>>> 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.
|
||||
They can be specified in the 'doctype' parameter:
|
||||
|
||||
-'strict' enables strict doctype
|
||||
-'transitional' enables transitional doctype (default)
|
||||
-'frameset' enables frameset doctype
|
||||
-'html5' enables HTML 5 doctype
|
||||
-any other string will be treated as user's own doctype
|
||||
- 'strict' enables strict doctype
|
||||
- 'transitional' enables transitional doctype (default)
|
||||
- 'frameset' enables frameset doctype
|
||||
- 'html5' enables HTML 5 doctype
|
||||
- any other string will be treated as user's own doctype
|
||||
|
||||
'lang' parameter specifies the language of the document.
|
||||
Defaults to 'en'.
|
||||
|
||||
See also :class:`DIV`
|
||||
See also `DIV`
|
||||
"""
|
||||
|
||||
tag = 'html'
|
||||
@@ -1289,10 +1332,10 @@ class XHTML(DIV):
|
||||
There are three predefined document type definitions.
|
||||
They can be specified in the 'doctype' parameter:
|
||||
|
||||
-'strict' enables strict doctype
|
||||
-'transitional' enables transitional doctype (default)
|
||||
-'frameset' enables frameset doctype
|
||||
-any other string will be treated as user's own doctype
|
||||
- 'strict' enables strict doctype
|
||||
- 'transitional' enables transitional doctype (default)
|
||||
- 'frameset' enables frameset doctype
|
||||
- any other string will be treated as user's own doctype
|
||||
|
||||
'lang' parameter specifies the language of the document and the xml document.
|
||||
Defaults to 'en'.
|
||||
@@ -1300,7 +1343,7 @@ class XHTML(DIV):
|
||||
'xmlns' parameter specifies the xml namespace.
|
||||
Defaults to 'http://www.w3.org/1999/xhtml'.
|
||||
|
||||
See also :class:`DIV`
|
||||
See also `DIV`
|
||||
"""
|
||||
|
||||
tag = 'html'
|
||||
@@ -1443,7 +1486,7 @@ class P(DIV):
|
||||
"""
|
||||
Will replace ``\\n`` by ``<br />`` if the `cr2br` attribute is provided.
|
||||
|
||||
see also :class:`DIV`
|
||||
see also `DIV`
|
||||
"""
|
||||
|
||||
tag = 'p'
|
||||
@@ -1476,6 +1519,23 @@ class HR(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'
|
||||
|
||||
@@ -1546,24 +1606,25 @@ class CENTER(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
|
||||
- link: can provide a link
|
||||
- styles: for styles
|
||||
Examples:
|
||||
|
||||
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\",
|
||||
\"web2py\", \"html\".
|
||||
The \"html\" language interprets {{ and }} tags as \"web2py\" code,
|
||||
\"html_plain\" doesn't.
|
||||
supported languages are
|
||||
|
||||
"python", "html_plain", "c", "cpp", "web2py", "html"
|
||||
|
||||
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
|
||||
the online docs.
|
||||
@@ -1607,7 +1668,6 @@ class UL(DIV):
|
||||
|
||||
If subcomponents are not LI-components they will be wrapped in a LI
|
||||
|
||||
see also :class:`DIV`
|
||||
"""
|
||||
|
||||
tag = 'ul'
|
||||
@@ -1637,7 +1697,6 @@ class TR(DIV):
|
||||
|
||||
If subcomponents are not TD/TH-components they will be wrapped in a TD
|
||||
|
||||
see also :class:`DIV`
|
||||
"""
|
||||
|
||||
tag = 'tr'
|
||||
@@ -1652,7 +1711,6 @@ class __TRHEAD__(DIV):
|
||||
|
||||
If subcomponents are not TD/TH-components they will be wrapped in a TH
|
||||
|
||||
see also :class:`DIV`
|
||||
"""
|
||||
|
||||
tag = 'tr'
|
||||
@@ -1702,7 +1760,6 @@ class TABLE(DIV):
|
||||
If subcomponents are not TR/TBODY/THEAD/TFOOT-components
|
||||
they will be wrapped in a TR
|
||||
|
||||
see also :class:`DIV`
|
||||
"""
|
||||
|
||||
tag = 'table'
|
||||
@@ -1724,34 +1781,36 @@ class IFRAME(DIV):
|
||||
class INPUT(DIV):
|
||||
|
||||
"""
|
||||
INPUT Component
|
||||
INPUT Component
|
||||
|
||||
examples::
|
||||
Takes two special attributes value= and requires=.
|
||||
|
||||
>>> 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\" />'
|
||||
|
||||
the input helper takes two special attributes value= and requires=.
|
||||
|
||||
:param value: used to pass the initial value for the input field.
|
||||
Args:
|
||||
value: used to pass the initial value for the input field.
|
||||
value differs from _value because it works for checkboxes, radio,
|
||||
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'.
|
||||
- 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
|
||||
requires: should be None, or a validator or a list of validators
|
||||
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/'
|
||||
@@ -1847,11 +1906,12 @@ class INPUT(DIV):
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
tag = 'textarea'
|
||||
@@ -1898,12 +1958,12 @@ class OPTGROUP(DIV):
|
||||
class SELECT(INPUT):
|
||||
|
||||
"""
|
||||
example::
|
||||
Examples:
|
||||
|
||||
>>> from validators import IS_IN_SET
|
||||
>>> SELECT('yes', 'no', _name='selector', value='yes',
|
||||
... requires=IS_IN_SET(['yes', 'no'])).xml()
|
||||
'<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>'
|
||||
>>> from validators import IS_IN_SET
|
||||
>>> SELECT('yes', 'no', _name='selector', value='yes',
|
||||
... requires=IS_IN_SET(['yes', 'no'])).xml()
|
||||
'<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):
|
||||
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -2107,12 +2168,12 @@ class FORM(DIV):
|
||||
you can use it instead of directly form.accepts.
|
||||
|
||||
Usage:
|
||||
In controller
|
||||
In controller::
|
||||
|
||||
def action():
|
||||
form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY()))
|
||||
form.validate() #you can pass some args here - see below
|
||||
return dict(form=form)
|
||||
def action():
|
||||
form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY()))
|
||||
form.validate() #you can pass some args here - see below
|
||||
return dict(form=form)
|
||||
|
||||
This can receive a bunch of arguments
|
||||
|
||||
@@ -2190,29 +2251,31 @@ class FORM(DIV):
|
||||
"""
|
||||
Perform the .validate() method but returns the form
|
||||
|
||||
Usage in controllers:
|
||||
# directly on return
|
||||
def action():
|
||||
#some code here
|
||||
return dict(form=FORM(...).process(...))
|
||||
Usage in controllers::
|
||||
|
||||
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:
|
||||
#response.flash messages
|
||||
def action():
|
||||
form = SQLFORM(db.table).process(message_onsuccess='Sucess!')
|
||||
retutn dict(form=form)
|
||||
You can use it with FORM, SQLFORM or FORM based plugins::
|
||||
|
||||
# response.flash messages
|
||||
def action():
|
||||
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)
|
||||
# necessary for SQLHTML forms
|
||||
@@ -2223,7 +2286,7 @@ class FORM(DIV):
|
||||
|
||||
def add_button(self, value, url, _class=None):
|
||||
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(
|
||||
TAG['button'](value, _class=_class,
|
||||
_onclick=url if url.startswith('javascript:') else
|
||||
@@ -2313,16 +2376,20 @@ class FORM(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
|
||||
:sorted: a function that takes the dict and returned sorted keys
|
||||
:keyfilter: a funciton that takes a key and returns its representation
|
||||
or None if the key is to be skipped. By default key[:1]=='_' is skipped.
|
||||
|
||||
- sorted: a function that takes the dict and returned sorted keys
|
||||
- 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'
|
||||
@@ -2394,16 +2461,18 @@ class MENU(DIV):
|
||||
"""
|
||||
Used to build menus
|
||||
|
||||
Optional arguments
|
||||
_class: defaults to 'web2py-menu web2py-menu-vertical'
|
||||
ul_class: defaults to 'web2py-menu-vertical'
|
||||
li_class: defaults to 'web2py-menu-expand'
|
||||
li_first: defaults to 'web2py-menu-first'
|
||||
li_last: defaults to 'web2py-menu-last'
|
||||
Args:
|
||||
_class: defaults to 'web2py-menu web2py-menu-vertical'
|
||||
ul_class: defaults to 'web2py-menu-vertical'
|
||||
li_class: defaults to 'web2py-menu-expand'
|
||||
li_first: defaults to 'web2py-menu-first'
|
||||
li_last: defaults to 'web2py-menu-last'
|
||||
|
||||
Use like::
|
||||
|
||||
Example:
|
||||
menu = MENU([['name', False, URL(...), [submenu]], ...])
|
||||
{{=menu}}
|
||||
|
||||
"""
|
||||
|
||||
tag = 'ul'
|
||||
@@ -2503,9 +2572,10 @@ def embed64(
|
||||
"""
|
||||
helper to encode the provided (binary) data into base64.
|
||||
|
||||
:param filename: if provided, opens and reads this file in 'rb' mode
|
||||
:param file: if provided, reads this file
|
||||
:param data: if provided, uses the provided data
|
||||
Args:
|
||||
filename: if provided, opens and reads this file in 'rb' mode
|
||||
file: if provided, reads this file
|
||||
data: if provided, uses the provided data
|
||||
"""
|
||||
|
||||
if filename and os.path.exists(file):
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
HTTP statuses helpers
|
||||
--------------------------------------------
|
||||
"""
|
||||
|
||||
import re
|
||||
@@ -58,6 +61,18 @@ defined_status = {
|
||||
regex_status = re.compile('^\d{3} [0-9A-Z ]+$')
|
||||
|
||||
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__(
|
||||
self,
|
||||
@@ -136,6 +151,14 @@ class HTTP(Exception):
|
||||
|
||||
|
||||
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:
|
||||
from gluon import current
|
||||
loc = location.replace('\r', '%0D').replace('\n', '%0A')
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| 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)
|
||||
<dbdevelop@gmail.com>
|
||||
Translation system
|
||||
--------------------------------------------
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -27,7 +28,6 @@ from gluon.portalocker import read_locked, LockedFile
|
||||
from utf8 import Utf8
|
||||
|
||||
from gluon.fileutils import listdir
|
||||
import gluon.settings as settings
|
||||
from gluon.cfs import getcfs
|
||||
from gluon.html import XML, xmlescape
|
||||
from gluon.contrib.markmin.markmin2html import render, markmin_escape
|
||||
@@ -41,7 +41,6 @@ pjoin = os.path.join
|
||||
pexists = os.path.exists
|
||||
pdirname = os.path.dirname
|
||||
isdir = os.path.isdir
|
||||
is_gae = False # settings.global_settings.web2py_runtime_gae
|
||||
|
||||
DEFAULT_LANGUAGE = 'en'
|
||||
DEFAULT_LANGUAGE_NAME = 'English'
|
||||
@@ -76,6 +75,10 @@ regex_plural_tuple = re.compile(
|
||||
'^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
|
||||
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):
|
||||
if text.strip():
|
||||
@@ -165,7 +168,7 @@ def read_dict_aux(filename):
|
||||
|
||||
|
||||
def read_dict(filename):
|
||||
""" return dictionary with translation messages
|
||||
""" Returns dictionary with translation messages
|
||||
"""
|
||||
return getcfs('lang:' + filename, filename,
|
||||
lambda: read_dict_aux(filename))
|
||||
@@ -173,8 +176,8 @@ def read_dict(filename):
|
||||
|
||||
def read_possible_plural_rules():
|
||||
"""
|
||||
create list of all possible plural rules files
|
||||
result is cached in PLURAL_RULES dictionary to increase speed
|
||||
Creates list of all possible plural rules files
|
||||
The result is cached in PLURAL_RULES dictionary to increase speed
|
||||
"""
|
||||
plurals = {}
|
||||
try:
|
||||
@@ -294,41 +297,49 @@ def read_plural_dict(filename):
|
||||
def write_plural_dict(filename, contents):
|
||||
if '__corrupted__' in contents:
|
||||
return
|
||||
fp = None
|
||||
try:
|
||||
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')
|
||||
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))
|
||||
for form in contents[key]]) + ']'
|
||||
for form in contents[key]]) + ']'
|
||||
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
|
||||
fp.write('}\n')
|
||||
except (IOError, OSError):
|
||||
if not is_gae:
|
||||
if is_writable():
|
||||
logging.warning('Unable to write to file %s' % filename)
|
||||
return
|
||||
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):
|
||||
if '__corrupted__' in contents:
|
||||
return
|
||||
fp = None
|
||||
try:
|
||||
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):
|
||||
if not settings.global_settings.web2py_runtime_gae:
|
||||
if is_writable():
|
||||
logging.warning('Unable to write to file %s' % filename)
|
||||
return
|
||||
fp.write('# -*- coding: utf-8 -*-\n{\n')
|
||||
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
|
||||
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
|
||||
fp.write('}\n')
|
||||
fp.close()
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
|
||||
|
||||
class lazyT(object):
|
||||
"""
|
||||
never to be called explicitly, returned by
|
||||
Never to be called explicitly, returned by
|
||||
translator.__call__() or translator.M()
|
||||
"""
|
||||
m = s = T = f = t = None
|
||||
@@ -428,28 +439,27 @@ copy_reg.pickle(lazyT, pickle_lazyT)
|
||||
|
||||
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
|
||||
::
|
||||
|
||||
Example:
|
||||
|
||||
T.force(None) # turns off translation
|
||||
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
|
||||
http_accept_language to determine a translation file.
|
||||
notice 2:
|
||||
en and en-en are considered different languages!
|
||||
notice 3:
|
||||
if language xx-yy is not found force() probes other similar
|
||||
languages using such algorithm:
|
||||
xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
|
||||
Note:
|
||||
- there is no need to force since, by default, T uses
|
||||
http_accept_language to determine a translation file.
|
||||
- en and en-en are considered different languages!
|
||||
- if language xx-yy is not found force() probes other similar languages
|
||||
using such algorithm: `xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py`
|
||||
"""
|
||||
|
||||
def __init__(self, langpath, http_accept_language):
|
||||
self.langpath = langpath
|
||||
self.http_accept_language = http_accept_language
|
||||
self.is_writable = not is_gae
|
||||
# filled in self.force():
|
||||
#------------------------
|
||||
# self.cache
|
||||
@@ -473,25 +483,26 @@ class translator(object):
|
||||
self.otherTs = {}
|
||||
self.filter = markmin
|
||||
self.ftag = 'markmin'
|
||||
|
||||
self.ns = None
|
||||
|
||||
def get_possible_languages_info(self, lang=None):
|
||||
"""
|
||||
return info for selected language or dictionary with all
|
||||
possible languages info from APP/languages/*.py
|
||||
args:
|
||||
*lang* (str): language
|
||||
returns:
|
||||
if *lang* is defined:
|
||||
return tuple(langcode, langname, langfile_mtime,
|
||||
pluraldict_fname, pluraldict_mtime,
|
||||
prules_langcode, nplurals,
|
||||
get_plural_id, construct_plural_form)
|
||||
or None
|
||||
Returns info for selected language or dictionary with all
|
||||
possible languages info from `APP/languages/*.py`
|
||||
It Returns:
|
||||
|
||||
- a tuple containing::
|
||||
|
||||
langcode, langname, langfile_mtime,
|
||||
pluraldict_fname, pluraldict_mtime,
|
||||
prules_langcode, nplurals,
|
||||
get_plural_id, construct_plural_form
|
||||
|
||||
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, # language code from !langcode!
|
||||
langname,
|
||||
@@ -504,6 +515,10 @@ class translator(object):
|
||||
get_plural_id, # get_plural_id() for current language
|
||||
construct_plural_form) # construct_plural_form() for current language
|
||||
}
|
||||
|
||||
Args:
|
||||
lang (str): language
|
||||
|
||||
"""
|
||||
info = read_possible_languages(self.langpath)
|
||||
if lang:
|
||||
@@ -511,19 +526,18 @@ class translator(object):
|
||||
return info
|
||||
|
||||
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 +
|
||||
[lang for lang in read_possible_languages(self.langpath).iterkeys()
|
||||
if lang != 'default']))
|
||||
|
||||
def set_current_languages(self, *languages):
|
||||
"""
|
||||
set current AKA "default" languages
|
||||
setting one of this languages makes force() function
|
||||
turn translation off to use default language
|
||||
Sets current AKA "default" languages
|
||||
Setting one of this languages makes the force() function to turn
|
||||
translation off
|
||||
"""
|
||||
if len(languages) == 1 and isinstance(
|
||||
languages[0], (tuple, list)):
|
||||
if len(languages) == 1 and isinstance(languages[0], (tuple, list)):
|
||||
languages = languages[0]
|
||||
if not languages or languages[0] is None:
|
||||
# set default language from default.py/DEFAULT_LANGUAGE
|
||||
@@ -543,17 +557,19 @@ class translator(object):
|
||||
self.force(self.http_accept_language)
|
||||
|
||||
def plural(self, word, n):
|
||||
""" get plural form of word for number *n*
|
||||
NOTE: *word" MUST be defined in current language
|
||||
(T.accepted_language)
|
||||
""" Gets plural form of word for number *n*
|
||||
invoked from T()/T.M() in `%%{}` tag
|
||||
|
||||
invoked from T()/T.M() in %%{} tag
|
||||
args:
|
||||
word (str): word in singular
|
||||
n (numeric): number plural form created for
|
||||
Args:
|
||||
word (str): word in singular
|
||||
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:
|
||||
return word
|
||||
@@ -574,7 +590,7 @@ class translator(object):
|
||||
form = self.construct_plural_form(word, id)
|
||||
forms[id - 1] = form
|
||||
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,
|
||||
self.plural_dict)
|
||||
return form
|
||||
@@ -582,11 +598,10 @@ class translator(object):
|
||||
|
||||
def force(self, *languages):
|
||||
"""
|
||||
|
||||
select language(s) for translation
|
||||
Selects language(s) for translation
|
||||
|
||||
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
|
||||
selected
|
||||
|
||||
@@ -691,11 +706,11 @@ class translator(object):
|
||||
self.ns = ns
|
||||
otherT = self.__get_otherT__(language, ns)
|
||||
return otherT(message, symbols, lazy=lazy)
|
||||
|
||||
|
||||
def __get_otherT__(self, language=None, namespace=None):
|
||||
if not language and not namespace:
|
||||
raise Exception('Incorrect parameters')
|
||||
|
||||
|
||||
if namespace:
|
||||
if language:
|
||||
index = '%s/%s' % (namespace, language)
|
||||
@@ -742,7 +757,7 @@ class translator(object):
|
||||
def M(self, message, symbols={}, language=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 is None:
|
||||
@@ -760,16 +775,16 @@ class translator(object):
|
||||
|
||||
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
|
||||
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 ## token') -> ' hello world '
|
||||
T('hello ## world## token') -> 'hello ## world'
|
||||
T(' hello world ') -> ' hello world '
|
||||
T(' hello world ## token') -> ' hello world '
|
||||
T('hello ## world## token') -> 'hello ## world'
|
||||
|
||||
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):
|
||||
message = message.encode('utf8')
|
||||
@@ -786,32 +801,38 @@ class translator(object):
|
||||
# guess translation same as original
|
||||
self.t[key] = mt = self.default_t.get(key, message)
|
||||
# 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)
|
||||
return regex_backslash.sub(
|
||||
lambda m: m.group(1).translate(ttab_in), mt)
|
||||
|
||||
def params_substitution(self, message, symbols):
|
||||
"""
|
||||
substitute parameters from symbols into message using %.
|
||||
also parse %%{} placeholders for plural-forms processing.
|
||||
returns: string with parameters
|
||||
NOTE: *symbols* MUST BE OR tuple OR dict of parameters!
|
||||
Substitutes parameters from symbols into message using %.
|
||||
also parse `%%{}` placeholders for plural-forms processing.
|
||||
|
||||
Returns:
|
||||
string with parameters
|
||||
|
||||
Note:
|
||||
*symbols* MUST BE OR tuple OR dict of parameters!
|
||||
"""
|
||||
def sub_plural(m):
|
||||
"""string in %{} is transformed by this rules:
|
||||
If string starts with \\, ! or ? such transformations
|
||||
take place:
|
||||
"""String in `%{}` is transformed by this rules:
|
||||
If string starts with `\\`, `!` or `?` such transformations
|
||||
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):
|
||||
""" word[number], !word[number], !!word[number], !!!word[number]
|
||||
@@ -894,7 +915,7 @@ class translator(object):
|
||||
|
||||
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,
|
||||
lambda: self.get_t(message))
|
||||
@@ -917,7 +938,8 @@ class translator(object):
|
||||
|
||||
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')
|
||||
sentences = read_dict(lang_file)
|
||||
@@ -954,6 +976,10 @@ def findT(path, language=DEFAULT_LANGUAGE):
|
||||
write_dict(lang_file, sentences)
|
||||
|
||||
def update_all_languages(application_path):
|
||||
"""
|
||||
Note:
|
||||
Must be run by the admin app
|
||||
"""
|
||||
path = pjoin(application_path, 'languages/')
|
||||
for language in oslistdir(path):
|
||||
if regex_langfile.match(language):
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
Contains:
|
||||
|
||||
- wsgibase: the gluon wsgi application
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
The gluon wsgi application
|
||||
---------------------------
|
||||
"""
|
||||
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
"""
|
||||
eget = env.get
|
||||
@@ -160,7 +158,7 @@ def get_client(env):
|
||||
|
||||
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,
|
||||
and then tries to render the output using a view/template.
|
||||
this function must run from the [application] folder.
|
||||
@@ -234,7 +232,7 @@ class LazyWSGI(object):
|
||||
return self._environ
|
||||
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.start_response
|
||||
@@ -249,7 +247,7 @@ class LazyWSGI(object):
|
||||
"""
|
||||
In you controller use::
|
||||
|
||||
@request.wsgi.middleware(middleware1, middleware2, ...)
|
||||
@request.wsgi.middleware(middleware1, middleware2, ...)
|
||||
|
||||
to decorate actions with WSGI middleware. actions must return strings.
|
||||
uses a simulated environment so it may have weird behavior in some cases
|
||||
@@ -271,9 +269,9 @@ class LazyWSGI(object):
|
||||
|
||||
def wsgibase(environ, responder):
|
||||
"""
|
||||
this is the gluon wsgi application. the first function called when a page
|
||||
is requested (static or dynamic). it can be called by paste.httpserver
|
||||
or by apache mod_wsgi.
|
||||
The gluon wsgi application. The first function called when a page
|
||||
is requested (static or dynamic). It can be called by paste.httpserver
|
||||
or by apache mod_wsgi (or any WSGI-compatible server).
|
||||
|
||||
- fills request with info
|
||||
- the environment variables, replacing '.' with '_'
|
||||
@@ -290,13 +288,11 @@ def wsgibase(environ, responder):
|
||||
2. for dynamic pages:
|
||||
|
||||
- /<application>[/<controller>[/<function>[/<sub>]]][.<extension>]
|
||||
- (sub may go several levels deep, currently 3 levels are supported:
|
||||
sub1/sub2/sub3)
|
||||
|
||||
The naming conventions are:
|
||||
|
||||
- application, controller, function and extension may only contain
|
||||
[a-zA-Z0-9_]
|
||||
`[a-zA-Z0-9_]`
|
||||
- file and sub may also contain '-', '=', '.' and '/'
|
||||
"""
|
||||
eget = environ.get
|
||||
@@ -567,7 +563,7 @@ def wsgibase(environ, responder):
|
||||
|
||||
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)
|
||||
@@ -607,10 +603,10 @@ def appfactory(wsgiapp=wsgibase,
|
||||
generates a wsgi application that does logging and profiling and calls
|
||||
wsgibase
|
||||
|
||||
.. function:: gluon.main.appfactory(
|
||||
[wsgiapp=wsgibase
|
||||
[, logfilename='httpserver.log'
|
||||
[, profilerfilename='profiler.log']]])
|
||||
Args:
|
||||
wsgiapp: the base application
|
||||
logfilename: where to store apache-compatible requests log
|
||||
profiler_dir: where to store profile files
|
||||
|
||||
"""
|
||||
if profilerfilename is not None:
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
Useful regexes
|
||||
---------------
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Created by Attila Csipa <web2py@csipa.in.rs>
|
||||
Modified by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| This file is part of the web2py Web Framework
|
||||
| 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
|
||||
@@ -27,7 +31,7 @@ _cron_subprocs = []
|
||||
|
||||
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):
|
||||
@@ -40,7 +44,7 @@ def absolute_path_link(path):
|
||||
|
||||
|
||||
def stopcron():
|
||||
"graceful shutdown of cron"
|
||||
"Graceful shutdown of cron"
|
||||
global _cron_stopping
|
||||
_cron_stopping = True
|
||||
while _cron_subprocs:
|
||||
@@ -107,7 +111,7 @@ class Token(object):
|
||||
|
||||
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
|
||||
|
||||
lock is implemented by writing a pickle (start, stop) in cron.master
|
||||
@@ -150,8 +154,7 @@ class Token(object):
|
||||
|
||||
def release(self):
|
||||
"""
|
||||
this function writes into cron.master the time when cron job
|
||||
was completed
|
||||
Writes into cron.master the time when cron job was completed
|
||||
"""
|
||||
if not self.master.closed:
|
||||
portalocker.lock(self.master, portalocker.LOCK_EX)
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- 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.
|
||||
|
||||
Synopsis:
|
||||
Synopsis::
|
||||
|
||||
import portalocker
|
||||
file = open(\"somefile\", \"r+\")
|
||||
@@ -16,18 +13,18 @@ Synopsis:
|
||||
file.write(\"foo\")
|
||||
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)
|
||||
|
||||
before closing the file, but why?
|
||||
|
||||
Methods:
|
||||
Methods::
|
||||
|
||||
lock( file, flags )
|
||||
unlock( file )
|
||||
|
||||
Constants:
|
||||
Constants::
|
||||
|
||||
LOCK_EX
|
||||
LOCK_SH
|
||||
@@ -163,6 +160,7 @@ def write_locked(filename, data):
|
||||
fp.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
f = LockedFile('test.txt', mode='wb')
|
||||
f.write('test ok')
|
||||
f.close()
|
||||
|
||||
@@ -22,6 +22,7 @@ __author__ = "Thadeus Burgess <thadeusb@thadeusb.com>"
|
||||
# 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
|
||||
# the cursor will throw an OperationalError for the SQL syntax.
|
||||
|
||||
COMMON = set((
|
||||
'SELECT',
|
||||
'INSERT',
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
Restricted environment to execute application's code
|
||||
-----------------------------------------------------
|
||||
"""
|
||||
|
||||
import sys
|
||||
@@ -26,7 +29,7 @@ __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
|
||||
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__(
|
||||
@@ -40,7 +43,7 @@ class TicketStorage(Storage):
|
||||
|
||||
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:
|
||||
self._store_in_db(request, ticket_id, ticket_data)
|
||||
@@ -51,12 +54,12 @@ class TicketStorage(Storage):
|
||||
self.db._adapter.reconnect()
|
||||
try:
|
||||
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),
|
||||
created_datetime=request.now)
|
||||
self.db.commit()
|
||||
message = 'In FILE: %(layer)s\n\n%(traceback)s\n'
|
||||
except Exception, e:
|
||||
except Exception:
|
||||
self.db.rollback()
|
||||
message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n'
|
||||
self.db.close()
|
||||
@@ -111,8 +114,8 @@ class TicketStorage(Storage):
|
||||
|
||||
class RestrictedError(Exception):
|
||||
"""
|
||||
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.
|
||||
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.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -123,7 +126,7 @@ class RestrictedError(Exception):
|
||||
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.
|
||||
"""
|
||||
if environment is None:
|
||||
@@ -148,7 +151,7 @@ class RestrictedError(Exception):
|
||||
|
||||
def log(self, request):
|
||||
"""
|
||||
logs the exception.
|
||||
Logs the exception.
|
||||
"""
|
||||
|
||||
try:
|
||||
@@ -168,7 +171,7 @@ class RestrictedError(Exception):
|
||||
|
||||
def load(self, request, app, ticket_id):
|
||||
"""
|
||||
loads a logged exception.
|
||||
Loads a logged exception.
|
||||
"""
|
||||
ticket_storage = TicketStorage(db=request.tickets_db)
|
||||
d = ticket_storage.load(request, app, ticket_id)
|
||||
@@ -201,8 +204,8 @@ def compile2(code, layer):
|
||||
|
||||
def restricted(code, environment=None, layer='Unknown'):
|
||||
"""
|
||||
runs code in environment and returns the output. if an exception occurs
|
||||
in code it raises a RestrictedError containing the traceback. layer is
|
||||
Runs code in environment and returns the output. If an exception occurs
|
||||
in code it raises a RestrictedError containing the traceback. Layer is
|
||||
passed to RestrictedError to identify where the error occurred.
|
||||
"""
|
||||
if environment is None:
|
||||
|
||||
107
gluon/rewrite.py
107
gluon/rewrite.py
@@ -2,9 +2,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
This file is part of the web2py Web Framework
|
||||
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
| This file is part of the web2py Web Framework
|
||||
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
|
||||
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
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():
|
||||
"return new copy of default base router"
|
||||
"Returns new copy of default base router"
|
||||
router = Storage(
|
||||
default_application='init',
|
||||
applications='ALL',
|
||||
@@ -85,7 +85,7 @@ def _router_default():
|
||||
|
||||
|
||||
def _params_default(app=None):
|
||||
"return new copy of default parameters"
|
||||
"Returns a new copy of default parameters"
|
||||
p = Storage()
|
||||
p.name = app or "BASE"
|
||||
p.default_application = app or "init"
|
||||
@@ -181,7 +181,7 @@ def fixup_missing_path_info(environ):
|
||||
|
||||
|
||||
def url_in(request, environ):
|
||||
"parse and rewrite incoming URL"
|
||||
"Parses and rewrites incoming URL"
|
||||
if routers:
|
||||
return map_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,
|
||||
args, other, scheme, host, port, language=None):
|
||||
"assemble and rewrite outgoing URL"
|
||||
"Assembles and rewrites outgoing URL"
|
||||
if routers:
|
||||
acf = map_url_out(request, environ, application, controller,
|
||||
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):
|
||||
"""
|
||||
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])
|
||||
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):
|
||||
"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])
|
||||
if status > 399 and THREAD_LOCAL.routes.routes_onerror:
|
||||
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):
|
||||
"""
|
||||
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]
|
||||
|
||||
@@ -427,7 +427,7 @@ def compile_regex(k, v, env=None):
|
||||
|
||||
|
||||
def load_routers(all_apps):
|
||||
"load-time post-processing of routers"
|
||||
"Load-time post-processing of routers"
|
||||
|
||||
for app in routers:
|
||||
# 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):
|
||||
"filter incoming URI against a list of regexes"
|
||||
"Filters incoming URI against a list of regexes"
|
||||
path = e['PATH_INFO']
|
||||
host = e.get('HTTP_HOST', e.get('SERVER_NAME', 'localhost')).lower()
|
||||
i = host.find(':')
|
||||
@@ -555,7 +555,7 @@ def regex_uri(e, regexes, tag, default=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:
|
||||
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):
|
||||
"regex rewrite incoming URL"
|
||||
"Regex rewrite incoming URL"
|
||||
routes = THREAD_LOCAL.routes
|
||||
query = e.get('QUERY_STRING', None)
|
||||
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
||||
@@ -603,7 +603,7 @@ def invalid_url(routes):
|
||||
web2py_error='invalid path')
|
||||
|
||||
def regex_url_in(request, environ):
|
||||
"rewrite and parse incoming URL"
|
||||
"Rewrites and parses incoming URL"
|
||||
|
||||
# ##################################################
|
||||
# select application
|
||||
@@ -667,7 +667,7 @@ def regex_url_in(request, environ):
|
||||
|
||||
|
||||
def regex_filter_out(url, e=None):
|
||||
"regex rewrite outgoing URL"
|
||||
"Regex rewrite outgoing URL"
|
||||
if not hasattr(THREAD_LOCAL, 'routes'):
|
||||
regex_select() # ensure routes is set (for application threads)
|
||||
routes = THREAD_LOCAL.routes
|
||||
@@ -810,10 +810,10 @@ def filter_err(status, application='app', ticket='tkt'):
|
||||
|
||||
|
||||
class MapUrlIn(object):
|
||||
"logic for mapping incoming URLs"
|
||||
"Logic for mapping incoming URLs"
|
||||
|
||||
def __init__(self, request=None, env=None):
|
||||
"initialize a map-in object"
|
||||
"Initializes a map-in object"
|
||||
self.request = request
|
||||
self.env = env
|
||||
|
||||
@@ -861,7 +861,7 @@ class MapUrlIn(object):
|
||||
self.port = '443' if self.scheme == 'https' else '80'
|
||||
|
||||
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
|
||||
if prefix:
|
||||
prefixlen = len(prefix)
|
||||
@@ -873,7 +873,7 @@ class MapUrlIn(object):
|
||||
self.args = List(self.args[prefixlen:]) # strip the prefix
|
||||
|
||||
def map_app(self):
|
||||
"determine application name"
|
||||
"Determines application name"
|
||||
base = routers.BASE # base router
|
||||
self.domain_application = None
|
||||
self.domain_controller = None
|
||||
@@ -935,12 +935,12 @@ class MapUrlIn(object):
|
||||
self._args_match = self.router._args_match
|
||||
|
||||
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,
|
||||
typically robots.txt & favicon.ico
|
||||
'''
|
||||
"""
|
||||
|
||||
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
||||
self.controller = self.request.controller = 'static'
|
||||
@@ -952,7 +952,7 @@ class MapUrlIn(object):
|
||||
return None, None
|
||||
|
||||
def map_language(self):
|
||||
"handle language (no hyphen mapping)"
|
||||
"Handles language (no hyphen mapping)"
|
||||
arg0 = self.arg0 # no hyphen mapping
|
||||
if arg0 and self.languages and arg0 in self.languages:
|
||||
self.language = arg0
|
||||
@@ -964,7 +964,7 @@ class MapUrlIn(object):
|
||||
arg0 = self.arg0
|
||||
|
||||
def map_controller(self):
|
||||
"identify controller"
|
||||
"Identifies controller"
|
||||
# handle controller
|
||||
#
|
||||
arg0 = self.harg0 # map hyphens
|
||||
@@ -981,7 +981,7 @@ class MapUrlIn(object):
|
||||
|
||||
def map_static(self):
|
||||
'''
|
||||
handle static files
|
||||
Handles static files
|
||||
file_match but no hyphen mapping
|
||||
'''
|
||||
if self.controller != 'static':
|
||||
@@ -1025,7 +1025,7 @@ class MapUrlIn(object):
|
||||
return static_file, version
|
||||
|
||||
def map_function(self):
|
||||
"handle function.extension"
|
||||
"Handles function.extension"
|
||||
arg0 = self.harg0 # map hyphens
|
||||
functions = self.functions.get(self.controller, set())
|
||||
if isinstance(self.router.default_function, dict):
|
||||
@@ -1058,9 +1058,9 @@ class MapUrlIn(object):
|
||||
web2py_error='invalid extension')
|
||||
|
||||
def validate_args(self):
|
||||
'''
|
||||
check args against validation pattern
|
||||
'''
|
||||
"""
|
||||
Checks args against validation pattern
|
||||
"""
|
||||
for arg in self.args:
|
||||
if not self.router._args_match.match(arg):
|
||||
raise HTTP(
|
||||
@@ -1068,16 +1068,15 @@ class MapUrlIn(object):
|
||||
web2py_error='invalid arg <%s>' % arg)
|
||||
|
||||
def sluggify(self):
|
||||
""
|
||||
self.request.env.update(
|
||||
(k.lower().replace('.', '_'), v) for k, v in self.env.iteritems())
|
||||
|
||||
def update_request(self):
|
||||
'''
|
||||
update request from self
|
||||
build env.request_uri
|
||||
make lower-case versions of http headers in env
|
||||
'''
|
||||
"""
|
||||
Updates request from self
|
||||
Builds env.request_uri
|
||||
Makes lower-case versions of http headers in env
|
||||
"""
|
||||
self.request.application = self.application
|
||||
self.request.controller = self.controller
|
||||
self.request.function = self.function
|
||||
@@ -1105,24 +1104,24 @@ class MapUrlIn(object):
|
||||
|
||||
@property
|
||||
def arg0(self):
|
||||
"return first arg"
|
||||
"Returns first arg"
|
||||
return self.args(0)
|
||||
|
||||
@property
|
||||
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):
|
||||
return self.args(0).replace('-', '_')
|
||||
return self.args(0)
|
||||
|
||||
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:
|
||||
self.args.pop(0)
|
||||
|
||||
|
||||
class MapUrlOut(object):
|
||||
"logic for mapping outgoing URLs"
|
||||
"Logic for mapping outgoing URLs"
|
||||
|
||||
def __init__(self, request, env, application, controller,
|
||||
function, args, other, scheme, host, port, language):
|
||||
@@ -1179,13 +1178,13 @@ class MapUrlOut(object):
|
||||
self.omit_function = False
|
||||
|
||||
def omit_lang(self):
|
||||
"omit language if possible"
|
||||
"Omits language if possible"
|
||||
|
||||
if not self.language or self.language == self.default_language:
|
||||
self.omit_language = True
|
||||
|
||||
def omit_acf(self):
|
||||
"omit what we can of a/c/f"
|
||||
"Omits what we can of a/c/f"
|
||||
|
||||
router = self.router
|
||||
|
||||
@@ -1254,7 +1253,7 @@ class MapUrlOut(object):
|
||||
self.omit_function = False
|
||||
|
||||
def build_acf(self):
|
||||
"build acf from components"
|
||||
"Builds a/c/f from components"
|
||||
acf = ''
|
||||
if self.map_hyphen:
|
||||
self.application = self.application.replace('_', '-')
|
||||
@@ -1280,7 +1279,7 @@ class MapUrlOut(object):
|
||||
return acf or '/'
|
||||
|
||||
def acf(self):
|
||||
"convert components to /app/lang/controller/function"
|
||||
"Converts components to /app/lang/controller/function"
|
||||
|
||||
if not routers:
|
||||
return None # use regex filter
|
||||
@@ -1290,7 +1289,7 @@ class MapUrlOut(object):
|
||||
|
||||
|
||||
def map_url_in(request, env, app=False):
|
||||
"route incoming URL"
|
||||
"Routes incoming URL"
|
||||
|
||||
# initialize router-url object
|
||||
#
|
||||
@@ -1333,8 +1332,8 @@ def map_url_in(request, env, app=False):
|
||||
|
||||
def map_url_out(request, env, application, controller,
|
||||
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
|
||||
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
|
||||
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 => /
|
||||
/a/dc/df => /a
|
||||
/a/c/df => /a/c
|
||||
/da/dc/df => /
|
||||
/a/dc/df => /a
|
||||
/a/c/df => /a/c
|
||||
|
||||
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/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 assume that language names do not collide with a/c/f names.
|
||||
'''
|
||||
"""
|
||||
map = MapUrlOut(request, env, application, controller,
|
||||
function, args, other, scheme, host, port, language)
|
||||
return map.acf()
|
||||
|
||||
|
||||
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:
|
||||
return None
|
||||
return Storage(routers[appname]) # return a copy
|
||||
|
||||
@@ -2599,8 +2599,11 @@ class SQLFORM(FORM):
|
||||
for k, v in sorted(exportManager.items()):
|
||||
if not v:
|
||||
continue
|
||||
label = v[1] if hasattr(v, "__getitem__") else k
|
||||
title = v[2] if hasattr(v, "__getitem__") else label
|
||||
if hasattr(v, "__getitem__"):
|
||||
label = v[1]
|
||||
title = v[2] if len(v)>2 else label
|
||||
else:
|
||||
label = title = k
|
||||
link = url2(vars=dict(
|
||||
order=request.vars.order or '',
|
||||
_export_type=k,
|
||||
@@ -2926,7 +2929,8 @@ class SQLTABLE(TABLE):
|
||||
if not sqlrows:
|
||||
return
|
||||
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':
|
||||
headers = {}
|
||||
for c in columns:
|
||||
|
||||
@@ -757,7 +757,8 @@ class Recaptcha(DIV):
|
||||
error_message='invalid',
|
||||
label='Verify:',
|
||||
options='',
|
||||
comment = ''
|
||||
comment = '',
|
||||
ajax=False
|
||||
):
|
||||
self.request_vars = request and request.vars or current.request.vars
|
||||
self.remote_addr = request.env.remote_addr
|
||||
@@ -772,6 +773,7 @@ class Recaptcha(DIV):
|
||||
self.label = label
|
||||
self.options = options
|
||||
self.comment = comment
|
||||
self.ajax = ajax
|
||||
|
||||
def _validate(self):
|
||||
|
||||
@@ -825,18 +827,43 @@ class Recaptcha(DIV):
|
||||
server = self.API_SSL_SERVER
|
||||
else:
|
||||
server = self.API_SERVER
|
||||
captcha = DIV(
|
||||
SCRIPT("var RecaptchaOptions = {%s};" % self.options),
|
||||
SCRIPT(_type="text/javascript",
|
||||
_src="%s/challenge?k=%s%s" % (server, public_key, error_param)),
|
||||
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.ajax:
|
||||
captcha = DIV(
|
||||
SCRIPT("var RecaptchaOptions = {%s};" % self.options),
|
||||
SCRIPT(_type="text/javascript",
|
||||
_src="%s/challenge?k=%s%s" % (server, public_key, error_param)),
|
||||
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')
|
||||
|
||||
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:
|
||||
return XML(captcha).xml()
|
||||
else:
|
||||
@@ -2035,8 +2062,9 @@ class Auth(object):
|
||||
if user and user.get(settings.passfield, False):
|
||||
password = settings.table_user[
|
||||
settings.passfield].validate(password)[0]
|
||||
if not user.registration_key.strip() and password == \
|
||||
user[settings.passfield]:
|
||||
if ((user.registration_key is None or
|
||||
not user.registration_key.strip()) and
|
||||
password == user[settings.passfield]):
|
||||
self.login_user(user)
|
||||
return user
|
||||
else:
|
||||
@@ -2322,8 +2350,8 @@ class Auth(object):
|
||||
elif temp_user.registration_key in ('disabled', 'blocked'):
|
||||
response.flash = self.messages.login_disabled
|
||||
return form
|
||||
elif not temp_user.registration_key is None and \
|
||||
temp_user.registration_key.strip():
|
||||
elif (not temp_user.registration_key is None
|
||||
and temp_user.registration_key.strip()):
|
||||
response.flash = \
|
||||
self.messages.registration_verifying
|
||||
return form
|
||||
|
||||
@@ -17,10 +17,10 @@ import cgi
|
||||
import urllib
|
||||
import struct
|
||||
import decimal
|
||||
import unicodedata
|
||||
from cStringIO import StringIO
|
||||
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
|
||||
from gluon.dal import FieldVirtual, FieldMethod
|
||||
from gluon.contrib import translitcodec
|
||||
|
||||
regex_isint = re.compile('^[+-]?\d+$')
|
||||
|
||||
@@ -2519,7 +2519,7 @@ def urlify(s, maxlen=80, keep_underscores=False):
|
||||
if isinstance(s, str):
|
||||
s = s.decode('utf-8') # to unicode
|
||||
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 = re.sub('&\w+?;', '', s) # strip html entities
|
||||
if keep_underscores:
|
||||
|
||||
@@ -1222,12 +1222,12 @@ end tell
|
||||
url = get_url(ip, proto=proto, port=port)
|
||||
|
||||
if not options.nobanner:
|
||||
print 'please visit:'
|
||||
print '\t', url
|
||||
message = '\nplease visit:\n\t%s\n' % url
|
||||
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:
|
||||
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
|
||||
# if the line was not found (under py2exe & when file was modified)
|
||||
|
||||
Reference in New Issue
Block a user