Compare commits

..

27 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
24 changed files with 1081 additions and 839 deletions

25
.travis.yml Normal file
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"

View File

@@ -1,3 +1,8 @@
## 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

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.3-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

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:

View File

@@ -1 +1 @@
Version 2.4.3-stable+timestamp.2013.03.11.14.40.17
Version 2.4.5-stable+timestamp.2013.03.18.17.37.19

View File

@@ -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-"]{

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 btn-mini',_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 btn-mini',_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 btn-mini',_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,45 +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">
<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>
<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 -->
@@ -140,330 +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">
<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>
{{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">
<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 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">
<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>
{{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">
<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>
{{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">
<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/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">
<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/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">
<button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Upload</button>
<div id="form7" class="row-fluid" style="display:none">
<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>

View File

@@ -36,7 +36,7 @@ Tweet attributes
<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"])),sanitize=True)}}
{{=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 }}

View File

@@ -0,0 +1 @@
session.connect(request,response,cookie_key='yoursecret')

View File

@@ -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-"]{

View File

@@ -43,7 +43,7 @@ random.shuffle(quotes)
<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 Powereb by web2py</a>
<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>

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',

View File

@@ -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-"]{

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':

View File

@@ -370,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')
#####
@@ -633,6 +633,7 @@ class BaseAdapter(ConnectionPool):
TRUE = 'T'
FALSE = 'F'
T_SEP = ' '
types = {
'boolean': 'CHAR(1)',
'string': 'CHAR(%(length)s)',
@@ -1810,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:
@@ -2418,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;')
@@ -3033,6 +3037,7 @@ class OracleAdapter(BaseAdapter):
class MSSQLAdapter(BaseAdapter):
drivers = ('pyodbc',)
T_SEP = 'T'
types = {
'boolean': 'BIT',
@@ -3935,7 +3940,7 @@ class IngresAdapter(BaseAdapter):
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()
@@ -4654,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
@@ -7052,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):
@@ -7181,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
@@ -8817,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)
@@ -9037,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
@@ -9237,7 +9264,7 @@ class Field(Expression):
def retrieve(self, name, path=None, nameonly=False):
"""
if nameonly==True return (filename, fullfilename) instead of
if nameonly==True return (filename, fullfilename) instead of
(filename, stream)
"""
self_uploadfield = self.uploadfield
@@ -9399,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,

View File

@@ -131,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()

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):

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():
"""

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):

View File

@@ -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

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__':

View File

@@ -4475,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:
@@ -4897,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()
@@ -4931,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', {
@@ -4973,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'}}),
@@ -5006,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):
@@ -5031,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()
@@ -5067,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
@@ -5108,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):
@@ -5176,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()]
@@ -5234,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):
@@ -5280,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"))
@@ -5329,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)
@@ -5428,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,
@@ -5489,6 +5515,7 @@ class Wiki(object):
request = current.request
return render(request.post_vars)
if __name__ == '__main__':
import doctest
doctest.testmod()

View File

@@ -2219,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:
@@ -2247,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)
@@ -2279,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:
@@ -2308,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)
@@ -2336,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:
@@ -2348,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)
@@ -2386,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:
@@ -2398,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)

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"