Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2b6c645ef | ||
|
|
36810aaccc | ||
|
|
8e14d1c92e | ||
|
|
9b7e944dea | ||
|
|
b6429ca5fd | ||
|
|
8607b09e64 | ||
|
|
64151a2c3e | ||
|
|
d5749db0cd | ||
|
|
df7d6847e6 | ||
|
|
c6b5ad8179 | ||
|
|
71ec500fde | ||
|
|
6cf05edf63 | ||
|
|
39c5341dbc | ||
|
|
e2234a8771 | ||
|
|
cac9741cb7 | ||
|
|
648cb296e6 | ||
|
|
5a0ebb7d8c | ||
|
|
b7b534ccf2 | ||
|
|
fab23d210b | ||
|
|
6a18539f7e | ||
|
|
4d87a90c10 | ||
|
|
2c9767e500 | ||
|
|
2b1d9acba5 | ||
|
|
8a2d18b63b | ||
|
|
b59f9f896c | ||
|
|
496e6663fd | ||
|
|
a4fbf9b6b4 | ||
|
|
b8afce746b | ||
|
|
4c239eaf79 | ||
|
|
1d03233c83 | ||
|
|
5358ad271e | ||
|
|
2fa7d72b01 | ||
|
|
f034d83c81 | ||
|
|
b3afa2ab57 | ||
|
|
36fc758690 | ||
|
|
f764eb2653 | ||
|
|
63f56ddecc | ||
|
|
0b2bab3f3d | ||
|
|
66de02929b | ||
|
|
3c4a142f9a | ||
|
|
bf6f090428 | ||
|
|
59ab818f5b | ||
|
|
77847daa77 | ||
|
|
690d2df774 | ||
|
|
643cc9c141 | ||
|
|
562cb58342 | ||
|
|
1c8ef29413 | ||
|
|
8e67c551de | ||
|
|
03e3324a12 | ||
|
|
a3ba3f662a | ||
|
|
2b21678d48 | ||
|
|
09f29257d2 | ||
|
|
62be48decf | ||
|
|
952a29d5da | ||
|
|
db9151b993 | ||
|
|
e1bb2b4556 | ||
|
|
668658b7c9 | ||
|
|
231a3e1278 | ||
|
|
41d234bf48 | ||
|
|
f16c3a394f | ||
|
|
570538042e | ||
|
|
6a97bfe517 | ||
|
|
7b6e5c5270 | ||
|
|
20deb56e33 | ||
|
|
a23a2c52c2 | ||
|
|
3f6fa941cb | ||
|
|
28f914463b | ||
|
|
f0c84b5230 | ||
|
|
dc51abe54c | ||
|
|
f501da74b7 | ||
|
|
c1d14a35ee | ||
|
|
4f4a0318aa | ||
|
|
54a1005af3 | ||
|
|
7b4fac7a7f | ||
|
|
c80a6db604 | ||
|
|
96247eda51 | ||
|
|
433a02537c | ||
|
|
bacff3453b | ||
|
|
b20e74c899 | ||
|
|
efa5ceb6de | ||
|
|
f79660dd2a | ||
|
|
606daabdd3 | ||
|
|
fc1e4ad620 | ||
|
|
54dcf2ead9 | ||
|
|
69cea23cd4 | ||
|
|
083b598c93 | ||
|
|
94f11501ed | ||
|
|
9ac4a11db1 | ||
|
|
a56cf59136 | ||
|
|
12cc81147c | ||
|
|
f70737ddc2 | ||
|
|
1a22497b64 | ||
|
|
6da330a001 | ||
|
|
f6652e7530 | ||
|
|
5148c6ec3a | ||
|
|
954c79abcb | ||
|
|
7ec3fe3bfe | ||
|
|
9ce1cde6ca | ||
|
|
672af73e96 | ||
|
|
67022578e3 | ||
|
|
65fe4c16fc | ||
|
|
b6f9cd1863 | ||
|
|
4908a5e7be | ||
|
|
853c0b1a3a | ||
|
|
d0d8a4827e | ||
|
|
4745ce42f9 | ||
|
|
bba13becd0 | ||
|
|
7712e005c4 | ||
|
|
07c667f469 | ||
|
|
57ad1ec1cf | ||
|
|
60a043479c | ||
|
|
1a43e1671f | ||
|
|
cd955d3603 | ||
|
|
492a0c51d1 | ||
|
|
dd4a4c281f | ||
|
|
41ec6da72c | ||
|
|
e18f38d4da | ||
|
|
bd27df8fa5 | ||
|
|
2f44c2de9b | ||
|
|
ccf154a807 | ||
|
|
837453dd6e | ||
|
|
64bc60cdf0 | ||
|
|
af258c334e | ||
|
|
627fff624a | ||
|
|
bc8127f6de | ||
|
|
19bf43815d | ||
|
|
7709074d7c | ||
|
|
4b73f249dd | ||
|
|
ae9e2c2be0 | ||
|
|
fd8edb5aa2 | ||
|
|
82ab59c46a | ||
|
|
74a63e98b5 | ||
|
|
9e23b3dac5 | ||
|
|
077ce011e2 | ||
|
|
1430bc824f | ||
|
|
722b16e620 | ||
|
|
30727ef9e4 | ||
|
|
0f322f8a69 | ||
|
|
ec5b4dde6f | ||
|
|
630dcb799e | ||
|
|
c6d1e47226 | ||
|
|
aa8315a7c9 | ||
|
|
80040bf8e5 | ||
|
|
8f382f322e | ||
|
|
9ccc7dc59a | ||
|
|
70f6f86827 | ||
|
|
3055c87567 | ||
|
|
473955611a | ||
|
|
21a7ded8e0 | ||
|
|
23347995e2 | ||
|
|
4027a3cda3 | ||
|
|
a9b6c4f1a3 | ||
|
|
c1330662aa | ||
|
|
3e38f8017f | ||
|
|
41bc1cb1e9 | ||
|
|
9fb94008ea | ||
|
|
5f647a46b8 | ||
|
|
69a2e76c3c | ||
|
|
4d6598c645 | ||
|
|
56acb685e9 | ||
|
|
f0be4416b3 | ||
|
|
93ce59f65b | ||
|
|
28b94eceb2 | ||
|
|
e4c63769f2 | ||
|
|
e0fc61932d | ||
|
|
3de3046260 | ||
|
|
b6a96301cd | ||
|
|
480dea23ad | ||
|
|
457ef24c04 | ||
|
|
dbf6ce6d6c | ||
|
|
7686a6fc93 | ||
|
|
afde6efafa | ||
|
|
af53f17f5a | ||
|
|
8f9cf4034d | ||
|
|
31a6c5df9b | ||
|
|
08d1b26738 | ||
|
|
abd739123c | ||
|
|
da8570b560 | ||
|
|
8d238890a2 | ||
|
|
ca29f262e5 | ||
|
|
cffefbeb2e | ||
|
|
52528f9311 | ||
|
|
8b75256281 | ||
|
|
2385ed22b1 | ||
|
|
7a0cf446ba | ||
|
|
586d85cf08 | ||
|
|
f371e72e07 | ||
|
|
8d9bc1cb53 | ||
|
|
29edf94498 | ||
|
|
d7a108ff06 | ||
|
|
5ed41285c0 | ||
|
|
b16d79e654 | ||
|
|
90e5d06d3d | ||
|
|
a1156cae6c | ||
|
|
037a200357 | ||
|
|
e282454bfe | ||
|
|
e499c1d5a4 | ||
|
|
8540b1b113 | ||
|
|
6b5e3f2abf | ||
|
|
f8be550e6a | ||
|
|
6a7b04199f | ||
|
|
2847e0f4df | ||
|
|
eec7f7c687 | ||
|
|
a4253deda3 | ||
|
|
372d639601 | ||
|
|
06e5c6869d | ||
|
|
933189c216 | ||
|
|
c942881f36 | ||
|
|
99e491ca0f | ||
|
|
ea539d50e2 | ||
|
|
497bbf002c | ||
|
|
2629fac624 | ||
|
|
30af5901eb | ||
|
|
42b60d3291 | ||
|
|
9b53fd2302 | ||
|
|
972f64b6f9 | ||
|
|
3eb4627ccf | ||
|
|
2ba88b8951 | ||
|
|
117b04169c | ||
|
|
c56d5bd066 | ||
|
|
00bfde9ce9 | ||
|
|
5f503d0427 | ||
|
|
47bcab6b26 | ||
|
|
ecca0439f1 | ||
|
|
e896be4037 | ||
|
|
b0e4284130 | ||
|
|
3e69889cca | ||
|
|
dfb2e62c6c |
@@ -1,4 +1,37 @@
|
|||||||
## 2.00.1
|
## 2.1.0
|
||||||
|
|
||||||
|
- overall faster web2py
|
||||||
|
- when apps are deleted, a w2p copy left in deposit folder
|
||||||
|
- change in cron (it is now disabled by default). removed -N option and introduced -Y.
|
||||||
|
- faster web2py_uuid() and request initialization logic, thanks Michele
|
||||||
|
- static asset management, thanks Niphlod
|
||||||
|
- improved mobile admin
|
||||||
|
- request.requires_https and Auth(secure=True), thanks Yarin and Niphlod
|
||||||
|
- better custom_import (works per app and is faster), thanks Michele
|
||||||
|
- redis_sesssion.py, thanks Niphlod
|
||||||
|
- allow entropy computation in IS_STRONG and web2py.js, thanks Jonathan and Niphlod
|
||||||
|
- fixed many aith.wiki problems
|
||||||
|
- support for auth.wiki(render='html')
|
||||||
|
- better welcome layout, thanks Paolo
|
||||||
|
- db.define_table(...,redefine=True)
|
||||||
|
- DAL, Row, and Rows object can now be pickled/unpickled, thanks to zombie DAL.
|
||||||
|
- admin uses codemirror
|
||||||
|
- allow syntax auth = Auth(db).define_tables()
|
||||||
|
- better auth.wiki with preview, thanks Alan
|
||||||
|
- better auth.impersonate, thanks Alan
|
||||||
|
- upgraded jQuery 1.8
|
||||||
|
- upgraded Bootstrap 2.1
|
||||||
|
- fixed problem with dropbox_account.py
|
||||||
|
- many fixes to cache.ram, cache.disk, memcache and gae_memcache
|
||||||
|
- cache.with_prefix(cache.ram,'prefix')
|
||||||
|
- db.table.field.epoch() counts seconds from epoch
|
||||||
|
- DAL support for SQL CASE, example: db().select(...query.case('true','false))
|
||||||
|
- DAL(...,do_connect=False) allows faking connections
|
||||||
|
- DAL(...,auto_import=True) now retieves some fiel attributes
|
||||||
|
- mail can specify a sender: mail.send(...,sender='Mr X <%(sender)s>')
|
||||||
|
- renamed gluon/contrib/comet_messaging.py -> gluon/contrib/websocket_messaging.py
|
||||||
|
|
||||||
|
## 2.0.1-11
|
||||||
|
|
||||||
### DAL Improvements
|
### DAL Improvements
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ update:
|
|||||||
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
|
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
|
||||||
echo "remember that pymysql was tweaked"
|
echo "remember that pymysql was tweaked"
|
||||||
src:
|
src:
|
||||||
echo 'Version 2.0.9 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION
|
echo 'Version 2.1.1 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION
|
||||||
### rm -f all junk files
|
### rm -f all junk files
|
||||||
make clean
|
make clean
|
||||||
### clean up baisc apps
|
### clean up baisc apps
|
||||||
@@ -107,10 +107,6 @@ win:
|
|||||||
cp applications/__init__.py ../web2py_win/web2py/applications
|
cp applications/__init__.py ../web2py_win/web2py/applications
|
||||||
cd ../web2py_win; zip -r web2py_win.zip web2py
|
cd ../web2py_win; zip -r web2py_win.zip web2py
|
||||||
mv ../web2py_win/web2py_win.zip .
|
mv ../web2py_win/web2py_win.zip .
|
||||||
pip:
|
|
||||||
# create Web2py distribution for upload to Pypi
|
|
||||||
# after upload clean Web2py sources with rm -R ./dist
|
|
||||||
python setup.py sdist
|
|
||||||
run:
|
run:
|
||||||
python2.5 web2py.py -a hello
|
python2.5 web2py.py -a hello
|
||||||
commit:
|
commit:
|
||||||
@@ -129,3 +125,10 @@ tag:
|
|||||||
hg tag -l '$(S)'
|
hg tag -l '$(S)'
|
||||||
make commit S='$(S)'
|
make commit S='$(S)'
|
||||||
make push
|
make push
|
||||||
|
pip:
|
||||||
|
# create Web2py distribution for upload to Pypi
|
||||||
|
# after upload clean Web2py sources with rm -R ./dist
|
||||||
|
# http://guide.python-distribute.org/creation.html
|
||||||
|
python setup.py sdist
|
||||||
|
python setup.py register
|
||||||
|
python setup.py sdist upload
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
Version 2.0.9 (2012-09-13 17:48:15) stable
|
Version 2.1.1 (2012-10-15 06:40:52) stable
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ skip_files: |
|
|||||||
|
|
||||||
builtins:
|
builtins:
|
||||||
- remote_api: on
|
- remote_api: on
|
||||||
# - datastore_admin: on
|
|
||||||
- appstats: on
|
- appstats: on
|
||||||
- admin_redirect: on
|
- admin_redirect: on
|
||||||
- deferred: on
|
- deferred: on
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ def query_by_table_type(tablename,db,request=request):
|
|||||||
# ## list all databases and tables
|
# ## list all databases and tables
|
||||||
# ###########################################################
|
# ###########################################################
|
||||||
|
|
||||||
|
|
||||||
def index():
|
def index():
|
||||||
return dict(databases=databases)
|
return dict(databases=databases)
|
||||||
|
|
||||||
@@ -207,6 +206,7 @@ def select():
|
|||||||
if match:
|
if match:
|
||||||
table = match.group('table')
|
table = match.group('table')
|
||||||
try:
|
try:
|
||||||
|
tb = None
|
||||||
nrows = db(query).count()
|
nrows = db(query).count()
|
||||||
if form.vars.update_check and form.vars.update_fields:
|
if form.vars.update_check and form.vars.update_fields:
|
||||||
db(query).update(**eval_in_global_env('dict(%s)'
|
db(query).update(**eval_in_global_env('dict(%s)'
|
||||||
@@ -221,6 +221,8 @@ def select():
|
|||||||
else:
|
else:
|
||||||
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
(rows, nrows) = ([], 0)
|
(rows, nrows) = ([], 0)
|
||||||
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
||||||
# begin handle upload csv
|
# begin handle upload csv
|
||||||
@@ -250,6 +252,7 @@ def select():
|
|||||||
rows=rows,
|
rows=rows,
|
||||||
query=request.vars.query,
|
query=request.vars.query,
|
||||||
formcsv = formcsv,
|
formcsv = formcsv,
|
||||||
|
tb = tb,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
EXPERIMENTAL_STUFF = True
|
EXPERIMENTAL_STUFF = True
|
||||||
|
|
||||||
if EXPERIMENTAL_STUFF:
|
if EXPERIMENTAL_STUFF:
|
||||||
is_mobile = request.user_agent().is_mobile
|
|
||||||
if is_mobile:
|
if is_mobile:
|
||||||
response.view = response.view.replace('default/','default.mobile/')
|
response.view = response.view.replace('default/','default.mobile/')
|
||||||
response.menu = []
|
response.menu = []
|
||||||
@@ -366,10 +365,15 @@ def uninstall():
|
|||||||
else:
|
else:
|
||||||
session.flash = T('no permission to uninstall "%s"', app)
|
session.flash = T('no permission to uninstall "%s"', app)
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
if app_uninstall(app, request):
|
try:
|
||||||
session.flash = T('application "%s" uninstalled', app)
|
filename = app_pack(app, request, raise_ex=True)
|
||||||
else:
|
except:
|
||||||
session.flash = T('unable to uninstall "%s"', app)
|
session.flash = T('unable to uninstall "%s"', app)
|
||||||
|
else:
|
||||||
|
if app_uninstall(app, request):
|
||||||
|
session.flash = T('application "%s" uninstalled', app)
|
||||||
|
else:
|
||||||
|
session.flash = T('unable to uninstall "%s"', app)
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
return dict(app=app, dialog=dialog)
|
return dict(app=app, dialog=dialog)
|
||||||
|
|
||||||
@@ -782,7 +786,7 @@ def edit_language():
|
|||||||
|
|
||||||
_class='untranslated' if k==s else 'translated'
|
_class='untranslated' if k==s else 'translated'
|
||||||
|
|
||||||
if len(key) <= 40:
|
if len(s) <= 40:
|
||||||
elem = INPUT(_type='text', _name=name, value=s,
|
elem = INPUT(_type='text', _name=name, value=s,
|
||||||
_size=70,_class=_class)
|
_size=70,_class=_class)
|
||||||
else:
|
else:
|
||||||
@@ -1647,7 +1651,6 @@ def git_pull():
|
|||||||
session.flash = T("Application updated via git pull")
|
session.flash = T("Application updated via git pull")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
except CheckoutError, message:
|
except CheckoutError, message:
|
||||||
logging.error(message)
|
|
||||||
session.flash = T("Pull failed, certain files could not be checked out. Check logs for details.")
|
session.flash = T("Pull failed, certain files could not be checked out. Check logs for details.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
except UnmergedEntriesError:
|
except UnmergedEntriesError:
|
||||||
@@ -1657,11 +1660,9 @@ def git_pull():
|
|||||||
session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.")
|
session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
except GitCommandError, status:
|
except GitCommandError, status:
|
||||||
logging.error(str(status))
|
|
||||||
session.flash = T("Pull failed, git exited abnormally. See logs for details.")
|
session.flash = T("Pull failed, git exited abnormally. See logs for details.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
except Exception,e:
|
except Exception,e:
|
||||||
logging.error("Unexpected error:", sys.exc_info()[0])
|
|
||||||
session.flash = T("Pull failed, git exited abnormally. See logs for details.")
|
session.flash = T("Pull failed, git exited abnormally. See logs for details.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
elif 'cancel' in request.vars:
|
elif 'cancel' in request.vars:
|
||||||
@@ -1693,7 +1694,6 @@ def git_push():
|
|||||||
session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.")
|
session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
logging.error("Unexpected error:", sys.exc_info()[0])
|
|
||||||
session.flash = T("Push failed, git exited abnormally. See logs for details.")
|
session.flash = T("Push failed, git exited abnormally. See logs for details.")
|
||||||
redirect(URL('site'))
|
redirect(URL('site'))
|
||||||
return dict(app=app,form=form)
|
return dict(app=app,form=form)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ def deploy():
|
|||||||
form = SQLFORM.factory(
|
form = SQLFORM.factory(
|
||||||
Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'),
|
Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'),
|
||||||
requires=EXISTS(error_message=T('file not found'))),
|
requires=EXISTS(error_message=T('file not found'))),
|
||||||
Field('google_application_id',requires=IS_ALPHANUMERIC(),label=T('Google Application Id')),
|
Field('google_application_id',requires=IS_MATCH('[\w\-]+'),label=T('Google Application Id')),
|
||||||
Field('applications','list:string',
|
Field('applications','list:string',
|
||||||
requires=IS_IN_SET(apps,multiple=True),
|
requires=IS_IN_SET(apps,multiple=True),
|
||||||
label=T('web2py apps to deploy')),
|
label=T('web2py apps to deploy')),
|
||||||
|
|||||||
+183
-125
@@ -3,143 +3,26 @@
|
|||||||
'!langcode!': 'it',
|
'!langcode!': 'it',
|
||||||
'!langname!': 'Italiano',
|
'!langname!': 'Italiano',
|
||||||
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ',
|
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ',
|
||||||
'%Y-%m-%d': '%d/%m/%Y',
|
|
||||||
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
|
|
||||||
'%s %%{row} deleted': '%s righe ("record") cancellate',
|
'%s %%{row} deleted': '%s righe ("record") cancellate',
|
||||||
'%s %%{row} updated': '%s righe ("record") modificate',
|
'%s %%{row} updated': '%s righe ("record") modificate',
|
||||||
|
'%Y-%m-%d': '%d/%m/%Y',
|
||||||
|
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
|
||||||
'(requires internet access)': '(requires internet access)',
|
'(requires internet access)': '(requires internet access)',
|
||||||
'(something like "it-it")': '(qualcosa simile a "it-it")',
|
'(something like "it-it")': '(qualcosa simile a "it-it")',
|
||||||
|
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)',
|
||||||
'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files',
|
'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files',
|
||||||
'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s',
|
'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s',
|
||||||
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)",
|
|
||||||
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")',
|
|
||||||
'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ",
|
|
||||||
'About': 'informazioni',
|
'About': 'informazioni',
|
||||||
'About application': "Informazioni sull'applicazione",
|
'About application': "Informazioni sull'applicazione",
|
||||||
'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura',
|
|
||||||
'Admin language': 'Admin language',
|
|
||||||
'Administrator Password:': 'Password Amministratore:',
|
|
||||||
'Application name:': 'Application name:',
|
|
||||||
'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?',
|
|
||||||
'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?',
|
|
||||||
'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?',
|
|
||||||
'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?',
|
|
||||||
'Available databases and tables': 'Database e tabelle disponibili',
|
|
||||||
'Cannot be empty': 'Non può essere vuoto',
|
|
||||||
'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.",
|
|
||||||
'Change admin password': 'change admin password',
|
|
||||||
'Check for upgrades': 'check for upgrades',
|
|
||||||
'Check to delete': 'Seleziona per cancellare',
|
|
||||||
'Checking for upgrades...': 'Controllo aggiornamenti in corso...',
|
|
||||||
'Clean': 'pulisci',
|
|
||||||
'Compile': 'compila',
|
|
||||||
'Controller': 'Controller',
|
|
||||||
'Controllers': 'Controllers',
|
|
||||||
'Copyright': 'Copyright',
|
|
||||||
'Create': 'crea',
|
|
||||||
'Create new simple application': 'Crea nuova applicazione',
|
|
||||||
'Current request': 'Richiesta (request) corrente',
|
|
||||||
'Current response': 'Risposta (response) corrente',
|
|
||||||
'Current session': 'Sessione (session) corrente',
|
|
||||||
'DB Model': 'Modello di DB',
|
|
||||||
'Database': 'Database',
|
|
||||||
'Date and Time': 'Data and Ora',
|
|
||||||
'Delete': 'Cancella',
|
|
||||||
'Delete:': 'Cancella:',
|
|
||||||
'Deploy': 'deploy',
|
|
||||||
'Deploy on Google App Engine': 'Installa su Google App Engine',
|
|
||||||
'EDIT': 'MODIFICA',
|
|
||||||
'Edit': 'modifica',
|
|
||||||
'Edit This App': 'Modifica questa applicazione',
|
|
||||||
'Edit application': 'Modifica applicazione',
|
|
||||||
'Edit current record': 'Modifica record corrente',
|
|
||||||
'Editing Language file': 'Modifica file linguaggio',
|
|
||||||
'Editing file "%s"': 'Modifica del file "%s"',
|
|
||||||
'Enterprise Web Framework': 'Enterprise Web Framework',
|
|
||||||
'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"',
|
|
||||||
'Errors': 'errori',
|
|
||||||
'Exception instance attributes': 'Exception instance attributes',
|
|
||||||
'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].',
|
|
||||||
'Hello World': 'Salve Mondo',
|
|
||||||
'Help': 'aiuto',
|
|
||||||
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
|
|
||||||
'Import/Export': 'Importa/Esporta',
|
|
||||||
'Index': 'Indice',
|
|
||||||
'Install': 'installa',
|
|
||||||
'Installed applications': 'Applicazioni installate',
|
|
||||||
'Internal State': 'Stato interno',
|
|
||||||
'Invalid Query': 'Richiesta (query) non valida',
|
|
||||||
'Invalid action': 'Azione non valida',
|
|
||||||
'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati',
|
|
||||||
'Languages': 'Linguaggi',
|
|
||||||
'Last saved on:': 'Ultimo salvataggio:',
|
|
||||||
'Layout': 'Layout',
|
|
||||||
'License for': 'Licenza relativa a',
|
|
||||||
'Login': 'Accesso',
|
|
||||||
'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa",
|
|
||||||
'Logout': 'uscita',
|
|
||||||
'Main Menu': 'Menu principale',
|
|
||||||
'Menu Model': 'Menu Modelli',
|
|
||||||
'Models': 'Modelli',
|
|
||||||
'Modules': 'Moduli',
|
|
||||||
'NO': 'NO',
|
|
||||||
'New Record': 'Nuovo elemento (record)',
|
|
||||||
'New application wizard': 'New application wizard',
|
|
||||||
'New simple application': 'New simple application',
|
|
||||||
'No databases in this application': 'Nessun database presente in questa applicazione',
|
|
||||||
'Original/Translation': 'Originale/Traduzione',
|
|
||||||
'Overwrite installed app': 'sovrascrivi applicazione installata',
|
|
||||||
'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui',
|
|
||||||
'Pack all': 'crea pacchetto',
|
|
||||||
'Pack compiled': 'crea pacchetto del codice compilato',
|
|
||||||
'Peeking at file': 'Uno sguardo al file',
|
|
||||||
'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione',
|
|
||||||
'Plugins': 'I Plugins',
|
|
||||||
'Powered by': 'Powered by',
|
|
||||||
'Query:': 'Richiesta (query):',
|
|
||||||
'Remove compiled': 'rimozione codice compilato',
|
|
||||||
'Resolve Conflict file': 'File di risoluzione conflitto',
|
|
||||||
'Rows in table': 'Righe nella tabella',
|
|
||||||
'Rows selected': 'Righe selezionate',
|
|
||||||
'Saved file hash:': 'Hash del file salvato:',
|
|
||||||
'Site': 'sito',
|
|
||||||
'Start wizard': 'start wizard',
|
|
||||||
'Static files': 'Files statici',
|
|
||||||
'Stylesheet': 'Foglio di stile (stylesheet)',
|
|
||||||
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
|
|
||||||
'TM': 'TM',
|
|
||||||
'Testing application': 'Test applicazione in corsg',
|
|
||||||
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.',
|
|
||||||
'There are no controllers': 'Non ci sono controller',
|
|
||||||
'There are no models': 'Non ci sono modelli',
|
|
||||||
'There are no modules': 'Non ci sono moduli',
|
|
||||||
'There are no static files': 'Non ci sono file statici',
|
|
||||||
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
|
|
||||||
'There are no views': 'Non ci sono viste ("view")',
|
|
||||||
'This is the %(filename)s template': 'Questo è il template %(filename)s',
|
|
||||||
'Ticket': 'Ticket',
|
|
||||||
'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]',
|
|
||||||
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
|
|
||||||
'Unable to download app because:': 'Impossibile scaricare applicazione perché',
|
|
||||||
'Unable to download because': 'Impossibile scaricare perché',
|
|
||||||
'Unable to download because:': 'Unable to download because:',
|
|
||||||
'Uninstall': 'disinstalla',
|
|
||||||
'Update:': 'Aggiorna:',
|
|
||||||
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
|
|
||||||
'Upload a package:': 'Upload a package:',
|
|
||||||
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
|
|
||||||
'Use an url:': 'Use an url:',
|
|
||||||
'Version': 'Versione',
|
|
||||||
'View': 'Vista',
|
|
||||||
'Views': 'viste',
|
|
||||||
'Welcome %s': 'Benvenuto %s',
|
|
||||||
'Welcome to web2py': 'Benvenuto su web2py',
|
|
||||||
'YES': 'SI',
|
|
||||||
'additional code for your application': 'righe di codice aggiuntive per la tua applicazione',
|
'additional code for your application': 'righe di codice aggiuntive per la tua applicazione',
|
||||||
|
'Additional code for your application': 'Additional code for your application',
|
||||||
'admin disabled because no admin password': 'amministrazione disabilitata per mancanza di password amministrativa',
|
'admin disabled because no admin password': 'amministrazione disabilitata per mancanza di password amministrativa',
|
||||||
'admin disabled because not supported on google app engine': 'amministrazione non supportata da Google Apps Engine',
|
'admin disabled because not supported on google app engine': 'amministrazione non supportata da Google Apps Engine',
|
||||||
'admin disabled because unable to access password file': 'amministrazione disabilitata per impossibilità di leggere il file delle password',
|
'admin disabled because unable to access password file': 'amministrazione disabilitata per impossibilità di leggere il file delle password',
|
||||||
|
'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura',
|
||||||
|
'Admin language': 'Admin language',
|
||||||
'administrative interface': 'administrative interface',
|
'administrative interface': 'administrative interface',
|
||||||
|
'Administrator Password:': 'Password Amministratore:',
|
||||||
'and rename it (required):': 'e rinominala (obbligatorio):',
|
'and rename it (required):': 'e rinominala (obbligatorio):',
|
||||||
'and rename it:': 'e rinominala:',
|
'and rename it:': 'e rinominala:',
|
||||||
'appadmin': 'appadmin ',
|
'appadmin': 'appadmin ',
|
||||||
@@ -147,44 +30,104 @@
|
|||||||
'application "%s" uninstalled': 'applicazione "%s" disinstallata',
|
'application "%s" uninstalled': 'applicazione "%s" disinstallata',
|
||||||
'application compiled': 'applicazione compilata',
|
'application compiled': 'applicazione compilata',
|
||||||
'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare",
|
'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare",
|
||||||
|
'Application name:': 'Application name:',
|
||||||
|
'are not used': 'are not used',
|
||||||
|
'are not used yet': 'are not used yet',
|
||||||
|
'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?',
|
||||||
|
'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?',
|
||||||
|
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
|
||||||
|
'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?',
|
||||||
|
'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?',
|
||||||
'arguments': 'arguments',
|
'arguments': 'arguments',
|
||||||
|
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)",
|
||||||
|
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")',
|
||||||
|
'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ",
|
||||||
|
'Available databases and tables': 'Database e tabelle disponibili',
|
||||||
'back': 'indietro',
|
'back': 'indietro',
|
||||||
'cache': 'cache',
|
'cache': 'cache',
|
||||||
'cache, errors and sessions cleaned': 'pulitura cache, errori and sessioni ',
|
'cache, errors and sessions cleaned': 'pulitura cache, errori and sessioni ',
|
||||||
|
'can be a git repo': 'can be a git repo',
|
||||||
|
'Cannot be empty': 'Non può essere vuoto',
|
||||||
|
'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.",
|
||||||
'cannot create file': 'impossibile creare il file',
|
'cannot create file': 'impossibile creare il file',
|
||||||
'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"',
|
'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"',
|
||||||
|
'Change admin password': 'change admin password',
|
||||||
'change password': 'cambia password',
|
'change password': 'cambia password',
|
||||||
'check all': 'controlla tutto',
|
'check all': 'controlla tutto',
|
||||||
|
'Check for upgrades': 'check for upgrades',
|
||||||
|
'Check to delete': 'Seleziona per cancellare',
|
||||||
|
'Checking for upgrades...': 'Controllo aggiornamenti in corso...',
|
||||||
|
'Clean': 'pulisci',
|
||||||
'click here for online examples': 'clicca per vedere gli esempi',
|
'click here for online examples': 'clicca per vedere gli esempi',
|
||||||
'click here for the administrative interface': "clicca per l'interfaccia amministrativa",
|
'click here for the administrative interface': "clicca per l'interfaccia amministrativa",
|
||||||
'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti',
|
'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti',
|
||||||
'code': 'code',
|
'code': 'code',
|
||||||
|
'collapse/expand all': 'collapse/expand all',
|
||||||
|
'Compile': 'compila',
|
||||||
'compiled application removed': "rimosso il codice compilato dell'applicazione",
|
'compiled application removed': "rimosso il codice compilato dell'applicazione",
|
||||||
|
'Controller': 'Controller',
|
||||||
|
'Controllers': 'Controllers',
|
||||||
'controllers': 'controllers',
|
'controllers': 'controllers',
|
||||||
|
'Copyright': 'Copyright',
|
||||||
|
'Create': 'crea',
|
||||||
'create file with filename:': 'crea un file col nome:',
|
'create file with filename:': 'crea un file col nome:',
|
||||||
'create new application:': 'create new application:',
|
'create new application:': 'create new application:',
|
||||||
|
'Create new simple application': 'Crea nuova applicazione',
|
||||||
'created by': 'creato da',
|
'created by': 'creato da',
|
||||||
'crontab': 'crontab',
|
'crontab': 'crontab',
|
||||||
|
'Current request': 'Richiesta (request) corrente',
|
||||||
|
'Current response': 'Risposta (response) corrente',
|
||||||
|
'Current session': 'Sessione (session) corrente',
|
||||||
'currently running': 'currently running',
|
'currently running': 'currently running',
|
||||||
'currently saved or': 'attualmente salvato o',
|
'currently saved or': 'attualmente salvato o',
|
||||||
'customize me!': 'Personalizzami!',
|
'customize me!': 'Personalizzami!',
|
||||||
'data uploaded': 'dati caricati',
|
'data uploaded': 'dati caricati',
|
||||||
|
'Database': 'Database',
|
||||||
'database': 'database',
|
'database': 'database',
|
||||||
'database %s select': 'database %s select',
|
'database %s select': 'database %s select',
|
||||||
'database administration': 'amministrazione database',
|
'database administration': 'amministrazione database',
|
||||||
|
'Date and Time': 'Data and Ora',
|
||||||
'db': 'db',
|
'db': 'db',
|
||||||
|
'DB Model': 'Modello di DB',
|
||||||
|
'Debug': 'Debug',
|
||||||
'defines tables': 'defininisce le tabelle',
|
'defines tables': 'defininisce le tabelle',
|
||||||
|
'Delete': 'Cancella',
|
||||||
'delete': 'Cancella',
|
'delete': 'Cancella',
|
||||||
'delete all checked': 'cancella tutti i selezionati',
|
'delete all checked': 'cancella tutti i selezionati',
|
||||||
'delete plugin': 'cancella plugin',
|
'delete plugin': 'cancella plugin',
|
||||||
|
'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)',
|
||||||
|
'Delete:': 'Cancella:',
|
||||||
|
'Deploy': 'deploy',
|
||||||
|
'Deploy on Google App Engine': 'Installa su Google App Engine',
|
||||||
|
'Deploy to OpenShift': 'Deploy to OpenShift',
|
||||||
'design': 'progetta',
|
'design': 'progetta',
|
||||||
|
'Detailed traceback description': 'Detailed traceback description',
|
||||||
'direction: ltr': 'direction: ltr',
|
'direction: ltr': 'direction: ltr',
|
||||||
|
'Disable': 'Disable',
|
||||||
|
'docs': 'docs',
|
||||||
'done!': 'fatto!',
|
'done!': 'fatto!',
|
||||||
|
'download layouts': 'download layouts',
|
||||||
|
'download plugins': 'download plugins',
|
||||||
|
'EDIT': 'MODIFICA',
|
||||||
|
'Edit': 'modifica',
|
||||||
|
'Edit application': 'Modifica applicazione',
|
||||||
'edit controller': 'modifica controller',
|
'edit controller': 'modifica controller',
|
||||||
|
'Edit current record': 'Modifica record corrente',
|
||||||
'edit profile': 'modifica profilo',
|
'edit profile': 'modifica profilo',
|
||||||
|
'Edit This App': 'Modifica questa applicazione',
|
||||||
'edit views:': 'modifica viste (view):',
|
'edit views:': 'modifica viste (view):',
|
||||||
|
'Editing file "%s"': 'Modifica del file "%s"',
|
||||||
|
'Editing Language file': 'Modifica file linguaggio',
|
||||||
|
'Enterprise Web Framework': 'Enterprise Web Framework',
|
||||||
|
'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"',
|
||||||
|
'Error snapshot': 'Error snapshot',
|
||||||
|
'Error ticket': 'Error ticket',
|
||||||
|
'Errors': 'errori',
|
||||||
|
'Exception instance attributes': 'Exception instance attributes',
|
||||||
|
'Expand Abbreviation': 'Expand Abbreviation',
|
||||||
'export as csv file': 'esporta come file CSV',
|
'export as csv file': 'esporta come file CSV',
|
||||||
'exposes': 'espone',
|
'exposes': 'espone',
|
||||||
|
'exposes:': 'exposes:',
|
||||||
'extends': 'estende',
|
'extends': 'estende',
|
||||||
'failed to reload module because:': 'ricaricamento modulo fallito perché:',
|
'failed to reload module because:': 'ricaricamento modulo fallito perché:',
|
||||||
'file "%(filename)s" created': 'creato il file "%(filename)s"',
|
'file "%(filename)s" created': 'creato il file "%(filename)s"',
|
||||||
@@ -195,73 +138,188 @@
|
|||||||
'file does not exist': 'file inesistente',
|
'file does not exist': 'file inesistente',
|
||||||
'file saved on %(time)s': "file salvato nell'istante %(time)s",
|
'file saved on %(time)s': "file salvato nell'istante %(time)s",
|
||||||
'file saved on %s': 'file salvato: %s',
|
'file saved on %s': 'file salvato: %s',
|
||||||
|
'filter': 'filter',
|
||||||
|
'Frames': 'Frames',
|
||||||
|
'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].',
|
||||||
|
'Get from URL:': 'Get from URL:',
|
||||||
|
'Git Pull': 'Git Pull',
|
||||||
|
'Git Push': 'Git Push',
|
||||||
|
'Hello World': 'Salve Mondo',
|
||||||
|
'Help': 'aiuto',
|
||||||
'htmledit': 'modifica come html',
|
'htmledit': 'modifica come html',
|
||||||
|
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
|
||||||
|
'Import/Export': 'Importa/Esporta',
|
||||||
'includes': 'include',
|
'includes': 'include',
|
||||||
|
'Index': 'Indice',
|
||||||
'insert new': 'inserisci nuovo',
|
'insert new': 'inserisci nuovo',
|
||||||
'insert new %s': 'inserisci nuovo %s',
|
'insert new %s': 'inserisci nuovo %s',
|
||||||
|
'inspect attributes': 'inspect attributes',
|
||||||
|
'Install': 'installa',
|
||||||
|
'Installed applications': 'Applicazioni installate',
|
||||||
'internal error': 'errore interno',
|
'internal error': 'errore interno',
|
||||||
|
'Internal State': 'Stato interno',
|
||||||
|
'Invalid action': 'Azione non valida',
|
||||||
'invalid password': 'password non valida',
|
'invalid password': 'password non valida',
|
||||||
|
'Invalid Query': 'Richiesta (query) non valida',
|
||||||
'invalid request': 'richiesta non valida',
|
'invalid request': 'richiesta non valida',
|
||||||
'invalid ticket': 'ticket non valido',
|
'invalid ticket': 'ticket non valido',
|
||||||
|
'Key bindings': 'Key bindings',
|
||||||
|
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
|
||||||
'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato',
|
'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato',
|
||||||
|
'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati',
|
||||||
'languages': 'linguaggi',
|
'languages': 'linguaggi',
|
||||||
|
'Languages': 'Linguaggi',
|
||||||
|
'Last saved on:': 'Ultimo salvataggio:',
|
||||||
|
'Layout': 'Layout',
|
||||||
|
'License for': 'Licenza relativa a',
|
||||||
'loading...': 'caricamento...',
|
'loading...': 'caricamento...',
|
||||||
|
'locals': 'locals',
|
||||||
'login': 'accesso',
|
'login': 'accesso',
|
||||||
|
'Login': 'Accesso',
|
||||||
|
'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa",
|
||||||
|
'Logout': 'uscita',
|
||||||
|
'Main Menu': 'Menu principale',
|
||||||
|
'Menu Model': 'Menu Modelli',
|
||||||
'merge': 'unisci',
|
'merge': 'unisci',
|
||||||
'models': 'modelli',
|
'models': 'modelli',
|
||||||
|
'Models': 'Modelli',
|
||||||
|
'Modules': 'Moduli',
|
||||||
'modules': 'moduli',
|
'modules': 'moduli',
|
||||||
'new application "%s" created': 'creata la nuova applicazione "%s"',
|
'new application "%s" created': 'creata la nuova applicazione "%s"',
|
||||||
|
'New application wizard': 'New application wizard',
|
||||||
'new plugin installed': 'installato nuovo plugin',
|
'new plugin installed': 'installato nuovo plugin',
|
||||||
|
'New Record': 'Nuovo elemento (record)',
|
||||||
'new record inserted': 'nuovo record inserito',
|
'new record inserted': 'nuovo record inserito',
|
||||||
|
'New simple application': 'New simple application',
|
||||||
'next 100 rows': 'prossime 100 righe',
|
'next 100 rows': 'prossime 100 righe',
|
||||||
|
'NO': 'NO',
|
||||||
|
'No databases in this application': 'Nessun database presente in questa applicazione',
|
||||||
'no match': 'nessuna corrispondenza',
|
'no match': 'nessuna corrispondenza',
|
||||||
'or import from csv file': 'oppure importa da file CSV',
|
'or import from csv file': 'oppure importa da file CSV',
|
||||||
'or provide app url:': "oppure fornisci url dell'applicazione:",
|
'or provide app url:': "oppure fornisci url dell'applicazione:",
|
||||||
|
'Original/Translation': 'Originale/Traduzione',
|
||||||
|
'Overwrite installed app': 'sovrascrivi applicazione installata',
|
||||||
|
'Pack all': 'crea pacchetto',
|
||||||
|
'Pack compiled': 'crea pacchetto del codice compilato',
|
||||||
'pack plugin': 'crea pacchetto del plugin',
|
'pack plugin': 'crea pacchetto del plugin',
|
||||||
|
'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui',
|
||||||
'password changed': 'password modificata',
|
'password changed': 'password modificata',
|
||||||
|
'Peeking at file': 'Uno sguardo al file',
|
||||||
'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato',
|
'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato',
|
||||||
|
'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione',
|
||||||
|
'plugins': 'plugins',
|
||||||
|
'Plugins': 'I Plugins',
|
||||||
|
'Plural-Forms:': 'Plural-Forms:',
|
||||||
|
'Powered by': 'Powered by',
|
||||||
'previous 100 rows': '100 righe precedenti',
|
'previous 100 rows': '100 righe precedenti',
|
||||||
|
'private files': 'private files',
|
||||||
|
'Private files': 'Private files',
|
||||||
|
'Query:': 'Richiesta (query):',
|
||||||
'record': 'record',
|
'record': 'record',
|
||||||
'record does not exist': 'il record non esiste',
|
'record does not exist': 'il record non esiste',
|
||||||
'record id': 'ID del record',
|
'record id': 'ID del record',
|
||||||
'register': 'registrazione',
|
'register': 'registrazione',
|
||||||
|
'Remove compiled': 'rimozione codice compilato',
|
||||||
|
'request': 'request',
|
||||||
|
'Resolve Conflict file': 'File di risoluzione conflitto',
|
||||||
|
'response': 'response',
|
||||||
'restore': 'ripristino',
|
'restore': 'ripristino',
|
||||||
'revert': 'versione precedente',
|
'revert': 'versione precedente',
|
||||||
|
'Rows in table': 'Righe nella tabella',
|
||||||
|
'Rows selected': 'Righe selezionate',
|
||||||
|
'rules are not defined': 'rules are not defined',
|
||||||
|
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')",
|
||||||
|
'Running on %s': 'Running on %s',
|
||||||
|
'Save': 'Save',
|
||||||
|
'Save via Ajax': 'Save via Ajax',
|
||||||
|
'Saved file hash:': 'Hash del file salvato:',
|
||||||
'selected': 'selezionato',
|
'selected': 'selezionato',
|
||||||
|
'session': 'session',
|
||||||
'session expired': 'sessions scaduta',
|
'session expired': 'sessions scaduta',
|
||||||
'shell': 'shell',
|
'shell': 'shell',
|
||||||
|
'Site': 'sito',
|
||||||
'some files could not be removed': 'non è stato possibile rimuovere alcuni files',
|
'some files could not be removed': 'non è stato possibile rimuovere alcuni files',
|
||||||
|
'Start wizard': 'start wizard',
|
||||||
'state': 'stato',
|
'state': 'stato',
|
||||||
'static': 'statico',
|
'static': 'statico',
|
||||||
|
'Static files': 'Files statici',
|
||||||
|
'Stylesheet': 'Foglio di stile (stylesheet)',
|
||||||
'submit': 'invia',
|
'submit': 'invia',
|
||||||
|
'Submit': 'Submit',
|
||||||
|
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
|
||||||
'table': 'tabella',
|
'table': 'tabella',
|
||||||
'test': 'test',
|
'test': 'test',
|
||||||
|
'Testing application': 'Test applicazione in corsg',
|
||||||
|
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.',
|
||||||
|
'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller',
|
||||||
'the application logic, each URL path is mapped in one exposed function in the controller': 'logica dell\'applicazione, ogni percorso "URL" corrisponde ad una funzione esposta da un controller',
|
'the application logic, each URL path is mapped in one exposed function in the controller': 'logica dell\'applicazione, ogni percorso "URL" corrisponde ad una funzione esposta da un controller',
|
||||||
|
'The data representation, define database tables and sets': 'The data representation, define database tables and sets',
|
||||||
'the data representation, define database tables and sets': 'rappresentazione dei dati, definizione di tabelle di database e di "set" ',
|
'the data representation, define database tables and sets': 'rappresentazione dei dati, definizione di tabelle di database e di "set" ',
|
||||||
|
'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates',
|
||||||
'the presentations layer, views are also known as templates': 'Presentazione dell\'applicazione, viste (views, chiamate anche "templates")',
|
'the presentations layer, views are also known as templates': 'Presentazione dell\'applicazione, viste (views, chiamate anche "templates")',
|
||||||
|
'There are no controllers': 'Non ci sono controller',
|
||||||
|
'There are no models': 'Non ci sono modelli',
|
||||||
|
'There are no modules': 'Non ci sono moduli',
|
||||||
|
'There are no plugins': 'There are no plugins',
|
||||||
|
'There are no static files': 'Non ci sono file statici',
|
||||||
|
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
|
||||||
|
'There are no views': 'Non ci sono viste ("view")',
|
||||||
|
'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app',
|
||||||
'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui',
|
'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui',
|
||||||
|
'These files are served without processing, your images go here': 'These files are served without processing, your images go here',
|
||||||
|
'This is the %(filename)s template': 'Questo è il template %(filename)s',
|
||||||
|
'Ticket': 'Ticket',
|
||||||
|
'Ticket ID': 'Ticket ID',
|
||||||
|
'TM': 'TM',
|
||||||
'to previous version.': 'torna a versione precedente',
|
'to previous version.': 'torna a versione precedente',
|
||||||
|
'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]',
|
||||||
|
'toggle breakpoint': 'toggle breakpoint',
|
||||||
|
'Toggle Fullscreen': 'Toggle Fullscreen',
|
||||||
|
'Traceback': 'Traceback',
|
||||||
'translation strings for the application': "stringhe di traduzioni per l'applicazione",
|
'translation strings for the application': "stringhe di traduzioni per l'applicazione",
|
||||||
|
'Translation strings for the application': 'Translation strings for the application',
|
||||||
'try': 'prova',
|
'try': 'prova',
|
||||||
'try something like': 'prova qualcosa come',
|
'try something like': 'prova qualcosa come',
|
||||||
|
'try view': 'try view',
|
||||||
|
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
|
||||||
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
|
'unable to create application "%s"': 'impossibile creare applicazione "%s"',
|
||||||
'unable to delete file "%(filename)s"': 'impossibile rimuovere file "%(plugin)s"',
|
'unable to delete file "%(filename)s"': 'impossibile rimuovere file "%(plugin)s"',
|
||||||
'unable to delete file plugin "%(plugin)s"': 'impossibile rimuovere file di plugin "%(plugin)s"',
|
'unable to delete file plugin "%(plugin)s"': 'impossibile rimuovere file di plugin "%(plugin)s"',
|
||||||
|
'Unable to download app because:': 'Impossibile scaricare applicazione perché',
|
||||||
|
'Unable to download because': 'Impossibile scaricare perché',
|
||||||
|
'Unable to download because:': 'Unable to download because:',
|
||||||
'unable to parse csv file': 'non riesco a decodificare questo file CSV',
|
'unable to parse csv file': 'non riesco a decodificare questo file CSV',
|
||||||
'unable to uninstall "%s"': 'impossibile disinstallare "%s"',
|
'unable to uninstall "%s"': 'impossibile disinstallare "%s"',
|
||||||
'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"',
|
'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"',
|
||||||
'uncheck all': 'smarca tutti',
|
'uncheck all': 'smarca tutti',
|
||||||
|
'Uninstall': 'disinstalla',
|
||||||
'update': 'aggiorna',
|
'update': 'aggiorna',
|
||||||
'update all languages': 'aggiorna tutti i linguaggi',
|
'update all languages': 'aggiorna tutti i linguaggi',
|
||||||
|
'Update:': 'Aggiorna:',
|
||||||
'upgrade web2py now': 'upgrade web2py now',
|
'upgrade web2py now': 'upgrade web2py now',
|
||||||
|
'upload': 'upload',
|
||||||
|
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
|
||||||
|
'Upload a package:': 'Upload a package:',
|
||||||
|
'Upload and install packed application': 'Upload and install packed application',
|
||||||
'upload application:': 'carica applicazione:',
|
'upload application:': 'carica applicazione:',
|
||||||
'upload file:': 'carica file:',
|
'upload file:': 'carica file:',
|
||||||
'upload plugin file:': 'carica file di plugin:',
|
'upload plugin file:': 'carica file di plugin:',
|
||||||
|
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
|
||||||
|
'Use an url:': 'Use an url:',
|
||||||
'variables': 'variables',
|
'variables': 'variables',
|
||||||
|
'Version': 'Versione',
|
||||||
|
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
|
||||||
'versioning': 'sistema di versioni',
|
'versioning': 'sistema di versioni',
|
||||||
|
'Versioning': 'Versioning',
|
||||||
'view': 'vista',
|
'view': 'vista',
|
||||||
|
'View': 'Vista',
|
||||||
|
'Views': 'viste',
|
||||||
'views': 'viste',
|
'views': 'viste',
|
||||||
'web2py Recent Tweets': 'Tweets recenti per web2py',
|
'Web Framework': 'Web Framework',
|
||||||
'web2py is up to date': 'web2py è aggiornato',
|
'web2py is up to date': 'web2py è aggiornato',
|
||||||
|
'web2py Recent Tweets': 'Tweets recenti per web2py',
|
||||||
'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo',
|
'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo',
|
||||||
|
'Welcome %s': 'Benvenuto %s',
|
||||||
|
'Welcome to web2py': 'Benvenuto su web2py',
|
||||||
|
'YES': 'SI',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,6 +123,21 @@ if session.authorized:
|
|||||||
else:
|
else:
|
||||||
session.last_time = t0
|
session.last_time = t0
|
||||||
|
|
||||||
|
|
||||||
|
if request.vars.is_mobile in ('true','false','auto'):
|
||||||
|
session.is_mobile = request.vars.is_mobile or 'auto'
|
||||||
|
if request.controller=='default' and request.function=='index':
|
||||||
|
if not request.vars.is_mobile:
|
||||||
|
session.is_mobile = 'auto'
|
||||||
|
if not session.is_mobile:
|
||||||
|
session.is_mobile = 'auto'
|
||||||
|
if session.is_mobile == 'true':
|
||||||
|
is_mobile = True
|
||||||
|
elif session.is_mobile == 'false':
|
||||||
|
is_mobile = False
|
||||||
|
else:
|
||||||
|
is_mobile = request.user_agent().is_mobile
|
||||||
|
|
||||||
if request.controller == "webservices":
|
if request.controller == "webservices":
|
||||||
basic = request.env.http_authorization
|
basic = request.env.http_authorization
|
||||||
if not basic or not basic[:6].lower() == 'basic ':
|
if not basic or not basic[:6].lower() == 'basic ':
|
||||||
@@ -132,8 +147,8 @@ if request.controller == "webservices":
|
|||||||
time.sleep(10)
|
time.sleep(10)
|
||||||
raise HTTP(403,"Not authorized")
|
raise HTTP(403,"Not authorized")
|
||||||
elif not session.authorized and not \
|
elif not session.authorized and not \
|
||||||
(request.controller == 'default' and \
|
(request.controller+'/'+request.function in
|
||||||
request.function in ('index','user')):
|
('default/index','default/user','plugin_jqmobile/index','plugin_jqmobile/about')):
|
||||||
|
|
||||||
if request.env.query_string:
|
if request.env.query_string:
|
||||||
query_string = '?' + request.env.query_string
|
query_string = '?' + request.env.query_string
|
||||||
@@ -150,7 +165,6 @@ elif session.authorized and \
|
|||||||
request.function == 'index':
|
request.function == 'index':
|
||||||
redirect(URL(request.application, 'default', 'site'))
|
redirect(URL(request.application, 'default', 'site'))
|
||||||
|
|
||||||
|
|
||||||
if request.controller=='appadmin' and DEMO_MODE:
|
if request.controller=='appadmin' and DEMO_MODE:
|
||||||
session.flash = 'Appadmin disabled in demo mode'
|
session.flash = 'Appadmin disabled in demo mode'
|
||||||
redirect(URL('default','sites'))
|
redirect(URL('default','sites'))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ def A_button(*a,**b):
|
|||||||
return A(*a,**b)
|
return A(*a,**b)
|
||||||
|
|
||||||
def button(href, label):
|
def button(href, label):
|
||||||
if request.user_agent().is_mobile:
|
if is_mobile:
|
||||||
ret = A_button(SPAN(label), _href=href)
|
ret = A_button(SPAN(label), _href=href)
|
||||||
else:
|
else:
|
||||||
ret = A(SPAN(label),_class='button',_href=href)
|
ret = A(SPAN(label),_class='button',_href=href)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ h3 {font-size:2.00em}
|
|||||||
h4 {font-size:1.50em}
|
h4 {font-size:1.50em}
|
||||||
h5 {font-size:1.25em}
|
h5 {font-size:1.25em}
|
||||||
h6 {font-size:1.12em}
|
h6 {font-size:1.12em}
|
||||||
th,label {font-weight:bold; white-space:nowrap}
|
th,label {font-weight:bold; white-space:nowrap;}
|
||||||
td,th {text-align:left; padding:2px 5px 2px 5px}
|
td,th {text-align:left; padding:2px 5px 2px 5px}
|
||||||
th {vertical-align:middle; border-right:1px solid white}
|
th {vertical-align:middle; border-right:1px solid white}
|
||||||
td {vertical-align:top}
|
td {vertical-align:top}
|
||||||
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
|
|||||||
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
||||||
|
|
||||||
/* fix ie problem with menu */
|
/* fix ie problem with menu */
|
||||||
.ie-lte7 .topbar .container {z-index:2}
|
|
||||||
|
|
||||||
td.w2p_fw {padding-bottom:1px}
|
td.w2p_fw {padding-bottom:1px}
|
||||||
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
||||||
@@ -187,10 +186,7 @@ div.error {
|
|||||||
.web2py_paginator {}
|
.web2py_paginator {}
|
||||||
.web2py_grid {width:100%}
|
.web2py_grid {width:100%}
|
||||||
.web2py_grid table {width:100%}
|
.web2py_grid table {width:100%}
|
||||||
.web2py_grid tbody td {
|
.web2py_grid tbody td {padding:2px 5px 2px 5px; vertical-align: middle;}
|
||||||
padding:2px 5px 2px 5px;
|
|
||||||
vertical-align:middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.web2py_grid thead th,.web2py_grid tfoot td {
|
.web2py_grid thead th,.web2py_grid tfoot td {
|
||||||
background-color:#EAEAEA;
|
background-color:#EAEAEA;
|
||||||
@@ -299,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
|
|||||||
.web2py_console input, .web2py_console select,
|
.web2py_console input, .web2py_console select,
|
||||||
.web2py_console a { margin: 2px; }
|
.web2py_console a { margin: 2px; }
|
||||||
|
|
||||||
.ie9 #w2p_query_panel {padding-bottom:2px}
|
|
||||||
|
|
||||||
#wiki_page_body {
|
#wiki_page_body {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fix some IE problems */
|
||||||
|
|
||||||
|
.ie-lte7 .topbar .container {z-index:2}
|
||||||
|
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
|
||||||
|
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
|
||||||
|
.ie9 #w2p_query_panel {padding-bottom:2px}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -39,7 +39,7 @@ function web2py_ajax_init(target) {
|
|||||||
|
|
||||||
function web2py_event_handlers() {
|
function web2py_event_handlers() {
|
||||||
var doc = jQuery(document)
|
var doc = jQuery(document)
|
||||||
doc.on('click', '.flash', function(e){jQuery(this).fadeOut('slow'); e.preventDefault();});
|
doc.on('click', '.flash', function(e){var t=jQuery(this); if(t.css('top')=='0px') t.slideUp('slow'); else t.fadeOut(); e.preventDefault();});
|
||||||
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
|
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
|
||||||
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
|
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
|
||||||
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
|
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
|
||||||
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
|
|||||||
jQuery(function() {
|
jQuery(function() {
|
||||||
var flash = jQuery('.flash');
|
var flash = jQuery('.flash');
|
||||||
flash.hide();
|
flash.hide();
|
||||||
if(flash.html()) flash.slideDown();
|
if(flash.html()) flash.append('<span style="float:right;">×</span>').slideDown();
|
||||||
web2py_ajax_init(document);
|
web2py_ajax_init(document);
|
||||||
web2py_event_handlers();
|
web2py_event_handlers();
|
||||||
});
|
});
|
||||||
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
|
|||||||
form.submit(function(e){
|
form.submit(function(e){
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('post',action,form.serialize(),target);
|
web2py_ajax_page('post',action,form.serialize(),target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_trap_link(target) {
|
function web2py_trap_link(target) {
|
||||||
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
||||||
var link=jQuery(this);
|
var link=jQuery(this);
|
||||||
link.click(function(e) {
|
link.click(function(e) {
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('get',link.attr('href'),[],target);
|
web2py_ajax_page('get',link.attr('href'),[],target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_ajax_page(method, action, data, target) {
|
function web2py_ajax_page(method, action, data, target) {
|
||||||
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
|
|||||||
web2py_trap_link(target);
|
web2py_trap_link(target);
|
||||||
web2py_ajax_init('#'+target);
|
web2py_ajax_init('#'+target);
|
||||||
if(command)
|
if(command)
|
||||||
eval(decodeURIComponent(command));
|
eval(decodeURIComponent(command));
|
||||||
if(flash)
|
if(flash)
|
||||||
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// run once (no timeout specified)
|
// run once (no timeout specified)
|
||||||
element.reload_counter = Infinity;
|
element.reload_counter = Infinity;
|
||||||
web2py_ajax_page('get', action, null, target);
|
web2py_ajax_page('get', action, null, target);
|
||||||
} }); }
|
} }); }
|
||||||
|
|
||||||
function web2py_comet(url,onmessage,onopen,onclose) {
|
function web2py_websocket(url,onmessage,onopen,onclose) {
|
||||||
if ("WebSocket" in window) {
|
if ("WebSocket" in window) {
|
||||||
var ws = new WebSocket(url);
|
var ws = new WebSocket(url);
|
||||||
ws.onopen = onopen?onopen:(function(){});
|
ws.onopen = onopen?onopen:(function(){});
|
||||||
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
|
|||||||
} else return false; // not supported
|
} else return false; // not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function web2py_calc_entropy(mystring) {
|
||||||
|
//calculate a simple entropy for a given string
|
||||||
|
var csets = new Array(
|
||||||
|
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
|
||||||
|
'0123456789abcdefghijklmnopqrstuvwxyz');
|
||||||
|
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
|
||||||
|
for (var i=0;i<mystringlist.length;i++) { // classify this character
|
||||||
|
var c = mystringlist[i], inset=5;
|
||||||
|
for(var j = 0; j<csets.length; j++)
|
||||||
|
if (csets[j].indexOf(c) != -1) {inset = j; break;}
|
||||||
|
//calculate effect of character on alphabet size
|
||||||
|
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
|
||||||
|
else if (!(c in other)) {score += 1;other[c] = 1;}
|
||||||
|
if (inset != lastset) {score += 1;lastset = inset;}
|
||||||
|
}
|
||||||
|
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
|
||||||
|
return Math.round(entropy*100)/100
|
||||||
|
}
|
||||||
|
|
||||||
|
function web2py_validate_entropy(myfield, req_entropy) {
|
||||||
|
var validator = function () {
|
||||||
|
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
|
||||||
|
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
|
||||||
|
if(v<=0.5) {r=1.0; g=2.0*v;}
|
||||||
|
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
|
||||||
|
var color = '#'+rs(r)+rs(g)+rs(b);
|
||||||
|
myfield.css('background-color',color);
|
||||||
|
entropy_callback = myfield.data('entropy_callback');
|
||||||
|
if(entropy_callback) entropy_callback(v);
|
||||||
|
}
|
||||||
|
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,173 +0,0 @@
|
|||||||
/*! jQuery Mobile v1.0 jquerymobile.com | jquery.org/license */
|
|
||||||
(function(a,e){if(a.cleanData){var b=a.cleanData;a.cleanData=function(f){for(var c=0,h;(h=f[c])!=null;c++)a(h).triggerHandler("remove");b(f)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){a(this).triggerHandler("remove")});return d.call(a(this),b,c)})}}a.widget=function(b,c,h){var d=b.split(".")[0],e,b=b.split(".")[1];e=d+"-"+b;if(!h)h=c,c=a.Widget;a.expr[":"][e]=function(c){return!!a.data(c,
|
|
||||||
b)};a[d]=a[d]||{};a[d][b]=function(a,b){arguments.length&&this._createWidget(a,b)};c=new c;c.options=a.extend(true,{},c.options);a[d][b].prototype=a.extend(true,c,{namespace:d,widgetName:b,widgetEventPrefix:a[d][b].prototype.widgetEventPrefix||b,widgetBaseClass:e},h);a.widget.bridge(b,a[d][b])};a.widget.bridge=function(b,c){a.fn[b]=function(d){var g=typeof d==="string",i=Array.prototype.slice.call(arguments,1),k=this,d=!g&&i.length?a.extend.apply(null,[true,d].concat(i)):d;if(g&&d.charAt(0)==="_")return k;
|
|
||||||
g?this.each(function(){var c=a.data(this,b);if(!c)throw"cannot call methods on "+b+" prior to initialization; attempted to call method '"+d+"'";if(!a.isFunction(c[d]))throw"no such method '"+d+"' for "+b+" widget instance";var g=c[d].apply(c,i);if(g!==c&&g!==e)return k=g,false}):this.each(function(){var e=a.data(this,b);e?e.option(d||{})._init():a.data(this,b,new c(d,this))});return k}};a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)};a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",
|
|
||||||
options:{disabled:false},_createWidget:function(b,c){a.data(c,this.widgetName,this);this.element=a(c);this.options=a.extend(true,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){var b={};a.metadata&&(b=a.metadata.get(element)[this.widgetName]);return b},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);
|
|
||||||
this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(b,c){var d=b;if(arguments.length===0)return a.extend({},this.options);if(typeof b==="string"){if(c===e)return this.options[b];d={};d[b]=c}this._setOptions(d);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b;a==="disabled"&&
|
|
||||||
this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(b,c,d){var e=this.options[b],c=a.Event(c);c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase();d=d||{};if(c.originalEvent)for(var b=a.event.props.length,i;b;)i=a.event.props[--b],c[i]=c.originalEvent[i];this.element.trigger(c,
|
|
||||||
d);return!(a.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.widget",{_createWidget:function(){a.Widget.prototype._createWidget.apply(this,arguments);this._trigger("init")},_getCreateOptions:function(){var b=this.element,d={};a.each(this.options,function(a){var c=b.jqmData(a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()}));c!==e&&(d[a]=c)});return d},enhanceWithin:function(b){var d=a(b).closest(":jqmData(role='page')").data("page"),d=d&&d.keepNativeSelector()||"";a(this.options.initSelector,b).not(d)[this.widgetName]()}})})(jQuery);
|
|
||||||
(function(a){a(window);var e=a("html");a.mobile.media=function(){var b={},d=a("<div id='jquery-mediatest'>"),f=a("<body>").append(d);return function(a){if(!(a in b)){var h=document.createElement("style"),g="@media "+a+" { #jquery-mediatest { position:absolute; } }";h.type="text/css";h.styleSheet?h.styleSheet.cssText=g:h.appendChild(document.createTextNode(g));e.prepend(f).prepend(h);b[a]=d.css("position")==="absolute";f.add(h).remove()}return b[a]}}()})(jQuery);
|
|
||||||
(function(a,e){function b(a){var b=a.charAt(0).toUpperCase()+a.substr(1),a=(a+" "+c.join(b+" ")+b).split(" "),d;for(d in a)if(f[a[d]]!==e)return true}var d=a("<body>").prependTo("html"),f=d[0].style,c=["Webkit","Moz","O"],h="palmGetResource"in window,g=window.operamini&&{}.toString.call(window.operamini)==="[object OperaMini]",i=window.blackberry;a.mobile.browser={};a.mobile.browser.ie=function(){for(var a=3,b=document.createElement("div"),c=b.all||[];b.innerHTML="<\!--[if gt IE "+ ++a+"]><br><![endif]--\>",
|
|
||||||
c[0];);return a>4?a:!a}();a.extend(a.support,{orientation:"orientation"in window&&"onorientationchange"in window,touch:"ontouchend"in document,cssTransitions:"WebKitTransitionEvent"in window,pushState:"pushState"in history&&"replaceState"in history,mediaquery:a.mobile.media("only all"),cssPseudoElement:!!b("content"),touchOverflow:!!b("overflowScrolling"),boxShadow:!!b("boxShadow")&&!i,scrollTop:("pageXOffset"in window||"scrollTop"in document.documentElement||"scrollTop"in d[0])&&!h&&!g,dynamicBaseTag:function(){var b=
|
|
||||||
location.protocol+"//"+location.host+location.pathname+"ui-dir/",c=a("head base"),f=null,e="",h;c.length?e=c.attr("href"):c=f=a("<base>",{href:b}).appendTo("head");h=a("<a href='testurl' />").prependTo(d)[0].href;c[0].href=e||location.pathname;f&&f.remove();return h.indexOf(b)===0}()});d.remove();h=function(){var a=window.navigator.userAgent;return a.indexOf("Nokia")>-1&&(a.indexOf("Symbian/3")>-1||a.indexOf("Series60/5")>-1)&&a.indexOf("AppleWebKit")>-1&&a.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/)}();
|
|
||||||
a.mobile.ajaxBlacklist=window.blackberry&&!window.WebKitPoint||g||h;h&&a(function(){a("head link[rel='stylesheet']").attr("rel","alternate stylesheet").attr("rel","stylesheet")});a.support.boxShadow||a("html").addClass("ui-mobile-nosupport-boxshadow")})(jQuery);
|
|
||||||
(function(a,e,b,d){function f(a){for(;a&&typeof a.originalEvent!=="undefined";)a=a.originalEvent;return a}function c(b){for(var c={},f,d;b;){f=a.data(b,n);for(d in f)if(f[d])c[d]=c.hasVirtualBinding=true;b=b.parentNode}return c}function h(){v&&(clearTimeout(v),v=0);v=setTimeout(function(){E=v=0;u.length=0;D=false;y=true},a.vmouse.resetTimerDuration)}function g(b,c,r){var e,h;if(!(h=r&&r[b])){if(r=!r)a:{for(r=c.target;r;){if((h=a.data(r,n))&&(!b||h[b]))break a;r=r.parentNode}r=null}h=r}if(h){e=c;var r=
|
|
||||||
e.type,j,g;e=a.Event(e);e.type=b;h=e.originalEvent;j=a.event.props;if(h)for(g=j.length;g;)b=j[--g],e[b]=h[b];if(r.search(/mouse(down|up)|click/)>-1&&!e.which)e.which=1;if(r.search(/^touch/)!==-1&&(b=f(h),r=b.touches,b=b.changedTouches,r=r&&r.length?r[0]:b&&b.length?b[0]:d))for(h=0,len=z.length;h<len;h++)b=z[h],e[b]=r[b];a(c.target).trigger(e)}return e}function i(b){var c=a.data(b.target,A);if(!D&&(!E||E!==c))if(c=g("v"+b.type,b))c.isDefaultPrevented()&&b.preventDefault(),c.isPropagationStopped()&&
|
|
||||||
b.stopPropagation(),c.isImmediatePropagationStopped()&&b.stopImmediatePropagation()}function k(b){var d=f(b).touches,e;if(d&&d.length===1&&(e=b.target,d=c(e),d.hasVirtualBinding))E=r++,a.data(e,A,E),v&&(clearTimeout(v),v=0),w=y=false,e=f(b).touches[0],x=e.pageX,t=e.pageY,g("vmouseover",b,d),g("vmousedown",b,d)}function l(a){y||(w||g("vmousecancel",a,c(a.target)),w=true,h())}function o(b){if(!y){var d=f(b).touches[0],r=w,e=a.vmouse.moveDistanceThreshold;w=w||Math.abs(d.pageX-x)>e||Math.abs(d.pageY-
|
|
||||||
t)>e;flags=c(b.target);w&&!r&&g("vmousecancel",b,flags);g("vmousemove",b,flags);h()}}function m(a){if(!y){y=true;var b=c(a.target),d;g("vmouseup",a,b);if(!w&&(d=g("vclick",a,b))&&d.isDefaultPrevented())d=f(a).changedTouches[0],u.push({touchID:E,x:d.clientX,y:d.clientY}),D=true;g("vmouseout",a,b);w=false;h()}}function p(b){var b=a.data(b,n),c;if(b)for(c in b)if(b[c])return true;return false}function j(){}function q(b){var c=b.substr(1);return{setup:function(){p(this)||a.data(this,n,{});a.data(this,
|
|
||||||
n)[b]=true;s[b]=(s[b]||0)+1;s[b]===1&&B.bind(c,i);a(this).bind(c,j);if(C)s.touchstart=(s.touchstart||0)+1,s.touchstart===1&&B.bind("touchstart",k).bind("touchend",m).bind("touchmove",o).bind("scroll",l)},teardown:function(){--s[b];s[b]||B.unbind(c,i);C&&(--s.touchstart,s.touchstart||B.unbind("touchstart",k).unbind("touchmove",o).unbind("touchend",m).unbind("scroll",l));var d=a(this),f=a.data(this,n);f&&(f[b]=false);d.unbind(c,j);p(this)||d.removeData(n)}}}var n="virtualMouseBindings",A="virtualTouchID",
|
|
||||||
e="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),z="clientX clientY pageX pageY screenX screenY".split(" "),s={},v=0,x=0,t=0,w=false,u=[],D=false,y=false,C="addEventListener"in b,B=a(b),r=1,E=0;a.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,resetTimerDuration:1500};for(var F=0;F<e.length;F++)a.event.special[e[F]]=q(e[F]);C&&b.addEventListener("click",function(b){var c=u.length,d=b.target,f,r,e,h,j;if(c){f=b.clientX;r=b.clientY;threshold=a.vmouse.clickDistanceThreshold;
|
|
||||||
for(e=d;e;){for(h=0;h<c;h++)if(j=u[h],e===d&&Math.abs(j.x-f)<threshold&&Math.abs(j.y-r)<threshold||a.data(e,A)===j.touchID){b.preventDefault();b.stopPropagation();return}e=e.parentNode}}},true)})(jQuery,window,document);
|
|
||||||
(function(a,e,b){function d(b,c,d){var f=d.type;d.type=c;a.event.handle.call(b,d);d.type=f}a.each("touchstart touchmove touchend orientationchange throttledresize tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(b,c){a.fn[c]=function(a){return a?this.bind(c,a):this.trigger(c)};a.attrFn[c]=true});var f=a.support.touch,c=f?"touchstart":"mousedown",h=f?"touchend":"mouseup",g=f?"touchmove":"mousemove";a.event.special.scrollstart={enabled:true,setup:function(){function b(a,
|
|
||||||
e){f=e;d(c,f?"scrollstart":"scrollstop",a)}var c=this,f,e;a(c).bind("touchmove scroll",function(c){a.event.special.scrollstart.enabled&&(f||b(c,true),clearTimeout(e),e=setTimeout(function(){b(c,false)},50))})}};a.event.special.tap={setup:function(){var b=this,c=a(b);c.bind("vmousedown",function(f){function e(){clearTimeout(q)}function h(){e();c.unbind("vclick",g).unbind("vmouseup",e).unbind("vmousecancel",h)}function g(a){h();j==a.target&&d(b,"tap",a)}if(f.which&&f.which!==1)return false;var j=f.target,
|
|
||||||
q;c.bind("vmousecancel",h).bind("vmouseup",e).bind("vclick",g);q=setTimeout(function(){d(b,"taphold",a.Event("taphold"))},750)})}};a.event.special.swipe={scrollSupressionThreshold:10,durationThreshold:1E3,horizontalDistanceThreshold:30,verticalDistanceThreshold:75,setup:function(){var d=a(this);d.bind(c,function(c){function f(b){if(m){var c=b.originalEvent.touches?b.originalEvent.touches[0]:b;p={time:(new Date).getTime(),coords:[c.pageX,c.pageY]};Math.abs(m.coords[0]-p.coords[0])>a.event.special.swipe.scrollSupressionThreshold&&
|
|
||||||
b.preventDefault()}}var e=c.originalEvent.touches?c.originalEvent.touches[0]:c,m={time:(new Date).getTime(),coords:[e.pageX,e.pageY],origin:a(c.target)},p;d.bind(g,f).one(h,function(){d.unbind(g,f);m&&p&&p.time-m.time<a.event.special.swipe.durationThreshold&&Math.abs(m.coords[0]-p.coords[0])>a.event.special.swipe.horizontalDistanceThreshold&&Math.abs(m.coords[1]-p.coords[1])<a.event.special.swipe.verticalDistanceThreshold&&m.origin.trigger("swipe").trigger(m.coords[0]>p.coords[0]?"swipeleft":"swiperight");
|
|
||||||
m=p=b})})}};(function(a,b){function c(){var a=f();a!==e&&(e=a,d.trigger("orientationchange"))}var d=a(b),f,e;a.event.special.orientationchange={setup:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;e=f();d.bind("throttledresize",c)},teardown:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;d.unbind("throttledresize",c)},add:function(a){var b=a.handler;a.handler=function(a){a.orientation=f();return b.apply(this,arguments)}}};a.event.special.orientationchange.orientation=
|
|
||||||
f=function(){var c=true,c=document.documentElement;return(c=a.support.orientation?b.orientation%180==0:c&&c.clientWidth/c.clientHeight<1.1)?"portrait":"landscape"}})(jQuery,e);(function(){a.event.special.throttledresize={setup:function(){a(this).bind("resize",b)},teardown:function(){a(this).unbind("resize",b)}};var b=function(){f=(new Date).getTime();e=f-c;e>=250?(c=f,a(this).trigger("throttledresize")):(d&&clearTimeout(d),d=setTimeout(b,250-e))},c=0,d,f,e})();a.each({scrollstop:"scrollstart",taphold:"tap",
|
|
||||||
swipeleft:"swipe",swiperight:"swipe"},function(b,c){a.event.special[b]={setup:function(){a(this).bind(c,a.noop)}}})})(jQuery,this);
|
|
||||||
(function(a,e,b){function d(a){a=a||location.href;return"#"+a.replace(/^[^#]*#?(.*)$/,"$1")}var f="hashchange",c=document,h,g=a.event.special,i=c.documentMode,k="on"+f in e&&(i===b||i>7);a.fn[f]=function(a){return a?this.bind(f,a):this.trigger(f)};a.fn[f].delay=50;g[f]=a.extend(g[f],{setup:function(){if(k)return false;a(h.start)},teardown:function(){if(k)return false;a(h.stop)}});h=function(){function h(){var b=d(),c=n(p);if(b!==p)q(p=b,c),a(e).trigger(f);else if(c!==p)location.href=location.href.replace(/#.*/,
|
|
||||||
"")+c;i=setTimeout(h,a.fn[f].delay)}var g={},i,p=d(),j=function(a){return a},q=j,n=j;g.start=function(){i||h()};g.stop=function(){i&&clearTimeout(i);i=b};a.browser.msie&&!k&&function(){var b,e;g.start=function(){if(!b)e=(e=a.fn[f].src)&&e+d(),b=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){e||q(d());h()}).attr("src",e||"javascript:0").insertAfter("body")[0].contentWindow,c.onpropertychange=function(){try{if(event.propertyName==="title")b.document.title=c.title}catch(a){}}};
|
|
||||||
g.stop=j;n=function(){return d(b.location.href)};q=function(d,e){var h=b.document,g=a.fn[f].domain;if(d!==e)h.title=c.title,h.open(),g&&h.write('<script>document.domain="'+g+'"<\/script>'),h.close(),b.location.hash=d}}();return g}()})(jQuery,this);
|
|
||||||
(function(a){a.widget("mobile.page",a.mobile.widget,{options:{theme:"c",domCache:false,keepNativeDefault:":jqmData(role='none'), :jqmData(role='nojs')"},_create:function(){this._trigger("beforecreate");this.element.attr("tabindex","0").addClass("ui-page ui-body-"+this.options.theme)},keepNativeSelector:function(){var e=this.options;return e.keepNative&&a.trim(e.keepNative)&&e.keepNative!==e.keepNativeDefault?[e.keepNative,e.keepNativeDefault].join(", "):e.keepNativeDefault}})})(jQuery);
|
|
||||||
(function(a,e){var b={};a.extend(a.mobile,{ns:"",subPageUrlKey:"ui-page",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",ajaxEnabled:true,hashListeningEnabled:true,linkBindingEnabled:true,defaultPageTransition:"slide",minScrollBack:250,defaultDialogTransition:"pop",loadingMessage:"loading",pageLoadErrorMessage:"Error Loading Page",autoInitializePage:true,pushStateEnabled:true,orientationChangeEnabled:true,gradeA:function(){return a.support.mediaquery||a.mobile.browser.ie&&a.mobile.browser.ie>=
|
|
||||||
7},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91},silentScroll:function(b){if(a.type(b)!=="number")b=a.mobile.defaultHomeScroll;a.event.special.scrollstart.enabled=false;
|
|
||||||
setTimeout(function(){e.scrollTo(0,b);a(document).trigger("silentscroll",{x:0,y:b})},20);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},nsNormalizeDict:b,nsNormalize:function(c){return!c?void 0:b[c]||(b[c]=a.camelCase(a.mobile.ns+c))},getInheritedTheme:function(a,b){for(var d=a[0],f="",e=/ui-(bar|body)-([a-z])\b/,l,o;d;){l=d.className||"";if((o=e.exec(l))&&(f=o[2]))break;d=d.parentNode}return f||b||"a"}});a.fn.jqmData=function(b,d){var f;typeof b!="undefined"&&(f=this.data(b?
|
|
||||||
a.mobile.nsNormalize(b):b,d));return f};a.jqmData=function(b,d,f){var e;typeof d!="undefined"&&(e=a.data(b,d?a.mobile.nsNormalize(d):d,f));return e};a.fn.jqmRemoveData=function(b){return this.removeData(a.mobile.nsNormalize(b))};a.jqmRemoveData=function(b,d){return a.removeData(b,a.mobile.nsNormalize(d))};a.fn.removeWithDependents=function(){a.removeWithDependents(this)};a.removeWithDependents=function(b){b=a(b);(b.jqmData("dependents")||a()).remove();b.remove()};a.fn.addDependents=function(b){a.addDependents(a(this),
|
|
||||||
b)};a.addDependents=function(b,d){var f=a(b).jqmData("dependents")||a();a(b).jqmData("dependents",a.merge(f,d))};a.fn.getEncodedText=function(){return a("<div/>").text(a(this).text()).html()};var d=a.find,f=/:jqmData\(([^)]*)\)/g;a.find=function(b,e,g,i){b=b.replace(f,"[data-"+(a.mobile.ns||"")+"$1]");return d.call(this,b,e,g,i)};a.extend(a.find,d);a.find.matches=function(b,d){return a.find(b,null,null,d)};a.find.matchesSelector=function(b,d){return a.find(d,null,null,[b]).length>0}})(jQuery,this);
|
|
||||||
(function(a,e){function b(a){var b=a.find(".ui-title:eq(0)");b.length?b.focus():a.focus()}function d(b){q&&(!q.closest(".ui-page-active").length||b)&&q.removeClass(a.mobile.activeBtnClass);q=null}function f(){z=false;A.length>0&&a.mobile.changePage.apply(null,A.pop())}function c(c,d,f,e){var g=a.mobile.urlHistory.getActive(),j=a.support.touchOverflow&&a.mobile.touchOverflowEnabled,i=g.lastScroll||(j?0:a.mobile.defaultHomeScroll),g=h();window.scrollTo(0,a.mobile.defaultHomeScroll);d&&d.data("page")._trigger("beforehide",
|
|
||||||
null,{nextPage:c});j||c.height(g+i);c.data("page")._trigger("beforeshow",null,{prevPage:d||a("")});a.mobile.hidePageLoadingMsg();j&&i&&(c.addClass("ui-mobile-pre-transition"),b(c),c.is(".ui-native-fixed")?c.find(".ui-content").scrollTop(i):c.scrollTop(i));f=(a.mobile.transitionHandlers[f||"none"]||a.mobile.defaultTransitionHandler)(f,e,c,d);f.done(function(){j||(c.height(""),b(c));j||a.mobile.silentScroll(i);d&&(j||d.height(""),d.data("page")._trigger("hide",null,{nextPage:c}));c.data("page")._trigger("show",
|
|
||||||
null,{prevPage:d||a("")})});return f}function h(){var b=a.event.special.orientationchange.orientation()==="portrait",c=b?screen.availHeight:screen.availWidth,b=Math.max(b?480:320,a(window).height());return Math.min(c,b)}function g(){(!a.support.touchOverflow||!a.mobile.touchOverflowEnabled)&&a("."+a.mobile.activePageClass).css("min-height",h())}function i(b,c){c&&b.attr("data-"+a.mobile.ns+"role",c);b.page()}function k(a){for(;a;){if(typeof a.nodeName==="string"&&a.nodeName.toLowerCase()=="a")break;
|
|
||||||
a=a.parentNode}return a}function l(b){var b=a(b).closest(".ui-page").jqmData("url"),c=t.hrefNoHash;if(!b||!j.isPath(b))b=c;return j.makeUrlAbsolute(b,c)}var o=a(window),m=a("html"),p=a("head"),j={urlParseRE:/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,parseUrl:function(b){if(a.type(b)==="object")return b;b=j.urlParseRE.exec(b||"")||[];return{href:b[0]||"",hrefNoHash:b[1]||
|
|
||||||
"",hrefNoSearch:b[2]||"",domain:b[3]||"",protocol:b[4]||"",doubleSlash:b[5]||"",authority:b[6]||"",username:b[8]||"",password:b[9]||"",host:b[10]||"",hostname:b[11]||"",port:b[12]||"",pathname:b[13]||"",directory:b[14]||"",filename:b[15]||"",search:b[16]||"",hash:b[17]||""}},makePathAbsolute:function(a,b){if(a&&a.charAt(0)==="/")return a;for(var a=a||"",c=(b=b?b.replace(/^\/|(\/[^\/]*|[^\/]+)$/g,""):"")?b.split("/"):[],d=a.split("/"),f=0;f<d.length;f++){var e=d[f];switch(e){case ".":break;case "..":c.length&&
|
|
||||||
c.pop();break;default:c.push(e)}}return"/"+c.join("/")},isSameDomain:function(a,b){return j.parseUrl(a).domain===j.parseUrl(b).domain},isRelativeUrl:function(a){return j.parseUrl(a).protocol===""},isAbsoluteUrl:function(a){return j.parseUrl(a).protocol!==""},makeUrlAbsolute:function(a,b){if(!j.isRelativeUrl(a))return a;var c=j.parseUrl(a),d=j.parseUrl(b),f=c.protocol||d.protocol,e=c.protocol?c.doubleSlash:c.doubleSlash||d.doubleSlash,h=c.authority||d.authority,g=c.pathname!=="",i=j.makePathAbsolute(c.pathname||
|
|
||||||
d.filename,d.pathname);return f+e+h+i+(c.search||!g&&d.search||"")+c.hash},addSearchParams:function(b,c){var d=j.parseUrl(b),f=typeof c==="object"?a.param(c):c,e=d.search||"?";return d.hrefNoSearch+e+(e.charAt(e.length-1)!=="?"?"&":"")+f+(d.hash||"")},convertUrlToDataUrl:function(a){var b=j.parseUrl(a);if(j.isEmbeddedPage(b))return b.hash.split(s)[0].replace(/^#/,"");else if(j.isSameDomain(b,t))return b.hrefNoHash.replace(t.domain,"");return a},get:function(a){if(a===e)a=location.hash;return j.stripHash(a).replace(/[^\/]*\.[^\/*]+$/,
|
|
||||||
"")},getFilePath:function(b){var c="&"+a.mobile.subPageUrlKey;return b&&b.split(c)[0].split(s)[0]},set:function(a){location.hash=a},isPath:function(a){return/\//.test(a)},clean:function(a){return a.replace(t.domain,"")},stripHash:function(a){return a.replace(/^#/,"")},cleanHash:function(a){return j.stripHash(a.replace(/\?.*$/,"").replace(s,""))},isExternal:function(a){a=j.parseUrl(a);return a.protocol&&a.domain!==x.domain?true:false},hasProtocol:function(a){return/^(:?\w+:)/.test(a)},isFirstPageUrl:function(b){var b=
|
|
||||||
j.parseUrl(j.makeUrlAbsolute(b,t)),c=a.mobile.firstPage,c=c&&c[0]?c[0].id:e;return(b.hrefNoHash===x.hrefNoHash||w&&b.hrefNoHash===t.hrefNoHash)&&(!b.hash||b.hash==="#"||c&&b.hash.replace(/^#/,"")===c)},isEmbeddedPage:function(a){a=j.parseUrl(a);return a.protocol!==""?a.hash&&(a.hrefNoHash===x.hrefNoHash||w&&a.hrefNoHash===t.hrefNoHash):/^#/.test(a.href)}},q=null,n={stack:[],activeIndex:0,getActive:function(){return n.stack[n.activeIndex]},getPrev:function(){return n.stack[n.activeIndex-1]},getNext:function(){return n.stack[n.activeIndex+
|
|
||||||
1]},addNew:function(a,b,c,d,f){n.getNext()&&n.clearForward();n.stack.push({url:a,transition:b,title:c,pageUrl:d,role:f});n.activeIndex=n.stack.length-1},clearForward:function(){n.stack=n.stack.slice(0,n.activeIndex+1)},directHashChange:function(b){var c,d,f;this.getActive();a.each(n.stack,function(a,e){b.currentUrl===e.url&&(c=a<n.activeIndex,d=!c,f=a)});this.activeIndex=f!==e?f:this.activeIndex;c?(b.either||b.isBack)(true):d&&(b.either||b.isForward)(false)},ignoreNextHashChange:false},A=[],z=false,
|
|
||||||
s="&ui-state=dialog",v=p.children("base"),x=j.parseUrl(location.href),t=v.length?j.parseUrl(j.makeUrlAbsolute(v.attr("href"),x.href)):x,w=x.hrefNoHash!==t.hrefNoHash,u=a.support.dynamicBaseTag?{element:v.length?v:a("<base>",{href:t.hrefNoHash}).prependTo(p),set:function(a){u.element.attr("href",j.makeUrlAbsolute(a,t))},reset:function(){u.element.attr("href",t.hrefNoHash)}}:e,D=true,y,C,B;y=function(){var b=o;a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&(b=a(".ui-page-active"),b=b.is(".ui-native-fixed")?
|
|
||||||
b.find(".ui-content"):b);return b};C=function(b){if(D){var c=a.mobile.urlHistory.getActive();if(c)b=b&&b.scrollTop(),c.lastScroll=b<a.mobile.minScrollBack?a.mobile.defaultHomeScroll:b}};B=function(){setTimeout(C,100,a(this))};o.bind(a.support.pushState?"popstate":"hashchange",function(){D=false});o.one(a.support.pushState?"popstate":"hashchange",function(){D=true});o.one("pagecontainercreate",function(){a.mobile.pageContainer.bind("pagechange",function(){var a=y();D=true;a.unbind("scrollstop",B);
|
|
||||||
a.bind("scrollstop",B)})});y().bind("scrollstop",B);a.mobile.getScreenHeight=h;a.fn.animationComplete=function(b){return a.support.cssTransitions?a(this).one("webkitAnimationEnd",b):(setTimeout(b,0),a(this))};a.mobile.path=j;a.mobile.base=u;a.mobile.urlHistory=n;a.mobile.dialogHashKey=s;a.mobile.noneTransitionHandler=function(b,c,d,f){f&&f.removeClass(a.mobile.activePageClass);d.addClass(a.mobile.activePageClass);return a.Deferred().resolve(b,c,d,f).promise()};a.mobile.defaultTransitionHandler=a.mobile.noneTransitionHandler;
|
|
||||||
a.mobile.transitionHandlers={none:a.mobile.defaultTransitionHandler};a.mobile.allowCrossDomainPages=false;a.mobile.getDocumentUrl=function(b){return b?a.extend({},x):x.href};a.mobile.getDocumentBase=function(b){return b?a.extend({},t):t.href};a.mobile._bindPageRemove=function(){var b=a(this);!b.data("page").options.domCache&&b.is(":jqmData(external-page='true')")&&b.bind("pagehide.remove",function(){var b=a(this),c=new a.Event("pageremove");b.trigger(c);c.isDefaultPrevented()||b.removeWithDependents()})};
|
|
||||||
a.mobile.loadPage=function(b,c){var d=a.Deferred(),f=a.extend({},a.mobile.loadPage.defaults,c),h=null,g=null,m=j.makeUrlAbsolute(b,a.mobile.activePage&&l(a.mobile.activePage)||t.hrefNoHash);if(f.data&&f.type==="get")m=j.addSearchParams(m,f.data),f.data=e;if(f.data&&f.type==="post")f.reloadPage=true;var s=j.getFilePath(m),p=j.convertUrlToDataUrl(m);f.pageContainer=f.pageContainer||a.mobile.pageContainer;h=f.pageContainer.children(":jqmData(url='"+p+"')");h.length===0&&p&&!j.isPath(p)&&(h=f.pageContainer.children("#"+
|
|
||||||
p).attr("data-"+a.mobile.ns+"url",p));if(h.length===0)if(a.mobile.firstPage&&j.isFirstPageUrl(s))a.mobile.firstPage.parent().length&&(h=a(a.mobile.firstPage));else if(j.isEmbeddedPage(s))return d.reject(m,c),d.promise();u&&u.reset();if(h.length){if(!f.reloadPage)return i(h,f.role),d.resolve(m,c,h),d.promise();g=h}var n=f.pageContainer,k=new a.Event("pagebeforeload"),q={url:b,absUrl:m,dataUrl:p,deferred:d,options:f};n.trigger(k,q);if(k.isDefaultPrevented())return d.promise();if(f.showLoadMsg)var v=
|
|
||||||
setTimeout(function(){a.mobile.showPageLoadingMsg()},f.loadMsgDelay);!a.mobile.allowCrossDomainPages&&!j.isSameDomain(x,m)?d.reject(m,c):a.ajax({url:s,type:f.type,data:f.data,dataType:"html",success:function(e,n,k){var o=a("<div></div>"),l=e.match(/<title[^>]*>([^<]*)/)&&RegExp.$1,t=RegExp("\\bdata-"+a.mobile.ns+"url=[\"']?([^\"'>]*)[\"']?");RegExp("(<[^>]+\\bdata-"+a.mobile.ns+"role=[\"']?page[\"']?[^>]*>)").test(e)&&RegExp.$1&&t.test(RegExp.$1)&&RegExp.$1&&(b=s=j.getFilePath(RegExp.$1));u&&u.set(s);
|
|
||||||
o.get(0).innerHTML=e;h=o.find(":jqmData(role='page'), :jqmData(role='dialog')").first();h.length||(h=a("<div data-"+a.mobile.ns+"role='page'>"+e.split(/<\/?body[^>]*>/gmi)[1]+"</div>"));l&&!h.jqmData("title")&&(~l.indexOf("&")&&(l=a("<div>"+l+"</div>").text()),h.jqmData("title",l));if(!a.support.dynamicBaseTag){var x=j.get(s);h.find("[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]").each(function(){var b=a(this).is("[href]")?"href":a(this).is("[src]")?"src":"action",c=a(this).attr(b),
|
|
||||||
c=c.replace(location.protocol+"//"+location.host+location.pathname,"");/^(\w+:|#|\/)/.test(c)||a(this).attr(b,x+c)})}h.attr("data-"+a.mobile.ns+"url",j.convertUrlToDataUrl(s)).attr("data-"+a.mobile.ns+"external-page",true).appendTo(f.pageContainer);h.one("pagecreate",a.mobile._bindPageRemove);i(h,f.role);m.indexOf("&"+a.mobile.subPageUrlKey)>-1&&(h=f.pageContainer.children(":jqmData(url='"+p+"')"));f.showLoadMsg&&(clearTimeout(v),a.mobile.hidePageLoadingMsg());q.xhr=k;q.textStatus=n;q.page=h;f.pageContainer.trigger("pageload",
|
|
||||||
q);d.resolve(m,c,h,g)},error:function(b,e,h){u&&u.set(j.get());q.xhr=b;q.textStatus=e;q.errorThrown=h;b=new a.Event("pageloadfailed");f.pageContainer.trigger(b,q);b.isDefaultPrevented()||(f.showLoadMsg&&(clearTimeout(v),a.mobile.hidePageLoadingMsg(),a("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+a.mobile.pageLoadErrorMessage+"</h1></div>").css({display:"block",opacity:0.96,top:o.scrollTop()+100}).appendTo(f.pageContainer).delay(800).fadeOut(400,function(){a(this).remove()})),
|
|
||||||
d.reject(m,c))}});return d.promise()};a.mobile.loadPage.defaults={type:"get",data:e,reloadPage:false,role:e,showLoadMsg:false,pageContainer:e,loadMsgDelay:50};a.mobile.changePage=function(b,h){if(z)A.unshift(arguments);else{var g=a.extend({},a.mobile.changePage.defaults,h);g.pageContainer=g.pageContainer||a.mobile.pageContainer;g.fromPage=g.fromPage||a.mobile.activePage;var p=g.pageContainer,k=new a.Event("pagebeforechange"),q={toPage:b,options:g};p.trigger(k,q);if(!k.isDefaultPrevented())if(b=q.toPage,
|
|
||||||
z=true,typeof b=="string")a.mobile.loadPage(b,g).done(function(b,c,d,f){z=false;c.duplicateCachedPage=f;a.mobile.changePage(d,c)}).fail(function(){z=false;d(true);f();g.pageContainer.trigger("pagechangefailed",q)});else{if(b[0]===a.mobile.firstPage[0]&&!g.dataUrl)g.dataUrl=x.hrefNoHash;var k=g.fromPage,l=g.dataUrl&&j.convertUrlToDataUrl(g.dataUrl)||b.jqmData("url"),v=l;j.getFilePath(l);var o=n.getActive(),t=n.activeIndex===0,w=0,u=document.title,y=g.role==="dialog"||b.jqmData("role")==="dialog";if(k&&
|
|
||||||
k[0]===b[0]&&!g.allowSamePageTransition)z=false,p.trigger("pagechange",q);else{i(b,g.role);g.fromHashChange&&n.directHashChange({currentUrl:l,isBack:function(){w=-1},isForward:function(){w=1}});try{document.activeElement&&document.activeElement.nodeName.toLowerCase()!="body"?a(document.activeElement).blur():a("input:focus, textarea:focus, select:focus").blur()}catch(B){}y&&o&&(l=(o.url||"")+s);if(g.changeHash!==false&&l)n.ignoreNextHashChange=true,j.set(l);var C=!o?u:b.jqmData("title")||b.children(":jqmData(role='header')").find(".ui-title").getEncodedText();
|
|
||||||
C&&u==document.title&&(u=C);b.jqmData("title")||b.jqmData("title",u);g.transition=g.transition||(w&&!t?o.transition:e)||(y?a.mobile.defaultDialogTransition:a.mobile.defaultPageTransition);w||n.addNew(l,g.transition,u,v,g.role);document.title=n.getActive().title;a.mobile.activePage=b;g.reverse=g.reverse||w<0;c(b,k,g.transition,g.reverse).done(function(){d();g.duplicateCachedPage&&g.duplicateCachedPage.remove();m.removeClass("ui-mobile-rendering");f();p.trigger("pagechange",q)})}}}};a.mobile.changePage.defaults=
|
|
||||||
{transition:e,reverse:false,changeHash:true,fromHashChange:false,role:e,duplicateCachedPage:e,pageContainer:e,showLoadMsg:true,dataUrl:e,fromPage:e,allowSamePageTransition:false};a.mobile._registerInternalEvents=function(){a("form").live("submit",function(b){var c=a(this);if(a.mobile.ajaxEnabled&&!c.is(":jqmData(ajax='false')")){var d=c.attr("method"),f=c.attr("target"),e=c.attr("action");if(!e&&(e=l(c),e===t.hrefNoHash))e=x.hrefNoSearch;e=j.makeUrlAbsolute(e,l(c));!j.isExternal(e)&&!f&&(a.mobile.changePage(e,
|
|
||||||
{type:d&&d.length&&d.toLowerCase()||"get",data:c.serialize(),transition:c.jqmData("transition"),direction:c.jqmData("direction"),reloadPage:true}),b.preventDefault())}});a(document).bind("vclick",function(b){if(!(b.which>1)&&a.mobile.linkBindingEnabled&&(b=k(b.target))&&j.parseUrl(b.getAttribute("href")||"#").hash!=="#")d(true),q=a(b).closest(".ui-btn").not(".ui-disabled"),q.addClass(a.mobile.activeBtnClass),a("."+a.mobile.activePageClass+" .ui-btn").not(b).blur()});a(document).bind("click",function(b){if(a.mobile.linkBindingEnabled){var c=
|
|
||||||
k(b.target);if(c&&!(b.which>1)){var f=a(c),h=function(){window.setTimeout(function(){d(true)},200)};if(f.is(":jqmData(rel='back')"))return window.history.back(),false;var g=l(f),c=j.makeUrlAbsolute(f.attr("href")||"#",g);if(!a.mobile.ajaxEnabled&&!j.isEmbeddedPage(c))h();else{if(c.search("#")!=-1)if(c=c.replace(/[^#]*#/,""))c=j.isPath(c)?j.makeUrlAbsolute(c,g):j.makeUrlAbsolute("#"+c,x.hrefNoHash);else{b.preventDefault();return}var g=f.is("[rel='external']")||f.is(":jqmData(ajax='false')")||f.is("[target]"),
|
|
||||||
i=a.mobile.allowCrossDomainPages&&x.protocol==="file:"&&c.search(/^https?:/)!=-1;g||j.isExternal(c)&&!i?h():(h=f.jqmData("transition"),g=(g=f.jqmData("direction"))&&g==="reverse"||f.jqmData("back"),f=f.attr("data-"+a.mobile.ns+"rel")||e,a.mobile.changePage(c,{transition:h,reverse:g,role:f}),b.preventDefault())}}}});a(".ui-page").live("pageshow.prefetch",function(){var b=[];a(this).find("a:jqmData(prefetch)").each(function(){var c=a(this),f=c.attr("href");f&&a.inArray(f,b)===-1&&(b.push(f),a.mobile.loadPage(f,
|
|
||||||
{role:c.attr("data-"+a.mobile.ns+"rel")}))})});a.mobile._handleHashChange=function(b){var c=j.stripHash(b),f={transition:a.mobile.urlHistory.stack.length===0?"none":e,changeHash:false,fromHashChange:true};if(!a.mobile.hashListeningEnabled||n.ignoreNextHashChange)n.ignoreNextHashChange=false;else{if(n.stack.length>1&&c.indexOf(s)>-1)if(a.mobile.activePage.is(".ui-dialog"))n.directHashChange({currentUrl:c,either:function(b){var d=a.mobile.urlHistory.getActive();c=d.pageUrl;a.extend(f,{role:d.role,transition:d.transition,
|
|
||||||
reverse:b})}});else{n.directHashChange({currentUrl:c,isBack:function(){window.history.back()},isForward:function(){window.history.forward()}});return}c?(c=typeof c==="string"&&!j.isPath(c)?j.makeUrlAbsolute("#"+c,t):c,a.mobile.changePage(c,f)):a.mobile.changePage(a.mobile.firstPage,f)}};o.bind("hashchange",function(){a.mobile._handleHashChange(location.hash)});a(document).bind("pageshow",g);a(window).bind("throttledresize",g)}})(jQuery);
|
|
||||||
(function(a,e){var b={},d=a(e),f=a.mobile.path.parseUrl(location.href);a.extend(b,{initialFilePath:f.pathname+f.search,initialHref:f.hrefNoHash,hashchangeFired:false,state:function(){return{hash:location.hash||"#"+b.initialFilePath,title:document.title,initialHref:b.initialHref}},resetUIKeys:function(b){var f="&"+a.mobile.subPageUrlKey,d=b.indexOf(a.mobile.dialogHashKey);d>-1?b=b.slice(0,d)+"#"+b.slice(d):b.indexOf(f)>-1&&(b=b.split(f).join("#"+f));return b},nextHashChangePrevented:function(c){a.mobile.urlHistory.ignoreNextHashChange=
|
|
||||||
c;b.onHashChangeDisabled=c},onHashChange:function(){if(!b.onHashChangeDisabled){var c,f;c=location.hash;var d=a.mobile.path.isPath(c),e=d?location.href:a.mobile.getDocumentUrl();c=d?c.replace("#",""):c;f=b.state();c=a.mobile.path.makeUrlAbsolute(c,e);d&&(c=b.resetUIKeys(c));history.replaceState(f,document.title,c)}},onPopState:function(c){var f=c.originalEvent.state;f&&(b.nextHashChangePrevented(true),setTimeout(function(){b.nextHashChangePrevented(false);a.mobile._handleHashChange(f.hash)},100))},
|
|
||||||
init:function(){d.bind("hashchange",b.onHashChange);d.bind("popstate",b.onPopState);location.hash===""&&history.replaceState(b.state(),document.title,location.href)}});a(function(){a.mobile.pushStateEnabled&&a.support.pushState&&b.init()})})(jQuery,this);
|
|
||||||
(function(a){function e(b,d,f,c){var e=new a.Deferred,g=d?" reverse":"",i="ui-mobile-viewport-transitioning viewport-"+b;f.animationComplete(function(){f.add(c).removeClass("out in reverse "+b);c&&c[0]!==f[0]&&c.removeClass(a.mobile.activePageClass);f.parent().removeClass(i);e.resolve(b,d,f,c)});f.parent().addClass(i);c&&c.addClass(b+" out"+g);f.addClass(a.mobile.activePageClass+" "+b+" in"+g);return e.promise()}a.mobile.css3TransitionHandler=e;if(a.mobile.defaultTransitionHandler===a.mobile.noneTransitionHandler)a.mobile.defaultTransitionHandler=
|
|
||||||
e})(jQuery,this);
|
|
||||||
(function(a){a.mobile.page.prototype.options.degradeInputs={color:false,date:false,datetime:false,"datetime-local":false,email:false,month:false,number:false,range:"number",search:"text",tel:false,time:false,url:false,week:false};a(document).bind("pagecreate create",function(e){var b=a(e.target).closest(':jqmData(role="page")').data("page"),d;if(b)d=b.options,a(e.target).find("input").not(b.keepNativeSelector()).each(function(){var b=a(this),c=this.getAttribute("type"),e=d.degradeInputs[c]||"text";
|
|
||||||
if(d.degradeInputs[c]){var g=a("<div>").html(b.clone()).html(),i=g.indexOf(" type=")>-1;b.replaceWith(g.replace(i?/\s+type=["']?\w+['"]?/:/\/?>/,' type="'+e+'" data-'+a.mobile.ns+'type="'+c+'"'+(i?"":">")))}})})})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.dialog",a.mobile.widget,{options:{closeBtnText:"Close",overlayTheme:"a",initSelector:":jqmData(role='dialog')"},_create:function(){var b=this,d=this.element,f=a("<a href='#' data-"+a.mobile.ns+"icon='delete' data-"+a.mobile.ns+"iconpos='notext'>"+this.options.closeBtnText+"</a>");d.addClass("ui-overlay-"+this.options.overlayTheme);d.attr("role","dialog").addClass("ui-dialog").find(":jqmData(role='header')").addClass("ui-corner-top ui-overlay-shadow").prepend(f).end().find(":jqmData(role='content'),:jqmData(role='footer')").addClass("ui-overlay-shadow").last().addClass("ui-corner-bottom");
|
|
||||||
f.bind("vclick",function(){b.close()});d.bind("vclick submit",function(b){var b=a(b.target).closest(b.type==="vclick"?"a":"form"),f;b.length&&!b.jqmData("transition")&&(f=a.mobile.urlHistory.getActive()||{},b.attr("data-"+a.mobile.ns+"transition",f.transition||a.mobile.defaultDialogTransition).attr("data-"+a.mobile.ns+"direction","reverse"))}).bind("pagehide",function(){a(this).find("."+a.mobile.activeBtnClass).removeClass(a.mobile.activeBtnClass)})},close:function(){e.history.back()}});a(a.mobile.dialog.prototype.options.initSelector).live("pagecreate",
|
|
||||||
function(){a(this).dialog()})})(jQuery,this);
|
|
||||||
(function(a){a.mobile.page.prototype.options.backBtnText="Back";a.mobile.page.prototype.options.addBackBtn=false;a.mobile.page.prototype.options.backBtnTheme=null;a.mobile.page.prototype.options.headerTheme="a";a.mobile.page.prototype.options.footerTheme="a";a.mobile.page.prototype.options.contentTheme=null;a(":jqmData(role='page'), :jqmData(role='dialog')").live("pagecreate",function(){var e=a(this),b=e.data("page").options,d=e.jqmData("role"),f=b.theme;a(":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')",
|
|
||||||
this).each(function(){var c=a(this),e=c.jqmData("role"),g=c.jqmData("theme"),i=g||b.contentTheme||d==="dialog"&&f,k;c.addClass("ui-"+e);if(e==="header"||e==="footer"){var l=g||(e==="header"?b.headerTheme:b.footerTheme)||f;c.addClass("ui-bar-"+l).attr("role",e==="header"?"banner":"contentinfo");g=c.children("a");i=g.hasClass("ui-btn-left");k=g.hasClass("ui-btn-right");i=i||g.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length;k||g.eq(1).addClass("ui-btn-right");b.addBackBtn&&e==="header"&&a(".ui-page").length>
|
|
||||||
1&&c.jqmData("url")!==a.mobile.path.stripHash(location.hash)&&!i&&a("<a href='#' class='ui-btn-left' data-"+a.mobile.ns+"rel='back' data-"+a.mobile.ns+"icon='arrow-l'>"+b.backBtnText+"</a>").attr("data-"+a.mobile.ns+"theme",b.backBtnTheme||l).prependTo(c);c.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({tabindex:"0",role:"heading","aria-level":"1"})}else e==="content"&&(i&&c.addClass("ui-body-"+i),c.attr("role","main"))})})})(jQuery);
|
|
||||||
(function(a){a.widget("mobile.collapsible",a.mobile.widget,{options:{expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsed:true,heading:"h1,h2,h3,h4,h5,h6,legend",theme:null,contentTheme:null,iconTheme:"d",initSelector:":jqmData(role='collapsible')"},_create:function(){var e=this.element,b=this.options,d=e.addClass("ui-collapsible"),f=e.children(b.heading).first(),c=d.wrapInner("<div class='ui-collapsible-content'></div>").find(".ui-collapsible-content"),
|
|
||||||
h=e.closest(":jqmData(role='collapsible-set')").addClass("ui-collapsible-set"),e=h.children(":jqmData(role='collapsible')");f.is("legend")&&(f=a("<div role='heading'>"+f.html()+"</div>").insertBefore(f),f.next().remove());if(h.length){if(!b.theme)b.theme=h.jqmData("theme");if(!b.contentTheme)b.contentTheme=h.jqmData("content-theme")}c.addClass(b.contentTheme?"ui-body-"+b.contentTheme:"");f.insertBefore(c).addClass("ui-collapsible-heading").append("<span class='ui-collapsible-heading-status'></span>").wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>").find("a").first().buttonMarkup({shadow:false,
|
|
||||||
corners:false,iconPos:"left",icon:"plus",theme:b.theme});h.length?(h.jqmData("collapsiblebound")||h.jqmData("collapsiblebound",true).bind("expand",function(b){a(b.target).closest(".ui-collapsible").siblings(".ui-collapsible").trigger("collapse")}),e.first().find("a").first().addClass("ui-corner-top").find(".ui-btn-inner").addClass("ui-corner-top"),e.last().jqmData("collapsible-last",true).find("a").first().addClass("ui-corner-bottom").find(".ui-btn-inner").addClass("ui-corner-bottom"),d.jqmData("collapsible-last")&&
|
|
||||||
f.find("a").first().add(f.find(".ui-btn-inner")).addClass("ui-corner-bottom")):f.find("a").first().add(f.find(".ui-btn-inner")).addClass("ui-corner-top ui-corner-bottom");d.bind("expand collapse",function(e){if(!e.isDefaultPrevented()){e.preventDefault();var i=a(this),e=e.type==="collapse",k=b.contentTheme;f.toggleClass("ui-collapsible-heading-collapsed",e).find(".ui-collapsible-heading-status").text(e?b.expandCueText:b.collapseCueText).end().find(".ui-icon").toggleClass("ui-icon-minus",!e).toggleClass("ui-icon-plus",
|
|
||||||
e);i.toggleClass("ui-collapsible-collapsed",e);c.toggleClass("ui-collapsible-content-collapsed",e).attr("aria-hidden",e);if(k&&(!h.length||d.jqmData("collapsible-last")))f.find("a").first().add(f.find(".ui-btn-inner")).toggleClass("ui-corner-bottom",e),c.toggleClass("ui-corner-bottom",!e);c.trigger("updatelayout")}}).trigger(b.collapsed?"collapse":"expand");f.bind("click",function(a){var b=f.is(".ui-collapsible-heading-collapsed")?"expand":"collapse";d.trigger(b);a.preventDefault()})}});a(document).bind("pagecreate create",
|
|
||||||
function(e){a(a.mobile.collapsible.prototype.options.initSelector,e.target).collapsible()})})(jQuery);(function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain ui-body ui-br")};a(document).bind("pagecreate create",function(e){a(":jqmData(role='fieldcontain')",e.target).fieldcontain()})})(jQuery);
|
|
||||||
(function(a){a.fn.grid=function(e){return this.each(function(){var b=a(this),d=a.extend({grid:null},e),f=b.children(),c={solo:1,a:2,b:3,c:4,d:5},d=d.grid;if(!d)if(f.length<=5)for(var h in c)c[h]===f.length&&(d=h);else d="a";c=c[d];b.addClass("ui-grid-"+d);f.filter(":nth-child("+c+"n+1)").addClass("ui-block-a");c>1&&f.filter(":nth-child("+c+"n+2)").addClass("ui-block-b");c>2&&f.filter(":nth-child(3n+3)").addClass("ui-block-c");c>3&&f.filter(":nth-child(4n+4)").addClass("ui-block-d");c>4&&f.filter(":nth-child(5n+5)").addClass("ui-block-e")})}})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.navbar",a.mobile.widget,{options:{iconpos:"top",grid:null,initSelector:":jqmData(role='navbar')"},_create:function(){var b=this.element,d=b.find("a"),f=d.filter(":jqmData(icon)").length?this.options.iconpos:e;b.addClass("ui-navbar").attr("role","navigation").find("ul").grid({grid:this.options.grid});f||b.addClass("ui-navbar-noicons");d.buttonMarkup({corners:false,shadow:false,iconpos:f});b.delegate("a","vclick",function(){d.not(".ui-state-persist").removeClass(a.mobile.activeBtnClass);
|
|
||||||
a(this).addClass(a.mobile.activeBtnClass)})}});a(document).bind("pagecreate create",function(b){a(a.mobile.navbar.prototype.options.initSelector,b.target).navbar()})})(jQuery);
|
|
||||||
(function(a){var e={};a.widget("mobile.listview",a.mobile.widget,{options:{theme:null,countTheme:"c",headerTheme:"b",dividerTheme:"b",splitIcon:"arrow-r",splitTheme:"b",inset:false,initSelector:":jqmData(role='listview')"},_create:function(){var a=this;a.element.addClass(function(d,f){return f+" ui-listview "+(a.options.inset?" ui-listview-inset ui-corner-all ui-shadow ":"")});a.refresh(true)},_removeCorners:function(a,d){a=a.add(a.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb"));d==="top"?a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl"):
|
|
||||||
d==="bottom"?a.removeClass("ui-corner-bottom ui-corner-br ui-corner-bl"):a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl ui-corner-bottom ui-corner-br ui-corner-bl")},_refreshCorners:function(a){var d,f;this.options.inset&&(d=this.element.children("li"),f=a?d.not(".ui-screen-hidden"):d.filter(":visible"),this._removeCorners(d),d=f.first().addClass("ui-corner-top"),d.add(d.find(".ui-btn-inner").not(".ui-li-link-alt span:first-child")).addClass("ui-corner-top").end().find(".ui-li-link-alt, .ui-li-link-alt span:first-child").addClass("ui-corner-tr").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-tl"),
|
|
||||||
f=f.last().addClass("ui-corner-bottom"),f.add(f.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-br").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-bl"));a||this.element.trigger("updatelayout")},_findFirstElementByTagName:function(a,d,f,c){var e={};for(e[f]=e[c]=true;a;){if(e[a.nodeName])return a;a=a[d]}return null},_getChildrenByTagName:function(b,d,f){var c=[],e={};e[d]=e[f]=true;for(b=b.firstChild;b;)e[b.nodeName]&&c.push(b),b=b.nextSibling;return a(c)},_addThumbClasses:function(b){var d,
|
|
||||||
f,c=b.length;for(d=0;d<c;d++)f=a(this._findFirstElementByTagName(b[d].firstChild,"nextSibling","img","IMG")),f.length&&(f.addClass("ui-li-thumb"),a(this._findFirstElementByTagName(f[0].parentNode,"parentNode","li","LI")).addClass(f.is(".ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb"))},refresh:function(b){this.parentPage=this.element.closest(".ui-page");this._createSubPages();var d=this.options,f=this.element,c=f.jqmData("dividertheme")||d.dividerTheme,e=f.jqmData("splittheme"),g=f.jqmData("spliticon"),
|
|
||||||
i=this._getChildrenByTagName(f[0],"li","LI"),k=a.support.cssPseudoElement||!a.nodeName(f[0],"ol")?0:1,l={},o,m,p,j,q;k&&f.find(".ui-li-dec").remove();if(!d.theme)d.theme=a.mobile.getInheritedTheme(this.element,"c");for(var n=0,A=i.length;n<A;n++){o=i.eq(n);m="ui-li";if(b||!o.hasClass("ui-li"))p=o.jqmData("theme")||d.theme,j=this._getChildrenByTagName(o[0],"a","A"),j.length?(q=o.jqmData("icon"),o.buttonMarkup({wrapperEls:"div",shadow:false,corners:false,iconpos:"right",icon:j.length>1||q===false?false:
|
|
||||||
q||"arrow-r",theme:p}),q!=false&&j.length==1&&o.addClass("ui-li-has-arrow"),j.first().addClass("ui-link-inherit"),j.length>1&&(m+=" ui-li-has-alt",j=j.last(),q=e||j.jqmData("theme")||d.splitTheme,j.appendTo(o).attr("title",j.getEncodedText()).addClass("ui-li-link-alt").empty().buttonMarkup({shadow:false,corners:false,theme:p,icon:false,iconpos:false}).find(".ui-btn-inner").append(a(document.createElement("span")).buttonMarkup({shadow:true,corners:true,theme:q,iconpos:"notext",icon:g||j.jqmData("icon")||
|
|
||||||
d.splitIcon})))):o.jqmData("role")==="list-divider"?(m+=" ui-li-divider ui-btn ui-bar-"+c,o.attr("role","heading"),k&&(k=1)):m+=" ui-li-static ui-body-"+p;k&&m.indexOf("ui-li-divider")<0&&(p=o.is(".ui-li-static:first")?o:o.find(".ui-link-inherit"),p.addClass("ui-li-jsnumbering").prepend("<span class='ui-li-dec'>"+k++ +". </span>"));l[m]||(l[m]=[]);l[m].push(o[0])}for(m in l)a(l[m]).addClass(m).children(".ui-btn-inner").addClass(m);f.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading").end().find("p, dl").addClass("ui-li-desc").end().find(".ui-li-aside").each(function(){var b=
|
|
||||||
a(this);b.prependTo(b.parent())}).end().find(".ui-li-count").each(function(){a(this).closest("li").addClass("ui-li-has-count")}).addClass("ui-btn-up-"+(f.jqmData("counttheme")||this.options.countTheme)+" ui-btn-corner-all");this._addThumbClasses(i);this._addThumbClasses(f.find(".ui-link-inherit"));this._refreshCorners(b)},_idStringEscape:function(a){return a.replace(/[^a-zA-Z0-9]/g,"-")},_createSubPages:function(){var b=this.element,d=b.closest(".ui-page"),f=d.jqmData("url"),c=f||d[0][a.expando],
|
|
||||||
h=b.attr("id"),g=this.options,i="data-"+a.mobile.ns,k=this,l=d.find(":jqmData(role='footer')").jqmData("id"),o;typeof e[c]==="undefined"&&(e[c]=-1);h=h||++e[c];a(b.find("li>ul, li>ol").toArray().reverse()).each(function(c){var d=a(this),e=d.attr("id")||h+"-"+c,c=d.parent(),k=a(d.prevAll().toArray().reverse()),k=k.length?k:a("<span>"+a.trim(c.contents()[0].nodeValue)+"</span>"),n=k.first().getEncodedText(),e=(f||"")+"&"+a.mobile.subPageUrlKey+"="+e,A=d.jqmData("theme")||g.theme,z=d.jqmData("counttheme")||
|
|
||||||
b.jqmData("counttheme")||g.countTheme;o=true;d.detach().wrap("<div "+i+"role='page' "+i+"url='"+e+"' "+i+"theme='"+A+"' "+i+"count-theme='"+z+"'><div "+i+"role='content'></div></div>").parent().before("<div "+i+"role='header' "+i+"theme='"+g.headerTheme+"'><div class='ui-title'>"+n+"</div></div>").after(l?a("<div "+i+"role='footer' "+i+"id='"+l+"'>"):"").parent().appendTo(a.mobile.pageContainer).page();d=c.find("a:first");d.length||(d=a("<a/>").html(k||n).prependTo(c.empty()));d.attr("href","#"+e)}).listview();
|
|
||||||
o&&d.is(":jqmData(external-page='true')")&&d.data("page").options.domCache===false&&d.unbind("pagehide.remove").bind("pagehide.remove",function(b,c){var e=c.nextPage;c.nextPage&&(e=e.jqmData("url"),e.indexOf(f+"&"+a.mobile.subPageUrlKey)!==0&&(k.childPages().remove(),d.remove()))})},childPages:function(){var b=this.parentPage.jqmData("url");return a(":jqmData(url^='"+b+"&"+a.mobile.subPageUrlKey+"')")}});a(document).bind("pagecreate create",function(b){a(a.mobile.listview.prototype.options.initSelector,
|
|
||||||
b.target).listview()})})(jQuery);
|
|
||||||
(function(a){a.mobile.listview.prototype.options.filter=false;a.mobile.listview.prototype.options.filterPlaceholder="Filter items...";a.mobile.listview.prototype.options.filterTheme="c";a.mobile.listview.prototype.options.filterCallback=function(a,b){return a.toLowerCase().indexOf(b)===-1};a(":jqmData(role='listview')").live("listviewcreate",function(){var e=a(this),b=e.data("listview");if(b.options.filter){var d=a("<form>",{"class":"ui-listview-filter ui-bar-"+b.options.filterTheme,role:"search"});
|
|
||||||
a("<input>",{placeholder:b.options.filterPlaceholder}).attr("data-"+a.mobile.ns+"type","search").jqmData("lastval","").bind("keyup change",function(){var d=a(this),c=this.value.toLowerCase(),h=null,h=d.jqmData("lastval")+"",g=false,i="";d.jqmData("lastval",c);i=c.substr(0,h.length-1).replace(h,"");h=c.length<h.length||i.length!=c.length-h.length?e.children():e.children(":not(.ui-screen-hidden)");if(c){for(var k=h.length-1;k>=0;k--)d=a(h[k]),i=d.jqmData("filtertext")||d.text(),d.is("li:jqmData(role=list-divider)")?
|
|
||||||
(d.toggleClass("ui-filter-hidequeue",!g),g=false):b.options.filterCallback(i,c)?d.toggleClass("ui-filter-hidequeue",true):g=true;h.filter(":not(.ui-filter-hidequeue)").toggleClass("ui-screen-hidden",false);h.filter(".ui-filter-hidequeue").toggleClass("ui-screen-hidden",true).toggleClass("ui-filter-hidequeue",false)}else h.toggleClass("ui-screen-hidden",false);b._refreshCorners()}).appendTo(d).textinput();a(this).jqmData("inset")&&d.addClass("ui-listview-filter-inset");d.bind("submit",function(){return false}).insertBefore(e)}})})(jQuery);
|
|
||||||
(function(a){a(document).bind("pagecreate create",function(e){a(":jqmData(role='nojs')",e.target).addClass("ui-nojs")})})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.checkboxradio",a.mobile.widget,{options:{theme:null,initSelector:"input[type='checkbox'],input[type='radio']"},_create:function(){var b=this,d=this.element,f=d.closest("form,fieldset,:jqmData(role='page')").find("label[for='"+d[0].id+"']"),c=d.attr("type"),h=c+"-on",g=c+"-off",i=d.parents(":jqmData(type='horizontal')").length?e:g;if(!(c!=="checkbox"&&c!=="radio")){a.extend(this,{label:f,inputtype:c,checkedClass:"ui-"+h+(i?"":" "+a.mobile.activeBtnClass),uncheckedClass:"ui-"+
|
|
||||||
g,checkedicon:"ui-icon-"+h,uncheckedicon:"ui-icon-"+g});if(!this.options.theme)this.options.theme=this.element.jqmData("theme");f.buttonMarkup({theme:this.options.theme,icon:i,shadow:false});d.add(f).wrapAll("<div class='ui-"+c+"'></div>");f.bind({vmouseover:function(b){a(this).parent().is(".ui-disabled")&&b.stopPropagation()},vclick:function(a){if(d.is(":disabled"))a.preventDefault();else return b._cacheVals(),d.prop("checked",c==="radio"&&true||!d.prop("checked")),d.triggerHandler("click"),b._getInputSet().not(d).prop("checked",
|
|
||||||
false),b._updateAll(),false}});d.bind({vmousedown:function(){b._cacheVals()},vclick:function(){var c=a(this);c.is(":checked")?(c.prop("checked",true),b._getInputSet().not(c).prop("checked",false)):c.prop("checked",false);b._updateAll()},focus:function(){f.addClass("ui-focus")},blur:function(){f.removeClass("ui-focus")}});this.refresh()}},_cacheVals:function(){this._getInputSet().each(function(){var b=a(this);b.jqmData("cacheVal",b.is(":checked"))})},_getInputSet:function(){return this.inputtype==
|
|
||||||
"checkbox"?this.element:this.element.closest("form,fieldset,:jqmData(role='page')").find("input[name='"+this.element.attr("name")+"'][type='"+this.inputtype+"']")},_updateAll:function(){var b=this;this._getInputSet().each(function(){var d=a(this);(d.is(":checked")||b.inputtype==="checkbox")&&d.trigger("change")}).checkboxradio("refresh")},refresh:function(){var b=this.element,d=this.label,f=d.find(".ui-icon");a(b[0]).prop("checked")?(d.addClass(this.checkedClass).removeClass(this.uncheckedClass),
|
|
||||||
f.addClass(this.checkedicon).removeClass(this.uncheckedicon)):(d.removeClass(this.checkedClass).addClass(this.uncheckedClass),f.removeClass(this.checkedicon).addClass(this.uncheckedicon));b.is(":disabled")?this.disable():this.enable()},disable:function(){this.element.prop("disabled",true).parent().addClass("ui-disabled")},enable:function(){this.element.prop("disabled",false).parent().removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(b){a.mobile.checkboxradio.prototype.enhanceWithin(b.target)})})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.button",a.mobile.widget,{options:{theme:null,icon:null,iconpos:null,inline:null,corners:true,shadow:true,iconshadow:true,initSelector:"button, [type='button'], [type='submit'], [type='reset'], [type='image']"},_create:function(){var b=this.element,d=this.options,f,c;this.button=a("<div></div>").text(b.text()||b.val()).insertBefore(b).buttonMarkup({theme:d.theme,icon:d.icon,iconpos:d.iconpos,inline:d.inline,corners:d.corners,shadow:d.shadow,iconshadow:d.iconshadow}).append(b.addClass("ui-btn-hidden"));
|
|
||||||
d=b.attr("type");f=b.attr("name");d!=="button"&&d!=="reset"&&f&&b.bind("vclick",function(){c===e&&(c=a("<input>",{type:"hidden",name:b.attr("name"),value:b.attr("value")}).insertBefore(b),a(document).one("submit",function(){c.remove();c=e}))});this.refresh()},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",
|
|
||||||
true);return this._setOption("disabled",true)},refresh:function(){var a=this.element;a.prop("disabled")?this.disable():this.enable();this.button.data("textWrapper").text(a.text()||a.val())}});a(document).bind("pagecreate create",function(b){a.mobile.button.prototype.enhanceWithin(b.target)})})(jQuery);
|
|
||||||
(function(a,e){a.widget("mobile.slider",a.mobile.widget,{options:{theme:null,trackTheme:null,disabled:false,initSelector:"input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"},_create:function(){var b=this,d=this.element,f=a.mobile.getInheritedTheme(d,"c"),c=this.options.theme||f,h=this.options.trackTheme||f,g=d[0].nodeName.toLowerCase(),f=g=="select"?"ui-slider-switch":"",i=d.attr("id"),k=i+"-label",i=a("[for='"+i+"']").attr("id",k),l=function(){return g=="input"?parseFloat(d.val()):
|
|
||||||
d[0].selectedIndex},o=g=="input"?parseFloat(d.attr("min")):0,m=g=="input"?parseFloat(d.attr("max")):d.find("option").length-1,p=window.parseFloat(d.attr("step")||1),j=a("<div class='ui-slider "+f+" ui-btn-down-"+h+" ui-btn-corner-all' role='application'></div>"),q=a("<a href='#' class='ui-slider-handle'></a>").appendTo(j).buttonMarkup({corners:true,theme:c,shadow:true}).attr({role:"slider","aria-valuemin":o,"aria-valuemax":m,"aria-valuenow":l(),"aria-valuetext":l(),title:l(),"aria-labelledby":k});
|
|
||||||
a.extend(this,{slider:j,handle:q,dragging:false,beforeStart:null,userModified:false,mouseMoved:false});g=="select"&&(j.wrapInner("<div class='ui-slider-inneroffset'></div>"),q.addClass("ui-slider-handle-snapping"),d.find("option"),d.find("option").each(function(b){var c=!b?"b":"a",d=!b?"right":"left",b=!b?" ui-btn-down-"+h:" "+a.mobile.activeBtnClass;a("<div class='ui-slider-labelbg ui-slider-labelbg-"+c+b+" ui-btn-corner-"+d+"'></div>").prependTo(j);a("<span class='ui-slider-label ui-slider-label-"+
|
|
||||||
c+b+" ui-btn-corner-"+d+"' role='img'>"+a(this).getEncodedText()+"</span>").prependTo(q)}));i.addClass("ui-slider");d.addClass(g==="input"?"ui-slider-input":"ui-slider-switch").change(function(){b.mouseMoved||b.refresh(l(),true)}).keyup(function(){b.refresh(l(),true,true)}).blur(function(){b.refresh(l(),true)});a(document).bind("vmousemove",function(a){if(b.dragging)return b.mouseMoved=true,g==="select"&&q.removeClass("ui-slider-handle-snapping"),b.refresh(a),b.userModified=b.beforeStart!==d[0].selectedIndex,
|
|
||||||
false});j.bind("vmousedown",function(a){b.dragging=true;b.userModified=false;b.mouseMoved=false;if(g==="select")b.beforeStart=d[0].selectedIndex;b.refresh(a);return false});j.add(document).bind("vmouseup",function(){if(b.dragging)return b.dragging=false,g==="select"&&(q.addClass("ui-slider-handle-snapping"),b.mouseMoved?b.userModified?b.refresh(b.beforeStart==0?1:0):b.refresh(b.beforeStart):b.refresh(b.beforeStart==0?1:0)),b.mouseMoved=false});j.insertAfter(d);this.handle.bind("vmousedown",function(){a(this).focus()}).bind("vclick",
|
|
||||||
false);this.handle.bind("keydown",function(c){var d=l();if(!b.options.disabled){switch(c.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:if(c.preventDefault(),!b._keySliding)b._keySliding=true,a(this).addClass("ui-state-active")}switch(c.keyCode){case a.mobile.keyCode.HOME:b.refresh(o);break;case a.mobile.keyCode.END:b.refresh(m);
|
|
||||||
break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:b.refresh(d+p);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:b.refresh(d-p)}}}).keyup(function(){if(b._keySliding)b._keySliding=false,a(this).removeClass("ui-state-active")});this.refresh(e,e,true)},refresh:function(a,d,f){(this.options.disabled||this.element.attr("disabled"))&&this.disable();var c=this.element,e,g=c[0].nodeName.toLowerCase(),i=g==="input"?parseFloat(c.attr("min")):
|
|
||||||
0,k=g==="input"?parseFloat(c.attr("max")):c.find("option").length-1;if(typeof a==="object"){if(!this.dragging||a.pageX<this.slider.offset().left-8||a.pageX>this.slider.offset().left+this.slider.width()+8)return;e=Math.round((a.pageX-this.slider.offset().left)/this.slider.width()*100)}else a==null&&(a=g==="input"?parseFloat(c.val()):c[0].selectedIndex),e=(parseFloat(a)-i)/(k-i)*100;if(!isNaN(e)&&(e<0&&(e=0),e>100&&(e=100),a=Math.round(e/100*(k-i))+i,a<i&&(a=i),a>k&&(a=k),this.handle.css("left",e+"%"),
|
|
||||||
this.handle.attr({"aria-valuenow":g==="input"?a:c.find("option").eq(a).attr("value"),"aria-valuetext":g==="input"?a:c.find("option").eq(a).getEncodedText(),title:a}),g==="select"&&(a===0?this.slider.addClass("ui-slider-switch-a").removeClass("ui-slider-switch-b"):this.slider.addClass("ui-slider-switch-b").removeClass("ui-slider-switch-a")),!f))f=false,g==="input"?(f=c.val()!==a,c.val(a)):(f=c[0].selectedIndex!==a,c[0].selectedIndex=a),!d&&f&&c.trigger("change")},enable:function(){this.element.attr("disabled",
|
|
||||||
false);this.slider.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.slider.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)}});a(document).bind("pagecreate create",function(b){a.mobile.slider.prototype.enhanceWithin(b.target)})})(jQuery);
|
|
||||||
(function(a){a.widget("mobile.textinput",a.mobile.widget,{options:{theme:null,initSelector:"input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])"},_create:function(){var e=this.element,
|
|
||||||
b=this.options.theme||a.mobile.getInheritedTheme(this.element,"c"),d=" ui-body-"+b,f,c;a("label[for='"+e.attr("id")+"']").addClass("ui-input-text");f=e.addClass("ui-input-text ui-body-"+b);typeof e[0].autocorrect!=="undefined"&&!a.support.touchOverflow&&(e[0].setAttribute("autocorrect","off"),e[0].setAttribute("autocomplete","off"));e.is("[type='search'],:jqmData(type='search')")?(f=e.wrap("<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield"+d+"'></div>").parent(),
|
|
||||||
c=a("<a href='#' class='ui-input-clear' title='clear text'>clear text</a>").tap(function(a){e.val("").focus();e.trigger("change");c.addClass("ui-input-clear-hidden");a.preventDefault()}).appendTo(f).buttonMarkup({icon:"delete",iconpos:"notext",corners:true,shadow:true}),b=function(){setTimeout(function(){c.toggleClass("ui-input-clear-hidden",!e.val())},0)},b(),e.bind("paste cut keyup focus change blur",b)):e.addClass("ui-corner-all ui-shadow-inset"+d);e.focus(function(){f.addClass("ui-focus")}).blur(function(){f.removeClass("ui-focus")});
|
|
||||||
if(e.is("textarea")){var h=function(){var a=e[0].scrollHeight;e[0].clientHeight<a&&e.height(a+15)},g;e.keyup(function(){clearTimeout(g);g=setTimeout(h,100)});a.trim(e.val())&&(a(window).load(h),a(document).one("pagechange",h))}},disable:function(){(this.element.attr("disabled",true).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).addClass("ui-disabled")},enable:function(){(this.element.attr("disabled",false).is("[type='search'],:jqmData(type='search')")?this.element.parent():
|
|
||||||
this.element).removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(e){a.mobile.textinput.prototype.enhanceWithin(e.target)})})(jQuery);
|
|
||||||
(function(a){var e=function(b){var d=b.selectID,f=b.label,c=b.select.closest(".ui-page"),e=a("<div>",{"class":"ui-selectmenu-screen ui-screen-hidden"}).appendTo(c),g=b._selectOptions(),i=b.isMultiple=b.select[0].multiple,k=d+"-button",l=d+"-menu",o=a("<div data-"+a.mobile.ns+"role='dialog' data-"+a.mobile.ns+"theme='"+b.options.theme+"' data-"+a.mobile.ns+"overlay-theme='"+b.options.overlayTheme+"'><div data-"+a.mobile.ns+"role='header'><div class='ui-title'>"+f.getEncodedText()+"</div></div><div data-"+
|
|
||||||
a.mobile.ns+"role='content'></div></div>").appendTo(a.mobile.pageContainer).page(),m=a("<div>",{"class":"ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-"+b.options.overlayTheme+" "+a.mobile.defaultDialogTransition}).insertAfter(e),p=a("<ul>",{"class":"ui-selectmenu-list",id:l,role:"listbox","aria-labelledby":k}).attr("data-"+a.mobile.ns+"theme",b.options.theme).appendTo(m),j=a("<div>",{"class":"ui-header ui-bar-"+b.options.theme}).prependTo(m),q=a("<h1>",{"class":"ui-title"}).appendTo(j),
|
|
||||||
n=a("<a>",{text:b.options.closeText,href:"#","class":"ui-btn-left"}).attr("data-"+a.mobile.ns+"iconpos","notext").attr("data-"+a.mobile.ns+"icon","delete").appendTo(j).buttonMarkup(),A=o.find(".ui-content"),z=o.find(".ui-header a");a.extend(b,{select:b.select,selectID:d,buttonId:k,menuId:l,thisPage:c,menuPage:o,label:f,screen:e,selectOptions:g,isMultiple:i,theme:b.options.theme,listbox:m,list:p,header:j,headerTitle:q,headerClose:n,menuPageContent:A,menuPageClose:z,placeholder:"",build:function(){var b=
|
|
||||||
this;b.refresh();b.select.attr("tabindex","-1").focus(function(){a(this).blur();b.button.focus()});b.button.bind("vclick keydown",function(c){if(c.type=="vclick"||c.keyCode&&(c.keyCode===a.mobile.keyCode.ENTER||c.keyCode===a.mobile.keyCode.SPACE))b.open(),c.preventDefault()});b.list.attr("role","listbox").delegate(".ui-li>a","focusin",function(){a(this).attr("tabindex","0")}).delegate(".ui-li>a","focusout",function(){a(this).attr("tabindex","-1")}).delegate("li:not(.ui-disabled, .ui-li-divider)",
|
|
||||||
"click",function(c){var d=b.select[0].selectedIndex,f=b.list.find("li:not(.ui-li-divider)").index(this),e=b._selectOptions().eq(f)[0];e.selected=b.isMultiple?!e.selected:true;b.isMultiple&&a(this).find(".ui-icon").toggleClass("ui-icon-checkbox-on",e.selected).toggleClass("ui-icon-checkbox-off",!e.selected);(b.isMultiple||d!==f)&&b.select.trigger("change");b.isMultiple||b.close();c.preventDefault()}).keydown(function(b){var c=a(b.target),d=c.closest("li");switch(b.keyCode){case 38:return b=d.prev(),
|
|
||||||
b.length&&(c.blur().attr("tabindex","-1"),b.find("a").first().focus()),false;case 40:return b=d.next(),b.length&&(c.blur().attr("tabindex","-1"),b.find("a").first().focus()),false;case 13:case 32:return c.trigger("click"),false}});b.menuPage.bind("pagehide",function(){b.list.appendTo(b.listbox);b._focusButton();a.mobile._bindPageRemove.call(b.thisPage)});b.screen.bind("vclick",function(){b.close()});b.headerClose.click(function(){if(b.menuType=="overlay")return b.close(),false});b.thisPage.addDependents(this.menuPage)},
|
|
||||||
_isRebuildRequired:function(){var a=this.list.find("li");return this._selectOptions().text()!==a.text()},refresh:function(b){var c=this;this._selectOptions();this.selected();var d=this.selectedIndices();(b||this._isRebuildRequired())&&c._buildList();c.setButtonText();c.setButtonCount();c.list.find("li:not(.ui-li-divider)").removeClass(a.mobile.activeBtnClass).attr("aria-selected",false).each(function(b){a.inArray(b,d)>-1&&(b=a(this),b.attr("aria-selected",true),c.isMultiple?b.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on"):
|
|
||||||
b.addClass(a.mobile.activeBtnClass))})},close:function(){if(!this.options.disabled&&this.isOpen)this.menuType=="page"?window.history.back():(this.screen.addClass("ui-screen-hidden"),this.listbox.addClass("ui-selectmenu-hidden").removeAttr("style").removeClass("in"),this.list.appendTo(this.listbox),this._focusButton()),this.isOpen=false},open:function(){if(!this.options.disabled){var b=this,c=b.list.parent().outerHeight(),d=b.list.parent().outerWidth(),f=a(".ui-page-active"),e=a.support.touchOverflow&&
|
|
||||||
a.mobile.touchOverflowEnabled,f=f.is(".ui-native-fixed")?f.find(".ui-content"):f;scrollTop=e?f.scrollTop():a(window).scrollTop();btnOffset=b.button.offset().top;screenHeight=window.innerHeight;screenWidth=window.innerWidth;b.button.addClass(a.mobile.activeBtnClass);setTimeout(function(){b.button.removeClass(a.mobile.activeBtnClass)},300);if(c>screenHeight-80||!a.support.scrollTop){b.thisPage.unbind("pagehide.remove");if(scrollTop==0&&btnOffset>screenHeight)b.thisPage.one("pagehide",function(){a(this).jqmData("lastScroll",
|
|
||||||
btnOffset)});b.menuPage.one("pageshow",function(){a(window).one("silentscroll",function(){b.list.find(a.mobile.activeBtnClass).focus()});b.isOpen=true});b.menuType="page";b.menuPageContent.append(b.list);b.menuPage.find("div .ui-title").text(b.label.text());a.mobile.changePage(b.menuPage,{transition:a.mobile.defaultDialogTransition})}else{b.menuType="overlay";b.screen.height(a(document).height()).removeClass("ui-screen-hidden");var f=btnOffset-scrollTop,h=scrollTop+screenHeight-btnOffset,g=c/2,e=
|
|
||||||
parseFloat(b.list.parent().css("max-width")),c=f>c/2&&h>c/2?btnOffset+b.button.outerHeight()/2-g:f>h?scrollTop+screenHeight-c-30:scrollTop+30;d<e?e=(screenWidth-d)/2:(e=b.button.offset().left+b.button.outerWidth()/2-d/2,e<30?e=30:e+d>screenWidth&&(e=screenWidth-d-30));b.listbox.append(b.list).removeClass("ui-selectmenu-hidden").css({top:c,left:e}).addClass("in");b.list.find(a.mobile.activeBtnClass).focus();b.isOpen=true}}},_buildList:function(){var b=this,c=this.options,d=this.placeholder,f=[],e=
|
|
||||||
[],h=b.isMultiple?"checkbox-off":"false";b.list.empty().filter(".ui-listview").listview("destroy");b.select.find("option").each(function(g){var j=a(this),i=j.parent(),m=j.getEncodedText(),p="<a href='#'>"+m+"</a>",k=[],n=[];i.is("optgroup")&&(i=i.attr("label"),a.inArray(i,f)===-1&&(e.push("<li data-"+a.mobile.ns+"role='list-divider'>"+i+"</li>"),f.push(i)));if(!this.getAttribute("value")||m.length==0||j.jqmData("placeholder"))c.hidePlaceholderMenuItems&&k.push("ui-selectmenu-placeholder"),d=b.placeholder=
|
|
||||||
m;this.disabled&&(k.push("ui-disabled"),n.push("aria-disabled='true'"));e.push("<li data-"+a.mobile.ns+"option-index='"+g+"' data-"+a.mobile.ns+"icon='"+h+"' class='"+k.join(" ")+"' "+n.join(" ")+">"+p+"</li>")});b.list.html(e.join(" "));b.list.find("li").attr({role:"option",tabindex:"-1"}).first().attr("tabindex","0");this.isMultiple||this.headerClose.hide();!this.isMultiple&&!d.length?this.header.hide():this.headerTitle.text(this.placeholder);b.list.listview()},_button:function(){return a("<a>",
|
|
||||||
{href:"#",role:"button",id:this.buttonId,"aria-haspopup":"true","aria-owns":this.menuId})}})};a("select").live("selectmenubeforecreate",function(){var b=a(this).data("selectmenu");b.options.nativeMenu||e(b)})})(jQuery);
|
|
||||||
(function(a){a.widget("mobile.selectmenu",a.mobile.widget,{options:{theme:null,disabled:false,icon:"arrow-d",iconpos:"right",inline:null,corners:true,shadow:true,iconshadow:true,menuPageTheme:"b",overlayTheme:"a",hidePlaceholderMenuItems:true,closeText:"Close",nativeMenu:true,initSelector:"select:not(:jqmData(role='slider'))"},_button:function(){return a("<div/>")},_setDisabled:function(a){this.element.attr("disabled",a);this.button.attr("aria-disabled",a);return this._setOption("disabled",a)},_focusButton:function(){var a=
|
|
||||||
this;setTimeout(function(){a.button.focus()},40)},_selectOptions:function(){return this.select.find("option")},_preExtension:function(){this.select=this.element.wrap("<div class='ui-select'>");this.selectID=this.select.attr("id");this.label=a("label[for='"+this.selectID+"']").addClass("ui-select");this.isMultiple=this.select[0].multiple;if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.select,"c")},_create:function(){this._preExtension();this._trigger("beforeCreate");this.button=
|
|
||||||
this._button();var e=this,b=this.options,d=this.button.text(a(this.select[0].options.item(this.select[0].selectedIndex==-1?0:this.select[0].selectedIndex)).text()).insertBefore(this.select).buttonMarkup({theme:b.theme,icon:b.icon,iconpos:b.iconpos,inline:b.inline,corners:b.corners,shadow:b.shadow,iconshadow:b.iconshadow});b.nativeMenu&&window.opera&&window.opera.version&&this.select.addClass("ui-select-nativeonly");if(this.isMultiple)this.buttonCount=a("<span>").addClass("ui-li-count ui-btn-up-c ui-btn-corner-all").hide().appendTo(d.addClass("ui-li-has-count"));
|
|
||||||
(b.disabled||this.element.attr("disabled"))&&this.disable();this.select.change(function(){e.refresh()});this.build()},build:function(){var e=this;this.select.appendTo(e.button).bind("vmousedown",function(){e.button.addClass(a.mobile.activeBtnClass)}).bind("focus vmouseover",function(){e.button.trigger("vmouseover")}).bind("vmousemove",function(){e.button.removeClass(a.mobile.activeBtnClass)}).bind("change blur vmouseout",function(){e.button.trigger("vmouseout").removeClass(a.mobile.activeBtnClass)}).bind("change blur",
|
|
||||||
function(){e.button.removeClass("ui-btn-down-"+e.options.theme)})},selected:function(){return this._selectOptions().filter(":selected")},selectedIndices:function(){var a=this;return this.selected().map(function(){return a._selectOptions().index(this)}).get()},setButtonText:function(){var e=this,b=this.selected();this.button.find(".ui-btn-text").text(function(){return!e.isMultiple?b.text():b.length?b.map(function(){return a(this).text()}).get().join(", "):e.placeholder})},setButtonCount:function(){var a=
|
|
||||||
this.selected();this.isMultiple&&this.buttonCount[a.length>1?"show":"hide"]().text(a.length)},refresh:function(){this.setButtonText();this.setButtonCount()},open:a.noop,close:a.noop,disable:function(){this._setDisabled(true);this.button.addClass("ui-disabled")},enable:function(){this._setDisabled(false);this.button.removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(e){a.mobile.selectmenu.prototype.enhanceWithin(e.target)})})(jQuery);
|
|
||||||
(function(a,e){function b(b){for(var c;b;){if((c=typeof b.className==="string"&&b.className.split(" "))&&a.inArray("ui-btn",c)>-1&&a.inArray("ui-disabled",c)<0)break;b=b.parentNode}return b}a.fn.buttonMarkup=function(b){for(var b=b||{},c=0;c<this.length;c++){var h=this.eq(c),g=h[0],i=a.extend({},a.fn.buttonMarkup.defaults,{icon:b.icon!==e?b.icon:h.jqmData("icon"),iconpos:b.iconpos!==e?b.iconpos:h.jqmData("iconpos"),theme:b.theme!==e?b.theme:h.jqmData("theme"),inline:b.inline!==e?b.inline:h.jqmData("inline"),
|
|
||||||
shadow:b.shadow!==e?b.shadow:h.jqmData("shadow"),corners:b.corners!==e?b.corners:h.jqmData("corners"),iconshadow:b.iconshadow!==e?b.iconshadow:h.jqmData("iconshadow")},b),k="ui-btn-inner",l,o,m=document.createElement(i.wrapperEls),p=document.createElement(i.wrapperEls),j=i.icon?document.createElement("span"):null;d&&d();if(!i.theme)i.theme=a.mobile.getInheritedTheme(h,"c");l="ui-btn ui-btn-up-"+i.theme;i.inline&&(l+=" ui-btn-inline");if(i.icon)i.icon="ui-icon-"+i.icon,i.iconpos=i.iconpos||"left",
|
|
||||||
o="ui-icon "+i.icon,i.iconshadow&&(o+=" ui-icon-shadow");i.iconpos&&(l+=" ui-btn-icon-"+i.iconpos,i.iconpos=="notext"&&!h.attr("title")&&h.attr("title",h.getEncodedText()));i.corners&&(l+=" ui-btn-corner-all",k+=" ui-btn-corner-all");i.shadow&&(l+=" ui-shadow");g.setAttribute("data-"+a.mobile.ns+"theme",i.theme);h.addClass(l);m.className=k;m.setAttribute("aria-hidden","true");p.className="ui-btn-text";m.appendChild(p);if(j)j.className=o,m.appendChild(j);for(;g.firstChild;)p.appendChild(g.firstChild);
|
|
||||||
g.appendChild(m);a.data(g,"textWrapper",a(p))}return this};a.fn.buttonMarkup.defaults={corners:true,shadow:true,iconshadow:true,inline:false,wrapperEls:"span"};var d=function(){a(document).bind({vmousedown:function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-up-"+c).addClass("ui-btn-down-"+c))},"vmousecancel vmouseup":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-down-"+c).addClass("ui-btn-up-"+
|
|
||||||
c))},"vmouseover focus":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-up-"+c).addClass("ui-btn-hover-"+c))},"vmouseout blur":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-hover-"+c+" ui-btn-down-"+c).addClass("ui-btn-up-"+c))}});d=null};a(document).bind("pagecreate create",function(b){a(":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a",
|
|
||||||
b.target).not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')").buttonMarkup()})})(jQuery);
|
|
||||||
(function(a){a.fn.controlgroup=function(e){return this.each(function(){function b(a){a.removeClass("ui-btn-corner-all ui-shadow").eq(0).addClass(h[0]).end().last().addClass(h[1]).addClass("ui-controlgroup-last")}var d=a(this),f=a.extend({direction:d.jqmData("type")||"vertical",shadow:false,excludeInvisible:true},e),c=d.children("legend"),h=f.direction=="horizontal"?["ui-corner-left","ui-corner-right"]:["ui-corner-top","ui-corner-bottom"];d.find("input").first().attr("type");c.length&&(d.wrapInner("<div class='ui-controlgroup-controls'></div>"),
|
|
||||||
a("<div role='heading' class='ui-controlgroup-label'>"+c.html()+"</div>").insertBefore(d.children(0)),c.remove());d.addClass("ui-corner-all ui-controlgroup ui-controlgroup-"+f.direction);b(d.find(".ui-btn"+(f.excludeInvisible?":visible":"")));b(d.find(".ui-btn-inner"));f.shadow&&d.addClass("ui-shadow")})};a(document).bind("pagecreate create",function(e){a(":jqmData(role='controlgroup')",e.target).controlgroup({excludeInvisible:false})})})(jQuery);
|
|
||||||
(function(a){a(document).bind("pagecreate create",function(e){a(e.target).find("a").not(".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')").addClass("ui-link")})})(jQuery);
|
|
||||||
(function(a,e){a.fn.fixHeaderFooter=function(){return!a.support.scrollTop||a.support.touchOverflow&&a.mobile.touchOverflowEnabled?this:this.each(function(){var b=a(this);b.jqmData("fullscreen")&&b.addClass("ui-page-fullscreen");b.find(".ui-header:jqmData(position='fixed')").addClass("ui-header-fixed ui-fixed-inline fade");b.find(".ui-footer:jqmData(position='fixed')").addClass("ui-footer-fixed ui-fixed-inline fade")})};a.mobile.fixedToolbars=function(){function b(){!i&&g==="overlay"&&(h||a.mobile.fixedToolbars.hide(true),
|
|
||||||
a.mobile.fixedToolbars.startShowTimer())}function d(a){var b=0,c,d;if(a){d=document.body;c=a.offsetParent;for(b=a.offsetTop;a&&a!=d;){b+=a.scrollTop||0;if(a==c)b+=c.offsetTop,c=a.offsetParent;a=a.parentNode}}return b}function f(b){var c=a(window).scrollTop(),e=d(b[0]),f=b.css("top")=="auto"?0:parseFloat(b.css("top")),h=window.innerHeight,g=b.outerHeight(),i=b.parents(".ui-page:not(.ui-page-fullscreen)").length;return b.is(".ui-header-fixed")?(f=c-e+f,f<e&&(f=0),b.css("top",i?f:c)):b.css("top",i?c+
|
|
||||||
h-g-(e-f):c+h-g)}if(a.support.scrollTop&&(!a.support.touchOverflow||!a.mobile.touchOverflowEnabled)){var c,h,g="inline",i=false,k=null,l=false,o=true;a(function(){var c=a(document),d=a(window);c.bind("vmousedown",function(){o&&(k=g)}).bind("vclick",function(b){o&&!a(b.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length&&!l&&(a.mobile.fixedToolbars.toggle(k),k=null)}).bind("silentscroll",b);(c.scrollTop()===0?d:c).bind("scrollstart",function(){l=true;k===
|
|
||||||
null&&(k=g);var b=k=="overlay";if(i=b||!!h)a.mobile.fixedToolbars.clearShowTimer(),b&&a.mobile.fixedToolbars.hide(true)}).bind("scrollstop",function(b){a(b.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length||(l=false,i&&(a.mobile.fixedToolbars.startShowTimer(),i=false),k=null)});d.bind("resize updatelayout",b)});a(".ui-page").live("pagebeforeshow",function(b,d){var e=a(b.target).find(":jqmData(role='footer')"),h=e.data("id"),g=d.prevPage,g=g&&g.find(":jqmData(role='footer')"),
|
|
||||||
g=g.length&&g.jqmData("id")===h;h&&g&&(c=e,f(c.removeClass("fade in out").appendTo(a.mobile.pageContainer)))}).live("pageshow",function(){var b=a(this);c&&c.length&&setTimeout(function(){f(c.appendTo(b).addClass("fade"));c=null},500);a.mobile.fixedToolbars.show(true,this)});a(".ui-collapsible-contain").live("collapse expand",b);return{show:function(b,c){a.mobile.fixedToolbars.clearShowTimer();g="overlay";return(c?a(c):a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var c=
|
|
||||||
a(this),e=a(window).scrollTop(),h=d(c[0]),g=window.innerHeight,i=c.outerHeight(),e=c.is(".ui-header-fixed")&&e<=h+i||c.is(".ui-footer-fixed")&&h<=e+g;c.addClass("ui-fixed-overlay").removeClass("ui-fixed-inline");!e&&!b&&c.animationComplete(function(){c.removeClass("in")}).addClass("in");f(c)})},hide:function(b){g="inline";return(a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var c=a(this),
|
|
||||||
d=c.css("top"),d=d=="auto"?0:parseFloat(d);c.addClass("ui-fixed-inline").removeClass("ui-fixed-overlay");if(d<0||c.is(".ui-header-fixed")&&d!==0)b?c.css("top",0):c.css("top")!=="auto"&&parseFloat(c.css("top"))!==0&&c.animationComplete(function(){c.removeClass("out reverse").css("top",0)}).addClass("out reverse")})},startShowTimer:function(){a.mobile.fixedToolbars.clearShowTimer();var b=[].slice.call(arguments);h=setTimeout(function(){h=e;a.mobile.fixedToolbars.show.apply(null,b)},100)},clearShowTimer:function(){h&&
|
|
||||||
clearTimeout(h);h=e},toggle:function(b){b&&(g=b);return g==="overlay"?a.mobile.fixedToolbars.hide():a.mobile.fixedToolbars.show()},setTouchToggleEnabled:function(a){o=a}}}}();a(document).bind("pagecreate create",function(b){a(":jqmData(position='fixed')",b.target).length&&a(b.target).each(function(){if(!a.support.scrollTop||a.support.touchOverflow&&a.mobile.touchOverflowEnabled)return this;var b=a(this);b.jqmData("fullscreen")&&b.addClass("ui-page-fullscreen");b.find(".ui-header:jqmData(position='fixed')").addClass("ui-header-fixed ui-fixed-inline fade");
|
|
||||||
b.find(".ui-footer:jqmData(position='fixed')").addClass("ui-footer-fixed ui-fixed-inline fade")})})})(jQuery);
|
|
||||||
(function(a){a.mobile.touchOverflowEnabled=false;a.mobile.touchOverflowZoomEnabled=false;a(document).bind("pagecreate",function(e){a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&(e=a(e.target),e.is(":jqmData(role='page')")&&e.each(function(){var b=a(this),d=b.find(":jqmData(role='header'), :jqmData(role='footer')").filter(":jqmData(position='fixed')"),e=b.jqmData("fullscreen"),c=d.length?b.find(".ui-content"):b;b.addClass("ui-mobile-touch-overflow");c.bind("scrollstop",function(){c.scrollTop()>
|
|
||||||
0&&window.scrollTo(0,a.mobile.defaultHomeScroll)});d.length&&(b.addClass("ui-native-fixed"),e&&(b.addClass("ui-native-fullscreen"),d.addClass("fade in"),a(document).bind("vclick",function(){d.removeClass("ui-native-bars-hidden").toggleClass("in out").animationComplete(function(){a(this).not(".in").addClass("ui-native-bars-hidden")})})))}))})})(jQuery);
|
|
||||||
(function(a,e){function b(){var b=a("meta[name='viewport']");b.length?b.attr("content",b.attr("content")+", user-scalable=no"):a("head").prepend("<meta>",{name:"viewport",content:"user-scalable=no"})}var d=a("html");a("head");var f=a(e);a(e.document).trigger("mobileinit");if(a.mobile.gradeA()){if(a.mobile.ajaxBlacklist)a.mobile.ajaxEnabled=false;d.addClass("ui-mobile ui-mobile-rendering");var c=a("<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>");
|
|
||||||
a.extend(a.mobile,{showPageLoadingMsg:function(){if(a.mobile.loadingMessage){var b=a("."+a.mobile.activeBtnClass).first();c.find("h1").text(a.mobile.loadingMessage).end().appendTo(a.mobile.pageContainer).css({top:a.support.scrollTop&&f.scrollTop()+f.height()/2||b.length&&b.offset().top||100})}d.addClass("ui-loading")},hidePageLoadingMsg:function(){d.removeClass("ui-loading")},initializePage:function(){var b=a(":jqmData(role='page')");b.length||(b=a("body").wrapInner("<div data-"+a.mobile.ns+"role='page'></div>").children(0));
|
|
||||||
b.add(":jqmData(role='dialog')").each(function(){var b=a(this);b.jqmData("url")||b.attr("data-"+a.mobile.ns+"url",b.attr("id")||location.pathname+location.search)});a.mobile.firstPage=b.first();a.mobile.pageContainer=b.first().parent().addClass("ui-mobile-viewport");f.trigger("pagecontainercreate");a.mobile.showPageLoadingMsg();!a.mobile.hashListeningEnabled||!a.mobile.path.stripHash(location.hash)?a.mobile.changePage(a.mobile.firstPage,{transition:"none",reverse:true,changeHash:false,fromHashChange:true}):
|
|
||||||
f.trigger("hashchange",[true])}});a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&!a.mobile.touchOverflowZoomEnabled&&b();a.mobile._registerInternalEvents();a(function(){e.scrollTo(0,1);a.mobile.defaultHomeScroll=!a.support.scrollTop||a(e).scrollTop()===1?0:1;a.mobile.autoInitializePage&&a.mobile.initializePage();f.load(a.mobile.silentScroll)})}})(jQuery,this);
|
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,7 +27,7 @@
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
{{qry=''}}
|
{{qry=''}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-size: 1.75em;">
|
<th style="font-size: 1.75em;">
|
||||||
@@ -37,13 +37,19 @@
|
|||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{pass}}
|
|
||||||
</table>
|
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
{{pass}}
|
||||||
|
</table>
|
||||||
|
|
||||||
{{elif request.function=='select':}}
|
{{elif request.function=='select':}}
|
||||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||||
</h2>
|
</h2>
|
||||||
|
{{if tb:}}
|
||||||
|
<h3>{{=T('Traceback')}}</h3>
|
||||||
|
<pre>
|
||||||
|
{{=tb}}
|
||||||
|
</pre>
|
||||||
|
{{pass}}
|
||||||
{{if table:}}
|
{{if table:}}
|
||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||||
@@ -149,7 +155,7 @@
|
|||||||
<h4>{{=T("RAM")}}</h4>
|
<h4>{{=T("RAM")}}</h4>
|
||||||
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
||||||
{{if ram['entries'] > 0:}}
|
{{if ram['entries'] > 0:}}
|
||||||
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
||||||
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -43,8 +43,7 @@ def file_create_form(location):
|
|||||||
form=FORM(T("create file with filename:")," ",
|
form=FORM(T("create file with filename:")," ",
|
||||||
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY),
|
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY),
|
||||||
INPUT(_type="hidden",_name="location",_value=location),
|
INPUT(_type="hidden",_name="location",_value=location),
|
||||||
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)),
|
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)))
|
||||||
INPUT(_type="submit",_value=T("Create")),_action=URL('create_file'))
|
|
||||||
return form
|
return form
|
||||||
def upload_plugin_form(app):
|
def upload_plugin_form(app):
|
||||||
form=FORM(T("upload plugin file:")," ",
|
form=FORM(T("upload plugin file:")," ",
|
||||||
@@ -207,7 +206,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
</ul>
|
</ul>
|
||||||
<div data-role="fieldcontain">{{=file_create_form('%s/static/' % app)}}
|
<div data-role="fieldcontain">{{=file_create_form('%s/static/' % app)}}
|
||||||
{{=file_upload_form('%s/static/' % app)}}</div>
|
{{#=file_upload_form('%s/static/' % app)}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -225,7 +224,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
|
|||||||
</span>
|
</span>
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<div data-role="fieldcontain">{{=file_create_form('%s/modules/' % app)}}
|
<div data-role="fieldcontain">{{=file_create_form('%s/modules/' % app)}}
|
||||||
{{=file_upload_form('%s/modules/' % app)}}</div>
|
{{#=file_upload_form('%s/modules/' % app)}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
<div><input type="hidden" name="send" value="{{=send}}"/></div>
|
<div><input type="hidden" name="send" value="{{=send}}"/></div>
|
||||||
<table>
|
<table>
|
||||||
<tr><td>{{=T('Administrator Password:')}}</td><td><input type="password" name="password" id="password"/></td></tr>
|
<tr><td>{{=T('Administrator Password:')}}</td><td><input type="password" name="password" id="password"/></td></tr>
|
||||||
<tr><td></td><td><input type="submit" name="login" value="{{=T('Login')}}"/></td></tr>
|
<tr><td></td><td><button name="login">{{=T('Login')}}</button></td></tr>
|
||||||
</table>
|
</table>
|
||||||
|
<input type="hidden" name="is_mobile" value="true"/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{else:}}
|
{{else:}}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
Remove this if you use the .htaccess -->
|
Remove this if you use the .htaccess -->
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
|
||||||
<title>web2py mobile admin</title>
|
<title>Web2py Mobile Admin</title>
|
||||||
|
|
||||||
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
|
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
|
||||||
<meta name="application-name" content="{{=request.application}}" />
|
<meta name="application-name" content="{{=request.application}}" />
|
||||||
@@ -24,16 +24,17 @@
|
|||||||
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
|
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
|
||||||
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
|
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
|
||||||
-->
|
-->
|
||||||
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
|
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
|
||||||
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
|
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
|
||||||
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
|
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
|
||||||
|
|
||||||
<!-- For the less-enabled mobile browsers like Opera Mini -->
|
<!-- For the less-enabled mobile browsers like Opera Mini -->
|
||||||
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.css')}}">
|
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.css')}}">
|
||||||
|
|
||||||
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
|
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
|
||||||
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
|
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script!>
|
||||||
|
|
||||||
{{include 'web2py_ajax.html'}}
|
{{include 'web2py_ajax.html'}}
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.js')}}"></script>
|
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.js')}}"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.error { color: red; }
|
.error { color: red; }
|
||||||
@@ -75,7 +76,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
|
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
|
||||||
|
|
||||||
<div data-role="page">
|
<div data-role="page">
|
||||||
<div data-role="header" data-fullscreen="true" data-position="fixed">
|
<div data-role="header">
|
||||||
<h1>web2py mobile admin/{{block sectionclass}}{{end}}</h1>
|
<h1>web2py mobile admin/{{block sectionclass}}{{end}}</h1>
|
||||||
{{block header}}
|
{{block header}}
|
||||||
{{if 'auth' in globals():}}
|
{{if 'auth' in globals():}}
|
||||||
@@ -85,7 +86,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
<a rel="external" href="{{=URL('default','user',args='logout')}}" data-icon="logout" class="ui-btn-left">Logout</a>
|
<a rel="external" href="{{=URL('default','user',args='logout')}}" data-icon="logout" class="ui-btn-left">Logout</a>
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<a rel="external" href="{{=URL('default','index')}}" data-icon="home" class="ui-btn-right">{{=T("Home")}}</a>
|
<a rel="external" href="{{=URL('default','index',vars=dict(is_mobile='true'))}}" data-icon="home" class="ui-btn-right">{{=T("Home")}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div data-role="content">
|
<div data-role="content">
|
||||||
@@ -100,7 +101,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{include}}
|
{{include}}
|
||||||
</div>
|
</div>
|
||||||
<div data-role="footer" data-fullscreen="true" data-position="fixed" style="padding: 5px; text-align: center">
|
<div data-role="footer">
|
||||||
{{block footer}}
|
{{block footer}}
|
||||||
powered by web2py - @{{=request.now.year}}
|
powered by web2py - @{{=request.now.year}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
|
|
||||||
<h2>{{=T("Installed applications")}}</h2>
|
<h2>{{=T("Installed applications")}}</h2>
|
||||||
|
|
||||||
<ul data-role="listview">
|
<ul data-role="listview" data-inset="true">
|
||||||
{{for a in apps:}}
|
{{for a in apps:}}
|
||||||
<li>
|
<li>
|
||||||
{{if a==request.application:}}
|
{{if a==request.application:}}
|
||||||
<h3>{{=a}} ({{=T('currently running')}})</h3>
|
<h3>{{=a}} ({{=T('currently running')}})</h3>
|
||||||
{{else:}}
|
{{else:}}
|
||||||
<h3>{{=a}}</h3>
|
<h3>{{=T("Application")}} {{=a}}</h3>
|
||||||
{{if MULTI_USER_MODE and db.app(name=a):}}<p>created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}</p>{{pass}}
|
{{if MULTI_USER_MODE and db.app(name=a):}}<p>created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}</p>{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<ul>
|
<ul data-role="listview" data-inset="true">
|
||||||
{{if a!=request.application:}}
|
{{if a!=request.application:}}
|
||||||
{{=LI(A(T('Goto'),_rel="external",_href=URL(a,'default','index')))}}
|
{{=LI(A(T('Goto'),_rel="external",_href=URL(a,'default','index')))}}
|
||||||
{{if not os.path.exists('applications/%s/compiled' % a):}}
|
{{if not os.path.exists('applications/%s/compiled' % a):}}
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
<script src="{{=cm}}/mode/css/css.js"></script>
|
<script src="{{=cm}}/mode/css/css.js"></script>
|
||||||
<script src="{{=cm}}/mode/javascript/javascript.js"></script>
|
<script src="{{=cm}}/mode/javascript/javascript.js"></script>
|
||||||
<script src="{{=cm}}/mode/htmlmixed/htmlmixed.js"></script>
|
<script src="{{=cm}}/mode/htmlmixed/htmlmixed.js"></script>
|
||||||
|
<script src="{{=cm}}/lib/util/search.js"></script>
|
||||||
|
<script src="{{=cm}}/lib/util/searchcursor.js"></script>
|
||||||
|
<script src="{{=cm}}/lib/util/dialog.js"></script>
|
||||||
|
<link rel="stylesheet" href="{{=cm}}/lib/util/dialog.css">
|
||||||
<script src="{{=cm}}/emmet.min.js"></script>
|
<script src="{{=cm}}/emmet.min.js"></script>
|
||||||
<script language="Javascript" type="text/javascript" src="{{=URL('static','js/ajax_editor.js')}}"></script>
|
<script language="Javascript" type="text/javascript" src="{{=URL('static','js/ajax_editor.js')}}"></script>
|
||||||
{{elif TEXT_EDITOR == 'ace':}}
|
{{elif TEXT_EDITOR == 'ace':}}
|
||||||
@@ -246,6 +250,11 @@ window.onload = function() {
|
|||||||
<ul>
|
<ul>
|
||||||
{{=shortcut('Ctrl+S', T('Save via Ajax'))}}
|
{{=shortcut('Ctrl+S', T('Save via Ajax'))}}
|
||||||
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
|
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
|
||||||
|
{{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}}
|
||||||
|
{{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}}
|
||||||
{{=shortcut('Tab', T('Expand Abbreviation'))}}
|
{{=shortcut('Tab', T('Expand Abbreviation'))}}
|
||||||
</ul>
|
</ul>
|
||||||
{{elif TEXT_EDITOR == 'codemirror':}}
|
{{elif TEXT_EDITOR == 'codemirror':}}
|
||||||
@@ -253,6 +262,11 @@ window.onload = function() {
|
|||||||
<ul>
|
<ul>
|
||||||
{{=shortcut('Ctrl+S', T('Save via Ajax'))}}
|
{{=shortcut('Ctrl+S', T('Save via Ajax'))}}
|
||||||
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
|
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
|
||||||
|
{{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}}
|
||||||
|
{{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}}
|
||||||
|
{{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}}
|
||||||
</ul>
|
</ul>
|
||||||
{{else:}}
|
{{else:}}
|
||||||
<h3>{{=T("Key bindings")}}</h3>
|
<h3>{{=T("Key bindings")}}</h3>
|
||||||
|
|||||||
@@ -76,6 +76,9 @@
|
|||||||
<p>
|
<p>
|
||||||
{{=T("Running on %s", request.env.server_software)}}
|
{{=T("Running on %s", request.env.server_software)}}
|
||||||
</p>
|
</p>
|
||||||
|
{{if session.is_mobile=='auto':}}<p>
|
||||||
|
{{=A(T('Try the mobile interface'),_href=URL('plugin_jqmobile','about'))}}</p>
|
||||||
|
{{pass}}
|
||||||
</div>
|
</div>
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<!-- MULTI_USER_INTERFACE -->
|
<!-- MULTI_USER_INTERFACE -->
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<iframe src="{{=URL('index')}}"></iframe>
|
<iframe src="{{=URL('default','index',vars=dict(is_mobile='true'))}}"></iframe>
|
||||||
<div id="about">
|
<div id="about">
|
||||||
<h1><a href="http://web2py.com">web2py</a> plugin</h1>
|
<h1><a href="http://web2py.com">web2py</a> plugin</h1>
|
||||||
<h2>for <a href="http://jquerymobile.com/">jQuery Mobile</a></h2>
|
<h2>for <a href="http://jquerymobile.com/">jQuery Mobile</a></h2>
|
||||||
|
|||||||
@@ -24,17 +24,17 @@
|
|||||||
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
|
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
|
||||||
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
|
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
|
||||||
-->
|
-->
|
||||||
<!--meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"-->
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
|
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
|
||||||
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
|
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
|
||||||
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
|
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
|
||||||
|
|
||||||
<!-- For the less-enabled mobile browsers like Opera Mini -->
|
<!-- For the less-enabled mobile browsers like Opera Mini -->
|
||||||
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.css')}}">
|
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.css')}}">
|
||||||
|
|
||||||
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
|
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
|
||||||
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
|
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script!>
|
||||||
|
|
||||||
{{include 'web2py_ajax.html'}}
|
{{include 'web2py_ajax.html'}}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.js')}}"></script>
|
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.js')}}"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.error { color: red; }
|
.error { color: red; }
|
||||||
@@ -76,7 +76,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
|
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
|
||||||
|
|
||||||
<div data-role="page">
|
<div data-role="page">
|
||||||
<div data-role="header" data-fullscreen="true" data-position="fixed">
|
<div data-role="header">
|
||||||
<h1>{{=response.title}}</h1>
|
<h1>{{=response.title}}</h1>
|
||||||
{{block header}}
|
{{block header}}
|
||||||
{{if 'auth' in globals():}}
|
{{if 'auth' in globals():}}
|
||||||
@@ -101,7 +101,7 @@ $(document).bind("mobileinit", function(){
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{include}}
|
{{include}}
|
||||||
</div>
|
</div>
|
||||||
<div data-role="footer" data-fullscreen="true" data-position="fixed" style="padding: 5px; text-align: center">
|
<div data-role="footer">
|
||||||
{{block footer}}
|
{{block footer}}
|
||||||
powered by web2py - @{{=request.now.year}}
|
powered by web2py - @{{=request.now.year}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ def query_by_table_type(tablename,db,request=request):
|
|||||||
# ## list all databases and tables
|
# ## list all databases and tables
|
||||||
# ###########################################################
|
# ###########################################################
|
||||||
|
|
||||||
|
|
||||||
def index():
|
def index():
|
||||||
return dict(databases=databases)
|
return dict(databases=databases)
|
||||||
|
|
||||||
@@ -207,6 +206,7 @@ def select():
|
|||||||
if match:
|
if match:
|
||||||
table = match.group('table')
|
table = match.group('table')
|
||||||
try:
|
try:
|
||||||
|
tb = None
|
||||||
nrows = db(query).count()
|
nrows = db(query).count()
|
||||||
if form.vars.update_check and form.vars.update_fields:
|
if form.vars.update_check and form.vars.update_fields:
|
||||||
db(query).update(**eval_in_global_env('dict(%s)'
|
db(query).update(**eval_in_global_env('dict(%s)'
|
||||||
@@ -221,6 +221,8 @@ def select():
|
|||||||
else:
|
else:
|
||||||
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
(rows, nrows) = ([], 0)
|
(rows, nrows) = ([], 0)
|
||||||
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
||||||
# begin handle upload csv
|
# begin handle upload csv
|
||||||
@@ -250,6 +252,7 @@ def select():
|
|||||||
rows=rows,
|
rows=rows,
|
||||||
query=request.vars.query,
|
query=request.vars.query,
|
||||||
formcsv = formcsv,
|
formcsv = formcsv,
|
||||||
|
tb = tb,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ def hello6():
|
|||||||
|
|
||||||
def status():
|
def status():
|
||||||
""" page that shows internal status"""
|
""" page that shows internal status"""
|
||||||
response.view = 'generic.html'
|
|
||||||
return dict(toolbar=response.toolbar())
|
return dict(toolbar=response.toolbar())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ h3 {font-size:2.00em}
|
|||||||
h4 {font-size:1.50em}
|
h4 {font-size:1.50em}
|
||||||
h5 {font-size:1.25em}
|
h5 {font-size:1.25em}
|
||||||
h6 {font-size:1.12em}
|
h6 {font-size:1.12em}
|
||||||
th,label {font-weight:bold; white-space:nowrap}
|
th,label {font-weight:bold; white-space:nowrap;}
|
||||||
td,th {text-align:left; padding:2px 5px 2px 5px}
|
td,th {text-align:left; padding:2px 5px 2px 5px}
|
||||||
th {vertical-align:middle; border-right:1px solid white}
|
th {vertical-align:middle; border-right:1px solid white}
|
||||||
td {vertical-align:top}
|
td {vertical-align:top}
|
||||||
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
|
|||||||
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
||||||
|
|
||||||
/* fix ie problem with menu */
|
/* fix ie problem with menu */
|
||||||
.ie-lte7 .topbar .container {z-index:2}
|
|
||||||
|
|
||||||
td.w2p_fw {padding-bottom:1px}
|
td.w2p_fw {padding-bottom:1px}
|
||||||
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
||||||
@@ -187,10 +186,7 @@ div.error {
|
|||||||
.web2py_paginator {}
|
.web2py_paginator {}
|
||||||
.web2py_grid {width:100%}
|
.web2py_grid {width:100%}
|
||||||
.web2py_grid table {width:100%}
|
.web2py_grid table {width:100%}
|
||||||
.web2py_grid tbody td {
|
.web2py_grid tbody td {padding:2px 5px 2px 5px; vertical-align: middle;}
|
||||||
padding:2px 5px 2px 5px;
|
|
||||||
vertical-align:middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.web2py_grid thead th,.web2py_grid tfoot td {
|
.web2py_grid thead th,.web2py_grid tfoot td {
|
||||||
background-color:#EAEAEA;
|
background-color:#EAEAEA;
|
||||||
@@ -299,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
|
|||||||
.web2py_console input, .web2py_console select,
|
.web2py_console input, .web2py_console select,
|
||||||
.web2py_console a { margin: 2px; }
|
.web2py_console a { margin: 2px; }
|
||||||
|
|
||||||
.ie9 #w2p_query_panel {padding-bottom:2px}
|
|
||||||
|
|
||||||
#wiki_page_body {
|
#wiki_page_body {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fix some IE problems */
|
||||||
|
|
||||||
|
.ie-lte7 .topbar .container {z-index:2}
|
||||||
|
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
|
||||||
|
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
|
||||||
|
.ie9 #w2p_query_panel {padding-bottom:2px}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ function web2py_ajax_init(target) {
|
|||||||
|
|
||||||
function web2py_event_handlers() {
|
function web2py_event_handlers() {
|
||||||
var doc = jQuery(document)
|
var doc = jQuery(document)
|
||||||
doc.on('click', '.flash', function(e){jQuery(this).fadeOut('slow'); e.preventDefault();});
|
doc.on('click', '.flash', function(e){var t=jQuery(this); if(t.css('top')=='0px') t.slideUp('slow'); else t.fadeOut(); e.preventDefault();});
|
||||||
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
|
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
|
||||||
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
|
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
|
||||||
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
|
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
|
||||||
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
|
|||||||
jQuery(function() {
|
jQuery(function() {
|
||||||
var flash = jQuery('.flash');
|
var flash = jQuery('.flash');
|
||||||
flash.hide();
|
flash.hide();
|
||||||
if(flash.html()) flash.slideDown();
|
if(flash.html()) flash.append('<span style="float:right;">×</span>').slideDown();
|
||||||
web2py_ajax_init(document);
|
web2py_ajax_init(document);
|
||||||
web2py_event_handlers();
|
web2py_event_handlers();
|
||||||
});
|
});
|
||||||
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
|
|||||||
form.submit(function(e){
|
form.submit(function(e){
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('post',action,form.serialize(),target);
|
web2py_ajax_page('post',action,form.serialize(),target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_trap_link(target) {
|
function web2py_trap_link(target) {
|
||||||
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
||||||
var link=jQuery(this);
|
var link=jQuery(this);
|
||||||
link.click(function(e) {
|
link.click(function(e) {
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('get',link.attr('href'),[],target);
|
web2py_ajax_page('get',link.attr('href'),[],target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_ajax_page(method, action, data, target) {
|
function web2py_ajax_page(method, action, data, target) {
|
||||||
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
|
|||||||
web2py_trap_link(target);
|
web2py_trap_link(target);
|
||||||
web2py_ajax_init('#'+target);
|
web2py_ajax_init('#'+target);
|
||||||
if(command)
|
if(command)
|
||||||
eval(decodeURIComponent(command));
|
eval(decodeURIComponent(command));
|
||||||
if(flash)
|
if(flash)
|
||||||
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// run once (no timeout specified)
|
// run once (no timeout specified)
|
||||||
element.reload_counter = Infinity;
|
element.reload_counter = Infinity;
|
||||||
web2py_ajax_page('get', action, null, target);
|
web2py_ajax_page('get', action, null, target);
|
||||||
} }); }
|
} }); }
|
||||||
|
|
||||||
function web2py_comet(url,onmessage,onopen,onclose) {
|
function web2py_websocket(url,onmessage,onopen,onclose) {
|
||||||
if ("WebSocket" in window) {
|
if ("WebSocket" in window) {
|
||||||
var ws = new WebSocket(url);
|
var ws = new WebSocket(url);
|
||||||
ws.onopen = onopen?onopen:(function(){});
|
ws.onopen = onopen?onopen:(function(){});
|
||||||
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
|
|||||||
} else return false; // not supported
|
} else return false; // not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function web2py_calc_entropy(mystring) {
|
||||||
|
//calculate a simple entropy for a given string
|
||||||
|
var csets = new Array(
|
||||||
|
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
|
||||||
|
'0123456789abcdefghijklmnopqrstuvwxyz');
|
||||||
|
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
|
||||||
|
for (var i=0;i<mystringlist.length;i++) { // classify this character
|
||||||
|
var c = mystringlist[i], inset=5;
|
||||||
|
for(var j = 0; j<csets.length; j++)
|
||||||
|
if (csets[j].indexOf(c) != -1) {inset = j; break;}
|
||||||
|
//calculate effect of character on alphabet size
|
||||||
|
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
|
||||||
|
else if (!(c in other)) {score += 1;other[c] = 1;}
|
||||||
|
if (inset != lastset) {score += 1;lastset = inset;}
|
||||||
|
}
|
||||||
|
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
|
||||||
|
return Math.round(entropy*100)/100
|
||||||
|
}
|
||||||
|
|
||||||
|
function web2py_validate_entropy(myfield, req_entropy) {
|
||||||
|
var validator = function () {
|
||||||
|
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
|
||||||
|
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
|
||||||
|
if(v<=0.5) {r=1.0; g=2.0*v;}
|
||||||
|
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
|
||||||
|
var color = '#'+rs(r)+rs(g)+rs(b);
|
||||||
|
myfield.css('background-color',color);
|
||||||
|
entropy_callback = myfield.data('entropy_callback');
|
||||||
|
if(entropy_callback) entropy_callback(v);
|
||||||
|
}
|
||||||
|
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
{{qry=''}}
|
{{qry=''}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-size: 1.75em;">
|
<th style="font-size: 1.75em;">
|
||||||
@@ -37,13 +37,19 @@
|
|||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{pass}}
|
|
||||||
</table>
|
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
{{pass}}
|
||||||
|
</table>
|
||||||
|
|
||||||
{{elif request.function=='select':}}
|
{{elif request.function=='select':}}
|
||||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||||
</h2>
|
</h2>
|
||||||
|
{{if tb:}}
|
||||||
|
<h3>{{=T('Traceback')}}</h3>
|
||||||
|
<pre>
|
||||||
|
{{=tb}}
|
||||||
|
</pre>
|
||||||
|
{{pass}}
|
||||||
{{if table:}}
|
{{if table:}}
|
||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||||
@@ -149,7 +155,7 @@
|
|||||||
<h4>{{=T("RAM")}}</h4>
|
<h4>{{=T("RAM")}}</h4>
|
||||||
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
||||||
{{if ram['entries'] > 0:}}
|
{{if ram['entries'] > 0:}}
|
||||||
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
||||||
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ Current version: {{="%s.%s.%s (%s) %s" % request.env.web2py_version}}</p>
|
|||||||
<br/>
|
<br/>
|
||||||
<a class="button" href="{{=URL('download')}}" style="width:90%">DOWNLOAD NOW</a><br/>
|
<a class="button" href="{{=URL('download')}}" style="width:90%">DOWNLOAD NOW</a><br/>
|
||||||
<a class="button" href="http://web2py.com/demo_admin" style="width:90%">ONLINE DEMO</a><br/>
|
<a class="button" href="http://web2py.com/demo_admin" style="width:90%">ONLINE DEMO</a><br/>
|
||||||
<a class="button" href="http://web2py.com/poweredby" style="width:90%">SITES POWERED BY WEB2PY</a>
|
<a class="button" href="http://web2py.com/poweredby" style="width:90%">SITES POWERED BY WEB2PY</a><br/>
|
||||||
<br/>
|
<a class="button" href="http://www.chipin.com/contribute/id/dcd384d58839aa4d" style="width:90%">SUPPORT/DONATE</a></br>
|
||||||
<object width="234" height="60"><param name="movie" value="http://widget.chipin.com/widget/id/dcd384d58839aa4d"></param><param name="allowScriptAccess" value="always"></param><param name="wmode" value="transparent"></param><param name="color_scheme" value="gray"></param><embed src="http://widget.chipin.com/widget/id/dcd384d58839aa4d" flashVars="color_scheme=gray" type="application/x-shockwave-flash" allowScriptAccess="always" wmode="transparent" width="234" height="60"></embed></object>
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
</li><li>Ian Reinhart Geiser (html helpers)
|
</li><li>Ian Reinhart Geiser (html helpers)
|
||||||
</li><li>Ionel Anton (Romanian translation)
|
</li><li>Ionel Anton (Romanian translation)
|
||||||
</li><li>Jan Beilicke (markmin)
|
</li><li>Jan Beilicke (markmin)
|
||||||
|
</li><li>Jeremy Dillworth
|
||||||
</li><li>Jonathan Benn (is_url validator and tests)
|
</li><li>Jonathan Benn (is_url validator and tests)
|
||||||
</li><li>Jonathan Lundell (multiple contributions)
|
</li><li>Jonathan Lundell (multiple contributions)
|
||||||
</li><li>Josh Goldfoot (xaml/html sanitizer)
|
</li><li>Josh Goldfoot (xaml/html sanitizer)
|
||||||
@@ -156,6 +157,7 @@
|
|||||||
<li><a href="http://www.danga.com/memcached/">memcache</a> developed by Evan Martin</li>
|
<li><a href="http://www.danga.com/memcached/">memcache</a> developed by Evan Martin</li>
|
||||||
<li><a href="http://jquery.com/">jQuery</a> developed by John Resig</li>
|
<li><a href="http://jquery.com/">jQuery</a> developed by John Resig</li>
|
||||||
<li>A syntax highlighter inspired by the code of <a href="http://www.petersblog.org/node/763">Peter Wilkinson</a></li>
|
<li>A syntax highlighter inspired by the code of <a href="http://www.petersblog.org/node/763">Peter Wilkinson</a></li>
|
||||||
|
<li><a href="https://github.com/jtauber/pyuca">pyUCA</a> developed by <a href="http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/">James Tauber</a></li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|||||||
@@ -113,7 +113,6 @@ def query_by_table_type(tablename,db,request=request):
|
|||||||
# ## list all databases and tables
|
# ## list all databases and tables
|
||||||
# ###########################################################
|
# ###########################################################
|
||||||
|
|
||||||
|
|
||||||
def index():
|
def index():
|
||||||
return dict(databases=databases)
|
return dict(databases=databases)
|
||||||
|
|
||||||
@@ -207,6 +206,7 @@ def select():
|
|||||||
if match:
|
if match:
|
||||||
table = match.group('table')
|
table = match.group('table')
|
||||||
try:
|
try:
|
||||||
|
tb = None
|
||||||
nrows = db(query).count()
|
nrows = db(query).count()
|
||||||
if form.vars.update_check and form.vars.update_fields:
|
if form.vars.update_check and form.vars.update_fields:
|
||||||
db(query).update(**eval_in_global_env('dict(%s)'
|
db(query).update(**eval_in_global_env('dict(%s)'
|
||||||
@@ -221,6 +221,8 @@ def select():
|
|||||||
else:
|
else:
|
||||||
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop))
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
import traceback
|
||||||
|
tb = traceback.format_exc()
|
||||||
(rows, nrows) = ([], 0)
|
(rows, nrows) = ([], 0)
|
||||||
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
response.flash = DIV(T('Invalid Query'),PRE(str(e)))
|
||||||
# begin handle upload csv
|
# begin handle upload csv
|
||||||
@@ -250,6 +252,7 @@ def select():
|
|||||||
rows=rows,
|
rows=rows,
|
||||||
query=request.vars.query,
|
query=request.vars.query,
|
||||||
formcsv = formcsv,
|
formcsv = formcsv,
|
||||||
|
tb = tb,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
'book': ['books'],
|
'book': ['books'],
|
||||||
'is': ['are'],
|
'is': ['are'],
|
||||||
'man': ['men'],
|
'man': ['men'],
|
||||||
|
'miss': ['misses'],
|
||||||
'person': ['people'],
|
'person': ['people'],
|
||||||
'quark': ['quarks'],
|
'quark': ['quarks'],
|
||||||
'shop': ['shops'],
|
'shop': ['shops'],
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
if not request.env.web2py_runtime_gae:
|
if not request.env.web2py_runtime_gae:
|
||||||
## if NOT running on Google App Engine use SQLite or other DB
|
## if NOT running on Google App Engine use SQLite or other DB
|
||||||
db = DAL('sqlite://storage.sqlite')
|
db = DAL('sqlite://storage.sqlite')
|
||||||
else:
|
else:
|
||||||
## connect to Google BigTable (optional 'google:datastore://namespace')
|
## connect to Google BigTable (optional 'google:datastore://namespace')
|
||||||
db = DAL('google:datastore')
|
db = DAL('google:datastore')
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ def _():
|
|||||||
# shortcuts
|
# shortcuts
|
||||||
app = request.application
|
app = request.application
|
||||||
ctr = request.controller
|
ctr = request.controller
|
||||||
# useful links to internal and external resources
|
# useful links to internal and external resources
|
||||||
response.menu+=[
|
response.menu+=[
|
||||||
(SPAN('web2py',_style='color:yellow'),False, 'http://web2py.com', [
|
(SPAN('web2py',_class='highlighted'),False, 'http://web2py.com', [
|
||||||
(T('My Sites'),False,URL('admin','default','site')),
|
(T('My Sites'),False,URL('admin','default','site')),
|
||||||
(T('This App'),False,URL('admin','default','design/%s' % app), [
|
(T('This App'),False,URL('admin','default','design/%s' % app), [
|
||||||
(T('Controller'),False,
|
(T('Controller'),False,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+2
-2
File diff suppressed because one or more lines are too long
@@ -61,7 +61,7 @@ input[type=text],input[type=password],select{width:300px; margin-right:5px}
|
|||||||
border-top:1px #DEDEDE solid;
|
border-top:1px #DEDEDE solid;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
// background:<fill here for header image>;
|
/* background:<fill here for header image>; */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
|
|||||||
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
|
||||||
|
|
||||||
/* fix ie problem with menu */
|
/* fix ie problem with menu */
|
||||||
.ie-lte7 .topbar .container {z-index:2}
|
|
||||||
|
|
||||||
td.w2p_fw {padding-bottom:1px}
|
td.w2p_fw {padding-bottom:1px}
|
||||||
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
|
||||||
@@ -183,7 +182,7 @@ div.error {
|
|||||||
* will look better with the declarations below
|
* will look better with the declarations below
|
||||||
* if needed to remove base.css consider keeping these following lines in some css file.
|
* if needed to remove base.css consider keeping these following lines in some css file.
|
||||||
*/
|
*/
|
||||||
// .web2py_table {border:1px solid #ccc}
|
/* .web2py_table {border:1px solid #ccc} */
|
||||||
.web2py_paginator {}
|
.web2py_paginator {}
|
||||||
.web2py_grid {width:100%}
|
.web2py_grid {width:100%}
|
||||||
.web2py_grid table {width:100%}
|
.web2py_grid table {width:100%}
|
||||||
@@ -296,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
|
|||||||
.web2py_console input, .web2py_console select,
|
.web2py_console input, .web2py_console select,
|
||||||
.web2py_console a { margin: 2px; }
|
.web2py_console a { margin: 2px; }
|
||||||
|
|
||||||
.ie9 #w2p_query_panel {padding-bottom:2px}
|
|
||||||
|
|
||||||
#wiki_page_body {
|
#wiki_page_body {
|
||||||
width: 600px;
|
width: 600px;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fix some IE problems */
|
||||||
|
|
||||||
|
.ie-lte7 .topbar .container {z-index:2}
|
||||||
|
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
|
||||||
|
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
|
||||||
|
.ie9 #w2p_query_panel {padding-bottom:2px}
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
#navbar .auth_navbar, #navbar .auth_navbar a {color:inherit;}
|
/*=============================================================
|
||||||
|
CUSTOM RULES
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
div.flash.flash-center {
|
body{height:auto;} /* to avoid vertical scroll bar */
|
||||||
left: 25%;
|
div.flash.flash-center{left:25%;right:25%;}
|
||||||
right: 25%;
|
div.flash.flash-top,div.flash.flash-top:hover{
|
||||||
}
|
position:relative;
|
||||||
|
display:block;
|
||||||
div.flash.flash-top, div.flash.flash-top:hover {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:1em;
|
padding:1em;
|
||||||
top:0;
|
top:0;
|
||||||
left:0;
|
left:0;
|
||||||
width:100%;
|
width:100%;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);
|
||||||
color:#865100;
|
color:#865100;
|
||||||
background:#feea9a;
|
background:#feea9a;
|
||||||
border:1px solid;
|
border:1px solid;
|
||||||
@@ -24,154 +23,209 @@ div.flash.flash-top, div.flash.flash-top:hover {
|
|||||||
border-radius:0;
|
border-radius:0;
|
||||||
opacity:1;
|
opacity:1;
|
||||||
}
|
}
|
||||||
|
#header{margin-top:60px;}
|
||||||
/* bootstrap dropdown */
|
|
||||||
|
|
||||||
.dropdown-menu ul {
|
|
||||||
left: 100%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
margin-top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu li:hover ul {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
#header {
|
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mastheader h1 {
|
.mastheader h1 {
|
||||||
margin-bottom: 9px;
|
margin-bottom:9px;
|
||||||
font-size: 81px;
|
font-size:81px;
|
||||||
font-weight: bold;
|
font-weight:bold;
|
||||||
letter-spacing: -1px;
|
letter-spacing:-1px;
|
||||||
line-height: 1;
|
line-height:1;
|
||||||
|
font-size:54px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mastheader h1 {
|
|
||||||
font-size: 54px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mastheader small {
|
.mastheader small {
|
||||||
font-size: 20px;
|
font-size:20px;
|
||||||
font-weight: 300;
|
font-weight:300;
|
||||||
}
|
}
|
||||||
|
/* auth navbar - primitive style */
|
||||||
.navbar .dropdown-menu ul:before {
|
.auth_navbar,.auth_navbar a{color:inherit;}
|
||||||
border-bottom: 7px solid transparent;
|
.ie-lte7 .auth_navbar,.auth_navbar a{color:expression(this.parentNode.currentStyle['color']); /* ie7 doesn't support inherit */}
|
||||||
border-left: none;
|
.auth_navbar a{white-space:nowrap;} /* to avoid the nav split on more lines */
|
||||||
border-right: 7px solid rgba(0, 0, 0, 0.2);
|
.auth_navbar a:hover{color:white;text-decoration:none;}
|
||||||
border-top: 7px solid transparent;
|
ul#navbar>.auth_navbar{
|
||||||
left: -7px;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar .dropdown-menu ul:after {
|
|
||||||
border-top: 6px solid transparent;
|
|
||||||
border-left: none;
|
|
||||||
border-right: 6px solid #fff;
|
|
||||||
border-bottom: 6px solid transparent;
|
|
||||||
left: 10px;
|
|
||||||
top: 6px;
|
|
||||||
left: -6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu span{
|
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
|
padding:5px;
|
||||||
}
|
}
|
||||||
|
/* form errors message box customization */
|
||||||
.chevron-right {
|
div.error_wrapper{margin-bottom:9px;}
|
||||||
border-left: 4px solid #000;
|
div.error_wrapper .error{
|
||||||
border-right: 4px solid transparent;
|
|
||||||
border-bottom: 4px solid transparent;
|
|
||||||
border-top: 4px solid transparent;
|
|
||||||
content: "";
|
|
||||||
display: inline-block;
|
|
||||||
height: 0;
|
|
||||||
opacity: 0.7;
|
|
||||||
vertical-align: top;
|
|
||||||
width: 0;
|
|
||||||
margin-right:-13px;
|
|
||||||
margin-top: 7px;
|
|
||||||
float:right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.open > .dropdown-menu ul { /* fix menu issue when BS2.0.4 is applied */
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ie-lte7 #navbar .auth_navbar, #navbar .auth_navbar a {color:expression(this.parentNode.currentStyle['color']); /* ie7 doesn't support inherit */}
|
|
||||||
|
|
||||||
#navbar .auth_navbar a:hover {color:white;text-decoration:none;} /* this overwrite bootswatch */
|
|
||||||
ul#navbar{padding:0;} /*reset ul padding */
|
|
||||||
ul#navbar>.auth_navbar{display:inline-block;padding:5px;} /* set padding of span.auth_navbar inside ul*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
height:auto; /*to avoid vertical scroll bar*/
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,h2,h3,h4,h5,h6 {font-family: inherit;}
|
|
||||||
|
|
||||||
li {margin-bottom: 0;} /*bootswatch*/
|
|
||||||
|
|
||||||
.auth_navbar, .navbar .btn-group { padding:0; }
|
|
||||||
|
|
||||||
[class^="icon-"],[class*=" icon-"]{background-image:url("../images/glyphicons-halflings.png")} /* right folder for bootstrap black images/icons */
|
|
||||||
.icon-white{background-image:url("../images/glyphicons-halflings-white.png");} /* right folder for bootstrap white images/icons */
|
|
||||||
|
|
||||||
.navbar-inner{
|
|
||||||
-webkit-border-radius: 0;
|
|
||||||
-moz-border-radius: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 979px) {
|
|
||||||
body {
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#navbar{bottom:-10px;left:4px;}
|
|
||||||
|
|
||||||
div.flash {
|
|
||||||
right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu ul {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.error_wrapper {
|
|
||||||
margin-bottom: 9px;
|
|
||||||
}
|
|
||||||
div.error {
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
-o-border-radius: 4px;
|
-o-border-radius: 4px;
|
||||||
-moz-border-radius: 4px;
|
-moz-border-radius: 4px;
|
||||||
-webkit-border-radius: 4px;
|
-webkit-border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
/* below rules are only for formstyle = bootstrap
|
||||||
|
trying to make errors look like bootstrap ones */
|
||||||
|
div.controls .error_wrapper{
|
||||||
|
display:inline-block;
|
||||||
|
margin-bottom:0;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
div.controls .error{
|
||||||
|
min-width:5px;
|
||||||
|
background:inherit;
|
||||||
|
color:#B94A48;
|
||||||
|
border:none;
|
||||||
|
padding:0;
|
||||||
|
margin:0;
|
||||||
|
//display:inline; /* uncommenting this, the animation effect is lost */
|
||||||
|
}
|
||||||
|
div.controls .inline-help{color:#3A87AD;}
|
||||||
|
div.controls .error_wrapper+.inline-help{margin-left:-99999px;}
|
||||||
|
/* beautify brand */
|
||||||
|
.navbar-inverse .brand{color:#c6cecc;}
|
||||||
|
.navbar-inverse .brand b{display:inline-block;margin-top:-1px;}
|
||||||
|
.navbar-inverse .brand b>span{font-size:22px;color:white}
|
||||||
|
.navbar-inverse .brand:hover b>span{color:white}
|
||||||
|
/* beautify web2py link in navbar */
|
||||||
|
span.highlighted{color:#d8d800;}
|
||||||
|
.open span.highlighted{color:#ffff00;}
|
||||||
|
|
||||||
input[type="button"], input[type="submit"] {
|
/*=============================================================
|
||||||
// to be fixed
|
OVERRIDING WEB2PY.CSS RULES
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
/* reset to default */
|
||||||
|
a{white-space:normal;}
|
||||||
|
li{margin-bottom:0;}
|
||||||
|
textarea,button{display:block;}
|
||||||
|
/*reset ul padding */
|
||||||
|
ul#navbar{padding:0;}
|
||||||
|
/* label aligned to related input */
|
||||||
|
td.w2p_fl,td.w2p_fc {padding:0;}
|
||||||
|
#web2py_user_form td{vertical-align:middle;}
|
||||||
|
|
||||||
|
/*=============================================================
|
||||||
|
OVERRIDING BOOTSTRAP.CSS RULES
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
/* because web2py handles this via js */
|
||||||
|
.hidden{visibility:visible;}
|
||||||
|
/* right folder for bootstrap black images/icons */
|
||||||
|
[class^="icon-"],[class*=" icon-"]{
|
||||||
|
background-image:url("../images/glyphicons-halflings.png")
|
||||||
|
}
|
||||||
|
/* right folder for bootstrap white images/icons */
|
||||||
|
.icon-white,
|
||||||
|
.nav-tabs > .active > a > [class^="icon-"],
|
||||||
|
.nav-tabs > .active > a > [class*=" icon-"],
|
||||||
|
.nav-pills > .active > a > [class^="icon-"],
|
||||||
|
.nav-pills > .active > a > [class*=" icon-"],
|
||||||
|
.nav-list > .active > a > [class^="icon-"],
|
||||||
|
.nav-list > .active > a > [class*=" icon-"],
|
||||||
|
.navbar-inverse .nav > .active > a > [class^="icon-"],
|
||||||
|
.navbar-inverse .nav > .active > a > [class*=" icon-"],
|
||||||
|
.dropdown-menu > li > a:hover > [class^="icon-"],
|
||||||
|
.dropdown-menu > li > a:hover > [class*=" icon-"],
|
||||||
|
.dropdown-menu > .active > a > [class^="icon-"],
|
||||||
|
.dropdown-menu > .active > a > [class*=" icon-"] {
|
||||||
|
background-image:url("../images/glyphicons-halflings-white.png");
|
||||||
|
}
|
||||||
|
/* bootstrap has a label as input's wrapper while web2py has a div */
|
||||||
|
div>input[type="radio"],div>input[type="checkbox"]{margin:0;}
|
||||||
|
/* bootstrap has button instead of input */
|
||||||
|
input[type="button"], input[type="submit"]{margin-right:8px;}
|
||||||
|
|
||||||
|
/*=============================================================
|
||||||
|
RULES FOR SOLVING CONFLICTS BETWEEN WEB2PY.CSS AND BOOTSTRAP.CSS
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
/*when formstyle=table3cols*/
|
||||||
|
tr#auth_user_remember__row>td.w2p_fw>div{padding-bottom:8px;}
|
||||||
|
td.w2p_fw div>label{vertical-align:middle;}
|
||||||
|
td.w2p_fc {padding-bottom:5px;}
|
||||||
|
/*when formstyle=divs*/
|
||||||
|
div#auth_user_remember__row{margin-top:4px;}
|
||||||
|
div#auth_user_remember__row>.w2p_fl{display:none;}
|
||||||
|
div#auth_user_remember__row>.w2p_fw{min-height:39px;}
|
||||||
|
div.w2p_fw,div.w2p_fc{
|
||||||
|
display:inline-block;
|
||||||
|
vertical-align:middle;
|
||||||
|
margin-bottom:0;
|
||||||
|
}
|
||||||
|
div.w2p_fc{
|
||||||
|
padding-left:5px;
|
||||||
|
margin-top:-8px;
|
||||||
|
}
|
||||||
|
/*when formstyle=ul*/
|
||||||
|
form>ul{
|
||||||
|
list-style:none;
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
li#auth_user_remember__row{margin-top:4px;}
|
||||||
|
li#auth_user_remember__row>.w2p_fl{display:none;}
|
||||||
|
li#auth_user_remember__row>.w2p_fw{min-height:39px;}
|
||||||
|
/*when formstyle=bootstrap*/
|
||||||
|
#auth_user_remember__row label.checkbox{display:block;}
|
||||||
|
span.inline-help{display:inline-block;}
|
||||||
|
input[type="text"].input-xlarge,input[type="password"].input-xlarge{width:270px;}
|
||||||
|
/*when recaptcha is used*/
|
||||||
|
#recaptcha{min-height:30px;display:inline-block;margin-bottom:0;line-height:30px;vertical-align:middle;}
|
||||||
|
td>#recaptcha{margin-bottom:6px;}
|
||||||
|
div>#recaptcha{margin-bottom:9px;}
|
||||||
|
div.control-group.error{
|
||||||
|
width:auto;
|
||||||
|
background:transparent;
|
||||||
|
border:0;
|
||||||
|
color:inherit;
|
||||||
|
padding:0;
|
||||||
|
background-repeat:repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 479px) {
|
/*=============================================================
|
||||||
body {
|
OTHER RULES
|
||||||
padding-left: 10px;
|
==============================================================*/
|
||||||
padding-right: 10px;
|
|
||||||
|
.navbar-inner{
|
||||||
|
position:relative; /*unnecessary ??*/
|
||||||
|
}
|
||||||
|
/* Massimo Di Pierro fixed alignment in forms with list:string */
|
||||||
|
form table tr{margin-bottom:9px;}
|
||||||
|
td.w2p_fw ul{margin-left:0px;}
|
||||||
|
|
||||||
|
/* web2py_console in grid and smartgrid */
|
||||||
|
.hidden{visibility:visible;}
|
||||||
|
.web2py_console input{
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.web2py_console input[type="submit"],
|
||||||
|
.web2py_console input[type="button"],
|
||||||
|
.web2py_console button{
|
||||||
|
padding-top:4px;
|
||||||
|
padding-bottom:4px;
|
||||||
|
margin:3px 0 0 2px;
|
||||||
|
}
|
||||||
|
.web2py_console a,
|
||||||
|
.web2py_console select,
|
||||||
|
.web2py_console input
|
||||||
|
{
|
||||||
|
margin:3px 0 0 2px;
|
||||||
|
}
|
||||||
|
.web2py_grid form table{width:auto;}
|
||||||
|
/* auth_user_remember checkbox extrapadding in IE fix */
|
||||||
|
.ie-lte9 input#auth_user_remember.checkbox {padding-left:0;}
|
||||||
|
|
||||||
|
/*=============================================================
|
||||||
|
MEDIA QUERIES
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
@media only screen and (max-width:979px){
|
||||||
|
body{padding-top:0px;}
|
||||||
|
#navbar{bottom:-10px;left:4px;}
|
||||||
|
div.flash{right:5px;}
|
||||||
|
.dropdown-menu ul{visibility:visible;}
|
||||||
|
}
|
||||||
|
@media only screen and (max-width:479px){
|
||||||
|
body{
|
||||||
|
padding-left:10px;
|
||||||
|
padding-right:10px;
|
||||||
}
|
}
|
||||||
|
.navbar-fixed-top,.navbar-fixed-bottom {
|
||||||
.navbar-fixed-top, .navbar-fixed-bottom {
|
margin-left:-10px;
|
||||||
margin-left: -10px;
|
margin-right:-10px;
|
||||||
margin-right: -10px;
|
|
||||||
}
|
}
|
||||||
|
input[type="text"],input[type="password"],select{
|
||||||
input[type="text"], input[type="password"], select {
|
width:95%;
|
||||||
width: 95%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +1,23 @@
|
|||||||
|
/*=============================================================
|
||||||
|
BOOTSTRAP DROPDOWN MENU
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
.dropdown-menu ul{
|
||||||
|
left:100%;
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
visibility:hidden;
|
||||||
|
margin-top:-1px;
|
||||||
|
}
|
||||||
|
.dropdown-menu li:hover ul{visibility:visible;}
|
||||||
|
.navbar .dropdown-menu ul:before{
|
||||||
|
border-bottom:7px solid transparent;
|
||||||
|
border-left:none;
|
||||||
|
border-right:7px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-top:7px solid transparent;
|
||||||
|
left:-7px;
|
||||||
|
top:5px;
|
||||||
|
}
|
||||||
.nav > li.dropdown > a:after {
|
.nav > li.dropdown > a:after {
|
||||||
border-left: 4px solid transparent;
|
border-left: 4px solid transparent;
|
||||||
border-right: 4px solid transparent;
|
border-right: 4px solid transparent;
|
||||||
@@ -15,8 +35,7 @@
|
|||||||
border-bottom-color: #FFFFFF;
|
border-bottom-color: #FFFFFF;
|
||||||
border-top-color: #FFFFFF;
|
border-top-color: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
.dropdown-menu span{display:inline-block;}
|
||||||
|
|
||||||
ul.dropdown-menu li.dropdown > a:after {
|
ul.dropdown-menu li.dropdown > a:after {
|
||||||
border-left: 4px solid #000;
|
border-left: 4px solid #000;
|
||||||
border-right: 4px solid transparent;
|
border-right: 4px solid transparent;
|
||||||
@@ -35,4 +54,69 @@ ul.dropdown-menu li.dropdown > a:after {
|
|||||||
|
|
||||||
ul.nav li.dropdown:hover ul.dropdown-menu {
|
ul.nav li.dropdown:hover ul.dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.open >.dropdown-menu ul{display:block;} /* fix menu issue when BS2.0.4 is applied */
|
||||||
|
|
||||||
|
/*=============================================================
|
||||||
|
BOOTSTRAP SUBMIT BUTTON
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
input[type='submit']:not(.btn) {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 14px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
|
||||||
|
vertical-align: middle;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: whiteSmoke;
|
||||||
|
background-image: -webkit-gradient(linear,0 0,0 100%,from(white),to(#E6E6E6));
|
||||||
|
background-image: -webkit-linear-gradient(top,white,#E6E6E6);
|
||||||
|
background-image: -o-linear-gradient(top,white,#E6E6E6);
|
||||||
|
background-image: linear-gradient(to bottom,white,#E6E6E6);
|
||||||
|
background-image: -moz-linear-gradient(top,white,#E6E6E6);
|
||||||
|
background-repeat: repeat-x;
|
||||||
|
border: 1px solid #BBB;
|
||||||
|
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||||
|
border-bottom-color: #A2A2A2;
|
||||||
|
-webkit-border-radius: 4px;
|
||||||
|
-moz-border-radius: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);
|
||||||
|
filter: progid:dximagetransform.microsoft.gradient(enabled=false);
|
||||||
|
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit']:not(.btn):hover {
|
||||||
|
color: #333;
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #E6E6E6;
|
||||||
|
background-position: 0 -15px;
|
||||||
|
-webkit-transition: background-position .1s linear;
|
||||||
|
-moz-transition: background-position .1s linear;
|
||||||
|
-o-transition: background-position .1s linear;
|
||||||
|
transition: background-position .1s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit']:not(.btn).active, input[type='submit']:not(.btn):active {
|
||||||
|
background-color: #E6E6E6;
|
||||||
|
background-color: #D9D9D9 9;
|
||||||
|
background-image: none;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
|
||||||
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*=============================================================
|
||||||
|
OTHER
|
||||||
|
==============================================================*/
|
||||||
|
|
||||||
|
.ie-lte8 .navbar-fixed-top {position:static;}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
+1
-1
File diff suppressed because one or more lines are too long
@@ -21,8 +21,8 @@ jQuery(function(){
|
|||||||
var title = escape(jQuery('title').text());
|
var title = escape(jQuery('title').text());
|
||||||
var twit = 'http://twitter.com/home?status='+title+'%20'+url;
|
var twit = 'http://twitter.com/home?status='+title+'%20'+url;
|
||||||
var facebook = 'http://www.facebook.com/sharer.php?u='+url;
|
var facebook = 'http://www.facebook.com/sharer.php?u='+url;
|
||||||
var buzz = 'http://www.google.com/reader/link?url='+url+'&title='+title+'&srcURL='+host;
|
var gplus = 'https://plus.google.com/share?url='+url;
|
||||||
var tbar = '<div id="socialdrawer"><span>Share<br/></span><div id="sicons"><a href="'+twit+'" id="twit" title="Share on twitter"><img src="'+path+'/twitter.png" alt="Share on Twitter" width="32" height="32" /></a><a href="'+facebook+'" id="facebook" title="Share on Facebook"><img src="'+path+'/facebook.png" alt="Share on facebook" width="32" height="32" /></a><a href="'+buzz+'" id="buzz" title="Share on Buzz"><img src="'+path+'/google-buzz.png" alt="Share on Buzz" width="32" height="32" /></a></div></div>';
|
var tbar = '<div id="socialdrawer"><span>Share<br/></span><div id="sicons"><a href="'+twit+'" id="twit" title="Share on twitter"><img src="'+path+'/twitter.png" alt="Share on Twitter" width="32" height="32" /></a><a href="'+facebook+'" id="facebook" title="Share on Facebook"><img src="'+path+'/facebook.png" alt="Share on facebook" width="32" height="32" /></a><a href="'+gplus+'" id="gplus" title="Share on Google Plus"><img src="'+path+'/gplus-32.png" alt="Share on Google Plus" width="32" height="32" /></a></div></div>';
|
||||||
// Add the share tool bar.
|
// Add the share tool bar.
|
||||||
jQuery('body').append(tbar);
|
jQuery('body').append(tbar);
|
||||||
var st = jQuery('#socialdrawer');
|
var st = jQuery('#socialdrawer');
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
|
|||||||
jQuery(function() {
|
jQuery(function() {
|
||||||
var flash = jQuery('.flash');
|
var flash = jQuery('.flash');
|
||||||
flash.hide();
|
flash.hide();
|
||||||
if(flash.html()) flash.append('<span style="float:right;">×<span>').slideDown();
|
if(flash.html()) flash.append('<span style="float:right;">×</span>').slideDown();
|
||||||
web2py_ajax_init(document);
|
web2py_ajax_init(document);
|
||||||
web2py_event_handlers();
|
web2py_event_handlers();
|
||||||
});
|
});
|
||||||
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
|
|||||||
form.submit(function(e){
|
form.submit(function(e){
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('post',action,form.serialize(),target);
|
web2py_ajax_page('post',action,form.serialize(),target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_trap_link(target) {
|
function web2py_trap_link(target) {
|
||||||
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
jQuery('#'+target+' a.w2p_trap').each(function(i){
|
||||||
var link=jQuery(this);
|
var link=jQuery(this);
|
||||||
link.click(function(e) {
|
link.click(function(e) {
|
||||||
jQuery('.flash').hide().html('');
|
jQuery('.flash').hide().html('');
|
||||||
web2py_ajax_page('get',link.attr('href'),[],target);
|
web2py_ajax_page('get',link.attr('href'),[],target);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function web2py_ajax_page(method, action, data, target) {
|
function web2py_ajax_page(method, action, data, target) {
|
||||||
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
|
|||||||
web2py_trap_link(target);
|
web2py_trap_link(target);
|
||||||
web2py_ajax_init('#'+target);
|
web2py_ajax_init('#'+target);
|
||||||
if(command)
|
if(command)
|
||||||
eval(decodeURIComponent(command));
|
eval(decodeURIComponent(command));
|
||||||
if(flash)
|
if(flash)
|
||||||
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
jQuery('.flash').html(decodeURIComponent(flash)).slideDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// run once (no timeout specified)
|
// run once (no timeout specified)
|
||||||
element.reload_counter = Infinity;
|
element.reload_counter = Infinity;
|
||||||
web2py_ajax_page('get', action, null, target);
|
web2py_ajax_page('get', action, null, target);
|
||||||
} }); }
|
} }); }
|
||||||
|
|
||||||
function web2py_comet(url,onmessage,onopen,onclose) {
|
function web2py_websocket(url,onmessage,onopen,onclose) {
|
||||||
if ("WebSocket" in window) {
|
if ("WebSocket" in window) {
|
||||||
var ws = new WebSocket(url);
|
var ws = new WebSocket(url);
|
||||||
ws.onopen = onopen?onopen:(function(){});
|
ws.onopen = onopen?onopen:(function(){});
|
||||||
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
|
|||||||
} else return false; // not supported
|
} else return false; // not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function web2py_calc_entropy(mystring) {
|
||||||
|
//calculate a simple entropy for a given string
|
||||||
|
var csets = new Array(
|
||||||
|
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
|
||||||
|
'0123456789abcdefghijklmnopqrstuvwxyz');
|
||||||
|
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
|
||||||
|
for (var i=0;i<mystringlist.length;i++) { // classify this character
|
||||||
|
var c = mystringlist[i], inset=5;
|
||||||
|
for(var j = 0; j<csets.length; j++)
|
||||||
|
if (csets[j].indexOf(c) != -1) {inset = j; break;}
|
||||||
|
//calculate effect of character on alphabet size
|
||||||
|
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
|
||||||
|
else if (!(c in other)) {score += 1;other[c] = 1;}
|
||||||
|
if (inset != lastset) {score += 1;lastset = inset;}
|
||||||
|
}
|
||||||
|
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
|
||||||
|
return Math.round(entropy*100)/100
|
||||||
|
}
|
||||||
|
|
||||||
|
function web2py_validate_entropy(myfield, req_entropy) {
|
||||||
|
var validator = function () {
|
||||||
|
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
|
||||||
|
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
|
||||||
|
if(v<=0.5) {r=1.0; g=2.0*v;}
|
||||||
|
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
|
||||||
|
var color = '#'+rs(r)+rs(g)+rs(b);
|
||||||
|
myfield.css('background-color',color);
|
||||||
|
entropy_callback = myfield.data('entropy_callback');
|
||||||
|
if(entropy_callback) entropy_callback(v);
|
||||||
|
}
|
||||||
|
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// this code improves bootstrap menus and adds dropdown support
|
||||||
|
jQuery(function(){
|
||||||
|
jQuery('.nav>li>a').each(function(){
|
||||||
|
if(jQuery(this).parent().find('ul').length)
|
||||||
|
jQuery(this).attr({'class':'dropdown-toggle','data-toggle':'dropdown'}).append('<b class="caret"></b>');
|
||||||
|
});
|
||||||
|
jQuery('.nav li li').each(function(){
|
||||||
|
if(jQuery(this).find('ul').length)
|
||||||
|
jQuery(this).addClass('dropdown-submenu');
|
||||||
|
});
|
||||||
|
function hoverMenu(){
|
||||||
|
var wid = document.documentElement.clientWidth; //faster than $(window).width() and cross browser
|
||||||
|
if (wid>=980){
|
||||||
|
jQuery('ul.nav a.dropdown-toggle').parent().hover(function(){
|
||||||
|
mi = jQuery(this).addClass('open');
|
||||||
|
mi.children('.dropdown-menu').stop(true, true).delay(200).fadeIn(400);
|
||||||
|
}, function(){
|
||||||
|
mi = jQuery(this);
|
||||||
|
mi.children('.dropdown-menu').stop(true, true).delay(200).fadeOut(function(){mi.removeClass('open')});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
hoverMenu(); // first page load
|
||||||
|
jQuery(window).resize(hoverMenu); // on resize event
|
||||||
|
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
|
||||||
|
// make all buttons bootstrap buttons
|
||||||
|
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn');
|
||||||
|
});
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
{{pass}}
|
{{pass}}
|
||||||
{{else:}}
|
{{else:}}
|
||||||
{{qry=''}}
|
{{qry=''}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
{{pass}}
|
{{pass}}
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-size: 1.75em;">
|
<th style="font-size: 1.75em;">
|
||||||
@@ -37,13 +37,19 @@
|
|||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{pass}}
|
|
||||||
</table>
|
|
||||||
{{pass}}
|
{{pass}}
|
||||||
|
{{pass}}
|
||||||
|
</table>
|
||||||
|
|
||||||
{{elif request.function=='select':}}
|
{{elif request.function=='select':}}
|
||||||
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
|
||||||
</h2>
|
</h2>
|
||||||
|
{{if tb:}}
|
||||||
|
<h3>{{=T('Traceback')}}</h3>
|
||||||
|
<pre>
|
||||||
|
{{=tb}}
|
||||||
|
</pre>
|
||||||
|
{{pass}}
|
||||||
{{if table:}}
|
{{if table:}}
|
||||||
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
|
||||||
<h3>{{=T("Rows in Table")}}</h3><br/>
|
<h3>{{=T("Rows in Table")}}</h3><br/>
|
||||||
@@ -149,7 +155,7 @@
|
|||||||
<h4>{{=T("RAM")}}</h4>
|
<h4>{{=T("RAM")}}</h4>
|
||||||
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
|
||||||
{{if ram['entries'] > 0:}}
|
{{if ram['entries'] > 0:}}
|
||||||
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
|
||||||
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -4,16 +4,20 @@
|
|||||||
{{
|
{{
|
||||||
if request.args(0)=='login':
|
if request.args(0)=='login':
|
||||||
if not 'register' in auth.settings.actions_disabled:
|
if not 'register' in auth.settings.actions_disabled:
|
||||||
form.add_button(T('Register'),URL(args='register'))
|
form.add_button(T('Register'),URL(args='register'),_class='btn')
|
||||||
pass
|
pass
|
||||||
if not 'request_reset_password' in auth.settings.actions_disabled:
|
if not 'request_reset_password' in auth.settings.actions_disabled:
|
||||||
form.add_button(T('Lost Password'),URL(args='request_reset_password'))
|
form.add_button(T('Lost Password'),URL(args='request_reset_password'),_class='btn')
|
||||||
pass
|
pass
|
||||||
pass
|
pass
|
||||||
=form
|
=form
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
<script language="javascript"><!--
|
<script language="javascript"><!--
|
||||||
jQuery("#web2py_user_form input:visible:enabled:first").focus();
|
jQuery("#web2py_user_form input:visible:enabled:first").focus();
|
||||||
//--></script>
|
{{if request.args(0)=='register':}}
|
||||||
|
web2py_validate_entropy(jQuery('#auth_user_password'),100);
|
||||||
|
{{elif request.args(0)=='change_password':}}
|
||||||
|
web2py_validate_entropy(jQuery('#no_table_new_password'),100);
|
||||||
|
{{pass}}
|
||||||
|
//--></script>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}}
|
<?xml version="1.0" encoding="ISO-8859-1"?>{{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}}
|
||||||
|
|||||||
@@ -39,10 +39,10 @@
|
|||||||
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
|
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
|
||||||
|
|
||||||
<!-- include stylesheets -->
|
<!-- include stylesheets -->
|
||||||
{{
|
{{
|
||||||
|
response.files.append(URL('static','css/web2py.css'))
|
||||||
response.files.append(URL('static','css/bootstrap.min.css'))
|
response.files.append(URL('static','css/bootstrap.min.css'))
|
||||||
response.files.append(URL('static','css/bootstrap-responsive.min.css'))
|
response.files.append(URL('static','css/bootstrap-responsive.min.css'))
|
||||||
response.files.append(URL('static','css/web2py.css'))
|
|
||||||
response.files.append(URL('static','css/web2py_bootstrap.css'))
|
response.files.append(URL('static','css/web2py_bootstrap.css'))
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -66,17 +66,17 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<!-- Navbar ================================================== -->
|
<!-- Navbar ================================================== -->
|
||||||
<div class="navbar navbar-fixed-top">
|
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||||
<div class="flash">{{=response.flash or ''}}</div>
|
<div class="flash">{{=response.flash or ''}}</div>
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- the next tag is necessary for bootstrap menus, do not remove -->
|
<!-- the next tag is necessary for bootstrap menus, do not remove -->
|
||||||
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</a>
|
</button>
|
||||||
<a class="brand" href="#">web2py™ </a>
|
<a class="brand" href="http://www.web2py.com/"><b>web<span>2</span>py</b>™ </a>
|
||||||
<ul id="navbar" class="nav pull-right">{{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}</ul>
|
<ul id="navbar" class="nav pull-right">{{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}</ul>
|
||||||
<div class="nav-collapse">
|
<div class="nav-collapse">
|
||||||
{{is_mobile=request.user_agent().is_mobile}}
|
{{is_mobile=request.user_agent().is_mobile}}
|
||||||
@@ -146,30 +146,8 @@
|
|||||||
|
|
||||||
<!-- The javascript =============================================
|
<!-- The javascript =============================================
|
||||||
(Placed at the end of the document so the pages load faster) -->
|
(Placed at the end of the document so the pages load faster) -->
|
||||||
<script>
|
|
||||||
// this code improves bootstrap menus and adds dropdown support
|
|
||||||
jQuery(function(){
|
|
||||||
jQuery('.nav>li>a').each(function(){
|
|
||||||
if(jQuery(this).parent().find('ul').length)
|
|
||||||
jQuery(this).attr({'class':'dropdown-toggle','data-toggle':'dropdown'}).append('<b class="caret"></b>');
|
|
||||||
});
|
|
||||||
jQuery('.nav li li').each(function(){
|
|
||||||
if(jQuery(this).find('ul').length)
|
|
||||||
jQuery(this).children('a').contents().before('<i class="chevron-right"></i>');
|
|
||||||
});
|
|
||||||
if(jQuery(document).width()>=980) {
|
|
||||||
jQuery('ul.nav li.dropdown').hover(function() {
|
|
||||||
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
|
|
||||||
}, function() {
|
|
||||||
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
|
|
||||||
// make all buttons bootstrap buttons
|
|
||||||
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn').css({'margin-right':'2px','margin-bottom':'2px'});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
|
<script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
|
||||||
|
<script src="{{=URL('static','js/web2py_bootstrap.js')}}"></script>
|
||||||
<!--[if lt IE 7 ]>
|
<!--[if lt IE 7 ]>
|
||||||
<script src="{{=URL('static','js/dd_belatedpng.js')}}"></script>
|
<script src="{{=URL('static','js/dd_belatedpng.js')}}"></script>
|
||||||
<script> DD_belatedPNG.fix('img, .png_bg'); //fix any <img> or .png_bg background-images </script>
|
<script> DD_belatedPNG.fix('img, .png_bg'); //fix any <img> or .png_bg background-images </script>
|
||||||
|
|||||||
@@ -83,9 +83,7 @@ def wsgiapp(env, res):
|
|||||||
if global_settings.web2py_runtime == 'gae:development':
|
if global_settings.web2py_runtime == 'gae:development':
|
||||||
gluon.admin.create_missing_folders()
|
gluon.admin.create_missing_folders()
|
||||||
|
|
||||||
from gluon.custom_import import custom_import_install
|
|
||||||
web2py_path = global_settings.applications_parent # backward compatibility
|
web2py_path = global_settings.applications_parent # backward compatibility
|
||||||
custom_import_install(web2py_path)
|
|
||||||
|
|
||||||
return gluon.main.wsgibase(env, res)
|
return gluon.main.wsgibase(env, res)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -60,7 +60,7 @@ def app_pack(app, request, raise_ex=False):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
app_cleanup(app, request)
|
app_cleanup(app, request)
|
||||||
filename = apath('../deposit/%s.w2p' % app, request)
|
filename = apath('../deposit/web2py.app.%s.w2p' % app, request)
|
||||||
w2p_pack(filename, apath(app, request))
|
w2p_pack(filename, apath(app, request))
|
||||||
return filename
|
return filename
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|||||||
+105
-111
@@ -11,7 +11,7 @@ Basic caching classes and methods
|
|||||||
|
|
||||||
- Cache - The generic caching object interfacing with the others
|
- Cache - The generic caching object interfacing with the others
|
||||||
- CacheInRam - providing caching in ram
|
- CacheInRam - providing caching in ram
|
||||||
- CacheInDisk - provides caches on disk
|
- CacheOnDisk - provides caches on disk
|
||||||
|
|
||||||
Memcache is also available via a different module (see gluon.contrib.memcache)
|
Memcache is also available via a different module (see gluon.contrib.memcache)
|
||||||
|
|
||||||
@@ -46,6 +46,19 @@ class CacheAbstract(object):
|
|||||||
Main function is now to provide referenced api documentation.
|
Main function is now to provide referenced api documentation.
|
||||||
|
|
||||||
Use CacheInRam or CacheOnDisk instead which are derived from this class.
|
Use CacheInRam or CacheOnDisk instead which are derived from this class.
|
||||||
|
|
||||||
|
Attentions, Michele says:
|
||||||
|
|
||||||
|
There are signatures inside gdbm files that are used directly
|
||||||
|
by the python gdbm adapter that often are lagging behind in the
|
||||||
|
detection code in python part.
|
||||||
|
On every occasion that a gdbm store is probed by the python adapter,
|
||||||
|
the probe fails, because gdbm file version is newer.
|
||||||
|
Using gdbm directly from C would work, because there is backward
|
||||||
|
compatibility, but not from python!
|
||||||
|
The .shelve file is discarded and a new one created (with new
|
||||||
|
signature) and it works until it is probed again...
|
||||||
|
The possible consequences are memory leaks and broken sessions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
cache_stats_name = 'web2py_cache_statistics'
|
cache_stats_name = 'web2py_cache_statistics'
|
||||||
@@ -130,22 +143,27 @@ class CacheInRam(CacheAbstract):
|
|||||||
meta_storage = {}
|
meta_storage = {}
|
||||||
|
|
||||||
def __init__(self, request=None):
|
def __init__(self, request=None):
|
||||||
self.locker.acquire()
|
self.initialized = False
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
if self.initialized: return
|
||||||
|
else: self.initialized = True
|
||||||
|
self.locker.acquire()
|
||||||
|
request = self.request
|
||||||
if request:
|
if request:
|
||||||
app = request.application
|
app = request.application
|
||||||
else:
|
else:
|
||||||
app = ''
|
app = ''
|
||||||
if not app in self.meta_storage:
|
if not app in self.meta_storage:
|
||||||
self.storage = self.meta_storage[app] = {CacheAbstract.cache_stats_name: {
|
self.storage = self.meta_storage[app] = {
|
||||||
'hit_total': 0,
|
CacheAbstract.cache_stats_name: {'hit_total': 0, 'misses': 0}}
|
||||||
'misses': 0,
|
|
||||||
}}
|
|
||||||
else:
|
else:
|
||||||
self.storage = self.meta_storage[app]
|
self.storage = self.meta_storage[app]
|
||||||
self.locker.release()
|
self.locker.release()
|
||||||
|
|
||||||
def clear(self, regex=None):
|
def clear(self, regex=None):
|
||||||
|
self.initialize()
|
||||||
self.locker.acquire()
|
self.locker.acquire()
|
||||||
storage = self.storage
|
storage = self.storage
|
||||||
if regex is None:
|
if regex is None:
|
||||||
@@ -154,10 +172,7 @@ class CacheInRam(CacheAbstract):
|
|||||||
self._clear(storage, regex)
|
self._clear(storage, regex)
|
||||||
|
|
||||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0}
|
||||||
'hit_total': 0,
|
|
||||||
'misses': 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
self.locker.release()
|
self.locker.release()
|
||||||
|
|
||||||
@@ -172,6 +187,7 @@ class CacheInRam(CacheAbstract):
|
|||||||
3) would work unless we deepcopy no storage and retrival which would make things slow.
|
3) would work unless we deepcopy no storage and retrival which would make things slow.
|
||||||
Anyway. You can deepcopy explicitly in the function generating the value to be cached.
|
Anyway. You can deepcopy explicitly in the function generating the value to be cached.
|
||||||
"""
|
"""
|
||||||
|
self.initialize()
|
||||||
|
|
||||||
dt = time_expire
|
dt = time_expire
|
||||||
now = time.time()
|
now = time.time()
|
||||||
@@ -200,6 +216,7 @@ class CacheInRam(CacheAbstract):
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
def increment(self, key, value=1):
|
def increment(self, key, value=1):
|
||||||
|
self.initialize()
|
||||||
self.locker.acquire()
|
self.locker.acquire()
|
||||||
try:
|
try:
|
||||||
if key in self.storage:
|
if key in self.storage:
|
||||||
@@ -226,58 +243,65 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
Values stored in disk cache must be pickable.
|
Values stored in disk cache must be pickable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
speedup_checks = set()
|
def _close_shelve_and_unlock(self):
|
||||||
|
try:
|
||||||
|
if self.storage:
|
||||||
|
self.storage.close()
|
||||||
|
finally:
|
||||||
|
if self.locker and self.locked:
|
||||||
|
portalocker.unlock(self.locker)
|
||||||
|
self.locker.close()
|
||||||
|
self.locked = False
|
||||||
|
|
||||||
def _open_shelf_with_lock(self):
|
def _open_shelve_and_lock(self):
|
||||||
"""Open and return a shelf object, obtaining an exclusive lock
|
"""Open and return a shelf object, obtaining an exclusive lock
|
||||||
on self.locker first. Replaces the close method of the
|
on self.locker first. Replaces the close method of the
|
||||||
returned shelf instance with one that releases the lock upon
|
returned shelf instance with one that releases the lock upon
|
||||||
closing."""
|
closing."""
|
||||||
def _close(self):
|
|
||||||
try:
|
storage = None
|
||||||
shelve.Shelf.close(self)
|
locker = None
|
||||||
finally:
|
locked = False
|
||||||
portalocker.unlock(self.locker)
|
|
||||||
self.locker.close()
|
|
||||||
|
|
||||||
storage, locker, locker_locked = None, None, False
|
|
||||||
try:
|
try:
|
||||||
locker = open(self.locker_name, 'a')
|
locker = locker = open(self.locker_name, 'a')
|
||||||
portalocker.lock(locker, portalocker.LOCK_EX)
|
portalocker.lock(locker, portalocker.LOCK_EX)
|
||||||
locker_locked = True
|
locked = True
|
||||||
storage = shelve.open(self.shelve_name)
|
try:
|
||||||
storage.close = _close.__get__(storage, shelve.Shelf)
|
storage = shelve.open(self.shelve_name)
|
||||||
storage.locker = locker
|
except:
|
||||||
except Exception:
|
logger.error('corrupted cache file %s, will try rebuild it' \
|
||||||
logger.error('corrupted cache file %s, will try to delete and recreate it!' % (self.shelve_name))
|
% (self.shelve_name))
|
||||||
|
storage = None
|
||||||
|
if not storage and os.path.exists(self.shelve_name):
|
||||||
|
os.unlink(self.shelve_name)
|
||||||
|
storage = shelve.open(self.shelve_name)
|
||||||
|
if not CacheAbstract.cache_stats_name in storage.keys():
|
||||||
|
storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0}
|
||||||
|
storage.sync()
|
||||||
|
except Exception, e:
|
||||||
if storage:
|
if storage:
|
||||||
storage.close()
|
storage.close()
|
||||||
storage = None
|
storage = None
|
||||||
|
if locked:
|
||||||
try:
|
portalocker.unlock(locker)
|
||||||
os.unlink(self.shelve_name)
|
locker.close()
|
||||||
storage = shelve.open(self.shelve_name)
|
locked = False
|
||||||
storage.close = _close.__get__(storage, shelve.Shelf)
|
raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name
|
||||||
storage.locker = locker
|
self.locker = locker
|
||||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
self.locked = locked
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
self.storage = storage
|
||||||
'hit_total': 0,
|
|
||||||
'misses': 0,
|
|
||||||
}
|
|
||||||
storage.sync()
|
|
||||||
except (IOError, OSError):
|
|
||||||
logger.warn('unable to delete and recreate cache file %s' % self.shelve_name)
|
|
||||||
if storage:
|
|
||||||
storage.close()
|
|
||||||
storage = None
|
|
||||||
if locker_locked:
|
|
||||||
portalocker.unlock(locker)
|
|
||||||
if locker:
|
|
||||||
locker.close()
|
|
||||||
return storage
|
return storage
|
||||||
|
|
||||||
def __init__(self, request, folder=None):
|
def __init__(self, request=None, folder=None):
|
||||||
|
self.initialized = False
|
||||||
self.request = request
|
self.request = request
|
||||||
|
self.folder = folder
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
if self.initialized: return
|
||||||
|
else: self.initialized = True
|
||||||
|
folder = self.folder
|
||||||
|
request = self.request
|
||||||
|
|
||||||
# Lets test if the cache folder exists, if not
|
# Lets test if the cache folder exists, if not
|
||||||
# we are going to create it
|
# we are going to create it
|
||||||
@@ -291,93 +315,54 @@ class CacheOnDisk(CacheAbstract):
|
|||||||
self.locker_name = os.path.join(folder,'cache.lock')
|
self.locker_name = os.path.join(folder,'cache.lock')
|
||||||
self.shelve_name = os.path.join(folder,'cache.shelve')
|
self.shelve_name = os.path.join(folder,'cache.shelve')
|
||||||
|
|
||||||
speedup_key = (folder,CacheAbstract.cache_stats_name)
|
|
||||||
if not speedup_key in self.speedup_checks or \
|
|
||||||
not os.path.exists(self.shelve_name):
|
|
||||||
try:
|
|
||||||
storage = self._open_shelf_with_lock()
|
|
||||||
try:
|
|
||||||
if not CacheAbstract.cache_stats_name in storage:
|
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
|
||||||
'hit_total': 0,
|
|
||||||
'misses': 0,
|
|
||||||
}
|
|
||||||
storage.sync()
|
|
||||||
finally:
|
|
||||||
storage.close()
|
|
||||||
self.speedup_checks.add(speedup_key)
|
|
||||||
except ImportError:
|
|
||||||
pass # no module _bsddb, ignoring exception now so it makes a ticket only if used
|
|
||||||
|
|
||||||
def clear(self, regex=None):
|
def clear(self, regex=None):
|
||||||
storage = self._open_shelf_with_lock()
|
self.initialize()
|
||||||
|
storage = self._open_shelve_and_lock()
|
||||||
try:
|
try:
|
||||||
if regex is None:
|
if regex is None:
|
||||||
storage.clear()
|
storage.clear()
|
||||||
else:
|
else:
|
||||||
self._clear(storage, regex)
|
self._clear(storage, regex)
|
||||||
if not CacheAbstract.cache_stats_name in storage.keys():
|
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
|
||||||
'hit_total': 0,
|
|
||||||
'misses': 0,
|
|
||||||
}
|
|
||||||
storage.sync()
|
storage.sync()
|
||||||
finally:
|
finally:
|
||||||
storage.close()
|
self._close_shelve_and_unlock()
|
||||||
|
|
||||||
def __call__(self, key, f,
|
def __call__(self, key, f,
|
||||||
time_expire = DEFAULT_TIME_EXPIRE):
|
time_expire = DEFAULT_TIME_EXPIRE):
|
||||||
|
self.initialize()
|
||||||
dt = time_expire
|
dt = time_expire
|
||||||
|
storage = self._open_shelve_and_lock()
|
||||||
storage = self._open_shelf_with_lock()
|
|
||||||
try:
|
try:
|
||||||
item = storage.get(key, None)
|
item = storage.get(key, None)
|
||||||
|
storage[CacheAbstract.cache_stats_name]['hit_total'] += 1
|
||||||
if item and f is None:
|
if item and f is None:
|
||||||
del storage[key]
|
del storage[key]
|
||||||
|
storage.sync()
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
now = time.time()
|
||||||
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'] + 1,
|
if f is None:
|
||||||
'misses': storage[CacheAbstract.cache_stats_name]['misses']
|
value = None
|
||||||
}
|
elif item and (dt is None or item[0] > now - dt):
|
||||||
|
value = item[1]
|
||||||
storage.sync()
|
else:
|
||||||
|
value = f()
|
||||||
|
storage[key] = (now, value)
|
||||||
|
storage[CacheAbstract.cache_stats_name]['misses']+=1
|
||||||
|
storage.sync()
|
||||||
finally:
|
finally:
|
||||||
if storage:
|
self._close_shelve_and_unlock()
|
||||||
storage.close()
|
|
||||||
|
|
||||||
now = time.time()
|
|
||||||
if f is None:
|
|
||||||
return None
|
|
||||||
if item and (dt is None or item[0] > now - dt):
|
|
||||||
return item[1]
|
|
||||||
value = f()
|
|
||||||
|
|
||||||
storage = self._open_shelf_with_lock()
|
|
||||||
try:
|
|
||||||
storage[key] = (now, value)
|
|
||||||
|
|
||||||
storage[CacheAbstract.cache_stats_name] = {
|
|
||||||
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'],
|
|
||||||
'misses': storage[CacheAbstract.cache_stats_name]['misses'] + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.sync()
|
|
||||||
finally:
|
|
||||||
if storage:
|
|
||||||
storage.close()
|
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def increment(self, key, value=1):
|
def increment(self, key, value=1):
|
||||||
storage = self._open_shelf_with_lock()
|
self.initialize()
|
||||||
|
storage = self._open_shelve_and_lock()
|
||||||
try:
|
try:
|
||||||
if key in storage:
|
if key in storage:
|
||||||
value = storage[key][1] + value
|
value = storage[key][1] + value
|
||||||
storage[key] = (time.time(), value)
|
storage[key] = (time.time(), value)
|
||||||
storage.sync()
|
storage.sync()
|
||||||
finally:
|
finally:
|
||||||
if storage:
|
self._close_shelve_and_unlock()
|
||||||
storage.close()
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class CacheAction(object):
|
class CacheAction(object):
|
||||||
@@ -423,10 +408,9 @@ class Cache(object):
|
|||||||
the global request object
|
the global request object
|
||||||
"""
|
"""
|
||||||
# GAE will have a special caching
|
# GAE will have a special caching
|
||||||
|
|
||||||
if have_settings and settings.global_settings.web2py_runtime_gae:
|
if have_settings and settings.global_settings.web2py_runtime_gae:
|
||||||
from contrib.gae_memcache import MemcacheClient
|
from contrib.gae_memcache import MemcacheClient
|
||||||
self.ram=self.disk=MemcacheClient(request)
|
self.ram = self.disk = MemcacheClient(request)
|
||||||
else:
|
else:
|
||||||
# Otherwise use ram (and try also disk)
|
# Otherwise use ram (and try also disk)
|
||||||
self.ram = CacheInRam(request)
|
self.ram = CacheInRam(request)
|
||||||
@@ -479,6 +463,16 @@ class Cache(object):
|
|||||||
return CacheAction(func,key,time_expire,self,cache_model)
|
return CacheAction(func,key,time_expire,self,cache_model)
|
||||||
return tmp
|
return tmp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def with_prefix(cache_model, prefix):
|
||||||
|
"""
|
||||||
|
allow replacing cache.ram with cache.with_prefix(cache.ram,'prefix')
|
||||||
|
it will add prefix to all the cache keys used.
|
||||||
|
"""
|
||||||
|
return lambda key, f, time_expire=DEFAULT_TIME_EXPIRE, prefix=prefix:\
|
||||||
|
cache_model(prefix + key, f, time_expire)
|
||||||
|
|
||||||
|
|
||||||
def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
def lazy_cache(key=None,time_expire=None,cache_model='ram'):
|
||||||
"""
|
"""
|
||||||
can be used to cache any function including in modules,
|
can be used to cache any function including in modules,
|
||||||
|
|||||||
+29
-27
@@ -40,6 +40,7 @@ import imp
|
|||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
import rewrite
|
import rewrite
|
||||||
|
from custom_import import custom_import_install
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import py_compile
|
import py_compile
|
||||||
@@ -358,22 +359,36 @@ OLD IMPLEMENTATION:
|
|||||||
return module
|
return module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
_base_environment_ = dict((k,getattr(html,k)) for k in html.__all__)
|
||||||
|
_base_environment_.update((k,getattr(validators,k)) for k in validators.__all__)
|
||||||
|
_base_environment_['__builtins__'] = __builtins__
|
||||||
|
_base_environment_['HTTP'] = HTTP
|
||||||
|
_base_environment_['redirect'] = redirect
|
||||||
|
_base_environment_['DAL'] = DAL
|
||||||
|
_base_environment_['Field'] = Field
|
||||||
|
_base_environment_['SQLDB'] = SQLDB # for backward compatibility
|
||||||
|
_base_environment_['SQLField'] = SQLField # for backward compatibility
|
||||||
|
_base_environment_['SQLFORM'] = SQLFORM
|
||||||
|
_base_environment_['SQLTABLE'] = SQLTABLE
|
||||||
|
_base_environment_['LOAD'] = LOAD
|
||||||
|
|
||||||
def build_environment(request, response, session, store_current=True):
|
def build_environment(request, response, session, store_current=True):
|
||||||
"""
|
"""
|
||||||
Build the environment dictionary into which web2py files are executed.
|
Build the environment dictionary into which web2py files are executed.
|
||||||
"""
|
"""
|
||||||
h,v = html,validators
|
#h,v = html,validators
|
||||||
environment = dict((k,getattr(h,k)) for k in h.__all__)
|
environment = dict(_base_environment_)
|
||||||
environment.update((k,getattr(v, k)) for k in v.__all__)
|
|
||||||
if not request.env:
|
if not request.env:
|
||||||
request.env = Storage()
|
request.env = Storage()
|
||||||
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
|
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
|
||||||
# /[controller]/[function]/*.py)
|
# /[controller]/[function]/*.py)
|
||||||
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
|
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
|
||||||
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
|
r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
|
||||||
|
|
||||||
t = environment['T'] = translator(request)
|
t = environment['T'] = translator(request)
|
||||||
c = environment['cache'] = Cache(request)
|
c = environment['cache'] = Cache(request)
|
||||||
|
|
||||||
if store_current:
|
if store_current:
|
||||||
current.globalenv = environment
|
current.globalenv = environment
|
||||||
current.request = request
|
current.request = request
|
||||||
@@ -389,27 +404,17 @@ def build_environment(request, response, session, store_current=True):
|
|||||||
__builtins__ = mybuiltin()
|
__builtins__ = mybuiltin()
|
||||||
else:
|
else:
|
||||||
__builtins__['__import__'] = __builtin__.__import__ ### WHY?
|
__builtins__['__import__'] = __builtin__.__import__ ### WHY?
|
||||||
environment['__builtins__'] = __builtins__
|
|
||||||
environment['HTTP'] = HTTP
|
|
||||||
environment['redirect'] = redirect
|
|
||||||
environment['request'] = request
|
environment['request'] = request
|
||||||
environment['response'] = response
|
environment['response'] = response
|
||||||
environment['session'] = session
|
environment['session'] = session
|
||||||
environment['DAL'] = DAL
|
|
||||||
environment['Field'] = Field
|
|
||||||
environment['SQLDB'] = SQLDB # for backward compatibility
|
|
||||||
environment['SQLField'] = SQLField # for backward compatibility
|
|
||||||
environment['SQLFORM'] = SQLFORM
|
|
||||||
environment['SQLTABLE'] = SQLTABLE
|
|
||||||
environment['LOAD'] = LOAD
|
|
||||||
environment['local_import'] = \
|
environment['local_import'] = \
|
||||||
lambda name, reload=False, app=request.application:\
|
lambda name, reload=False, app=request.application:\
|
||||||
local_import_aux(name,reload,app)
|
local_import_aux(name,reload,app)
|
||||||
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
|
BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
|
||||||
response._view_environment = copy.copy(environment)
|
response._view_environment = copy.copy(environment)
|
||||||
|
custom_import_install()
|
||||||
return environment
|
return environment
|
||||||
|
|
||||||
|
|
||||||
def save_pyc(filename):
|
def save_pyc(filename):
|
||||||
"""
|
"""
|
||||||
Bytecode compiles the file `filename`
|
Bytecode compiles the file `filename`
|
||||||
@@ -527,7 +532,6 @@ def run_controller_in(controller, function, environment):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# if compiled should run compiled!
|
# if compiled should run compiled!
|
||||||
|
|
||||||
folder = environment['request'].folder
|
folder = environment['request'].folder
|
||||||
path = pjoin(folder, 'compiled')
|
path = pjoin(folder, 'compiled')
|
||||||
badc = 'invalid controller (%s/%s)' % (controller, function)
|
badc = 'invalid controller (%s/%s)' % (controller, function)
|
||||||
@@ -537,7 +541,7 @@ def run_controller_in(controller, function, environment):
|
|||||||
% (controller, function))
|
% (controller, function))
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badf,
|
rewrite.THREAD_LOCAL.routes.error_message % badf,
|
||||||
web2py_error=badf)
|
web2py_error=badf)
|
||||||
restricted(read_pyc(filename), environment, layer=filename)
|
restricted(read_pyc(filename), environment, layer=filename)
|
||||||
elif function == '_TEST':
|
elif function == '_TEST':
|
||||||
@@ -552,7 +556,7 @@ def run_controller_in(controller, function, environment):
|
|||||||
% controller)
|
% controller)
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badc,
|
rewrite.THREAD_LOCAL.routes.error_message % badc,
|
||||||
web2py_error=badc)
|
web2py_error=badc)
|
||||||
environment['__symbols__'] = environment.keys()
|
environment['__symbols__'] = environment.keys()
|
||||||
code = read_file(filename)
|
code = read_file(filename)
|
||||||
@@ -563,13 +567,13 @@ def run_controller_in(controller, function, environment):
|
|||||||
% controller)
|
% controller)
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badc,
|
rewrite.THREAD_LOCAL.routes.error_message % badc,
|
||||||
web2py_error=badc)
|
web2py_error=badc)
|
||||||
code = read_file(filename)
|
code = read_file(filename)
|
||||||
exposed = regex_expose.findall(code)
|
exposed = regex_expose.findall(code)
|
||||||
if not function in exposed:
|
if not function in exposed:
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badf,
|
rewrite.THREAD_LOCAL.routes.error_message % badf,
|
||||||
web2py_error=badf)
|
web2py_error=badf)
|
||||||
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
|
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
|
||||||
if is_gae:
|
if is_gae:
|
||||||
@@ -579,8 +583,7 @@ def run_controller_in(controller, function, environment):
|
|||||||
response = environment['response']
|
response = environment['response']
|
||||||
vars=response._vars
|
vars=response._vars
|
||||||
if response.postprocessing:
|
if response.postprocessing:
|
||||||
for p in response.postprocessing:
|
vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
|
||||||
vars = p(vars)
|
|
||||||
if isinstance(vars,unicode):
|
if isinstance(vars,unicode):
|
||||||
vars = vars.encode('utf8')
|
vars = vars.encode('utf8')
|
||||||
elif hasattr(vars,'xml') and callable(vars.xml):
|
elif hasattr(vars,'xml') and callable(vars.xml):
|
||||||
@@ -594,7 +597,6 @@ def run_view_in(environment):
|
|||||||
or `view/generic.extension`
|
or `view/generic.extension`
|
||||||
It tries the pre-compiled views_controller_function.pyc before compiling it.
|
It tries the pre-compiled views_controller_function.pyc before compiling it.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
request = environment['request']
|
request = environment['request']
|
||||||
response = environment['response']
|
response = environment['response']
|
||||||
view = response.view
|
view = response.view
|
||||||
@@ -630,7 +632,7 @@ def run_view_in(environment):
|
|||||||
restricted(code, environment, layer=filename)
|
restricted(code, environment, layer=filename)
|
||||||
return
|
return
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badv,
|
rewrite.THREAD_LOCAL.routes.error_message % badv,
|
||||||
web2py_error=badv)
|
web2py_error=badv)
|
||||||
else:
|
else:
|
||||||
filename = pjoin(folder, 'views', view)
|
filename = pjoin(folder, 'views', view)
|
||||||
@@ -639,7 +641,7 @@ def run_view_in(environment):
|
|||||||
filename = pjoin(folder, 'views', view)
|
filename = pjoin(folder, 'views', view)
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(filename):
|
||||||
raise HTTP(404,
|
raise HTTP(404,
|
||||||
rewrite.thread.routes.error_message % badv,
|
rewrite.THREAD_LOCAL.routes.error_message % badv,
|
||||||
web2py_error=badv)
|
web2py_error=badv)
|
||||||
layer = filename
|
layer = filename
|
||||||
if is_gae:
|
if is_gae:
|
||||||
|
|||||||
@@ -12,47 +12,56 @@ cache.ram=cache.disk=MemcacheClient(request)
|
|||||||
import time
|
import time
|
||||||
from google.appengine.api.memcache import Client
|
from google.appengine.api.memcache import Client
|
||||||
|
|
||||||
|
class MemcacheClient(object):
|
||||||
|
|
||||||
class MemcacheClient(Client):
|
client = Client()
|
||||||
|
|
||||||
def __init__(self, request):
|
def __init__(self, request):
|
||||||
self.request = request
|
self.request = request
|
||||||
Client.__init__(self)
|
|
||||||
|
|
||||||
def __call__(
|
def __call__(
|
||||||
self,
|
self,
|
||||||
key,
|
key,
|
||||||
f,
|
f,
|
||||||
time_expire=300,
|
time_expire=300,
|
||||||
):
|
):
|
||||||
key = '%s/%s' % (self.request.application, key)
|
key = '%s/%s' % (self.request.application, key)
|
||||||
dt = time_expire
|
dt = time_expire
|
||||||
value = None
|
value = None
|
||||||
obj = self.get(key)
|
obj = self.client.get(key)
|
||||||
if obj and (dt == None or obj[0] > time.time() - dt):
|
if obj and (dt == None or obj[0] > time.time() - dt):
|
||||||
value = obj[1]
|
value = obj[1]
|
||||||
elif f is None:
|
elif f is None:
|
||||||
if obj:
|
if obj:
|
||||||
self.delete(key)
|
self.client.delete(key)
|
||||||
else:
|
else:
|
||||||
value = f()
|
value = f()
|
||||||
self.set(key, (time.time(), value))
|
self.client.set(key, (time.time(), value))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def increment(self, key, value=1):
|
def increment(self, key, value=1):
|
||||||
key = '%s/%s' % (self.request.application, key)
|
key = '%s/%s' % (self.request.application, key)
|
||||||
obj = self.get(key)
|
obj = self.client.get(key)
|
||||||
if obj:
|
if obj:
|
||||||
value = obj[1] + value
|
value = obj[1] + value
|
||||||
self.set((time.time(), value))
|
self.client.set(key, (time.time(), value))
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def clear(self, key):
|
def clear(self, key = None):
|
||||||
key = '%s/%s' % (self.request.application, key)
|
if key:
|
||||||
self.delete(key)
|
key = '%s/%s' % (self.request.application, key)
|
||||||
|
self.client.delete(key)
|
||||||
|
else:
|
||||||
|
self.client.flush_all()
|
||||||
|
|
||||||
|
def delete(self,*a,**b):
|
||||||
|
return self.client.delete(*a,**b)
|
||||||
|
|
||||||
|
def get(self,*a,**b):
|
||||||
|
return self.client.delete(*a,**b)
|
||||||
|
|
||||||
|
def set(self,*a,**b):
|
||||||
|
return self.client.delete(*a,**b)
|
||||||
|
|
||||||
|
def flush_all(self,*a,**b):
|
||||||
|
return self.client.delete(*a,**b)
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
# coding: utf8
|
# coding: utf8
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dropbox Authentication for web2py
|
Dropbox Authentication for web2py
|
||||||
Developed by Massimo Di Pierro (2012)
|
Developed by Massimo Di Pierro (2012)
|
||||||
Same License as Web2py License
|
Same License as Web2py License
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# mind here session is dropbox session, not current.session
|
# mind here session is dropbox session, not current.session
|
||||||
@@ -27,7 +27,7 @@ class DropboxAccount(object):
|
|||||||
key="...",
|
key="...",
|
||||||
secret="...",
|
secret="...",
|
||||||
access_type="...",
|
access_type="...",
|
||||||
url = "http://localhost:8000/%s/default/user/login" % request.application)
|
login_url = "http://localhost:8000/%s/default/user/login" % request.application)
|
||||||
when logged in
|
when logged in
|
||||||
client = auth.settings.login_form.client
|
client = auth.settings.login_form.client
|
||||||
"""
|
"""
|
||||||
@@ -40,7 +40,7 @@ class DropboxAccount(object):
|
|||||||
login_url = "",
|
login_url = "",
|
||||||
on_login_failure=None,
|
on_login_failure=None,
|
||||||
):
|
):
|
||||||
|
|
||||||
self.request=request
|
self.request=request
|
||||||
self.key=key
|
self.key=key
|
||||||
self.secret=secret
|
self.secret=secret
|
||||||
@@ -53,39 +53,48 @@ class DropboxAccount(object):
|
|||||||
|
|
||||||
def get_user(self):
|
def get_user(self):
|
||||||
request = self.request
|
request = self.request
|
||||||
token = current.session.dropbox_token
|
if not current.session.dropbox_request_token:
|
||||||
if not token:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
self.sess.set_token(token[0],token[1])
|
|
||||||
except:
|
|
||||||
# invalid token, should never happen
|
|
||||||
return None
|
return None
|
||||||
|
elif not current.session.dropbox_access_token:
|
||||||
|
|
||||||
|
request_token = current.session.dropbox_request_token
|
||||||
|
self.sess.set_request_token(request_token[0],request_token[1])
|
||||||
|
access_token = self.sess.obtain_access_token(self.sess.token)
|
||||||
|
current.session.dropbox_access_token = \
|
||||||
|
(access_token.key,access_token.secret)
|
||||||
else:
|
else:
|
||||||
user = Storage()
|
access_token = current.session.dropbox_access_token
|
||||||
self.client = client.DropboxClient(self.sess)
|
self.sess.set_token(access_token[0],access_token[1])
|
||||||
data = self.client.account_info()
|
|
||||||
display_name = data.get('display_name','').split(' ',1)
|
|
||||||
user = dict(email = data.get('email',None),
|
user = Storage()
|
||||||
first_name = display_name[0],
|
self.client = client.DropboxClient(self.sess)
|
||||||
last_name = display_name[-1],
|
data = self.client.account_info()
|
||||||
registration_id = data.get('uid',None))
|
display_name = data.get('display_name','').split(' ',1)
|
||||||
if not user['registration_id'] and self.on_login_failure:
|
user = dict(email = data.get('email',None),
|
||||||
redirect(self.on_login_failure)
|
first_name = display_name[0],
|
||||||
return user
|
last_name = display_name[-1],
|
||||||
|
registration_id = data.get('uid',None))
|
||||||
|
if not user['registration_id'] and self.on_login_failure:
|
||||||
|
redirect(self.on_login_failure)
|
||||||
|
return user
|
||||||
|
|
||||||
def login_form(self):
|
def login_form(self):
|
||||||
token = self.sess.obtain_request_token()
|
|
||||||
current.session.dropbox_token = (token.key,token.secret)
|
request_token = self.sess.obtain_request_token()
|
||||||
dropbox_url = self.sess.build_authorize_url(token,self.login_url)
|
current.session.dropbox_request_token = \
|
||||||
|
(request_token.key,request_token.secret)
|
||||||
|
dropbox_url = self.sess.build_authorize_url(request_token,
|
||||||
|
self.login_url)
|
||||||
redirect(dropbox_url)
|
redirect(dropbox_url)
|
||||||
form = IFRAME(_src=dropbox_url,
|
form = IFRAME(_src=dropbox_url,
|
||||||
_scrolling="no",
|
_scrolling="no",
|
||||||
_frameborder="no",
|
_frameborder="no",
|
||||||
_style="width:400px;height:240px;")
|
_style="width:400px;height:240px;")
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def logout_url(self, next = "/"):
|
def logout_url(self, next = "/"):
|
||||||
current.session.dropbox_token=None
|
current.session.dropbox_request_token=None
|
||||||
current.session.auth=None
|
current.session.auth=None
|
||||||
redirect('https://www.dropbox.com/logout')
|
redirect('https://www.dropbox.com/logout')
|
||||||
return next
|
return next
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
|||||||
path_info = next
|
path_info = next
|
||||||
else:
|
else:
|
||||||
path_info = r.env.path_info
|
path_info = r.env.path_info
|
||||||
uri = '%s://%s%s' %(url_scheme, http_host, path_info)
|
uri = '%s://%s%s' % (url_scheme, http_host, path_info)
|
||||||
if r.get_vars and not next:
|
if r.get_vars and not next:
|
||||||
uri += '?' + urlencode(r.get_vars)
|
uri += '?' + urlencode(r.get_vars)
|
||||||
return uri
|
return uri
|
||||||
|
|||||||
@@ -26,8 +26,10 @@ class MemcacheClientObj(Client):
|
|||||||
|
|
||||||
def __init__(self, request, servers, debug=0, pickleProtocol=0,
|
def __init__(self, request, servers, debug=0, pickleProtocol=0,
|
||||||
pickler=pickle.Pickler, unpickler=pickle.Unpickler,
|
pickler=pickle.Pickler, unpickler=pickle.Unpickler,
|
||||||
pload=None, pid=None):
|
pload=None, pid=None,
|
||||||
|
default_time_expire = DEFAULT_TIME_EXPIRE):
|
||||||
self.request=request
|
self.request=request
|
||||||
|
self.default_time_expire = default_time_expire
|
||||||
if request:
|
if request:
|
||||||
app = request.application
|
app = request.application
|
||||||
else:
|
else:
|
||||||
@@ -43,8 +45,9 @@ class MemcacheClientObj(Client):
|
|||||||
else:
|
else:
|
||||||
self.storage = self.meta_storage[app]
|
self.storage = self.meta_storage[app]
|
||||||
|
|
||||||
def __call__(self, key, f,
|
def __call__(self, key, f, time_expire = 'default'):
|
||||||
time_expire=DEFAULT_TIME_EXPIRE):
|
if time_expire == 'default':
|
||||||
|
time_expire = self.default_time_expire
|
||||||
if time_expire == None:
|
if time_expire == None:
|
||||||
time_expire = self.max_time_expire
|
time_expire = self.max_time_expire
|
||||||
# this must be commented because get and set are redefined
|
# this must be commented because get and set are redefined
|
||||||
@@ -70,8 +73,10 @@ class MemcacheClientObj(Client):
|
|||||||
self.set(key, (now,value), self.max_time_expire)
|
self.set(key, (now,value), self.max_time_expire)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def increment(self, key, value=1, time_expire=DEFAULT_TIME_EXPIRE):
|
def increment(self, key, value=1, time_expire='default'):
|
||||||
""" time_expire is ignored """
|
""" time_expire is ignored """
|
||||||
|
if time_expire == 'default':
|
||||||
|
time_expire = self.default_time_expire
|
||||||
newKey = self.__keyFormat__(key)
|
newKey = self.__keyFormat__(key)
|
||||||
obj = Client.get(self, newKey)
|
obj = Client.get(self, newKey)
|
||||||
if obj:
|
if obj:
|
||||||
@@ -86,7 +91,9 @@ class MemcacheClientObj(Client):
|
|||||||
Client.set(self, newKey, value, self.max_time_expire)
|
Client.set(self, newKey, value, self.max_time_expire)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def set(self, key, value, time_expire=DEFAULT_TIME_EXPIRE):
|
def set(self, key, value, time_expire='default'):
|
||||||
|
if time_expire == 'default':
|
||||||
|
time_expire = self.default_time_expire
|
||||||
newKey = self.__keyFormat__(key)
|
newKey = self.__keyFormat__(key)
|
||||||
return Client.set(self, newKey, value, time_expire)
|
return Client.set(self, newKey, value, time_expire)
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import cssmin
|
|||||||
import jsmin
|
import jsmin
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import re
|
||||||
|
|
||||||
def read_binary_file(filename):
|
def read_binary_file(filename):
|
||||||
f = open(filename,'rb')
|
f = open(filename,'rb')
|
||||||
@@ -24,9 +25,10 @@ def write_binary_file(filename,data):
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def fix_links(css,static_path):
|
def fix_links(css,static_path):
|
||||||
return css.replace('../',static_path+'/')
|
return re.sub(r'url\((["\'])\.\./', 'url(\\1' + static_path, css)
|
||||||
|
|
||||||
|
|
||||||
def minify(files, path_info, folder, optimize_css, optimize_js,
|
def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||||
ignore_concat = [],
|
ignore_concat = [],
|
||||||
ignore_minify = ['/jquery.js', '/anytime.js']):
|
ignore_minify = ['/jquery.js', '/anytime.js']):
|
||||||
@@ -60,17 +62,30 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
processed = []
|
processed = []
|
||||||
for k,filename in enumerate(files):
|
for k,filename in enumerate(files):
|
||||||
if not filename.startswith('/') or \
|
if not filename.startswith('/') or \
|
||||||
any(filename.endswith(x) for x in ignore_concat):
|
any(filename.endswith(x) \
|
||||||
|
for x in ignore_concat):
|
||||||
new_files.append(filename)
|
new_files.append(filename)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
abs_filename = os.path.join(folder,'static',
|
abs_filename = os.path.join(
|
||||||
filename[len(static_path)+1:])
|
folder,'static', filename[len(static_path)+1:])
|
||||||
|
|
||||||
if filename.lower().endswith('.css'):
|
if filename.lower().endswith('.css'):
|
||||||
processed.append(filename)
|
processed.append(filename)
|
||||||
|
spath_info, sfilename = \
|
||||||
|
path_info.split('/'), filename.split('/')
|
||||||
|
u = 0
|
||||||
|
for i,a in enumerate(sfilename):
|
||||||
|
try:
|
||||||
|
if a != spath_info[i]:
|
||||||
|
u = i
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
if concat_css:
|
if concat_css:
|
||||||
contents = read_binary_file(abs_filename)
|
contents = read_binary_file(abs_filename)
|
||||||
|
replacement = '/'.join(spath_info[:u]) + '/'
|
||||||
|
contents = fix_links(contents, replacement)
|
||||||
if minify_css:
|
if minify_css:
|
||||||
css.append(cssmin.cssmin(contents))
|
css.append(cssmin.cssmin(contents))
|
||||||
else:
|
else:
|
||||||
@@ -80,9 +95,12 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
elif filename.lower().endswith('.js'):
|
elif filename.lower().endswith('.js'):
|
||||||
processed.append(filename)
|
processed.append(filename)
|
||||||
if concat_js:
|
if concat_js:
|
||||||
contents = read_binary_file(abs_filename)
|
contents = read_binary_file(abs_filename)
|
||||||
if minify_js and not filename.endswith('.min.js') and \
|
|
||||||
not any(filename.endswith(x) for x in ignore_minify):
|
if minify_js and \
|
||||||
|
not filename.endswith('.min.js') and \
|
||||||
|
not any(filename.endswith(x) \
|
||||||
|
for x in ignore_minify):
|
||||||
js.append(jsmin.jsmin(contents))
|
js.append(jsmin.jsmin(contents))
|
||||||
else:
|
else:
|
||||||
js.append(contents)
|
js.append(contents)
|
||||||
@@ -91,16 +109,17 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
dest_key = hashlib.md5(repr(processed)).hexdigest()
|
dest_key = hashlib.md5(repr(processed)).hexdigest()
|
||||||
if css and concat_css:
|
if css and concat_css:
|
||||||
css = '\n\n'.join(contents for contents in css)
|
css = '\n\n'.join(contents for contents in css)
|
||||||
if inline_css:
|
if not inline_css:
|
||||||
css = ('css:inline',fix_links(css,static_path))
|
|
||||||
else:
|
|
||||||
temppath = os.path.join(folder,'static',temp)
|
temppath = os.path.join(folder,'static',temp)
|
||||||
if not os.path.exists(temppath): os.mkdir(temppath)
|
if not os.path.exists(temppath):
|
||||||
|
os.mkdir(temppath)
|
||||||
dest = "compressed_%s.css" % dest_key
|
dest = "compressed_%s.css" % dest_key
|
||||||
tempfile = os.path.join(temppath, dest)
|
tempfile = os.path.join(temppath, dest)
|
||||||
write_binary_file(tempfile,css)
|
write_binary_file(tempfile,css)
|
||||||
css = path_info+'/%s' % dest
|
css = path_info+'/%s' % dest
|
||||||
new_files.append(css)
|
new_files.append(css)
|
||||||
|
else:
|
||||||
|
new_files.append(('css:inline',css))
|
||||||
else:
|
else:
|
||||||
new_files += css
|
new_files += css
|
||||||
if js and concat_js:
|
if js and concat_js:
|
||||||
@@ -109,7 +128,8 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
|||||||
js = ('js:inline',js)
|
js = ('js:inline',js)
|
||||||
else:
|
else:
|
||||||
temppath = os.path.join(folder,'static',temp)
|
temppath = os.path.join(folder,'static',temp)
|
||||||
if not os.path.exists(temppath): os.mkdir(temppath)
|
if not os.path.exists(temppath):
|
||||||
|
os.mkdir(temppath)
|
||||||
dest = "compressed_%s.js" % dest_key
|
dest = "compressed_%s.js" % dest_key
|
||||||
tempfile = os.path.join(folder,'static',temp,dest)
|
tempfile = os.path.join(folder,'static',temp,dest)
|
||||||
write_binary_file(tempfile,js)
|
write_binary_file(tempfile,js)
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Copyright (c) 2006-2012 James Tauber and contributors
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# pyuca: Python Unicode Collation Algorithm implementation
|
||||||
|
(http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/)
|
||||||
|
|
||||||
|
This is my preliminary attempt at a Python implementation of the
|
||||||
|
[Unicode Collation Algorithm (UCA)](http://unicode.org/reports/tr10/).
|
||||||
|
I originally posted it to my blog in 2006 but it seems to get enough
|
||||||
|
usage it really belongs here (and in PyPI).
|
||||||
|
|
||||||
|
What do you use it for? In short, sorting non-English strings properly.
|
||||||
|
|
||||||
|
The core of the algorithm involves multi-level comparison. For example,
|
||||||
|
``café`` comes before ``caff`` because at the primary level, the accent
|
||||||
|
is ignored and the first word is treated as if it were ``cafe``.
|
||||||
|
The secondary level (which considers accents) only applies then to words
|
||||||
|
that are equivalent at the primary level.
|
||||||
|
|
||||||
|
The Unicode Collation Algorithm and pyuca also support contraction and
|
||||||
|
expansion. **Contraction** is where multiple letters are treated as a
|
||||||
|
single unit. In Spanish, ``ch`` is treated as a letter coming between
|
||||||
|
``c`` and ``d`` so that, for example, words beginning ``ch`` should
|
||||||
|
sort after all other words beginnings with ``c``. **Expansion** is where
|
||||||
|
a single letter is treated as though it were multiple letters. In German,
|
||||||
|
``ä`` is sorted as if it were ``ae``, i.e. after ``ad`` but before ``af``.
|
||||||
|
|
||||||
|
## Here is how to use the ``pyuca`` module:
|
||||||
|
``
|
||||||
|
git clone https://github.com/jtauber/pyuca.git
|
||||||
|
cd pyuca
|
||||||
|
pip install pyuca
|
||||||
|
``
|
||||||
|
|
||||||
|
**Usage example:**
|
||||||
|
``
|
||||||
|
from pyuca import Collator
|
||||||
|
c = Collator("allkeys.txt")
|
||||||
|
|
||||||
|
sorted_words = sorted(words, key=c.sort_key)
|
||||||
|
``
|
||||||
|
|
||||||
|
``allkeys.txt`` (1 MB) is available at
|
||||||
|
|
||||||
|
http://www.unicode.org/Public/UCA/latest/allkeys.txt
|
||||||
|
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import pyuca
|
import pyuca
|
||||||
|
|
||||||
unicode_collator = pyuca.Collator(os.path.join(os.path.dirname(__file__), 'allkeys.txt'))
|
unicode_collator = None
|
||||||
|
|
||||||
|
def set_unicode_collator(file):
|
||||||
|
global unicode_collator
|
||||||
|
unicode_collator = pyuca.Collator(file)
|
||||||
|
|
||||||
|
set_unicode_collator(os.path.join(os.path.dirname(__file__), 'allkeys.txt'))
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
# pyuca - Unicode Collation Algorithm
|
# pyuca - Unicode Collation Algorithm
|
||||||
# Version: 2006-02-13
|
# Version: 2012-06-21
|
||||||
#
|
#
|
||||||
# James Tauber
|
# James Tauber
|
||||||
# http://jtauber.com/
|
# http://jtauber.com/
|
||||||
|
|
||||||
# Copyright (c) 2006 James Tauber
|
# Copyright (c) 2006-2012 James Tauber and contributors
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
# in the Software without restriction, including without limitation the rights
|
# in the Software without restriction, including without limitation the rights
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
# furnished to do so, subject to the following conditions:
|
# furnished to do so, subject to the following conditions:
|
||||||
#
|
#
|
||||||
# The above copyright notice and this permission notice shall be included in
|
# The above copyright notice and this permission notice shall be included in
|
||||||
# all copies or substantial portions of the Software.
|
# all copies or substantial portions of the Software.
|
||||||
#
|
#
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
@@ -28,7 +28,6 @@
|
|||||||
"""
|
"""
|
||||||
Preliminary implementation of the Unicode Collation Algorithm.
|
Preliminary implementation of the Unicode Collation Algorithm.
|
||||||
|
|
||||||
|
|
||||||
This only implements the simple parts of the algorithm but I have successfully
|
This only implements the simple parts of the algorithm but I have successfully
|
||||||
tested it using the Default Unicode Collation Element Table (DUCET) to collate
|
tested it using the Default Unicode Collation Element Table (DUCET) to collate
|
||||||
Ancient Greek correctly.
|
Ancient Greek correctly.
|
||||||
@@ -48,31 +47,39 @@ but you can always subset this for just the characters you are dealing with.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Trie:
|
class Node:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.root = [None, {}]
|
self.value = None
|
||||||
|
self.children = {}
|
||||||
|
|
||||||
|
|
||||||
|
class Trie:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.root = Node()
|
||||||
|
|
||||||
def add(self, key, value):
|
def add(self, key, value):
|
||||||
curr_node = self.root
|
curr_node = self.root
|
||||||
for part in key:
|
for part in key:
|
||||||
curr_node = curr_node[1].setdefault(part, [None, {}])
|
curr_node = curr_node.children.setdefault(part, Node())
|
||||||
curr_node[0] = value
|
curr_node.value = value
|
||||||
|
|
||||||
def find_prefix(self, key):
|
def find_prefix(self, key):
|
||||||
curr_node = self.root
|
curr_node = self.root
|
||||||
remainder = key
|
remainder = key
|
||||||
for part in key:
|
for part in key:
|
||||||
if part not in curr_node[1]:
|
if part not in curr_node.children:
|
||||||
break
|
break
|
||||||
curr_node = curr_node[1][part]
|
curr_node = curr_node.children[part]
|
||||||
remainder = remainder[1:]
|
remainder = remainder[1:]
|
||||||
return (curr_node[0], remainder)
|
return (curr_node.value, remainder)
|
||||||
|
|
||||||
|
|
||||||
class Collator:
|
class Collator:
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
|
|
||||||
self.table = Trie()
|
self.table = Trie()
|
||||||
self.load(filename)
|
self.load(filename)
|
||||||
|
|
||||||
@@ -85,7 +92,7 @@ class Collator:
|
|||||||
line = line[:line.find("#")] + "\n"
|
line = line[:line.find("#")] + "\n"
|
||||||
line = line[:line.find("%")] + "\n"
|
line = line[:line.find("%")] + "\n"
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
|
|
||||||
if line.startswith("@"):
|
if line.startswith("@"):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -96,32 +103,36 @@ class Collator:
|
|||||||
while True:
|
while True:
|
||||||
begin = x.find("[")
|
begin = x.find("[")
|
||||||
if begin == -1:
|
if begin == -1:
|
||||||
break
|
break
|
||||||
end = x[begin:].find("]")
|
end = x[begin:].find("]")
|
||||||
collElement = x[begin:begin+end+1]
|
collElement = x[begin:begin+end+1]
|
||||||
x = x[begin + 1:]
|
x = x[begin + 1:]
|
||||||
|
|
||||||
alt = collElement[1]
|
alt = collElement[1]
|
||||||
chars = collElement[2:-1].split(".")
|
chars = collElement[2:-1].split(".")
|
||||||
|
|
||||||
collElements.append((alt, chars))
|
collElements.append((alt, chars))
|
||||||
integer_points = [int(ch, 16) for ch in charList]
|
integer_points = [int(ch, 16) for ch in charList]
|
||||||
self.table.add(integer_points, collElements)
|
self.table.add(integer_points, collElements)
|
||||||
|
|
||||||
def sort_key(self, string):
|
def sort_key(self, string):
|
||||||
|
|
||||||
collation_elements = []
|
collation_elements = []
|
||||||
|
|
||||||
lookup_key = [ord(ch) for ch in string]
|
lookup_key = [ord(ch) for ch in string]
|
||||||
while lookup_key:
|
while lookup_key:
|
||||||
value, lookup_key = self.table.find_prefix(lookup_key)
|
value, lookup_key = self.table.find_prefix(lookup_key)
|
||||||
if not value:
|
if not value:
|
||||||
# @@@
|
# Calculate implicit weighting for CJK Ideographs
|
||||||
raise ValueError, map(hex, lookup_key)
|
# contributed by David Schneider 2009-07-27
|
||||||
|
# http://www.unicode.org/reports/tr10/#Implicit_Weights
|
||||||
|
value = []
|
||||||
|
value.append((".", ["%X" % (0xFB40 + (lookup_key[0] >> 15)), "0020", "0002", "0001"]))
|
||||||
|
value.append((".", ["%X" % ((lookup_key[0] & 0x7FFF) | 0x8000), "0000", "0000", "0000"]))
|
||||||
|
lookup_key = lookup_key[1:]
|
||||||
collation_elements.extend(value)
|
collation_elements.extend(value)
|
||||||
|
|
||||||
sort_key = []
|
sort_key = []
|
||||||
|
|
||||||
for level in range(4):
|
for level in range(4):
|
||||||
if level:
|
if level:
|
||||||
sort_key.append(0) # level separator
|
sort_key.append(0) # level separator
|
||||||
@@ -129,5 +140,5 @@ class Collator:
|
|||||||
ce_l = int(element[1][level], 16)
|
ce_l = int(element[1][level], 16)
|
||||||
if ce_l:
|
if ce_l:
|
||||||
sort_key.append(ce_l)
|
sort_key.append(ce_l)
|
||||||
|
|
||||||
return tuple(sort_key)
|
return tuple(sort_key)
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
"""
|
||||||
|
Developed by niphlod@gmail.com
|
||||||
|
"""
|
||||||
|
|
||||||
|
import redis
|
||||||
|
from redis.exceptions import ConnectionError
|
||||||
|
from gluon import current
|
||||||
|
from gluon.storage import Storage
|
||||||
|
import cPickle as pickle
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import logging
|
||||||
|
import thread
|
||||||
|
|
||||||
|
logger = logging.getLogger("web2py.session.redis")
|
||||||
|
|
||||||
|
locker = thread.allocate_lock()
|
||||||
|
|
||||||
|
def RedisSession(*args, **vars):
|
||||||
|
"""
|
||||||
|
Usage example: put in models
|
||||||
|
from gluon.contrib.redis_session import RedisSession
|
||||||
|
sessiondb = RedisSession('localhost:6379',db=0, session_expiry=False)
|
||||||
|
session.connect(request, response, db = sessiondb)
|
||||||
|
|
||||||
|
Simple slip-in storage for session
|
||||||
|
"""
|
||||||
|
|
||||||
|
locker.acquire()
|
||||||
|
try:
|
||||||
|
if not hasattr(RedisSession, 'redis_instance'):
|
||||||
|
RedisSession.redis_instance = RedisClient(*args, **vars)
|
||||||
|
finally:
|
||||||
|
locker.release()
|
||||||
|
return RedisSession.redis_instance
|
||||||
|
|
||||||
|
|
||||||
|
class RedisClient(object):
|
||||||
|
|
||||||
|
meta_storage = {}
|
||||||
|
MAX_RETRIES = 5
|
||||||
|
RETRIES = 0
|
||||||
|
|
||||||
|
def __init__(self, server='localhost:6379', db=None, debug=False, session_expiry=False):
|
||||||
|
"""session_expiry can be an integer, in seconds, to set the default expiration
|
||||||
|
of sessions. The corresponding record will be deleted from the redis instance,
|
||||||
|
and there's virtually no need to run sessions2trash.py
|
||||||
|
"""
|
||||||
|
self.server = server
|
||||||
|
self.db = db or 0
|
||||||
|
host,port = (self.server.split(':')+['6379'])[:2]
|
||||||
|
port = int(port)
|
||||||
|
self.debug = debug
|
||||||
|
if current and current.request:
|
||||||
|
self.app = current.request.application
|
||||||
|
else:
|
||||||
|
self.app = ''
|
||||||
|
self.r_server = redis.Redis(host=host, port=port, db=self.db)
|
||||||
|
self.tablename = None
|
||||||
|
self.session_expiry = session_expiry
|
||||||
|
|
||||||
|
def get(self, what, default):
|
||||||
|
return self.tablename
|
||||||
|
|
||||||
|
def Field(self, fieldname, type='string', length=None, default=None,
|
||||||
|
required=False,requires=None):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def define_table(self,tablename,*fields,**args):
|
||||||
|
if not self.tablename:
|
||||||
|
self.tablename = MockTable(self, self.r_server, tablename, self.session_expiry)
|
||||||
|
return self.tablename
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return self.tablename
|
||||||
|
|
||||||
|
def __call__(self, where=''):
|
||||||
|
q = self.tablename.query
|
||||||
|
return q
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
#this is only called by session2trash.py
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MockTable(object):
|
||||||
|
|
||||||
|
def __init__(self, db, r_server, tablename, session_expiry):
|
||||||
|
self.db = db
|
||||||
|
self.r_server = r_server
|
||||||
|
self.tablename = tablename
|
||||||
|
#set the namespace for sessions of this app
|
||||||
|
self.keyprefix = 'w2p:sess:%s' % tablename.replace('web2py_session_', '')
|
||||||
|
#fast auto-increment id (needed for session handling)
|
||||||
|
self.serial = "%s:serial" % self.keyprefix
|
||||||
|
#index of all the session keys of this app
|
||||||
|
self.id_idx = "%s:id_idx" % self.keyprefix
|
||||||
|
#remember the session_expiry setting
|
||||||
|
self.session_expiry = session_expiry
|
||||||
|
|
||||||
|
def getserial(self):
|
||||||
|
#return an auto-increment id
|
||||||
|
return "%s" % self.r_server.incr(self.serial, 1)
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
if key == 'id':
|
||||||
|
#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)
|
||||||
|
return self.query
|
||||||
|
elif key == '_db':
|
||||||
|
#needed because of the calls in sessions2trash.py and globals.py
|
||||||
|
return self.db
|
||||||
|
|
||||||
|
def insert(self, **kwargs):
|
||||||
|
#usually kwargs would be a Storage with several keys:
|
||||||
|
#'locked', 'client_ip','created_datetime','modified_datetime'
|
||||||
|
#'unique_key', 'session_data'
|
||||||
|
#retrieve a new key
|
||||||
|
newid = self.getserial()
|
||||||
|
key = "%s:%s" % (self.keyprefix, newid)
|
||||||
|
#add it to the index
|
||||||
|
self.r_server.sadd(self.id_idx, key)
|
||||||
|
#set a hash key with the Storage
|
||||||
|
self.r_server.hmset(key, kwargs)
|
||||||
|
if self.session_expiry:
|
||||||
|
self.r_server.expire(key, self.session_expiry)
|
||||||
|
return newid
|
||||||
|
|
||||||
|
class MockQuery(object):
|
||||||
|
"""a fake Query object that supports querying by id
|
||||||
|
and listing all keys. No other operation is supported
|
||||||
|
"""
|
||||||
|
def __init__(self, field=None, db=None, prefix=None, session_expiry=False):
|
||||||
|
self.field = field
|
||||||
|
self.value = None
|
||||||
|
self.db = db
|
||||||
|
self.keyprefix = prefix
|
||||||
|
self.op = None
|
||||||
|
self.session_expiry = session_expiry
|
||||||
|
|
||||||
|
def __eq__(self, value, op='eq'):
|
||||||
|
self.value = value
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def __gt__(self, value, op='ge'):
|
||||||
|
self.value = value
|
||||||
|
self.op = op
|
||||||
|
|
||||||
|
def select(self):
|
||||||
|
if self.op == 'eq' and self.field == 'id' and self.value:
|
||||||
|
#means that someone wants to retrieve the key self.value
|
||||||
|
rtn = self.db.hgetall("%s:%s" % (self.keyprefix, self.value))
|
||||||
|
if rtn == dict():
|
||||||
|
#return an empty resultset for non existing key
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return [Storage(rtn)]
|
||||||
|
elif self.op == 'ge' and self.field == 'id' and self.value == 0:
|
||||||
|
#means that someone wants the complete list
|
||||||
|
rtn = []
|
||||||
|
id_idx = "%s:id_idx" % self.keyprefix
|
||||||
|
#find all session keys of this app
|
||||||
|
allkeys = self.db.smembers(id_idx)
|
||||||
|
for sess in allkeys:
|
||||||
|
val = self.db.hgetall(sess)
|
||||||
|
if val == dict():
|
||||||
|
if self.session_expiry:
|
||||||
|
#clean up the idx, because the key expired
|
||||||
|
self.db.srem(id_idx, sess)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
val = Storage(val)
|
||||||
|
#add a delete_record method (necessary for sessions2trash.py)
|
||||||
|
val.delete_record = RecordDeleter(self.db, sess, self.keyprefix)
|
||||||
|
rtn.append(val)
|
||||||
|
return rtn
|
||||||
|
else:
|
||||||
|
raise Exception("Operation not supported")
|
||||||
|
|
||||||
|
def update(self, **kwargs):
|
||||||
|
#means that the session has been found and needs an update
|
||||||
|
if self.op == 'eq' and self.field == 'id' and self.value:
|
||||||
|
rtn = self.db.hmset("%s:%s" % (self.keyprefix, self.value), kwargs)
|
||||||
|
if self.session_expiry:
|
||||||
|
self.db.expire(key, self.session.expiry)
|
||||||
|
return rtn
|
||||||
|
|
||||||
|
class RecordDeleter(object):
|
||||||
|
"""Dumb record deleter to support sessions2trash.py"""
|
||||||
|
|
||||||
|
def __init__(self, db, key, keyprefix):
|
||||||
|
self.db, self.key, self.keyprefix = db, key, keyprefix
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
id_idx = "%s:id_idx" % self.keyprefix
|
||||||
|
#remove from the index
|
||||||
|
self.db.srem(id_idx, self.key)
|
||||||
|
#remove the key itself
|
||||||
|
self.db.delete(self.key)
|
||||||
|
|
||||||
|
|
||||||
@@ -12,18 +12,18 @@ Attention: Requires Chrome or Safari. For IE of Firefox you need https://github.
|
|||||||
|
|
||||||
2) start this app:
|
2) start this app:
|
||||||
|
|
||||||
python gluon/contrib/comet_messaging.py -k mykey -p 8888
|
python gluon/contrib/websocket_messaging.py -k mykey -p 8888
|
||||||
|
|
||||||
3) from any web2py app you can post messages with
|
3) from any web2py app you can post messages with
|
||||||
|
|
||||||
from gluon.contrib.comet_messaging import comet_send
|
from gluon.contrib.websocket_messaging import websocket_send
|
||||||
comet_send('http://127.0.0.1:8888','Hello World','mykey','mygroup')
|
websocket_send('http://127.0.0.1:8888','Hello World','mykey','mygroup')
|
||||||
|
|
||||||
4) from any template you can receive them with
|
4) from any template you can receive them with
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
if(!web2py_comet('ws://127.0.0.1:8888/realtime/mygroup',function(e){alert(e.data)}))
|
if(!web2py_websocket('ws://127.0.0.1:8888/realtime/mygroup',function(e){alert(e.data)}))
|
||||||
alert("html5 websocket not supported by your browser, try Google Chrome");
|
alert("html5 websocket not supported by your browser, try Google Chrome");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -34,14 +34,14 @@ Or if you want to send json messages and store evaluated json in a var called da
|
|||||||
<script>
|
<script>
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
var data;
|
var data;
|
||||||
web2py_comet('ws://127.0.0.1:8888/realtime/mygroup',function(e){data=eval('('+e.data+')')});
|
web2py_websocket('ws://127.0.0.1:8888/realtime/mygroup',function(e){data=eval('('+e.data+')')});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
- All communications between web2py and comet_messaging will be digitally signed with hmac.
|
- All communications between web2py and websocket_messaging will be digitally signed with hmac.
|
||||||
- All validation is handled on the web2py side and there is no need to modify comet_messaging.py
|
- All validation is handled on the web2py side and there is no need to modify websocket_messaging.py
|
||||||
- Multiple web2py instances can talk with one or more comet_messaging servers.
|
- Multiple web2py instances can talk with one or more websocket_messaging servers.
|
||||||
- "ws://127.0.0.1:8888/realtime/" must be contain the IP of the comet_messaging server.
|
- "ws://127.0.0.1:8888/realtime/" must be contain the IP of the websocket_messaging server.
|
||||||
- Via group='mygroup' name you can support multiple groups of clients (think of many chat-rooms)
|
- Via group='mygroup' name you can support multiple groups of clients (think of many chat-rooms)
|
||||||
|
|
||||||
Here is a complete sample web2py action:
|
Here is a complete sample web2py action:
|
||||||
@@ -51,7 +51,7 @@ Here is a complete sample web2py action:
|
|||||||
script=SCRIPT('''
|
script=SCRIPT('''
|
||||||
jQuery(document).ready(function(){
|
jQuery(document).ready(function(){
|
||||||
var callback=function(e){alert(e.data)};
|
var callback=function(e){alert(e.data)};
|
||||||
if(!web2py_comet('ws://127.0.0.1:8888/realtime/mygroup',callback))
|
if(!web2py_websocket('ws://127.0.0.1:8888/realtime/mygroup',callback))
|
||||||
alert("html5 websocket not supported by your browser, try Google Chrome");
|
alert("html5 websocket not supported by your browser, try Google Chrome");
|
||||||
});
|
});
|
||||||
''')
|
''')
|
||||||
@@ -60,8 +60,8 @@ Here is a complete sample web2py action:
|
|||||||
def ajax_form():
|
def ajax_form():
|
||||||
form=SQLFORM.factory(Field('message'))
|
form=SQLFORM.factory(Field('message'))
|
||||||
if form.accepts(request,session):
|
if form.accepts(request,session):
|
||||||
from gluon.contrib.comet_messaging import comet_send
|
from gluon.contrib.websocket_messaging import websocket_send
|
||||||
comet_send('http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
|
websocket_send('http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
|
||||||
return form
|
return form
|
||||||
|
|
||||||
Acknowledgements:
|
Acknowledgements:
|
||||||
@@ -83,7 +83,7 @@ listeners = {}
|
|||||||
names = {}
|
names = {}
|
||||||
tokens = {}
|
tokens = {}
|
||||||
|
|
||||||
def comet_send(url,message,hmac_key=None,group='default'):
|
def websocket_send(url,message,hmac_key=None,group='default'):
|
||||||
sig = hmac_key and hmac.new(hmac_key,message).hexdigest() or ''
|
sig = hmac_key and hmac.new(hmac_key,message).hexdigest() or ''
|
||||||
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
|
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
|
||||||
f = urllib.urlopen(url, params)
|
f = urllib.urlopen(url, params)
|
||||||
+94
-239
@@ -6,153 +6,137 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
|
import traceback
|
||||||
|
from gluon import current
|
||||||
|
|
||||||
# Install the new import function:
|
NATIVE_IMPORTER = __builtin__.__import__
|
||||||
def custom_import_install(web2py_path):
|
INVALID_MODULES = set(('','gluon','applications','custom_import'))
|
||||||
global _web2py_importer
|
|
||||||
global _web2py_path
|
|
||||||
if isinstance(__builtin__.__import__, _Web2pyImporter):
|
|
||||||
return #aready installed
|
|
||||||
_web2py_path = web2py_path
|
|
||||||
_web2py_importer = _Web2pyImporter(web2py_path)
|
|
||||||
__builtin__.__import__ = _web2py_importer
|
|
||||||
|
|
||||||
def is_tracking_changes():
|
# backward compatibility API
|
||||||
"""
|
def custom_import_install():
|
||||||
@return: True: neo_importer is tracking changes made to Python source
|
if __builtin__.__import__ != custom_importer:
|
||||||
files. False: neo_import does not reload Python modules.
|
INVALID_MODULES.update(sys.modules.keys())
|
||||||
"""
|
__builtin__.__import__ = custom_importer
|
||||||
|
|
||||||
global _is_tracking_changes
|
|
||||||
return _is_tracking_changes
|
|
||||||
|
|
||||||
def track_changes(track=True):
|
def track_changes(track=True):
|
||||||
|
assert track in (True,False), "must be True or False"
|
||||||
|
current.request._custom_import_track_changes = track
|
||||||
|
|
||||||
|
def is_tracking_changes():
|
||||||
|
return current.request._custom_import_track_changes
|
||||||
|
|
||||||
|
class CustomImportException(ImportError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
|
||||||
"""
|
"""
|
||||||
Tell neo_importer to start/stop tracking changes made to Python modules.
|
The web2py custom importer. Like the standard Python importer but it
|
||||||
@param track: True: Start tracking changes. False: Stop tracking changes.
|
tries to transform import statements as something like
|
||||||
|
"import applications.app_name.modules.x".
|
||||||
|
If the import failed, fall back on naive_importer
|
||||||
"""
|
"""
|
||||||
|
|
||||||
global _is_tracking_changes
|
globals = globals or {}
|
||||||
global _web2py_importer
|
locals = locals or {}
|
||||||
global _web2py_date_tracker_importer
|
fromlist = fromlist or []
|
||||||
assert track is True or track is False, "Boolean expected."
|
|
||||||
if track == _is_tracking_changes:
|
|
||||||
return
|
|
||||||
if track:
|
|
||||||
if not _web2py_date_tracker_importer:
|
|
||||||
_web2py_date_tracker_importer = \
|
|
||||||
_Web2pyDateTrackerImporter(_web2py_path)
|
|
||||||
__builtin__.__import__ = _web2py_date_tracker_importer
|
|
||||||
else:
|
|
||||||
__builtin__.__import__ = _web2py_importer
|
|
||||||
_is_tracking_changes = track
|
|
||||||
|
|
||||||
_STANDARD_PYTHON_IMPORTER = __builtin__.__import__ # Keep standard importer
|
try:
|
||||||
_web2py_importer = None # The standard web2py importer
|
if current.request._custom_import_track_changes:
|
||||||
_web2py_date_tracker_importer = None # The web2py importer with date tracking
|
base_importer = TRACK_IMPORTER
|
||||||
_web2py_path = None # Absolute path of the web2py directory
|
else:
|
||||||
|
base_importer = NATIVE_IMPORTER
|
||||||
|
except: # there is no current.request (should never happen)
|
||||||
|
base_importer = NATIVE_IMPORTER
|
||||||
|
|
||||||
_is_tracking_changes = False # The tracking mode
|
# if not relative and not from applications:
|
||||||
|
if hasattr(current,'request') \
|
||||||
|
and level<=0 \
|
||||||
|
and not name.split('.')[0] in INVALID_MODULES \
|
||||||
|
and isinstance(globals, dict):
|
||||||
|
import_tb = None
|
||||||
|
try:
|
||||||
|
items = current.request.folder.split(os.path.sep)
|
||||||
|
if not items[-1]: items = items[:-1]
|
||||||
|
modules_prefix = '.'.join(items[-2:])+'.modules'
|
||||||
|
if not fromlist:
|
||||||
|
# import like "import x" or "import x.y"
|
||||||
|
result = None
|
||||||
|
for itemname in name.split("."):
|
||||||
|
new_mod = base_importer(
|
||||||
|
modules_prefix, globals,locals, [itemname], level)
|
||||||
|
try:
|
||||||
|
result = result or new_mod.__dict__[itemname]
|
||||||
|
except KeyError, e:
|
||||||
|
raise ImportError, 'Cannot import module %s' % str(e)
|
||||||
|
modules_prefix += "." + itemname
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
# import like "from x import a, b, ..."
|
||||||
|
pname = modules_prefix + "." + name
|
||||||
|
return base_importer(pname, globals, locals, fromlist, level)
|
||||||
|
except ImportError, e1:
|
||||||
|
import_tb = sys.exc_info()[2]
|
||||||
|
try:
|
||||||
|
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
|
||||||
|
except ImportError, e3:
|
||||||
|
raise ImportError, e1, import_tb # there an import error in the module
|
||||||
|
except Exception, e2:
|
||||||
|
raise e2 # there is an error in the module
|
||||||
|
finally:
|
||||||
|
if import_tb:
|
||||||
|
import_tb = None
|
||||||
|
|
||||||
class _BaseImporter(object):
|
return NATIVE_IMPORTER(name,globals,locals,fromlist,level)
|
||||||
"""
|
|
||||||
The base importer. Dispatch the import the call to the standard Python
|
|
||||||
importer.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def begin(self):
|
|
||||||
"""
|
|
||||||
Many imports can be made for a single import statement. This method
|
|
||||||
help the management of this aspect.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._STANDARD_PYTHON_IMPORTER = _STANDARD_PYTHON_IMPORTER
|
|
||||||
def __call__(self, name, globals=None, locals=None,
|
|
||||||
fromlist=None, level=-1):
|
|
||||||
"""
|
|
||||||
The import method itself.
|
|
||||||
"""
|
|
||||||
return self._STANDARD_PYTHON_IMPORTER(name,
|
|
||||||
globals,
|
|
||||||
locals,
|
|
||||||
fromlist,
|
|
||||||
level)
|
|
||||||
|
|
||||||
def end(self):
|
|
||||||
"""
|
|
||||||
Needed for clean up.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class _DateTrackerImporter(_BaseImporter):
|
class TrackImporter(object):
|
||||||
"""
|
"""
|
||||||
An importer tracking the date of the module files and reloading them when
|
An importer tracking the date of the module files and reloading them when
|
||||||
they have changed.
|
they have changed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_PACKAGE_PATH_SUFFIX = os.path.sep+"__init__.py"
|
THREAD_LOCAL = threading.local()
|
||||||
|
PACKAGE_PATH_SUFFIX = os.path.sep+"__init__.py"
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(_DateTrackerImporter, self).__init__()
|
|
||||||
self._import_dates = {} # Import dates of the files of the modules
|
self._import_dates = {} # Import dates of the files of the modules
|
||||||
# Avoid reloading cause by file modifications of reload:
|
|
||||||
self._tl = threading.local()
|
|
||||||
self._tl._modules_loaded = None
|
|
||||||
|
|
||||||
def begin(self):
|
def __call__(self,name,globals=None,locals=None,fromlist=None,level=-1):
|
||||||
self._tl._modules_loaded = set()
|
|
||||||
|
|
||||||
def __call__(self, name, globals=None, locals=None,
|
|
||||||
fromlist=None, level=-1):
|
|
||||||
"""
|
"""
|
||||||
The import method itself.
|
The import method itself.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
globals = globals or {}
|
globals = globals or {}
|
||||||
locals = locals or {}
|
locals = locals or {}
|
||||||
fromlist = fromlist or []
|
fromlist = fromlist or []
|
||||||
|
if not hasattr(self.THREAD_LOCAL,'_modules_loaded'):
|
||||||
call_begin_end = self._tl._modules_loaded is None
|
self.THREAD_LOCAL._modules_loaded = set()
|
||||||
if call_begin_end:
|
|
||||||
self.begin()
|
|
||||||
try:
|
try:
|
||||||
self._tl.globals = globals
|
|
||||||
self._tl.locals = locals
|
|
||||||
self._tl.level = level
|
|
||||||
|
|
||||||
# Check the date and reload if needed:
|
# Check the date and reload if needed:
|
||||||
self._update_dates(name, fromlist)
|
self._update_dates(name, globals, locals, fromlist, level)
|
||||||
|
|
||||||
# Try to load the module and update the dates if it works:
|
# Try to load the module and update the dates if it works:
|
||||||
result = super(_DateTrackerImporter, self) \
|
result = NATIVE_IMPORTER(name, globals, locals, fromlist, level)
|
||||||
.__call__(name, globals, locals, fromlist, level)
|
|
||||||
# Module maybe loaded for the 1st time so we need to set the date
|
# Module maybe loaded for the 1st time so we need to set the date
|
||||||
self._update_dates(name, fromlist)
|
self._update_dates(name, globals, locals, fromlist, level)
|
||||||
return result
|
return result
|
||||||
except Exception:
|
except Exception, e:
|
||||||
raise # Don't hide something that went wrong
|
raise # Don't hide something that went wrong
|
||||||
finally:
|
|
||||||
if call_begin_end:
|
|
||||||
self.end()
|
|
||||||
|
|
||||||
def _update_dates(self, name, fromlist):
|
def _update_dates(self, name, globals, locals, fromlist, level):
|
||||||
"""
|
"""
|
||||||
Update all the dates associated to the statement import. A single
|
Update all the dates associated to the statement import. A single
|
||||||
import statement may import many modules.
|
import statement may import many modules.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._reload_check(name)
|
self._reload_check(name, globals, locals, level)
|
||||||
if fromlist:
|
for fromlist_name in fromlist or []:
|
||||||
for fromlist_name in fromlist:
|
pname = "%s.%s" % (name, fromlist_name)
|
||||||
self._reload_check("%s.%s" % (name, fromlist_name))
|
self._reload_check(pname, globals, locals, level)
|
||||||
|
|
||||||
def _reload_check(self, name):
|
def _reload_check(self, name, globals, locals, level):
|
||||||
"""
|
"""
|
||||||
Update the date associated to the module and reload the module if
|
Update the date associated to the module and reload the module if
|
||||||
the file has changed.
|
the file has changed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
module = sys.modules.get(name)
|
module = sys.modules.get(name)
|
||||||
file = self._get_module_file(module)
|
file = self._get_module_file(module)
|
||||||
if file:
|
if file:
|
||||||
@@ -170,7 +154,7 @@ class _DateTrackerImporter(_BaseImporter):
|
|||||||
# Get path without file ext:
|
# Get path without file ext:
|
||||||
file = os.path.splitext(file)[0]
|
file = os.path.splitext(file)[0]
|
||||||
reload_mod = os.path.isdir(file) \
|
reload_mod = os.path.isdir(file) \
|
||||||
and os.path.isfile(file+self._PACKAGE_PATH_SUFFIX)
|
and os.path.isfile(file+self.PACKAGE_PATH_SUFFIX)
|
||||||
mod_to_pack = reload_mod
|
mod_to_pack = reload_mod
|
||||||
else: # Package turning into module?
|
else: # Package turning into module?
|
||||||
file += ".py"
|
file += ".py"
|
||||||
@@ -180,156 +164,27 @@ class _DateTrackerImporter(_BaseImporter):
|
|||||||
if reload_mod or not date or new_date > date:
|
if reload_mod or not date or new_date > date:
|
||||||
self._import_dates[file] = new_date
|
self._import_dates[file] = new_date
|
||||||
if reload_mod or (date and new_date > date):
|
if reload_mod or (date and new_date > date):
|
||||||
if module not in self._tl._modules_loaded:
|
if module not in self.THREAD_LOCAL._modules_loaded:
|
||||||
if mod_to_pack:
|
if mod_to_pack:
|
||||||
# Module turning into a package:
|
# Module turning into a package:
|
||||||
mod_name = module.__name__
|
mod_name = module.__name__
|
||||||
del sys.modules[mod_name] # Delete the module
|
del sys.modules[mod_name] # Delete the module
|
||||||
# Reload the module:
|
# Reload the module:
|
||||||
super(_DateTrackerImporter, self).__call__ \
|
NATIVE_IMPORTER(mod_name, globals, locals, [], level)
|
||||||
(mod_name, self._tl.globals, self._tl.locals, [],
|
|
||||||
self._tl.level)
|
|
||||||
else:
|
else:
|
||||||
reload(module)
|
reload(module)
|
||||||
self._tl._modules_loaded.add(module)
|
self.THREAD_LOCAL._modules_loaded.add(module)
|
||||||
|
|
||||||
def end(self):
|
def _get_module_file(self, module):
|
||||||
self._tl._modules_loaded = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _get_module_file(cls, module):
|
|
||||||
"""
|
"""
|
||||||
Get the absolute path file associated to the module or None.
|
Get the absolute path file associated to the module or None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file = getattr(module, "__file__", None)
|
file = getattr(module, "__file__", None)
|
||||||
if file:
|
if file:
|
||||||
# Make path absolute if not:
|
# Make path absolute if not:
|
||||||
#file = os.path.join(cls.web2py_path, file)
|
|
||||||
|
|
||||||
file = os.path.splitext(file)[0]+".py" # Change .pyc for .py
|
file = os.path.splitext(file)[0]+".py" # Change .pyc for .py
|
||||||
if file.endswith(cls._PACKAGE_PATH_SUFFIX):
|
if file.endswith(self.PACKAGE_PATH_SUFFIX):
|
||||||
file = os.path.dirname(file) # Track dir for packages
|
file = os.path.dirname(file) # Track dir for packages
|
||||||
return file
|
return file
|
||||||
|
|
||||||
class _Web2pyImporter(_BaseImporter):
|
TRACK_IMPORTER = TrackImporter()
|
||||||
"""
|
|
||||||
The standard web2py importer. Like the standard Python importer but it
|
|
||||||
tries to transform import statements as something like
|
|
||||||
"import applications.app_name.modules.x". If the import failed, fall back
|
|
||||||
on _BaseImporter.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_RE_ESCAPED_PATH_SEP = re.escape(os.path.sep) # os.path.sep escaped for re
|
|
||||||
|
|
||||||
def __init__(self, web2py_path):
|
|
||||||
"""
|
|
||||||
@param web2py_path: The absolute path of the web2py installation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
global DEBUG
|
|
||||||
self.super_class = super(_Web2pyImporter, self)
|
|
||||||
self.super_class.__init__()
|
|
||||||
self.web2py_path = web2py_path
|
|
||||||
self.__web2py_path_os_path_sep = self.web2py_path+os.path.sep
|
|
||||||
self.__web2py_path_os_path_sep_len = len(self.__web2py_path_os_path_sep)
|
|
||||||
self.__RE_APP_DIR = re.compile(
|
|
||||||
self._RE_ESCAPED_PATH_SEP.join( \
|
|
||||||
( \
|
|
||||||
#"^" + re.escape(web2py_path), # Not working with Python 2.5
|
|
||||||
"^(" + "applications",
|
|
||||||
"[^",
|
|
||||||
"]+)",
|
|
||||||
"",
|
|
||||||
) ))
|
|
||||||
|
|
||||||
def _matchAppDir(self, file_path):
|
|
||||||
"""
|
|
||||||
Does the file in a directory inside the "applications" directory?
|
|
||||||
"""
|
|
||||||
|
|
||||||
if file_path.startswith(self.__web2py_path_os_path_sep):
|
|
||||||
file_path = file_path[self.__web2py_path_os_path_sep_len:]
|
|
||||||
return self.__RE_APP_DIR.match(file_path)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def __call__(self, name, globals=None, locals=None,
|
|
||||||
fromlist=None, level=-1):
|
|
||||||
"""
|
|
||||||
The import method itself.
|
|
||||||
"""
|
|
||||||
|
|
||||||
globals = globals or {}
|
|
||||||
locals = locals or {}
|
|
||||||
fromlist = fromlist or []
|
|
||||||
|
|
||||||
self.begin()
|
|
||||||
#try:
|
|
||||||
# if not relative and not from applications:
|
|
||||||
if not name.startswith(".") and level <= 0 \
|
|
||||||
and not name.startswith("applications.") \
|
|
||||||
and isinstance(globals, dict):
|
|
||||||
# Get the name of the file do the import
|
|
||||||
caller_file_name = os.path.join(self.web2py_path, \
|
|
||||||
globals.get("__file__", ""))
|
|
||||||
# Is the path in an application directory?
|
|
||||||
match_app_dir = self._matchAppDir(caller_file_name)
|
|
||||||
if match_app_dir:
|
|
||||||
try:
|
|
||||||
# Get the prefix to add for the import
|
|
||||||
# (like applications.app_name.modules):
|
|
||||||
modules_prefix = \
|
|
||||||
".".join((match_app_dir.group(1). \
|
|
||||||
replace(os.path.sep, "."), "modules"))
|
|
||||||
if not fromlist:
|
|
||||||
# import like "import x" or "import x.y"
|
|
||||||
return self.__import__dot(modules_prefix, name,
|
|
||||||
globals, locals, fromlist, level)
|
|
||||||
else:
|
|
||||||
# import like "from x import a, b, ..."
|
|
||||||
return self.super_class \
|
|
||||||
.__call__(modules_prefix+"."+name,
|
|
||||||
globals, locals, fromlist, level)
|
|
||||||
except ImportError, e:
|
|
||||||
try:
|
|
||||||
return self.super_class.__call__(name, globals, locals,
|
|
||||||
fromlist, level)
|
|
||||||
except ImportError, e1:
|
|
||||||
raise e
|
|
||||||
return self.super_class.__call__(name, globals, locals,
|
|
||||||
fromlist, level)
|
|
||||||
|
|
||||||
def __import__dot(self, prefix, name, globals, locals, fromlist,
|
|
||||||
level):
|
|
||||||
"""
|
|
||||||
Here we will import x.y.z as many imports like:
|
|
||||||
from applications.app_name.modules import x
|
|
||||||
from applications.app_name.modules.x import y
|
|
||||||
from applications.app_name.modules.x.y import z.
|
|
||||||
x will be the module returned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
result = None
|
|
||||||
for name in name.split("."):
|
|
||||||
new_mod = super(_Web2pyImporter, self).__call__(prefix, globals,
|
|
||||||
locals, [name], level)
|
|
||||||
try:
|
|
||||||
result = result or new_mod.__dict__[name]
|
|
||||||
except KeyError, e:
|
|
||||||
raise ImportError, 'Cannot import module %s' % str(e)
|
|
||||||
prefix += "." + name
|
|
||||||
return result
|
|
||||||
|
|
||||||
class _Web2pyDateTrackerImporter(_Web2pyImporter, _DateTrackerImporter):
|
|
||||||
"""
|
|
||||||
Like _Web2pyImporter but using a _DateTrackerImporter.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+620
-567
File diff suppressed because it is too large
Load Diff
+33
-21
@@ -128,6 +128,7 @@ class Request(Storage):
|
|||||||
and secure the session.
|
and secure the session.
|
||||||
"""
|
"""
|
||||||
if not global_settings.cronjob and not self.is_https:
|
if not global_settings.cronjob and not self.is_https:
|
||||||
|
current.session.forget()
|
||||||
redirect(URL(scheme='https', args=self.args, vars=self.vars))
|
redirect(URL(scheme='https', args=self.args, vars=self.vars))
|
||||||
|
|
||||||
current.session.secure()
|
current.session.secure()
|
||||||
@@ -229,7 +230,7 @@ class Response(Storage):
|
|||||||
for k,v in (self.meta or {}).iteritems())
|
for k,v in (self.meta or {}).iteritems())
|
||||||
self.write(s,escape=False)
|
self.write(s,escape=False)
|
||||||
|
|
||||||
def include_files(self):
|
def include_files(self, extensions=None):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Caching method for writing out files.
|
Caching method for writing out files.
|
||||||
@@ -240,11 +241,22 @@ class Response(Storage):
|
|||||||
from gluon import URL
|
from gluon import URL
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
|
has_js = has_css = False
|
||||||
for item in self.files:
|
for item in self.files:
|
||||||
if not item in files: files.append(item)
|
if extensions and not item.split('.')[-1] in extensions:
|
||||||
if have_minify and (self.optimize_css or self.optimize_js):
|
continue
|
||||||
|
if item in files:
|
||||||
|
continue
|
||||||
|
if item.endswith('.js'):
|
||||||
|
has_js = True
|
||||||
|
if item.endswith('.css'):
|
||||||
|
has_css = True
|
||||||
|
files.append(item)
|
||||||
|
|
||||||
|
if have_minify and ((self.optimize_css and has_css) or (self.optimize_js and has_js)):
|
||||||
# cache for 5 minutes by default
|
# cache for 5 minutes by default
|
||||||
key = hashlib.md5(repr(files)).hexdigest()
|
key = hashlib.md5(repr(files)).hexdigest()
|
||||||
|
|
||||||
cache = self.cache_includes or (current.cache.ram, 60*5)
|
cache = self.cache_includes or (current.cache.ram, 60*5)
|
||||||
def call_minify(files=files):
|
def call_minify(files=files):
|
||||||
return minify.minify(files,
|
return minify.minify(files,
|
||||||
@@ -263,6 +275,8 @@ class Response(Storage):
|
|||||||
for item in files:
|
for item in files:
|
||||||
if isinstance(item,str):
|
if isinstance(item,str):
|
||||||
f = item.lower().split('?')[0]
|
f = item.lower().split('?')[0]
|
||||||
|
if self.static_version:
|
||||||
|
item = item.replace('/static/', '/static/_%s/' % self.static_version, 1)
|
||||||
if f.endswith('.css'): s += css_template % item
|
if f.endswith('.css'): s += css_template % item
|
||||||
elif f.endswith('.js'): s += js_template % item
|
elif f.endswith('.js'): s += js_template % item
|
||||||
elif f.endswith('.coffee'): s += coffee_template % item
|
elif f.endswith('.coffee'): s += coffee_template % item
|
||||||
@@ -316,7 +330,8 @@ class Response(Storage):
|
|||||||
stream_file_or_304_or_206(stream,
|
stream_file_or_304_or_206(stream,
|
||||||
chunk_size=chunk_size,
|
chunk_size=chunk_size,
|
||||||
request=request,
|
request=request,
|
||||||
headers=headers)
|
headers=headers,
|
||||||
|
status=self.status)
|
||||||
|
|
||||||
# ## the following is for backward compatibility
|
# ## the following is for backward compatibility
|
||||||
if hasattr(stream, 'name'):
|
if hasattr(stream, 'name'):
|
||||||
@@ -402,15 +417,15 @@ class Response(Storage):
|
|||||||
return handler(request, self, methods)
|
return handler(request, self, methods)
|
||||||
|
|
||||||
def toolbar(self):
|
def toolbar(self):
|
||||||
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL
|
from html import DIV, SCRIPT, BEAUTIFY, TAG, URL, A
|
||||||
BUTTON = TAG.button
|
BUTTON = TAG.button
|
||||||
admin = URL("admin","default","design",
|
admin = URL("admin","default","design",
|
||||||
args=current.request.application)
|
args=current.request.application)
|
||||||
from gluon.dal import thread
|
from gluon.dal import THREAD_LOCAL
|
||||||
if hasattr(thread,'instances'):
|
if hasattr(THREAD_LOCAL,'instances'):
|
||||||
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
|
dbstats = [TABLE(*[TR(PRE(row[0]),'%.2fms' % (row[1]*1000)) \
|
||||||
for row in i.db._timings]) \
|
for row in i.db._timings]) \
|
||||||
for i in thread.instances]
|
for i in THREAD_LOCAL.instances]
|
||||||
dbtables = dict([(regex_nopasswd.sub('******',i.uri),
|
dbtables = dict([(regex_nopasswd.sub('******',i.uri),
|
||||||
{'defined':
|
{'defined':
|
||||||
sorted(list(set(i.db.tables) -
|
sorted(list(set(i.db.tables) -
|
||||||
@@ -418,24 +433,26 @@ class Response(Storage):
|
|||||||
'[no defined tables]',
|
'[no defined tables]',
|
||||||
'lazy': sorted(i.db._LAZY_TABLES.keys()) or
|
'lazy': sorted(i.db._LAZY_TABLES.keys()) or
|
||||||
'[no lazy tables]'})
|
'[no lazy tables]'})
|
||||||
for i in thread.instances])
|
for i in THREAD_LOCAL.instances])
|
||||||
else:
|
else:
|
||||||
dbstats = [] # if no db or on GAE
|
dbstats = [] # if no db or on GAE
|
||||||
dbtables = {}
|
dbtables = {}
|
||||||
u = web2py_uuid()
|
u = web2py_uuid()
|
||||||
|
backtotop = A('Back to top', _href="#totop-%s" % u)
|
||||||
return DIV(
|
return DIV(
|
||||||
BUTTON('design',_onclick="document.location='%s'" % admin),
|
BUTTON('design',_onclick="document.location='%s'" % admin),
|
||||||
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
|
BUTTON('request',_onclick="jQuery('#request-%s').slideToggle()"%u),
|
||||||
DIV(BEAUTIFY(current.request),_class="hidden",_id="request-%s"%u),
|
|
||||||
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
|
|
||||||
DIV(BEAUTIFY(current.session),_class="hidden",_id="session-%s"%u),
|
|
||||||
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
|
BUTTON('response',_onclick="jQuery('#response-%s').slideToggle()"%u),
|
||||||
DIV(BEAUTIFY(current.response),_class="hidden",_id="response-%s"%u),
|
BUTTON('session',_onclick="jQuery('#session-%s').slideToggle()"%u),
|
||||||
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
|
BUTTON('db tables',_onclick="jQuery('#db-tables-%s').slideToggle()"%u),
|
||||||
DIV(BEAUTIFY(dbtables),_class="hidden",_id="db-tables-%s"%u),
|
|
||||||
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
|
BUTTON('db stats',_onclick="jQuery('#db-stats-%s').slideToggle()"%u),
|
||||||
DIV(BEAUTIFY(dbstats),_class="hidden",_id="db-stats-%s"%u),
|
DIV(BEAUTIFY(current.request), backtotop,_class="hidden",_id="request-%s"%u),
|
||||||
|
DIV(BEAUTIFY(current.session), backtotop, _class="hidden",_id="session-%s"%u),
|
||||||
|
DIV(BEAUTIFY(current.response), backtotop, _class="hidden",_id="response-%s"%u),
|
||||||
|
DIV(BEAUTIFY(dbtables), backtotop, _class="hidden",_id="db-tables-%s"%u),
|
||||||
|
DIV(BEAUTIFY(dbstats), backtotop, _class="hidden",_id="db-stats-%s"%u),
|
||||||
SCRIPT("jQuery('.hidden').hide()")
|
SCRIPT("jQuery('.hidden').hide()")
|
||||||
|
,_id="totop-%s" % u
|
||||||
)
|
)
|
||||||
|
|
||||||
class Session(Storage):
|
class Session(Storage):
|
||||||
@@ -467,7 +484,7 @@ class Session(Storage):
|
|||||||
if not masterapp:
|
if not masterapp:
|
||||||
masterapp = request.application
|
masterapp = request.application
|
||||||
response.session_id_name = 'session_id_%s' % masterapp.lower()
|
response.session_id_name = 'session_id_%s' % masterapp.lower()
|
||||||
|
|
||||||
# Load session data from cookie
|
# Load session data from cookie
|
||||||
cookies = request.cookies
|
cookies = request.cookies
|
||||||
|
|
||||||
@@ -703,8 +720,3 @@ class Session(Storage):
|
|||||||
del response.session_file
|
del response.session_file
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+38
-16
@@ -32,6 +32,11 @@ regex_crlf = re.compile('\r|\n')
|
|||||||
|
|
||||||
join = ''.join
|
join = ''.join
|
||||||
|
|
||||||
|
# name2codepoint is incomplete respect to xhtml (and xml): 'apos' is missing.
|
||||||
|
entitydefs = dict(map(lambda (k,v): (k, unichr(v).encode('utf-8')), name2codepoint.iteritems()))
|
||||||
|
entitydefs.setdefault('apos', u"'".encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'A',
|
'A',
|
||||||
'B',
|
'B',
|
||||||
@@ -243,7 +248,7 @@ def URL(
|
|||||||
elif a and c and not f: (c,f,a)=(a,c,f)
|
elif a and c and not f: (c,f,a)=(a,c,f)
|
||||||
from globals import current
|
from globals import current
|
||||||
if hasattr(current,'request'):
|
if hasattr(current,'request'):
|
||||||
r = current.request
|
r = current.request
|
||||||
|
|
||||||
if r:
|
if r:
|
||||||
application = r.application
|
application = r.application
|
||||||
@@ -487,7 +492,7 @@ class XmlComponent(object):
|
|||||||
components += [other]
|
components += [other]
|
||||||
return CAT(*components)
|
return CAT(*components)
|
||||||
|
|
||||||
def add_class(self, name):
|
def add_class(self, name):
|
||||||
""" add a class to _class attribute """
|
""" add a class to _class attribute """
|
||||||
c = self['_class']
|
c = self['_class']
|
||||||
classes = (set(c.split()) if c else set())|set(name.split())
|
classes = (set(c.split()) if c else set())|set(name.split())
|
||||||
@@ -1745,7 +1750,7 @@ class INPUT(DIV):
|
|||||||
elif not t == 'submit':
|
elif not t == 'submit':
|
||||||
if value is None:
|
if value is None:
|
||||||
self['value'] = _value
|
self['value'] = _value
|
||||||
else:
|
elif not isinstance(value,list):
|
||||||
self['_value'] = value
|
self['_value'] = value
|
||||||
|
|
||||||
def xml(self):
|
def xml(self):
|
||||||
@@ -1938,6 +1943,7 @@ class FORM(DIV):
|
|||||||
# check formname and formkey
|
# check formname and formkey
|
||||||
|
|
||||||
status = True
|
status = True
|
||||||
|
changed = False
|
||||||
request_vars = self.request_vars
|
request_vars = self.request_vars
|
||||||
if session:
|
if session:
|
||||||
formkey = session.get('_formkey[%s]' % formname, None)
|
formkey = session.get('_formkey[%s]' % formname, None)
|
||||||
@@ -1950,18 +1956,23 @@ class FORM(DIV):
|
|||||||
# check if editing a record that has been modified by the server
|
# check if editing a record that has been modified by the server
|
||||||
if hasattr(self,'record_hash') and self.record_hash != formkey:
|
if hasattr(self,'record_hash') and self.record_hash != formkey:
|
||||||
status = False
|
status = False
|
||||||
self.record_changed = True
|
self.record_changed = changed = True
|
||||||
status = self._traverse(status,hideerror)
|
status = self._traverse(status,hideerror)
|
||||||
status = self.assert_status(status, request_vars)
|
status = self.assert_status(status, request_vars)
|
||||||
if onvalidation:
|
if onvalidation:
|
||||||
if isinstance(onvalidation, dict):
|
if isinstance(onvalidation, dict):
|
||||||
onsuccess = onvalidation.get('onsuccess', None)
|
onsuccess = onvalidation.get('onsuccess', None)
|
||||||
onfailure = onvalidation.get('onfailure', None)
|
onfailure = onvalidation.get('onfailure', None)
|
||||||
|
onchange = onvalidation.get('onchange', None)
|
||||||
if onsuccess and status:
|
if onsuccess and status:
|
||||||
onsuccess(self)
|
onsuccess(self)
|
||||||
if onfailure and request_vars and not status:
|
if onfailure and request_vars and not status:
|
||||||
onfailure(self)
|
onfailure(self)
|
||||||
status = len(self.errors) == 0
|
status = len(self.errors) == 0
|
||||||
|
if changed:
|
||||||
|
if onchange and self.record_changed and \
|
||||||
|
self.detect_record_change:
|
||||||
|
onchange(self)
|
||||||
elif status:
|
elif status:
|
||||||
if isinstance(onvalidation, (list, tuple)):
|
if isinstance(onvalidation, (list, tuple)):
|
||||||
[f(self) for f in onvalidation]
|
[f(self) for f in onvalidation]
|
||||||
@@ -2030,8 +2041,13 @@ class FORM(DIV):
|
|||||||
onfailure = 'flash' - will show message_onfailure in response.flash
|
onfailure = 'flash' - will show message_onfailure in response.flash
|
||||||
None - will do nothing
|
None - will do nothing
|
||||||
can be a function (lambda form: pass)
|
can be a function (lambda form: pass)
|
||||||
|
onchange = 'flash' - will show message_onchange in response.flash
|
||||||
|
None - will do nothing
|
||||||
|
can be a function (lambda form: pass)
|
||||||
|
|
||||||
message_onsuccess
|
message_onsuccess
|
||||||
message_onfailure
|
message_onfailure
|
||||||
|
message_onchange
|
||||||
next = where to redirect in case of success
|
next = where to redirect in case of success
|
||||||
any other kwargs will be passed for form.accepts(...)
|
any other kwargs will be passed for form.accepts(...)
|
||||||
"""
|
"""
|
||||||
@@ -2042,13 +2058,17 @@ class FORM(DIV):
|
|||||||
|
|
||||||
onsuccess = kwargs.get('onsuccess','flash')
|
onsuccess = kwargs.get('onsuccess','flash')
|
||||||
onfailure = kwargs.get('onfailure','flash')
|
onfailure = kwargs.get('onfailure','flash')
|
||||||
|
onchange = kwargs.get('onchange', 'flash')
|
||||||
message_onsuccess = kwargs.get('message_onsuccess',
|
message_onsuccess = kwargs.get('message_onsuccess',
|
||||||
current.T("Success!"))
|
current.T("Success!"))
|
||||||
message_onfailure = kwargs.get('message_onfailure',
|
message_onfailure = kwargs.get('message_onfailure',
|
||||||
current.T("Errors in form, please check it out."))
|
current.T("Errors in form, please check it out."))
|
||||||
|
message_onchange = kwargs.get('message_onchange',
|
||||||
|
current.T("Form consecutive submissions not allowed. " +
|
||||||
|
"Try re-submitting or refreshing the form page."))
|
||||||
next = kwargs.get('next',None)
|
next = kwargs.get('next',None)
|
||||||
for key in ('message_onsuccess','message_onfailure','onsuccess',
|
for key in ('message_onsuccess','message_onfailure','onsuccess',
|
||||||
'onfailure','next'):
|
'onfailure','next', 'message_onchange', 'onchange'):
|
||||||
if key in kwargs:
|
if key in kwargs:
|
||||||
del kwargs[key]
|
del kwargs[key]
|
||||||
|
|
||||||
@@ -2075,6 +2095,13 @@ class FORM(DIV):
|
|||||||
elif callable(onfailure):
|
elif callable(onfailure):
|
||||||
onfailure(self)
|
onfailure(self)
|
||||||
return False
|
return False
|
||||||
|
elif hasattr(self, "record_changed"):
|
||||||
|
if self.record_changed and self.detect_record_change:
|
||||||
|
if onchange == 'flash':
|
||||||
|
current.response.flash = message_onchange
|
||||||
|
elif callable(onchange):
|
||||||
|
onchange(self)
|
||||||
|
return False
|
||||||
|
|
||||||
def process(self, **kwargs):
|
def process(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -2111,8 +2138,10 @@ class FORM(DIV):
|
|||||||
REDIRECT_JS = "window.location='%s';return false"
|
REDIRECT_JS = "window.location='%s';return false"
|
||||||
|
|
||||||
def add_button(self,value,url,_class=None):
|
def add_button(self,value,url,_class=None):
|
||||||
self[0][-1][1].append(INPUT(_type="button",_value=value,_class=_class,
|
submit = self.element('input[type=submit]')
|
||||||
_onclick=self.REDIRECT_JS % url))
|
submit.parent.append(
|
||||||
|
INPUT(_type="button",_value=value,_class=_class,
|
||||||
|
_onclick=self.REDIRECT_JS % url))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2276,7 +2305,7 @@ class MENU(DIV):
|
|||||||
select = SELECT(**self.attributes)
|
select = SELECT(**self.attributes)
|
||||||
for item in data:
|
for item in data:
|
||||||
if len(item) <= 4 or item[4] == True:
|
if len(item) <= 4 or item[4] == True:
|
||||||
select.append(OPTION(CAT(prefix, item[0]),
|
select.append(OPTION(CAT(prefix, item[0]),
|
||||||
_value=item[2], _selected=item[1]))
|
_value=item[2], _selected=item[1]))
|
||||||
if len(item)>3 and len(item[3]):
|
if len(item)>3 and len(item[3]):
|
||||||
self.serialize_mobile(
|
self.serialize_mobile(
|
||||||
@@ -2402,7 +2431,7 @@ class web2pyHTMLParser(HTMLParser):
|
|||||||
else:
|
else:
|
||||||
self.parent.append(unichr(int(name)).encode('utf8'))
|
self.parent.append(unichr(int(name)).encode('utf8'))
|
||||||
def handle_entityref(self,name):
|
def handle_entityref(self,name):
|
||||||
self.parent.append(unichr(name2codepoint[name]).encode('utf8'))
|
self.parent.append(entitydefs[name])
|
||||||
def handle_endtag(self, tagname):
|
def handle_endtag(self, tagname):
|
||||||
# this deals with unbalanced tags
|
# this deals with unbalanced tags
|
||||||
if tagname==self.last:
|
if tagname==self.last:
|
||||||
@@ -2506,10 +2535,3 @@ class MARKMIN(XmlComponent):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ defined_status = {
|
|||||||
415: 'UNSUPPORTED MEDIA TYPE',
|
415: 'UNSUPPORTED MEDIA TYPE',
|
||||||
416: 'REQUESTED RANGE NOT SATISFIABLE',
|
416: 'REQUESTED RANGE NOT SATISFIABLE',
|
||||||
417: 'EXPECTATION FAILED',
|
417: 'EXPECTATION FAILED',
|
||||||
|
422: 'UNPROCESSABLE ENTITY',
|
||||||
500: 'INTERNAL SERVER ERROR',
|
500: 'INTERNAL SERVER ERROR',
|
||||||
501: 'NOT IMPLEMENTED',
|
501: 'NOT IMPLEMENTED',
|
||||||
502: 'BAD GATEWAY',
|
502: 'BAD GATEWAY',
|
||||||
|
|||||||
+2
-2
@@ -155,9 +155,9 @@ def read_possible_plural_rules():
|
|||||||
create list of all possible plural rules files
|
create list of all possible plural rules files
|
||||||
result is cached in PLURAL_RULES dictionary to increase speed
|
result is cached in PLURAL_RULES dictionary to increase speed
|
||||||
"""
|
"""
|
||||||
|
plurals = {}
|
||||||
try:
|
try:
|
||||||
import gluon.contrib.plural_rules as package
|
import contrib.plural_rules as package
|
||||||
plurals = {}
|
|
||||||
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
|
for importer, modname, ispkg in pkgutil.iter_modules(package.__path__):
|
||||||
if len(modname)==2:
|
if len(modname)==2:
|
||||||
module = __import__(package.__name__+'.'+modname,
|
module = __import__(package.__name__+'.'+modname,
|
||||||
|
|||||||
+49
-36
@@ -35,8 +35,6 @@ from settings import global_settings
|
|||||||
from admin import add_path_first, create_missing_folders, create_missing_app_folders
|
from admin import add_path_first, create_missing_folders, create_missing_app_folders
|
||||||
from globals import current
|
from globals import current
|
||||||
|
|
||||||
from custom_import import custom_import_install
|
|
||||||
|
|
||||||
# Remarks:
|
# Remarks:
|
||||||
# calling script has inserted path to script directory into sys.path
|
# calling script has inserted path to script directory into sys.path
|
||||||
# applications_parent (path to applications/, site-packages/ etc)
|
# applications_parent (path to applications/, site-packages/ etc)
|
||||||
@@ -55,8 +53,6 @@ from custom_import import custom_import_install
|
|||||||
|
|
||||||
web2py_path = global_settings.applications_parent # backward compatibility
|
web2py_path = global_settings.applications_parent # backward compatibility
|
||||||
|
|
||||||
custom_import_install(web2py_path)
|
|
||||||
|
|
||||||
create_missing_folders()
|
create_missing_folders()
|
||||||
|
|
||||||
# set up logging for subsequent imports
|
# set up logging for subsequent imports
|
||||||
@@ -88,10 +84,10 @@ from contenttype import contenttype
|
|||||||
from dal import BaseAdapter
|
from dal import BaseAdapter
|
||||||
from settings import global_settings
|
from settings import global_settings
|
||||||
from validators import CRYPT
|
from validators import CRYPT
|
||||||
from cache import Cache
|
from cache import CacheInRam
|
||||||
from html import URL, xmlescape
|
from html import URL, xmlescape
|
||||||
from utils import is_valid_ip_address
|
from utils import is_valid_ip_address
|
||||||
from rewrite import load, url_in, thread as rwthread, \
|
from rewrite import load, url_in, THREAD_LOCAL as rwthread, \
|
||||||
try_rewrite_on_error, fixup_missing_path_info
|
try_rewrite_on_error, fixup_missing_path_info
|
||||||
import newcron
|
import newcron
|
||||||
|
|
||||||
@@ -123,6 +119,8 @@ except:
|
|||||||
|
|
||||||
load()
|
load()
|
||||||
|
|
||||||
|
HTTPS_SCHEMES = set(('https','HTTPS'))
|
||||||
|
|
||||||
def get_client(env):
|
def get_client(env):
|
||||||
"""
|
"""
|
||||||
guess the client address from the environment variables
|
guess the client address from the environment variables
|
||||||
@@ -132,7 +130,7 @@ def get_client(env):
|
|||||||
"""
|
"""
|
||||||
g = regex_client.search(env.get('http_x_forwarded_for', ''))
|
g = regex_client.search(env.get('http_x_forwarded_for', ''))
|
||||||
if g:
|
if g:
|
||||||
client = g.group()
|
client = (g.group() or '').split(',')[0]
|
||||||
else:
|
else:
|
||||||
g = regex_client.search(env.get('remote_addr', ''))
|
g = regex_client.search(env.get('remote_addr', ''))
|
||||||
if g:
|
if g:
|
||||||
@@ -146,29 +144,32 @@ def get_client(env):
|
|||||||
def copystream_progress(request, chunk_size= 10**5):
|
def copystream_progress(request, chunk_size= 10**5):
|
||||||
"""
|
"""
|
||||||
copies request.env.wsgi_input into request.body
|
copies request.env.wsgi_input into request.body
|
||||||
and stores progress upload status in cache.ram
|
and stores progress upload status in cache_ram
|
||||||
X-Progress-ID:length and X-Progress-ID:uploaded
|
X-Progress-ID:length and X-Progress-ID:uploaded
|
||||||
"""
|
"""
|
||||||
env = request.env
|
env = request.env
|
||||||
if not env.content_length:
|
if not env.content_length:
|
||||||
return cStringIO.StringIO()
|
return cStringIO.StringIO()
|
||||||
source = env.wsgi_input
|
source = env.wsgi_input
|
||||||
size = int(env.content_length)
|
try:
|
||||||
|
size = int(env.content_length)
|
||||||
|
except ValueError:
|
||||||
|
raise HTTP(400,"Invalid Content-Length header")
|
||||||
dest = tempfile.TemporaryFile()
|
dest = tempfile.TemporaryFile()
|
||||||
if not 'X-Progress-ID' in request.vars:
|
if not 'X-Progress-ID' in request.vars:
|
||||||
copystream(source, dest, size, chunk_size)
|
copystream(source, dest, size, chunk_size)
|
||||||
return dest
|
return dest
|
||||||
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
|
cache_key = 'X-Progress-ID:'+request.vars['X-Progress-ID']
|
||||||
cache = Cache(request)
|
cache_ram = CacheInRam(request) # same as cache.ram because meta_storage
|
||||||
cache.ram(cache_key+':length', lambda: size, 0)
|
cache_ram(cache_key+':length', lambda: size, 0)
|
||||||
cache.ram(cache_key+':uploaded', lambda: 0, 0)
|
cache_ram(cache_key+':uploaded', lambda: 0, 0)
|
||||||
while size > 0:
|
while size > 0:
|
||||||
if size < chunk_size:
|
if size < chunk_size:
|
||||||
data = source.read(size)
|
data = source.read(size)
|
||||||
cache.ram.increment(cache_key+':uploaded', size)
|
cache_ram.increment(cache_key+':uploaded', size)
|
||||||
else:
|
else:
|
||||||
data = source.read(chunk_size)
|
data = source.read(chunk_size)
|
||||||
cache.ram.increment(cache_key+':uploaded', chunk_size)
|
cache_ram.increment(cache_key+':uploaded', chunk_size)
|
||||||
length = len(data)
|
length = len(data)
|
||||||
if length > size:
|
if length > size:
|
||||||
(data, length) = (data[:size], size)
|
(data, length) = (data[:size], size)
|
||||||
@@ -179,8 +180,8 @@ def copystream_progress(request, chunk_size= 10**5):
|
|||||||
if length < chunk_size:
|
if length < chunk_size:
|
||||||
break
|
break
|
||||||
dest.seek(0)
|
dest.seek(0)
|
||||||
cache.ram(cache_key+':length', None)
|
cache_ram(cache_key+':length', None)
|
||||||
cache.ram(cache_key+':uploaded', None)
|
cache_ram(cache_key+':uploaded', None)
|
||||||
return dest
|
return dest
|
||||||
|
|
||||||
|
|
||||||
@@ -325,10 +326,12 @@ def parse_get_post_vars(request, environ):
|
|||||||
dpk = dpost[key]
|
dpk = dpost[key]
|
||||||
# if en element is not a file replace it with its value else leave it alone
|
# if en element is not a file replace it with its value else leave it alone
|
||||||
if isinstance(dpk, list):
|
if isinstance(dpk, list):
|
||||||
if not dpk[0].filename:
|
value = []
|
||||||
value = [x.value for x in dpk]
|
for _dpk in dpk:
|
||||||
else:
|
if not _dpk.filename:
|
||||||
value = [x for x in dpk]
|
value.append(_dpk.value)
|
||||||
|
else:
|
||||||
|
value.append(_dpk)
|
||||||
elif not dpk.filename:
|
elif not dpk.filename:
|
||||||
value = dpk.value
|
value = dpk.value
|
||||||
else:
|
else:
|
||||||
@@ -400,7 +403,7 @@ def wsgibase(environ, responder):
|
|||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
fixup_missing_path_info(environ)
|
fixup_missing_path_info(environ)
|
||||||
(static_file, environ) = url_in(request, environ)
|
(static_file, version, environ) = url_in(request, environ)
|
||||||
response.status = env.web2py_status_code or response.status
|
response.status = env.web2py_status_code or response.status
|
||||||
|
|
||||||
if static_file:
|
if static_file:
|
||||||
@@ -408,24 +411,34 @@ def wsgibase(environ, responder):
|
|||||||
'attachment'):
|
'attachment'):
|
||||||
response.headers['Content-Disposition'] \
|
response.headers['Content-Disposition'] \
|
||||||
= 'attachment'
|
= 'attachment'
|
||||||
|
if version:
|
||||||
|
response.headers['Cache-Control'] = 'max-age=315360000'
|
||||||
|
response.headers['Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT'
|
||||||
response.stream(static_file, request=request)
|
response.stream(static_file, request=request)
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# fill in request items
|
# fill in request items
|
||||||
# ##################################################
|
# ##################################################
|
||||||
app = request.application ## must go after url_in!
|
app = request.application ## must go after url_in!
|
||||||
|
|
||||||
http_host = env.http_host.split(':',1)[0]
|
if not global_settings.local_hosts:
|
||||||
local_hosts = [http_host,'::1','127.0.0.1',
|
local_hosts = ['127.0.0.1','::ffff:127.0.0.1']
|
||||||
'::ffff:127.0.0.1']
|
if not global_settings.web2py_runtime_gae:
|
||||||
if not global_settings.web2py_runtime_gae:
|
try:
|
||||||
local_hosts.append(socket.gethostname())
|
local_hosts.append(socket.gethostname())
|
||||||
try:
|
except TypeError:
|
||||||
local_hosts.append(
|
pass
|
||||||
socket.gethostbyname(http_host))
|
try:
|
||||||
except socket.gaierror:
|
if env.server_name:
|
||||||
pass
|
local_hosts += [
|
||||||
client = get_client(env)
|
env.server_name,
|
||||||
|
socket.gethostbyname(env.server_name)]
|
||||||
|
except (socket.gaierror,TypeError):
|
||||||
|
pass
|
||||||
|
global_settings.local_hosts = local_hosts
|
||||||
|
else:
|
||||||
|
local_hosts = global_settings.local_hosts
|
||||||
|
client = get_client(env)
|
||||||
x_req_with = str(env.http_x_requested_with).lower()
|
x_req_with = str(env.http_x_requested_with).lower()
|
||||||
|
|
||||||
request.update(
|
request.update(
|
||||||
@@ -434,8 +447,9 @@ def wsgibase(environ, responder):
|
|||||||
ajax = x_req_with == 'xmlhttprequest',
|
ajax = x_req_with == 'xmlhttprequest',
|
||||||
cid = env.http_web2py_component_element,
|
cid = env.http_web2py_component_element,
|
||||||
is_local = env.remote_addr in local_hosts,
|
is_local = env.remote_addr in local_hosts,
|
||||||
is_https = env.wsgi_url_scheme \
|
is_https = env.wsgi_url_scheme in HTTPS_SCHEMES \
|
||||||
in ['https', 'HTTPS'] or env.https=='on')
|
or request.env.http_x_forwarded_proto in HTTPS_SCHEMES \
|
||||||
|
or env.https=='on')
|
||||||
request.uuid = request.compute_uuid() # requires client
|
request.uuid = request.compute_uuid() # requires client
|
||||||
request.url = environ['PATH_INFO']
|
request.url = environ['PATH_INFO']
|
||||||
|
|
||||||
@@ -781,7 +795,6 @@ class HttpServer(object):
|
|||||||
global_settings.applications_parent = path
|
global_settings.applications_parent = path
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
[add_path_first(p) for p in (path, abspath('site-packages'), "")]
|
[add_path_first(p) for p in (path, abspath('site-packages'), "")]
|
||||||
custom_import_install(web2py_path)
|
|
||||||
if exists("logging.conf"):
|
if exists("logging.conf"):
|
||||||
logging.config.fileConfig("logging.conf")
|
logging.config.fileConfig("logging.conf")
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -314,22 +314,26 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
|
|||||||
(action,models,command) = (True,'-M',command[1:])
|
(action,models,command) = (True,'-M',command[1:])
|
||||||
else:
|
else:
|
||||||
action=False
|
action=False
|
||||||
|
|
||||||
if action and command.endswith('.py'):
|
if action and command.endswith('.py'):
|
||||||
commands.extend(('-J', # cron job
|
commands.extend(('-J', # cron job
|
||||||
models, # import models?
|
models, # import models?
|
||||||
'-S', app, # app name
|
'-S', app, # app name
|
||||||
'-a', '"<recycle>"', # password
|
'-a', '"<recycle>"', # password
|
||||||
'-R', command)) # command
|
'-R', command)) # command
|
||||||
shell = True
|
|
||||||
elif action:
|
elif action:
|
||||||
commands.extend(('-J', # cron job
|
commands.extend(('-J', # cron job
|
||||||
models, # import models?
|
models, # import models?
|
||||||
'-S', app+'/'+command, # app name
|
'-S', app+'/'+command, # app name
|
||||||
'-a', '"<recycle>"')) # password
|
'-a', '"<recycle>"')) # password
|
||||||
shell = True
|
|
||||||
else:
|
else:
|
||||||
commands = command
|
commands = command
|
||||||
shell = False
|
|
||||||
|
# from python docs:
|
||||||
|
# You do not need shell=True to run a batch file or
|
||||||
|
# console-based executable.
|
||||||
|
shell = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cronlauncher(commands, shell=shell).start()
|
cronlauncher(commands, shell=shell).start()
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
|||||||
@@ -140,8 +140,9 @@ class LockedFile(object):
|
|||||||
self.file.close()
|
self.file.close()
|
||||||
self.file = None
|
self.file = None
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
self.close()
|
if not self.file is None:
|
||||||
|
self.close()
|
||||||
|
|
||||||
def read_locked(filename):
|
def read_locked(filename):
|
||||||
fp = LockedFile(filename, 'r')
|
fp = LockedFile(filename, 'r')
|
||||||
data = fp.read()
|
data = fp.read()
|
||||||
|
|||||||
+129
-183
@@ -33,12 +33,29 @@ exists = os.path.exists
|
|||||||
pjoin = os.path.join
|
pjoin = os.path.join
|
||||||
|
|
||||||
logger = logging.getLogger('web2py.rewrite')
|
logger = logging.getLogger('web2py.rewrite')
|
||||||
thread = threading.local() # thread-local storage for routing params
|
THREAD_LOCAL = threading.local() # thread-local storage for routing params
|
||||||
|
|
||||||
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
regex_at = re.compile(r'(?<!\\)\$[a-zA-Z]\w*')
|
||||||
regex_anything = re.compile(r'(?<!\\)\$anything')
|
regex_anything = re.compile(r'(?<!\\)\$anything')
|
||||||
regex_redirect = re.compile(r'(\d+)->(.*)')
|
regex_redirect = re.compile(r'(\d+)->(.*)')
|
||||||
regex_full_url = re.compile(r'^(?P<scheme>http|https|HTTP|HTTPS)\://(?P<host>[^/]*)(?P<uri>.*)')
|
regex_full_url = re.compile(r'^(?P<scheme>http|https|HTTP|HTTPS)\://(?P<host>[^/]*)(?P<uri>.*)')
|
||||||
|
regex_version = re.compile(r'^(_[\d]+\.[\d]+\.[\d]+)$')
|
||||||
|
# pattern to replace spaces with underscore in URL
|
||||||
|
# also the html escaped variants '+' and '%20' are covered
|
||||||
|
regex_space = re.compile('(\+|\s|%20)+')
|
||||||
|
|
||||||
|
# pattern to find valid paths in url /application/controller/...
|
||||||
|
# this could be:
|
||||||
|
# for static pages:
|
||||||
|
# /<b:application>/static/<x:file>
|
||||||
|
# for dynamic pages:
|
||||||
|
# /<a:application>[/<c:controller>[/<f:function>[.<e:ext>][/<s:args>]]]
|
||||||
|
# application, controller, function and ext may only contain [a-zA-Z0-9_]
|
||||||
|
# file and args may also contain '-', '=', '.' and '/'
|
||||||
|
# apps in routes_apps_raw must parse raw_args into args
|
||||||
|
|
||||||
|
regex_url = re.compile('^/((?P<a>\w+)(/(?P<c>\w+)(/(?P<z>(?P<f>\w+)(\.(?P<e>[\w.]+))?(?P<s>[/\w@=-]*(\.[/\w@=-]+)*)))?)?)?$')
|
||||||
|
|
||||||
|
|
||||||
def _router_default():
|
def _router_default():
|
||||||
"return new copy of default base router"
|
"return new copy of default base router"
|
||||||
@@ -84,7 +101,7 @@ def _params_default(app=None):
|
|||||||
|
|
||||||
params_apps = dict()
|
params_apps = dict()
|
||||||
params = _params_default(app=None) # regex rewrite parameters
|
params = _params_default(app=None) # regex rewrite parameters
|
||||||
thread.routes = params # default to base regex rewrite parameters
|
THREAD_LOCAL.routes = params # default to base regex rewrite parameters
|
||||||
routers = None
|
routers = None
|
||||||
|
|
||||||
def log_rewrite(string):
|
def log_rewrite(string):
|
||||||
@@ -107,16 +124,16 @@ def log_rewrite(string):
|
|||||||
logger.debug(string)
|
logger.debug(string)
|
||||||
|
|
||||||
ROUTER_KEYS = set(
|
ROUTER_KEYS = set(
|
||||||
('default_application', 'applications',
|
('default_application', 'applications',
|
||||||
'default_controller', 'controllers',
|
'default_controller', 'controllers',
|
||||||
'default_function', 'functions',
|
'default_function', 'functions',
|
||||||
'default_language', 'languages',
|
'default_language', 'languages',
|
||||||
'domain', 'domains', 'root_static', 'path_prefix',
|
'domain', 'domains', 'root_static', 'path_prefix',
|
||||||
'exclusive_domain', 'map_hyphen', 'map_static',
|
'exclusive_domain', 'map_hyphen', 'map_static',
|
||||||
'acfe_match', 'file_match', 'args_match'))
|
'acfe_match', 'file_match', 'args_match'))
|
||||||
|
|
||||||
ROUTER_BASE_KEYS = set(
|
ROUTER_BASE_KEYS = set(
|
||||||
('applications', 'default_application',
|
('applications', 'default_application',
|
||||||
'domains', 'path_prefix'))
|
'domains', 'path_prefix'))
|
||||||
|
|
||||||
# The external interface to rewrite consists of:
|
# The external interface to rewrite consists of:
|
||||||
@@ -125,11 +142,11 @@ ROUTER_BASE_KEYS = set(
|
|||||||
# url_in: parse and rewrite incoming URL
|
# url_in: parse and rewrite incoming URL
|
||||||
# url_out: assemble and rewrite outgoing URL
|
# url_out: assemble and rewrite outgoing URL
|
||||||
#
|
#
|
||||||
# thread.routes.default_application
|
# THREAD_LOCAL.routes.default_application
|
||||||
# thread.routes.error_message
|
# THREAD_LOCAL.routes.error_message
|
||||||
# thread.routes.error_message_ticket
|
# THREAD_LOCAL.routes.error_message_ticket
|
||||||
# thread.routes.try_redirect_on_error
|
# THREAD_LOCAL.routes.try_redirect_on_error
|
||||||
# thread.routes.error_handler
|
# THREAD_LOCAL.routes.error_handler
|
||||||
#
|
#
|
||||||
# filter_url: helper for doctest & unittest
|
# filter_url: helper for doctest & unittest
|
||||||
# filter_err: helper for doctest & unittest
|
# filter_err: helper for doctest & unittest
|
||||||
@@ -140,8 +157,8 @@ def fixup_missing_path_info(environ):
|
|||||||
path_info = eget('PATH_INFO')
|
path_info = eget('PATH_INFO')
|
||||||
request_uri = eget('REQUEST_URI')
|
request_uri = eget('REQUEST_URI')
|
||||||
if not path_info and request_uri:
|
if not path_info and request_uri:
|
||||||
# for fcgi, get path_info and
|
# for fcgi, get path_info and
|
||||||
# query_string from request_uri
|
# query_string from request_uri
|
||||||
items = request_uri.split('?')
|
items = request_uri.split('?')
|
||||||
path_info = environ['PATH_INFO'] = items[0]
|
path_info = environ['PATH_INFO'] = items[0]
|
||||||
environ['QUERY_STRING'] = items[1] if len(items) > 1 else ''
|
environ['QUERY_STRING'] = items[1] if len(items) > 1 else ''
|
||||||
@@ -154,45 +171,36 @@ def fixup_missing_path_info(environ):
|
|||||||
if not eget('HTTP_HOST'):
|
if not eget('HTTP_HOST'):
|
||||||
environ['HTTP_HOST'] = \
|
environ['HTTP_HOST'] = \
|
||||||
'%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT'))
|
'%s:%s' % (eget('SERVER_NAME'),eget('SERVER_PORT'))
|
||||||
|
|
||||||
|
|
||||||
def url_in(request, environ):
|
def url_in(request, environ):
|
||||||
"parse and rewrite incoming URL"
|
"parse and rewrite incoming URL"
|
||||||
if routers:
|
if routers:
|
||||||
return map_url_in(request, environ)
|
return map_url_in(request, environ)
|
||||||
return regex_url_in(request, environ)
|
return regex_url_in(request, environ)
|
||||||
|
|
||||||
def url_out(request, env, application, controller, function,
|
def url_out(request, environ, application, controller, function,
|
||||||
args, other, scheme, host, port):
|
args, other, scheme, host, port):
|
||||||
"assemble and rewrite outgoing URL"
|
"assemble and rewrite outgoing URL"
|
||||||
if routers:
|
if routers:
|
||||||
acf = map_url_out(request, env, application, controller,
|
acf = map_url_out(request, environ, application, controller,
|
||||||
function, args, other, scheme, host, port)
|
function, args, other, scheme, host, port)
|
||||||
url = '%s%s' % (acf, other)
|
url = '%s%s' % (acf, other)
|
||||||
else:
|
else:
|
||||||
url = '/%s/%s/%s%s' % (application, controller, function, other)
|
url = '/%s/%s/%s%s' % (application, controller, function, other)
|
||||||
url = regex_filter_out(url, env)
|
url = regex_filter_out(url, environ)
|
||||||
#
|
#
|
||||||
# fill in scheme and host if absolute URL is requested
|
# fill in scheme and host if absolute URL is requested
|
||||||
# scheme can be a string, eg 'http', 'https', 'ws', 'wss'
|
# scheme can be a string, eg 'http', 'https', 'ws', 'wss'
|
||||||
#
|
#
|
||||||
if scheme or port is not None:
|
if host is True or (host is None and (scheme or port!=None)):
|
||||||
if host is None: # scheme or port implies host
|
host = request.env.http_host
|
||||||
host = True
|
|
||||||
if not scheme or scheme is True:
|
if not scheme or scheme is True:
|
||||||
if request and request.env:
|
scheme = request.env.get('wsgi_url_scheme', 'http').lower() \
|
||||||
scheme = request.env.get('wsgi_url_scheme', 'http').lower()
|
if request else 'http'
|
||||||
else:
|
|
||||||
scheme = 'http' # some reasonable default in case we need it
|
|
||||||
if host is not None:
|
|
||||||
if host is True:
|
|
||||||
host = request.env.http_host
|
|
||||||
if host:
|
if host:
|
||||||
if port is None:
|
host_port = host if not port else host.split(':',1)[0]+':%s'%port
|
||||||
port = ''
|
url = '%s://%s%s' % (scheme, host_port, url)
|
||||||
else:
|
|
||||||
port = ':%s' % port
|
|
||||||
url = '%s://%s%s%s' % (scheme, host, port, url)
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
||||||
@@ -200,12 +208,12 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
called from main.wsgibase to rewrite the http response.
|
called from main.wsgibase to rewrite the http response.
|
||||||
"""
|
"""
|
||||||
status = int(str(http_response.status).split()[0])
|
status = int(str(http_response.status).split()[0])
|
||||||
if status>=399 and thread.routes.routes_onerror:
|
if status>=399 and THREAD_LOCAL.routes.routes_onerror:
|
||||||
keys=set(('%s/%s' % (request.application, status),
|
keys=set(('%s/%s' % (request.application, status),
|
||||||
'%s/*' % (request.application),
|
'%s/*' % (request.application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,uri) in thread.routes.routes_onerror:
|
for (key,uri) in THREAD_LOCAL.routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if uri == '!':
|
if uri == '!':
|
||||||
# do nothing!
|
# do nothing!
|
||||||
@@ -229,7 +237,7 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
path_info = '/' + path_info.lstrip('/') # add leading '/' if missing
|
path_info = '/' + path_info.lstrip('/') # add leading '/' if missing
|
||||||
environ['PATH_INFO'] = path_info
|
environ['PATH_INFO'] = path_info
|
||||||
error_handling_path = \
|
error_handling_path = \
|
||||||
url_in(request, environ)[1]['PATH_INFO']
|
url_in(request, environ)[2]['PATH_INFO']
|
||||||
# Avoid infinite loop.
|
# Avoid infinite loop.
|
||||||
if error_handling_path != error_raising_path:
|
if error_handling_path != error_raising_path:
|
||||||
# wsgibase will be called recursively with the routes_onerror path.
|
# wsgibase will be called recursively with the routes_onerror path.
|
||||||
@@ -243,12 +251,12 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
|
|||||||
def try_redirect_on_error(http_object, request, ticket=None):
|
def try_redirect_on_error(http_object, request, ticket=None):
|
||||||
"called from main.wsgibase to rewrite the http response"
|
"called from main.wsgibase to rewrite the http response"
|
||||||
status = int(str(http_object.status).split()[0])
|
status = int(str(http_object.status).split()[0])
|
||||||
if status>399 and thread.routes.routes_onerror:
|
if status>399 and THREAD_LOCAL.routes.routes_onerror:
|
||||||
keys=set(('%s/%s' % (request.application, status),
|
keys=set(('%s/%s' % (request.application, status),
|
||||||
'%s/*' % (request.application),
|
'%s/*' % (request.application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,redir) in thread.routes.routes_onerror:
|
for (key,redir) in THREAD_LOCAL.routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if redir == '!':
|
if redir == '!':
|
||||||
break
|
break
|
||||||
@@ -281,7 +289,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
global params_apps
|
global params_apps
|
||||||
params_apps = dict()
|
params_apps = dict()
|
||||||
params = _params_default(app=None) # regex rewrite parameters
|
params = _params_default(app=None) # regex rewrite parameters
|
||||||
thread.routes = params # default to base regex rewrite parameters
|
THREAD_LOCAL.routes = params # default to base regex rewrite parameters
|
||||||
routers = None
|
routers = None
|
||||||
|
|
||||||
if isinstance(rdict, dict):
|
if isinstance(rdict, dict):
|
||||||
@@ -328,7 +336,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
|
|||||||
|
|
||||||
if app is None:
|
if app is None:
|
||||||
params = p # install base rewrite parameters
|
params = p # install base rewrite parameters
|
||||||
thread.routes = params # install default as current routes
|
THREAD_LOCAL.routes = params # install default as current routes
|
||||||
#
|
#
|
||||||
# create the BASE router if routers in use
|
# create the BASE router if routers in use
|
||||||
#
|
#
|
||||||
@@ -537,24 +545,25 @@ def regex_select(env=None, app=None, request=None):
|
|||||||
select a set of regex rewrite params for the current request
|
select a set of regex rewrite params for the current request
|
||||||
"""
|
"""
|
||||||
if app:
|
if app:
|
||||||
thread.routes = params_apps.get(app, params)
|
THREAD_LOCAL.routes = params_apps.get(app, params)
|
||||||
elif env and params.routes_app:
|
elif env and params.routes_app:
|
||||||
if routers:
|
if routers:
|
||||||
map_url_in(request, env, app=True)
|
map_url_in(request, env, app=True)
|
||||||
else:
|
else:
|
||||||
app = regex_uri(env, params.routes_app, "routes_app")
|
app = regex_uri(env, params.routes_app, "routes_app")
|
||||||
thread.routes = params_apps.get(app, params)
|
THREAD_LOCAL.routes = params_apps.get(app, params)
|
||||||
else:
|
else:
|
||||||
thread.routes = params # default to base rewrite parameters
|
THREAD_LOCAL.routes = params # default to base rewrite parameters
|
||||||
log_rewrite("select routing parameters: %s" % thread.routes.name)
|
log_rewrite("select routing parameters: %s" % THREAD_LOCAL.routes.name)
|
||||||
return app # for doctest
|
return app # for doctest
|
||||||
|
|
||||||
def regex_filter_in(e):
|
def regex_filter_in(e):
|
||||||
"regex rewrite incoming URL"
|
"regex rewrite incoming URL"
|
||||||
|
routes = THREAD_LOCAL.routes
|
||||||
query = e.get('QUERY_STRING', None)
|
query = e.get('QUERY_STRING', None)
|
||||||
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
||||||
if thread.routes.routes_in:
|
if routes.routes_in:
|
||||||
path = regex_uri(e, thread.routes.routes_in,
|
path = regex_uri(e, routes.routes_in,
|
||||||
"routes_in", e['PATH_INFO'])
|
"routes_in", e['PATH_INFO'])
|
||||||
rmatch = regex_redirect.match(path)
|
rmatch = regex_redirect.match(path)
|
||||||
if rmatch:
|
if rmatch:
|
||||||
@@ -570,58 +579,7 @@ def regex_filter_in(e):
|
|||||||
e['REQUEST_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
e['REQUEST_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
|
||||||
return e
|
return e
|
||||||
|
|
||||||
|
|
||||||
# pattern to replace spaces with underscore in URL
|
|
||||||
# also the html escaped variants '+' and '%20' are covered
|
|
||||||
regex_space = re.compile('(\+|\s|%20)+')
|
|
||||||
|
|
||||||
# pattern to find valid paths in url /application/controller/...
|
|
||||||
# this could be:
|
|
||||||
# for static pages:
|
|
||||||
# /<b:application>/static/<x:file>
|
|
||||||
# for dynamic pages:
|
|
||||||
# /<a:application>[/<c:controller>[/<f:function>[.<e:ext>][/<s:args>]]]
|
|
||||||
# application, controller, function and ext may only contain [a-zA-Z0-9_]
|
|
||||||
# file and args may also contain '-', '=', '.' and '/'
|
|
||||||
# apps in routes_apps_raw must parse raw_args into args
|
|
||||||
|
|
||||||
regex_static = re.compile(r'''
|
|
||||||
(^ # static pages
|
|
||||||
/(?P<b> \w+) # b=app
|
|
||||||
/static # /b/static
|
|
||||||
/(?P<x> (\w[\-\=\./]?)* ) # x=file
|
|
||||||
$)
|
|
||||||
''', re.X)
|
|
||||||
|
|
||||||
regex_url = re.compile(r'''
|
|
||||||
(^( # (/a/c/f.e/s)
|
|
||||||
/(?P<a> [\w\s+]+ ) # /a=app
|
|
||||||
( # (/c.f.e/s)
|
|
||||||
/(?P<c> [\w\s+]+ ) # /a/c=controller
|
|
||||||
( # (/f.e/s)
|
|
||||||
/(?P<f> [\w\s+]+ ) # /a/c/f=function
|
|
||||||
( # (.e)
|
|
||||||
\.(?P<e> [\w\s+]+ ) # /a/c/f.e=extension
|
|
||||||
)?
|
|
||||||
( # (/s)
|
|
||||||
/(?P<r> # /a/c/f.e/r=raw_args
|
|
||||||
.*
|
|
||||||
)
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
/?$)
|
|
||||||
''', re.X)
|
|
||||||
|
|
||||||
regex_args = re.compile(r'''
|
|
||||||
(^
|
|
||||||
(?P<s>
|
|
||||||
( [\w@/-][=.]? )* # s=args
|
|
||||||
)?
|
|
||||||
/?$) # trailing slash
|
|
||||||
''', re.X)
|
|
||||||
|
|
||||||
def sluggify(key):
|
def sluggify(key):
|
||||||
return key.lower().replace('.','_')
|
return key.lower().replace('.','_')
|
||||||
|
|
||||||
@@ -635,72 +593,61 @@ def regex_url_in(request, environ):
|
|||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
regex_select(env=environ, request=request)
|
regex_select(env=environ, request=request)
|
||||||
|
routes = THREAD_LOCAL.routes
|
||||||
if thread.routes.routes_in:
|
if routes.routes_in:
|
||||||
environ = regex_filter_in(environ)
|
environ = regex_filter_in(environ)
|
||||||
|
|
||||||
request.env.update((sluggify(k),v) for k,v in environ.iteritems())
|
request.env.update((sluggify(k),v) for k,v in environ.iteritems())
|
||||||
|
|
||||||
path = request.env.path_info.replace('\\', '/')
|
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# serve if a static file
|
# serve if a static file
|
||||||
# ##################################################
|
# ##################################################
|
||||||
|
|
||||||
match = regex_static.match(regex_space.sub('_', path))
|
path = request.env.path_info.replace('\\', '/') or '/'
|
||||||
if match and match.group('x'):
|
path = regex_space.sub('_',path)
|
||||||
static_file = pjoin(request.env.applications_parent,
|
if path.endswith('/') and len(path)>1: path = path[:-1]
|
||||||
'applications', match.group('b'),
|
|
||||||
'static', match.group('x'))
|
|
||||||
return (static_file, environ)
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# parse application, controller and function
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
path = re.sub('%20', ' ', path)
|
|
||||||
match = regex_url.match(path)
|
match = regex_url.match(path)
|
||||||
if not match or match.group('c') == 'static':
|
if not match:
|
||||||
raise HTTP(400,
|
raise HTTP(400,
|
||||||
thread.routes.error_message % 'invalid request',
|
routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid path')
|
web2py_error='invalid path')
|
||||||
|
elif match.group('c')=='static':
|
||||||
request.application = \
|
application = match.group('a')
|
||||||
regex_space.sub('_', match.group('a') or thread.routes.default_application)
|
version, filename = None, match.group('z')
|
||||||
request.controller = \
|
items = filename.split('/',1)
|
||||||
regex_space.sub('_', match.group('c') or thread.routes.default_controller)
|
if regex_version.match(items[0]):
|
||||||
request.function = \
|
version,filename = items
|
||||||
regex_space.sub('_', match.group('f') or thread.routes.default_function)
|
static_file = pjoin(request.env.applications_parent,
|
||||||
group_e = match.group('e')
|
'applications', application,
|
||||||
request.raw_extension = group_e and regex_space.sub('_', group_e) or None
|
'static', filename)
|
||||||
request.extension = request.raw_extension or 'html'
|
return (static_file, version, environ)
|
||||||
request.raw_args = match.group('r')
|
else:
|
||||||
request.args = List([])
|
# ##################################################
|
||||||
if request.application in thread.routes.routes_apps_raw:
|
# parse application, controller and function
|
||||||
# application is responsible for parsing args
|
# ##################################################
|
||||||
request.args = None
|
request.application = match.group('a') or routes.default_application
|
||||||
elif request.raw_args:
|
request.controller = match.group('c') or routes.default_controller
|
||||||
match = regex_args.match(request.raw_args.replace(' ', '_'))
|
request.function = match.group('f') or routes.default_function
|
||||||
if match:
|
request.raw_extension = match.group('e')
|
||||||
group_s = match.group('s')
|
request.extension = request.raw_extension or 'html'
|
||||||
request.args = \
|
request.raw_args = match.group('s')
|
||||||
List((group_s and group_s.split('/')) or [])
|
if request.application in routes.routes_apps_raw:
|
||||||
if request.args and request.args[-1] == '':
|
# application is responsible for parsing args
|
||||||
request.args.pop() # adjust for trailing empty arg
|
request.args = None
|
||||||
|
elif request.raw_args:
|
||||||
|
request.args = List(request.raw_args.split('/')[1:])
|
||||||
else:
|
else:
|
||||||
raise HTTP(400,
|
request.args = List([])
|
||||||
thread.routes.error_message % 'invalid request',
|
return (None, None, environ)
|
||||||
web2py_error='invalid path (args)')
|
|
||||||
return (None, environ)
|
|
||||||
|
|
||||||
|
|
||||||
def regex_filter_out(url, e=None):
|
def regex_filter_out(url, e=None):
|
||||||
"regex rewrite outgoing URL"
|
"regex rewrite outgoing URL"
|
||||||
if not hasattr(thread, 'routes'):
|
if not hasattr(THREAD_LOCAL, 'routes'):
|
||||||
regex_select() # ensure thread.routes is set (for application threads)
|
regex_select() # ensure routes is set (for application threads)
|
||||||
|
routes = THREAD_LOCAL.routes
|
||||||
if routers:
|
if routers:
|
||||||
return url # already filtered
|
return url # already filtered
|
||||||
if thread.routes.routes_out:
|
if routes.routes_out:
|
||||||
items = url.split('?', 1)
|
items = url.split('?', 1)
|
||||||
if e:
|
if e:
|
||||||
host = e.get('http_host', 'localhost').lower()
|
host = e.get('http_host', 'localhost').lower()
|
||||||
@@ -713,7 +660,7 @@ def regex_filter_out(url, e=None):
|
|||||||
e.get('request_method', 'get').lower(), items[0])
|
e.get('request_method', 'get').lower(), items[0])
|
||||||
else:
|
else:
|
||||||
items[0] = ':http://localhost:get %s' % items[0]
|
items[0] = ':http://localhost:get %s' % items[0]
|
||||||
for (regex, value, tmp) in thread.routes.routes_out:
|
for (regex, value, tmp) in routes.routes_out:
|
||||||
if regex.match(items[0]):
|
if regex.match(items[0]):
|
||||||
rewritten = '?'.join([regex.sub(value, items[0])] + items[1:])
|
rewritten = '?'.join([regex.sub(value, items[0])] + items[1:])
|
||||||
log_rewrite('routes_out: [%s] -> %s' % (url, rewritten))
|
log_rewrite('routes_out: [%s] -> %s' % (url, rewritten))
|
||||||
@@ -722,9 +669,9 @@ def regex_filter_out(url, e=None):
|
|||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
def filter_url(url, method='get', remote='0.0.0.0',
|
def filter_url(url, method='get', remote='0.0.0.0',
|
||||||
out=False, app=False, lang=None,
|
out=False, app=False, lang=None,
|
||||||
domain=(None,None), env=False, scheme=None,
|
domain=(None,None), env=False, scheme=None,
|
||||||
host=None, port=None):
|
host=None, port=None):
|
||||||
"""
|
"""
|
||||||
doctest/unittest interface to regex_filter_in() and regex_filter_out()
|
doctest/unittest interface to regex_filter_in() and regex_filter_out()
|
||||||
@@ -793,7 +740,7 @@ def filter_url(url, method='get', remote='0.0.0.0',
|
|||||||
|
|
||||||
# rewrite inbound URL
|
# rewrite inbound URL
|
||||||
#
|
#
|
||||||
(static, e) = url_in(request, e)
|
(static, version, e) = url_in(request, e)
|
||||||
if static:
|
if static:
|
||||||
return static
|
return static
|
||||||
result = "/%s/%s/%s" % (request.application, request.controller, request.function)
|
result = "/%s/%s/%s" % (request.application, request.controller, request.function)
|
||||||
@@ -812,12 +759,13 @@ def filter_url(url, method='get', remote='0.0.0.0',
|
|||||||
|
|
||||||
def filter_err(status, application='app', ticket='tkt'):
|
def filter_err(status, application='app', ticket='tkt'):
|
||||||
"doctest/unittest interface to routes_onerror"
|
"doctest/unittest interface to routes_onerror"
|
||||||
if status > 399 and thread.routes.routes_onerror:
|
routes = THREAD_LOCAL.routes
|
||||||
|
if status > 399 and routes.routes_onerror:
|
||||||
keys = set(('%s/%s' % (application, status),
|
keys = set(('%s/%s' % (application, status),
|
||||||
'%s/*' % (application),
|
'%s/*' % (application),
|
||||||
'*/%s' % (status),
|
'*/%s' % (status),
|
||||||
'*/*'))
|
'*/*'))
|
||||||
for (key,redir) in thread.routes.routes_onerror:
|
for (key,redir) in routes.routes_onerror:
|
||||||
if key in keys:
|
if key in keys:
|
||||||
if redir == '!':
|
if redir == '!':
|
||||||
break
|
break
|
||||||
@@ -921,12 +869,12 @@ class MapUrlIn(object):
|
|||||||
self.pop_arg_if(self.application == arg0)
|
self.pop_arg_if(self.application == arg0)
|
||||||
|
|
||||||
if not base._acfe_match.match(self.application):
|
if not base._acfe_match.match(self.application):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error="invalid application: '%s'" % self.application)
|
web2py_error="invalid application: '%s'" % self.application)
|
||||||
|
|
||||||
if self.application not in routers and \
|
if self.application not in routers and \
|
||||||
(self.application != thread.routes.default_application or self.application == 'welcome'):
|
(self.application != THREAD_LOCAL.routes.default_application or self.application == 'welcome'):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error="unknown application: '%s'" % self.application)
|
web2py_error="unknown application: '%s'" % self.application)
|
||||||
|
|
||||||
# set the application router
|
# set the application router
|
||||||
@@ -956,14 +904,15 @@ class MapUrlIn(object):
|
|||||||
a root-static file is one whose incoming URL expects it to be at the root,
|
a root-static file is one whose incoming URL expects it to be at the root,
|
||||||
typically robots.txt & favicon.ico
|
typically robots.txt & favicon.ico
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
if len(self.args) == 1 and self.arg0 in self.router.root_static:
|
||||||
self.controller = self.request.controller = 'static'
|
self.controller = self.request.controller = 'static'
|
||||||
root_static_file = pjoin(self.request.env.applications_parent,
|
root_static_file = pjoin(self.request.env.applications_parent,
|
||||||
'applications', self.application,
|
'applications', self.application,
|
||||||
self.controller, self.arg0)
|
self.controller, self.arg0)
|
||||||
log_rewrite("route: root static=%s" % root_static_file)
|
log_rewrite("route: root static=%s" % root_static_file)
|
||||||
return root_static_file
|
return root_static_file, None
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
def map_language(self):
|
def map_language(self):
|
||||||
"handle language (no hyphen mapping)"
|
"handle language (no hyphen mapping)"
|
||||||
@@ -989,7 +938,7 @@ class MapUrlIn(object):
|
|||||||
self.pop_arg_if(arg0 == self.controller)
|
self.pop_arg_if(arg0 == self.controller)
|
||||||
log_rewrite("route: controller=%s" % self.controller)
|
log_rewrite("route: controller=%s" % self.controller)
|
||||||
if not self.router._acfe_match.match(self.controller):
|
if not self.router._acfe_match.match(self.controller):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid controller')
|
web2py_error='invalid controller')
|
||||||
|
|
||||||
def map_static(self):
|
def map_static(self):
|
||||||
@@ -998,8 +947,12 @@ class MapUrlIn(object):
|
|||||||
file_match but no hyphen mapping
|
file_match but no hyphen mapping
|
||||||
'''
|
'''
|
||||||
if self.controller != 'static':
|
if self.controller != 'static':
|
||||||
return None
|
return None, None
|
||||||
file = '/'.join(self.args)
|
version = regex_version.match(self.args(0))
|
||||||
|
if self.args and version:
|
||||||
|
file = '/'.join(self.args[1:])
|
||||||
|
else:
|
||||||
|
file = '/'.join(self.args)
|
||||||
if len(self.args) == 0:
|
if len(self.args) == 0:
|
||||||
bad_static = True # require a file name
|
bad_static = True # require a file name
|
||||||
elif '/' in self.file_match:
|
elif '/' in self.file_match:
|
||||||
@@ -1013,7 +966,7 @@ class MapUrlIn(object):
|
|||||||
if bad_static:
|
if bad_static:
|
||||||
log_rewrite('bad static path=%s' % file)
|
log_rewrite('bad static path=%s' % file)
|
||||||
raise HTTP(400,
|
raise HTTP(400,
|
||||||
thread.routes.error_message % 'invalid request',
|
THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid static file')
|
web2py_error='invalid static file')
|
||||||
#
|
#
|
||||||
# support language-specific static subdirectories,
|
# support language-specific static subdirectories,
|
||||||
@@ -1030,7 +983,7 @@ class MapUrlIn(object):
|
|||||||
'static', file)
|
'static', file)
|
||||||
self.extension = None
|
self.extension = None
|
||||||
log_rewrite("route: static=%s" % static_file)
|
log_rewrite("route: static=%s" % static_file)
|
||||||
return static_file
|
return static_file, version
|
||||||
|
|
||||||
def map_function(self):
|
def map_function(self):
|
||||||
"handle function.extension"
|
"handle function.extension"
|
||||||
@@ -1055,10 +1008,10 @@ class MapUrlIn(object):
|
|||||||
log_rewrite("route: function.ext=%s.%s" % (self.function, self.extension))
|
log_rewrite("route: function.ext=%s.%s" % (self.function, self.extension))
|
||||||
|
|
||||||
if not self.router._acfe_match.match(self.function):
|
if not self.router._acfe_match.match(self.function):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid function')
|
web2py_error='invalid function')
|
||||||
if self.extension and not self.router._acfe_match.match(self.extension):
|
if self.extension and not self.router._acfe_match.match(self.extension):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid extension')
|
web2py_error='invalid extension')
|
||||||
|
|
||||||
def validate_args(self):
|
def validate_args(self):
|
||||||
@@ -1067,14 +1020,14 @@ class MapUrlIn(object):
|
|||||||
'''
|
'''
|
||||||
for arg in self.args:
|
for arg in self.args:
|
||||||
if not self.router._args_match.match(arg):
|
if not self.router._args_match.match(arg):
|
||||||
raise HTTP(400, thread.routes.error_message % 'invalid request',
|
raise HTTP(400, THREAD_LOCAL.routes.error_message % 'invalid request',
|
||||||
web2py_error='invalid arg <%s>' % arg)
|
web2py_error='invalid arg <%s>' % arg)
|
||||||
|
|
||||||
def sluggify(self):
|
def sluggify(self):
|
||||||
""
|
""
|
||||||
self.request.env.update(
|
self.request.env.update(
|
||||||
(sluggify(k),v) for k,v in self.env.iteritems())
|
(sluggify(k),v) for k,v in self.env.iteritems())
|
||||||
|
|
||||||
def update_request(self):
|
def update_request(self):
|
||||||
'''
|
'''
|
||||||
update request from self
|
update request from self
|
||||||
@@ -1098,7 +1051,7 @@ class MapUrlIn(object):
|
|||||||
if self.language:
|
if self.language:
|
||||||
uri = '/%s%s' % (self.language, uri)
|
uri = '/%s%s' % (self.language, uri)
|
||||||
uri = '/%s%s%s%s' % (
|
uri = '/%s%s%s%s' % (
|
||||||
app,
|
app,
|
||||||
uri,
|
uri,
|
||||||
urllib.quote('/'+'/'.join(str(x) for x in self.args)) if self.args else '',
|
urllib.quote('/'+'/'.join(str(x) for x in self.args)) if self.args else '',
|
||||||
('?' + self.query) if self.query else '')
|
('?' + self.query) if self.query else '')
|
||||||
@@ -1292,24 +1245,24 @@ def map_url_in(request, env, app=False):
|
|||||||
|
|
||||||
# initialize router-url object
|
# initialize router-url object
|
||||||
#
|
#
|
||||||
thread.routes = params # default to base routes
|
THREAD_LOCAL.routes = params # default to base routes
|
||||||
map = MapUrlIn(request=request, env=env)
|
map = MapUrlIn(request=request, env=env)
|
||||||
map.sluggify()
|
map.sluggify()
|
||||||
map.map_prefix() # strip prefix if present
|
map.map_prefix() # strip prefix if present
|
||||||
map.map_app() # determine application
|
map.map_app() # determine application
|
||||||
|
|
||||||
# configure thread.routes for error rewrite
|
# configure THREAD_LOCAL.routes for error rewrite
|
||||||
#
|
#
|
||||||
if params.routes_app:
|
if params.routes_app:
|
||||||
thread.routes = params_apps.get(app, params)
|
THREAD_LOCAL.routes = params_apps.get(app, params)
|
||||||
|
|
||||||
if app:
|
if app:
|
||||||
return map.application
|
return map.application
|
||||||
|
|
||||||
root_static_file = map.map_root_static() # handle root-static files
|
root_static_file, version = map.map_root_static() # handle root-static files
|
||||||
if root_static_file:
|
if root_static_file:
|
||||||
map.update_request()
|
map.update_request()
|
||||||
return (root_static_file, map.env)
|
return (root_static_file, version, map.env)
|
||||||
# handle mapping of lang/static to static/lang in externally-rewritten URLs
|
# handle mapping of lang/static to static/lang in externally-rewritten URLs
|
||||||
# in case we have to handle them ourselves
|
# in case we have to handle them ourselves
|
||||||
if map.languages and map.map_static is False and map.arg0 == 'static' and map.args(1) in map.languages:
|
if map.languages and map.map_static is False and map.arg0 == 'static' and map.args(1) in map.languages:
|
||||||
@@ -1318,14 +1271,14 @@ def map_url_in(request, env, app=False):
|
|||||||
else:
|
else:
|
||||||
map.map_language()
|
map.map_language()
|
||||||
map.map_controller()
|
map.map_controller()
|
||||||
static_file = map.map_static()
|
static_file, version = map.map_static()
|
||||||
if static_file:
|
if static_file:
|
||||||
map.update_request()
|
map.update_request()
|
||||||
return (static_file, map.env)
|
return (static_file, version, map.env)
|
||||||
map.map_function()
|
map.map_function()
|
||||||
map.validate_args()
|
map.validate_args()
|
||||||
map.update_request()
|
map.update_request()
|
||||||
return (None, map.env)
|
return (None, None, map.env)
|
||||||
|
|
||||||
def map_url_out(request, env, application, controller,
|
def map_url_out(request, env, application, controller,
|
||||||
function, args, other, scheme, host, port):
|
function, args, other, scheme, host, port):
|
||||||
@@ -1363,10 +1316,3 @@ def get_effective_router(appname):
|
|||||||
if not routers or appname not in routers:
|
if not routers or appname not in routers:
|
||||||
return None
|
return None
|
||||||
return Storage(routers[appname]) # return a copy
|
return Storage(routers[appname]) # return a copy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+16
-222
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
# This file is part of the Rocket Web Server
|
# This file is part of the Rocket Web Server
|
||||||
# Copyright (c) 2011 Timothy Farrell
|
# Copyright (c) 2011 Timothy Farrell
|
||||||
|
# Modified by Massimo Di Pierro
|
||||||
|
|
||||||
# Import System Modules
|
# Import System Modules
|
||||||
import sys
|
import sys
|
||||||
@@ -12,7 +13,7 @@ import platform
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
# Define Constants
|
# Define Constants
|
||||||
VERSION = '1.2.4'
|
VERSION = '1.2.5'
|
||||||
SERVER_NAME = socket.gethostname()
|
SERVER_NAME = socket.gethostname()
|
||||||
SERVER_SOFTWARE = 'Rocket %s' % VERSION
|
SERVER_SOFTWARE = 'Rocket %s' % VERSION
|
||||||
HTTP_SERVER_SOFTWARE = '%s Python/%s' % (SERVER_SOFTWARE, sys.version.split(' ')[0])
|
HTTP_SERVER_SOFTWARE = '%s Python/%s' % (SERVER_SOFTWARE, sys.version.split(' ')[0])
|
||||||
@@ -1454,41 +1455,34 @@ class Worker(Thread):
|
|||||||
return req
|
return req
|
||||||
|
|
||||||
|
|
||||||
def read_headers(self, sock_file):
|
def read_headers(self, sock_file, environ):
|
||||||
try:
|
try:
|
||||||
headers = dict()
|
|
||||||
l = sock_file.readline()
|
|
||||||
|
|
||||||
lname = None
|
lname = None
|
||||||
lval = None
|
|
||||||
while True:
|
while True:
|
||||||
|
l = sock_file.readline()
|
||||||
if PY3K:
|
if PY3K:
|
||||||
try:
|
try:
|
||||||
l = str(l, 'ISO-8859-1')
|
l = str(l, 'ISO-8859-1')
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
self.err_log.warning('Client sent invalid header: ' + repr(l))
|
self.err_log.warning('Invalid request header: '+repr(l))
|
||||||
|
|
||||||
if l == '\r\n':
|
if l.strip().replace('\0','') == '':
|
||||||
break
|
break
|
||||||
|
elif l[0] in ' \t' and lname:
|
||||||
if l[0] in ' \t' and lname:
|
|
||||||
# Some headers take more than one line
|
# Some headers take more than one line
|
||||||
lval += ',' + l.strip()
|
environ[lname] += ' ' + l.strip()
|
||||||
else:
|
else:
|
||||||
# HTTP header values are latin-1 encoded
|
# HTTP header values are latin-1 encoded
|
||||||
l = l.split(':', 1)
|
l = l.split(':', 1)
|
||||||
# HTTP header names are us-ascii encoded
|
# HTTP header names are us-ascii encoded
|
||||||
|
|
||||||
lname = l[0].strip().upper().replace('-', '_')
|
lname = str('HTTP_'+l[0].strip().upper().replace('-', '_'))
|
||||||
lval = l[-1].strip()
|
lval = str(l[-1].strip())
|
||||||
headers[str(lname)] = str(lval)
|
environ[lname] = lval
|
||||||
|
|
||||||
l = sock_file.readline()
|
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
raise SocketTimeout("Socket timed out before request.")
|
raise SocketTimeout("Socket timed out before request.")
|
||||||
|
|
||||||
return headers
|
|
||||||
|
|
||||||
class SocketTimeout(Exception):
|
class SocketTimeout(Exception):
|
||||||
"Exception for when a socket times out between requests."
|
"Exception for when a socket times out between requests."
|
||||||
pass
|
pass
|
||||||
@@ -1549,209 +1543,10 @@ class ChunkedReader(object):
|
|||||||
yield self.readline()
|
yield self.readline()
|
||||||
|
|
||||||
def get_method(method):
|
def get_method(method):
|
||||||
|
methods = dict(wsgi=WSGIWorker)
|
||||||
|
|
||||||
methods = dict(wsgi=WSGIWorker,
|
|
||||||
fs=FileSystemWorker)
|
|
||||||
return methods[method.lower()]
|
return methods[method.lower()]
|
||||||
|
|
||||||
# Monolithic build...end of module: rocket\worker.py
|
# Monolithic build...end of module: rocket\worker.py
|
||||||
# Monolithic build...start of module: rocket\methods\__init__.py
|
|
||||||
|
|
||||||
# Monolithic build...end of module: rocket\methods\__init__.py
|
|
||||||
# Monolithic build...start of module: rocket\methods\fs.py
|
|
||||||
|
|
||||||
# Import System Modules
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import mimetypes
|
|
||||||
from email.utils import formatdate
|
|
||||||
from wsgiref.headers import Headers
|
|
||||||
from wsgiref.util import FileWrapper
|
|
||||||
# Import Package Modules
|
|
||||||
# package imports removed in monolithic build
|
|
||||||
|
|
||||||
|
|
||||||
# Define Constants
|
|
||||||
CHUNK_SIZE = 2**16 # 64 Kilobyte chunks
|
|
||||||
HEADER_RESPONSE = '''HTTP/1.1 %s\r\n%s'''
|
|
||||||
INDEX_HEADER = '''\
|
|
||||||
<html>
|
|
||||||
<head><title>Directory Index: %(path)s</title>
|
|
||||||
<style> .parent { margin-bottom: 1em; }</style>
|
|
||||||
</head>
|
|
||||||
<body><h1>Directory Index: %(path)s</h1>
|
|
||||||
<table>
|
|
||||||
<tr><th>Directories</th></tr>
|
|
||||||
'''
|
|
||||||
INDEX_ROW = '''<tr><td><div class="%(cls)s"><a href="/%(link)s">%(name)s</a></div></td></tr>'''
|
|
||||||
INDEX_FOOTER = '''</table></body></html>\r\n'''
|
|
||||||
|
|
||||||
class LimitingFileWrapper(FileWrapper):
|
|
||||||
def __init__(self, limit=None, *args, **kwargs):
|
|
||||||
self.limit = limit
|
|
||||||
FileWrapper.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def read(self, amt):
|
|
||||||
if amt > self.limit:
|
|
||||||
amt = self.limit
|
|
||||||
self.limit -= amt
|
|
||||||
return FileWrapper.read(self, amt)
|
|
||||||
|
|
||||||
class FileSystemWorker(Worker):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Builds some instance variables that will last the life of the
|
|
||||||
thread."""
|
|
||||||
|
|
||||||
Worker.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
self.root = os.path.abspath(self.app_info['document_root'])
|
|
||||||
self.display_index = self.app_info['display_index']
|
|
||||||
|
|
||||||
def serve_file(self, filepath, headers):
|
|
||||||
filestat = os.stat(filepath)
|
|
||||||
self.size = filestat.st_size
|
|
||||||
modtime = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
|
|
||||||
time.gmtime(filestat.st_mtime))
|
|
||||||
self.headers.add_header('Last-Modified', modtime)
|
|
||||||
if headers.get('if_modified_since') == modtime:
|
|
||||||
# The browser cache is up-to-date, send a 304.
|
|
||||||
self.status = "304 Not Modified"
|
|
||||||
self.data = []
|
|
||||||
return
|
|
||||||
|
|
||||||
ct = mimetypes.guess_type(filepath)[0]
|
|
||||||
self.content_type = ct if ct else 'text/plain'
|
|
||||||
try:
|
|
||||||
f = open(filepath, 'rb')
|
|
||||||
self.headers['Pragma'] = 'cache'
|
|
||||||
self.headers['Cache-Control'] = 'private'
|
|
||||||
self.headers['Content-Length'] = str(self.size)
|
|
||||||
if self.etag:
|
|
||||||
self.headers.add_header('Etag', self.etag)
|
|
||||||
if self.expires:
|
|
||||||
self.headers.add_header('Expires', self.expires)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Implement 206 partial file support.
|
|
||||||
start, end = headers['range'].split('-')
|
|
||||||
start = 0 if not start.isdigit() else int(start)
|
|
||||||
end = self.size if not end.isdigit() else int(end)
|
|
||||||
if self.size < end or start < 0:
|
|
||||||
self.status = "214 Unsatisfiable Range Requested"
|
|
||||||
self.data = FileWrapper(f, CHUNK_SIZE)
|
|
||||||
else:
|
|
||||||
f.seek(start)
|
|
||||||
self.data = LimitingFileWrapper(f, CHUNK_SIZE, limit=end)
|
|
||||||
self.status = "206 Partial Content"
|
|
||||||
except:
|
|
||||||
self.data = FileWrapper(f, CHUNK_SIZE)
|
|
||||||
except IOError:
|
|
||||||
self.status = "403 Forbidden"
|
|
||||||
|
|
||||||
def serve_dir(self, pth, rpth):
|
|
||||||
def rel_path(path):
|
|
||||||
return os.path.normpath(path[len(self.root):] if path.startswith(self.root) else path)
|
|
||||||
|
|
||||||
if not self.display_index:
|
|
||||||
self.status = '404 File Not Found'
|
|
||||||
return b('')
|
|
||||||
else:
|
|
||||||
self.content_type = 'text/html'
|
|
||||||
|
|
||||||
dir_contents = [os.path.join(pth, x) for x in os.listdir(os.path.normpath(pth))]
|
|
||||||
dir_contents.sort()
|
|
||||||
|
|
||||||
dirs = [rel_path(x)+'/' for x in dir_contents if os.path.isdir(x)]
|
|
||||||
files = [rel_path(x) for x in dir_contents if os.path.isfile(x)]
|
|
||||||
|
|
||||||
self.data = [INDEX_HEADER % dict(path='/'+rpth)]
|
|
||||||
if rpth:
|
|
||||||
self.data += [INDEX_ROW % dict(name='(parent directory)', cls='dir parent', link='/'.join(rpth[:-1].split('/')[:-1]))]
|
|
||||||
self.data += [INDEX_ROW % dict(name=os.path.basename(x[:-1]), link=os.path.join(rpth, os.path.basename(x[:-1])).replace('\\', '/'), cls='dir') for x in dirs]
|
|
||||||
self.data += ['<tr><th>Files</th></tr>']
|
|
||||||
self.data += [INDEX_ROW % dict(name=os.path.basename(x), link=os.path.join(rpth, os.path.basename(x)).replace('\\', '/'), cls='file') for x in files]
|
|
||||||
self.data += [INDEX_FOOTER]
|
|
||||||
self.headers['Content-Length'] = self.size = str(sum([len(x) for x in self.data]))
|
|
||||||
self.status = '200 OK'
|
|
||||||
|
|
||||||
def run_app(self, conn):
|
|
||||||
self.status = "200 OK"
|
|
||||||
self.size = 0
|
|
||||||
self.expires = None
|
|
||||||
self.etag = None
|
|
||||||
self.content_type = 'text/plain'
|
|
||||||
self.content_length = None
|
|
||||||
|
|
||||||
if __debug__:
|
|
||||||
self.err_log.debug('Getting sock_file')
|
|
||||||
|
|
||||||
# Build our file-like object
|
|
||||||
sock_file = conn.makefile('rb',BUF_SIZE)
|
|
||||||
request = self.read_request_line(sock_file)
|
|
||||||
if request['method'].upper() not in ('GET', ):
|
|
||||||
self.status = "501 Not Implemented"
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Get our file path
|
|
||||||
reader = self.read_headers(sock_file)
|
|
||||||
headers = dict((k.lower(),v) for k,v in reader.iteritems())
|
|
||||||
rpath = request.get('path', '').lstrip('/')
|
|
||||||
filepath = os.path.join(self.root, rpath)
|
|
||||||
filepath = os.path.abspath(filepath)
|
|
||||||
if __debug__:
|
|
||||||
self.err_log.debug('Request for path: %s' % filepath)
|
|
||||||
|
|
||||||
self.closeConnection = headers.get('connection', 'close').lower() == 'close'
|
|
||||||
self.headers = Headers([('Date', formatdate(usegmt=True)),
|
|
||||||
('Server', HTTP_SERVER_SOFTWARE),
|
|
||||||
('Connection', headers.get('connection', 'close')),
|
|
||||||
])
|
|
||||||
|
|
||||||
if not filepath.lower().startswith(self.root.lower()):
|
|
||||||
# File must be within our root directory
|
|
||||||
self.status = "400 Bad Request"
|
|
||||||
self.closeConnection = True
|
|
||||||
elif not os.path.exists(filepath):
|
|
||||||
self.status = "404 File Not Found"
|
|
||||||
self.closeConnection = True
|
|
||||||
elif os.path.isdir(filepath):
|
|
||||||
self.serve_dir(filepath, rpath)
|
|
||||||
elif os.path.isfile(filepath):
|
|
||||||
self.serve_file(filepath, headers)
|
|
||||||
else:
|
|
||||||
# It exists but it's not a file or a directory????
|
|
||||||
# What is it then?
|
|
||||||
self.status = "501 Not Implemented"
|
|
||||||
self.closeConnection = True
|
|
||||||
|
|
||||||
h = self.headers
|
|
||||||
statcode, statstr = self.status.split(' ', 1)
|
|
||||||
statcode = int(statcode)
|
|
||||||
if statcode >= 400:
|
|
||||||
h.add_header('Content-Type', self.content_type)
|
|
||||||
self.data = [statstr]
|
|
||||||
|
|
||||||
# Build our output headers
|
|
||||||
header_data = HEADER_RESPONSE % (self.status, str(h))
|
|
||||||
|
|
||||||
# Send the headers
|
|
||||||
if __debug__:
|
|
||||||
self.err_log.debug('Sending Headers: %s' % repr(header_data))
|
|
||||||
self.conn.sendall(b(header_data))
|
|
||||||
|
|
||||||
for data in self.data:
|
|
||||||
self.conn.sendall(b(data))
|
|
||||||
|
|
||||||
if hasattr(self.data, 'close'):
|
|
||||||
self.data.close()
|
|
||||||
|
|
||||||
finally:
|
|
||||||
if __debug__:
|
|
||||||
self.err_log.debug('Finally closing sock_file')
|
|
||||||
sock_file.close()
|
|
||||||
|
|
||||||
# Monolithic build...end of module: rocket\methods\fs.py
|
|
||||||
# Monolithic build...start of module: rocket\methods\wsgi.py
|
# Monolithic build...start of module: rocket\methods\wsgi.py
|
||||||
|
|
||||||
# Import System Modules
|
# Import System Modules
|
||||||
@@ -1819,16 +1614,15 @@ class WSGIWorker(Worker):
|
|||||||
environ = self.base_environ.copy()
|
environ = self.base_environ.copy()
|
||||||
|
|
||||||
# Grab the headers
|
# Grab the headers
|
||||||
for k, v in self.read_headers(sock_file).items():
|
self.read_headers(sock_file,environ)
|
||||||
environ[str('HTTP_'+k)] = v
|
|
||||||
|
|
||||||
# Add CGI Variables
|
# Add CGI Variables
|
||||||
environ['REQUEST_METHOD'] = request['method']
|
|
||||||
environ['PATH_INFO'] = request['path']
|
|
||||||
environ['SERVER_PROTOCOL'] = request['protocol']
|
|
||||||
environ['SERVER_PORT'] = str(conn.server_port)
|
environ['SERVER_PORT'] = str(conn.server_port)
|
||||||
environ['REMOTE_PORT'] = str(conn.client_port)
|
environ['REMOTE_PORT'] = str(conn.client_port)
|
||||||
environ['REMOTE_ADDR'] = str(conn.client_addr)
|
environ['REMOTE_ADDR'] = str(conn.client_addr)
|
||||||
|
environ['REQUEST_METHOD'] = request['method']
|
||||||
|
environ['PATH_INFO'] = request['path']
|
||||||
|
environ['SERVER_PROTOCOL'] = request['protocol']
|
||||||
environ['QUERY_STRING'] = request['query_string']
|
environ['QUERY_STRING'] = request['query_string']
|
||||||
if 'HTTP_CONTENT_LENGTH' in environ:
|
if 'HTTP_CONTENT_LENGTH' in environ:
|
||||||
environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']
|
environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']
|
||||||
|
|||||||
+4
-3
@@ -88,7 +88,7 @@ except:
|
|||||||
from simplejson import loads, dumps
|
from simplejson import loads, dumps
|
||||||
|
|
||||||
|
|
||||||
from gluon import DAL, Field, IS_NOT_EMPTY, IS_IN_SET, IS_NOT_IN_DB, IS_INT_IN_RANGE
|
from gluon import DAL, Field, IS_NOT_EMPTY, IS_IN_SET, IS_NOT_IN_DB, IS_INT_IN_RANGE, IS_DATETIME
|
||||||
from gluon.utils import web2py_uuid
|
from gluon.utils import web2py_uuid
|
||||||
|
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ class Scheduler(MetaScheduler):
|
|||||||
Field('application_name',requires=IS_NOT_EMPTY(),
|
Field('application_name',requires=IS_NOT_EMPTY(),
|
||||||
default=None,writable=False),
|
default=None,writable=False),
|
||||||
Field('task_name',default=None),
|
Field('task_name',default=None),
|
||||||
Field('group_name',default='main',writable=False),
|
Field('group_name',default='main'),
|
||||||
Field('status',requires=IS_IN_SET(TASK_STATUS),
|
Field('status',requires=IS_IN_SET(TASK_STATUS),
|
||||||
default=QUEUED,writable=False),
|
default=QUEUED,writable=False),
|
||||||
Field('function_name',
|
Field('function_name',
|
||||||
@@ -454,7 +454,8 @@ class Scheduler(MetaScheduler):
|
|||||||
Field('args','text',default='[]',requires=TYPE(list)),
|
Field('args','text',default='[]',requires=TYPE(list)),
|
||||||
Field('vars','text',default='{}',requires=TYPE(dict)),
|
Field('vars','text',default='{}',requires=TYPE(dict)),
|
||||||
Field('enabled','boolean',default=True),
|
Field('enabled','boolean',default=True),
|
||||||
Field('start_time','datetime',default=now, requires=IS_NOT_EMPTY()),
|
Field('start_time','datetime',default=now,
|
||||||
|
requires = IS_DATETIME()),
|
||||||
Field('next_run_time','datetime',default=now),
|
Field('next_run_time','datetime',default=now),
|
||||||
Field('stop_time','datetime'),
|
Field('stop_time','datetime'),
|
||||||
Field('repeats','integer',default=1,comment="0=unlimited",
|
Field('repeats','integer',default=1,comment="0=unlimited",
|
||||||
|
|||||||
+1
-1
@@ -6,6 +6,7 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import socket
|
||||||
import platform
|
import platform
|
||||||
from storage import Storage
|
from storage import Storage
|
||||||
|
|
||||||
@@ -37,4 +38,3 @@ global_settings.is_jython = \
|
|||||||
str(sys.copyright).find('Jython') > 0
|
str(sys.copyright).find('Jython') > 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -19,7 +19,7 @@ import optparse
|
|||||||
import glob
|
import glob
|
||||||
import traceback
|
import traceback
|
||||||
import fileutils
|
import fileutils
|
||||||
import settings
|
from settings import global_settings
|
||||||
from utils import web2py_uuid
|
from utils import web2py_uuid
|
||||||
from compileapp import build_environment, read_pyc, run_models_in
|
from compileapp import build_environment, read_pyc, run_models_in
|
||||||
from restricted import RestrictedError
|
from restricted import RestrictedError
|
||||||
@@ -28,7 +28,6 @@ from storage import Storage
|
|||||||
from admin import w2p_unpack
|
from admin import w2p_unpack
|
||||||
from dal import BaseAdapter
|
from dal import BaseAdapter
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
def exec_environment(
|
def exec_environment(
|
||||||
@@ -112,7 +111,7 @@ def env(
|
|||||||
request.env.path_info = '/%s/%s/%s' % (a, c, f)
|
request.env.path_info = '/%s/%s/%s' % (a, c, f)
|
||||||
request.env.http_host = '127.0.0.1:8000'
|
request.env.http_host = '127.0.0.1:8000'
|
||||||
request.env.remote_addr = '127.0.0.1'
|
request.env.remote_addr = '127.0.0.1'
|
||||||
request.env.web2py_runtime_gae = settings.global_settings.web2py_runtime_gae
|
request.env.web2py_runtime_gae = global_settings.web2py_runtime_gae
|
||||||
|
|
||||||
for k,v in extra_request.items():
|
for k,v in extra_request.items():
|
||||||
request[k] = v
|
request[k] = v
|
||||||
|
|||||||
+100
-56
@@ -28,7 +28,7 @@ from dal import DAL, Field, Table, Row, CALLABLETYPES, smart_query, \
|
|||||||
from storage import Storage
|
from storage import Storage
|
||||||
from utils import md5_hash
|
from utils import md5_hash
|
||||||
from validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE, \
|
from validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE, \
|
||||||
IS_DATETIME, IS_INT_IN_RANGE, IS_FLOAT_IN_RANGE
|
IS_DATETIME, IS_INT_IN_RANGE, IS_FLOAT_IN_RANGE, IS_STRONG
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import urllib
|
import urllib
|
||||||
@@ -174,8 +174,7 @@ class TextWidget(FormWidget):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
default = dict(value = value)
|
default = dict(value = value)
|
||||||
attr = cls._attributes(field, default,
|
attr = cls._attributes(field, default,**attributes)
|
||||||
**attributes)
|
|
||||||
return TEXTAREA(**attr)
|
return TEXTAREA(**attr)
|
||||||
|
|
||||||
|
|
||||||
@@ -239,11 +238,14 @@ class ListWidget(StringWidget):
|
|||||||
_name = field.name
|
_name = field.name
|
||||||
if field.type=='list:integer': _class = 'integer'
|
if field.type=='list:integer': _class = 'integer'
|
||||||
else: _class = 'string'
|
else: _class = 'string'
|
||||||
requires = field.requires if isinstance(field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None
|
requires = field.requires if isinstance(
|
||||||
|
field.requires, (IS_NOT_EMPTY, IS_LIST_OF)) else None
|
||||||
attributes['_style'] = 'list-style:none'
|
attributes['_style'] = 'list-style:none'
|
||||||
items=[LI(INPUT(_id=_id, _class=_class, _name=_name,
|
nvalue = value or ['']
|
||||||
value=v, hideerror=True, requires=requires),
|
items = [LI(INPUT(_id=_id, _class=_class, _name=_name,
|
||||||
**attributes) for v in value or ['']]
|
value=v, hideerror=k<len(nvalue)-1,
|
||||||
|
requires=requires),
|
||||||
|
**attributes) for (k,v) in enumerate(nvalue)]
|
||||||
script=SCRIPT("""
|
script=SCRIPT("""
|
||||||
// from http://refactormycode.com/codes/694-expanding-input-list-using-jquery
|
// from http://refactormycode.com/codes/694-expanding-input-list-using-jquery
|
||||||
(function(){
|
(function(){
|
||||||
@@ -377,12 +379,11 @@ class CheckboxesWidget(OptionsWidget):
|
|||||||
requires = field.requires
|
requires = field.requires
|
||||||
if not isinstance(requires, (list, tuple)):
|
if not isinstance(requires, (list, tuple)):
|
||||||
requires = [requires]
|
requires = [requires]
|
||||||
if requires:
|
if requires and hasattr(requires[0], 'options'):
|
||||||
if hasattr(requires[0], 'options'):
|
options = requires[0].options()
|
||||||
options = requires[0].options()
|
else:
|
||||||
else:
|
raise SyntaxError, 'widget cannot determine options of %s' \
|
||||||
raise SyntaxError, 'widget cannot determine options of %s' \
|
% field
|
||||||
% field
|
|
||||||
|
|
||||||
options = [(k, v) for k, v in options if k != '']
|
options = [(k, v) for k, v in options if k != '']
|
||||||
opts = []
|
opts = []
|
||||||
@@ -439,14 +440,23 @@ class PasswordWidget(FormWidget):
|
|||||||
|
|
||||||
see also: :meth:`FormWidget.widget`
|
see also: :meth:`FormWidget.widget`
|
||||||
"""
|
"""
|
||||||
|
# detect if attached a IS_STRONG with entropy
|
||||||
default=dict(
|
default=dict(
|
||||||
_type='password',
|
_type='password',
|
||||||
_value=(value and cls.DEFAULT_PASSWORD_DISPLAY) or '',
|
_value=(value and cls.DEFAULT_PASSWORD_DISPLAY) or '',
|
||||||
)
|
)
|
||||||
attr = cls._attributes(field, default, **attributes)
|
attr = cls._attributes(field, default, **attributes)
|
||||||
|
output = CAT(INPUT(**attr))
|
||||||
|
|
||||||
return INPUT(**attr)
|
# deal with entropy check!
|
||||||
|
requires = field.requires
|
||||||
|
if not isinstance(requires,(list,tuple)): requires = [requires]
|
||||||
|
is_strong = [r for r in requires if isinstance(r, IS_STRONG)]
|
||||||
|
if is_strong:
|
||||||
|
output.append(SCRIPT("web2py_validate_entropy(jQuery('#%s'),%s);" \
|
||||||
|
% (attr['_id'],is_strong[0].entropy)))
|
||||||
|
# end entropy check
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
class UploadWidget(FormWidget):
|
class UploadWidget(FormWidget):
|
||||||
@@ -679,6 +689,16 @@ def formstyle_divs(form, fields):
|
|||||||
table.append(DIV(_label, _controls, _help, _id=id))
|
table.append(DIV(_label, _controls, _help, _id=id))
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
def formstyle_inline(form, fields):
|
||||||
|
''' divs only '''
|
||||||
|
if len(fields)!=2:
|
||||||
|
raise RuntimeError, "Not possible"
|
||||||
|
id, label, controls, help = fields[0]
|
||||||
|
submit_button = fields[1][2]
|
||||||
|
return CAT(DIV(controls,_style='display:inline'),
|
||||||
|
submit_button)
|
||||||
|
|
||||||
|
|
||||||
def formstyle_ul(form, fields):
|
def formstyle_ul(form, fields):
|
||||||
''' unordered list '''
|
''' unordered list '''
|
||||||
table = UL()
|
table = UL()
|
||||||
@@ -804,6 +824,7 @@ class SQLFORM(FORM):
|
|||||||
divs = formstyle_divs,
|
divs = formstyle_divs,
|
||||||
ul = formstyle_ul,
|
ul = formstyle_ul,
|
||||||
bootstrap = formstyle_bootstrap,
|
bootstrap = formstyle_bootstrap,
|
||||||
|
inline = formstyle_inline,
|
||||||
))
|
))
|
||||||
|
|
||||||
FIELDNAME_REQUEST_DELETE = 'delete_this_record'
|
FIELDNAME_REQUEST_DELETE = 'delete_this_record'
|
||||||
@@ -865,6 +886,8 @@ class SQLFORM(FORM):
|
|||||||
self.ignore_rw = ignore_rw
|
self.ignore_rw = ignore_rw
|
||||||
self.formstyle = formstyle
|
self.formstyle = formstyle
|
||||||
self.readonly = readonly
|
self.readonly = readonly
|
||||||
|
# Default dbio setting
|
||||||
|
self.detect_record_change = None
|
||||||
|
|
||||||
nbsp = XML(' ') # Firefox2 does not display fields with blanks
|
nbsp = XML(' ') # Firefox2 does not display fields with blanks
|
||||||
FORM.__init__(self, *[], **attributes)
|
FORM.__init__(self, *[], **attributes)
|
||||||
@@ -998,11 +1021,11 @@ class SQLFORM(FORM):
|
|||||||
else:
|
else:
|
||||||
inp = field.formatter(default)
|
inp = field.formatter(default)
|
||||||
elif field.type == 'upload':
|
elif field.type == 'upload':
|
||||||
if hasattr(field, 'widget') and field.widget:
|
if field.widget:
|
||||||
inp = field.widget(field, default, upload)
|
inp = field.widget(field, default, upload)
|
||||||
else:
|
else:
|
||||||
inp = self.widgets.upload.widget(field, default, upload)
|
inp = self.widgets.upload.widget(field, default, upload)
|
||||||
elif hasattr(field, 'widget') and field.widget:
|
elif field.widget:
|
||||||
inp = field.widget(field, default)
|
inp = field.widget(field, default)
|
||||||
elif field.type == 'boolean':
|
elif field.type == 'boolean':
|
||||||
inp = self.widgets.boolean.widget(field, default)
|
inp = self.widgets.boolean.widget(field, default)
|
||||||
@@ -1036,7 +1059,7 @@ class SQLFORM(FORM):
|
|||||||
|
|
||||||
xfields.append((row_id,label,inp,comment))
|
xfields.append((row_id,label,inp,comment))
|
||||||
self.custom.dspval[fieldname] = dspval or nbsp
|
self.custom.dspval[fieldname] = dspval or nbsp
|
||||||
self.custom.inpval[fieldname] = inpval or ''
|
self.custom.inpval[fieldname] = inpval if not inpval is None else ''
|
||||||
self.custom.widget[fieldname] = inp
|
self.custom.widget[fieldname] = inp
|
||||||
|
|
||||||
# if a record is provided and found, as is linkto
|
# if a record is provided and found, as is linkto
|
||||||
@@ -1118,16 +1141,16 @@ class SQLFORM(FORM):
|
|||||||
if defaults and len(args) - len(defaults) == 4 or len(args) == 4:
|
if defaults and len(args) - len(defaults) == 4 or len(args) == 4:
|
||||||
table = TABLE()
|
table = TABLE()
|
||||||
for id,a,b,c in xfields:
|
for id,a,b,c in xfields:
|
||||||
raw_b = self.field_parent[id] = b
|
newrows = formstyle(id,a,b,c)
|
||||||
newrows = formstyle(id,a,raw_b,c)
|
self.field_parent[id] = getattr(b,'parent',None)
|
||||||
if type(newrows).__name__ != "tuple":
|
if type(newrows).__name__ != "tuple":
|
||||||
newrows = [newrows]
|
newrows = [newrows]
|
||||||
for newrow in newrows:
|
for newrow in newrows:
|
||||||
table.append(newrow)
|
table.append(newrow)
|
||||||
else:
|
else:
|
||||||
for id,a,b,c in xfields:
|
|
||||||
self.field_parent[id] = b
|
|
||||||
table = formstyle(self, xfields)
|
table = formstyle(self, xfields)
|
||||||
|
for id,a,b,c in xfields:
|
||||||
|
self.field_parent[id] = getattr(b,'parent',None)
|
||||||
else:
|
else:
|
||||||
raise RuntimeError, 'formstyle not supported'
|
raise RuntimeError, 'formstyle not supported'
|
||||||
return table
|
return table
|
||||||
@@ -1142,6 +1165,7 @@ class SQLFORM(FORM):
|
|||||||
dbio=True,
|
dbio=True,
|
||||||
hideerror=False,
|
hideerror=False,
|
||||||
detect_record_change=False,
|
detect_record_change=False,
|
||||||
|
**kwargs
|
||||||
):
|
):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -1166,7 +1190,8 @@ class SQLFORM(FORM):
|
|||||||
# implement logic to detect whether record exist but has been modified
|
# implement logic to detect whether record exist but has been modified
|
||||||
# server side
|
# server side
|
||||||
self.record_changed = None
|
self.record_changed = None
|
||||||
if detect_record_change:
|
self.detect_record_change = detect_record_change
|
||||||
|
if self.detect_record_change:
|
||||||
if self.record:
|
if self.record:
|
||||||
self.record_changed = False
|
self.record_changed = False
|
||||||
serialized = '|'.join(str(self.record[k]) for k in self.table.fields())
|
serialized = '|'.join(str(self.record[k]) for k in self.table.fields())
|
||||||
@@ -1222,6 +1247,7 @@ class SQLFORM(FORM):
|
|||||||
keepvalues,
|
keepvalues,
|
||||||
onvalidation,
|
onvalidation,
|
||||||
hideerror=hideerror,
|
hideerror=hideerror,
|
||||||
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
self.deleted = \
|
self.deleted = \
|
||||||
@@ -1231,31 +1257,41 @@ class SQLFORM(FORM):
|
|||||||
|
|
||||||
auch = record_id and self.errors and self.deleted
|
auch = record_id and self.errors and self.deleted
|
||||||
|
|
||||||
# auch is true when user tries to delete a record
|
if self.record_changed and self.detect_record_change:
|
||||||
# that does not pass validation, yet it should be deleted
|
message_onchange = \
|
||||||
|
kwargs.setdefault("message_onchange",
|
||||||
if not ret and not auch:
|
current.T("A record change was detected. " +
|
||||||
|
"Consecutive update self-submissions " +
|
||||||
|
"are not allowed. Try re-submitting or " +
|
||||||
|
"refreshing the form page."))
|
||||||
|
if message_onchange is not None:
|
||||||
|
current.response.flash = message_onchange
|
||||||
|
return ret
|
||||||
|
elif (not ret) and (not auch):
|
||||||
|
# auch is true when user tries to delete a record
|
||||||
|
# that does not pass validation, yet it should be deleted
|
||||||
for fieldname in self.fields:
|
for fieldname in self.fields:
|
||||||
field = self.table[fieldname]
|
field = self.table[fieldname]
|
||||||
### this is a workaround! widgets should always have default not None!
|
### this is a workaround! widgets should always have default not None!
|
||||||
if not field.widget and field.type.startswith('list:') and \
|
if not field.widget and field.type.startswith('list:') and \
|
||||||
not OptionsWidget.has_options(field):
|
not OptionsWidget.has_options(field):
|
||||||
field.widget = self.widgets.list.widget
|
field.widget = self.widgets.list.widget
|
||||||
if hasattr(field, 'widget') and field.widget and fieldname in request_vars:
|
if field.widget and fieldname in request_vars:
|
||||||
if fieldname in self.vars:
|
if fieldname in self.vars:
|
||||||
value = self.vars[fieldname]
|
value = self.vars[fieldname]
|
||||||
elif self.record:
|
elif self.record:
|
||||||
value = self.record[fieldname]
|
value = self.record[fieldname]
|
||||||
else:
|
else:
|
||||||
value = self.table[fieldname].default
|
value = self.table[fieldname].default
|
||||||
if field.type.startswith('list:') and \
|
if field.type.startswith('list:') and isinstance(value, str):
|
||||||
isinstance(value, str):
|
|
||||||
value = [value]
|
value = [value]
|
||||||
row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX)
|
row_id = '%s_%s%s' % (self.table, fieldname, SQLFORM.ID_ROW_SUFFIX)
|
||||||
widget = field.widget(field, value)
|
widget = field.widget(field, value)
|
||||||
self.field_parent[row_id].components = [ widget ]
|
parent = self.field_parent[row_id]
|
||||||
self.field_parent[row_id]._traverse(False, hideerror)
|
if parent:
|
||||||
self.custom.widget[ fieldname ] = widget
|
parent.components = [ widget ]
|
||||||
|
parent._traverse(False, hideerror)
|
||||||
|
self.custom.widget[fieldname] = widget
|
||||||
self.accepted = ret
|
self.accepted = ret
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -1492,15 +1528,20 @@ class SQLFORM(FORM):
|
|||||||
'datetime':['=','!=','<','>','<=','>='],
|
'datetime':['=','!=','<','>','<=','>='],
|
||||||
'integer':['=','!=','<','>','<=','>='],
|
'integer':['=','!=','<','>','<=','>='],
|
||||||
'double':['=','!=','<','>','<=','>='],
|
'double':['=','!=','<','>','<=','>='],
|
||||||
|
'id':['=','!=','<','>','<=','>='],
|
||||||
|
'reference':['=','!=','<','>','<=','>='],
|
||||||
'boolean':['=','!=']}
|
'boolean':['=','!=']}
|
||||||
if fields[0]._db._adapter.dbengine=='google:datastore':
|
if fields[0]._db._adapter.dbengine=='google:datastore':
|
||||||
search_options['string'] = ['=','!=','<','>','<=','>=']
|
search_options['string'] = ['=','!=','<','>','<=','>=']
|
||||||
search_options['text'] = ['=','!=','<','>','<=','>=']
|
search_options['text'] = ['=','!=','<','>','<=','>=']
|
||||||
|
search_options['list:string'] = ['contains']
|
||||||
|
search_options['list:integer'] = ['contains']
|
||||||
|
search_options['list:reference'] = ['contains']
|
||||||
criteria = []
|
criteria = []
|
||||||
selectfields = []
|
selectfields = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
name = str(field).replace('.','-')
|
name = str(field).replace('.','-')
|
||||||
options = search_options.get(field.type,None)
|
options = search_options.get(field.type.split(' ')[0],None)
|
||||||
if options:
|
if options:
|
||||||
label = isinstance(field.label,str) and T(field.label) or field.label
|
label = isinstance(field.label,str) and T(field.label) or field.label
|
||||||
selectfields.append(OPTION(label, _value=str(field)))
|
selectfields.append(OPTION(label, _value=str(field)))
|
||||||
@@ -1789,16 +1830,19 @@ class SQLFORM(FORM):
|
|||||||
table = db[request.args[-2]]
|
table = db[request.args[-2]]
|
||||||
record = table(request.args[-1]) or redirect(URL('error'))
|
record = table(request.args[-1]) or redirect(URL('error'))
|
||||||
sqlformargs.update(editargs)
|
sqlformargs.update(editargs)
|
||||||
update_form = SQLFORM(table, record, upload=upload, ignore_rw=ignore_rw,
|
update_form = SQLFORM(
|
||||||
formstyle=formstyle, deletable=deletable,
|
table,
|
||||||
_class='web2py_form',
|
record, upload=upload, ignore_rw=ignore_rw,
|
||||||
submit_button=T('Submit'),
|
formstyle=formstyle, deletable=deletable,
|
||||||
delete_label=T('Check to delete'),
|
_class='web2py_form',
|
||||||
**sqlformargs)
|
submit_button=T('Submit'),
|
||||||
update_form.process(formname=formname,
|
delete_label=T('Check to delete'),
|
||||||
onvalidation=onvalidation,
|
**sqlformargs)
|
||||||
onsuccess=onupdate,
|
update_form.process(
|
||||||
next=referrer)
|
formname=formname,
|
||||||
|
onvalidation=onvalidation,
|
||||||
|
onsuccess=onupdate,
|
||||||
|
next=referrer)
|
||||||
res = DIV(buttons(view=details, record=record),
|
res = DIV(buttons(view=details, record=record),
|
||||||
update_form, formfooter, _class=_class)
|
update_form, formfooter, _class=_class)
|
||||||
res.create_form = create_form
|
res.create_form = create_form
|
||||||
@@ -1852,7 +1896,7 @@ class SQLFORM(FORM):
|
|||||||
rows = dbset.select(cacheable=True)
|
rows = dbset.select(cacheable=True)
|
||||||
else:
|
else:
|
||||||
rows = dbset.select(left=left,orderby=orderby,
|
rows = dbset.select(left=left,orderby=orderby,
|
||||||
cacheable=True*columns)
|
cacheable=True,*columns)
|
||||||
|
|
||||||
if export_type in exportManager:
|
if export_type in exportManager:
|
||||||
value = exportManager[export_type]
|
value = exportManager[export_type]
|
||||||
@@ -1890,7 +1934,7 @@ class SQLFORM(FORM):
|
|||||||
search_widget = search_widget[tablename]
|
search_widget = search_widget[tablename]
|
||||||
if search_widget=='default':
|
if search_widget=='default':
|
||||||
search_menu = SQLFORM.search_menu(sfields)
|
search_menu = SQLFORM.search_menu(sfields)
|
||||||
search_widget = lambda sfield, url: CAT(add,FORM(
|
search_widget = lambda sfield, url: CAT(FORM(
|
||||||
INPUT(_name='keywords',_value=request.vars.keywords,
|
INPUT(_name='keywords',_value=request.vars.keywords,
|
||||||
_id='web2py_keywords',_onfocus="jQuery('#w2p_query_fields').change();jQuery('#w2p_query_panel').slideDown();"),
|
_id='web2py_keywords',_onfocus="jQuery('#w2p_query_fields').change();jQuery('#w2p_query_panel').slideDown();"),
|
||||||
INPUT(_type='submit',_value=T('Search'),_class="btn"),
|
INPUT(_type='submit',_value=T('Search'),_class="btn"),
|
||||||
@@ -1898,6 +1942,7 @@ class SQLFORM(FORM):
|
|||||||
_onclick="jQuery('#web2py_keywords').val('');"),
|
_onclick="jQuery('#web2py_keywords').val('');"),
|
||||||
_method="GET",_action=url),search_menu)
|
_method="GET",_action=url),search_menu)
|
||||||
form = search_widget and search_widget(sfields,url()) or ''
|
form = search_widget and search_widget(sfields,url()) or ''
|
||||||
|
console.append(add)
|
||||||
console.append(form)
|
console.append(form)
|
||||||
keywords = request.vars.get('keywords','')
|
keywords = request.vars.get('keywords','')
|
||||||
try:
|
try:
|
||||||
@@ -1942,8 +1987,7 @@ class SQLFORM(FORM):
|
|||||||
if columns and not str(field) in columns: continue
|
if columns and not str(field) in columns: continue
|
||||||
if not field.readable: continue
|
if not field.readable: continue
|
||||||
key = str(field)
|
key = str(field)
|
||||||
header = headers.get(str(field),
|
header = headers.get(str(field), field.label or key)
|
||||||
hasattr(field,'label') and field.label or key)
|
|
||||||
if sortable:
|
if sortable:
|
||||||
if key == order:
|
if key == order:
|
||||||
key, marker = '~'+order, sorter_icons[0]
|
key, marker = '~'+order, sorter_icons[0]
|
||||||
@@ -2003,7 +2047,7 @@ class SQLFORM(FORM):
|
|||||||
table_fields = [f for f in fields if f._tablename in tablenames]
|
table_fields = [f for f in fields if f._tablename in tablenames]
|
||||||
rows = dbset.select(left=left,orderby=orderby,
|
rows = dbset.select(left=left,orderby=orderby,
|
||||||
groupby=groupby,limitby=limitby,
|
groupby=groupby,limitby=limitby,
|
||||||
cacheable=True,*table_fields)
|
*table_fields)
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
rows = None
|
rows = None
|
||||||
error = T("Query Not Supported")
|
error = T("Query Not Supported")
|
||||||
@@ -2226,7 +2270,9 @@ class SQLFORM(FORM):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if nargs>len(args)+1:
|
if nargs>len(args)+1:
|
||||||
query = (field == id)
|
query = (field == id)
|
||||||
|
if isinstance(linked_tables,dict):
|
||||||
|
linked_tables = linked_tables.get(table._tablename,[])
|
||||||
if linked_tables is None or referee in linked_tables:
|
if linked_tables is None or referee in linked_tables:
|
||||||
field.represent = lambda id,r=None,referee=referee,rep=field.represent: A(callable(rep) and rep(id) or id,_class=trap_class(),_href=url(args=['view',referee,id]))
|
field.represent = lambda id,r=None,referee=referee,rep=field.represent: A(callable(rep) and rep(id) or id,_class=trap_class(),_href=url(args=['view',referee,id]))
|
||||||
except (KeyError,ValueError,TypeError):
|
except (KeyError,ValueError,TypeError):
|
||||||
@@ -2251,6 +2297,8 @@ class SQLFORM(FORM):
|
|||||||
if rfield.readable:
|
if rfield.readable:
|
||||||
check[rfield.tablename] = \
|
check[rfield.tablename] = \
|
||||||
check.get(rfield.tablename,[])+[rfield.name]
|
check.get(rfield.tablename,[])+[rfield.name]
|
||||||
|
if isinstance(linked_tables,dict):
|
||||||
|
linked_tables = linked_tables.get(table._tablename,[])
|
||||||
for tablename in sorted(check):
|
for tablename in sorted(check):
|
||||||
linked_fieldnames = check[tablename]
|
linked_fieldnames = check[tablename]
|
||||||
tb = db[tablename]
|
tb = db[tablename]
|
||||||
@@ -2366,6 +2414,7 @@ class SQLTABLE(TABLE):
|
|||||||
extracolumns=None,
|
extracolumns=None,
|
||||||
selectid=None,
|
selectid=None,
|
||||||
renderstyle=False,
|
renderstyle=False,
|
||||||
|
cid=None,
|
||||||
**attributes
|
**attributes
|
||||||
):
|
):
|
||||||
|
|
||||||
@@ -2403,7 +2452,7 @@ class SQLTABLE(TABLE):
|
|||||||
row.append(TH(coldict['label'],**attrcol))
|
row.append(TH(coldict['label'],**attrcol))
|
||||||
elif orderby:
|
elif orderby:
|
||||||
row.append(TH(A(headers.get(c, c),
|
row.append(TH(A(headers.get(c, c),
|
||||||
_href=th_link+'?orderby=' + c)))
|
_href=th_link+'?orderby=' + c, cid=cid)))
|
||||||
else:
|
else:
|
||||||
row.append(TH(headers.get(c, c)))
|
row.append(TH(headers.get(c, c)))
|
||||||
|
|
||||||
@@ -2698,8 +2747,3 @@ class ExporterXML(ExportClass):
|
|||||||
out.write('</row>\n')
|
out.write('</row>\n')
|
||||||
out.write('</rows>')
|
out.write('</rows>')
|
||||||
return str(out.getvalue())
|
return str(out.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+6
-19
@@ -38,30 +38,17 @@ class Storage(dict):
|
|||||||
>>> print o.a
|
>>> print o.a
|
||||||
None
|
None
|
||||||
"""
|
"""
|
||||||
__slots__=()
|
__slots__=()
|
||||||
|
|
||||||
__setattr__ = dict.__setitem__
|
__setattr__ = dict.__setitem__
|
||||||
__delattr__ = dict.__delitem__
|
__delattr__ = dict.__delitem__
|
||||||
__getitem__ = dict.get
|
__getitem__ = dict.get
|
||||||
__getattr__ = dict.get
|
__getattr__ = dict.get
|
||||||
__repr__ = lambda self: '<Storage %s>' % dict.__repr__(self)
|
__repr__ = lambda self: '<Storage %s>' % dict.__repr__(self)
|
||||||
__getstate__ = dict
|
# http://stackoverflow.com/questions/5247250/why-does-pickle-getstate-accept-as-a-return-value-the-very-instance-it-requi
|
||||||
__setstate__ = dict.update
|
__getstate__ = lambda self: None
|
||||||
# def __getattr__(self, key):
|
__copy__ = lambda self: Storage(self)
|
||||||
# return dict.get(self, key, None)
|
|
||||||
# def __setattr__(self, key, value):
|
|
||||||
# self[key] = value
|
|
||||||
# def __getitem__(self, key):
|
|
||||||
# return dict.get(self, key, None)
|
|
||||||
# def __delattr__(self, key):
|
|
||||||
# del self[key]
|
|
||||||
# def __repr__(self):
|
|
||||||
# return '<Storage %s>' % dict.__repr__(self)
|
|
||||||
# def __getstate__(self):
|
|
||||||
# return dict(self)
|
|
||||||
# def __setstate__(self,values):
|
|
||||||
# self.update(values)
|
|
||||||
|
|
||||||
def getlist(self,key):
|
def getlist(self,key):
|
||||||
"""
|
"""
|
||||||
Return a Storage value as a list.
|
Return a Storage value as a list.
|
||||||
|
|||||||
+39
-34
@@ -43,10 +43,11 @@ def stream_file_or_304_or_206(
|
|||||||
chunk_size = DEFAULT_CHUNK_SIZE,
|
chunk_size = DEFAULT_CHUNK_SIZE,
|
||||||
request = None,
|
request = None,
|
||||||
headers = {},
|
headers = {},
|
||||||
|
status = 200,
|
||||||
error_message = None,
|
error_message = None,
|
||||||
):
|
):
|
||||||
if error_message is None:
|
if error_message is None:
|
||||||
error_message = rewrite.thread.routes.error_message % 'invalid request'
|
error_message = rewrite.THREAD_LOCAL.routes.error_message % 'invalid request'
|
||||||
try:
|
try:
|
||||||
fp = open(static_file)
|
fp = open(static_file)
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
@@ -60,56 +61,60 @@ def stream_file_or_304_or_206(
|
|||||||
fp.close()
|
fp.close()
|
||||||
stat_file = os.stat(static_file)
|
stat_file = os.stat(static_file)
|
||||||
fsize = stat_file[stat.ST_SIZE]
|
fsize = stat_file[stat.ST_SIZE]
|
||||||
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT',
|
modified = stat_file[stat.ST_MTIME]
|
||||||
time.gmtime(stat_file[stat.ST_MTIME]))
|
mtime = time.strftime('%a, %d %b %Y %H:%M:%S GMT',time.gmtime(modified))
|
||||||
headers.setdefault('Content-Type', contenttype(static_file))
|
headers.setdefault('Content-Type', contenttype(static_file))
|
||||||
headers.setdefault('Last-Modified', mtime)
|
headers.setdefault('Last-Modified', mtime)
|
||||||
headers.setdefault('Pragma', 'cache')
|
headers.setdefault('Pragma', 'cache')
|
||||||
headers.setdefault('Cache-Control', 'private')
|
headers.setdefault('Cache-Control', 'private')
|
||||||
|
|
||||||
if request and request.env.http_if_modified_since == mtime:
|
# if this is a normal response and not a respnse to an error page
|
||||||
raise HTTP(304, **{'Content-Type': headers['Content-Type']})
|
if status == 200:
|
||||||
|
if request and request.env.http_if_modified_since == mtime:
|
||||||
|
raise HTTP(304, **{'Content-Type': headers['Content-Type']})
|
||||||
|
|
||||||
elif request and request.env.http_range:
|
elif request and request.env.http_range:
|
||||||
start_items = regex_start_range.findall(request.env.http_range)
|
start_items = regex_start_range.findall(request.env.http_range)
|
||||||
if not start_items:
|
if not start_items:
|
||||||
start_items = [0]
|
start_items = [0]
|
||||||
stop_items = regex_stop_range.findall(request.env.http_range)
|
stop_items = regex_stop_range.findall(request.env.http_range)
|
||||||
if not stop_items or int(stop_items[0]) > fsize - 1:
|
if not stop_items or int(stop_items[0]) > fsize - 1:
|
||||||
stop_items = [fsize - 1]
|
stop_items = [fsize - 1]
|
||||||
part = (int(start_items[0]), int(stop_items[0]), fsize)
|
part = (int(start_items[0]), int(stop_items[0]), fsize)
|
||||||
bytes = part[1] - part[0] + 1
|
bytes = part[1] - part[0] + 1
|
||||||
try:
|
try:
|
||||||
stream = open(static_file, 'rb')
|
stream = open(static_file, 'rb')
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
if e[0] in (errno.EISDIR, errno.EACCES):
|
if e[0] in (errno.EISDIR, errno.EACCES):
|
||||||
raise HTTP(403)
|
raise HTTP(403)
|
||||||
else:
|
else:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
stream.seek(part[0])
|
stream.seek(part[0])
|
||||||
headers['Content-Range'] = 'bytes %i-%i/%i' % part
|
headers['Content-Range'] = 'bytes %i-%i/%i' % part
|
||||||
headers['Content-Length'] = '%i' % bytes
|
headers['Content-Length'] = '%i' % bytes
|
||||||
status = 206
|
status = 206
|
||||||
else:
|
# in all the other cases (not 304, not 206, but 200 or error page)
|
||||||
|
if status != 206:
|
||||||
|
enc = request.env.http_accept_encoding
|
||||||
|
if enc and 'gzip' in enc and not 'Content-Encoding' in headers:
|
||||||
|
gzipped = static_file + '.gz'
|
||||||
|
if os.path.isfile(gzipped) and os.path.getmtime(gzipped)>modified:
|
||||||
|
static_file = gzipped
|
||||||
|
fsize = os.path.getsize(gzipped)
|
||||||
|
headers['Content-Encoding'] = 'gzip'
|
||||||
|
headers['Vary'] = 'Accept-Encoding'
|
||||||
try:
|
try:
|
||||||
stream = open(static_file, 'rb')
|
stream = open(static_file, 'rb')
|
||||||
except IOError, e:
|
except IOError, e:
|
||||||
|
# this better does not happer when returning an error page ;-)
|
||||||
if e[0] in (errno.EISDIR, errno.EACCES):
|
if e[0] in (errno.EISDIR, errno.EACCES):
|
||||||
raise HTTP(403)
|
raise HTTP(403)
|
||||||
else:
|
else:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
headers['Content-Length'] = fsize
|
headers['Content-Length'] = fsize
|
||||||
bytes = None
|
bytes = None
|
||||||
status = 200
|
|
||||||
if request and request.env.web2py_use_wsgi_file_wrapper:
|
if request and request.env.web2py_use_wsgi_file_wrapper:
|
||||||
wrapped = request.env.wsgi_file_wrapper(stream, chunk_size)
|
wrapped = request.env.wsgi_file_wrapper(stream, chunk_size)
|
||||||
else:
|
else:
|
||||||
wrapped = streamer(stream, chunk_size=chunk_size, bytes=bytes)
|
wrapped = streamer(stream, chunk_size=chunk_size, bytes=bytes)
|
||||||
raise HTTP(status, wrapped, **headers)
|
raise HTTP(status, wrapped, **headers)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -416,7 +416,7 @@ class TemplateParser(object):
|
|||||||
# Allow Views to include other views dynamically
|
# Allow Views to include other views dynamically
|
||||||
context = self.context
|
context = self.context
|
||||||
if current and not "response" in context:
|
if current and not "response" in context:
|
||||||
context["response"] = current.response
|
context["response"] = getattr(current,'response',None)
|
||||||
|
|
||||||
# Get the filename; filename looks like ``"template.html"``.
|
# Get the filename; filename looks like ``"template.html"``.
|
||||||
# We need to eval to remove the quotes and get the string type.
|
# We need to eval to remove the quotes and get the string type.
|
||||||
|
|||||||
+12
-7
@@ -311,12 +311,13 @@ class TestDatetime(unittest.TestCase):
|
|||||||
9, 30)), 3)
|
9, 30)), 3)
|
||||||
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12,
|
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12,
|
||||||
21, 11, 30)).select()), 1)
|
21, 11, 30)).select()), 1)
|
||||||
self.assertEqual(len(db(db.t.a.year() == 1971).select()), 2)
|
self.assertEqual(db(db.t.a.year() == 1971).count(), 2)
|
||||||
self.assertEqual(len(db(db.t.a.month() == 12).select()), 2)
|
self.assertEqual(db(db.t.a.month() == 12).count(), 2)
|
||||||
self.assertEqual(len(db(db.t.a.day() == 21).select()), 3)
|
self.assertEqual(db(db.t.a.day() == 21).count(), 3)
|
||||||
self.assertEqual(len(db(db.t.a.hour() == 11).select()), 1)
|
self.assertEqual(db(db.t.a.hour() == 11).count(), 1)
|
||||||
self.assertEqual(len(db(db.t.a.minutes() == 30).select()), 3)
|
self.assertEqual(db(db.t.a.minutes() == 30).count(), 3)
|
||||||
self.assertEqual(len(db(db.t.a.seconds() == 0).select()), 3)
|
self.assertEqual(db(db.t.a.seconds() == 0).count(), 3)
|
||||||
|
self.assertEqual(db(db.t.a.epoch()<365*24*3600).count(),1)
|
||||||
db.t.drop()
|
db.t.drop()
|
||||||
|
|
||||||
|
|
||||||
@@ -329,7 +330,7 @@ class TestExpressions(unittest.TestCase):
|
|||||||
self.assertEqual(db.t.insert(a=2), 2)
|
self.assertEqual(db.t.insert(a=2), 2)
|
||||||
self.assertEqual(db.t.insert(a=3), 3)
|
self.assertEqual(db.t.insert(a=3), 3)
|
||||||
self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1)
|
self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1)
|
||||||
self.assertEqual(len(db(db.t.a == 4).select()), 1)
|
self.assertEqual(db(db.t.a == 4).count(), 1)
|
||||||
db.t.drop()
|
db.t.drop()
|
||||||
|
|
||||||
|
|
||||||
@@ -447,18 +448,22 @@ class TestMigrations(unittest.TestCase):
|
|||||||
db = DAL('sqlite://.storage.db')
|
db = DAL('sqlite://.storage.db')
|
||||||
db.define_table('t', Field('a'), migrate='.storage.table')
|
db.define_table('t', Field('a'), migrate='.storage.table')
|
||||||
db.commit()
|
db.commit()
|
||||||
|
db.close()
|
||||||
db = DAL('sqlite://.storage.db')
|
db = DAL('sqlite://.storage.db')
|
||||||
db.define_table('t', Field('a'), Field('b'),
|
db.define_table('t', Field('a'), Field('b'),
|
||||||
migrate='.storage.table')
|
migrate='.storage.table')
|
||||||
db.commit()
|
db.commit()
|
||||||
|
db.close()
|
||||||
db = DAL('sqlite://.storage.db')
|
db = DAL('sqlite://.storage.db')
|
||||||
db.define_table('t', Field('a'), Field('b', 'text'),
|
db.define_table('t', Field('a'), Field('b', 'text'),
|
||||||
migrate='.storage.table')
|
migrate='.storage.table')
|
||||||
db.commit()
|
db.commit()
|
||||||
|
db.close()
|
||||||
db = DAL('sqlite://.storage.db')
|
db = DAL('sqlite://.storage.db')
|
||||||
db.define_table('t', Field('a'), migrate='.storage.table')
|
db.define_table('t', Field('a'), migrate='.storage.table')
|
||||||
db.t.drop()
|
db.t.drop()
|
||||||
db.commit()
|
db.commit()
|
||||||
|
db.close()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
if os.path.exists('.storage.db'):
|
if os.path.exists('.storage.db'):
|
||||||
|
|||||||
@@ -163,17 +163,17 @@ default_application = 'defapp'
|
|||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/bad!ctl')
|
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/bad!ctl')
|
||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/ctlr/bad!fcn')
|
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/ctlr/bad!fcn')
|
||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/ctlr/fcn.bad!ext')
|
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/init/ctlr/fcn.bad!ext')
|
||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path \(args\)\]', filter_url, 'http://domain.com/appc/init/fcn/bad!arg')
|
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/appc/init/fcn/bad!arg')
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), "/welcome/default/fcn_1")
|
self.assertEqual(filter_url('http://domain.com/welcome/default/fcn_1'), "/welcome/default/fcn_1")
|
||||||
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1')
|
#self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/default/fcn-1')
|
||||||
try:
|
#try:
|
||||||
# 2.7+ only
|
# # 2.7+ only
|
||||||
self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/welcome/default/fcn-1')
|
# self.assertRaisesRegexp(HTTP, '400 BAD REQUEST \[invalid path\]', filter_url, 'http://domain.com/welcome/default/fcn-1')
|
||||||
except AttributeError:
|
#except AttributeError:
|
||||||
pass
|
# pass
|
||||||
|
|
||||||
def test_routes_error(self):
|
def test_routes_error(self):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -46,6 +46,18 @@ class TestWeb(unittest.TestCase):
|
|||||||
client.get('site')
|
client.get('site')
|
||||||
client.get('design/welcome')
|
client.get('design/welcome')
|
||||||
|
|
||||||
|
class TestStaticCacheControl(unittest.TestCase):
|
||||||
|
def testWebClient(self):
|
||||||
|
s=WebClient('http://127.0.0.1:8000/welcome/')
|
||||||
|
s.get('static/js/web2py.js')
|
||||||
|
assert('expires' not in s.headers)
|
||||||
|
assert(not s.headers['cache-control'].startswith('max-age'))
|
||||||
|
text = s.text
|
||||||
|
s.get('static/_1.2.3/js/web2py.js')
|
||||||
|
assert(text == s.text)
|
||||||
|
assert('expires' in s.headers)
|
||||||
|
assert(s.headers['cache-control'].startswith('max-age'))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|||||||
+185
-101
@@ -264,6 +264,7 @@ class Mail(object):
|
|||||||
cc=None,
|
cc=None,
|
||||||
bcc=None,
|
bcc=None,
|
||||||
reply_to=None,
|
reply_to=None,
|
||||||
|
sender='%(sender)s',
|
||||||
encoding='utf-8',
|
encoding='utf-8',
|
||||||
raw=False,
|
raw=False,
|
||||||
headers={}
|
headers={}
|
||||||
@@ -606,7 +607,8 @@ class Mail(object):
|
|||||||
# no cryptography process as usual
|
# no cryptography process as usual
|
||||||
payload=payload_in
|
payload=payload_in
|
||||||
|
|
||||||
payload['From'] = encoded_or_raw(self.settings.sender.decode(encoding))
|
sender = sender % dict(sender=self.settings.sender)
|
||||||
|
payload['From'] = encoded_or_raw(sender.decode(encoding))
|
||||||
origTo = to[:]
|
origTo = to[:]
|
||||||
if to:
|
if to:
|
||||||
payload['To'] = encoded_or_raw(', '.join(to).decode(encoding))
|
payload['To'] = encoded_or_raw(', '.join(to).decode(encoding))
|
||||||
@@ -623,10 +625,10 @@ class Mail(object):
|
|||||||
for k,v in headers.iteritems():
|
for k,v in headers.iteritems():
|
||||||
payload[k] = encoded_or_raw(v.decode(encoding))
|
payload[k] = encoded_or_raw(v.decode(encoding))
|
||||||
result = {}
|
result = {}
|
||||||
try:
|
try:
|
||||||
if self.settings.server == 'logging':
|
if self.settings.server == 'logging':
|
||||||
logger.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \
|
logger.warn('email not sent\n%s\nFrom: %s\nTo: %s\nSubject: %s\n\n%s\n%s\n' % \
|
||||||
('-'*40,self.settings.sender,
|
('-'*40,sender,
|
||||||
', '.join(to),subject,
|
', '.join(to),subject,
|
||||||
text or html,'-'*40))
|
text or html,'-'*40))
|
||||||
elif self.settings.server == 'gae':
|
elif self.settings.server == 'gae':
|
||||||
@@ -794,6 +796,10 @@ def addrow(form, a, b, c, style, _id, position=-1):
|
|||||||
DIV(b, _class='w2p_fw'),
|
DIV(b, _class='w2p_fw'),
|
||||||
DIV(c, _class='w2p_fc'),
|
DIV(c, _class='w2p_fc'),
|
||||||
_id = _id))
|
_id = _id))
|
||||||
|
elif style == "bootstrap":
|
||||||
|
form[0].insert(position, DIV(LABEL(a,_class='control-label'),
|
||||||
|
DIV(b,SPAN(c, _class='inline-help'),_class='controls'),
|
||||||
|
_class='control-group',_id = _id))
|
||||||
else:
|
else:
|
||||||
form[0].insert(position, TR(TD(LABEL(a),_class='w2p_fl'),
|
form[0].insert(position, TR(TD(LABEL(a),_class='w2p_fl'),
|
||||||
TD(b,_class='w2p_fw'),
|
TD(b,_class='w2p_fw'),
|
||||||
@@ -1044,7 +1050,7 @@ class Auth(object):
|
|||||||
|
|
||||||
def __init__(self, environment=None, db=None, mailer=True,
|
def __init__(self, environment=None, db=None, mailer=True,
|
||||||
hmac_key=None, controller='default', function='user',
|
hmac_key=None, controller='default', function='user',
|
||||||
cas_provider=None, signature=True):
|
cas_provider=None, signature=True, secure=False):
|
||||||
"""
|
"""
|
||||||
auth=Auth(db)
|
auth=Auth(db)
|
||||||
|
|
||||||
@@ -1064,6 +1070,8 @@ class Auth(object):
|
|||||||
session = current.session
|
session = current.session
|
||||||
auth = session.auth
|
auth = session.auth
|
||||||
self.user_groups = auth and auth.user_groups or {}
|
self.user_groups = auth and auth.user_groups or {}
|
||||||
|
if secure:
|
||||||
|
request.requires_https()
|
||||||
if auth and auth.last_visit and auth.last_visit + \
|
if auth and auth.last_visit and auth.last_visit + \
|
||||||
datetime.timedelta(days=0, seconds=auth.expiration) > request.now:
|
datetime.timedelta(days=0, seconds=auth.expiration) > request.now:
|
||||||
self.user = auth.user
|
self.user = auth.user
|
||||||
@@ -1191,7 +1199,10 @@ class Auth(object):
|
|||||||
'reset_password','request_reset_password',
|
'reset_password','request_reset_password',
|
||||||
'change_password','profile','groups',
|
'change_password','profile','groups',
|
||||||
'impersonate','not_authorized'):
|
'impersonate','not_authorized'):
|
||||||
return getattr(self,args[0])()
|
if len(request.args) >= 2 and args[0]=='impersonate':
|
||||||
|
return getattr(self,args[0])(request.args[1])
|
||||||
|
else:
|
||||||
|
return getattr(self,args[0])()
|
||||||
elif args[0]=='cas' and not self.settings.cas_provider:
|
elif args[0]=='cas' and not self.settings.cas_provider:
|
||||||
if args(1) == self.settings.cas_actions['login']:
|
if args(1) == self.settings.cas_actions['login']:
|
||||||
return self.cas_login(version=2)
|
return self.cas_login(version=2)
|
||||||
@@ -1598,7 +1609,7 @@ class Auth(object):
|
|||||||
if settings.cas_provider: ### THIS IS NOT LAZY
|
if settings.cas_provider: ### THIS IS NOT LAZY
|
||||||
settings.actions_disabled = \
|
settings.actions_disabled = \
|
||||||
['profile','register','change_password',
|
['profile','register','change_password',
|
||||||
'request_reset_password']
|
'request_reset_password','retrieve_username']
|
||||||
from gluon.contrib.login_methods.cas_auth import CasAuth
|
from gluon.contrib.login_methods.cas_auth import CasAuth
|
||||||
maps = settings.cas_maps
|
maps = settings.cas_maps
|
||||||
if not maps:
|
if not maps:
|
||||||
@@ -1616,6 +1627,7 @@ class Auth(object):
|
|||||||
urlbase = settings.cas_provider,
|
urlbase = settings.cas_provider,
|
||||||
actions=actions,
|
actions=actions,
|
||||||
maps=maps)
|
maps=maps)
|
||||||
|
return self
|
||||||
|
|
||||||
def log_event(self, description, vars=None, origin='auth'):
|
def log_event(self, description, vars=None, origin='auth'):
|
||||||
"""
|
"""
|
||||||
@@ -1700,7 +1712,8 @@ class Auth(object):
|
|||||||
"""
|
"""
|
||||||
login the user = db.auth_user(id)
|
login the user = db.auth_user(id)
|
||||||
"""
|
"""
|
||||||
# user=Storage(self.table_user()._filter_fields(user,id=True))
|
user = Storage(self.table_user()._filter_fields(user,id=True))
|
||||||
|
if 'password' in user: del user.password
|
||||||
current.session.auth = Storage(
|
current.session.auth = Storage(
|
||||||
user = user,
|
user = user,
|
||||||
last_visit = current.request.now,
|
last_visit = current.request.now,
|
||||||
@@ -1901,20 +1914,33 @@ class Auth(object):
|
|||||||
|
|
||||||
if self.settings.remember_me_form:
|
if self.settings.remember_me_form:
|
||||||
## adds a new input checkbox "remember me for longer"
|
## adds a new input checkbox "remember me for longer"
|
||||||
addrow(form,XML(" "),
|
if self.settings.formstyle != 'bootstrap':
|
||||||
DIV(XML(" "),
|
addrow(form,XML(" "),
|
||||||
INPUT(_type='checkbox',
|
DIV(XML(" "),
|
||||||
_class='checkbox',
|
INPUT(_type='checkbox',
|
||||||
_id="auth_user_remember",
|
_class='checkbox',
|
||||||
_name="remember",
|
_id="auth_user_remember",
|
||||||
),
|
_name="remember",
|
||||||
XML(" "),
|
),
|
||||||
|
XML(" "),
|
||||||
|
LABEL(
|
||||||
|
self.messages.label_remember_me,
|
||||||
|
_for="auth_user_remember",
|
||||||
|
)),"",
|
||||||
|
self.settings.formstyle,
|
||||||
|
'auth_user_remember__row')
|
||||||
|
elif self.settings.formstyle == 'bootstrap':
|
||||||
|
addrow(form,
|
||||||
|
"",
|
||||||
LABEL(
|
LABEL(
|
||||||
self.messages.label_remember_me,
|
INPUT(_type='checkbox',
|
||||||
_for="auth_user_remember",
|
_id="auth_user_remember",
|
||||||
)),"",
|
_name="remember"),
|
||||||
self.settings.formstyle,
|
self.messages.label_remember_me,
|
||||||
'auth_user_remember__row')
|
_class="checkbox"),
|
||||||
|
"",
|
||||||
|
self.settings.formstyle,
|
||||||
|
'auth_user_remember__row')
|
||||||
|
|
||||||
captcha = self.settings.login_captcha or \
|
captcha = self.settings.login_captcha or \
|
||||||
(self.settings.login_captcha!=False and self.settings.captcha)
|
(self.settings.login_captcha!=False and self.settings.captcha)
|
||||||
@@ -2131,6 +2157,9 @@ class Auth(object):
|
|||||||
repr(request.vars.get(passfield, None)),
|
repr(request.vars.get(passfield, None)),
|
||||||
error_message=self.messages.mismatched_password))
|
error_message=self.messages.mismatched_password))
|
||||||
|
|
||||||
|
if formstyle == 'bootstrap' :
|
||||||
|
form.custom.widget.password_two['_class'] = 'input-xlarge'
|
||||||
|
|
||||||
addrow(form, self.messages.verify_password + self.settings.label_separator,
|
addrow(form, self.messages.verify_password + self.settings.label_separator,
|
||||||
form.custom.widget.password_two,
|
form.custom.widget.password_two,
|
||||||
self.messages.verify_password_comment,
|
self.messages.verify_password_comment,
|
||||||
@@ -2420,7 +2449,7 @@ class Auth(object):
|
|||||||
session = current.session
|
session = current.session
|
||||||
|
|
||||||
if next is DEFAULT:
|
if next is DEFAULT:
|
||||||
next = self.settings.reset_password_next
|
next = self.next or self.settings.reset_password_next
|
||||||
try:
|
try:
|
||||||
key = request.vars.key or getarg(-1)
|
key = request.vars.key or getarg(-1)
|
||||||
t0 = int(key.split('-')[0])
|
t0 = int(key.split('-')[0])
|
||||||
@@ -2723,7 +2752,7 @@ class Auth(object):
|
|||||||
self.user = session.auth.user
|
self.user = session.auth.user
|
||||||
if requested_id is DEFAULT and not request.post_vars:
|
if requested_id is DEFAULT and not request.post_vars:
|
||||||
return SQLFORM.factory(Field('user_id', 'integer'))
|
return SQLFORM.factory(Field('user_id', 'integer'))
|
||||||
return self.user
|
return SQLFORM(table_user, user.id, readonly=True)
|
||||||
|
|
||||||
def update_groups(self):
|
def update_groups(self):
|
||||||
if not self.user:
|
if not self.user:
|
||||||
@@ -3179,7 +3208,7 @@ class Auth(object):
|
|||||||
def wiki(self,
|
def wiki(self,
|
||||||
slug=None,
|
slug=None,
|
||||||
env=None,
|
env=None,
|
||||||
render=None,
|
render='markmin',
|
||||||
manage_permissions=False,
|
manage_permissions=False,
|
||||||
force_prefix='',
|
force_prefix='',
|
||||||
restrict_search=False,
|
restrict_search=False,
|
||||||
@@ -3318,6 +3347,7 @@ class Crud(object):
|
|||||||
message=DEFAULT,
|
message=DEFAULT,
|
||||||
deletable=DEFAULT,
|
deletable=DEFAULT,
|
||||||
formname=DEFAULT,
|
formname=DEFAULT,
|
||||||
|
**attributes
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
method: Crud.update(table, record, [next=DEFAULT
|
method: Crud.update(table, record, [next=DEFAULT
|
||||||
@@ -3370,7 +3400,8 @@ class Crud(object):
|
|||||||
deletable=deletable,
|
deletable=deletable,
|
||||||
upload=self.settings.download_url,
|
upload=self.settings.download_url,
|
||||||
formstyle=self.settings.formstyle,
|
formstyle=self.settings.formstyle,
|
||||||
separator=self.settings.label_separator
|
separator=self.settings.label_separator,
|
||||||
|
**attributes
|
||||||
)
|
)
|
||||||
self.accepted = False
|
self.accepted = False
|
||||||
self.deleted = False
|
self.deleted = False
|
||||||
@@ -3428,6 +3459,7 @@ class Crud(object):
|
|||||||
log=DEFAULT,
|
log=DEFAULT,
|
||||||
message=DEFAULT,
|
message=DEFAULT,
|
||||||
formname=DEFAULT,
|
formname=DEFAULT,
|
||||||
|
**attributes
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
method: Crud.create(table, [next=DEFAULT [, onvalidation=DEFAULT
|
method: Crud.create(table, [next=DEFAULT [, onvalidation=DEFAULT
|
||||||
@@ -3454,6 +3486,7 @@ class Crud(object):
|
|||||||
message=message,
|
message=message,
|
||||||
deletable=False,
|
deletable=False,
|
||||||
formname=formname,
|
formname=formname,
|
||||||
|
**attributes
|
||||||
)
|
)
|
||||||
|
|
||||||
def read(self, table, record):
|
def read(self, table, record):
|
||||||
@@ -4313,34 +4346,39 @@ def prettydate(d,T=lambda x:x):
|
|||||||
return ''
|
return ''
|
||||||
else:
|
else:
|
||||||
return '[invalid date]'
|
return '[invalid date]'
|
||||||
|
if dt.days < 0:
|
||||||
|
suffix = ' from now'
|
||||||
|
dt = -dt
|
||||||
|
else:
|
||||||
|
suffix = ' ago'
|
||||||
if dt.days >= 2*365:
|
if dt.days >= 2*365:
|
||||||
return T('%d years ago') % int(dt.days / 365)
|
return T('%d years'+suffix) % int(dt.days / 365)
|
||||||
elif dt.days >= 365:
|
elif dt.days >= 365:
|
||||||
return T('1 year ago')
|
return T('1 year'+suffix)
|
||||||
elif dt.days >= 60:
|
elif dt.days >= 60:
|
||||||
return T('%d months ago') % int(dt.days / 30)
|
return T('%d months'+suffix) % int(dt.days / 30)
|
||||||
elif dt.days > 21:
|
elif dt.days > 21:
|
||||||
return T('1 month ago')
|
return T('1 month'+suffix)
|
||||||
elif dt.days >= 14:
|
elif dt.days >= 14:
|
||||||
return T('%d weeks ago') % int(dt.days / 7)
|
return T('%d weeks'+suffix) % int(dt.days / 7)
|
||||||
elif dt.days >= 7:
|
elif dt.days >= 7:
|
||||||
return T('1 week ago')
|
return T('1 week'+suffix)
|
||||||
elif dt.days > 1:
|
elif dt.days > 1:
|
||||||
return T('%d days ago') % dt.days
|
return T('%d days'+suffix) % dt.days
|
||||||
elif dt.days == 1:
|
elif dt.days == 1:
|
||||||
return T('1 day ago')
|
return T('1 day'+suffix)
|
||||||
elif dt.seconds >= 2*60*60:
|
elif dt.seconds >= 2*60*60:
|
||||||
return T('%d hours ago') % int(dt.seconds / 3600)
|
return T('%d hours'+suffix) % int(dt.seconds / 3600)
|
||||||
elif dt.seconds >= 60*60:
|
elif dt.seconds >= 60*60:
|
||||||
return T('1 hour ago')
|
return T('1 hour'+suffix)
|
||||||
elif dt.seconds >= 2*60:
|
elif dt.seconds >= 2*60:
|
||||||
return T('%d minutes ago') % int(dt.seconds / 60)
|
return T('%d minutes'+suffix) % int(dt.seconds / 60)
|
||||||
elif dt.seconds >= 60:
|
elif dt.seconds >= 60:
|
||||||
return T('1 minute ago')
|
return T('1 minute'+suffix)
|
||||||
elif dt.seconds > 1:
|
elif dt.seconds > 1:
|
||||||
return T('%d seconds ago') % dt.seconds
|
return T('%d seconds'+suffix) % dt.seconds
|
||||||
elif dt.seconds == 1:
|
elif dt.seconds == 1:
|
||||||
return T('1 second ago')
|
return T('1 second'+suffix)
|
||||||
else:
|
else:
|
||||||
return T('now')
|
return T('now')
|
||||||
|
|
||||||
@@ -4539,6 +4577,7 @@ class Wiki(object):
|
|||||||
self.env['component'] = Wiki.component
|
self.env['component'] = Wiki.component
|
||||||
if render == 'markmin': render=self.markmin_render
|
if render == 'markmin': render=self.markmin_render
|
||||||
elif render == 'html': render=self.html_render
|
elif render == 'html': render=self.html_render
|
||||||
|
self.render = render
|
||||||
self.auth = auth
|
self.auth = auth
|
||||||
if self.auth.user:
|
if self.auth.user:
|
||||||
self.force_prefix = force_prefix % self.auth.user
|
self.force_prefix = force_prefix % self.auth.user
|
||||||
@@ -4548,47 +4587,56 @@ class Wiki(object):
|
|||||||
perms = self.manage_permissions = manage_permissions
|
perms = self.manage_permissions = manage_permissions
|
||||||
self.restrict_search = restrict_search
|
self.restrict_search = restrict_search
|
||||||
db = auth.db
|
db = auth.db
|
||||||
table_definitions = {
|
table_definitions = [
|
||||||
'wiki_page':{
|
('wiki_page',{
|
||||||
'args':[
|
'args':[
|
||||||
Field('slug',
|
Field('slug',
|
||||||
requires=[IS_SLUG(),
|
requires=[IS_SLUG(),
|
||||||
IS_NOT_IN_DB(db,'wiki_page.slug')],
|
IS_NOT_IN_DB(db,'wiki_page.slug')],
|
||||||
readable=False,writable=False),
|
readable=False,writable=False),
|
||||||
Field('title',unique=True),
|
Field('title',unique=True),
|
||||||
Field('body','text',notnull=True),
|
Field('body','text',notnull=True),
|
||||||
Field('tags','list:string'),
|
Field('tags','list:string'),
|
||||||
Field('can_read','list:string',
|
Field('can_read','list:string',
|
||||||
writable=perms,
|
writable=perms,
|
||||||
readable=perms,
|
readable=perms,
|
||||||
default=[Wiki.everybody]),
|
default=[Wiki.everybody]),
|
||||||
Field('can_edit', 'list:string',
|
Field('can_edit', 'list:string',
|
||||||
writable=perms,readable=perms,
|
writable=perms,readable=perms,
|
||||||
default=[Wiki.everybody]),
|
default=[Wiki.everybody]),
|
||||||
Field('changelog'),
|
Field('changelog'),
|
||||||
Field('html','text',compute=render,
|
Field('html','text',compute=render,
|
||||||
readable=False, writable=False),
|
readable=False, writable=False),
|
||||||
auth.signature],
|
auth.signature],
|
||||||
'vars':{'format':'%(title)s'}},
|
'vars':{'format':'%(title)s'}}),
|
||||||
'wiki_tag':{
|
('wiki_tag',{
|
||||||
'args':[
|
'args':[
|
||||||
Field('name'),
|
Field('name'),
|
||||||
Field('wiki_page','reference wiki_page'),
|
Field('wiki_page','reference wiki_page'),
|
||||||
auth.signature],
|
auth.signature],
|
||||||
'vars':{'format':'%(name)s'}},
|
'vars':{'format':'%(name)s'}}),
|
||||||
'wiki_media':{
|
('wiki_media',{
|
||||||
'args':[
|
'args':[
|
||||||
Field('wiki_page','reference wiki_page'),
|
Field('wiki_page','reference wiki_page'),
|
||||||
Field('title',required=True),
|
Field('title',required=True),
|
||||||
Field('file','upload',required=True),
|
Field('filename','upload',required=True),
|
||||||
auth.signature],
|
auth.signature],
|
||||||
'vars':{'format':'%(title)s'}}
|
'vars':{'format':'%(title)s'}})
|
||||||
}
|
]
|
||||||
|
|
||||||
# define only non-existent tables
|
# define only non-existent tables
|
||||||
for key, value in table_definitions.iteritems():
|
for key, value in table_definitions:
|
||||||
|
args = []
|
||||||
if not key in db.tables():
|
if not key in db.tables():
|
||||||
db.define_table(key, *value['args'], **value['vars'])
|
# look for wiki_ extra fields in auth.settings
|
||||||
|
extra_fields = auth.settings.extra_fields
|
||||||
|
if extra_fields:
|
||||||
|
if key in extra_fields:
|
||||||
|
if extra_fields[key]:
|
||||||
|
for field in extra_fields[key]:
|
||||||
|
args.append(field)
|
||||||
|
args += value['args']
|
||||||
|
db.define_table(key, *args, **value['vars'])
|
||||||
|
|
||||||
def update_tags_insert(page,id,db=db):
|
def update_tags_insert(page,id,db=db):
|
||||||
for tag in page.tags or []:
|
for tag in page.tags or []:
|
||||||
@@ -4665,17 +4713,19 @@ class Wiki(object):
|
|||||||
)
|
)
|
||||||
elif zero=='_cloud':
|
elif zero=='_cloud':
|
||||||
return self.cloud()
|
return self.cloud()
|
||||||
|
elif zero == '_preview':
|
||||||
|
return self.preview(self.render)
|
||||||
|
|
||||||
def first_paragraph(self,page):
|
def first_paragraph(self,page):
|
||||||
if not self.can_read(page):
|
if not self.can_read(page):
|
||||||
mm = page.body.replace('\r','')
|
mm = (page.body or '').replace('\r','')
|
||||||
ps = [p for p in mm.split('\n\n') \
|
ps = [p for p in mm.split('\n\n') \
|
||||||
if not p.startswith('#') and p.strip()]
|
if not p.startswith('#') and p.strip()]
|
||||||
if ps: return ps[0]
|
if ps: return ps[0]
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def fix_hostname(self,body):
|
def fix_hostname(self,body):
|
||||||
return body.replace('://HOSTNAME','://%s' % self.host)
|
return (body or '').replace('://HOSTNAME','://%s' % self.host)
|
||||||
|
|
||||||
def read(self,slug):
|
def read(self,slug):
|
||||||
if slug in '_cloud':
|
if slug in '_cloud':
|
||||||
@@ -4730,7 +4780,8 @@ class Wiki(object):
|
|||||||
db.wiki_page.title.default = title_guess
|
db.wiki_page.title.default = title_guess
|
||||||
db.wiki_page.slug.default = slug
|
db.wiki_page.slug.default = slug
|
||||||
if slug == 'wiki-menu':
|
if slug == 'wiki-menu':
|
||||||
db.wiki_page.body.default = '- Menu Item > @////index\n- - Submenu > http://web2py.com'
|
db.wiki_page.body.default = \
|
||||||
|
'- Menu Item > @////index\n- - Submenu > http://web2py.com'
|
||||||
else:
|
else:
|
||||||
db.wiki_page.body.default = '## %s\n\npage content' % title_guess
|
db.wiki_page.body.default = '## %s\n\npage content' % title_guess
|
||||||
vars = current.request.post_vars
|
vars = current.request.post_vars
|
||||||
@@ -4744,17 +4795,41 @@ class Wiki(object):
|
|||||||
elif form.accepted:
|
elif form.accepted:
|
||||||
current.session.flash = 'page created'
|
current.session.flash = 'page created'
|
||||||
redirect(URL(args=slug))
|
redirect(URL(args=slug))
|
||||||
return dict(content=form)
|
script = """
|
||||||
|
$(function() {
|
||||||
|
if (!$('#wiki_page_body').length) return;
|
||||||
|
var pagecontent = $('#wiki_page_body');
|
||||||
|
pagecontent.css('font-family', 'Monaco,Menlo,Consolas,"Courier New",monospace');
|
||||||
|
var prevbutton = $('<button class="btn nopreview">Preview</button>');
|
||||||
|
var preview = $('<div id="preview"></div>').hide();
|
||||||
|
var table = $('form');
|
||||||
|
prevbutton.insertBefore(table);
|
||||||
|
preview.insertBefore(table);
|
||||||
|
prevbutton.on('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (prevbutton.hasClass('nopreview')) {
|
||||||
|
prevbutton.addClass('preview').removeClass('nopreview').html('Edit Source');
|
||||||
|
web2py_ajax_page('post', '%(url)s', {body : $('#wiki_page_body').val()}, 'preview');
|
||||||
|
table.fadeOut('medium', function() {preview.fadeIn()});
|
||||||
|
} else {
|
||||||
|
prevbutton.addClass('nopreview').removeClass('preview').html('Preview');
|
||||||
|
preview.fadeOut('medium', function() {table.fadeIn()});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
""" % dict(url=URL(args=('_preview')))
|
||||||
|
return dict(content=TAG[''](form, SCRIPT(script)))
|
||||||
|
|
||||||
def editmedia(self,slug):
|
def editmedia(self,slug):
|
||||||
auth = self.auth
|
auth = self.auth
|
||||||
db = auth.db
|
db = auth.db
|
||||||
page = db.wiki_page(slug=slug)
|
page = db.wiki_page(slug=slug)
|
||||||
if not (page and self.can_edit(page)): return self.not_authorized(page)
|
if not (page and self.can_edit(page)): return self.not_authorized(page)
|
||||||
self.auth.db.wiki_media.id.represent = lambda id,row:\
|
self.auth.db.wiki_media.id.represent = lambda id,row: \
|
||||||
|
id if not row.filename else \
|
||||||
SPAN('@////%i/%s.%s' % \
|
SPAN('@////%i/%s.%s' % \
|
||||||
(id,IS_SLUG.urlify(row.title.split('.')[0]),
|
(id,IS_SLUG.urlify(row.title.split('.')[0]),
|
||||||
row.file.split('.')[-1]))
|
row.filename.split('.')[-1]))
|
||||||
self.auth.db.wiki_media.wiki_page.default = page.id
|
self.auth.db.wiki_media.wiki_page.default = page.id
|
||||||
self.auth.db.wiki_media.wiki_page.writable = False
|
self.auth.db.wiki_media.wiki_page.writable = False
|
||||||
content = SQLFORM.grid(
|
content = SQLFORM.grid(
|
||||||
@@ -4799,7 +4874,7 @@ class Wiki(object):
|
|||||||
if self.manage_permissions:
|
if self.manage_permissions:
|
||||||
page = db.wiki_page(media.wiki_page)
|
page = db.wiki_page(media.wiki_page)
|
||||||
if not self.can_read(page): return self.not_authorized(page)
|
if not self.can_read(page): return self.not_authorized(page)
|
||||||
request.args = [media.file]
|
request.args = [media.filename]
|
||||||
return current.response.download(request,db)
|
return current.response.download(request,db)
|
||||||
else:
|
else:
|
||||||
raise HTTP(404)
|
raise HTTP(404)
|
||||||
@@ -4884,12 +4959,13 @@ class Wiki(object):
|
|||||||
if query is None:
|
if query is None:
|
||||||
query = (db.wiki_page.id==db.wiki_tag.wiki_page)&\
|
query = (db.wiki_page.id==db.wiki_tag.wiki_page)&\
|
||||||
(db.wiki_tag.name.belongs(tags))
|
(db.wiki_tag.name.belongs(tags))
|
||||||
query = query|db.wiki_page.title.startswith(request.vars.q)
|
query = query|db.wiki_page.title.contains(request.vars.q)
|
||||||
if self.restrict_search and not self.manage():
|
if self.restrict_search and not self.manage():
|
||||||
query = query&(db.wiki_page.created_by==self.auth.user_id)
|
query = query&(db.wiki_page.created_by==self.auth.user_id)
|
||||||
pages = db(query).select(
|
pages = db(query).select(count,
|
||||||
*fields,**dict(orderby=orderby or ~count,
|
*fields,**dict(orderby=orderby or ~count,
|
||||||
groupby=db.wiki_page.id,
|
groupby=reduce(lambda a,b:a|b,fields),
|
||||||
|
distinct=True,
|
||||||
limitby=limitby))
|
limitby=limitby))
|
||||||
if request.extension in ('html','load'):
|
if request.extension in ('html','load'):
|
||||||
if not pages:
|
if not pages:
|
||||||
@@ -4897,18 +4973,19 @@ class Wiki(object):
|
|||||||
_class='w2p_wiki_form'))
|
_class='w2p_wiki_form'))
|
||||||
def link(t):
|
def link(t):
|
||||||
return A(t,_href=URL(args='_search',vars=dict(q=t)))
|
return A(t,_href=URL(args='_search',vars=dict(q=t)))
|
||||||
items = [DIV(H3(A(p.title,_href=URL(args=p.slug))),
|
items = [DIV(H3(A(p.wiki_page.title,_href=URL(
|
||||||
MARKMIN(self.first_paragraph(p)) \
|
args=p.wiki_page.slug))),
|
||||||
|
MARKMIN(self.first_paragraph(p.wiki_page)) \
|
||||||
if preview else '',
|
if preview else '',
|
||||||
DIV(_class='w2p_wiki_tags',
|
DIV(_class='w2p_wiki_tags',
|
||||||
*[link(t.strip()) for t in \
|
*[link(t.strip()) for t in \
|
||||||
p.tags or [] if t.strip()]),
|
p.wiki_page.tags or [] if t.strip()]),
|
||||||
_class='w2p_wiki_search_item')
|
_class='w2p_wiki_search_item')
|
||||||
for p in pages]
|
for p in pages]
|
||||||
content.append(DIV(_class='w2p_wiki_pages',*items))
|
content.append(DIV(_class='w2p_wiki_pages',*items))
|
||||||
else:
|
else:
|
||||||
cloud=False
|
cloud=False
|
||||||
content = [p.as_dict() for p in pages]
|
content = [p.wiki_page.as_dict() for p in pages]
|
||||||
elif cloud:
|
elif cloud:
|
||||||
content.append(self.cloud()['content'])
|
content.append(self.cloud()['content'])
|
||||||
if request.extension=='load':
|
if request.extension=='load':
|
||||||
@@ -4919,19 +4996,26 @@ class Wiki(object):
|
|||||||
count = db.wiki_tag.wiki_page.count(distinct=True)
|
count = db.wiki_tag.wiki_page.count(distinct=True)
|
||||||
ids = db(db.wiki_tag).select(
|
ids = db(db.wiki_tag).select(
|
||||||
db.wiki_tag.name,count,
|
db.wiki_tag.name,count,
|
||||||
groupby=db.wiki_tag.name,
|
distinct=True,
|
||||||
orderby=~count,limitby=(0,20))
|
groupby = db.wiki_tag.name,
|
||||||
|
orderby = ~count, limitby=(0,20))
|
||||||
if ids:
|
if ids:
|
||||||
a,b = ids[0](count), ids[-1](count)
|
a,b = ids[0](count), ids[-1](count)
|
||||||
def scale(c):
|
def style(c):
|
||||||
return '%.2f' % (3.0*(c-b)/max(a-b,1)+1)
|
STYLE ='padding:0 0.2em;line-height:%.2fem;font-size:%.2fem'
|
||||||
items = [A(item.wiki_tag.name,
|
size = (1.5*(c-b)/max(a-b,1)+1.3)
|
||||||
_style='padding:0.2em;font-size:%sem' \
|
return STYLE % (1.3,size)
|
||||||
% scale(item(count)),
|
items = []
|
||||||
_href=URL(args='_search',
|
for item in ids:
|
||||||
vars=dict(q=item.wiki_tag.name)))
|
items.append(A(item.wiki_tag.name,
|
||||||
for item in ids]
|
_style=style(item(count)),
|
||||||
return dict(content=DIV(_class='w2p_cloud',*items))
|
_href=URL(args='_search',
|
||||||
|
vars=dict(q=item.wiki_tag.name))))
|
||||||
|
items.append(' ')
|
||||||
|
return dict(content = DIV(_class='w2p_cloud',*items))
|
||||||
|
def preview(self, render):
|
||||||
|
request = current.request
|
||||||
|
return render(request.post_vars)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import doctest
|
import doctest
|
||||||
|
|||||||
+46
-15
@@ -9,6 +9,9 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
|
|||||||
This file specifically includes utilities for security.
|
This file specifically includes utilities for security.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import string
|
||||||
|
import threading
|
||||||
|
import struct
|
||||||
import hashlib
|
import hashlib
|
||||||
import hmac
|
import hmac
|
||||||
import uuid
|
import uuid
|
||||||
@@ -18,7 +21,11 @@ import os
|
|||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
from contrib.pbkdf2 import pbkdf2_hex
|
try:
|
||||||
|
from contrib.pbkdf2 import pbkdf2_hex
|
||||||
|
HAVE_PBKDF2 = True
|
||||||
|
except ImportError:
|
||||||
|
HAVE_PBKDF2 = False
|
||||||
|
|
||||||
logger = logging.getLogger("web2py")
|
logger = logging.getLogger("web2py")
|
||||||
|
|
||||||
@@ -88,7 +95,7 @@ DIGEST_ALG_BY_SIZE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
### compute constant ctokens
|
### compute constant CTOKENS
|
||||||
def initialize_urandom():
|
def initialize_urandom():
|
||||||
"""
|
"""
|
||||||
This function and the web2py_uuid follow from the following discussion:
|
This function and the web2py_uuid follow from the following discussion:
|
||||||
@@ -108,6 +115,7 @@ def initialize_urandom():
|
|||||||
random.seed(node_id + microseconds)
|
random.seed(node_id + microseconds)
|
||||||
try:
|
try:
|
||||||
os.urandom(1)
|
os.urandom(1)
|
||||||
|
have_urandom = True
|
||||||
try:
|
try:
|
||||||
# try to add process-specific entropy
|
# try to add process-specific entropy
|
||||||
frandom = open('/dev/urandom','wb')
|
frandom = open('/dev/urandom','wb')
|
||||||
@@ -119,14 +127,33 @@ def initialize_urandom():
|
|||||||
# works anyway
|
# works anyway
|
||||||
pass
|
pass
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
|
have_urandom = False
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"""Cryptographically secure session management is not possible on your system because
|
"""Cryptographically secure session management is not possible on your system because
|
||||||
your system does not provide a cryptographically secure entropy source.
|
your system does not provide a cryptographically secure entropy source.
|
||||||
This is not specific to web2py; consider deploying on a different operating system.""")
|
This is not specific to web2py; consider deploying on a different operating system.""")
|
||||||
return ctokens
|
unpacked_ctokens = struct.unpack('=QQ',string.join(
|
||||||
ctokens = initialize_urandom()
|
(chr(x) for x in ctokens),''))
|
||||||
|
return unpacked_ctokens, have_urandom
|
||||||
|
UNPACKED_CTOKENS, HAVE_URANDOM = initialize_urandom()
|
||||||
|
|
||||||
def web2py_uuid():
|
def fast_urandom16(urandom=[], locker = threading.RLock()):
|
||||||
|
"""
|
||||||
|
this is 4x faster than calling os.urandom(16) and prevents
|
||||||
|
the "too many files open" issue with concurrent access to os.urandom()
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return urandom.pop()
|
||||||
|
except IndexError:
|
||||||
|
try:
|
||||||
|
locker.acquire()
|
||||||
|
ur = os.urandom(16*1024)
|
||||||
|
urandom += [ur[i:i+16] for i in xrange(16,1024*16,16)]
|
||||||
|
return ur[0:16]
|
||||||
|
finally:
|
||||||
|
locker.release()
|
||||||
|
|
||||||
|
def web2py_uuid(ctokens=UNPACKED_CTOKENS):
|
||||||
"""
|
"""
|
||||||
This function follows from the following discussion:
|
This function follows from the following discussion:
|
||||||
http://groups.google.com/group/web2py-developers/browse_thread/thread/7fd5789a7da3f09
|
http://groups.google.com/group/web2py-developers/browse_thread/thread/7fd5789a7da3f09
|
||||||
@@ -134,15 +161,17 @@ def web2py_uuid():
|
|||||||
It works like uuid.uuid4 except that tries to use os.urandom() if possible
|
It works like uuid.uuid4 except that tries to use os.urandom() if possible
|
||||||
and it XORs the output with the tokens uniquely associated with this machine.
|
and it XORs the output with the tokens uniquely associated with this machine.
|
||||||
"""
|
"""
|
||||||
bytes = [random.randrange(256) for i in range(16)]
|
rand_longs = (random.getrandbits(64),random.getrandbits(64))
|
||||||
try:
|
if HAVE_URANDOM:
|
||||||
ubytes = [ord(c) for c in os.urandom(16)] # use /dev/urandom if possible
|
urand_longs = struct.unpack('=QQ', fast_urandom16())
|
||||||
bytes = [bytes[i] ^ ubytes[i] for i in range(16)]
|
byte_s = struct.pack('=QQ',
|
||||||
except NotImplementedError:
|
rand_longs[0]^urand_longs[0]^ctokens[0],
|
||||||
pass
|
rand_longs[1]^urand_longs[1]^ctokens[1])
|
||||||
## xor bytes with constant ctokens
|
else:
|
||||||
bytes = ''.join(chr(c ^ ctokens[i]) for i,c in enumerate(bytes))
|
byte_s = struct.pack('=QQ',
|
||||||
return str(uuid.UUID(bytes=bytes, version=4))
|
rand_longs[0]^ctokens[0],
|
||||||
|
rand_longs[1]^ctokens[1])
|
||||||
|
return str(uuid.UUID(bytes=byte_s, version=4))
|
||||||
|
|
||||||
REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')
|
REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')
|
||||||
|
|
||||||
@@ -161,6 +190,8 @@ def is_valid_ip_address(address):
|
|||||||
elif address.lower() in ('unkown',''):
|
elif address.lower() in ('unkown',''):
|
||||||
return False
|
return False
|
||||||
elif address.count('.')==3: # assume IPv4
|
elif address.count('.')==3: # assume IPv4
|
||||||
|
if address.startswith('::ffff:'):
|
||||||
|
address = address[7:]
|
||||||
if hasattr(socket,'inet_aton'): # try validate using the OS
|
if hasattr(socket,'inet_aton'): # try validate using the OS
|
||||||
try:
|
try:
|
||||||
addr = socket.inet_aton(address)
|
addr = socket.inet_aton(address)
|
||||||
@@ -169,7 +200,7 @@ def is_valid_ip_address(address):
|
|||||||
return False
|
return False
|
||||||
else: # try validate using Regex
|
else: # try validate using Regex
|
||||||
match = REGEX_IPv4.match(address)
|
match = REGEX_IPv4.match(address)
|
||||||
if match and all(0<=int(math.group(i))<256 for i in (1,2,3,4)):
|
if match and all(0<=int(match.group(i))<256 for i in (1,2,3,4)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
elif hasattr(socket,'inet_pton'): # assume IPv6, try using the OS
|
elif hasattr(socket,'inet_pton'): # assume IPv6, try using the OS
|
||||||
|
|||||||
+119
-20
@@ -117,6 +117,10 @@ class Validator(object):
|
|||||||
"""
|
"""
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def __call__(self,value):
|
||||||
|
raise NotImplementedError
|
||||||
|
return (value, None)
|
||||||
|
|
||||||
|
|
||||||
class IS_MATCH(Validator):
|
class IS_MATCH(Validator):
|
||||||
"""
|
"""
|
||||||
@@ -462,12 +466,13 @@ class IS_IN_DB(Validator):
|
|||||||
groupby = self.groupby
|
groupby = self.groupby
|
||||||
distinct = self.distinct
|
distinct = self.distinct
|
||||||
dd = dict(orderby=orderby, groupby=groupby,
|
dd = dict(orderby=orderby, groupby=groupby,
|
||||||
distinct=distinct, cache=self.cache)
|
distinct=distinct, cache=self.cache,
|
||||||
|
cacheable=True)
|
||||||
records = self.dbset(table).select(*fields, **dd)
|
records = self.dbset(table).select(*fields, **dd)
|
||||||
else:
|
else:
|
||||||
orderby = self.orderby or \
|
orderby = self.orderby or \
|
||||||
reduce(lambda a,b:a|b,(f for f in fields if not f.name=='id'))
|
reduce(lambda a,b:a|b,(f for f in fields if not f.name=='id'))
|
||||||
dd = dict(orderby=orderby, cache=self.cache)
|
dd = dict(orderby=orderby, cache=self.cache, cacheable=True)
|
||||||
records = self.dbset(table).select(table.ALL, **dd)
|
records = self.dbset(table).select(table.ALL, **dd)
|
||||||
self.theset = [str(r[self.kfield]) for r in records]
|
self.theset = [str(r[self.kfield]) for r in records]
|
||||||
if isinstance(self.label,str):
|
if isinstance(self.label,str):
|
||||||
@@ -488,6 +493,8 @@ class IS_IN_DB(Validator):
|
|||||||
table = self.dbset.db[self.ktable]
|
table = self.dbset.db[self.ktable]
|
||||||
field = table[self.kfield]
|
field = table[self.kfield]
|
||||||
if self.multiple:
|
if self.multiple:
|
||||||
|
if self._and:
|
||||||
|
raise NotImplementedError
|
||||||
if isinstance(value,list):
|
if isinstance(value,list):
|
||||||
values=value
|
values=value
|
||||||
elif value:
|
elif value:
|
||||||
@@ -497,8 +504,20 @@ class IS_IN_DB(Validator):
|
|||||||
if isinstance(self.multiple,(tuple,list)) and \
|
if isinstance(self.multiple,(tuple,list)) and \
|
||||||
not self.multiple[0]<=len(values)<self.multiple[1]:
|
not self.multiple[0]<=len(values)<self.multiple[1]:
|
||||||
return (values, translate(self.error_message))
|
return (values, translate(self.error_message))
|
||||||
if self.dbset(field.belongs(values)).count()==len(values):
|
if self.theset:
|
||||||
return (values, None)
|
if not [v for v in values if not v in self.theset]:
|
||||||
|
return (values, None)
|
||||||
|
else:
|
||||||
|
from dal import GoogleDatastoreAdapter
|
||||||
|
def count(values, s=self.dbset, f=field):
|
||||||
|
return s(f.belongs(map(int,values))).count()
|
||||||
|
if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
|
||||||
|
range_ids = range(0,len(values),30)
|
||||||
|
total = sum(count(values[i:i+30]) for i in range_ids)
|
||||||
|
if total == len(values):
|
||||||
|
return (values, None)
|
||||||
|
elif count(values) == len(values):
|
||||||
|
return (values, None)
|
||||||
elif self.theset:
|
elif self.theset:
|
||||||
if str(value) in self.theset:
|
if str(value) in self.theset:
|
||||||
if self._and:
|
if self._and:
|
||||||
@@ -2329,11 +2348,12 @@ class IS_LIST_OF(Validator):
|
|||||||
new_value = []
|
new_value = []
|
||||||
if self.other:
|
if self.other:
|
||||||
for item in ivalue:
|
for item in ivalue:
|
||||||
(v, e) = self.other(item)
|
if item.strip():
|
||||||
if e:
|
(v, e) = self.other(item)
|
||||||
return (value, e)
|
if e:
|
||||||
else:
|
return (ivalue, e)
|
||||||
new_value.append(v)
|
else:
|
||||||
|
new_value.append(v)
|
||||||
ivalue = new_value
|
ivalue = new_value
|
||||||
return (ivalue, None)
|
return (ivalue, None)
|
||||||
|
|
||||||
@@ -2592,7 +2612,9 @@ class LazyCrypt(object):
|
|||||||
key = self.crypt.key
|
key = self.crypt.key
|
||||||
else:
|
else:
|
||||||
key = ''
|
key = ''
|
||||||
if stored_password.count('$')==2:
|
if stored_password is None:
|
||||||
|
return False
|
||||||
|
elif stored_password.count('$')==2:
|
||||||
(digest_alg, salt, hash) = stored_password.split('$')
|
(digest_alg, salt, hash) = stored_password.split('$')
|
||||||
h = simple_hash(self.password, key, salt, digest_alg)
|
h = simple_hash(self.password, key, salt, digest_alg)
|
||||||
temp_pass = '%s$%s$%s' % (digest_alg, salt, h)
|
temp_pass = '%s$%s$%s' % (digest_alg, salt, h)
|
||||||
@@ -2707,6 +2729,43 @@ class CRYPT(object):
|
|||||||
return ('', translate(self.error_message))
|
return ('', translate(self.error_message))
|
||||||
return (LazyCrypt(self,value),None)
|
return (LazyCrypt(self,value),None)
|
||||||
|
|
||||||
|
# entropy calculator for IS_STRONG
|
||||||
|
#
|
||||||
|
lowerset = frozenset(unicode('abcdefghijklmnopqrstuvwxyz'))
|
||||||
|
upperset = frozenset(unicode('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
|
||||||
|
numberset = frozenset(unicode('0123456789'))
|
||||||
|
sym1set = frozenset(unicode('!@#$%^&*()'))
|
||||||
|
sym2set = frozenset(unicode('~`-_=+[]{}\\|;:\'",.<>?/'))
|
||||||
|
otherset = frozenset(unicode('0123456789abcdefghijklmnopqrstuvwxyz')) # anything else
|
||||||
|
|
||||||
|
def calc_entropy(string):
|
||||||
|
" calculate a simple entropy for a given string "
|
||||||
|
import math
|
||||||
|
alphabet = 0 # alphabet size
|
||||||
|
other = set()
|
||||||
|
seen = set()
|
||||||
|
lastset = None
|
||||||
|
if isinstance(string, str):
|
||||||
|
string = unicode(string, encoding='utf8')
|
||||||
|
for c in string:
|
||||||
|
# classify this character
|
||||||
|
inset = otherset
|
||||||
|
for cset in (lowerset, upperset, numberset, sym1set, sym2set):
|
||||||
|
if c in cset:
|
||||||
|
inset = cset
|
||||||
|
break
|
||||||
|
# calculate effect of character on alphabet size
|
||||||
|
if inset not in seen:
|
||||||
|
seen.add(inset)
|
||||||
|
alphabet += len(inset) # credit for a new character set
|
||||||
|
elif c not in other:
|
||||||
|
alphabet += 1 # credit for unique characters
|
||||||
|
other.add(c)
|
||||||
|
if inset is not lastset:
|
||||||
|
alphabet += 1 # credit for set transitions
|
||||||
|
lastset = cset
|
||||||
|
entropy = len(string) * math.log(alphabet) / 0.6931471805599453 # math.log(2)
|
||||||
|
return round(entropy, 2)
|
||||||
|
|
||||||
class IS_STRONG(object):
|
class IS_STRONG(object):
|
||||||
"""
|
"""
|
||||||
@@ -2716,23 +2775,61 @@ class IS_STRONG(object):
|
|||||||
requires=IS_STRONG(min=10, special=2, upper=2))
|
requires=IS_STRONG(min=10, special=2, upper=2))
|
||||||
|
|
||||||
enforces complexity requirements on a field
|
enforces complexity requirements on a field
|
||||||
|
|
||||||
|
>>> IS_STRONG(es=True)('Abcd1234')
|
||||||
|
('Abcd1234', 'Must include at least 1 of the following: ~!@#$%^&*()_+-=?<>,.:;{}[]|')
|
||||||
|
>>> IS_STRONG(es=True)('Abcd1234!')
|
||||||
|
('Abcd1234!', None)
|
||||||
|
>>> IS_STRONG(es=True, entropy=1)('a')
|
||||||
|
('a', None)
|
||||||
|
>>> IS_STRONG(es=True, entropy=1, min=2)('a')
|
||||||
|
('a', 'Minimum length is 2')
|
||||||
|
>>> IS_STRONG(es=True, entropy=100)('abc123')
|
||||||
|
('abc123', 'Entropy (32.35) less than required (100)')
|
||||||
|
>>> IS_STRONG(es=True, entropy=100)('and')
|
||||||
|
('and', 'Entropy (14.57) less than required (100)')
|
||||||
|
>>> IS_STRONG(es=True, entropy=100)('aaa')
|
||||||
|
('aaa', 'Entropy (14.42) less than required (100)')
|
||||||
|
>>> IS_STRONG(es=True, entropy=100)('a1d')
|
||||||
|
('a1d', 'Entropy (15.97) less than required (100)')
|
||||||
|
>>> IS_STRONG(es=True, entropy=100)('añd')
|
||||||
|
('a\\xc3\\xb1d', 'Entropy (18.13) less than required (100)')
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, min=8, max=20, upper=1, lower=1, number=1,
|
def __init__(self, min=None, max=None, upper=None, lower=None, number=None,
|
||||||
special=1, specials=r'~!@#$%^&*()_+-=?<>,.:;{}[]|',
|
entropy=None,
|
||||||
invalid=' "', error_message=None):
|
special=None, specials=r'~!@#$%^&*()_+-=?<>,.:;{}[]|',
|
||||||
self.min = min
|
invalid=' "', error_message=None, es=False):
|
||||||
self.max = max
|
self.entropy = entropy
|
||||||
self.upper = upper
|
if entropy is None:
|
||||||
self.lower = lower
|
# enforce default requirements
|
||||||
self.number = number
|
self.min = 8 if min is None else min
|
||||||
self.special = special
|
self.max = max # was 20, but that doesn't make sense
|
||||||
|
self.upper = 1 if upper is None else upper
|
||||||
|
self.lower = 1 if lower is None else lower
|
||||||
|
self.number = 1 if number is None else number
|
||||||
|
self.special = 1 if special is None else special
|
||||||
|
else:
|
||||||
|
# by default, an entropy spec is exclusive
|
||||||
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
self.upper = upper
|
||||||
|
self.lower = lower
|
||||||
|
self.number = number
|
||||||
|
self.special = special
|
||||||
self.specials = specials
|
self.specials = specials
|
||||||
self.invalid = invalid
|
self.invalid = invalid
|
||||||
self.error_message = error_message
|
self.error_message = error_message
|
||||||
|
self.estring = es # return error message as string (for doctest)
|
||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
failures = []
|
failures = []
|
||||||
|
if self.entropy is not None:
|
||||||
|
entropy = calc_entropy(value)
|
||||||
|
if entropy < self.entropy:
|
||||||
|
failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") \
|
||||||
|
% dict(have=entropy, need=self.entropy))
|
||||||
if type(self.min) == int and self.min > 0:
|
if type(self.min) == int and self.min > 0:
|
||||||
if not len(value) >= self.min:
|
if not len(value) >= self.min:
|
||||||
failures.append(translate("Minimum length is %s") % self.min)
|
failures.append(translate("Minimum length is %s") % self.min)
|
||||||
@@ -2743,7 +2840,7 @@ class IS_STRONG(object):
|
|||||||
all_special = [ch in value for ch in self.specials]
|
all_special = [ch in value for ch in self.specials]
|
||||||
if self.special > 0:
|
if self.special > 0:
|
||||||
if not all_special.count(True) >= self.special:
|
if not all_special.count(True) >= self.special:
|
||||||
failures.append(translate("Must include at least %s of the following : %s") \
|
failures.append(translate("Must include at least %s of the following: %s") \
|
||||||
% (self.special, self.specials))
|
% (self.special, self.specials))
|
||||||
if self.invalid:
|
if self.invalid:
|
||||||
all_invalid = [ch in value for ch in self.invalid]
|
all_invalid = [ch in value for ch in self.invalid]
|
||||||
@@ -2783,6 +2880,8 @@ class IS_STRONG(object):
|
|||||||
if len(failures) == 0:
|
if len(failures) == 0:
|
||||||
return (value, None)
|
return (value, None)
|
||||||
if not self.error_message:
|
if not self.error_message:
|
||||||
|
if self.estring:
|
||||||
|
return (value, '|'.join(failures))
|
||||||
from html import XML
|
from html import XML
|
||||||
return (value, XML('<br />'.join(failures)))
|
return (value, XML('<br />'.join(failures)))
|
||||||
else:
|
else:
|
||||||
|
|||||||
+32
-18
@@ -804,12 +804,12 @@ def console():
|
|||||||
default=False,
|
default=False,
|
||||||
help=msg)
|
help=msg)
|
||||||
|
|
||||||
parser.add_option('-N',
|
parser.add_option('-Y',
|
||||||
'--no-cron',
|
'--run-cron',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
dest='nocron',
|
dest='runcron',
|
||||||
default=False,
|
default=False,
|
||||||
help='do not start cron automatically')
|
help='start the background cron process')
|
||||||
|
|
||||||
parser.add_option('-J',
|
parser.add_option('-J',
|
||||||
'--cronjob',
|
'--cronjob',
|
||||||
@@ -904,7 +904,7 @@ def console():
|
|||||||
|
|
||||||
if options.cronjob:
|
if options.cronjob:
|
||||||
global_settings.cronjob = True # tell the world
|
global_settings.cronjob = True # tell the world
|
||||||
options.nocron = True # don't start cron jobs
|
options.run = False # don't start cron jobs
|
||||||
options.plain = True # cronjobs use a plain shell
|
options.plain = True # cronjobs use a plain shell
|
||||||
options.nobanner = True
|
options.nobanner = True
|
||||||
options.nogui = True
|
options.nogui = True
|
||||||
@@ -955,6 +955,19 @@ def check_existent_app(options,appname):
|
|||||||
if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
|
if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_code_for_scheduler(app, options):
|
||||||
|
if len(app) == 1 or app[1] == None:
|
||||||
|
code = "from gluon import current;current._scheduler.loop()"
|
||||||
|
else:
|
||||||
|
code = "from gluon import current;current._scheduler.group_names = ['%s'];"
|
||||||
|
code += "current._scheduler.loop()"
|
||||||
|
code = code % ("','".join(app[1:]))
|
||||||
|
app_ = app[0]
|
||||||
|
if not check_existent_app(options, app_):
|
||||||
|
print "Application '%s' doesn't exist, skipping" % (app_)
|
||||||
|
return None, None
|
||||||
|
return app_, code
|
||||||
|
|
||||||
def start_schedulers(options):
|
def start_schedulers(options):
|
||||||
try:
|
try:
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
@@ -965,20 +978,21 @@ def start_schedulers(options):
|
|||||||
apps = [(app.strip(), None) for app in options.scheduler.split(',')]
|
apps = [(app.strip(), None) for app in options.scheduler.split(',')]
|
||||||
if options.scheduler_groups:
|
if options.scheduler_groups:
|
||||||
apps = options.scheduler_groups
|
apps = options.scheduler_groups
|
||||||
|
code = "from gluon import current;current._scheduler.loop()"
|
||||||
|
logging.getLogger().setLevel(options.debuglevel)
|
||||||
|
if len(apps) == 1 and not options.with_scheduler:
|
||||||
|
app_, code = get_code_for_scheduler(apps[0], options)
|
||||||
|
if not app_:
|
||||||
|
return
|
||||||
|
print 'starting single-scheduler for "%s"...' % app_
|
||||||
|
run(app_,True,True,None,False,code)
|
||||||
|
return
|
||||||
for app in apps:
|
for app in apps:
|
||||||
if len(app) == 1 or app[1] == None:
|
app_, code = get_code_for_scheduler(app, options)
|
||||||
code = "from gluon import current;current._scheduler.loop()"
|
if not app_:
|
||||||
else:
|
|
||||||
code = "from gluon import current;current._scheduler.group_names = ['%s'];"
|
|
||||||
code += "current._scheduler.loop()"
|
|
||||||
code = code % ("','".join(app[1:]))
|
|
||||||
app_ = app[0]
|
|
||||||
if not check_existent_app(options, app_):
|
|
||||||
print "Application '%s' doesn't exist, skipping" % (app_)
|
|
||||||
continue
|
continue
|
||||||
print 'starting scheduler for "%s"...' % app_
|
print 'starting scheduler for "%s"...' % app_
|
||||||
args = (app_,True,True,None,False,code)
|
args = (app_,True,True,None,False,code)
|
||||||
logging.getLogger().setLevel(options.debuglevel)
|
|
||||||
p = Process(target=run, args=args)
|
p = Process(target=run, args=args)
|
||||||
processes.append(p)
|
processes.append(p)
|
||||||
print "Currently running %s scheduler processes" % (len(processes))
|
print "Currently running %s scheduler processes" % (len(processes))
|
||||||
@@ -1069,13 +1083,13 @@ def start(cron=True):
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
# ## if -N or not cron disable cron in this *process*
|
# ## if -H cron is enabled in this *process*
|
||||||
# ## if --softcron use softcron
|
# ## if --softcron use softcron
|
||||||
# ## use hardcron in all other cases
|
# ## use hardcron in all other cases
|
||||||
if cron and not options.nocron and options.softcron:
|
if cron and options.runcron and options.softcron:
|
||||||
print 'Using softcron (but this is not very efficient)'
|
print 'Using softcron (but this is not very efficient)'
|
||||||
global_settings.web2py_crontype = 'soft'
|
global_settings.web2py_crontype = 'soft'
|
||||||
elif cron and not options.nocron:
|
elif cron and options.runcron:
|
||||||
logger.debug('Starting hardcron...')
|
logger.debug('Starting hardcron...')
|
||||||
global_settings.web2py_crontype = 'hard'
|
global_settings.web2py_crontype = 'hard'
|
||||||
newcron.hardcron(options.folder).start()
|
newcron.hardcron(options.folder).start()
|
||||||
|
|||||||
+2
-2
@@ -94,8 +94,6 @@ class Web2pyService(Service):
|
|||||||
os.chdir(dir)
|
os.chdir(dir)
|
||||||
from gluon.settings import global_settings
|
from gluon.settings import global_settings
|
||||||
global_settings.gluon_parent = dir
|
global_settings.gluon_parent = dir
|
||||||
from gluon.custom_import import custom_import_install
|
|
||||||
custom_import_install(dir)
|
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
self.log("Can't change to web2py working path; server is stopped")
|
self.log("Can't change to web2py working path; server is stopped")
|
||||||
@@ -154,6 +152,8 @@ class Web2pyService(Service):
|
|||||||
def web2py_windows_service_handler(argv=None, opt_file='options'):
|
def web2py_windows_service_handler(argv=None, opt_file='options'):
|
||||||
path = os.path.dirname(__file__)
|
path = os.path.dirname(__file__)
|
||||||
web2py_path = up(path)
|
web2py_path = up(path)
|
||||||
|
if web2py_path.endswith('.zip'): # in case bianry distro 'library.zip'
|
||||||
|
web2py_path = os.path.dirname(web2py_path)
|
||||||
os.chdir(web2py_path)
|
os.chdir(web2py_path)
|
||||||
classstring = os.path.normpath(
|
classstring = os.path.normpath(
|
||||||
os.path.join(web2py_path,'gluon.winservice.Web2pyService'))
|
os.path.join(web2py_path,'gluon.winservice.Web2pyService'))
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ propagate=0
|
|||||||
# welcome app handler
|
# welcome app handler
|
||||||
[logger_welcome]
|
[logger_welcome]
|
||||||
level=WARNING
|
level=WARNING
|
||||||
qualname=web2py.app.welcome,rotatingFileHandler
|
qualname=web2py.app.welcome
|
||||||
handlers=consoleHandler
|
handlers=consoleHandler,rotatingFileHandler
|
||||||
propagate=0
|
propagate=0
|
||||||
|
|
||||||
# loggers for legacy getLogger calls: Rocket and markdown
|
# loggers for legacy getLogger calls: Rocket and markdown
|
||||||
|
|||||||
+6
-4
@@ -35,11 +35,13 @@ BASE = '' # optonal prefix for incoming URLs
|
|||||||
|
|
||||||
routes_in = (
|
routes_in = (
|
||||||
# do not reroute admin unless you want to disable it
|
# do not reroute admin unless you want to disable it
|
||||||
(BASE+'/admin/?$anything','/admin/$anything'),
|
(BASE+'/admin','/admin/default/index'),
|
||||||
|
(BASE+'/admin/$anything','/admin/$anything'),
|
||||||
# do not reroute appadmin unless you want to disable it
|
# do not reroute appadmin unless you want to disable it
|
||||||
(BASE+'/$app/appadmin/?$anything','/$app/appadmin/$anything'),
|
(BASE+'/$app/appadmin','/$app/appadmin/index'),
|
||||||
|
(BASE+'/$app/appadmin/$anything','/$app/appadmin/$anything'),
|
||||||
# do not reroute static files
|
# do not reroute static files
|
||||||
(BASE+'/$app/static/?$anything','/$app/static/$anything'),
|
(BASE+'/$app/static/$anything','/$app/static/$anything'),
|
||||||
# reroute favicon and robots, use exable for lack of better choice
|
# reroute favicon and robots, use exable for lack of better choice
|
||||||
('/favicon.ico', '/examples/static/favicon.ico'),
|
('/favicon.ico', '/examples/static/favicon.ico'),
|
||||||
('/robots.txt', '/examples/static/robots.txt'),
|
('/robots.txt', '/examples/static/robots.txt'),
|
||||||
@@ -55,7 +57,7 @@ routes_in = (
|
|||||||
|
|
||||||
routes_out = (
|
routes_out = (
|
||||||
# do not reroute admin unless you want to disable it
|
# do not reroute admin unless you want to disable it
|
||||||
('/admin/$anything', BASE+'/admin/?$anything'),
|
('/admin/$anything', BASE+'/admin/$anything'),
|
||||||
# do not reroute appadmin unless you want to disable it
|
# do not reroute appadmin unless you want to disable it
|
||||||
('/$app/appadmin/$anything',BASE+'/$app/appadmin/$anything'),
|
('/$app/appadmin/$anything',BASE+'/$app/appadmin/$anything'),
|
||||||
# do not reroute static files
|
# do not reroute static files
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
paths = [sys.argv[1]]
|
|
||||||
paths1 = []
|
|
||||||
paths2 = []
|
|
||||||
while paths:
|
|
||||||
path = paths.pop()
|
|
||||||
for filename in os.listdir(path):
|
|
||||||
fullname = os.path.join(path,filename)
|
|
||||||
if os.path.isdir(fullname):
|
|
||||||
paths.append(fullname)
|
|
||||||
else:
|
|
||||||
extension = filename.split('.')[-1]
|
|
||||||
if extension.lower() in ('png','gif','jpg','jpeg','js','css'):
|
|
||||||
paths1.append((filename,fullname))
|
|
||||||
if extension.lower() in ('css','js','py','html'):
|
|
||||||
paths2.append(fullname)
|
|
||||||
for filename,fullname in paths1:
|
|
||||||
for otherfullname in paths2:
|
|
||||||
if open(otherfullname).read().find(filename)>=0:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print fullname
|
|
||||||
# os.system('hg rm '+fullname)
|
|
||||||
# os.system('rm '+fullname)
|
|
||||||
@@ -23,7 +23,7 @@ applications/welcome/controllers/default.py
|
|||||||
|
|
||||||
# files and folders to exclude from gluon folder (comment with # if needed)
|
# files and folders to exclude from gluon folder (comment with # if needed)
|
||||||
IGNORED = """
|
IGNORED = """
|
||||||
gluon/contrib/comet_messaging.py
|
gluon/contrib/websocket_messaging.py
|
||||||
gluon/contrib/feedparser.py
|
gluon/contrib/feedparser.py
|
||||||
gluon/contrib/generics.py
|
gluon/contrib/generics.py
|
||||||
gluon/contrib/gql.py
|
gluon/contrib/gql.py
|
||||||
|
|||||||
@@ -94,11 +94,11 @@ class SessionSetDb(SessionSet):
|
|||||||
"""Return list of SessionDb instances for existing sessions."""
|
"""Return list of SessionDb instances for existing sessions."""
|
||||||
sessions = []
|
sessions = []
|
||||||
tablename = 'web2py_session'
|
tablename = 'web2py_session'
|
||||||
if request.application:
|
from gluon import current
|
||||||
tablename = 'web2py_session_' + request.application
|
(record_id_name, table, record_id, unique_key) = \
|
||||||
if tablename in db:
|
current.response._dbtable_and_field
|
||||||
for row in db(db[tablename].id > 0).select():
|
for row in table._db(table.id > 0).select():
|
||||||
sessions.append(SessionDb(row))
|
sessions.append(SessionDb(row))
|
||||||
return sessions
|
return sessions
|
||||||
|
|
||||||
|
|
||||||
@@ -121,8 +121,11 @@ class SessionDb(object):
|
|||||||
self.row = row
|
self.row = row
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
|
from gluon import current
|
||||||
|
(record_id_name, table, record_id, unique_key) = \
|
||||||
|
current.response._dbtable_and_field
|
||||||
self.row.delete_record()
|
self.row.delete_record()
|
||||||
db.commit()
|
table._db.commit()
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
session = Storage()
|
session = Storage()
|
||||||
@@ -130,7 +133,13 @@ class SessionDb(object):
|
|||||||
return session
|
return session
|
||||||
|
|
||||||
def last_visit_default(self):
|
def last_visit_default(self):
|
||||||
return self.row.modified_datetime
|
if isinstance(self.row.modified_datetime, datetime.datetime):
|
||||||
|
return self.row.modified_datetime
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return datetime.datetime.strptime(self.row.modified_datetime, '%Y-%m-%d %H:%M:%S.%f')
|
||||||
|
except:
|
||||||
|
print 'failed to retrieve last modified time (value: %s)' % self.row.modified_datetime
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.row.unique_key
|
return self.row.unique_key
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user