Compare commits

...

96 Commits

Author SHA1 Message Date
Massimo b9d80fcdc7 R-2.4.5 2013-03-18 17:37:49 -05:00
Massimo 3f7d085f73 fixed timezone and textarea default width 2013-03-18 17:34:06 -05:00
Massimo d57dd72780 upgraded fpdf, better tests, passes travis.ci tests, thanks Niphlod 2013-03-18 16:59:57 -05:00
Massimo a8d1d5cfcf upgraded fpdf, better tests, passes travis.ci tests, thanks Niphlod 2013-03-18 16:52:47 -05:00
mdipierro 8857e3d521 IS_DATE/DATETIME/etc. (timezone=user_timezone in hrs) 2013-03-17 18:53:19 -05:00
mdipierro 3b1a5be1be fixed (possibly) truncate for GAE 2013-03-17 15:27:47 -05:00
mdipierro c9a63a8524 fixed issue 1393, cast_keys in dict for python 2.5 support, thanks Alan and Niphlod 2013-03-17 12:11:10 -05:00
mdipierro cd967d2551 new travis.ci 2013-03-16 20:53:24 -05:00
mdipierro 92b5247f9f typo Fir(e)bird, thanks Jonathan 2013-03-15 21:27:52 -05:00
mdipierro 45a5b436c8 patched fpdf for 2.5, thanks Niphlod 2013-03-15 15:28:50 -05:00
mdipierro bfd385f969 travis badge in README, thanks niphlod 2013-03-15 15:12:01 -05:00
mdipierro f693fe6b2a added .travis.yml 2013-03-15 14:55:36 -05:00
mdipierro b040159a9b fixed issue 1385, thanks Alan 2013-03-15 14:14:40 -05:00
mdipierro f3af2a1999 fixed issue 1382, sanitizer accepts mailto, thanks lightdot 2013-03-15 10:17:33 -05:00
mdipierro f613a4cc99 wiki.settings, thanks Alan 2013-03-15 10:14:33 -05:00
mdipierro e4a96125a6 fixed issue 1390, dateTtime separator in MSSQL, thanks score2000 2013-03-15 10:05:52 -05:00
mdipierro e48074ff54 fixed issue 1391, sub for decimal 2013-03-15 10:00:45 -05:00
mdipierro 2ed122a534 fixed typo, thank you Philipp Storz 2013-03-15 09:49:46 -05:00
mdipierro 65c0d9b18b italian patch 2013-03-15 09:46:55 -05:00
mdipierro 6702694590 fixed slidetoggle boxes in new admin, thanks Annet and Niphlod 2013-03-15 09:44:22 -05:00
mdipierro 6f0d4d039e fixed upload default values, thanks Marin Pranjic 2013-03-15 09:38:47 -05:00
mdipierro 1325b0e48f Merge pull request #65 from michele-comitini/master
HTTP exceptions in jsonrpc2
2013-03-15 07:25:55 -07:00
Michele Comitini 7421eb8068 avoid treating HTTP redirections or status codes generated in jsonrpc2 functions as error exceptions 2013-03-14 21:54:38 +01:00
Michele Comitini 6c7a9a4030 avoid treating HTTP redirections or status codes generated in jsonrpc2 functions as error exceptions 2013-03-14 21:49:05 +01:00
Massimo ba0a143717 scheduler patch allows termination of tasks, thanks niphlod 2013-03-12 10:40:57 -05:00
mdipierro 28bcb5ed6c added sessions in cookie for examples 2013-03-11 18:12:01 -05:00
mdipierro 44fd637a1f R-2.4.4 2013-03-11 15:26:36 -05:00
mdipierro f13aed1a84 R-2.4.3 2013-03-11 14:41:10 -05:00
mdipierro cb3de825f2 /examples/default/version/raw 2013-03-11 14:38:04 -05:00
mdipierro fd4c775710 split->partition, thanks Ricardo 2013-03-11 14:28:48 -05:00
mdipierro 3e550b3e97 Merge branch 'master' of github.com:web2py/web2py 2013-03-11 14:25:01 -05:00
mdipierro 7a543250f9 Merge pull request #64 from michele-comitini/master
uwsgi doen't like unicode headers
2013-03-11 12:24:38 -07:00
mdipierro a071f07634 fixed issue with version 2013-03-11 14:22:40 -05:00
Michele Comitini a078f860d2 basic auth header changed to string from unicode 2013-03-11 18:42:00 +01:00
mdipierro 2bfd7a2467 fixed issue 1327, breadcrumbs in smartgrid 2013-03-11 11:10:45 -05:00
mdipierro 55c9392de4 fixed ccache, issue 1377 2013-03-11 10:22:40 -05:00
mdipierro 7991b555ff fixed some style issues 2013-03-11 10:01:03 -05:00
mdipierro eab7815c24 Merge pull request #63 from michele-comitini/master
rfc basic auth challenge
2013-03-11 07:46:29 -07:00
mdipierro 79c09de103 fixed download bug 2013-03-11 09:45:20 -05:00
mdipierro 24daa335b6 examples uses cookie based sessions 2013-03-11 08:50:01 -05:00
Michele Comitini 812ba9d52b docstring improved 2013-03-11 14:40:34 +01:00
mdipierro 00fb3b56cc fixed some links 2013-03-11 08:25:44 -05:00
Michele Comitini 3644d081f6 rfc compliant basic auth 2013-03-11 13:13:08 +01:00
mdipierro f05c46b3f1 partially fixed alignment in examples 2013-03-11 01:02:31 -05:00
mdipierro 13e76fe278 removed sitemap 2013-03-11 00:58:14 -05:00
mdipierro 0906ae28c1 no more plugin wiki 2013-03-10 22:12:46 -05:00
mdipierro 04905adb89 new examples based on bootstrap. no more skeleton.css 2013-03-10 21:58:50 -05:00
mdipierro 9767109d4e changed retrieve again 2013-03-10 17:40:36 -05:00
mdipierro a57919bc4d share widget 2013-03-10 17:08:43 -05:00
mdipierro c7c94d1f8f added image 2013-03-10 17:04:53 -05:00
mdipierro 817e6c3313 changed header of example... should move examples to bootstrap 2013-03-10 16:55:47 -05:00
mdipierro a23f4c5c88 Merge branch 'master' of github.com:web2py/web2py 2013-03-10 15:58:24 -05:00
mdipierro eac9768549 Merge pull request #62 from michele-comitini/master
jsonrpc-2.0
2013-03-10 13:58:18 -07:00
mdipierro a42fb6b558 Merge branch 'master' of github.com:web2py/web2py 2013-03-10 15:57:17 -05:00
mdipierro 879e825b61 Merge pull request #61 from clach04/master
Improved Ingres support, no longer needs IngresDBI, uses pyodbc instead
2013-03-10 13:56:55 -07:00
mdipierro 07f4310153 questions in examples 2013-03-10 15:45:15 -05:00
mdipierro 10c67e5be3 google group, no chat 2013-03-10 13:05:58 -05:00
mdipierro 1279d5ddaa added codemirror link, thanks Niphlod 2013-03-10 12:55:22 -05:00
mdipierro 2af15e4b27 fixed help link again 2013-03-10 12:51:09 -05:00
mdipierro 3096fcf045 chat in admin 2013-03-10 12:48:04 -05:00
mdipierro 831492448a a2enmod wsgi in ubuntu setup script, thanks badweather.FBI 2013-03-10 11:38:36 -05:00
mdipierro a5284e846c DIV(...,data=dict(action='whatever') 2013-03-10 11:27:13 -05:00
mdipierro 22accaced3 cache.client 2013-03-10 11:14:30 -05:00
mdipierro f92d97f36e fixed bug in encode/decode, thanks Jonathan 2013-03-10 11:06:20 -05:00
mdipierro 4f0a16a24c fixed flash 2013-03-10 11:04:43 -05:00
mdipierro 72dade793a attempt to support unicode in IT_MATCH 2013-03-10 10:45:29 -05:00
mdipierro 8addc54015 better cron stopping, thanks Álvaro José Iradier 2013-03-10 10:21:47 -05:00
mdipierro 25ab6f9a07 better twitter feed display 2013-03-10 09:10:30 -05:00
mdipierro b853f72e0c possibly fixed size spike in uploads in grid, thanks Ricardo 2013-03-10 08:25:54 -05:00
mdipierro 449a658521 fixed GAE to better support ustom_qualifier, thanks cfhowes 2013-03-10 08:19:28 -05:00
mdipierro 7776a22533 fixed Ingres constr typo, thanks Chris Clark 2013-03-10 07:54:44 -05:00
Michele Comitini 554eccc14a fixed request missing current prefix causing wrongly handled exceptions. 2013-03-09 21:29:06 +01:00
Michele Comitini 8cd6928da3 Revert "fixed request missing current prefix causing wrongly handled exceptions."
This reverts commit 8c28be1041.
Removed NEWINSTALL by error.
2013-03-09 21:21:26 +01:00
Michele Comitini 8c28be1041 fixed request missing current prefix causing wrongly handled exceptions. 2013-03-09 21:16:58 +01:00
mdipierro 810aa8f4f3 noconfirm in grid, thanks Niphlod 2013-03-09 09:36:17 -06:00
mdipierro 8e5be952bf fixed alignment in admin tooltips, thanks Paolo 2013-03-09 09:34:27 -06:00
mdipierro 5a560fee8a cleaned up examples/controller/global.py 2013-03-09 00:17:56 -06:00
clach04 21819ffc5d Remove dependency on IngresDBI, use pyodbc instead for Ingres/Vectorwise support. 2013-03-08 18:09:41 -08:00
mdipierro 1f8bdd6061 fixed search in design 2013-03-08 19:22:51 -06:00
Massimo 523c813439 fixed some layout problems 2013-03-08 16:30:11 -06:00
mdipierro 7ee638bc36 Merge pull request #60 from michele-comitini/master
oauth 2 fixes
2013-03-08 10:13:17 -08:00
mdipierro 76c515d3af Merge pull request #59 from pyner/patch-1
Update gluon/globals.py
2013-03-08 10:11:50 -08:00
Massimo 1c8ce00657 reverted recent change to retrieve, thanks Anthony for pointing out why the original correct is better than the 'fixed' one 2013-03-08 12:06:05 -06:00
Michele Comitini 0f4bbecd2a Merge with upstream 2013-03-08 18:36:52 +01:00
Michele Comitini e292f5a869 removed http_x_forwarded for until a better solution is found. 2013-03-08 18:36:06 +01:00
Massimo da25c6f843 fixed CONTAINS (again) 2013-03-08 10:19:13 -06:00
Massimo 5122fbe110 fixed issue 1366, minor rewrite in retrieve, thanks Dominic 2013-03-08 10:16:48 -06:00
Massimo dee3a3b655 fixed issue 1358, himBH>jQuery, thanks FeDjango 2013-03-08 10:06:03 -06:00
mdipierro 4e2bcd22d9 added cheatsheet 2013-03-06 12:37:44 -06:00
mdipierro 17890027a6 fixed issue 1357, date/datetime serialization in as_dict, thanks Alan 2013-03-06 12:31:39 -06:00
mdipierro 5e93804525 fixed issue 1359, typo in dal custom qualifier, thanks Vincenzo 2013-03-06 12:28:48 -06:00
mdipierro 778070dd8a fixed issue 1361, typo in contains, thanks Villas 2013-03-06 12:27:06 -06:00
mdipierro 74ecc762f3 fixed issue 1363, failsafe when languages folder does not exist, thanks Roberto 2013-03-06 12:24:30 -06:00
mdipierro 63f7b35cf8 fixed issue 1364, unicode in emails, thanks Corne 2013-03-06 12:21:22 -06:00
Massimo 0c3f4601cc 5th edition book link 2013-03-05 14:29:54 -06:00
pyner 101b74398f Update gluon/globals.py
typescript support (http://www.typescriptlang.org/)
2013-02-18 14:36:12 -05:00
90 changed files with 2658 additions and 3324 deletions
+25
View File
@@ -0,0 +1,25 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
env:
- DB=sqlite:memory
- DB=mysql://root:@localhost/test_w2p
- DB=postgres://postgres:@localhost/test_w2p
before_script:
- pip install unittest2
- if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
#Temporal solution to travis issue #155
- sudo chmod 777 /dev/shm
- sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
email: false
irc:
channels: "irc.freenode.org#web2py"
+6 -1
View File
@@ -1,4 +1,9 @@
## 2.4.1- 2.4.2
## 2.4.5
- travis.ci integration (thanks Marc Abramowitz and Niphlod). Passes all tests (thanks Niplod).
- IS_DATE and IS_DATETIME can specify timezone
## 2.4.1- 2.4.3
- 2D GEO API: geoPoint, getLine, geoPolygon
- support for 'json' field type in DAL
+1 -1
View File
@@ -3,7 +3,7 @@
Web2py is Licensed under the LGPL license version 3
(http://www.gnu.org/licenses/lgpl.html)
Copyrighted (c) by Massimo Di Pierro (2007-2011)
Copyrighted (c) by Massimo Di Pierro (2007-2013)
### On Commercial Redistribution
+2 -2
View File
@@ -5,7 +5,7 @@ all:
clean:
rm -f httpserver.log
rm -f parameters*.py
rm -f -r applications/*/compiled
rm -f -r applications/*/compiled
find ./ -name '*~' -exec rm -f {} \;
find ./ -name '*.orig' -exec rm -f {} \;
find ./ -name '*.rej' -exec rm -f {} \;
@@ -30,7 +30,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.4.2-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.4.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
### clean up baisc apps
+5
View File
@@ -6,6 +6,11 @@ It is written and programmable in Python. LGPLv3 License
Learn more at http://web2py.com
## Tests
[![Build Status](https://travis-ci.org/web2py/web2py.png)](https://travis-ci.org/web2py/web2py)
## Installation Instructions
To start web2py there is NO NEED to install it. Just unzip and do:
+1 -1
View File
@@ -1 +1 @@
Version 2.4.2-stable+timestamp.2013.03.03.21.22.42
Version 2.4.5-stable+timestamp.2013.03.18.17.37.19
+4 -1
View File
@@ -319,6 +319,9 @@ def state():
def ccache():
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
@@ -385,7 +388,7 @@ def ccache():
return (hours, minutes, seconds)
for key, value in cache.ram.storage.items():
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
+5 -9
View File
@@ -148,9 +148,7 @@ def check_version():
elif platform.system().lower() in ('windows', 'win32', 'win64') and os.path.exists("web2py.exe"):
return SPAN('You should upgrade to version %s.%s.%s' % version_number[:3])
else:
return sp_button(URL('upgrade_web2py'), T('upgrade now')) \
+ XML(' <strong class="upgrade_version">%s.%s.%s</strong>'
% version_number[:3])
return sp_button(URL('upgrade_web2py'), T('upgrade now to %s') % version_number.split('-')[0])
def logout():
@@ -205,14 +203,12 @@ def site():
is_appname = IS_VALID_APPNAME()
form_create = SQLFORM.factory(Field('name', requires=is_appname),
table_name='appcreate',
_class='well well-small')
table_name='appcreate')
form_update = SQLFORM.factory(Field('name', requires=is_appname),
Field('file', 'upload', uploadfield=False),
Field('url'),
Field('overwrite', 'boolean'),
table_name='appupdate',
_class='well well-small')
table_name='appupdate')
form_create.process()
form_update.process()
@@ -1430,7 +1426,7 @@ def errors():
hash2error[hash]['count'] += 1
except KeyError:
error_lines = error['traceback'].split("\n")
last_line = error_lines[-2]
last_line = error_lines[-2] if len(error_lines)>1 else 'unknown'
error_causer = os.path.split(error['layer'])[1]
hash2error[hash] = dict(count=1, pickel=error,
causer=error_causer,
@@ -1667,7 +1663,7 @@ def twitter():
d = dict()
for e in data:
d[e["id"]] = e
r = reversed(sorted(d))
r = reversed(sorted(d))
return dict(tweets=[d[k] for k in r])
else:
return 'disabled'
+2 -2
View File
@@ -23,9 +23,9 @@ if request.vars.app or request.args:
if os.path.exists('applications/examples'):
response.menu.append(
(T('Help'), False, URL('examples', 'default', 'index')))
(T('Help'), False, URL('examples', 'default', 'documentation')))
else:
response.menu.append((T('Help'), False, 'http://web2py.com/examples'))
response.menu.append((T('Help'), False, 'http://web2py.com/examples/default/documentation'))
if not session.authorized:
response.menu = [(T('Login'), True, URL('site'))]
File diff suppressed because it is too large Load Diff
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/
/* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

+376 -367
View File
@@ -1,69 +1,69 @@
{{extend 'layout.html'}}
{{
def all(items):
return reduce(lambda a,b:a and b,items,True)
return reduce(lambda a,b:a and b,items,True)
def peekfile(path,file,vars={},title=None):
args=(path,file) if 'app' in vars else (app,path,file)
return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars))
args=(path,file) if 'app' in vars else (app,path,file)
return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars))
def editfile(path,file,vars={}):
args=(path,file) if 'app' in vars else (app,path,file)
return A(SPAN(T('Edit')),_class='button editbutton btn',_href=URL('edit', args=args, vars=vars))
args=(path,file) if 'app' in vars else (app,path,file)
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit', args=args, vars=vars))
def testfile(path,file):
return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('test')),
SPAN(T("Run tests in this file (to run all files, you may also use the button labelled 'test')"))),
_class='icon test',
_href=URL('test', args=(app, file)),
_rel="tooltip",
**{'_data-placement':'right',
'_data-original-title':T("Run tests in this file (to run all files, you may also use the button labelled 'test')")})
return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('test')),
SPAN(T("Run tests in this file (to run all files, you may also use the button labelled 'test')"))),
_class='icon test',
_href=URL('test', args=(app, file)),
_rel="tooltip",
**{'_data-placement':'right',
'_data-original-title':T("Run tests in this file (to run all files, you may also use the button labelled 'test')")})
def editlanguagefile(path,file,vars={}):
return A(SPAN(T('Edit')),_class='button editbutton btn',_href=URL('edit_language', args=(app, path, file), vars=vars))
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_language', args=(app, path, file), vars=vars))
def editpluralsfile(path,file,vars={}):
return A(SPAN(T('Edit')),_class='button editbutton btn',_href=URL('edit_plurals', args=(app, path, file), vars=vars))
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_plurals', args=(app, path, file), vars=vars))
def file_upload_form(location, anchor=None):
form=FORM(
LABEL(T("upload file:")),
INPUT(_type="file",_name="file"),
LABEL(T("and rename it:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)),
_action=URL('upload_file'),
_class="generatedbyw2p well well-small")
return form
form=FORM(
LABEL(T("upload file:")),
INPUT(_type="file",_name="file"),
LABEL(T("and rename it:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)),
_action=URL('upload_file'),
_class="generatedbyw2p well well-small")
return form
def file_create_form(location, anchor=None, helptext=""):
form=FORM(
LABEL(T("create file with filename:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''),
TAG['SMALL'](helptext,_class="help-block"),
DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="id",_value=anchor),
_action=URL('create_file'),
_class="generatedbyw2p well well-small")
return form
form=FORM(
LABEL(T("create file with filename:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''),
TAG['SMALL'](helptext,_class="help-block"),
DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="id",_value=anchor),
_action=URL('create_file'),
_class="generatedbyw2p well well-small")
return form
def upload_plugin_form(app, anchor=None):
form=FORM(
LABEL(T("upload plugin file:")),
INPUT(_type="file",_name="pluginfile"),
INPUT(_type="hidden",_name="id",_value=anchor),
INPUT(_type="hidden",_name="token",_value=session.token),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
_class="generatedbyw2p well well-small")
return form
form=FORM(
LABEL(T("upload plugin file:")),
INPUT(_type="file",_name="pluginfile"),
INPUT(_type="hidden",_name="id",_value=anchor),
INPUT(_type="hidden",_name="token",_value=session.token),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
_class="generatedbyw2p well well-small")
return form
def deletefile(arglist, vars={}):
vars.update({'sender':request.function+'/'+app})
return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')),
SPAN(T('Delete this file (you will be asked to confirm deletion)'))),
_href=URL('delete',args=arglist,vars=vars),
_class='icon delete',
_rel="tooltip",
**{'_data-placement':'right',
'_data-original-title':T('Delete this file (you will be asked to confirm deletion)')})
vars.update({'sender':request.function+'/'+app})
return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')),
SPAN(T('Delete this file (you will be asked to confirm deletion)'))),
_href=URL('delete',args=arglist,vars=vars),
_class='icon delete',
_rel="tooltip",
**{'_data-placement':'right',
'_data-original-title':T('Delete this file (you will be asked to confirm deletion)')})
}}
{{block sectionclass}}design{{end}}
@@ -91,44 +91,45 @@ def deletefile(arglist, vars={}):
<!-- MODELS -->
<h3 id="_models" rel="pagebookmark">
<span class="component" onclick="collapse('models_inner');">{{=T("Models")}}</span>
<a href="#models" rel="tooltip" data-placement="right" data-original-title="{{=T('The data representation, define database tables and sets')}}">
{{=helpicon()}}
<span>{{=T("The data representation, define database tables and sets")}}</span>
</a><span id="models" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('models_inner');">{{=T("Models")}}</span>
<a href="#models" rel="tooltip" data-placement="right" data-original-title="{{=T('The data representation, define database tables and sets')}}">
{{=helpicon()}}
<span>{{=T("The data representation, define database tables and sets")}}</span>
</a><span id="models" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="models_inner" class="component_contents">
{{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL(a=app,c='appadmin',f='index'), T('database administration'))}}
{{if os.access(os.path.join(request.folder,'..',app,'databases','sql.log'),os.R_OK):}}
{{=button(URL('peek/%s/databases/sql.log'%app), 'sql.log')}}
{{pass}}
{{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL(a=app,c='appadmin',f='index'), T('database administration'))}}
{{if os.access(os.path.join(request.folder,'..',app,'databases','sql.log'),os.R_OK):}}
{{=button(URL('peek/%s/databases/sql.log'%app), 'sql.log')}}
{{pass}}
{{=button(URL(a=app, c='appadmin',f='graph_model'), T('graph model'))}}
</div>
<ul class="unstyled act_edit">
{{for m in models:}}
{{id="models__"+m.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('models',m, dict(id=id))}}
{{=deletefile([app, 'models', m], dict(id=id, id2='models'))}}
</span>
<span class="file">
{{=peekfile('models',m, dict(id=id))}}
</span>
<span class="extras">
{{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div>
</div>
</div>
</div>
<ul class="unstyled act_edit">
{{for m in models:}}
{{id="models__"+m.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('models',m, dict(id=id))}}
{{=deletefile([app, 'models', m], dict(id=id, id2='models'))}}
</span>
<span class="file">
{{=peekfile('models',m, dict(id=id))}}
</span>
<span class="extras">
{{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form1').slideToggle()" class="btn btn-mini">Create</button>
<div id="form1" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div>
</div>
</div>
</div>
<!-- FIND CONTROLLER FUNCTIONS -->
@@ -139,322 +140,330 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
<!-- CONTROLLERS -->
<h3 id="_controllers" rel="pagebookmark">
<span class="component" onclick="collapse('controllers_inner');">{{=T("Controllers")}}</span>
<a href="#controllers" rel="tooltip" data-placement="right" data-original-title="{{=T('The application logic, each URL path is mapped in one exposed function in the controller')}}">
{{=helpicon()}}
<span>{{=T("The application logic, each URL path is mapped in one exposed function in the controller")}}</span>
</a><span id="controllers" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('controllers_inner');">{{=T("Controllers")}}</span>
<a href="#controllers" rel="tooltip" data-placement="right" data-original-title="{{=T('The application logic, each URL path is mapped in one exposed function in the controller')}}">
{{=helpicon()}}
<span>{{=T("The application logic, each URL path is mapped in one exposed function in the controller")}}</span>
</a><span id="controllers" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="controllers_inner" class="component_contents">
{{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}}
{{=button(URL('test',args=app), T("test"))}}
{{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}}
</div>
<ul class="unstyled act_edit">
{{for c in controllers:}}
{{id="controllers__"+c.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('controllers',c, dict(id=id))}}
{{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}}
{{=testfile('controllers',c)}}
</span>
<span class="file">
{{=peekfile('controllers',c, dict(id=id))}}
</span>
<span class="extras celled">
{{if functions[c]:}}{{=T("exposes")}}{{pass}} {{=XML(', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div>
</div>
</div>
{{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}}
{{=button(URL('test',args=app), T("test"))}}
{{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}}
</div>
<ul class="unstyled act_edit">
{{for c in controllers:}}
{{id="controllers__"+c.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('controllers',c, dict(id=id))}}
{{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}}
{{=testfile('controllers',c)}}
</span>
<span class="file">
{{=peekfile('controllers',c, dict(id=id))}}
</span>
<span class="extras celled">
{{if functions[c]:}}{{=T("exposes")}}{{pass}} {{=XML(', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form2').slideToggle()" class="btn btn-mini">Create</button>
<div id="form2" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div>
</div>
</div>
</div>
<!-- VIEWS -->
<h3 id="_views" rel="pagebookmark">
<span class="component" onclick="collapse('views_inner');">{{=T("Views")}}</span>
<a href="#views" rel="tooltip" data-placement="right" data-original-title="{{=T('The presentations layer, views are also known as templates')}}">
{{=helpicon()}}
<span>{{=T("The presentations layer, views are also known as templates")}}</span>
</a><span id="views" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('views_inner');">{{=T("Views")}}</span>
<a href="#views" rel="tooltip" data-placement="right" data-original-title="{{=T('The presentations layer, views are also known as templates')}}">
{{=helpicon()}}
<span>{{=T("The presentations layer, views are also known as templates")}}</span>
</a><span id="views" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="views_inner" class="component_contents">
{{if not views:}}<p><strong>{{=T("There are no views")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(LAYOUTS_APP, T("download layouts"))}}
</div>
<ul class="unstyled act_edit">
{{for c in views:}}
{{id="views__"+c.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('views',c, dict(id=id))}}
{{=deletefile([app, 'views', c], dict(id=id, id2='views'))}}
</span>
<span class="file">
{{=peekfile('views',c, dict(id=id))}}
</span>
<span class="extras celled celled-one">
{{if extend.has_key(c):}}{{=T("extends")}} <b>{{=extend[c]}}</b> {{pass}}
{{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div>
</div>
</div>
<div class="controls comptools">
{{=button(LAYOUTS_APP, T("download layouts"))}}
</div>
<ul class="unstyled act_edit">
{{for c in views:}}
{{id="views__"+c.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls">
{{=editfile('views',c, dict(id=id))}}
{{=deletefile([app, 'views', c], dict(id=id, id2='views'))}}
</span>
<span class="file">
{{=peekfile('views',c, dict(id=id))}}
</span>
<span class="extras celled celled-one">
{{if extend.has_key(c):}}{{=T("extends")}} <b>{{=extend[c]}}</b> {{pass}}
{{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form3').slideToggle()" class="btn btn-mini">Create</button>
<div id="form3" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div>
</div>
</div>
</div>
<!-- LANGUAGES -->
<h3 id="_languages" rel="pagebookmark">
<span class="component" onclick="collapse('languages_inner');">{{=T("Languages")}}</span>
<a href="#languages" rel="tooltip" data-placement="right" data-original-title="{{=T('Translation strings for the application')}}">
{{=helpicon()}}
<span>{{=T("Translation strings for the application")}}</span>
</a><span id="languages" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('languages_inner');">{{=T("Languages")}}</span>
<a href="#languages" rel="tooltip" data-placement="right" data-original-title="{{=T('Translation strings for the application')}}">
{{=helpicon()}}
<span>{{=T("Translation strings for the application")}}</span>
</a><span id="languages" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="languages_inner" class="component_contents">
{{if not languages:}}<p><strong>{{=T("There are no translators, only default language is supported")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL('update_languages/'+app), T('update all languages'))}}
</div>
<ul class="unstyled act_edit">
{{for lang in sorted(languages):
file = lang+'.py'
id = "languages__"+file.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="li-controls">
<span class="filetools controls">
{{=editlanguagefile('languages',file)}}
{{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}}
</span>
<span class="">
{{=peekfile('languages',file, dict(id=id))}}
</span>
</span> <!-- /li-row -->
<span class="extras celled">
(
{{=T("Plural-Forms:")}}
{{p=languages[lang][3:7]}}
{{if p[2] == 'default':}}
<span class='error text-error'>{{=T("rules are not defined")}}</span> {{=T.M("(file **gluon/contrib/plural_rules/%s.py** is not found)",lang[:2])}}
{{else:}}
{{if p[3] == 1:}}
{{=B(T("are not used"))}}
{{else:}}
{{pfile=p[0]}}
{{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;">
<span class="filetools controls">
{{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}}
</span>
<span class="file">
{{=peekfile('languages',pfile,dict(id=id))}}
</span></span>
{{else:}}
<b>{{=T("are not used yet")}}</b>
{{pass}}
{{pass}}
{{pass}}
)
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/languages/' % app, 'languages', T('(something like "it-it")'))}}</div>
</div>
</div>
{{if not languages:}}<p><strong>{{=T("There are no translators, only default language is supported")}}</strong></p>{{else:}}
<div class="controls comptools">
{{=button(URL('update_languages/'+app), T('update all languages'))}}
</div>
<ul class="unstyled act_edit">
{{for lang in sorted(languages):
file = lang+'.py'
id = "languages__"+file.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="li-controls">
<span class="filetools controls">
{{=editlanguagefile('languages',file)}}
{{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}}
</span>
<span class="">
{{=peekfile('languages',file, dict(id=id))}}
</span>
</span> <!-- /li-row -->
<span class="extras celled">
(
{{=T("Plural-Forms:")}}
{{p=languages[lang][3:7]}}
{{if p[2] == 'default':}}
<span class='error text-error'>{{=T("rules are not defined")}}</span> {{=T.M("(file **gluon/contrib/plural_rules/%s.py** is not found)",lang[:2])}}
{{else:}}
{{if p[3] == 1:}}
{{=B(T("are not used"))}}
{{else:}}
{{pfile=p[0]}}
{{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;">
<span class="filetools controls">
{{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}}
</span>
<span class="file">
{{=peekfile('languages',pfile,dict(id=id))}}
</span></span>
{{else:}}
<b>{{=T("are not used yet")}}</b>
{{pass}}
{{pass}}
{{pass}}
)
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form4').slideToggle()" class="btn btn-mini">Create</button>
<div id="form4" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/languages/' % app, 'languages', T('(something like "it-it")'))}}</div>
</div>
</div>
</div>
<!-- STATIC -->
<h3 id="_static" rel="pagebookmark">
<span class="component" onclick="collapse('static_inner');">{{=T("Static")}}</span>
<a href="#static" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are served without processing, your images go here')}}">
{{=helpicon()}}
<span>{{=T("These files are served without processing, your images go here")}}</span>
</a><span id="static" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('static_inner');">{{=T("Static")}}</span>
<a href="#static" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are served without processing, your images go here')}}">
{{=helpicon()}}
<span>{{=T("These files are served without processing, your images go here")}}</span>
</a><span id="static" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="static_inner" class="component_contents">
{{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{
path=[]
for file in statics+['']:
items=file.split('/')
file_path=items[:-1]
filename=items[-1]
while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)])
thispath='static__'+'__'.join(path)
}}
<li class="folder"><i>&nbsp;</i>
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else:
path = path[:-1]
}}
</ul></li>
{{
pass
pass
if filename:
}}
<li>
<span class="filetools controls">
{{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}}
</span>
<span class="file">
<a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a>
</span>
</li>{{
pass
pass
}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/static/' % app, 'static')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div>
</div>
</div>
{{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{
path=[]
for file in statics+['']:
items=file.split('/')
file_path=items[:-1]
filename=items[-1]
while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)])
thispath='static__'+'__'.join(path)
}}
<li class="folder"><i>&nbsp;</i>
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else:
path = path[:-1]
}}
</ul></li>
{{
pass
pass
if filename:
}}
<li>
<span class="filetools controls">
{{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}}
</span>
<span class="file">
<a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a>
</span>
</li>{{
pass
pass
}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form5" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/static/' % app, 'static')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div>
</div>
</div>
</div>
<!-- MODULES -->
<h3 id="_modules" rel="pagebookmark">
<span class="component" onclick="collapse('modules_inner');">{{=T("Modules")}}</span>
<a href="#modules" rel="tooltip" data-placement="right" data-original-title="{{=T('Additional code for your application')}}">
{{=helpicon()}}
<span>{{=T("Additional code for your application")}}</span>
</a><span id="modules" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('modules_inner');">{{=T("Modules")}}</span>
<a href="#modules" rel="tooltip" data-placement="right" data-original-title="{{=T('Additional code for your application')}}">
{{=helpicon()}}
<span>{{=T("Additional code for your application")}}</span>
</a><span id="modules" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="modules_inner" class="component_contents">
{{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{for m in modules:}}
{{id="modules__"+m.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetols controls">
{{=editfile('modules',m,dict(id=id))}}
{{if m!='__init__.py':}}
{{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}}
{{pass}}
</span>
<span class="file">
{{=peekfile('modules',m, dict(id=id))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/modules/' % app, 'modules')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div>
</div>
</div>
{{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{for m in modules:}}
{{id="modules__"+m.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetols controls">
{{=editfile('modules',m,dict(id=id))}}
{{if m!='__init__.py':}}
{{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}}
{{pass}}
</span>
<span class="file">
{{=peekfile('modules',m, dict(id=id))}}
</span>
</li>
{{pass}}
</ul>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form6').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form6" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/modules/' % app, 'modules')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div>
</div>
</div>
</div>
<!-- PRIVATE -->
<h3 id="_private" rel="pagebookmark">
<span class="component" onclick="collapse('private_inner');">{{=T("Private files")}}</span>
<a href="#private" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are not served, they are only available from within your app')}}">
{{=helpicon()}}
<span>{{=T("These files are not served, they are only available from within your app")}}</span>
</a><span id="private" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('private_inner');">{{=T("Private files")}}</span>
<a href="#private" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are not served, they are only available from within your app')}}">
{{=helpicon()}}
<span>{{=T("These files are not served, they are only available from within your app")}}</span>
</a><span id="private" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="private_inner" class="component_contents">
{{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{
path=[]
for file in privates+['']:
items=file.split('/')
file_path=items[:-1]
filename=items[-1]
while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)])
thispath='private__'+'__'.join(path)
}}
<li class="folder">
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else:
path = path[:-1]
}}
</ul>
</li>
{{
pass
pass
if filename:
}}
<li>
<span class="filetools controls">
{{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}}
</span>
<span class="file">
{{=peekfile('private',file, dict(id="private"))}}
</span>
</li>{{
pass
pass
}}
{{pass}}
</ul>
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=file_create_form('%s/private/' % app, 'private')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div>
</div>
</div>
{{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit">
{{
path=[]
for file in privates+['']:
items=file.split('/')
file_path=items[:-1]
filename=items[-1]
while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)])
thispath='private__'+'__'.join(path)
}}
<li class="folder">
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else:
path = path[:-1]
}}
</ul>
</li>
{{
pass
pass
if filename:
}}
<li>
<span class="filetools controls">
{{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}}
</span>
<span class="file">
{{=peekfile('private',file, dict(id="private"))}}
</span>
</li>{{
pass
pass
}}
{{pass}}
</ul>
<div class="controls formfield">
<button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form7" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/private/' % app, 'private')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div>
</div>
</div>
</div>
<!-- PLUGINS -->
<h3 id="_plugins" rel="pagebookmark">
<span class="component" onclick="collapse('plugins_inner');">{{=T("Plugins")}}</span>
<a href="#plugins" rel="tooltip" data-placement="right" data-original-title="{{=T('To create a plugin, name a file/folder plugin_[name]')}}">
{{=helpicon()}}
<span>{{=T("To create a plugin, name a file/folder plugin_[name]")}}</span>
</a><span id="plugins" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
<span class="component" onclick="collapse('plugins_inner');">{{=T("Plugins")}}</span>
<a href="#plugins" rel="tooltip" data-placement="right" data-original-title="{{=T('To create a plugin, name a file/folder plugin_[name]')}}">
{{=helpicon()}}
<span>{{=T("To create a plugin, name a file/folder plugin_[name]")}}</span>
</a><span id="plugins" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3>
<div id="plugins_inner" class="component_contents">
<div class="controls comptools">
{{=button(PLUGINS_APP, T('download plugins'))}}
</div>
{{if plugins:}}
<ul class="unstyled act_edit">
{{for plugin in plugins:}}
{{id="plugins__"+plugin.replace('/','__').replace('.','__')}}
<li id="{{=id}}">
{{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}}
</li>
{{pass}}
</ul>
{{else:}}
<p><strong>{{=T('There are no plugins')}}</strong></p>
{{pass}}
<div class="controls formfield">
<div class="row-fluid">
<div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div>
</div>
</div>
<div class="controls comptools">
{{=button(PLUGINS_APP, T('download plugins'))}}
</div>
{{if plugins:}}
<ul class="unstyled act_edit">
{{for plugin in plugins:}}
{{id="plugins__"+plugin.replace('/','__').replace('.','__')}}
<li id="{{=id}}">
{{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}}
</li>
{{pass}}
</ul>
{{else:}}
<p><strong>{{=T('There are no plugins')}}</strong></p>
{{pass}}
<div class="controls formfield">
<button onclick="jQuery('#form8').slideToggle()" class="btn btn-mini">Upload</button>
<div id="form8" class="row-fluid" style="display:none">
<div class="row-fluid">
<div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div>
</div>
</div>
</div>
<script>
@@ -465,7 +474,7 @@ function filter_files() {
files=data['files'];
message=data['message'];
for(var i=0; i<files.length; i++)
jQuery('li#'+files[i].replace(/\//g,'__').replace('.','__')).slideDown();
jQuery('li#_'+files[i].replace(/\//g,'__').replace('.','__')).slideDown();
jQuery('.flash').html(message).slideDown();
});
} else {
+1 -1
View File
@@ -132,7 +132,7 @@ jQuery(document).ready(function(){
<form action="{{=URL('edit',args=filename)}}" method="post" name="editform" id="editform">
<div class="editor-bar-column">
<label>{{=T('Save file:')}}</label>
<a value="save" name="save" onclick="return doClickSave();" class="icon saveicon btn btn-mini" style="background-image: -webkit-linear-gradient(top,white,#E6E6E6);">
<a value="save" name="save" onclick="return doClickSave();" class="icon saveicon" style="background-image: -webkit-linear-gradient(top,white,#E6E6E6);">
{{=IMG(_src=URL('static', 'images/save_icon.png'), _alt=T('Save'))}}
</a>
</div>
+3 -3
View File
@@ -6,7 +6,7 @@ def all(items):
def peekfile(path,file):
return A(file.replace('\\\\','/'),_href=URL('peek', args=(app, path, file)))
def editfile(path,file):
return A(SPAN(T('Edit')),_class='button editbutton btn',_href=URL('edit', args=(app, path, file)))
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit', args=(app, path, file)))
def testfile(path,file):
return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('test')),
SPAN(T("Run tests in this file"))),
@@ -16,7 +16,7 @@ def testfile(path,file):
**{'_data-placement':'right',
'_data-original-title':T("Run tests in this file")})
def editlanguagefile(path,file):
return A(SPAN(T('Edit')),_class='button editbutton',_href=URL('edit_language', args=(app, path, file)))
return A(SPAN(T('Edit')),_class='button editbutton btn-mini',_href=URL('edit_language', args=(app, path, file)))
def file_upload_form(location):
form=FORM(T("upload file:")," ",
INPUT(_type="file",_name="file")," ",T("and rename it:")," ",
@@ -220,4 +220,4 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
</ul>
{{pass}}
</div>
<!-- end "plugin" block -->
<!-- end "plugin" block -->
+152 -138
View File
@@ -3,143 +3,157 @@
{{block sectionclass}}site{{end}}
<!-- begin "site" block -->
<div class="row-fluid">
<div class="applist f60 span7">
<div class="applist_inner">
<h2>{{=T("Installed applications")}}</h2>
<ul class="unstyled">
{{for a in apps:}}
<li class="application"> <!-- onmouseover="jQuery(this).children('p').show()" onmouseout="jQuery(this).children('p').hide()"-->
{{if a==request.application:}}
<h3 class="currentapp muted">{{=a}} ({{=T('currently running')}})</h3>
<p class="controls row-buttons">
{{else:}}
<h3 class="editableapp muted">{{=A(a,_href=URL(a,'default','index'))}}</h3>
{{if MULTI_USER_MODE and db.app(name=a):}}(created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}){{pass}}
<p class="controls row-buttons">
{{if not os.path.exists('applications/%s/compiled' % a):}}
{{=sp_button(URL('design',args=a), T("Edit"))}}
{{else:}}
{{=button(URL(a,'appadmin','index'), T("appadmin"))}}
{{pass}}
{{=button(URL('about',args=a), T("About"))}}
{{pass}}
{{=button(URL('errors',args=a), T("Errors"))}}
{{=button(URL('cleanup',args=a), T("Clean"))}}
{{=button(URL('pack',args=a), T("Pack all"))}}
{{if not os.path.exists('applications/%s/compiled' % a):}}
{{=button(URL('compile_app',args=a), T("Compile"))}}
{{else:}}
{{=button(URL('pack',args=(a, 'compiled')), T("Pack compiled"))}}
{{if glob.glob('applications/%s/controllers/*.py' % a):}}
{{=button(URL('remove_compiled_app',args=a), T("Remove compiled"))}}
{{pass}}
{{pass}}
{{if os.path.exists(os.path.join(apath(r=request),a,'.git')): }}
{{=button(URL('git_pull',args=a), T("Git Pull"))}}
{{=button(URL('git_push',args=a), T("Git Push"))}}
{{pass}}
{{if a!=request.application:}}
{{=button(URL('uninstall',args=a), T("Uninstall"))}}
{{=button_enable(URL('enable',args=a), a)}}
{{pass}}
</p>
</li>
{{pass}}
</ul>
</div>
</div> <!-- /applist -->
<div class="sidebar fl60 span5">
<div class="sidebar_inner controls well well-small">
<!-- CHANGE ADMIN PWD -->
<div class="pwdchange pull-right">
{{if MULTI_USER_MODE:}}
{{=auth.navbar()}}
{{else:}}
{{=sp_button(URL('change_password'), T('Change admin password'))}}
{{pass}}
</div> <!-- /CHANGE ADMIN PWD -->
{{if is_manager():}}
<!-- VERSION -->
<div class="box">
<h3>{{=T("Version %s.%s.%s %s (%s)", myversion)}}</h3>
<p id="check_version" class="row-buttons">
{{if session.check_version:}}
{{=T('Checking for upgrades...')}}
</p>
<script>ajax('{{=URL('check_version')}}',[],'check_version');</script>
{{session.check_version=False}}
{{else:}}
{{=button("javascript:ajax('"+URL('check_version')+"',[],'check_version')", T('Check for upgrades'))}}
{{=button(URL('default','reload_routes'), T('Reload routes'))}}
</p>
{{pass}}
<p>{{=T("Running on %s", request.env.server_software)}}</p>
{{if session.is_mobile=='auto':}}
<p>{{=A(T('Try the mobile interface'),_href=URL('plugin_jqmobile','about'))}}</p>
{{pass}}
</div> <!-- /VERSION -->
{{pass}}
{{if MULTI_USER_MODE and is_manager():}}
<!-- MULTI_USER_INTERFACE -->
<div class="box">
<h3>{{=T("Multi User Mode")}}</h3>
<p class="row-buttons">
{{=button(URL('bulk_register'),T('Bulk Register'))}}
{{=button(URL('manage_students',vars={'order':'auth_user.id'}),T('Manage Students'))}}
</p>
</div> <!-- /MULTI_USER_INTERFACE -->
{{pass}}
<!-- APP WIZARD -->
<div class="box">
<h3>{{=T("New application wizard")}}</h3>
<p>{{=button(URL('wizard','index'), T('Start wizard'))}}
{{=T("(requires internet access)")}}</p>
</div> <!-- /APP WIZARD -->
<!-- SCAFFOLD APP -->
<div class="box">
<h3>{{=T("New simple application")}}</h3>
{{=form_create.custom.begin}}
{{=LABEL(T("Application name:"))}}
{{=form_create.custom.widget.name}}
<div class="controls"><button type="submit" class="btn">{{=T('Create')}}</button></div>
{{=form_create.custom.end}}
</div> <!-- /SCAFFOLD APP -->
<!-- UPLOAD PACKAGE -->
<div class="box">
<h3>{{=T("Upload and install packed application")}}</h3>
{{=form_update.custom.begin}}
<label for="appupdate_name">{{=T("Application name:")}}</label>
{{=form_update.custom.widget.name}}
<label for="appupdate_file">{{=T("Upload a package:")}}</label>
{{=form_update.custom.widget.file}}
<label for="appupdate_url">{{=T("Or Get from URL:")}}</label>
{{=form_update.custom.widget.url}}<small class="help-block">({{=T('can be a git repo')}})</small>
<div class="controls">
<label class="checkbox">
{{=form_update.custom.widget.overwrite}} {{=T("Overwrite installed app")}}
</label>
<button type="submit" class='btn'>{{=T('Install')}}</button>
</div>
{{=form_update.custom.end}}
</div> <!-- /UPLOAD PACKAGE -->
<!-- DEPLOY ON GAE -->
<div class="box">
<h3>{{=T("Deploy")}}</h3>
<p class="row-buttons">
{{=button(URL('gae','deploy'), T('Deploy on Google App Engine'))}}
{{=button(URL('openshift','deploy'),T('Deploy to OpenShift'))}}
</p>
</div> <!-- /DEPLOY ON GAE -->
<br/>
{{if TWITTER_HASH:}}
<!-- TWITTER -->
<div class="box">
<h3>{{=T("%s Recent Tweets"%TWITTER_HASH)}}</h3>
<div id="tweets">{{=T('loading...')}}</div>
<script>jQuery(document).ready(function(){jQuery('#tweets').load('{{=URL('twitter.load')}}');});</script>
</div> <!-- /TWITTER -->
{{pass}}
</div>
</div> <!-- /sidebar -->
<div class="applist f60 span7">
<div class="applist_inner">
<h2>{{=T("Installed applications")}}</h2>
<table width="100%" class="table">
{{for a in apps:}}
<tr>{{buttons = []}}
<td>
{{if a==request.application:}}
<h4 class="currentapp">{{=a}} ({{=T('currently running')}})</h4>
{{else:}}
<h4 class="editableapp">{{=A(a,_href=URL(a,'default','index'))}}</h4>
{{if MULTI_USER_MODE and db.app(name=a):}}(created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}){{pass}}
{{if not os.path.exists('applications/%s/compiled' % a):}}
{{buttons.append((URL('design',args=a), T("Edit")))}}
{{else:}}
{{buttons.append((URL(a,'appadmin','index'), T("appadmin")))}}
{{pass}}
{{buttons.append((URL('about',args=a), T("About")))}}
{{pass}}
{{buttons.append((URL('errors',args=a), T("Errors")))}}
{{buttons.append((URL('cleanup',args=a), T("Clean")))}}
{{buttons.append((URL('pack',args=a), T("Pack all")))}}
{{if not os.path.exists('applications/%s/compiled' % a):}}
{{buttons.append((URL('compile_app',args=a), T("Compile")))}}
{{else:}}
{{buttons.append((URL('pack',args=(a, 'compiled')), T("Pack compiled")))}}
{{if glob.glob('applications/%s/controllers/*.py' % a):}}
{{buttons.append((URL('remove_compiled_app',args=a), T("Remove compiled")))}}
{{pass}}
{{pass}}
{{if os.path.exists(os.path.join(apath(r=request),a,'.git')): }}
{{buttons.append((URL('git_pull',args=a), T("Git Pull")))}}
{{buttons.append((URL('git_push',args=a), T("Git Push")))}}
{{pass}}
{{if a!=request.application:}}
{{buttons.append((URL('uninstall',args=a), T("Uninstall")))}}
{{pass}}
</td>
<td>
<div class="btn-group">
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
{{=T('Manage')}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
{{for link,name in buttons:}}
{{=LI(A(name,_href=link))}}
{{pass}}
</ul>
</div>
{{=button_enable(URL('enable',args=a), a)}}
</td>
</tr>
{{pass}}
</table>
</div>
</div> <!-- /applist -->
<div class="sidebar fl60 span5">
<div class="sidebar_inner controls well well-small">
<!-- CHANGE ADMIN PWD -->
<div class="pwdchange pull-right">
{{if MULTI_USER_MODE:}}
{{=auth.navbar()}}
{{else:}}
{{=sp_button(URL('change_password'), T('Change admin password'))}}
{{=button(URL('default','reload_routes'), T('Reload routes'))}}
{{pass}}
</div> <!-- /CHANGE ADMIN PWD -->
{{if is_manager():}}
<!-- VERSION -->
<div class="box">
<h4>{{=T("Version")}}</h4>
<p>
<tt>{{=myversion}}</tt><br/>
({{=T("Running on %s", request.env.server_software)}})
</p>
<p id="check_version" class="row-buttons">
{{if session.check_version:}}
{{=T('Checking for upgrades...')}}
<script>ajax('{{=URL('check_version')}}',[],'check_version');</script>
{{session.check_version=False}}
{{else:}}
{{=button("javascript:ajax('"+URL('check_version')+"',[],'check_version')", T('Check for upgrades'))}}
{{pass}}
</p>
{{if session.is_mobile=='auto':}}
<p>{{=A(T('Try the mobile interface'),_href=URL('plugin_jqmobile','about'))}}</p>
{{pass}}
</div> <!-- /VERSION -->
{{pass}}
{{if MULTI_USER_MODE and is_manager():}}
<!-- MULTI_USER_INTERFACE -->
<div class="box">
<h4>{{=T("Multi User Mode")}}</h4>
<p class="row-buttons">
{{=button(URL('bulk_register'),T('Bulk Register'))}}
{{=button(URL('manage_students',vars={'order':'auth_user.id'}),T('Manage Students'))}}
</p>
</div> <!-- /MULTI_USER_INTERFACE -->
{{pass}}
<!-- SCAFFOLD APP -->
<div class="box">
<h4>{{=T("New simple application")}}</h4>
{{=form_create.custom.begin}}
{{=LABEL(T("Application name:"))}}
{{=form_create.custom.widget.name}}
<div class="controls"><button type="submit" class="btn">{{=T('Create')}}</button></div>
{{=form_create.custom.end}}
</div> <!-- /SCAFFOLD APP -->
<!-- UPLOAD PACKAGE -->
<div class="box">
<h4>{{=T("Upload and install packed application")}}</h4>
{{=form_update.custom.begin}}
<label for="appupdate_name">{{=T("Application name:")}}</label>
{{=form_update.custom.widget.name}}
<label for="appupdate_file">{{=T("Upload a package:")}}</label>
{{=form_update.custom.widget.file}}
<label for="appupdate_url">{{=T("Or Get from URL:")}}</label>
{{=form_update.custom.widget.url}}<small class="help-block">({{=T('can be a git repo')}})</small>
<div class="controls">
<label class="checkbox">
{{=form_update.custom.widget.overwrite}} {{=T("Overwrite installed app")}}
</label>
<button type="submit" class='btn'>{{=T('Install')}}</button>
</div>
{{=form_update.custom.end}}
</div> <!-- /UPLOAD PACKAGE -->
<!-- DEPLOY ON GAE -->
<div class="box">
<h4>{{=T("Deploy")}}</h4>
<p class="row-buttons">
{{=button(URL('gae','deploy'), T('Deploy on Google App Engine'))}}
{{=button(URL('openshift','deploy'),T('Deploy to OpenShift'))}}
</p>
</div> <!-- /DEPLOY ON GAE -->
<!-- APP WIZARD -->
<div class="box">
<h4>{{=T("New application wizard")}}</h4>
<p>{{=button(URL('wizard','index'), T('Start wizard'))}}<br/>
{{=T("(requires internet access, experimental)")}}</p>
</div> <!-- /APP WIZARD -->
{{if TWITTER_HASH:}}
<!-- TWITTER -->
<div class="box">
<h4>{{=T("%s Recent Tweets"%TWITTER_HASH)}}</h4>
<div id="tweets">{{=T('loading...')}}</div>
<script>jQuery(document).ready(function(){jQuery('#tweets').load('{{=URL('twitter.load')}}');});</script>
</div> <!-- /TWITTER -->
{{pass}}
</div>
</div> <!-- /sidebar -->
</div> <!-- /row-fluid
<!-- end "site" block -->
+1 -1
View File
@@ -10,7 +10,7 @@
<tbody>
<tr>
<th>web2py&trade;</th>
<td>{{=myversion}}</td>
<td>Version {{=myversion}}</td>
</tr>
{{if snapshot:}}
<tr>
+12 -3
View File
@@ -27,8 +27,17 @@ Tweet attributes
"""
}}
{{import re}}
{{hashtag = re.compile('([#@])(\w+)')}}
{{link = re.compile('(?!<")https?\:\/\/[\w\./?&]+')}}
<table class="twitter">
{{ for t in tweets: }}
{{ =DIV(H5(t["from_user_name"])) }}
{{ =DIV(t["text"]) }}
{{ =BR() }}
<tr class="tweet">
<td><img src="{{=t['profile_image_url_https']}}"/></td>
<td>
<a href="http://twitter.com/{{=t['from_user']}}">{{=t["from_user_name"]}}</a>:
{{=XML(hashtag.sub('<a href="http://twitter.com/\\g<2>">\\g<1>\\g<2></a>',link.sub('<a href="\\g<0>">\\g<0></a>',t["text"].encode('utf8'))),sanitize=True)}}
</td>
</tr>
{{ pass }}
</table>
+7 -1
View File
@@ -8,6 +8,7 @@
<title>{{=response.title or URL()}}</title>
{{
response.files.append(URL('static','css/bootstrap.min.css'))
#response.files.append(URL('static','css/styles.css'))
response.files.append(URL('static','css/bootstrap_essentials.css'))
# response.files.append(URL('static','css/bootstrap_adapters.css'))
response.files.append(URL('static','css/bootstrap-responsive.min.css'))
@@ -79,6 +80,11 @@
jQuery("[rel=tooltip]").tooltip();
});
</script>
{{if request.function in ('index','site'):}}
<a style="position:fixed;bottom:0;left:0;z-index:1000" href="https://groups.google.com/forum/?fromgroups#!forum/web2py" target="_blank">
<!-- http://webchat.freenode.net/?channels=web2py" //-->
<img src="{{=URL('static','images/questions.png')}}" />
</a>
{{pass}}
</body>
</html>
@@ -7,7 +7,6 @@ for application "{{=request.args[0]}}"</h2>
<h3>Commit form</h3>
{{=form}}
{{if repo['.'].rev()>=0:}}
<h3>Last Revision</h3>
<table>
@@ -319,6 +319,9 @@ def state():
def ccache():
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
@@ -385,7 +388,7 @@ def ccache():
return (hours, minutes, seconds)
for key, value in cache.ram.storage.items():
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
+10 -6
View File
@@ -20,7 +20,7 @@ def what():
import urllib
try:
images = XML(urllib.urlopen(
'http://web2py.com/poweredby/default/images').read())
'http://www.web2py.com/poweredby/default/images').read())
except:
images = []
return response.render(images=images)
@@ -61,11 +61,11 @@ def videos():
def security():
redirect('http://www.web2py.com/book/default/chapter/01#security')
redirect('http://www.web2py.com/book/default/chapter/01#Security')
def api():
redirect('http://web2py.com/book/default/chapter/04#API')
redirect('http://www.web2py.com/book/default/chapter/04#API')
@cache('license', time_expire=cache_expire)
@@ -74,10 +74,14 @@ def license():
filename = os.path.join(request.env.gluon_parent, 'LICENSE')
return response.render(dict(license=MARKMIN(read_file(filename))))
def version():
return 'Version %s.%s.%s (%s) %s' % request.env.web2py_version
if request.args(0)=='raw':
return request.env.web2py_version
from gluon.fileutils import parse_version
(a, b, c, pre_release, build) = parse_version(request.env.web2py_version)
return 'Version %i.%i.%i (%.4i-%.2i-%.2i %.2i:%.2i:%.2i) %s' % (
a,b,c,build.year,build.month,build.day,
build.hour,build.minute,build.second,pre_release)
@cache('examples', time_expire=cache_expire)
def examples():
+35 -61
View File
@@ -1,75 +1,49 @@
session.forget()
response.menu = [['home', False, '/%s/default/index'
% request.application], ['docs', True,
'/%s/global/vars' % request.application]]
def get(args):
if args[0].startswith('__'):
return None
try:
obj = globals(),get(args[0])
for k in range(1,len(args)):
obj = getattr(obj,args[k])
return obj
except:
return None
def vars():
"""the running controller function!"""
title = '.'.join(request.args)
attributes = {}
if not request.args:
(
doc,
keys,
t,
c,
d,
value,
) = (
'Global variables',
globals(),
None,
None,
(),
None,
)
(title, args) = ('globals()', '')
(doc,keys,t,c,d,value)=('Global variables',globals(),None,None,[],None)
elif len(request.args) < 3:
args = '.'.join(request.args)
try:
doc = eval(args + '.__doc__')
except:
doc = 'no documentation'
try:
keys = eval('dir(%s)' % args)
except:
obj = get(request.args)
if obj:
doc = getattr(obj,'__doc__','no documentation')
keys = dir(obj)
t = type(obj)
c = getattr(obj,'__class__',None)
d = getattr(obj,'__bases__',None)
for key in keys:
a = getattr(obj,key,None)
if a and not isinstance(a,DAL):
doc1 = getattr(a, '__doc__', '')
t1 = type(a)
c1 = getattr(a,'__class__',None)
d1 = getattr(a,'__bases__',None)
key = '.'.join(request.args)+'.'+key
attributes[key] = (doc1, t1, c1, d1)
else:
doc = 'Unkown'
keys = []
t = eval('type(%s)' % args)
try:
c = eval('%s.__class__' % args)
except:
c = None
try:
d = eval('%s.__bases__' % args)
except:
d = None
title = args
args += '.'
t = c = d = None
else:
raise HTTP(400)
attributes = {}
for key in keys:
a = args + key
if eval('isinstance(%s,SQLDB)' % a) or a == 'vars':
continue
try:
doc1 = eval(a + '.__doc__')
except:
doc1 = 'no documentation'
t1 = eval('type(%s)' % a)
try:
c1 = eval('%s.__class__' % a)
except:
c1 = None
try:
d1 = eval('%s.__bases__' % a)
except:
d1 = ()
attributes[a] = (doc1, t1, c1, d1)
return dict(
title=title,
args=args,
args=request.args,
t=t,
c=c,
d=d,
@@ -54,13 +54,6 @@ def raisehttp():
raise HTTP(400, 'internal error')
def raiseexception():
""" generates an exeption, logs the event and returns a ticket number """
1 / 0
return 'oops'
def servejs():
""" serves a js document """
@@ -69,7 +62,6 @@ def servejs():
gluon.contenttype.contenttype('.js')
return 'alert("This is a Javascript document, it is not supposed to run!");'
def makejson():
import gluon.contrib.simplejson as sj
return sj.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
@@ -1,6 +1,5 @@
from gluon.contrib.spreadsheet import Sheet
def callback():
return cache.ram('sheet1', lambda: None, None).process(request)
+1
View File
@@ -0,0 +1 @@
session.connect(request,response,cookie_key='yoursecret')
+1
View File
@@ -0,0 +1 @@
403
+1
View File
@@ -0,0 +1 @@
404
+1
View File
@@ -0,0 +1 @@
500
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+12 -113
View File
@@ -1,117 +1,16 @@
footer-content {position: relative; bottom: 0; width: 100%;}
.statusbar {
padding: 2px 0;
border: 0;
margin-bottom: 15px;
background-color: #C1CDCD;
/*background: url('../images/menu.png') repeat-x;*/
}
.statusbar a, .statusbar a:visited {
color: #406361;
}
.footer {
position: relative;
}
.footer-content {
height: 100%;
position: absolute;
}
@import url(http://fonts.googleapis.com/css?family=Economica);
@@import url(http://fonts.googleapis.com/css?family=Belleza);
body { font-family: Arial, Helvetica; }
a, a:visited, a:hover, h1,h2,h3,h4,h5 {color: #658883}
a.button {color: #658883}
.sf-menu li, .sf-menu li a { border-radius: 5px;}
.wrapper {
background: url('../images/back-02.png') repeat-x;
padding-top:10px;
a.btn-danger, a.btn-warning, a.btn-success {color:white}
h1,h2,h3,h4,h5 { font-family: "Economica", Arial, Helevtica; }
body {
background: url('../images/stripes.png') repeat-x;
}
h1 { font-size: 32px }
h2 { font-size: 24px }
h3 { font-size: 20px }
h4 { font-size: 14px }
sup {
font-size: 0.5em;
line-height: 2em;
vertical-align: top;
}
ul { list-style: circle outside; padding-left: 30px;}
.frame {
border: 3px solid #959595;
margin-bottom: 5px;
}
.header h5 {
float: left;
}
.logo: {
float:left;
}
.announce {
text-align: center;
color: white;
top: 0;
font-weight: bold;
text-align: center;
left: 0;
position: relative;
background: #000;
border-radius: 5px;
margin: 7px 0 15px;
}
.announce a {color: white}
input:focus, textarea:focus {background:#f0f0f0}
table.downloads { width:100%; }
table.downloads th, table.downloads td {text-align:center;}
table.downloads a.button { width: 150px; }
th,td {padding-right: 10px;}
p {text-align: left;}
p + p {
margin-bottom: .75em;
}
strong {color: inherit;}
img.centered {
margin: 0 auto;
display: block;
}
.tagCloud {
width: 100%;
max-width: 489px;
}
.mainbody {
margin-bottom: 20px;
}
.aboutW2P {
margin-top: 23px;
margin-bottom: 15px;
}
.aboutW2P p {
padding: 0 0.5em;
font-size: 1.15em;
}
.aboutW2P h3 {
padding: 0 0.3em;
}
.userQuotes {
/*border-top: #eee 2px solid;*/
padding-top: 15px;
}
.userQuotes p {
text-align: left;
font-size: 111%;
color: #777;
}
#menu {
padding: 0.1em 0.5em;
#header {
margin-top: 40px;
}
.btn-180 {
width: 180px;
}
-629
View File
@@ -1,629 +0,0 @@
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Content
==================================================
#Reset & Basics
#Basic Styles
#Site Styles
#Typography
#Links
#Lists
#Images
#Buttons
#Tabs
#Forms
#Misc */
/* #Reset & Basics (Inspired by E. Meyers)
================================================== */
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline; }
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
display: block; }
body {
line-height: 1; }
// ol, ul { list-style: none; } /* WEB2PY EDIT */
blockquote, q {
quotes: none; }
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none; }
table {
border-collapse: collapse;
border-spacing: 0; }
/* #Basic Styles
================================================== */
body {
background: #fff;
font: 14px/21px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #444;
-webkit-font-smoothing: antialiased; /* Fix for webkit rendering */
-webkit-text-size-adjust: 100%;
}
/* #Typography
================================================== */
h1, h2, h3, h4, h5, h6 {
color: #181818;
font-family: "Georgia", "Times New Roman", Helvetica, Arial, sans-serif;
font-weight: normal; }
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { font-weight: inherit; }
h1 { font-size: 46px; line-height: 50px; }
h2 { font-size: 35px; line-height: 40px; }
h3 { font-size: 28px; line-height: 34px; }
h4 { font-size: 21px; line-height: 30px; }
h5 { font-size: 17px; line-height: 24px; }
h6 { font-size: 14px; line-height: 21px; }
.subheader { color: #777; }
p { margin: 0 0 20px 0; }
p img { margin: 0; }
p.lead { font-size: 21px; line-height: 27px; color: #777; }
em { font-style: italic; }
strong { font-weight: bold; color: #333; }
small { font-size: 80%; }
/* Blockquotes */
blockquote, blockquote p { font-size: 17px; line-height: 24px; color: #777; font-style: italic; }
blockquote { margin: 0 0 20px; padding: 9px 20px 0 19px;} /* WEB2PY EDIT */
blockquote cite { display: block; font-size: 12px; color: #555; }
blockquote cite:before { content: "\2014 \0020"; }
blockquote cite a, blockquote cite a:visited, blockquote cite a:visited { color: #555; }
hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 10px 0 30px; height: 0; }
/* #Links
================================================== */
a, a:visited { color: #333; text-decoration: underline; outline: 0; }
a:hover, a:focus { color: #000; }
p a, p a:visited { line-height: inherit; }
/* #Lists
================================================== */
ul, ol { margin-bottom: 20px; }
// ul { list-style: none outside; } /* WEB2PY EDIT */
ol { list-style: decimal; }
ol, ul.square, ul.circle, ul.disc { margin-left: 30px; }
ul.square { list-style: square outside; }
ul.circle { list-style: circle outside; }
ul.disc { list-style: disc outside; }
ul ul, ul ol,
ol ol, ol ul { margin: 4px 0 5px 30px; font-size: 90%; }
ul ul li, ul ol li,
ol ol li, ol ul li { margin-bottom: 6px; }
li { line-height: 18px; margin-bottom: 12px; }
ul.large li { line-height: 21px; }
li p { line-height: 21px; }
/* #Images
================================================== */
img.scale-with-grid {
max-width: 100%;
height: auto; }
/* #Buttons
================================================== */
a.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
background: #eee; /* Old browsers */
background: #eee -moz-linear-gradient(top, rgba(255,255,255,.2) 0%, rgba(0,0,0,.2) 100%); /* FF3.6+ */
background: #eee -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.2)), color-stop(100%,rgba(0,0,0,.2))); /* Chrome,Safari4+ */
background: #eee -webkit-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Chrome10+,Safari5.1+ */
background: #eee -o-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* Opera11.10+ */
background: #eee -ms-linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* IE10+ */
background: #eee linear-gradient(top, rgba(255,255,255,.2) 0%,rgba(0,0,0,.2) 100%); /* W3C */
border: 1px solid #aaa;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
padding: 4px 12px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
color: #444;
display: inline-block;
font-size: 1em; /** WEB2PY EDIT **/
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px rgba(255, 255, 255, .75);
cursor: pointer;
margin-bottom: 5px; /** WEB2PY EDIT **/
line-height: 21px;
font-family: "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; }
a.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover {
color: #222;
background: #ddd; /* Old browsers */
background: #ddd -moz-linear-gradient(top, rgba(255,255,255,.3) 0%, rgba(0,0,0,.3) 100%); /* FF3.6+ */
background: #ddd -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.3)), color-stop(100%,rgba(0,0,0,.3))); /* Chrome,Safari4+ */
background: #ddd -webkit-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Chrome10+,Safari5.1+ */
background: #ddd -o-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* Opera11.10+ */
background: #ddd -ms-linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* IE10+ */
background: #ddd linear-gradient(top, rgba(255,255,255,.3) 0%,rgba(0,0,0,.3) 100%); /* W3C */
border: 1px solid #888;
border-top: 1px solid #aaa;
border-left: 1px solid #aaa; }
a.button:active,
button:active,
input[type="submit"]:active,
input[type="reset"]:active,
input[type="button"]:active {
border: 1px solid #666;
background: #ccc; /* Old browsers */
background: #ccc -moz-linear-gradient(top, rgba(255,255,255,.35) 0%, rgba(10,10,10,.4) 100%); /* FF3.6+ */
background: #ccc -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(255,255,255,.35)), color-stop(100%,rgba(10,10,10,.4))); /* Chrome,Safari4+ */
background: #ccc -webkit-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Chrome10+,Safari5.1+ */
background: #ccc -o-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* Opera11.10+ */
background: #ccc -ms-linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* IE10+ */
background: #ccc linear-gradient(top, rgba(255,255,255,.35) 0%,rgba(10,10,10,.4) 100%); /* W3C */ }
.button.full-width,
button.full-width,
input[type="submit"].full-width,
input[type="reset"].full-width,
input[type="button"].full-width {
width: 100%;
padding-left: 0 !important;
padding-right: 0 !important;
text-align: center; }
/* #Tabs (activate in tabs.js)
================================================== */
ul.tabs {
display: block;
margin: 0 0 20px 0;
padding: 0;
border-bottom: solid 1px #ddd; }
ul.tabs li {
display: block;
width: auto;
height: 30px;
padding: 0;
float: left;
margin-bottom: 0; }
ul.tabs li a {
display: block;
text-decoration: none;
width: auto;
height: 29px;
padding: 0px 20px;
line-height: 30px;
border: solid 1px #ddd;
border-width: 1px 1px 0 0;
margin: 0;
background: #f5f5f5;
font-size: 13px; }
ul.tabs li a.active {
background: #fff;
height: 30px;
position: relative;
top: -4px;
padding-top: 4px;
border-left-width: 1px;
margin: 0 0 0 -1px;
color: #111;
-moz-border-radius-topleft: 2px;
-webkit-border-top-left-radius: 2px;
border-top-left-radius: 2px;
-moz-border-radius-topright: 2px;
-webkit-border-top-right-radius: 2px;
border-top-right-radius: 2px; }
ul.tabs li:first-child a.active {
margin-left: 0; }
ul.tabs li:first-child a {
border-width: 1px 1px 0 1px;
-moz-border-radius-topleft: 2px;
-webkit-border-top-left-radius: 2px;
border-top-left-radius: 2px; }
ul.tabs li:last-child a {
-moz-border-radius-topright: 2px;
-webkit-border-top-right-radius: 2px;
border-top-right-radius: 2px; }
ul.tabs-content { margin: 0; display: block; }
ul.tabs-content > li { display:none; }
ul.tabs-content > li.active { display: block; }
/* Clearfixing tabs for beautiful stacking */
ul.tabs:before,
ul.tabs:after {
content: '\0020';
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0; }
ul.tabs:after {
clear: both; }
ul.tabs {
zoom: 1; }
/* #Forms
================================================== */
form {
margin-bottom: 5px; } /** WEB2PY EDIT **/
fieldset {
margin-bottom: 5px; } /** WEB2PY EDIT **/
input[type="text"],
input[type="password"],
input[type="email"],
textarea,
select {
border: 1px solid #ccc;
padding: 6px 4px;
outline: none;
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
border-radius: 2px;
font: 13px "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #777;
margin: 0;
width: 210px;
max-width: 100%;
// display: block; /** WEB2PY EDIT **/
margin-bottom: 0; /** WEB2PY EDIT **/
background: #fff; }
select {
padding: 0; }
input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus,
textarea:focus {
border: 1px solid #aaa;
color: #444;
-moz-box-shadow: 0 0 3px rgba(0,0,0,.2);
-webkit-box-shadow: 0 0 3px rgba(0,0,0,.2);
box-shadow: 0 0 3px rgba(0,0,0,.2); }
textarea {
min-height: 60px; }
label,
legend {
display: block;
font-weight: bold;
font-size: 13px; }
select {
width: 220px; }
input[type="checkbox"] {
display: inline; }
label span,
legend span {
font-weight: normal;
font-size: 13px;
color: #444; }
/* #Misc
================================================== */
.remove-bottom { margin-bottom: 0 !important; }
.half-bottom { margin-bottom: 10px !important; }
.add-bottom { margin-bottom: 20px !important; }
/*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Content
==================================================
#Site Styles
#Page Styles
#Media Queries
#Font-Face */
/* #Site Styles
================================================== */
/* #Page Styles
================================================== */
/* #Media Queries
================================================== */
/* Smaller than standard 960 (devices and browsers) */
@media only screen and (max-width: 959px) {}
/* Tablet Portrait size to standard 960 (devices and browsers) */
@media only screen and (min-width: 768px) and (max-width: 959px) {}
/* All Mobile Sizes (devices and browser) */
@media only screen and (max-width: 767px) {}
/* Mobile Landscape Size to Tablet Portrait (devices and browsers) */
@media only screen and (min-width: 480px) and (max-width: 767px) {}
/* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */
@media only screen and (max-width: 479px) {}
/* #Font-Face
================================================== */
/* This is the proper syntax for an @font-face file
Just create a "fonts" folder at the root,
copy your FontName into code below and remove
comment brackets */
/* @font-face {
font-family: 'FontName';
src: url('../fonts/FontName.eot');
src: url('../fonts/FontName.eot?iefix') format('eot'),
url('../fonts/FontName.woff') format('woff'),
url('../fonts/FontName.ttf') format('truetype'),
url('../fonts/FontName.svg#webfontZam02nTh') format('svg');
font-weight: normal;
font-style: normal; }
*//*
* Skeleton V1.1
* Copyright 2011, Dave Gamache
* www.getskeleton.com
* Free to use under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
* 8/17/2011
*/
/* Table of Contents
==================================================
#Base 960 Grid
#Tablet (Portrait)
#Mobile (Portrait)
#Mobile (Landscape)
#Clearing */
/* #Base 960 Grid
================================================== */
.container { position: relative; width: 960px; margin: 0 auto; padding: 0; }
.column, .columns { float: left; display: inline; margin-left: 10px; margin-right: 10px; }
.row { margin-bottom: 20px; }
/* Nested Column Classes */
.column.alpha, .columns.alpha { margin-left: 0; }
.column.omega, .columns.omega { margin-right: 0; }
/* Base Grid */
.container .one.column { width: 40px; }
.container .two.columns { width: 100px; }
.container .three.columns { width: 160px; }
.container .four.columns { width: 220px; }
.container .five.columns { width: 280px; }
.container .six.columns { width: 340px; }
.container .seven.columns { width: 400px; }
.container .eight.columns { width: 460px; }
.container .nine.columns { width: 520px; }
.container .ten.columns { width: 580px; }
.container .eleven.columns { width: 640px; }
.container .twelve.columns { width: 700px; }
.container .thirteen.columns { width: 760px; }
.container .fourteen.columns { width: 820px; }
.container .fifteen.columns { width: 880px; }
.container .sixteen.columns { width: 940px; }
.container .one-third.column { width: 300px; }
.container .two-thirds.column { width: 620px; }
/* Offsets */
.container .offset-by-one { padding-left: 60px; }
.container .offset-by-two { padding-left: 120px; }
.container .offset-by-three { padding-left: 180px; }
.container .offset-by-four { padding-left: 240px; }
.container .offset-by-five { padding-left: 300px; }
.container .offset-by-six { padding-left: 360px; }
.container .offset-by-seven { padding-left: 420px; }
.container .offset-by-eight { padding-left: 480px; }
.container .offset-by-nine { padding-left: 540px; }
.container .offset-by-ten { padding-left: 600px; }
.container .offset-by-eleven { padding-left: 660px; }
.container .offset-by-twelve { padding-left: 720px; }
.container .offset-by-thirteen { padding-left: 780px; }
.container .offset-by-fourteen { padding-left: 840px; }
.container .offset-by-fifteen { padding-left: 900px; }
/* #Tablet (Portrait)
================================================== */
/* Note: Design for a width of 768px */
@media only screen and (min-width: 768px) and (max-width: 959px) {
.container { width: 768px; }
.container .column,
.container .columns { margin-left: 10px; margin-right: 10px; }
.column.alpha, .columns.alpha { margin-left: 0; margin-right: 10px; }
.column.omega, .columns.omega { margin-right: 0; margin-left: 10px; }
.container .one.column { width: 28px; }
.container .two.columns { width: 76px; }
.container .three.columns { width: 124px; }
.container .four.columns { width: 172px; }
.container .five.columns { width: 220px; }
.container .six.columns { width: 268px; }
.container .seven.columns { width: 316px; }
.container .eight.columns { width: 364px; }
.container .nine.columns { width: 412px; }
.container .ten.columns { width: 460px; }
.container .eleven.columns { width: 508px; }
.container .twelve.columns { width: 556px; }
.container .thirteen.columns { width: 604px; }
.container .fourteen.columns { width: 652px; }
.container .fifteen.columns { width: 700px; }
.container .sixteen.columns { width: 748px; }
.container .one-third.column { width: 236px; }
.container .two-thirds.column { width: 492px; }
/* Offsets */
.container .offset-by-one { padding-left: 48px; }
.container .offset-by-two { padding-left: 96px; }
.container .offset-by-three { padding-left: 144px; }
.container .offset-by-four { padding-left: 192px; }
.container .offset-by-five { padding-left: 240px; }
.container .offset-by-six { padding-left: 288px; }
.container .offset-by-seven { padding-left: 336px; }
.container .offset-by-eight { padding-left: 348px; }
.container .offset-by-nine { padding-left: 432px; }
.container .offset-by-ten { padding-left: 480px; }
.container .offset-by-eleven { padding-left: 528px; }
.container .offset-by-twelve { padding-left: 576px; }
.container .offset-by-thirteen { padding-left: 624px; }
.container .offset-by-fourteen { padding-left: 672px; }
.container .offset-by-fifteen { padding-left: 720px; }
}
/* #Mobile (Portrait)
================================================== */
/* Note: Design for a width of 320px */
@media only screen and (max-width: 767px) {
.container { width: 300px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 300px; }
/* Offsets */
.container .offset-by-one,
.container .offset-by-two,
.container .offset-by-three,
.container .offset-by-four,
.container .offset-by-five,
.container .offset-by-six,
.container .offset-by-seven,
.container .offset-by-eight,
.container .offset-by-nine,
.container .offset-by-ten,
.container .offset-by-eleven,
.container .offset-by-twelve,
.container .offset-by-thirteen,
.container .offset-by-fourteen,
.container .offset-by-fifteen { padding-left: 0; }
}
/* #Mobile (Landscape)
================================================== */
/* Note: Design for a width of 480px */
@media only screen and (min-width: 480px) and (max-width: 767px) {
.container { width: 420px; }
.columns, .column { margin: 0; }
.container .one.column,
.container .two.columns,
.container .three.columns,
.container .four.columns,
.container .five.columns,
.container .six.columns,
.container .seven.columns,
.container .eight.columns,
.container .nine.columns,
.container .ten.columns,
.container .eleven.columns,
.container .twelve.columns,
.container .thirteen.columns,
.container .fourteen.columns,
.container .fifteen.columns,
.container .sixteen.columns,
.container .one-third.column,
.container .two-thirds.column { width: 420px; }
}
/* #Clearing
================================================== */
/* Self Clearing Goodness */
.container:after { content: "\0020"; display: block; height: 0; clear: both; visibility: hidden; }
/* Use clearfix class on parent to clear nested columns,
or wrap each row of columns in a <div class="row"> */
.clearfix:before,
.clearfix:after,
.row:before,
.row:after {
content: '\0020';
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0; }
.row:after,
.clearfix:after {
clear: both; }
.row,
.clearfix {
zoom: 1; }
/* You can also use a <br class="clear" /> to clear columns */
.clear {
clear: both;
display: block;
overflow: hidden;
visibility: hidden;
width: 0;
height: 0;
}
@@ -1,152 +0,0 @@
/*** ESSENTIAL STYLES ***/
.sf-menu, .sf-menu * {
margin: 0;
padding: 0;
list-style: none;
}
.sf-menu {
line-height: 1.0;
}
.sf-menu ul {
position: absolute;
top: -999em;
width: 10em; /* left offset of submenus need to match (see below) */
}
.sf-menu ul li {
width: 100%;
}
.sf-menu li:hover {
visibility: inherit; /* fixes IE7 'sticky bug' */
}
.sf-menu li {
float: left;
position: relative;
}
.sf-menu a {
padding: 5px;
display: block;
position: relative;
}
.sf-menu li:hover ul,
.sf-menu li.sfHover ul {
left: 0;
top: 26px; /* match top ul list item height */
z-index: 99;
}
ul.sf-menu li:hover li ul,
ul.sf-menu li.sfHover li ul {
top: -999em;
}
ul.sf-menu li li:hover ul,
ul.sf-menu li li.sfHover ul {
left: 10em; /* match ul width */
top: 0;
}
ul.sf-menu li li:hover li ul,
ul.sf-menu li li.sfHover li ul {
top: -999em;
}
ul.sf-menu li li li:hover ul,
ul.sf-menu li li li.sfHover ul {
left: 10em; /* match ul width */
top: 0;
}
/*** DEMO SKIN ***/
.sf-menu {
float: left;
/*margin-bottom: 1em;*/
}
.sf-menu a {
padding-left: 1em;
padding-right: 1em;
text-decoration:none;
}
.sf-menu a, .sf-menu a:visited { /* visited pseudo selector so IE6 applies text colour*/
// color: #959595;
background-color: transparent;
font-weight: bold;
}
.sf-menu li {
background-color: transparent;
}
.sf-menu li li {
background: #232323;
text-align: left;
}
.sf-menu li li a {
padding-top: 0.25em;
padding-bottom: 0.25em;
color: #959595;
}
.sf-menu li li li {
background: #232323;
text-align: left;
}
.sf-menu li li li a {
padding-top: 0.25em;
padding-bottom: 0.25em;
color: #959595;
}
.sf-menu li:hover, .sf-menu a:focus, .sf-menu a:active, .sf-menu li.sfHover, .sf-menu a:hover {
color: #FFFFFF;
background-color: #303030;
outline: 0;
}
/*** arrows **/
.sf-menu a.sf-with-ul {
padding-right: 2.25em;
min-width: 1px; /* trigger IE7 hasLayout so spans position accurately */
}
.sf-sub-indicator {
position: absolute;
display: block;
right: .75em;
top: 1.05em; /* IE6 only */
width: 10px;
height: 10px;
text-indent: -999em;
overflow: hidden;
background: url('../images/arrows-ffffff.png') no-repeat -10px -100px; /* 8-bit indexed alpha png. IE6 gets solid image only */
}
a > .sf-sub-indicator { /* give all except IE6 the correct values */
top: .8em;
background-position: 0 -100px; /* use translucent arrow for modern browsers*/
}
/* apply hovers to modern browsers */
a:focus > .sf-sub-indicator,
a:hover > .sf-sub-indicator,
a:active > .sf-sub-indicator,
li:hover > a > .sf-sub-indicator,
li.sfHover > a > .sf-sub-indicator {
background-position: -10px -100px; /* arrow hovers for modern browsers*/
}
/* point right for anchors in subs */
.sf-menu ul .sf-sub-indicator { background-position: -10px 0; }
.sf-menu ul a > .sf-sub-indicator { background-position: 0 0; }
/* apply hovers to modern browsers */
.sf-menu ul a:focus > .sf-sub-indicator,
.sf-menu ul a:hover > .sf-sub-indicator,
.sf-menu ul a:active > .sf-sub-indicator,
.sf-menu ul li:hover > a > .sf-sub-indicator,
.sf-menu ul li.sfHover > a > .sf-sub-indicator {
background-position: -10px 0; /* arrow hovers for modern browsers*/
}
/*** shadows for all but IE6 ***/
.sf-shadow ul {
background: url('../images/shadow.png') no-repeat bottom right;
padding: 0 8px 9px 0;
-moz-border-radius-bottomleft: 17px;
-moz-border-radius-topright: 17px;
-webkit-border-top-right-radius: 17px;
-webkit-border-bottom-left-radius: 17px;
}
.sf-shadow ul.sf-shadow-off {
background: transparent;
}
@@ -0,0 +1,232 @@
/*=============================================================
CUSTOM RULES
==============================================================*/
body{height:auto;} /* to avoid vertical scroll bar */
div.flash.flash-center{left:25%;right:25%;}
div.flash.flash-top,div.flash.flash-top:hover{
position:relative;
display:block;
margin:0;
padding:1em;
top:0;
left:0;
width:100%;
text-align:center;
text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);
color:#865100;
background:#feea9a;
border:1px solid;
border-top:0px;
border-left:0px;
border-right:0px;
border-radius:0;
opacity:1;
}
#header{margin-top:60px;}
.mastheader h1 {
margin-bottom:9px;
font-size:81px;
font-weight:bold;
letter-spacing:-1px;
line-height:1;
font-size:54px;
}
.mastheader small {
font-size:20px;
font-weight:300;
}
/* auth navbar - primitive style */
.auth_navbar,.auth_navbar a{color:inherit;}
.ie-lte7 .auth_navbar,.auth_navbar a{color:expression(this.parentNode.currentStyle['color']); /* ie7 doesn't support inherit */}
.auth_navbar a{white-space:nowrap;} /* to avoid the nav split on more lines */
.auth_navbar a:hover{color:white;text-decoration:none;}
ul#navbar>.auth_navbar{
display:inline-block;
padding:5px;
}
/* form errors message box customization */
div.error_wrapper{margin-bottom:9px;}
div.error_wrapper .error{
border-radius: 4px;
-o-border-radius: 4px;
-moz-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 .help-inline{color:#3A87AD;}
div.controls .error_wrapper +.help-inline {margin-left:-99999px;}
div.controls select +.error_wrapper {margin-left:5px;}
.ie-lte7 div.error{color:#fff;}
/* 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;}
/*=============================================================
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 */
textarea { width:90%}
.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;
}
/*=============================================================
OTHER RULES
==============================================================*/
/* 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{top:5px;}
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 {
margin-left:-10px;
margin-right:-10px;
}
input[type="text"],input[type="password"],select{
width:95%;
}
}
@@ -0,0 +1,122 @@
/*=============================================================
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 {
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #000000;
content: "";
display: inline-block;
height: 0;
opacity: 0.7;
vertical-align: top;
width: 0;
margin-left: 2px;
margin-top: 8px;
border-bottom-color: #FFFFFF;
border-top-color: #FFFFFF;
}
.dropdown-menu span{display:inline-block;}
ul.dropdown-menu li.dropdown > a:after {
border-left: 4px solid #000;
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-left: 8px;
margin-top: 6px;
}
ul.nav li.dropdown:hover ul.dropdown-menu {
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: 478 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+44
View File
@@ -0,0 +1,44 @@
/**
Created and copyrighted by Massimo Di Pierro <massimo.dipierro@gmail.com>
(MIT license)
Example:
<script src="share.js"></script>
**/
jQuery(function(){
var script_source = jQuery('script[src*="share.js"]').attr('src');
var params = function(name,default_value) {
var match = RegExp('[?&]' + name + '=([^&]*)').exec(script_source);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '))||default_value;
}
var path = params('static','social');
var url = encodeURIComponent(window.location.href);
var host = window.location.hostname;
var title = escape(jQuery('title').text());
var twit = 'http://twitter.com/home?status='+title+'%20'+url;
var facebook = 'http://www.facebook.com/sharer.php?u='+url;
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="'+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.
jQuery('body').append(tbar);
var st = jQuery('#socialdrawer');
st.css({'opacity':'.7','z-index':'3000','background':'#FFF','border':'solid 1px #666','border-width':' 1px 0 0 1px','height':'20px','width':'40px','position':'fixed','bottom':'0','right':'0','padding':'2px 5px','overflow':'hidden','-webkit-border-top-left-radius':' 12px','-moz-border-radius-topleft':' 12px','border-top-left-radius':' 12px','-moz-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','-webkit-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)'});
jQuery('#socialdrawer a').css({'float':'left','width':'32px','margin':'3px 2px 2px 2px','padding':'0','cursor':'pointer'});
jQuery('#socialdrawer span').css({'float':'left','margin':'2px 3px','text-shadow':' 1px 1px 1px #FFF','color':'#444','font-size':'12px','line-height':'1em'});
jQuery('#socialdrawer img').hide();
// hover
st.click(function(){
jQuery(this).animate({height:'40px', width:'160px', opacity: 0.95}, 300);
jQuery('#socialdrawer img').show();
});
//leave
st.mouseleave(function(){
st.animate({height:'20px', width: '40px', opacity: .7}, 300);
jQuery('#socialdrawer img').hide();
return false;
} );
});
@@ -1,223 +0,0 @@
/*
* Superfish v1.4.8 - jQuery menu widget
* Copyright (c) 2008 Joel Birch
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* CHANGELOG: http://users.tpg.com.au/j_birch/plugins/superfish/changelog.txt
*/
;(function($){
$.fn.superfish = function(op){
var sf = $.fn.superfish,
c = sf.c,
$arrow = $(['<span class="',c.arrowClass,'"> &#187;</span>'].join('')),
over = function(){
var $$ = $(this), menu = getMenu($$);
clearTimeout(menu.sfTimer);
$$.showSuperfishUl().siblings().hideSuperfishUl();
},
out = function(){
var $$ = $(this), menu = getMenu($$), o = sf.op;
clearTimeout(menu.sfTimer);
menu.sfTimer=setTimeout(function(){
o.retainPath=($.inArray($$[0],o.$path)>-1);
$$.hideSuperfishUl();
if (o.$path.length && $$.parents(['li.',o.hoverClass].join('')).length<1){over.call(o.$path);}
},o.delay);
},
getMenu = function($menu){
var menu = $menu.parents(['ul.',c.menuClass,':first'].join(''))[0];
sf.op = sf.o[menu.serial];
return menu;
},
addArrow = function($a){ $a.addClass(c.anchorClass).append($arrow.clone()); };
return this.each(function() {
var s = this.serial = sf.o.length;
var o = $.extend({},sf.defaults,op);
o.$path = $('li.'+o.pathClass,this).slice(0,o.pathLevels).each(function(){
$(this).addClass([o.hoverClass,c.bcClass].join(' '))
.filter('li:has(ul)').removeClass(o.pathClass);
});
sf.o[s] = sf.op = o;
$('li:has(ul)',this)[($.fn.hoverIntent && !o.disableHI) ? 'hoverIntent' : 'hover'](over,out).each(function() {
if (o.autoArrows) addArrow( $('>a:first-child',this) );
})
.not('.'+c.bcClass)
.hideSuperfishUl();
var $a = $('a',this);
$a.each(function(i){
var $li = $a.eq(i).parents('li');
$a.eq(i).focus(function(){over.call($li);}).blur(function(){out.call($li);});
});
o.onInit.call(this);
}).each(function() {
var menuClasses = [c.menuClass];
if (sf.op.dropShadows && !($.browser.msie && $.browser.version < 7)) menuClasses.push(c.shadowClass);
$(this).addClass(menuClasses.join(' '));
});
};
var sf = $.fn.superfish;
sf.o = [];
sf.op = {};
sf.IE7fix = function(){
var o = sf.op;
if ($.browser.msie && $.browser.version > 6 && o.dropShadows && o.animation.opacity!=undefined)
this.toggleClass(sf.c.shadowClass+'-off');
};
sf.c = {
bcClass : 'sf-breadcrumb',
menuClass : 'sf-js-enabled',
anchorClass : 'sf-with-ul',
arrowClass : 'sf-sub-indicator',
shadowClass : 'sf-shadow'
};
sf.defaults = {
hoverClass : 'sfHover',
pathClass : 'overideThisToUse',
pathLevels : 1,
delay : 800,
animation : {opacity:'show'},
speed : 'normal',
autoArrows : true,
dropShadows : true,
disableHI : false, // true disables hoverIntent detection
onInit : function(){}, // callback functions
onBeforeShow: function(){},
onShow : function(){},
onHide : function(){}
};
$.fn.extend({
hideSuperfishUl : function(){
var o = sf.op,
not = (o.retainPath===true) ? o.$path : '';
o.retainPath = false;
var $ul = $(['li.',o.hoverClass].join(''),this).add(this).not(not).removeClass(o.hoverClass)
.find('>ul').hide().css('visibility','hidden');
o.onHide.call($ul);
return this;
},
showSuperfishUl : function(){
var o = sf.op,
sh = sf.c.shadowClass+'-off',
$ul = this.addClass(o.hoverClass)
.find('>ul:hidden').css('visibility','visible');
sf.IE7fix.call($ul);
o.onBeforeShow.call($ul);
$ul.animate(o.animation,o.speed,function(){ sf.IE7fix.call($ul); o.onShow.call($ul); });
return this;
}
});
})(jQuery);
/*
* Supersubs v0.2b - jQuery plugin
* Copyright (c) 2008 Joel Birch
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*
* This plugin automatically adjusts submenu widths of suckerfish-style menus to that of
* their longest list item children. If you use this, please expect bugs and report them
* to the jQuery Google Group with the word 'Superfish' in the subject line.
*
*/
;(function($){ // $ will refer to jQuery within this closure
$.fn.supersubs = function(options){
var opts = $.extend({}, $.fn.supersubs.defaults, options);
// return original object to support chaining
return this.each(function() {
// cache selections
var $$ = $(this);
// support metadata
var o = $.meta ? $.extend({}, opts, $$.data()) : opts;
// get the font size of menu.
// .css('fontSize') returns various results cross-browser, so measure an em dash instead
var fontsize = $('<li id="menu-fontsize">&#8212;</li>').css({
'padding' : 0,
'position' : 'absolute',
'top' : '-999em',
'width' : 'auto'
}).appendTo($$).width(); //clientWidth is faster, but was incorrect here
// remove em dash
$('#menu-fontsize').remove();
// cache all ul elements
$ULs = $$.find('ul');
// loop through each ul in menu
$ULs.each(function(i) {
// cache this ul
var $ul = $ULs.eq(i);
// get all (li) children of this ul
var $LIs = $ul.children();
// get all anchor grand-children
var $As = $LIs.children('a');
// force content to one line and save current float property
var liFloat = $LIs.css('white-space','nowrap').css('float');
// remove width restrictions and floats so elements remain vertically stacked
var emWidth = $ul.add($LIs).add($As).css({
'float' : 'none',
'width' : 'auto'
})
// this ul will now be shrink-wrapped to longest li due to position:absolute
// so save its width as ems. Clientwidth is 2 times faster than .width() - thanks Dan Switzer
.end().end()[0].clientWidth / fontsize;
// add more width to ensure lines don't turn over at certain sizes in various browsers
emWidth += o.extraWidth;
// restrict to at least minWidth and at most maxWidth
if (emWidth > o.maxWidth) { emWidth = o.maxWidth; }
else if (emWidth < o.minWidth) { emWidth = o.minWidth; }
emWidth += 'em';
// set ul to width in ems
$ul.css('width',emWidth);
// restore li floats to avoid IE bugs
// set li width to full width of this ul
// revert white-space to normal
$LIs.css({
'float' : liFloat,
'width' : '100%',
'white-space' : 'normal'
})
// update offset position of descendant ul to reflect new width of parent
.each(function(){
var $childUl = $('>ul',this);
var offsetDirection = $childUl.css('left')!==undefined ? 'left' : 'right';
$childUl.css(offsetDirection,$ul.width());
});
});
});
};
// expose defaults
$.fn.supersubs.defaults = {
minWidth : 9, // requires em unit.
maxWidth : 25, // requires em unit.
extraWidth : 0 // extra width can ensure lines don't sometimes turn over due to slight browser differences in how they round-off values
};
})(jQuery); // plugin code ends
/**
* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+
* <http://cherne.net/brian/resources/jquery.hoverIntent.html>
*
* @param f onMouseOver function || An object with configuration options
* @param g onMouseOut function || Nothing (use configuration options object)
* @author Brian Cherne brian(at)cherne(dot)net
*/
(function($){$.fn.hoverIntent=function(f,g){var cfg={sensitivity:7,interval:100,timeout:0};cfg=$.extend(cfg,g?{over:f,out:g}:f);var cX,cY,pX,pY;var track=function(ev){cX=ev.pageX;cY=ev.pageY};var compare=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);if((Math.abs(pX-cX)+Math.abs(pY-cY))<cfg.sensitivity){$(ob).unbind("mousemove",track);ob.hoverIntent_s=1;return cfg.over.apply(ob,[ev])}else{pX=cX;pY=cY;ob.hoverIntent_t=setTimeout(function(){compare(ev,ob)},cfg.interval)}};var delay=function(ev,ob){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t);ob.hoverIntent_s=0;return cfg.out.apply(ob,[ev])};var handleHover=function(e){var ev=jQuery.extend({},e);var ob=this;if(ob.hoverIntent_t){ob.hoverIntent_t=clearTimeout(ob.hoverIntent_t)}if(e.type=="mouseenter"){pX=ev.pageX;pY=ev.pageY;$(ob).bind("mousemove",track);if(ob.hoverIntent_s!=1){ob.hoverIntent_t=setTimeout(function(){compare(ev,ob)},cfg.interval)}}else{$(ob).unbind("mousemove",track);if(ob.hoverIntent_s==1){ob.hoverIntent_t=setTimeout(function(){delay(ev,ob)},cfg.timeout)}}};return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover)}})(jQuery);
@@ -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');
});
Binary file not shown.
@@ -5,38 +5,32 @@
<h2>web2py<sup style="font-size:0.5em;">TM</sup> Download</h2>
<div style="width: 90%; margin: 0 auto;">
<center>
<center style="padding:20px">
<table class="downloads">
<tr>
<th>Current ({{="%s.%s.%s %s" % (version[0],version[1],version[2],version[4])}})</th>
<th>Nightly Build (for testers)</th>
<th>Trunk (for developers)</th>
<th>plugin_wiki (add-on)</th>
<th>For Normal Users</th>
<th>For Testers</th>
<th>For Developers</th>
</tr>
<tr>
<td><a class="button" href="http://www.web2py.com/examples/static/web2py_win.zip">For Windows</a></td>
<td><a class="button" href="http://www.web2py.com/examples/static/nightly/web2py_win.zip">For Windows</a></td>
<td><a class="button" href="http://github.com/web2py/web2py/" target="_blank">Git Repository</a></td>
<td><a class="button" href="http://web2py.com/examples/static/web2py.plugin.wiki.w2p">Download</a></td>
<td><a class="btn btn-180 btn-success" href="http://www.web2py.com/examples/static/web2py_win.zip">For Windows</a></td>
<td><a class="btn btn-180 btn-warning" href="http://www.web2py.com/examples/static/nightly/web2py_win.zip">For Windows</a></td>
<td><a class="btn btn-180 btn-danger" href="http://github.com/web2py/web2py/">Git Repository</a></td>
</tr>
<tr>
<td><a class="button" href="http://www.web2py.com/examples/static/web2py_osx.zip">For Mac</a></td>
<td><a class="button" href="http://www.web2py.com/examples/static/nightly/web2py_osx.zip">For Mac</a></td>
<td><a class="button" href="http://code.google.com/p/web2py/" target="_blank">Mercurial Repository</a></td>
<td><a class="button" href="http://code.google.com/p/cube2py/" target="_blank">Mercurial Repository</a>
<td><a class="btn btn-180 btn-success" href="http://www.web2py.com/examples/static/web2py_osx.zip">For Mac</a></td>
<td><a class="btn btn-180 btn-warning" href="http://www.web2py.com/examples/static/nightly/web2py_osx.zip">For Mac</a></td>
<td><a class="btn btn-180 btn-danger" href="http://code.google.com/p/web2py/">Mercurial Repository</a></td>
</tr>
<tr>
<td><a class="button" href="http://www.web2py.com/examples/static/web2py_src.zip">Source Code</a></td>
<td><a class="button" href="http://www.web2py.com/examples/static/nightly/web2py_src.zip">Source Code</a></td>
<td><a class="button" href="{{=URL('static', 'epydoc/index.html')}}" target="_blank">Source Code Docs</a></td>
<td><a class="button" href="http://vimeo.com/13485916" target="_blank">What is plugin_wiki?</a></td>
<td><a class="btn btn-180 btn-success" href="http://www.web2py.com/examples/static/web2py_src.zip">Source Code</a></td>
<td><a class="btn btn-180 btn-warning" href="http://www.web2py.com/examples/static/nightly/web2py_src.zip">Source Code</a></td>
<td><a class="btn btn-180 btn-danger" href="{{=URL('static', 'epydoc/index.html')}}">Epydoc</a></td>
</tr>
<tr>
<td><a class="button" href="{{=URL('changelog')}}">Change Log</a></td>
<td><a class="button" href="http://www.web2py.com/examples/static/nightly/tests.log">Unittest Log</a></td>
<td><a class="button" href="http://code.google.com/p/web2py/issues/list" target="_blank">Issue Tracker</a></td>
<td></td>
<td><a class="btn btn-180 btn-success" href="https://dl.dropbox.com/u/18065445/web2py/web2py_manual_5th.pdf">Manual</a></td>
<td><a class="btn btn-180" href="{{=URL('changelog')}}">Change Log</a></td>
<td><a class="btn btn-180" href="http://code.google.com/p/web2py/issues/list">Report a Bug</a></td>
</tr>
</table>
</center>
@@ -64,28 +58,28 @@
<p>Applications built with web2py can be released under any license the author wishes as long they do not contain web2py code. They can link unmodified web2py libraries and they can be distributed with official web2py binaries. In particular web2py applications can be distributed in closed source. The admin interface provides a button to byte-code compile.</p>
<p>It is fine to distribute web2py (source or compiled) with your applications as long as you make it clear in the license where your application ends and web2py starts.</p>
<p>web2py is copyrighted by Massimo Di Pierro. The web2py trademark is owned by Massimo Di Pierro.</p>
[<a href="{{=URL('license')}}">read more</a>]
<a class="btn btn-small" href="{{=URL('license')}}">read more</a>
<h3>Artwork</h3>
<center>
<a href="{{=URL('static', 'images/logo_lb.png')}}"><img src="{{=URL('static', 'images/logo_lb.png')}}" width="200px"/></a>
<a href="{{=URL('static', 'images/logo_db.png')}}"><img src="{{=URL('static', '\
images/logo_db.png')}}" width="200px"/></a>
<a href="{{=URL('static', 'images/logo_bw.png')}}"><img src="{{=URL('static', '\
images/logo_bw.png')}}" width="200px"/></a>
</center>
<h3>Stickers</h3>
<center>
<a href="{{=URL('static', 'images/Stickers1.png')}}"><img src="{{=URL('static', 'images/Stickers1.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers2.png')}}"><img src="{{=URL('static', 'images/Stickers2.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers3.png')}}"><img src="{{=URL('static', 'images/Stickers3.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers4.png')}}"><img src="{{=URL('static', 'images/Stickers4.png')}}" /></a>
<center>
<a href="{{=URL('static', 'images/logo_lb.png')}}"><img src="{{=URL('static', 'images/logo_lb.png')}}" width="200px"/></a>
<a href="{{=URL('static', 'images/logo_db.png')}}"><img src="{{=URL('static', '\
images/logo_db.png')}}" width="200px"/></a>
<a href="{{=URL('static', 'images/logo_bw.png')}}"><img src="{{=URL('static', '\
images/logo_bw.png')}}" width="200px"/></a>
</center>
<h3>Stickers</h3>
<center>
<a href="{{=URL('static', 'images/Stickers1.png')}}"><img src="{{=URL('static', 'images/Stickers1.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers2.png')}}"><img src="{{=URL('static', 'images/Stickers2.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers3.png')}}"><img src="{{=URL('static', 'images/Stickers3.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers4.png')}}"><img src="{{=URL('static', 'images/Stickers4.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers5.png')}}"><img src="{{=URL('static', 'images/Stickers5.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers6.png')}}"><img src="{{=URL('static', 'images/Stickers6.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers7.png')}}"><img src="{{=URL('static', 'images/Stickers7.png')}}" /></a>
<a href="{{=URL('static', 'images/Stickers8.png')}}"><img src="{{=URL('static', 'images/Stickers8.png')}}" /></a>
</center>
<a href="{{=URL('static', 'images/Stickers8.png')}}"><img src="{{=URL('static', 'images/Stickers8.png')}}" /></a>
</center>
<p></p>
<p>
<a href="{{=URL('static', 'artwork.tar.gz')}}" >Download WEB2PY artwork pack in editable .png format</a>
</p>
@@ -99,6 +93,3 @@
Icon set made by <a href="http://chrfb.deviantart.com">Christian Burprich</a> licensed under a <a rel="license" target="_blank" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 License</a>
</p>
{{block sidebar}}{{end}}
{{block leftbadges}}{{end}}
</div>
@@ -119,7 +119,6 @@ def raiseexception():
return 'oops'
""".strip(),language='web2py',link=URL('global','vars'),_class='boxCode')}}
<p>If an exception occurs (other than HTTP) a ticket is generated and the event is logged for the administrator. These tickets and logs can be accessed, reviewed and deleted at any later time.
<br/>Try it here: <a href="/{{=request.application}}/simple_examples/raiseexception">raiseexception</a></p>
<h3>Example {{=c}}{{c+=1}}</h3><b>In controller: simple_examples.py</b>
{{=CODE("""
@@ -605,7 +604,7 @@ def fade():
<h3>Excel-like spreadsheet via Ajax</h3>
Web2py includes a widget that acts like an Excel-like spreadsheet and can be used to build forms
[<a href="{{=URL('spreadsheet','index')}}">read more</a>].
<a class="btn btn-small" href="{{=URL('spreadsheet','index')}}">read more</a>.
<h2 id="testing_examples">Testing Examples</h2>
+56 -60
View File
@@ -1,4 +1,3 @@
{{right_sidebar_enabled=True}}
{{extend 'layout.html'}}
{{
import random
@@ -14,73 +13,70 @@ quotes = [
random.shuffle(quotes)
}}
<table width="100%">
<tr>
<td>
<a href="http://web2py.com/book">
<img src="{{=URL('static','images/book-4th.png')}}" />
</a>
</td>
<td>
<a href="http://link.packtpub.com/SUlnrN">
<img src="{{=URL('static','images/book-recipes.png')}}" />
</a>
</td>
<td>
<a href="http://www.youtube.com/playlist?list=PL5E2E223FE3777851">
<img src="{{=URL('static','images/videos.png')}}" />
</a>
</td>
</tr>
</table>
<h3>WEB2PY<sup>TM</sup> WEB FRAMEWORK</h3>
<p>Free open source full-stack framework for rapid development of fast, scalable, <a href="http://www.web2py.com/book/default/chapter/01#security" target="_blank">secure</a> and portable database-driven web-based applications. Written and programmable in <a href="http://www.python.org" target="_blank">Python</a>. <a href="http://www.gnu.org/licenses/lgpl.html">LGPLv3 License</a>.
Current version: {{="%s.%s.%s (%s) %s" % request.env.web2py_version}}</p>
{{block extra}}
<div class="container aboutW2P">
<div class="sixteen columns">
<div class="one-third column alpha">
<h3><a href="{{=URL('what')}}">BATTERIES INCLUDED</a></h3>
<p>Everything you need in one package including fast multi-threaded web server, SQL database and web-based interface. No third party dependencies but works with <a href={{=URL('what')}}>third party tools</a>.</p>
<div class="row-fluid">
<div class="span12">
<div class="span8">
<h3>web2py<sup>TM</sup> Web Framework</h3>
<p>Free open source full-stack framework for rapid development of fast, scalable, <a href="http://www.web2py.com/book/default/chapter/01#Security" target="_blank">secure</a> and portable database-driven web-based applications. Written and programmable in <a href="http://www.python.org" target="_blank">Python</a>.</p>
<table width="100%">
<tr>
<td>
<a href="http://web2py.com/book">
<img src="{{=URL('static','images/book-5th.png')}}" />
</a>
</td>
<td>
<a href="http://link.packtpub.com/SUlnrN">
<img src="{{=URL('static','images/book-recipes.png')}}" />
</a>
</td>
<td>
<a href="http://www.youtube.com/playlist?list=PL5E2E223FE3777851">
<img src="{{=URL('static','images/videos.png')}}" />
</a>
</td>
</tr>
</table>
<p>Current version: <a href="{{=URL('download')}}">{{=request.env.web2py_version}} (<a href="http://www.gnu.org/licenses/lgpl.html">LGPLv3 License</a>)</p>
</div>
<div class="one-third column">
<h3><a href="http://web2py.com/demo_admin">WEB-BASED IDE</a></h3>
<p>Create, modify, deploy and manage application from anywhere using your browser. One web2py instance can run multiple web sites using different databases. Try the <a href="http://web2py.com/demo_admin">interactive demo</a>.</p>
</div>
<div class="one-third column omega">
<h3><a href="{{=URL('documentation')}}">EXTENSIVE DOCS</a></h3>
<p>Start with some <a href="{{=URL('examples')}}">quick examples</a>, then read the <a href="http://web2py.com/book" target="_blank">manual</a>, watch <a href="http://vimeo.com/album/178500" target="_blank">videos</a>, and join a <a href="{{=URL('default', 'usergroups')}}">user group</a> for discussion. Take advantage of the <a href="http://web2py.com/layouts" target="_blank">layouts</a>, <a href="http://dev.s-cubism.com/web2py_plugins" target="_blank">plugins</a>, <a href="http://www.web2py.com/appliances" target="_blank">appliances</a>, and <a href="http://web2pyslices.com" target="_blank">recipes</a>.</p>
<div class="span4" style="text-align:center">
<a href="http://www.infoworld.com/slideshow/24605/infoworlds-2012-technology-of-the-year-award-winners-183313#slide23"><img src="{{=URL('static','images/infoworld2012.jpeg')}}" width="200px"/></a><br/>
<a class="btn btn-danger" href="{{=URL('download')}}" style="margin-top:10px; width:180px; color:white">Download Now</a><br/>
<a class="btn btn-danger" href="http://web2py.com/demo_admin" style="margin-top:10px; width:180px; color:white">Online Demo</a><br/>
<a class="btn btn-danger" href="http://web2py.com/poweredby" style="margin-top:10px; width:180px; color:white">Sites Powered by web2py</a>
</div>
</div>
</div>
<img class="scale-with-grid centered" src="/examples/static/images/shadow-bottom.png">
<div class="container userQuotes">
<div class="sixteen columns">
<!-- img src="{{=URL('static','images/shadow-bottom.png')}}" width="100%"/ -->
<div class="row-fluid">
<div class="span12">
<div class="span4">
<h3><a href="{{=URL('what')}}">Batteries Included</a></h3>
<p>Everything you need in one package including fast multi-threaded web server, SQL database and web-based interface. No third party dependencies but works with <a href={{=URL('what')}}>third party tools</a>.</p>
</div>
<div class="span4">
<h3><a href="http://web2py.com/demo_admin">Web-Based IDE</a></h3>
<p>Create, modify, deploy and manage application from anywhere using your browser. One web2py instance can run multiple web sites using different databases. Try the <a href="http://www.web2py.com/demo_admin">interactive demo</a>.</p>
</div>
<div class="span4">
<h3><a href="{{=URL('documentation')}}">Extensive Docs</a></h3>
<p>Start with some <a href="{{=URL('examples')}}">quick examples</a>, then read the <a href="http://www.web2py.com/book" target="_blank">manual</a>, watch <a href="http://vimeo.com/album/178500" target="_blank">videos</a>, and join a <a href="{{=URL('default', 'usergroups')}}">user group</a> for discussion. Take advantage of the <a href="http://www.web2py.com/layouts" target="_blank">layouts</a>, <a href="http://dev.s-cubism.com/web2py_plugins" target="_blank">plugins</a>, <a href="http://www.web2py.com/appliances" target="_blank">appliances</a>, and <a href="http://web2pyslices.com" target="_blank">recipes</a>.</p>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<img class="scale-with-grid centered" src="/examples/static/images/shadow-bottom.png">
</div>
</div>
<div class="row-fluid">
<div class="span12">
{{for k,quote in enumerate(quotes[:3]):}}
<div class="one-third column {{=[' alpha', '', ' omega'][k]}}">
<em>
<p>{{=quote[0]}}</p>
</em>
<div class="span4">
<p style="text-align: left"><em>{{=quote[0]}}</em></p>
<span class="right">
<a href="{{=quote[2]}}"><em>&mdash;{{=quote[1]}}</em></a>
<a href="{{=quote[2]}}">{{=quote[1]}}</a>
</span>
</div>
{{pass}}
</div>
</div>
{{end}}
{{block right_sidebar}}
<div class="one-third column" style="text-align:center">
<img class="scale-with-grid centered" src="{{=URL('static','images/tag-cloud-color-small.png')}}" width="300px"/>
<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/poweredby" style="width:90%">SITES POWERED BY WEB2PY</a><br/>
<a class="button" href="http://www.chipin.com/contribute/id/dcd384d58839aa4d" style="width:90%">SUPPORT/DONATE</a></br>
</div>
{{end}}
+2 -3
View File
@@ -147,8 +147,7 @@
<li><a href="http://www.python.org">Python</a> created by Guido van Rossum.</li>
<li>Rocket Web Server developed by Timothy Farrell.</li>
<li><a href="http://www.cdolivet.com/index.php?page=editArea">EditArea</a> developed by Christophe Dolivet</li>
<li><a href="http://nicedit.com">nicEdit</a> developed by <a href="http://bkirchoff.com">Brian Kirchoff</a></li>
<li><a href="http://codemirror.net/">CodeMirror</a></li>
<li><a href="http://cheeseshop.python.org/pypi/simplejson">simplejson</a> developed by Bob Ippolito</li>
<li><a href="http://pyrtf.sourceforge.net/">PyRTF</a> developed by Simon Cusack and revised by Grant Edwards</li>
<li><a href="http://www.dalkescientific.com/Python/PyRSS2Gen.html">PyRSS2Gen</a> developed by Dalke Scientific Software</li>
@@ -159,8 +158,8 @@
<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 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>
(... and other third party modules in the contrib folder).
</div>
+42 -35
View File
@@ -2,46 +2,53 @@
{{import cgi}}
<div class="contentleft">
<h1>{{=T('Docs for')}} {{=title}}</h1>
<div align="right">
<h1>{{=T('Docs for')}} {{=title}}</h1>
<div align="right">
[ <a href="http://docs.python.org/tut/">Python Tutorial</a> ]
[ <a href="http://docs.python.org/lib/">Python Libraries</a> ]
[ <a href="/{{=request.application}}/static/epydoc/index.html">web2py epydoc</a> ]
</div>
<h2>{{=T('Description')}}</h2>
<br/>
{{if t:}}
{{=t}}{{if d:}} extends {{=d}}{{pass}}
{{pass}}
<br/>
{{pass}}
{{if doc:}}<br/><br/>{{=CODE(str(doc),language=None,counter=None,_class='boxCode')}}{{pass}}
<br/><br/>
<div class="boxInfo">
</div>
<h2>{{=T('Description')}}</h2>
<br/>
{{if t:}}
{{=t}}{{if d:}} extends {{=d}}{{pass}}
{{pass}}
<br/>
{{pass}}
{{if doc:}}<br/><br/>{{=MARKMIN(doc)}}{{pass}}
<br/><br/>
<div class="boxInfo">
{{if attributes:}}
<h2>{{=T('Attributes')}}</h2>
{{keys=attributes.keys(); keys.sort()}}
<table>
<tr><td colspan=2><hr/></td></tr>
{{for a in keys:}}
{{doc1,t1,c1,d1=attributes[a]}}
<tr>
<td><b>{{#=a}}</b>{{=A(a,_href=URL(r=request,args=a.split('.')))}}</td>
<td>
{{if t1:}}
{{=t1}}{{if d1:}} extends {{=d1}}{{pass}}
{{if c1:}} belongs to class {{=c1}}{{pass}}
<br/>
{{pass}}
{{if doc1:}}{{=XML(cgi.escape(str(doc1)).replace(chr(13),'<br/>'))}}{{pass}}
<tr><td colspan=2><hr/></td></tr>
{{for key in sorted(attributes):}}
{{doc1,t1,c1,d1=attributes[key]}}
<tr>
<td>
{{if key.count('.')<2:}}
{{=A(key,_rel="nofollow",_href=URL(args=key.split('.')))}}
{{else:}}
{{=key}}
{{pass}}
</td>
<td>
{{if t1:}}
{{=t1}}{{if d1:}} extends {{=d1}}{{pass}}
{{if c1:}} belongs to class {{=c1}}{{pass}}
<br/>
{{pass}}
{{if doc1:}}{{=MARKMIN(doc1)}}{{pass}}
</td>
</tr>
<tr><td colspan=2><hr/></td></tr>
{{pass}}
</tr>
<tr><td colspan=2><hr/></td></tr>
{{pass}}
</table>
</div>
</div>
{{pass}}
</div>
+126 -97
View File
@@ -1,3 +1,4 @@
<!--[if HTML5]><![endif]-->
<!DOCTYPE html>
<!-- paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/ -->
<!--[if lt IE 7]><html class="ie ie6 ie-lte9 ie-lte8 ie-lte7 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
@@ -6,139 +7,167 @@
<!--[if IE 9]><html class="ie9 ie-lte9 no-js" lang="{{=T.accepted_language or 'en'}}"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html class="no-js" lang="{{=T.accepted_language or 'en'}}"> <!--<![endif]-->
<head>
<meta charset="utf-8" />
<title>{{=response.title or request.application}}</title>
<!--[if !HTML5]>
<meta http-equiv="X-UA-Compatible" content="IE=edge{{=not request.is_local and ',chrome=1' or ''}}">
<![endif]-->
<!-- www.phpied.com/conditional-comments-block-downloads/ -->
<!-- Always force latest IE rendering engine
(even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
<!--[if IE]>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<![endif]-->
<title>{{=response.title or request.application}}</title>
(even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
<meta charset="utf-8" />
<!-- http://dev.w3.org/html5/markup/meta.name.html -->
<meta name="application-name" content="{{=request.application}}" />
<!-- Speaking of Google, don't forget to set your site up:
http://google.com/webmasters -->
http://google.com/webmasters -->
<meta name="google-site-verification" content="" />
<!-- Mobile Viewport Fix
j.mp/mobileviewport & davidbcalhoun.com/2010/viewport-metatag
device-width: Occupy full width of the screen in its current orientation
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
user-scalable = yes allows the user to zoom in -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<!-- 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="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
j.mp/mobileviewport & davidbcalhoun.com/2010/viewport-metatag
device-width: Occupy full width of the screen in its current orientation
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
user-scalable = yes allows the user to zoom in -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- All JavaScript at the bottom, except for Modernizr which enables
HTML5 elements & feature detects -->
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
<link rel="shortcut icon" href="{{=URL('static','images/favicon.ico')}}" type="image/x-icon">
<link rel="apple-touch-icon" href="{{=URL('static','images/favicon.png')}}">
<!-- All JavaScript at the bottom, except for Modernizr which enables
HTML5 elements & feature detects -->
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script>
<!-- include stylesheets -->
{{
response.files.append(URL('static','css/skeleton.css'))
{{
response.files.append(URL('static','css/web2py.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/web2py_bootstrap.css'))
response.files.append(URL('static','css/examples.css'))
response.files.append(URL('static','css/superfish.css'))
response.files.append(URL('static','js/superfish.js'))
}}
{{include 'web2py_ajax.html'}}
<script type="text/javascript">
jQuery(function(){jQuery('.sf-menu').superfish();});
</script>
{{
# using sidebars need to know what sidebar you want to use
left_sidebar_enabled = globals().get('left_sidebar_enabled',False)
right_sidebar_enabled = globals().get('right_sidebar_enabled',False)
middle_columns = {0:'span12',1:'span9',2:'span6'}[
(left_sidebar_enabled and 1 or 0)+(right_sidebar_enabled and 1 or 0)]
}}
<!-- uncomment here to load jquery-ui
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" type="text/css" media="all" />
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
uncomment to load jquery-ui //-->
</style>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" type="text/css" media="all" />
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
uncomment to load jquery-ui //-->
<noscript><link href="{{=URL('static', 'css/web2py_bootstrap_nojs.css')}}" rel="stylesheet" type="text/css" /></noscript>
{{block head}}{{end}}
</head>
<body>
<div class="wrapper"><!-- for sticky footer -->
<div class="flash">{{=response.flash or ''}}</div>
<div class="header">
<!-- Navbar ================================================== -->
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="flash">{{=response.flash or ''}}</div>
<div class="navbar-inner">
<div class="container">
<div class="sixteen columns">
<img src="{{=URL('static','images/web2py_logo.png')}}" class="logo" alt="web2py logo" />
<h5>{{=response.subtitle or ''}}</h5>
</div>
<div class="sixteen columns statusbar">
{{block statusbar}}
{{is_mobile=request.user_agent().is_mobile}}
<div id="menu" class="clearfix">{{=MENU(response.menu,_class='mobile-menu' if is_mobile else 'sf-menu',mobile=is_mobile)}}
{{end}}
<!-- AddToAny BEGIN -->
<div style="float:right;padding-top:6px;" class="a2a_kit a2a_default_style">
<a class="a2a_dd" href="http://www.addtoany.com/share_save">Share</a></div>
<script type="text/javascript" src="http://static.addtoany.com/menu/page.js"></script>
<!-- AddToAny END -->
</div>
</div>
<div class="sixteen columns announce">
<a href="http://www.infoworld.com/slideshow/24605/infoworlds-2012-technology-of-the-year-award-winners-183313#slide23"target="_blank">InfoWorld's 2012 Technology of the Year Award Winner</a>
</div>
<!-- the next tag is necessary for bootstrap menus, do not remove -->
<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>
</button>
{{=response.logo or ''}}
<ul id="navbar" class="nav pull-right">{{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}</ul>
<div class="nav-collapse">
{{is_mobile=request.user_agent().is_mobile}}
{{if response.menu:}}
{{=MENU(response.menu, _class='mobile-menu nav' if is_mobile else 'nav',mobile=is_mobile,li_class='dropdown',ul_class='dropdown-menu')}}
{{pass}}
</div><!--/.nav-collapse -->
</div>
</div>
<div class="main">
<div class="container mainbody">
<div class="sixteen columns">
<div class="{{=right_sidebar_enabled and 'two-thirds column alpha' or 'sixteen columns alpha omega'}}">
</div><!--/top navbar -->
<div class="container">
<!-- Masthead ================================================== -->
<header class="mastheader" id="header">
<div class="span4">
<div class="page-header">
<img src="{{=URL('static','images/web2py_logo.png')}}" class="logo" alt="web2py logo" />
</div>
</div>
</header>
</div>
<div class="container">
<section id="main" class="main row">
{{if left_sidebar_enabled:}}
<div class="span3 left-sidebar">
{{block left_sidebar}}
<h3>Left Sidebar</h3>
<p></p>
{{end}}
</div>
{{pass}}
<div class="{{=middle_columns}}">
{{block center}}
{{include}}
{{end}}
</div>
{{if right_sidebar_enabled:}}
<div class="one-third column omega">
</div>
{{if right_sidebar_enabled:}}
<div class="span3">
{{block right_sidebar}}
<h3>Right Sidebar</h3>
<p></p>
{{end}}
</div>
{{pass}}
</div>
</div><!-- container -->
{{block extra}}{{end}}
</div><!-- main -->
<div class="push"></div>
</div><!-- wrapper -->
{{pass}}
</section><!--/main-->
<div class="footer">
<div class="container header">
<div class="sixteen columns">
{{block footer}} <!-- this is default footer -->
<div class="footer-content" >
{{=T('Copyright')}} &#169; {{=request.now.year}}
- User communities in <a href="https://groups.google.com/forum/?fromgroups#!forum/web2py" target="_blank">English<a>, <a href="https://groups.google.com/forum/?fromgroups#!forum/web2py-fr" target="_blank">French</a>, <a href="https://groups.google.com/forum/?fromgroups#!forum/web2py-japan" target="_blank">Japanese</a>, <a href="https://groups.google.com/forum/?fromgroups#!forum/web2py-users-brazil" target="_blank">Portuguese</a>, and <a href="https://groups.google.com/forum/?fromgroups#!forum/web2py-usuarios" target="_blank">Spanish</a>.
<div style="float: right;">
<a href="http://www.web2py.com/" style="float: left; padding-right: 6px;">
<img style="padding-bottom: 0;" src="{{=URL('static','images/poweredby.png')}}"/>
</a>
</div>
</div>
{{end}}
</div>
</div><!-- container -->
</div><!-- footer -->
<!-- Footer ================================================== -->
<div class="row">
<footer class="footer span12" id="footer">
<div class="footer-content">
{{block footer}} <!-- this is default footer -->
<div id="poweredBy" class="pull-right">
{{=T('Copyright')}} &#169; {{=request.now.year}} -
{{=T('Powered by')}}
<a href="http://www.web2py.com/">web2py</a> -
{{=T('Hosted by')}}
<a href="http://pythonanywhere.com">PythonAnywhere</a>
</div>
{{end}}
</div>
</footer>
</div>
</div> <!-- /container -->
<!-- The javascript =============================================
(Placed at the end of the document so the pages load faster) -->
<script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
<script src="{{=URL('static','js/web2py_bootstrap.js')}}"></script>
<!--[if lt IE 7 ]>
<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>
<![endif]-->
{{if response.google_analytics_id:}}<script>/* http://mathiasbynens.be/notes/async-analytics-snippet */ var _gaq=[['_setAccount','{{=response.google_analytics_id}}'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')) </script>{{pass}}
<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>
<![endif]-->
{{if response.google_analytics_id:}}
<script src="{{=URL('static','js/analytics.min.js')}}"></script>
<script type="text/javascript">
analytics.initialize({
'Google Analytics':{trackingId:'{{=response.google_analytics_id}}'}
});</script>
{{pass}}
<script src="{{=URL('static','js/share.js',vars=dict(static=URL('static','images')))}}"></script>
<a style="position:fixed;bottom:0;left:0;z-index:1000" href="https://groups.google.com/forum/?fromgroups#!forum/web2py" target="_blank">
<img src="{{=URL('static','images/questions.png')}}" />
</a>
</body>
</html>
@@ -1,5 +1,9 @@
{{extend 'layout.html'}}
<style>
input {width: 70px;}
input:focus {background-color: yellow}
table, td, tr { border: 0; margin: 0; padding: 0}
</style>
<h1>Excel-like spreadsheet widget</h1>
Try insert "=r0c1+1" in cell r0c0 and "2" in r0c1. Formulas start with "=" as in Excel. You can use a subset of python commands and math function, and reference cells by r[row]c[col]. All computations are performed serverside via Ajax (input is validated for security). Cell values and formulas can be set and locked serverside. The shape of the spreadsheet can be modifed serverside and does not need to be tabular (think of it as a graph of css-friendly widgets you can place where you want). Cells can be given arbistrary names. This example is distributed with web2py so look at the source code of the example to learn more.
+4 -1
View File
@@ -319,6 +319,9 @@ def state():
def ccache():
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
@@ -385,7 +388,7 @@ def ccache():
return (hours, minutes, seconds)
for key, value in cache.ram.storage.items():
for key, value in cache.ram.storage.iteritems():
if isinstance(value, dict):
ram['hits'] = value['hit_total'] - value['misses']
ram['misses'] = value['misses']
+72 -66
View File
@@ -4,8 +4,8 @@
'!langcode!': 'it',
'!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 ',
'%(nrows)s records found': '%(nrows)s records found',
'%d seconds ago': '%d seconds ago',
'%(nrows)s records found': '%(nrows)s record trovati',
'%d seconds ago': '%d secondi fa',
'%s %%{row} deleted': '%s righe ("record") cancellate',
'%s %%{row} updated': '%s righe ("record") modificate',
'%s selected': '%s selezionato',
@@ -16,40 +16,41 @@
'=': '=',
'>': '>',
'>=': '>=',
'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**',
'@markmin\x01Number of entries: **%s**': 'Numero di entità: **%s**',
'About': 'About',
'Access Control': 'Access Control',
'Add': 'Add',
'Administrative Interface': 'Administrative Interface',
'Access Control': 'Controllo Accessi',
'Add': 'Aggiungi',
'Administrative Interface': 'Interfaccia Amministrativa',
'Administrative interface': 'Interfaccia amministrativa',
'Ajax Recipes': 'Ajax Recipes',
'And': 'And',
'An error occured, please %s the page': "E' stato rilevato un errore, prego %s la pagina",
'And': 'E',
'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
'Are you sure you want to delete this object?': 'Sicuro di voler cancellare questo oggetto ?',
'Available Databases and Tables': 'Database e tabelle disponibili',
'Back': 'Back',
'Buy this book': 'Buy this book',
'Back': 'Indietro',
'Buy this book': 'Compra questo libro',
'cache': 'cache',
'Cache': 'Cache',
'Cache Keys': 'Cache Keys',
'Cannot be empty': 'Non può essere vuoto',
'Change password': 'Change password',
'Change password': 'Cambia Password',
'change password': 'Cambia password',
'Check to delete': 'Seleziona per cancellare',
'Clear': 'Clear',
'Clear CACHE?': 'Clear CACHE?',
'Clear DISK': 'Clear DISK',
'Clear RAM': 'Clear RAM',
'Clear': 'Resetta',
'Clear CACHE?': 'Resetta CACHE?',
'Clear DISK': 'Resetta DISK',
'Clear RAM': 'Resetta RAM',
'Client IP': 'Client IP',
'Close': 'Close',
'Close': 'Chiudi',
'Cognome': 'Cognome',
'Community': 'Community',
'Components and Plugins': 'Components and Plugins',
'contains': 'contains',
'Components and Plugins': 'Componenti and Plugin',
'contains': 'contiene',
'Controller': 'Controller',
'Copyright': 'Copyright',
'Created By': 'Created By',
'Created On': 'Created On',
'Created By': 'Creato Da',
'Created On': 'Creato Il',
'CSV': 'CSV',
'CSV (hidden cols)': 'CSV (hidden cols)',
'Current request': 'Richiesta (request) corrente',
@@ -61,7 +62,7 @@
'Database %s select': 'Database %s select',
'db': 'db',
'DB Model': 'Modello di DB',
'Delete': 'Delete',
'Delete': 'Cancella',
'Delete:': 'Cancella:',
'Demo': 'Demo',
'Deployment Recipes': 'Deployment Recipes',
@@ -71,7 +72,7 @@
'Disk Cache Keys': 'Disk Cache Keys',
'Disk Cleared': 'Disk Cleared',
'Documentation': 'Documentazione',
"Don't know what to do?": "Don't know what to do?",
"Don't know what to do?": 'Non sai cosa fare?',
'done!': 'fatto!',
'Download': 'Download',
'E-mail': 'E-mail',
@@ -79,18 +80,19 @@
'Edit current record': 'Modifica record corrente',
'edit profile': 'modifica profilo',
'Edit This App': 'Modifica questa applicazione',
'Email and SMS': 'Email and SMS',
'Email and SMS': 'Email e SMS',
'Email non valida': 'Email non valida',
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g',
'Errors': 'Errors',
'Errors in form, please check it out.': 'Errors in form, please check it out.',
'enter an integer between %(min)g and %(max)g': 'inserisci un intero tra %(min)g e %(max)g',
'Errors': 'Errori',
'Errors in form, please check it out.': 'Errori nel form, ricontrollalo',
'export as csv file': 'esporta come file CSV',
'Export:': 'Export:',
'Export:': 'Esporta:',
'FAQ': 'FAQ',
'First name': 'Nome',
'Forgot username?': 'Forgot username?',
'Forgot username?': 'Dimenticato lo username?',
'Forms and Validators': 'Forms and Validators',
'Free Applications': 'Free Applications',
'Graph Model': 'Graph Model',
'Group %(group_id)s created': 'Group %(group_id)s created',
'Group ID': 'ID Gruppo',
'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s',
@@ -100,69 +102,70 @@
'Hello World': 'Salve Mondo',
'Hello World in a flash!': 'Salve Mondo in un flash!',
'Home': 'Home',
'How did you get here?': 'How did you get here?',
'How did you get here?': 'Come sei arrivato qui?',
'HTML': 'HTML',
'import': 'import',
'import': 'importa',
'Import/Export': 'Importa/Esporta',
'Index': 'Indice',
'insert new': 'inserisci nuovo',
'insert new %s': 'inserisci nuovo %s',
'Internal State': 'Stato interno',
'Introduction': 'Introduction',
'Introduction': 'Introduzione',
'Invalid email': 'Email non valida',
'Invalid login': 'Invalid login',
'Invalid login': 'Login non valido',
'Invalid Query': 'Richiesta (query) non valida',
'invalid request': 'richiesta non valida',
'Is Active': 'Is Active',
'Key': 'Key',
'Is Active': "E' attivo",
'Key': 'Chiave',
'Last name': 'Cognome',
'Layout': 'Layout',
'Layout Plugins': 'Layout Plugins',
'Layouts': 'Layouts',
'Live Chat': 'Live Chat',
'Logged in': 'Logged in',
'Logged out': 'Logged out',
'Logged in': 'Loggato',
'Logged out': 'Disconnesso',
'login': 'accesso',
'Login': 'Login',
'logout': 'uscita',
'Logout': 'Logout',
'Lost Password': 'Lost Password',
'Lost password?': 'Lost password?',
'Lost Password': 'Password Smarrita',
'Lost password?': 'Password smarrita?',
'lost password?': 'dimenticato la password?',
'Main Menu': 'Menu principale',
'Manage Cache': 'Manage Cache',
'Menu Model': 'Menu Modelli',
'Modified By': 'Modified By',
'Modified On': 'Modified On',
'Modified By': 'Modificato da',
'Modified On': 'Modificato il',
'My Sites': 'My Sites',
'Name': 'Nome',
'New': 'New',
'New password': 'New password',
'New': 'Nuovo',
'New password': 'Nuova password',
'New Record': 'Nuovo elemento (record)',
'new record inserted': 'nuovo record inserito',
'next 100 rows': 'prossime 100 righe',
'No databases in this application': 'Nessun database presente in questa applicazione',
'No records found': 'No records found',
'No records found': 'Nessun record trovato',
'Nome': 'Nome',
'Non può essere vuoto': 'Non può essere vuoto',
'not authorized': 'non autorizzato',
'Object or table name': 'Object or table name',
'Old password': 'Old password',
'Object or table name': 'Oggeto o nome tabella',
'Old password': 'Vecchia password',
'Online examples': 'Vedere gli esempi',
'Or': 'Or',
'Or': 'O',
'or import from csv file': 'oppure importa da file CSV',
'Origin': 'Origine',
'Other Plugins': 'Other Plugins',
'Other Recipes': 'Other Recipes',
'Overview': 'Overview',
'Password': 'Password',
"Password fields don't match": "Password fields don't match",
'please input your password again': 'please input your password again',
"Password fields don't match": 'I campi password non sono uguali',
'please input your password again': 'perfavore reimmeti la tua password',
'Plugins': 'Plugins',
'Powered by': 'Powered by',
'Preface': 'Preface',
'previous 100 rows': '100 righe precedenti',
'Profile': 'Profile',
'Profile': 'Profilo',
'pygraphviz library not found': 'pygraphviz library not found',
'Python': 'Python',
'Query:': 'Richiesta (query):',
'Quick Examples': 'Quick Examples',
@@ -174,28 +177,30 @@
'record does not exist': 'il record non esiste',
'Record ID': 'Record ID',
'Record id': 'Record id',
'Register': 'Register',
'Register': 'Registrati',
'register': 'registrazione',
'Registration identifier': 'Registration identifier',
'Registration key': 'Chiave di Registazione',
'Registration successful': 'Registration successful',
'Remember me (for 30 days)': 'Remember me (for 30 days)',
'Request reset password': 'Request reset password',
'Registration successful': 'Registrazione avvenuta',
'reload': 'reload',
'Remember me (for 30 days)': 'Ricordami (per 30 giorni)',
'Request reset password': 'Richiedi il reset della password',
'Reset Password key': 'Resetta chiave Password ',
'Role': 'Ruolo',
'Rows in Table': 'Righe nella tabella',
'Rows selected': 'Righe selezionate',
'Save profile': 'Save profile',
'Search': 'Search',
'Save model as...': 'Salva modello come...',
'Save profile': 'Salva profilo',
'Search': 'Ricerca',
'Semantic': 'Semantic',
'Services': 'Services',
'Services': 'Servizi',
'Size of cache:': 'Size of cache:',
'starts with': 'starts with',
'starts with': 'comincia con',
'state': 'stato',
'Statistics': 'Statistics',
'Stylesheet': 'Foglio di stile (stylesheet)',
'submit': 'submit',
'Submit': 'Submit',
'submit': 'Inviai',
'Submit': 'Invia',
'Support': 'Support',
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
'Table': 'tabella',
@@ -208,12 +213,13 @@
'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)",
'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)',
'Timestamp': 'Ora (timestamp)',
'too short': 'too short',
'TSV (Excel compatible)': 'TSV (Excel compatible)',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatible, hidden cols)',
'too short': 'troppo corto',
'Traceback': 'Traceback',
'TSV (Excel compatible)': 'TSV (Excel compatibile)',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatibile, hidden cols)',
'Twitter': 'Twitter',
'unable to parse csv file': 'non riesco a decodificare questo file CSV',
'Update': 'Update',
'Update': 'Aggiorna',
'Update:': 'Aggiorna:',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
'User %(id)s Logged-in': 'User %(id)s Logged-in',
@@ -223,14 +229,14 @@
'User %(id)s Profile updated': 'User %(id)s Profile updated',
'User %(id)s Registered': 'User %(id)s Registered',
'User ID': 'ID Utente',
'value already in database or empty': 'value already in database or empty',
'Verify Password': 'Verify Password',
'value already in database or empty': 'valore già presente nel database o vuoto',
'Verify Password': 'Verifica Password',
'Videos': 'Videos',
'View': 'Vista',
'Welcome': 'Welcome',
'Welcome %s': 'Benvenuto %s',
'Welcome to web2py': 'Benvenuto su web2py',
'Welcome to web2py!': 'Welcome to web2py!',
'Welcome to web2py!': 'Benvenuto in web2py!',
'Which called the function %s located in the file %s': 'che ha chiamato la funzione %s presente nel file %s',
'XML': 'XML',
'You are successfully running web2py': 'Stai eseguendo web2py con successo',
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/
/* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{
+8 -4
View File
@@ -373,12 +373,16 @@ def check_new_version(myversion, version_URL):
the most up-to-version available
"""
try:
from urllib import urlopen
version = parse_version(urlopen(version_URL).read())
except Exception:
from urllib import urlopen
version = urlopen(version_URL).read()
pversion = parse_version(version)
pmyversion = parse_version(myversion)
except Exception,e:
import traceback
print traceback.format_exc()
return -1, myversion
if version > myversion:
if pversion[:3]+pversion[-6:] > pmyversion[:3]+pmyversion[-6:]:
return True, version
else:
return False, version
+87 -3
View File
@@ -27,6 +27,8 @@ import thread
import os
import logging
import re
import hashlib
import datetime
try:
import settings
have_settings = True
@@ -241,7 +243,7 @@ class CacheOnDisk(CacheAbstract):
This is implemented as a shelve object and it is shared by multiple web2py
processes (and threads) as long as they share the same filesystem.
The file is locked wen accessed.
The file is locked when accessed.
Disk cache provides persistance when web2py is started/stopped but it slower
than `CacheInRam`
@@ -376,7 +378,6 @@ class CacheOnDisk(CacheAbstract):
self._close_shelve_and_unlock()
return value
class CacheAction(object):
def __init__(self, func, key, time_expire, cache, cache_model):
self.__name__ = func.__name__
@@ -436,6 +437,89 @@ class Cache(object):
# been accounted for
logger.warning('no cache.disk (AttributeError)')
def client(self, time_expire=DEFAULT_TIME_EXPIRE, cache_model=None,
prefix=None, session=False, vars=True, lang=True,
user_agent=False, public=True, valid_statuses=None,
quick=None):
"""
Experimental!
Currently only HTTP 1.1 compliant
reference : http://code.google.com/p/doctype-mirror/wiki/ArticleHttpCaching
time_expire: same as @cache
cache_model: same as @cache
prefix: add a prefix to the calculated key
session: adds response.session_id to the key
vars: adds request.env.query_string
lang: adds T.accepted_language
user_agent: if True, adds is_mobile and is_tablet to the key.
Pass a dict to use all the needed values (uses str(.items())) (e.g. user_agent=request.user_agent())
used only if session is not True
public: if False forces the Cache-Control to be 'private'
valid_statuses: by default only status codes starting with 1,2,3 will be cached.
pass an explicit list of statuses on which turn the cache on
quick: Session,Vars,Lang,User-agent,Public,duration:
fast overrides with initial strings, e.g. 'SVLP' or 'VLP', or 'VLP'
"""
from gluon import current
def wrap(func):
def wrapped_f():
if current.request.env.request_method == 'GET':
if time_expire:
cache_control = 'max-age=%(time_expire)s, s-maxage=%(time_expire)s' % dict(time_expire=time_expire)
if quick:
session_ = True if 'S' in quick else False
vars_ = True if 'V' in quick else False
lang_ = True if 'L' in quick else False
user_agent_ = True if 'U' in quick else False
public_ = True if 'P' in quick else False
else:
session_, vars_, lang_, user_agent_, public_ = session, vars, lang, user_agent, public
if not session_ and public_:
cache_control += ', public'
expires = (current.request.utcnow + datetime.timedelta(seconds=time_expire)).strftime('%a, %d %b %Y %H:%M:%S GMT')
vary = None
else:
cache_control += ', private'
expires = 'Fri, 01 Jan 1990 00:00:00 GMT'
if cache_model:
cache_key = [current.request.env.path_info, current.response.view]
if session_:
cache_key.append(current.response.session_id)
elif user_agent_:
if user_agent_ is True:
cache_key.append("%(is_mobile)s_%(is_tablet)s" % current.request.user_agent())
else:
cache_key.append(str(user_agent_.items()))
if vars_:
cache_key.append(current.request.env.query_string)
if lang_:
cache_key.append(current.T.accepted_language)
cache_key = hashlib.md5('__'.join(cache_key)).hexdigest()
if prefix:
cache_key = prefix + cache_key
rtn = cache_model(cache_key, lambda : func(), time_expire=time_expire)
else:
rtn = func()
send_headers = False
if isinstance(valid_statuses, list):
if current.response.status in valid_statuses:
send_headers = True
elif valid_statuses is None:
if str(current.response.status)[0] in '123':
send_headers = True
if send_headers:
current.response.headers['Pragma'] = None
current.response.headers['Expires'] = expires
current.response.headers['Cache-Control'] = cache_control
if cache_model and not send_headers:
cache_model.clear(cache_key)
return rtn
return func()
wrapped_f.__name__ = func.__name__
wrapped_f.__doc__ = func.__doc__
return wrapped_f
return wrap
def __call__(self,
key=None,
time_expire=DEFAULT_TIME_EXPIRE,
@@ -469,7 +553,7 @@ class Cache(object):
refresh.
If the function `f` is an action, we suggest using
`request.env.path_info` as key.
@cache.client instead
"""
def tmp(func, cache=self, cache_model=cache_model):
+63 -30
View File
@@ -52,7 +52,7 @@ def set_global(var, val):
class FPDF(object):
"PDF Generation class"
def __init__(self, orientation='P',unit='mm',format='A4'):
# Some checks
self._dochecks()
@@ -356,7 +356,7 @@ class FPDF(object):
elif (self.current_font['desc']['MissingWidth']) :
w += self.current_font['desc']['MissingWidth']
#elif (isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; }
else:
else:
w += 500
else:
for i in xrange(0, l):
@@ -373,6 +373,21 @@ class FPDF(object):
"Draw a line"
self._out(sprintf('%.2f %.2f m %.2f %.2f l S',x1*self.k,(self.h-y1)*self.k,x2*self.k,(self.h-y2)*self.k))
def _set_dash(self, dash_length=False, space_length=False):
if(dash_length and space_length):
s = sprintf('[%.3f %.3f] 0 d', dash_length*self.k, space_length*self.k)
else:
s = '[] 0 d'
self._out(s)
def dashed_line(self, x1,y1,x2,y2, dash_length=1, space_length=1):
"""Draw a dashed line. Same interface as line() except:
- dash_length: Length of the dash
- space_length: Length of the space between dashes"""
self._set_dash(dash_length, space_length)
self.line(x1, y1, x2, y2)
self._set_dash()
def rect(self, x,y,w,h,style=''):
"Draw a rectangle"
if(style=='F'):
@@ -401,10 +416,10 @@ class FPDF(object):
global SYSTEM_TTFONTS
if os.path.exists(fname):
ttffilename = fname
elif (FPDF_FONT_DIR and
elif (FPDF_FONT_DIR and
os.path.exists(os.path.join(FPDF_FONT_DIR, fname))):
ttffilename = os.path.join(FPDF_FONT_DIR, fname)
elif (SYSTEM_TTFONTS and
elif (SYSTEM_TTFONTS and
os.path.exists(os.path.join(SYSTEM_TTFONTS, fname))):
ttffilename = os.path.join(SYSTEM_TTFONTS, fname)
else:
@@ -450,7 +465,7 @@ class FPDF(object):
fh = open(unifilename, "w")
pickle.dump(font_dict, fh)
fh.close()
except IOError as e:
except IOError, e:
if not e.errno == errno.EACCES:
raise # Not a permission error.
del ttf
@@ -459,11 +474,11 @@ class FPDF(object):
else:
sbarr = range(0,32)
self.fonts[fontkey] = {
'i': len(self.fonts)+1, 'type': font_dict['type'],
'name': font_dict['name'], 'desc': font_dict['desc'],
'up': font_dict['up'], 'ut': font_dict['ut'],
'cw': font_dict['cw'],
'ttffile': font_dict['ttffile'], 'fontkey': fontkey,
'i': len(self.fonts)+1, 'type': font_dict['type'],
'name': font_dict['name'], 'desc': font_dict['desc'],
'up': font_dict['up'], 'ut': font_dict['ut'],
'cw': font_dict['cw'],
'ttffile': font_dict['ttffile'], 'fontkey': fontkey,
'subset': sbarr, 'unifilename': unifilename,
}
self.font_files[fontkey] = {'length1': font_dict['originalsize'],
@@ -494,7 +509,7 @@ class FPDF(object):
if (type == 'TrueType'):
self.font_files[filename]={'length1': originalsize}
else:
self.font_files[filename]={'length1': size1,
self.font_files[filename]={'length1': size1,
'length2': size2}
def set_font(self, family,style='',size=0):
@@ -660,7 +675,7 @@ class FPDF(object):
dx=self.c_margin
if(self.color_flag):
s+='q '+self.text_color+' '
# If multibyte, Tw has no effect - do word spacing using an adjustment before each space
if (self.ws and self.unifontsubset):
for uni in UTF8StringToArray(txt):
@@ -686,7 +701,7 @@ class FPDF(object):
else:
txt2 = self._escape(txt)
s += sprintf('BT %.2f %.2f Td (%s) Tj ET',(self.x+dx)*k,(self.h-(self.y+.5*h+.3*self.font_size))*k,txt2)
if(self.underline):
s+=' '+self._dounderline(self.x+dx,self.y+.5*h+.3*self.font_size,txt)
if(self.color_flag):
@@ -764,7 +779,7 @@ class FPDF(object):
sep=i
ls=l
ns+=1
if self.unifontsubset:
if self.unifontsubset:
l += self.get_string_width(c) / self.font_size*1000.0
else:
l += cw.get(c,0)
@@ -848,7 +863,7 @@ class FPDF(object):
continue
if(c==' '):
sep=i
if self.unifontsubset:
if self.unifontsubset:
l += self.get_string_width(c) / self.font_size*1000.0
else:
l += cw.get(c,0)
@@ -900,6 +915,24 @@ class FPDF(object):
info=self._parsepng(name)
else:
#Allow for additional formats
#maybe the image is not showing the correct extension,
#but the header is OK,
succeed_parsing = False
#try all the parsing functions
parsing_functions = [self._parsejpg,self._parsepng,self._parsegif]
for pf in parsing_functions:
try:
info = pf(name)
succeed_parsing = True
break;
except:
pass
#last resource
if not succeed_parsing:
mtd='_parse'+type
if not hasattr(self,mtd):
self.error('Unsupported image type: '+type)
info=getattr(self, mtd)(name)
mtd='_parse'+type
if not hasattr(self,mtd):
self.error('Unsupported image type: '+type)
@@ -1202,7 +1235,7 @@ class FPDF(object):
self._out('<</Type /Font');
self._out('/Subtype /Type0');
self._out('/BaseFont /' + fontname + '');
self._out('/Encoding /Identity-H');
self._out('/Encoding /Identity-H');
self._out('/DescendantFonts [' + str(self.n + 1) + ' 0 R]')
self._out('/ToUnicode ' + str(self.n + 2) + ' 0 R')
self._out('>>')
@@ -1264,7 +1297,7 @@ class FPDF(object):
for kd in ('Ascent', 'Descent', 'CapHeight', 'Flags', 'FontBBox', 'ItalicAngle', 'StemV', 'MissingWidth'):
v = font['desc'][kd]
if (kd == 'Flags'):
v = v | 4;
v = v | 4;
v = v & ~32; # SYMBOLIC font flag
self._out(' /%s %s' % (kd, v))
self._out('/FontFile2 ' + str(self.n + 2) + ' 0 R')
@@ -1286,7 +1319,7 @@ class FPDF(object):
self._putstream(cidtogidmap)
self._out('endobj')
#Font file
#Font file
self._newobj()
self._out('<</Length ' + str(len(fontstream)))
self._out('/Filter /FlateDecode')
@@ -1341,14 +1374,14 @@ class FPDF(object):
font_dict['range'] = range_
pickle.dump(font_dict, fh)
fh.close()
except IOError as e:
except IOError, e:
if not e.errno == errno.EACCES:
raise # Not a permission error.
if (font['cw'][cid] == 0):
continue
width = font['cw'][cid]
if (width == 65535): width = 0
if (cid > 255 and (cid not in font['subset']) or not cid): #
if (cid > 255 and (cid not in font['subset']) or not cid): #
continue
if ('dw' not in font or (font['dw'] and width != font['dw'])):
if (cid == (prevcid + 1)):
@@ -1400,7 +1433,7 @@ class FPDF(object):
if (len(set(ws)) == 1):
w.append(' %s %s %s' % (k, k + len(ws) - 1, ws[0]))
else:
w.append(' %s [ %s ]\n' % (k, ' '.join([str(int(h)) for h in ws]))) ##
w.append(' %s [ %s ]\n' % (k, ' '.join([str(int(h)) for h in ws]))) ##
self._out('/W [%s]' % ''.join(w))
def _putimages(self):
@@ -1412,7 +1445,7 @@ class FPDF(object):
del info['data']
if 'smask' in info:
del info['smask']
def _putimage(self, info):
if 'data' in info:
self._newobj()
@@ -1791,20 +1824,20 @@ class FPDF(object):
def interleaved2of5(self, txt, x, y, w=1.0, h=10.0):
"Barcode I2of5 (numeric), adds a 0 if odd lenght"
narrow = w / 3.0
narrow = w / 3.0
wide = w
# wide/narrow codes for the digits
bar_char={'0': 'nnwwn', '1': 'wnnnw', '2': 'nwnnw', '3': 'wwnnn',
'4': 'nnwnw', '5': 'wnwnn', '6': 'nwwnn', '7': 'nnnww',
'8': 'wnnwn', '9': 'nwnwn', 'A': 'nn', 'Z': 'wn'}
self.set_fill_color(0)
code = txt
# add leading zero if code-length is odd
if len(code) % 2 != 0:
code = '0' + code
# add start and stop codes
code = 'AA' + code.lower() + 'ZA'
@@ -1843,7 +1876,7 @@ class FPDF(object):
narrow = w / 3.0
gap = narrow
bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw',
bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw',
'3': 'wnwwnnnnn', '4': 'nnnwwnnnw', '5': 'wnnwwnnnn',
'6': 'nnwwwnnnn', '7': 'nnnwnnwnw', '8': 'wnnwnnwnn',
'9': 'nnwwnnwnn', 'A': 'wnnnnwnnw', 'B': 'nnwnnwnnw',
@@ -1860,8 +1893,8 @@ class FPDF(object):
'+': 'nwnnnwnwn', '%': 'nnnwnwnwn'}
self.set_fill_color(0)
code = txt
code = txt
code = code.upper()
for i in xrange (0, len(code), 2):
char_bar = code[i]
@@ -1871,7 +1904,7 @@ class FPDF(object):
seq= ''
for s in xrange(0, len(bar_char[char_bar])):
seq += bar_char[char_bar][s]
seq += bar_char[char_bar][s]
for bar in xrange(0, len(seq)):
if seq[bar] == 'n':
@@ -15,8 +15,6 @@ Dependencies:
import oauth2 as oauth
import cgi
from urllib2 import urlopen
import urllib2
from urllib import urlencode
from gluon import current
@@ -64,9 +62,7 @@ class OAuthAccount(object):
Appends the _next action to the generated url so the flows continues.
"""
r = self.request
http_host = r.env.http_x_forwarded_for
if not http_host:
http_host = r.env.http_host
http_host = r.env.http_host
url_scheme = r.env.wsgi_url_scheme
if next:
@@ -103,9 +103,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
"""
r = current.request
http_host = r.env.http_x_forwarded_for
if not http_host:
http_host = r.env.http_host
http_host = r.env.http_host
if r.env.https == 'on':
url_scheme = 'https'
@@ -163,7 +161,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
open_url = None
opener = self.__build_url_opener(self.token_url)
try:
open_url = opener.open(self.token_url, urlencode(data))
open_url = opener.open(self.token_url, urlencode(data), self.socket_timeout)
except urllib2.HTTPError, e:
tmp = e.read()
raise Exception(tmp)
@@ -208,7 +206,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
def __init__(self, g=None,
client_id=None, client_secret=None,
auth_url=None, token_url=None, **args):
auth_url=None, token_url=None, socket_timeout=60, **args):
"""
first argument is unused. Here only for legacy reasons.
"""
@@ -224,6 +222,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
self.auth_url = auth_url
self.token_url = token_url
self.args = args
self.socket_timeout = socket_timeout
def login_url(self, next="/"):
self.__oauth_login(next)
+87 -46
View File
@@ -117,6 +117,7 @@ Supported DAL URI strings:
'firebird_embedded://username:password@c://path'
'informix://user:password@server:3050/database'
'informixu://user:password@server:3050/database' # unicode informix
'ingres://database' # or use an ODBC connection string, e.g. 'ingres://dsn=dsn_name'
'google:datastore' # for google app engine datastore
'google:sql' # for google app engine with sql (mysql compatible)
'teradata://DSN=dsn;UID=user;PWD=pass; DATABASE=database' # experimental
@@ -350,8 +351,9 @@ if not 'google' in DRIVERS:
DRIVERS.append('MSSQL(pyodbc)')
DRIVERS.append('DB2(pyodbc)')
DRIVERS.append('Teradata(pyodbc)')
DRIVERS.append('Ingres(pyodbc)')
except ImportError:
LOGGER.debug('no MSSQL/DB2/Teradata driver pyodbc')
LOGGER.debug('no MSSQL/DB2/Teradata/Ingres driver pyodbc')
try:
import Sybase
@@ -368,7 +370,7 @@ if not 'google' in DRIVERS:
try:
import fdb
DRIVERS.append('Firbird(fdb)')
DRIVERS.append('Firebird(fdb)')
except ImportError:
LOGGER.debug('no Firebird driver fdb')
#####
@@ -413,13 +415,6 @@ if not 'google' in DRIVERS:
LOGGER.debug('no SQLite/PostgreSQL driver zxJDBC')
is_jdbc = False
try:
import ingresdbi
DRIVERS.append('Ingres(ingresdbi)')
except ImportError:
LOGGER.debug('no Ingres driver ingresdbi')
# NOTE could try JDBC.......
try:
import couchdb
DRIVERS.append('CouchDB(couchdb)')
@@ -638,6 +633,7 @@ class BaseAdapter(ConnectionPool):
TRUE = 'T'
FALSE = 'F'
T_SEP = ' '
types = {
'boolean': 'CHAR(1)',
'string': 'CHAR(%(length)s)',
@@ -1815,7 +1811,7 @@ class BaseAdapter(ConnectionPool):
obj = str(obj)
elif fieldtype == 'datetime':
if isinstance(obj, datetime.datetime):
obj = obj.isoformat()[:19].replace('T',' ')
obj = obj.isoformat(self.T_SEP)[:19]
elif isinstance(obj, datetime.date):
obj = obj.isoformat()[:10]+' 00:00:00'
else:
@@ -2423,6 +2419,9 @@ class MySQLAdapter(BaseAdapter):
return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table,
'SET FOREIGN_KEY_CHECKS=1;']
def _insert_empty(self, table):
return 'INSERT INTO %s VALUES (DEFAULT);' % table
def distributed_transaction_begin(self,key):
self.execute('XA START;')
@@ -2664,11 +2663,11 @@ class PostgreSQLAdapter(BaseAdapter):
def CONTAINS(self,first,second,case_sensitive=False):
if first.type in ('string','text', 'json'):
key = '%'+str(second).replace('%','%%')+'%'
second = '%'+str(second).replace('%','%%')+'%'
elif first.type.startswith('list:'):
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
second = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
op = case_sensitive and self.LIKE or self.ILIKE
return op(first,key)
return op(first,second)
# GIS functions
@@ -3038,6 +3037,7 @@ class OracleAdapter(BaseAdapter):
class MSSQLAdapter(BaseAdapter):
drivers = ('pyodbc',)
T_SEP = 'T'
types = {
'boolean': 'BIT',
@@ -3455,9 +3455,9 @@ class FireBirdAdapter(BaseAdapter):
def CONTAINS(self, first, second, case_sensitive=False):
if first.type in ('string','text'):
key = str(second).replace('%','%%')
second = str(second).replace('%','%%')
elif first.type.startswith('list:'):
key = '|'+str(second).replace('|','||').replace('%','%%')+'|'
second = '|'+str(second).replace('|','||').replace('%','%%')+'|'
return self.CONTAINING(first,second)
def _drop(self,table,mode):
@@ -3862,7 +3862,7 @@ INGRES_SEQNAME='ii***lineitemsequence' # NOTE invalid database object name
# to be a delimited identifier)
class IngresAdapter(BaseAdapter):
drivers = ('ingresdbi',)
drivers = ('pyodbc',)
types = {
'boolean': 'CHAR(1)',
@@ -3913,6 +3913,7 @@ class IngresAdapter(BaseAdapter):
adapter_args={}, do_connect=True, after_connection=None):
self.db = db
self.dbengine = "ingres"
self._driver = pyodbc
self.uri = uri
if do_connect: self.find_driver(adapter_args,uri)
self.pool_size = pool_size
@@ -3920,22 +3921,27 @@ class IngresAdapter(BaseAdapter):
self.db_codec = db_codec
self._after_connection = after_connection
self.find_or_make_work_folder()
connstr = self._uri.split(':', 1)[1]
connstr = uri.split(':', 1)[1]
# Simple URI processing
connstr = connstr.lstrip()
while connstr.startswith('/'):
connstr = connstr[1:]
database_name=connstr # Assume only (local) dbname is passed in
vnode = '(local)'
servertype = 'ingres'
trace = (0, None) # No tracing
driver_args.update(database=database_name,
vnode=vnode,
servertype=servertype,
trace=trace)
def connector(driver_args=driver_args):
return self.driver.connect(**driver_args)
if '=' in connstr:
# Assume we have a regular ODBC connection string and just use it
ruri = connstr
else:
# Assume only (local) dbname is passed in with OS auth
database_name = connstr
default_driver_name = 'Ingres'
vnode = '(local)'
servertype = 'ingres'
ruri = 'Driver={%s};Server=%s;Database=%s' % (default_driver_name, vnode, database_name)
def connector(cnxn=ruri,driver_args=driver_args):
return self.driver.connect(cnxn,**driver_args)
self.connector = connector
# TODO if version is >= 10, set types['id'] to Identity column, see http://community.actian.com/wiki/Using_Ingres_Identity_Columns
if do_connect: self.reconnect()
def create_sequence_and_triggers(self, query, table, **args):
@@ -3961,12 +3967,12 @@ class IngresAdapter(BaseAdapter):
return int(self.cursor.fetchone()[0]) # don't really need int type cast here...
def integrity_error_class(self):
return ingresdbi.IntegrityError
return self._driver.IntegrityError
class IngresUnicodeAdapter(IngresAdapter):
drivers = ('ingresdbi',)
drivers = ('pyodbc',)
types = {
'boolean': 'CHAR(1)',
@@ -4464,7 +4470,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
adapter_args={}, do_connect=True, after_connection=None):
self.types.update({
'boolean': gae.BooleanProperty,
'string': (lambda: gae.StringProperty(multiline=True)),
'string': (lambda **kwargs: gae.StringProperty(multiline=True, **kwargs)),
'text': gae.TextProperty,
'json': gae.TextProperty,
'password': gae.StringProperty,
@@ -4480,9 +4486,9 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
'datetime': gae.DateTimeProperty,
'id': None,
'reference': gae.IntegerProperty,
'list:string': (lambda: gae.StringListProperty(default=None)),
'list:integer': (lambda: gae.ListProperty(int,default=None)),
'list:reference': (lambda: gae.ListProperty(int,default=None)),
'list:string': (lambda **kwargs: gae.StringListProperty(default=None, **kwargs)),
'list:integer': (lambda **kwargs: gae.ListProperty(int,default=None, **kwargs)),
'list:reference': (lambda **kwargs: gae.ListProperty(int,default=None, **kwargs)),
})
self.db = db
self.uri = uri
@@ -4505,7 +4511,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
if isinstance(polymodel,Table) and field.name in polymodel.fields():
continue
attr = {}
if isinstance(field.custom_qaulifier, dict):
if isinstance(field.custom_qualifier, dict):
#this is custom properties to add to the GAE field declartion
attr = field.custom_qualifier
field_type = field.type
@@ -4653,7 +4659,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
return self.expand(first)
def truncate(self,table,mode):
self.db(table._id).delete()
self.db(self.db._adapter.id_query(table)).delete()
def select_raw(self,query,fields=None,attributes=None):
db = self.db
@@ -7051,15 +7057,13 @@ class DAL(object):
Example::
db = DAL('sqlite://test.db')
or
db = DAL({"uri": ..., "items": ...}) # experimental
db.define_table('tablename', Field('fieldname1'),
Field('fieldname2'))
(experimental)
you can pass a dict object as uri with the uri string
and table/field definitions. For an example of valid data check
the output of:
>>> db.as_dict(flat=True, sanitize=False)
"""
def __new__(cls, uri='sqlite://dummy.db', *args, **kwargs):
@@ -7180,6 +7184,28 @@ class DAL(object):
:uri: string that contains information for connecting to a database.
(default: 'sqlite://dummy.db')
experimental: you can specify a dictionary as uri
parameter i.e. with
db = DAL({"uri": "sqlite://storage.sqlite",
"items": {...}, ...})
for an example of dict input you can check the output
of the scaffolding db model with
db.as_dict()
Note that for compatibility with Python older than
version 2.6.5 you should cast your dict input keys
to str due to a syntax limitation on kwarg names.
for proper DAL dictionary input you can use one of:
obj = serializers.cast_keys(dict, [encoding="utf-8"])
or else (for parsing json input)
obj = serializers.loads_json(data, unicode_keys=False)
:pool_size: How many open connections to make to the database object.
:folder: where .table files will be created.
automatically set within web2py
@@ -8816,6 +8842,8 @@ class Expression(object):
result_type = 'integer'
elif self.type in ['date','time','datetime','double','float']:
result_type = 'double'
elif self.type.startswith('decimal('):
result_type = self.type
else:
raise SyntaxError("subtraction operation not supported for type")
return Expression(db,db._adapter.SUB,self,other,result_type)
@@ -9036,7 +9064,7 @@ class FieldVirtual(object):
(self.name, self.f) = (name, f) if f else ('unkown', name)
self.type = ftype
self.label = label or self.name.capitalize().replace('_',' ')
self.represent = IDENTITY
self.represent = IDENTITY
self.formatter = IDENTITY
self.comment = None
self.readable = True
@@ -9234,7 +9262,11 @@ class Field(Expression):
dest_file.close()
return newfilename
def retrieve(self, name, path=None):
def retrieve(self, name, path=None, nameonly=False):
"""
if nameonly==True return (filename, fullfilename) instead of
(filename, stream)
"""
self_uploadfield = self.uploadfield
if self.custom_retrieve:
return self.custom_retrieve(name, path)
@@ -9262,7 +9294,12 @@ class Field(Expression):
stream = self.uploadfs.open(name, 'rb')
else:
# ## if file is on regular filesystem
stream = pjoin(file_properties['path'], name)
# this is intentially a sting with filename and not a stream
# this propagates and allows stream_file_or_304_or_206 to be called
fullname = pjoin(file_properties['path'],name)
if nameonly:
return (filename, fullname)
stream = open(fullname,'rb')
return (filename, stream)
def retrieve_file_properties(self, name, path=None):
@@ -9389,7 +9426,7 @@ class Field(Expression):
if k == "other":
if isinstance(v, dict):
otype, other = v.popitem()
else:
else:
otype = flatten(type(v))
other = v
newr[k] = {otype: filter_requires(otype, other,
@@ -9535,6 +9572,10 @@ class Query(object):
newd[k] = loop(v.__dict__)
elif isinstance(v, SERIALIZABLE_TYPES):
newd[k] = v
elif isinstance(v, (datetime.date,
datetime.time,
datetime.datetime)):
newd[k] = unicode(v)
elif k == "op":
if callable(v):
newd[k] = v.__name__
+2 -2
View File
@@ -47,8 +47,8 @@ __all__ = [
def parse_semantic(version="Version 1.99.0-rc.1+timestamp.2011.09.19.08.23.26"):
"http://semver.org/"
re_version = re.compile('Version (\d+)\.(\d+)\.(\d+)(\-(?P<pre>[^\s+]*))?(\+(?P<build>\S*))')
m = re_version.match(version)
re_version = re.compile('(\d+)\.(\d+)\.(\d+)(\-(?P<pre>[^\s+]*))?(\+(?P<build>\S*))')
m = re_version.match(version.strip().split()[-1])
if not m:
return None
a, b, c = int(m.group(1)), int(m.group(2)), int(m.group(3))
+5 -1
View File
@@ -56,6 +56,7 @@ current = threading.local() # thread-local storage for request-scope globals
css_template = '<link href="%s" rel="stylesheet" type="text/css" />'
js_template = '<script src="%s" type="text/javascript"></script>'
coffee_template = '<script src="%s" type="text/coffee"></script>'
typescript_template = '<script src="%s" type="text/typescript"></script>'
less_template = '<link href="%s" rel="stylesheet/less" type="text/css" />'
css_inline = '<style type="text/css">\n%s\n</style>'
js_inline = '<script type="text/javascript">\n%s\n</script>'
@@ -292,6 +293,9 @@ class Response(Storage):
s += js_template % item
elif f.endswith('.coffee'):
s += coffee_template % item
elif f.endswith('.ts'):
# http://www.typescriptlang.org/
s += typescript_template % item
elif f.endswith('.less'):
s += less_template % item
elif isinstance(item, (list, tuple)):
@@ -400,7 +404,7 @@ class Response(Storage):
except AttributeError:
raise HTTP(404)
try:
(filename, stream) = field.retrieve(name)
(filename, stream) = field.retrieve(name,nameonly=True)
except IOError:
raise HTTP(404)
headers = self.headers
+13 -6
View File
@@ -887,9 +887,8 @@ class DIV(XmlComponent):
# get the attributes for this component
# (they start with '_', others may have special meanings)
fa = ''
for key in sorted(self.attributes):
value = self[key]
attr = []
for key, value in self.attributes.iteritems():
if key[:1] != '_':
continue
name = key[1:]
@@ -897,8 +896,16 @@ class DIV(XmlComponent):
value = name
elif value is False or value is None:
continue
attr.append((name, value))
data = self.attributes.get('data',{})
for key, value in data.iteritems():
name = 'data-' + key
value = data[key]
attr.append((name,value))
attr.sort()
fa = ''
for name,value in attr:
fa += ' %s="%s"' % (name, xmlescape(value, True))
# get the xml for the inner components
co = join([xmlescape(component) for component in
self.components])
@@ -1479,7 +1486,7 @@ class A(DIV):
self['_href'] = self['_href'] or '#null'
elif self['callback']:
returnfalse = "var e = arguments[0] || window.event; e.cancelBubble=true; if (e.stopPropagation) {e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault();}"
if d:
if d and not self['noconfirm']:
self['_onclick'] = "if(confirm(w2p_ajax_confirm_message||'Are you sure you want to delete this object?')){ajax('%s',[],'%s');%s};%s" % \
(self['callback'], self['target'] or '', d, returnfalse)
else:
@@ -2305,7 +2312,7 @@ class BEAUTIFY(DIV):
for c in self.components:
if hasattr(c, 'value') and not callable(c.value):
if c.value:
components.append(c.value)
components.append(c.value)
if hasattr(c, 'xml') and callable(c.xml):
components.append(c)
continue
+2 -1
View File
@@ -237,7 +237,8 @@ def read_possible_languages_aux(langdir):
construct_plural_form) # construct_plural_form() for current language
plurals = {}
flist = oslistdir(langdir)
flist = oslistdir(langdir) if isdir(langdir) else []
# scan languages directory for plural dict files:
for pname in flist:
if regex_plural_file.match(pname):
+7 -9
View File
@@ -102,16 +102,14 @@ requests = 0 # gc timer
# pattern used to validate client address
regex_client = re.compile('[\w\-:]+(\.[\w\-]+)*\.?') # ## to account for IPV6
#try:
if 1:
try:
version_info = open(pjoin(global_settings.gluon_parent, 'VERSION'), 'r')
raw_version_string = version_info.read().strip()
raw_version_string = version_info.read().split()[-1].strip()
version_info.close()
global_settings.web2py_version = parse_version(raw_version_string)
#except:
# raise RuntimeError("Cannot determine web2py version")
web2py_version = global_settings.web2py_version
global_settings.web2py_version = raw_version_string
web2py_version = global_settings.web2py_version
except:
raise RuntimeError("Cannot determine web2py version")
try:
import rocket
@@ -133,7 +131,7 @@ def get_client(env):
"""
g = regex_client.search(env.get('http_x_forwarded_for', ''))
client = (g.group() or '').split(',')[0] if g else None
if client in (None, '', 'unkown'):
if client in (None, '', 'unknown'):
g = regex_client.search(env.get('remote_addr', ''))
if g:
client = g.group()
+5 -1
View File
@@ -22,6 +22,7 @@ from settings import global_settings
logger = logging.getLogger("web2py.cron")
_cron_stopping = False
_cron_subprocs = []
def absolute_path_link(path):
@@ -42,7 +43,8 @@ def stopcron():
"graceful shutdown of cron"
global _cron_stopping
_cron_stopping = True
while _cron_subprocs:
_cron_subprocs.pop().terminate()
class extcron(threading.Thread):
@@ -232,6 +234,7 @@ class cronlauncher(threading.Thread):
def run(self):
import subprocess
global _cron_subprocs
if isinstance(self.cmd, (list, tuple)):
cmd = self.cmd
else:
@@ -241,6 +244,7 @@ class cronlauncher(threading.Thread):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=self.shell)
_cron_subprocs.append(proc)
(stdoutdata, stderrdata) = proc.communicate()
if proc.returncode != 0:
logger.warning(
+3 -2
View File
@@ -66,7 +66,7 @@ class XssCleaner(HTMLParser):
# The only schemes allowed in URLs (for href and src attributes).
# Adding "javascript" or "vbscript" to this list would not be smart.
self.allowed_schemes = ['http', 'https', 'ftp']
self.allowed_schemes = ['http', 'https', 'ftp', 'mailto']
#to strip or escape disallowed tags?
self.strip_disallowed = strip_disallowed
@@ -151,11 +151,12 @@ class XssCleaner(HTMLParser):
def url_is_acceptable(self, url):
"""
Accepts relative and absolute urls
Accepts relative, absolute, and mailto urls
"""
parsed = urlparse(url)
return (parsed[0] in self.allowed_schemes and '.' in parsed[1]) \
or (parsed[0] in self.allowed_schemes and '@' in parsed[2]) \
or (parsed[0] == '' and parsed[2].startswith('/'))
def strip(self, rawstring, escape=True):
+40 -2
View File
@@ -107,6 +107,7 @@ TERMINATE = 'TERMINATE'
DISABLED = 'DISABLED'
KILL = 'KILL'
PICK = 'PICK'
STOP_TASK = 'STOP_TASK'
EXPIRED = 'EXPIRED'
SECONDS = 1
HEARTBEAT = 3 * SECONDS
@@ -398,7 +399,7 @@ class MetaScheduler(threading.Thread):
TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED)
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED)
WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL)
WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL, STOP_TASK)
class TYPE(object):
@@ -746,7 +747,7 @@ class Scheduler(MetaScheduler):
# keep sleeping
self.worker_status[0] = DISABLED
if self.worker_status[1] == MAXHIBERNATION:
logger.debug('........recording heartbeat')
logger.debug('........recording heartbeat (%s)', self.worker_status[0])
db(sw.worker_name == self.worker_name).update(
last_heartbeat=now)
elif mybackedstatus == TERMINATE:
@@ -758,6 +759,9 @@ class Scheduler(MetaScheduler):
self.worker_status[0] = KILL
self.die()
else:
if mybackedstatus == STOP_TASK:
logger.info('Asked to kill the current task')
self.terminate_process()
logger.debug('........recording heartbeat (%s)', self.worker_status[0])
db(sw.worker_name == self.worker_name).update(
last_heartbeat=now, status=ACTIVE)
@@ -1007,6 +1011,40 @@ class Scheduler(MetaScheduler):
object_hook=_decode_dict) or None
return row
def stop_task(self, ref):
"""
Experimental!!!
Shortcut for task termination.
If the task is RUNNING it will terminate it --> execution will be set as FAILED
If the task is QUEUED, its stop_time will be set as to "now",
the enabled flag will be set to False, status to STOPPED
:param ref: can be
- integer --> lookup will be done by scheduler_task.id
- string --> lookup will be done by scheduler_task.uuid
Returns:
- 1 if task was stopped (meaning an update has been done)
- None if task was not found, or if task was not RUNNING or QUEUED
"""
from gluon.dal import Query
st, sw = self.db.scheduler_task, self.db.scheduler_worker
if isinstance(ref, int):
q = st.id == ref
elif isinstance(ref, str):
q = st.uuid == ref
else:
raise SyntaxError(
"You can retrieve results only by id or uuid")
task = self.db(q).select(st.id, st.status, st.assigned_worker_name).first()
rtn = None
if not task:
return rtn
if task.status == 'RUNNING':
rtn = self.db(sw.worker_name == task.assigned_worker_name).update(status=STOP_TASK)
elif task.status == 'QUEUED':
rtn = self.db(q).update(stop_time=self.now(), enabled=False, status=STOPPED)
return rtn
def main():
"""
+45 -2
View File
@@ -25,9 +25,52 @@ try:
except ImportError:
have_yaml = False
def loads_json(o):
def cast_keys(o, cast=str, encoding="utf-8"):
""" Builds a new object with <cast> type keys
Arguments:
o is the object input
cast (defaults to str) is an object type or function
which supports conversion such as:
>>> converted = cast(o)
encoding (defaults to utf-8) is the encoding for unicode
keys. This is not used for custom cast functions
Use this funcion if you are in Python < 2.6.5
This avoids syntax errors when unpacking dictionary arguments.
"""
if isinstance(o, (dict, Storage)):
if isinstance(o, dict):
newobj = dict()
else:
newobj = Storage()
for k, v in o.items():
if (cast == str) and isinstance(k, unicode):
key = k.encode(encoding)
else:
key = cast(k)
if isinstance(v, (dict, Storage)):
value = cast_keys(v, cast=cast, encoding=encoding)
else:
value = v
newobj[key] = value
else:
raise TypeError("Cannot cast keys: %s is not supported" % \
type(o))
return newobj
def loads_json(o, unicode_keys=True, **kwargs):
# deserialize a json string
return json_parser.loads(o)
result = json_parser.loads(o, **kwargs)
if not unicode_keys:
# filter non-str keys in dictionary objects
result = cast_keys(result,
encoding=kwargs.get("encoding", "utf-8"))
return result
def custom_json(o):
if hasattr(o, 'custom_json') and callable(o.custom_json):
+53 -31
View File
@@ -286,9 +286,9 @@ jQuery.fn.grow_input = function() {
function pe(ul, e) {
var new_line = ml(ul);
rel(ul);
if ($(e.target).parent().is(':visible')) {
if (jQuery(e.target).parent().is(':visible')) {
//make sure we didn't delete the element before we insert after
new_line.insertAfter($(e.target).parent());
new_line.insertAfter(jQuery(e.target).parent());
} else {
//the line we clicked on was deleted, just add to end of list
new_line.appendTo(ul);
@@ -299,7 +299,7 @@ function pe(ul, e) {
function rl(ul, e) {
if (jQuery(ul).children().length > 1) {
//only remove if we have more than 1 item so the list is never empty
$(e.target).parent().remove();
jQuery(e.target).parent().remove();
}
}
function ml(ul) {
@@ -461,8 +461,8 @@ class CheckboxesWidget(OptionsWidget):
if opts:
opts.append(
INPUT(requires=attr.get('requires', None),
_style="display:none;",
_disabled="disabled",
_style="display:none;",
_disabled="disabled",
_name=field.name,
hideerror=False))
return parent(*opts, **attr)
@@ -542,7 +542,7 @@ class UploadWidget(FormWidget):
requires = attr["requires"]
if requires == [] or isinstance(requires, IS_EMPTY_OR):
inp = DIV(inp,
inp = DIV(inp,
SPAN('[',
A(current.T(
UploadWidget.GENERIC_DESCRIPTION), _href=url),
@@ -553,13 +553,13 @@ class UploadWidget(FormWidget):
LABEL(current.T(cls.DELETE_FILE),
_for=field.name + cls.ID_DELETE_SUFFIX,
_style='display:inline'),
']', _style='white-space:nowrap'),
']', _style='white-space:nowrap'),
br, image)
else:
inp = DIV(inp,
inp = DIV(inp,
SPAN('[',
A(cls.GENERIC_DESCRIPTION, _href=url),
']', _style='white-space:nowrap'),
']', _style='white-space:nowrap'),
br, image)
return inp
@@ -1180,7 +1180,7 @@ class SQLFORM(FORM):
widget,
col3.get(self.FIELDKEY_DELETE_RECORD, '')))
self.custom.delete = self.custom.deletable = widget
# when writable, add submit button
self.custom.submit = ''
@@ -1440,7 +1440,8 @@ class SQLFORM(FORM):
f = self.table[fieldname].default or ''
fields[fieldname] = f
else:
fields[fieldname] = ''
f = self.table[fieldname].default or ''
fields[fieldname] = f
self.vars[fieldname] = fields[fieldname]
if not f:
continue
@@ -1654,7 +1655,7 @@ class SQLFORM(FORM):
value_input = SQLFORM.widgets.date.widget(field,field.default,_id=_id)
elif field.type == 'datetime':
value_input = SQLFORM.widgets.datetime.widget(field,field.default,_id=_id)
elif (field.type.startswith('reference ') or
elif (field.type.startswith('reference ') or
field.type.startswith('list:reference ')) and \
hasattr(field.requires,'options'):
value_input = SELECT(
@@ -1669,7 +1670,7 @@ class SQLFORM(FORM):
else:
value_input = INPUT(
_type='text', _id=_id, _class=field.type)
new_button = INPUT(
_type="button", _value=T('New'), _class="btn",
_onclick="%s_build_query('new','%s')" % (prefix,field))
@@ -1760,7 +1761,8 @@ class SQLFORM(FORM):
editargs={},
viewargs={},
buttons_placement = 'right',
links_placement = 'right'
links_placement = 'right',
noconfirm=False
):
# jQuery UI ThemeRoller classes (empty if ui is disabled)
@@ -1816,7 +1818,7 @@ class SQLFORM(FORM):
def url(**b):
b['args'] = args + b.get('args', [])
localvars = request.vars.copy()
localvars = request.get_vars.copy()
localvars.update(b.get('vars', {}))
b['vars'] = localvars
b['hash_vars'] = False
@@ -1825,7 +1827,7 @@ class SQLFORM(FORM):
def url2(**b):
b['args'] = request.args + b.get('args', [])
localvars = request.vars.copy()
localvars = request.get_vars.copy()
localvars.update(b.get('vars', {}))
b['vars'] = localvars
b['hash_vars'] = False
@@ -1849,7 +1851,7 @@ class SQLFORM(FORM):
def gridbutton(buttonclass='buttonadd', buttontext=T('Add'),
buttonurl=url(args=[]), callback=None,
delete=None, trap=True):
delete=None, trap=True, noconfirm=None):
if showbuttontext:
return A(SPAN(_class=ui.get(buttonclass)),
SPAN(T(buttontext), _title=buttontext,
@@ -1857,12 +1859,14 @@ class SQLFORM(FORM):
_href=buttonurl,
callback=callback,
delete=delete,
noconfirm=noconfirm,
_class=trap_class(ui.get('button'), trap))
else:
return A(SPAN(_class=ui.get(buttonclass)),
_href=buttonurl,
callback=callback,
delete=delete,
noconfirm=noconfirm,
_title=buttontext,
_class=trap_class(ui.get('buttontext'), trap))
@@ -2056,7 +2060,8 @@ class SQLFORM(FORM):
elif not request.vars.records:
request.vars.records = []
session['_web2py_grid_referrer_' + formname] = url2(vars=request.vars)
session['_web2py_grid_referrer_' + formname] = \
url2(vars=request.get_vars)
console = DIV(_class='web2py_console %(header)s %(cornertop)s' % ui)
error = None
if create:
@@ -2341,6 +2346,7 @@ class SQLFORM(FORM):
'buttondelete', 'Delete',
url(args=['delete', tablename, id]),
callback=url(args=['delete', tablename, id]),
noconfirm=noconfirm,
delete='tr'))
if buttons_placement in ['right', 'both']:
trcols.append(row_buttons)
@@ -2458,6 +2464,16 @@ class SQLFORM(FORM):
if constraints is None:
constraints = {}
field = None
name = None
def format(table,row):
if not row:
return 'Unknown'
elif isinstance(table._format,str):
return table._format % row
elif callable(table._format):
return table._format(row)
else:
return '#'+str(row.id)
try:
nargs = len(args) + 1
previous_tablename, previous_fieldname, previous_id = \
@@ -2484,14 +2500,7 @@ class SQLFORM(FORM):
raise HTTP(400)
previous_tablename, previous_fieldname, previous_id = \
tablename, fieldname, id
try:
format = db[referee]._format
if callable(format):
name = format(record)
else:
name = format % record
except TypeError:
name = id
name = format(db[referee],record)
breadcrumbs.append(
LI(A(T(db[referee]._plural),
_class=trap_class(),
@@ -2553,13 +2562,27 @@ class SQLFORM(FORM):
grid = SQLFORM.grid(query, args=request.args[:nargs], links=links,
links_in_grid=links_in_grid,
user_signature=user_signature, **kwargs)
if isinstance(grid, DIV):
header = table._plural + (field and ' for ' + field.label or '')
breadcrumbs.append(LI(A(T(header), _class=trap_class(),
_href=url()), _class='active w2p_grid_breadcrumb_elem'))
header = table._plural
next = grid.create_form or grid.update_form or grid.view_form
breadcrumbs.append(LI(
A(T(header), _class=trap_class(),_href=url()),
SPAN(divider, _class='divider') if next else '',
_class='active w2p_grid_breadcrumb_elem'))
if grid.create_form:
header = T('New %s' % table._singular)
elif grid.update_form:
header = T('Edit %s' % format(table,grid.update_form.record))
elif grid.view_form:
header = T('View %s' % format(table,grid.view_form.record))
if next:
breadcrumbs.append(LI(
A(T(header), _class=trap_class(),_href=url()),
_class='active w2p_grid_breadcrumb_elem'))
grid.insert(
0, DIV(UL(*breadcrumbs, **{'_class': breadcrumbs_class}),
_class='web2py_breadcrumbs'))
_class='web2py_breadcrumbs'))
return grid
@@ -2994,4 +3017,3 @@ class ExporterJSON(ExportClass):
return self.rows.as_json()
else:
return 'null'
+304 -282
View File
@@ -6,6 +6,8 @@
import sys
import os
import glob
if os.path.isdir('gluon'):
sys.path.append(os.path.realpath('gluon'))
else:
@@ -19,6 +21,11 @@ except:
from io import StringIO
from dal import DAL, Field, Table, SQLALL
#for travis-ci
DEFAULT_URI = os.environ.get('DB', 'sqlite:memory')
print 'Testing against %s engine (%s)' % (DEFAULT_URI.partition(':')[0], DEFAULT_URI)
ALLOWED_DATATYPES = [
'string',
'text',
@@ -41,6 +48,8 @@ def setUpModule():
def tearDownModule():
if os.path.isfile('sql.log'):
os.unlink('sql.log')
for a in glob.glob('*.table'):
os.unlink(a)
class TestFields(unittest.TestCase):
@@ -96,35 +105,35 @@ class TestFields(unittest.TestCase):
isinstance(f.formatter(datetime.datetime.now()), str)
def testRun(self):
db = DAL('sqlite:memory:')
db = DAL(DEFAULT_URI, check_reserved=['all'])
for ft in ['string', 'text', 'password', 'upload', 'blob']:
db.define_table('t', Field('a', ft, default=''))
self.assertEqual(db.t.insert(a='x'), 1)
self.assertEqual(db().select(db.t.a)[0].a, 'x')
db.t.drop()
db.define_table('t', Field('a', 'integer', default=1))
self.assertEqual(db.t.insert(a=3), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3)
db.t.drop()
db.define_table('t', Field('a', 'double', default=1))
self.assertEqual(db.t.insert(a=3.1), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3.1)
db.t.drop()
db.define_table('t', Field('a', 'boolean', default=True))
self.assertEqual(db.t.insert(a=True), 1)
self.assertEqual(db().select(db.t.a)[0].a, True)
db.t.drop()
db.define_table('t', Field('a', 'json', default={}))
self.assertEqual(db.t.insert(a={}), 1)
self.assertEqual(db().select(db.t.a)[0].a, {})
db.t.drop()
db.define_table('t', Field('a', 'date',
db.define_table('tt', Field('aa', ft, default=''))
self.assertEqual(db.tt.insert(aa='x'), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, 'x')
db.tt.drop()
db.define_table('tt', Field('aa', 'integer', default=1))
self.assertEqual(db.tt.insert(aa=3), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, 3)
db.tt.drop()
db.define_table('tt', Field('aa', 'double', default=1))
self.assertEqual(db.tt.insert(aa=3.1), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, 3.1)
db.tt.drop()
db.define_table('tt', Field('aa', 'boolean', default=True))
self.assertEqual(db.tt.insert(aa=True), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, True)
db.tt.drop()
db.define_table('tt', Field('aa', 'json', default={}))
self.assertEqual(db.tt.insert(aa={}), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, {})
db.tt.drop()
db.define_table('tt', Field('aa', 'date',
default=datetime.date.today()))
t0 = datetime.date.today()
self.assertEqual(db.t.insert(a=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0)
db.t.drop()
db.define_table('t', Field('a', 'datetime',
self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.tt.drop()
db.define_table('tt', Field('aa', 'datetime',
default=datetime.datetime.today()))
t0 = datetime.datetime(
1971,
@@ -135,34 +144,34 @@ class TestFields(unittest.TestCase):
55,
0,
)
self.assertEqual(db.t.insert(a=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0)
self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
## Row APIs
row = db().select(db.t.a)[0]
self.assertEqual(db.t[1].a,t0)
self.assertEqual(db.t['a'],db.t.a)
self.assertEqual(db.t(1).a,t0)
self.assertTrue(db.t(1,a=None)==None)
self.assertFalse(db.t(1,a=t0)==None)
self.assertEqual(row.a,t0)
self.assertEqual(row['a'],t0)
self.assertEqual(row['t.a'],t0)
self.assertEqual(row('t.a'),t0)
row = db().select(db.tt.aa)[0]
self.assertEqual(db.tt[1].aa,t0)
self.assertEqual(db.tt['aa'],db.tt.aa)
self.assertEqual(db.tt(1).aa,t0)
self.assertTrue(db.tt(1,aa=None)==None)
self.assertFalse(db.tt(1,aa=t0)==None)
self.assertEqual(row.aa,t0)
self.assertEqual(row['aa'],t0)
self.assertEqual(row['tt.aa'],t0)
self.assertEqual(row('tt.aa'),t0)
## Lazy and Virtual fields
db.t.b = Field.Virtual(lambda row: row.t.a)
db.t.c = Field.Lazy(lambda row: row.t.a)
row = db().select(db.t.a)[0]
db.tt.b = Field.Virtual(lambda row: row.tt.aa)
db.tt.c = Field.Lazy(lambda row: row.tt.aa)
row = db().select(db.tt.aa)[0]
self.assertEqual(row.b,t0)
self.assertEqual(row.c(),t0)
db.t.drop()
db.define_table('t', Field('a', 'time', default='11:30'))
db.tt.drop()
db.define_table('tt', Field('aa', 'time', default='11:30'))
t0 = datetime.time(10, 30, 55)
self.assertEqual(db.t.insert(a=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0)
db.t.drop()
self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.tt.drop()
class TestAll(unittest.TestCase):
@@ -198,7 +207,7 @@ class TestTable(unittest.TestCase):
in str(persons.ALL))
def testTableAlias(self):
db = DAL('sqlite:memory:')
db = DAL(DEFAULT_URI, check_reserved=['all'])
persons = Table(db, 'persons', Field('firstname',
'string'), Field('lastname', 'string'))
aliens = persons.with_alias('aliens')
@@ -221,276 +230,278 @@ class TestTable(unittest.TestCase):
class TestInsert(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
self.assertEqual(db.t.insert(a='1'), 1)
self.assertEqual(db.t.insert(a='1'), 2)
self.assertEqual(db.t.insert(a='1'), 3)
self.assertEqual(db(db.t.a == '1').count(), 3)
self.assertEqual(db(db.t.a == '1').update(a='2'), 3)
self.assertEqual(db(db.t.a == '2').count(), 3)
self.assertEqual(db(db.t.a == '2').delete(), 3)
db.t.drop()
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.tt.insert(aa='1'), 2)
self.assertEqual(db.tt.insert(aa='1'), 3)
self.assertEqual(db(db.tt.aa == '1').count(), 3)
self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3)
self.assertEqual(db(db.tt.aa == '2').count(), 3)
self.assertEqual(db(db.tt.aa == '2').delete(), 3)
db.tt.drop()
class TestSelect(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
self.assertEqual(db.t.insert(a='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3)
self.assertEqual(len(db(db.t.id > 0).select()), 3)
self.assertEqual(db(db.t.id > 0).select(orderby=~db.t.a
| db.t.id)[0].a, '3')
self.assertEqual(len(db(db.t.id > 0).select(limitby=(1, 2))), 1)
self.assertEqual(db(db.t.id > 0).select(limitby=(1, 2))[0].a,
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.tt.id > 0).select()), 3)
self.assertEqual(db(db.tt.id > 0).select(orderby=~db.tt.aa
| db.tt.id)[0].aa, '3')
self.assertEqual(len(db(db.tt.id > 0).select(limitby=(1, 2))), 1)
self.assertEqual(db(db.tt.id > 0).select(limitby=(1, 2))[0].aa,
'2')
self.assertEqual(len(db().select(db.t.ALL)), 3)
self.assertEqual(len(db(db.t.a == None).select()), 0)
self.assertEqual(len(db(db.t.a != None).select()), 3)
self.assertEqual(len(db(db.t.a > '1').select()), 2)
self.assertEqual(len(db(db.t.a >= '1').select()), 3)
self.assertEqual(len(db(db.t.a == '1').select()), 1)
self.assertEqual(len(db(db.t.a != '1').select()), 2)
self.assertEqual(len(db(db.t.a < '3').select()), 2)
self.assertEqual(len(db(db.t.a <= '3').select()), 3)
self.assertEqual(len(db(db.t.a > '1')(db.t.a < '3').select()), 1)
self.assertEqual(len(db((db.t.a > '1') & (db.t.a < '3')).select()), 1)
self.assertEqual(len(db((db.t.a > '1') | (db.t.a < '3')).select()), 3)
self.assertEqual(len(db((db.t.a > '1') & ~(db.t.a > '2')).select()), 1)
self.assertEqual(len(db(~(db.t.a > '1') & (db.t.a > '2')).select()), 0)
db.t.drop()
self.assertEqual(len(db().select(db.tt.ALL)), 3)
self.assertEqual(len(db(db.tt.aa == None).select()), 0)
self.assertEqual(len(db(db.tt.aa != None).select()), 3)
self.assertEqual(len(db(db.tt.aa > '1').select()), 2)
self.assertEqual(len(db(db.tt.aa >= '1').select()), 3)
self.assertEqual(len(db(db.tt.aa == '1').select()), 1)
self.assertEqual(len(db(db.tt.aa != '1').select()), 2)
self.assertEqual(len(db(db.tt.aa < '3').select()), 2)
self.assertEqual(len(db(db.tt.aa <= '3').select()), 3)
self.assertEqual(len(db(db.tt.aa > '1')(db.tt.aa < '3').select()), 1)
self.assertEqual(len(db((db.tt.aa > '1') & (db.tt.aa < '3')).select()), 1)
self.assertEqual(len(db((db.tt.aa > '1') | (db.tt.aa < '3')).select()), 3)
self.assertEqual(len(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).select()), 1)
self.assertEqual(len(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).select()), 0)
db.tt.drop()
class TestBelongs(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
self.assertEqual(db.t.insert(a='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3)
self.assertEqual(len(db(db.t.a.belongs(('1', '3'))).select()),
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.tt.aa.belongs(('1', '3'))).select()),
2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.id
> 2)._select(db.t.a))).select()), 1)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(('1',
'3')))._select(db.t.a))).select()), 2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(db
(db.t.a.belongs(('1', '3')))._select(db.t.a)))._select(
db.t.a))).select()),
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.id
> 2)._select(db.tt.aa))).select()), 1)
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
'3')))._select(db.tt.aa))).select()), 2)
self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
(db.tt.aa.belongs(('1', '3')))._select(db.tt.aa)))._select(
db.tt.aa))).select()),
2)
db.t.drop()
db.tt.drop()
class TestContains(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a', 'list:string'))
self.assertEqual(db.t.insert(a=['aaa','bbb']), 1)
self.assertEqual(db.t.insert(a=['bbb','ddd']), 2)
self.assertEqual(db.t.insert(a=['eee','aaa']), 3)
self.assertEqual(len(db(db.t.a.contains('aaa')).select()),
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa', 'list:string'))
self.assertEqual(db.tt.insert(aa=['aaa','bbb']), 1)
self.assertEqual(db.tt.insert(aa=['bbb','ddd']), 2)
self.assertEqual(db.tt.insert(aa=['eee','aaa']), 3)
self.assertEqual(len(db(db.tt.aa.contains('aaa')).select()),
2)
self.assertEqual(len(db(db.t.a.contains('bbb')).select()),
self.assertEqual(len(db(db.tt.aa.contains('bbb')).select()),
2)
self.assertEqual(len(db(db.t.a.contains('aa')).select()),
self.assertEqual(len(db(db.tt.aa.contains('aa')).select()),
0)
db.t.drop()
db.tt.drop()
class TestLike(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
self.assertEqual(db.t.insert(a='abc'), 1)
self.assertEqual(len(db(db.t.a.like('a%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%b%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%c')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%d%')).select()), 0)
self.assertEqual(len(db(db.t.a.lower().like('A%')).select()), 1)
self.assertEqual(len(db(db.t.a.lower().like('%B%')).select()),
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
self.assertEqual(db.tt.insert(aa='abc'), 1)
self.assertEqual(len(db(db.tt.aa.like('a%')).select()), 1)
self.assertEqual(len(db(db.tt.aa.like('%b%')).select()), 1)
self.assertEqual(len(db(db.tt.aa.like('%c')).select()), 1)
self.assertEqual(len(db(db.tt.aa.like('%d%')).select()), 0)
self.assertEqual(len(db(db.tt.aa.lower().like('A%')).select()), 1)
self.assertEqual(len(db(db.tt.aa.lower().like('%B%')).select()),
1)
self.assertEqual(len(db(db.t.a.lower().like('%C')).select()), 1)
self.assertEqual(len(db(db.t.a.upper().like('A%')).select()), 1)
self.assertEqual(len(db(db.t.a.upper().like('%B%')).select()),
self.assertEqual(len(db(db.tt.aa.lower().like('%C')).select()), 1)
self.assertEqual(len(db(db.tt.aa.upper().like('A%')).select()), 1)
self.assertEqual(len(db(db.tt.aa.upper().like('%B%')).select()),
1)
self.assertEqual(len(db(db.t.a.upper().like('%C')).select()), 1)
db.t.drop()
db.define_table('t', Field('a', 'integer'))
self.assertEqual(db.t.insert(a=1111111111), 1)
self.assertEqual(len(db(db.t.a.like('1%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('2%')).select()), 0)
db.t.drop()
self.assertEqual(len(db(db.tt.aa.upper().like('%C')).select()), 1)
db.tt.drop()
db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.tt.insert(aa=1111111111), 1)
self.assertEqual(len(db(db.tt.aa.like('1%')).select()), 1)
self.assertEqual(len(db(db.tt.aa.like('2%')).select()), 0)
db.tt.drop()
class TestDatetime(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a', 'datetime'))
self.assertEqual(db.t.insert(a=datetime.datetime(1971, 12, 21,
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa', 'datetime'))
self.assertEqual(db.tt.insert(aa=datetime.datetime(1971, 12, 21,
11, 30)), 1)
self.assertEqual(db.t.insert(a=datetime.datetime(1971, 11, 21,
self.assertEqual(db.tt.insert(aa=datetime.datetime(1971, 11, 21,
10, 30)), 2)
self.assertEqual(db.t.insert(a=datetime.datetime(1970, 12, 21,
self.assertEqual(db.tt.insert(aa=datetime.datetime(1970, 12, 21,
9, 30)), 3)
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12,
self.assertEqual(len(db(db.tt.aa == datetime.datetime(1971, 12,
21, 11, 30)).select()), 1)
self.assertEqual(db(db.t.a.year() == 1971).count(), 2)
self.assertEqual(db(db.t.a.month() == 12).count(), 2)
self.assertEqual(db(db.t.a.day() == 21).count(), 3)
self.assertEqual(db(db.t.a.hour() == 11).count(), 1)
self.assertEqual(db(db.t.a.minutes() == 30).count(), 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()
self.assertEqual(db(db.tt.aa.year() == 1971).count(), 2)
self.assertEqual(db(db.tt.aa.month() == 12).count(), 2)
self.assertEqual(db(db.tt.aa.day() == 21).count(), 3)
self.assertEqual(db(db.tt.aa.hour() == 11).count(), 1)
self.assertEqual(db(db.tt.aa.minutes() == 30).count(), 3)
self.assertEqual(db(db.tt.aa.seconds() == 0).count(), 3)
self.assertEqual(db(db.tt.aa.epoch()<365*24*3600).count(),1)
db.tt.drop()
class TestExpressions(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a', 'integer'))
self.assertEqual(db.t.insert(a=1), 1)
self.assertEqual(db.t.insert(a=2), 2)
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 == 4).count(), 1)
db.t.drop()
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.tt.insert(aa=3), 3)
self.assertEqual(db(db.tt.aa == 3).update(aa=db.tt.aa + 1), 1)
self.assertEqual(db(db.tt.aa == 4).count(), 1)
db.tt.drop()
class TestJoin(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t1', Field('a'))
db.define_table('t2', Field('a'), Field('b', db.t1))
i1 = db.t1.insert(a='1')
i2 = db.t1.insert(a='2')
i3 = db.t1.insert(a='3')
db.t2.insert(a='4', b=i1)
db.t2.insert(a='5', b=i2)
db.t2.insert(a='6', b=i2)
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t1', Field('aa'))
db.define_table('t2', Field('aa'), Field('b', db.t1))
i1 = db.t1.insert(aa='1')
i2 = db.t1.insert(aa='2')
i3 = db.t1.insert(aa='3')
db.t2.insert(aa='4', b=i1)
db.t2.insert(aa='5', b=i2)
db.t2.insert(aa='6', b=i2)
self.assertEqual(len(db(db.t1.id
== db.t2.b).select(orderby=db.t1.a
| db.t2.a)), 3)
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a
| db.t2.a)[2].t1.a, '2')
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a
| db.t2.a)[2].t2.a, '6')
== db.t2.b).select(orderby=db.t1.aa
| db.t2.aa)), 3)
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.aa)[2].t1.aa, '2')
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.aa)[2].t2.aa, '6')
self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)), 4)
orderby=db.t1.aa | db.t2.aa)), 4)
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[2].t1.a, '2')
orderby=db.t1.aa | db.t2.aa)[2].t1.aa, '2')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[2].t2.a, '6')
orderby=db.t1.aa | db.t2.aa)[2].t2.aa, '6')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[3].t1.a, '3')
orderby=db.t1.aa | db.t2.aa)[3].t1.aa, '3')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[3].t2.a, None)
self.assertEqual(len(db().select(db.t1.ALL, db.t2.id.count(),
orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, groupby=db.t1.a)),
orderby=db.t1.aa, groupby=db.t1.aa)),
3)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(),
self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a,
groupby=db.t1.a)[0]._extra[db.t2.id.count()],
orderby=db.t1.aa,
groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
1)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(),
self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a,
groupby=db.t1.a)[1]._extra[db.t2.id.count()],
orderby=db.t1.aa,
groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
2)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(),
self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a,
groupby=db.t1.a)[2]._extra[db.t2.id.count()],
orderby=db.t1.aa,
groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
0)
db.t1.drop()
db.t2.drop()
db.t1.drop()
db.define_table('person',Field('name'))
id = db.person.insert(name="max")
self.assertEqual(id.name,'max')
db.define_table('dog',Field('name'),Field('owner','reference person'))
db.dog.insert(name='skipper',owner=1)
row = db(db.person.id==db.dog.owner).select().first()
db.define_table('dog',Field('name'),Field('ownerperson','reference person'))
db.dog.insert(name='skipper',ownerperson=1)
row = db(db.person.id==db.dog.ownerperson).select().first()
self.assertEqual(row[db.person.name],'max')
self.assertEqual(row['person.name'],'max')
db.dog.drop()
self.assertEqual(len(db.person._referenced_by),0)
db.person.drop()
class TestMinMaxSum(unittest.TestCase):
class TestMinMaxSumAvg(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a', 'integer'))
self.assertEqual(db.t.insert(a=1), 1)
self.assertEqual(db.t.insert(a=2), 2)
self.assertEqual(db.t.insert(a=3), 3)
s = db.t.a.min()
self.assertEqual(db(db.t.id > 0).select(s)[0]._extra[s], 1)
self.assertEqual(db(db.t.id > 0).select(s).first()[s], 1)
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.tt.insert(aa=3), 3)
s = db.tt.aa.min()
self.assertEqual(db(db.tt.id > 0).select(s)[0]._extra[s], 1)
self.assertEqual(db(db.tt.id > 0).select(s).first()[s], 1)
self.assertEqual(db().select(s).first()[s], 1)
s = db.t.a.max()
s = db.tt.aa.max()
self.assertEqual(db().select(s).first()[s], 3)
s = db.t.a.sum()
s = db.tt.aa.sum()
self.assertEqual(db().select(s).first()[s], 6)
s = db.t.a.count()
s = db.tt.aa.count()
self.assertEqual(db().select(s).first()[s], 3)
db.t.drop()
s = db.tt.aa.avg()
self.assertEqual(db().select(s).first()[s], 2)
db.tt.drop()
class TestCache(unittest.TestCase):
def testRun(self):
from cache import CacheInRam
cache = CacheInRam()
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
db.t.insert(a='1')
r0 = db().select(db.t.ALL)
r1 = db().select(db.t.ALL, cache=(cache, 1000))
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
db.tt.insert(aa='1')
r0 = db().select(db.tt.ALL)
r1 = db().select(db.tt.ALL, cache=(cache, 1000))
self.assertEqual(len(r0),len(r1))
r2 = db().select(db.t.ALL, cache=(cache, 1000))
r2 = db().select(db.tt.ALL, cache=(cache, 1000))
self.assertEqual(len(r0),len(r2))
r3 = db().select(db.t.ALL, cache=(cache, 1000), cacheable=True)
r3 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
self.assertEqual(len(r0),len(r3))
r4 = db().select(db.t.ALL, cache=(cache, 1000), cacheable=True)
r4 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
self.assertEqual(len(r0),len(r4))
db.t.drop()
db.tt.drop()
class TestMigrations(unittest.TestCase):
def testRun(self):
db = DAL('sqlite://.storage.db')
db.define_table('t', Field('a'), migrate='.storage.table')
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'), migrate='.storage.table')
db.commit()
db.close()
db = DAL('sqlite://.storage.db')
db.define_table('t', Field('a'), Field('b'),
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'), Field('b'),
migrate='.storage.table')
db.commit()
db.close()
db = DAL('sqlite://.storage.db')
db.define_table('t', Field('a'), Field('b', 'text'),
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'), Field('b', 'text'),
migrate='.storage.table')
db.commit()
db.close()
db = DAL('sqlite://.storage.db')
db.define_table('t', Field('a'), migrate='.storage.table')
db.t.drop()
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'), migrate='.storage.table')
db.tt.drop()
db.commit()
db.close()
@@ -500,81 +511,81 @@ class TestMigrations(unittest.TestCase):
if os.path.exists('.storage.table'):
os.unlink('.storage.table')
class TestReferece(unittest.TestCase):
class TestReference(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('name'), Field('a','reference t'))
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('name'), Field('aa','reference tt'))
db.commit()
x = db.t.insert(name='max')
x = db.tt.insert(name='max')
assert x.id == 1
assert x['id'] == 1
x.a = x
assert x.a == 1
x.aa = x
assert x.aa == 1
x.update_record()
y = db.t[1]
assert y.a == 1
assert y.a.a.a.a.a.a.name == 'max'
z=db.t.insert(name='xxx', a = y)
assert z.a == y.id
db.t.drop()
y = db.tt[1]
assert y.aa == 1
assert y.aa.aa.aa.aa.aa.aa.name == 'max'
z=db.tt.insert(name='xxx', aa = y)
assert z.aa == y.id
db.tt.drop()
db.commit()
class TestClientLevelOps(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
db.commit()
db.t.insert(a="test")
rows1 = db(db.t.id>0).select()
rows2 = db(db.t.id>0).select()
db.tt.insert(aa="test")
rows1 = db(db.tt.id>0).select()
rows2 = db(db.tt.id>0).select()
rows3 = rows1 & rows2
assert len(rows3) == 2
rows4 = rows1 | rows2
assert len(rows4) == 1
rows5 = rows1.find(lambda row: row.a=="test")
rows5 = rows1.find(lambda row: row.aa=="test")
assert len(rows5) == 1
rows6 = rows2.exclude(lambda row: row.a=="test")
rows6 = rows2.exclude(lambda row: row.aa=="test")
assert len(rows6) == 1
rows7 = rows5.sort(lambda row: row.a)
rows7 = rows5.sort(lambda row: row.aa)
assert len(rows7) == 1
db.t.drop()
db.tt.drop()
db.commit()
class TestVirtualFields(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t', Field('a'))
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt', Field('aa'))
db.commit()
db.t.insert(a="test")
db.tt.insert(aa="test")
class Compute:
def a_upper(row): return row.t.a.upper()
db.t.virtualfields.append(Compute())
assert db(db.t.id>0).select().first().a_upper == 'TEST'
db.t.drop()
def a_upper(row): return row.tt.aa.upper()
db.tt.virtualfields.append(Compute())
assert db(db.tt.id>0).select().first().a_upper == 'TEST'
db.tt.drop()
db.commit()
class TestComputedFields(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db.define_table('t',
Field('a'),
Field('b',default='x'),
Field('c',compute=lambda r: r.a+r.b))
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('tt',
Field('aa'),
Field('bb',default='x'),
Field('cc',compute=lambda r: r.aa+r.bb))
db.commit()
id = db.t.insert(a="z")
self.assertEqual(db.t[id].c,'zx')
db.t.drop()
id = db.tt.insert(aa="z")
self.assertEqual(db.tt[id].cc,'zx')
db.tt.drop()
db.commit()
class TestImportExportFields(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name'))
db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2):
@@ -598,7 +609,7 @@ class TestImportExportFields(unittest.TestCase):
class TestImportExportUuidFields(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name'),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2):
@@ -622,7 +633,7 @@ class TestImportExportUuidFields(unittest.TestCase):
class TestDALDictImportExport(unittest.TestCase):
def testRun(self):
db = DAL('sqlite:memory:')
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name', default="Michael"),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name'))
dbdict = db.as_dict(flat=True, sanitize=False)
@@ -635,77 +646,88 @@ class TestDALDictImportExport(unittest.TestCase):
assert dbdict["items"]["person"]["items"]["name"]["default"] == db.person.name.default
assert dbdict
db2 = DAL(dbdict)
db2 = DAL(dbdict, check_reserved=['all'])
assert len(db.tables) == len(db2.tables)
assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
db.pet.drop()
db.commit()
db2.commit()
have_serializers = True
try:
import serializers
dbjson = db.as_json(sanitize=False)
assert isinstance(dbjson, basestring) and len(dbjson) > 0
db3 = DAL(serializers.loads_json(dbjson))
unicode_keys = True
if sys.version < "2.6.5":
unicode_keys = False
db3 = DAL(serializers.loads_json(dbjson,
unicode_keys=unicode_keys))
assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
db3.person.uuid.type == db.person.uuid.type
db3.pet.drop()
db3.person.drop()
db3.commit()
except ImportError:
pass
mpfc = "Monty Python's Flying Circus"
dbdict4 = {"uri": 'sqlite:memory:',
dbdict4 = {"uri": DEFAULT_URI,
"items":{"staff":{"items": {"name":
{"default":"Michael"},
"food":
{"default":"Spam"},
"show":
{"type": "reference show"}
"tvshow":
{"type": "reference tvshow"}
}},
"show":{"items": {"name":
"tvshow":{"items": {"name":
{"default":mpfc},
"rating":
{"type":"double"}}}}}
db4 = DAL(dbdict4)
db4 = DAL(dbdict4, check_reserved=['all'])
assert "staff" in db4.tables
assert "name" in db4.staff
assert db4.show.rating.type == "double"
assert (db4.show.insert(), db4.show.insert(name="Loriot"),
db4.show.insert(name="Il Mattatore")) == (1, 2, 3)
assert db4(db4.show).select().first().id == 1
assert db4(db4.show).select().first().name == mpfc
assert db4.tvshow.rating.type == "double"
assert (db4.tvshow.insert(), db4.tvshow.insert(name="Loriot"),
db4.tvshow.insert(name="Il Mattatore")) == (1, 2, 3)
assert db4(db4.tvshow).select().first().id == 1
assert db4(db4.tvshow).select().first().name == mpfc
dbdict5 = {"uri": 'sqlite:memory:'}
db5 = DAL(dbdict5)
db4.staff.drop()
db4.tvshow.drop()
db4.commit()
dbdict5 = {"uri": DEFAULT_URI}
db5 = DAL(dbdict5, check_reserved=['all'])
assert db5.tables in ([], None)
assert not (str(db5) in ("", None))
dbdict6 = {"uri": 'sqlite:memory:',
dbdict6 = {"uri": DEFAULT_URI,
"items":{"staff":{},
"show":{"items": {"name": {},
"tvshow":{"items": {"name": {},
"rating":
{"type":"double"}}}}}
db6 = DAL(dbdict6)
{"type":"double"}
}
}
}
}
db6 = DAL(dbdict6, check_reserved=['all'])
assert len(db6["staff"].fields) == 1
assert "name" in db6["show"].fields
assert "name" in db6["tvshow"].fields
assert db6.staff.insert() is not None
assert db6(db6.staff).select().first().id == 1
db6.staff.drop()
db6.show.drop()
db6.tvshow.drop()
db6.commit()
db4.staff.drop()
db4.show.drop()
db4.commit()
db2.pet.drop()
db2.person.drop()
db2.commit()
db.pet.drop()
db.person.drop()
db.commit()
if __name__ == '__main__':
+94 -38
View File
@@ -22,7 +22,7 @@ import urllib
import urllib2
import Cookie
import cStringIO
from email import MIMEBase, MIMEMultipart, MIMEText, Encoders, Header, message_from_string
from email import MIMEBase, MIMEMultipart, MIMEText, Encoders, Header, message_from_string, Charset
from gluon.contenttype import contenttype
from gluon.storage import Storage, StorageList, Settings, Messages
@@ -347,7 +347,10 @@ class Mail(object):
mail.send_mail() method
self.error: Exception message or None if above was successful
"""
# We don't want to use base64 encoding for unicode mail
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
def encode_header(key):
if [c for c in key if 32 > ord(c) or ord(c) > 127]:
return Header.Header(key.encode('utf-8'), 'utf-8')
@@ -1752,19 +1755,45 @@ class Auth(object):
self.add_membership(self.settings.everybody_group_id, user_id)
return user
def basic(self):
def basic(self, basic_auth_realm=False):
"""
perform basic login.
:param basic_auth_realm: optional basic http authentication realm.
:type basic_auth_realm: str or unicode or function or callable or boolean.
reads current.request.env.http_authorization
and returns basic_allowed,basic_accepted,user
and returns basic_allowed,basic_accepted,user.
if basic_auth_realm is defined is a callable it's return value
is used to set the basic authentication realm, if it's a string
its content is used instead. Otherwise basic authentication realm
is set to the application name.
If basic_auth_realm is None or False (the default) the behavior
is to skip sending any challenge.
"""
if not self.settings.allow_basic_login:
return (False, False, False)
basic = current.request.env.http_authorization
if basic_auth_realm:
if callable(basic_auth_realm):
basic_auth_realm = basic_auth_auth()
elif isinstance(basic_auth_realm, (unicode, str)):
basic_realm = unicode(basic_auth_realm)
elif basic_auth_realm is True:
basic_realm = u'' + current.request.application
http_401 = HTTP(401, u'Not Authorized',
**{'WWW-Authenticate': u'Basic realm="' + basic_realm + '"'})
if not basic or not basic[:6].lower() == 'basic ':
if basic_auth_realm:
raise http_401
return (True, False, False)
(username, password) = base64.b64decode(basic[6:]).split(':')
return (True, True, self.login_bare(username, password))
(username, sep, password) = base64.b64decode(basic[6:]).partition(':')
is_valid_user = sep and self.login_bare(username, password)
if not is_valid_user and basic_auth_realm:
raise http_401
return (True, True, is_valid_user)
def login_user(self, user):
"""
@@ -4402,9 +4431,9 @@ class Service(object):
request = current.request
response = current.response
if not data:
request = current.request
response = current.response
response.headers['Content-Type'] = 'application/json; charset=utf-8'
try:
data = json_parser.loads(request.body.read())
@@ -4446,6 +4475,8 @@ class Service(object):
return return_response(id, s)
else:
return ''
except HTTP, e:
raise e
except Service.JsonRpcException, e:
return return_error(id, e.code, e.info)
except BaseException:
@@ -4868,7 +4899,7 @@ class Wiki(object):
everybody = 'everybody'
rows_page = 25
def markmin_base(self,body):
return MARKMIN(body, extra=self.extra,
return MARKMIN(body, extra=self.settings.extra,
url=True, environment=self.env,
autolinks=lambda link: expand_one(link, {})).xml()
@@ -4902,29 +4933,43 @@ class Wiki(object):
controller, function, args = items[0], items[1], items[2:]
return LOAD(controller, function, args=args, ajax=True).xml()
def get_render(self):
if isinstance(self.settings.render, basestring):
r = getattr(self, "%s_render" % self.settings.render)
elif callable(self.settings.render):
r = self.settings.render
else:
raise ValueError("Invalid render type %s" % type(render))
return r
def __init__(self, auth, env=None, render='markmin',
manage_permissions=False, force_prefix='',
restrict_search=False, extra=None,
menu_groups=None, templates=None):
settings = self.settings = Settings()
# render: "markmin", "html", ..., <function>
settings.render = render
perms = settings.manage_permissions = manage_permissions
settings.force_prefix = force_prefix
settings.restrict_search = restrict_search
settings.extra = extra or {}
settings.menu_groups = menu_groups
settings.templates = templates
db = auth.db
self.env = env or {}
self.env['component'] = Wiki.component
if render == 'markmin':
render = self.markmin_render
elif render == 'html':
render = self.html_render
self.render = render
self.auth = auth
self.menu_groups = menu_groups
if self.auth.user:
self.force_prefix = force_prefix % self.auth.user
self.settings.force_prefix = force_prefix % self.auth.user
else:
self.force_prefix = force_prefix
self.settings.force_prefix = force_prefix
self.host = current.request.env.http_host
perms = self.manage_permissions = manage_permissions
self.restrict_search = restrict_search
self.extra = extra or {}
self.templates = templates
table_definitions = [
('wiki_page', {
@@ -4944,7 +4989,8 @@ class Wiki(object):
writable=perms, readable=perms,
default=[Wiki.everybody]),
Field('changelog'),
Field('html', 'text', compute=render,
Field('html', 'text',
compute=self.get_render(),
readable=False, writable=False),
auth.signature],
'vars':{'format':'%(title)s'}}),
@@ -4977,8 +5023,9 @@ class Wiki(object):
args += value['args']
db.define_table(key, *args, **value['vars'])
if self.templates is None and not self.manage_permissions:
self.templates = db.wiki_page.tags.contains('template')&\
if self.settings.templates is None and not \
self.settings.manage_permissions:
self.settings.templates = db.wiki_page.tags.contains('template')&\
db.wiki_page.can_read.contains('everybody')
def update_tags_insert(page, id, db=db):
@@ -5002,13 +5049,17 @@ class Wiki(object):
gid = group.id if group else db.auth_group.insert(
role='wiki_editor')
auth.add_membership(gid)
settings.lock_keys = True
# WIKI ACCESS POLICY
def not_authorized(self, page=None):
raise HTTP(401)
def can_read(self, page):
if 'everybody' in page.can_read or not self.manage_permissions:
if 'everybody' in page.can_read or not \
self.settings.manage_permissions:
return True
elif self.auth.user:
groups = self.auth.user_groups.values()
@@ -5038,11 +5089,11 @@ class Wiki(object):
return True
def can_see_menu(self):
if self.menu_groups is None:
if self.settings.menu_groups is None:
return True
if self.auth.user:
groups = self.auth.user_groups.values()
if any(t in self.menu_groups for t in groups):
if any(t in self.settings.menu_groups for t in groups):
return True
return False
@@ -5079,7 +5130,7 @@ class Wiki(object):
elif zero == '_cloud':
return self.cloud()
elif zero == '_preview':
return self.preview(self.render)
return self.preview(self.get_render())
def first_paragraph(self, page):
if not self.can_read(page):
@@ -5147,9 +5198,9 @@ class Wiki(object):
title_guess = ' '.join(c.capitalize() for c in slug.split('-'))
if not page:
if not (self.can_manage() or
slug.startswith(self.force_prefix)):
slug.startswith(self.settings.force_prefix)):
current.session.flash = 'slug must have "%s" prefix' \
% self.force_prefix
% self.settings.force_prefix
redirect(URL(args=('_create')))
db.wiki_page.can_read.default = [Wiki.everybody]
db.wiki_page.can_edit.default = [auth.user_group_role()]
@@ -5205,7 +5256,10 @@ class Wiki(object):
}
})
})
""" % dict(url=URL(args=('_preview')), urlmedia=URL(extension='load',args=('_editmedia'),vars=dict(embedded=1)))
""" % dict(url=URL(args=('_preview', slug)),
urlmedia=URL(extension='load',
args=('_editmedia',slug),
vars=dict(embedded=1)))
return dict(content=TAG[''](form, SCRIPT(script)))
def editmedia(self, slug):
@@ -5251,14 +5305,15 @@ class Wiki(object):
options=[OPTION(row.slug,_value=row.id) for row in slugs]
options.insert(0, OPTION('',_value=''))
fields = [Field("slug", default=current.request.args(1) or
self.force_prefix,
self.settings.force_prefix,
requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),]
if self.templates:
if self.settings.templates:
fields.append(
Field("from_template", "reference wiki_page",
requires=IS_EMPTY_OR(IS_IN_DB(db(self.templates),
db.wiki_page._id,
'%(slug)s')),
requires=IS_EMPTY_OR(
IS_IN_DB(db(self.settings.templates),
db.wiki_page._id,
'%(slug)s')),
comment=current.T(
"Choose Template or empty for new Page")))
form = SQLFORM.factory(*fields, **dict(_class="well span6"))
@@ -5300,7 +5355,7 @@ class Wiki(object):
request, db = current.request, self.auth.db
media = db.wiki_media(id)
if media:
if self.manage_permissions:
if self.settings.manage_permissions:
page = db.wiki_page(media.wiki_page)
if not self.can_read(page):
return self.not_authorized(page)
@@ -5399,7 +5454,7 @@ class Wiki(object):
query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\
(db.wiki_tag.name.belongs(tags))
query = query | db.wiki_page.title.contains(request.vars.q)
if self.restrict_search and not self.manage():
if self.settings.restrict_search and not self.manage():
query = query & (db.wiki_page.created_by == self.auth.user_id)
pages = db(query).select(count,
*fields, **dict(orderby=orderby or ~count,
@@ -5460,6 +5515,7 @@ class Wiki(object):
request = current.request
return render(request.post_vars)
if __name__ == '__main__':
import doctest
doctest.testmod()
+47 -15
View File
@@ -171,19 +171,29 @@ class IS_MATCH(Validator):
"""
def __init__(self, expression, error_message='invalid expression',
strict=False, search=False, extract=False):
strict=False, search=False, extract=False,
unicode=False):
if strict or not search:
if not expression.startswith('^'):
expression = '^(%s)' % expression
if strict:
if not expression.endswith('$'):
expression = '(%s)$' % expression
self.regex = re.compile(expression)
if unicode:
if not isinstance(expression,unicode):
expression = expression.decode('utf8')
self.regex = re.compile(expression,re.UNICODE)
else:
self.regex = re.compile(expression)
self.error_message = error_message
self.extract = extract
self.unicode = unicode
def __call__(self, value):
match = self.regex.search(value)
if self.unicode and not isinstance(value,unicode):
match = self.regex.search(str(value).decode('utf8'))
else:
match = self.regex.search(str(value))
if match is not None:
return (self.extract and match.group() or value, None)
return (value, translate(self.error_message))
@@ -2209,22 +2219,29 @@ class IS_DATE(Validator):
"""
def __init__(self, format='%Y-%m-%d',
error_message='enter date as %(format)s'):
error_message='enter date as %(format)s',
timezone = None):
self.format = translate(format)
self.error_message = str(error_message)
self.timezone = timezone
self.extremes = {}
def __call__(self, value):
ovalue = value
if isinstance(value, datetime.date):
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None)
try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format))
value = datetime.date(y, m, d)
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None)
except:
self.extremes.update(IS_DATETIME.nice(self.format))
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
def formatter(self, value):
if value is None:
@@ -2237,6 +2254,8 @@ class IS_DATE(Validator):
if year < 1900:
year = 2000
d = datetime.date(year, value.month, value.day)
if self.timezone is not None:
d = d + datetime.timedelta(seconds=self.timezone*3600)
return d.strftime(format)
@@ -2269,22 +2288,27 @@ class IS_DATETIME(Validator):
return dict(format=format)
def __init__(self, format='%Y-%m-%d %H:%M:%S',
error_message='enter date and time as %(format)s'):
error_message='enter date and time as %(format)s',
timezone=None):
self.format = translate(format)
self.error_message = str(error_message)
self.extremes = {}
self.timezone = timezone
def __call__(self, value):
ovalue = value
if isinstance(value, datetime.datetime):
return (value, None)
try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format))
value = datetime.datetime(y, m, d, hh, mm, ss)
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None)
except:
self.extremes.update(IS_DATETIME.nice(self.format))
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
def formatter(self, value):
if value is None:
@@ -2298,6 +2322,8 @@ class IS_DATETIME(Validator):
year = 2000
d = datetime.datetime(year, value.month, value.day,
value.hour, value.minute, value.second)
if self.timezone is not None:
d = d + datetime.timedelta(seconds=self.timezone*3600)
return d.strftime(format)
@@ -2326,7 +2352,8 @@ class IS_DATE_IN_RANGE(IS_DATE):
minimum=None,
maximum=None,
format='%Y-%m-%d',
error_message=None):
error_message=None,
timezone=None):
self.minimum = minimum
self.maximum = maximum
if error_message is None:
@@ -2338,17 +2365,19 @@ class IS_DATE_IN_RANGE(IS_DATE):
error_message = "enter date in range %(min)s %(max)s"
IS_DATE.__init__(self,
format=format,
error_message=error_message)
error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value):
ovalue = value
(value, msg) = IS_DATE.__call__(self, value)
if msg is not None:
return (value, msg)
if self.minimum and self.minimum > value:
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
if self.maximum and value > self.maximum:
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
return (value, None)
@@ -2376,7 +2405,8 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
minimum=None,
maximum=None,
format='%Y-%m-%d %H:%M:%S',
error_message=None):
error_message=None,
timezone=None):
self.minimum = minimum
self.maximum = maximum
if error_message is None:
@@ -2388,17 +2418,19 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
error_message = "enter date and time in range %(min)s %(max)s"
IS_DATETIME.__init__(self,
format=format,
error_message=error_message)
error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value):
ovalue = value
(value, msg) = IS_DATETIME.__call__(self, value)
if msg is not None:
return (value, msg)
if self.minimum and self.minimum > value:
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
if self.maximum and value > self.maximum:
return (value, translate(self.error_message) % self.extremes)
return (ovalue, translate(self.error_message) % self.extremes)
return (value, None)
+4
View File
@@ -434,6 +434,10 @@ class web2pyDialog(object):
self.try_stop_scheduler(t)
except:
pass
try:
newcron.stopcron()
except:
pass
try:
self.server.stop()
except:
+1 -1
View File
@@ -26,7 +26,7 @@ data = re.compile('\*/\s+/\*').sub(' ', data)
data = re.compile('[ ]+\n').sub('\n', data)
data = re.compile('\n\s*/[\*]+(?P<a>.*?)[\*]+/', re.DOTALL).sub(
'\n/*\g<a>*/\n', data)
data = re.compile('[ \t]+(?P<a>\S.+?){').sub('\g<a>{', data)
data = re.compile('[ \t]+(?P<a>\S.+?){').sub(' \g<a>{', data)
data = data.replace('}', '}\n')
print data
+1
View File
@@ -68,6 +68,7 @@ a2enmod proxy
a2enmod proxy_http
a2enmod headers
a2enmod expires
a2enmod wsgi
mkdir /etc/apache2/ssl
echo "creating a self signed certificate"
-17
View File
@@ -1,17 +0,0 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
before_script:
- pip install unittest2
- mysql -e 'create database test_pymysql;'
- mysql -e 'create database test_pymysql2;'
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
irc:
channels: "irc.freenode.org#web2py"
+1 -1
View File
@@ -4,7 +4,7 @@ import sys
"""
Author: Christopher Steel on behalf of Voice of Access
Copyright: Copyrighted (c) by Massimo Di Pierro (2007-2011)
Copyright: Copyrighted (c) by Massimo Di Pierro (2007-2013)
web2py_clone becomes part of the web2py distribution available
on Pypi via 'pip install web2py'