Compare commits

...
26 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
23 changed files with 1080 additions and 838 deletions
+25
View File
@@ -0,0 +1,25 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
env:
- DB=sqlite:memory
- DB=mysql://root:@localhost/test_w2p
- DB=postgres://postgres:@localhost/test_w2p
before_script:
- pip install unittest2
- if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
#Temporal solution to travis issue #155
- sudo chmod 777 /dev/shm
- sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
email: false
irc:
channels: "irc.freenode.org#web2py"
+5
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 ## 2.4.1- 2.4.3
- 2D GEO API: geoPoint, getLine, geoPolygon - 2D GEO API: geoPoint, getLine, geoPolygon
+2 -2
View File
@@ -5,7 +5,7 @@ all:
clean: clean:
rm -f httpserver.log rm -f httpserver.log
rm -f parameters*.py rm -f parameters*.py
rm -f -r applications/*/compiled rm -f -r applications/*/compiled
find ./ -name '*~' -exec rm -f {} \; find ./ -name '*~' -exec rm -f {} \;
find ./ -name '*.orig' -exec rm -f {} \; find ./ -name '*.orig' -exec rm -f {} \;
find ./ -name '*.rej' -exec rm -f {} \; find ./ -name '*.rej' -exec rm -f {} \;
@@ -30,7 +30,7 @@ update:
echo "remember that pymysql was tweaked" echo "remember that pymysql was tweaked"
src: src:
### Use semantic versioning ### Use semantic versioning
echo 'Version 2.4.4-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 ### rm -f all junk files
make clean make clean
### clean up baisc apps ### clean up baisc apps
+5
View File
@@ -6,6 +6,11 @@ It is written and programmable in Python. LGPLv3 License
Learn more at http://web2py.com Learn more at http://web2py.com
## Tests
[![Build Status](https://travis-ci.org/web2py/web2py.png)](https://travis-ci.org/web2py/web2py)
## Installation Instructions ## Installation Instructions
To start web2py there is NO NEED to install it. Just unzip and do: To start web2py there is NO NEED to install it. Just unzip and do:
+1 -1
View File
@@ -1 +1 @@
Version 2.4.4-stable+timestamp.2013.03.11.15.25.57 Version 2.4.5-stable+timestamp.2013.03.18.17.37.19
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
+375 -375
View File
@@ -1,69 +1,69 @@
{{extend 'layout.html'}} {{extend 'layout.html'}}
{{ {{
def all(items): 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): def peekfile(path,file,vars={},title=None):
args=(path,file) if 'app' in vars else (app,path,file) args=(path,file) if 'app' in vars else (app,path,file)
return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars)) return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars))
def editfile(path,file,vars={}): def editfile(path,file,vars={}):
args=(path,file) if 'app' in vars else (app,path,file) 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)) return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit', args=args, vars=vars))
def testfile(path,file): def testfile(path,file):
return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('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')"))), SPAN(T("Run tests in this file (to run all files, you may also use the button labelled 'test')"))),
_class='icon test', _class='icon test',
_href=URL('test', args=(app, file)), _href=URL('test', args=(app, file)),
_rel="tooltip", _rel="tooltip",
**{'_data-placement':'right', **{'_data-placement':'right',
'_data-original-title':T("Run tests in this file (to run all files, you may also use the button labelled 'test')")}) '_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={}): 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={}): 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): def file_upload_form(location, anchor=None):
form=FORM( form=FORM(
LABEL(T("upload file:")), LABEL(T("upload file:")),
INPUT(_type="file",_name="file"), INPUT(_type="file",_name="file"),
LABEL(T("and rename it:")), LABEL(T("and rename it:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""), INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)), INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)),
_action=URL('upload_file'), _action=URL('upload_file'),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def file_create_form(location, anchor=None, helptext=""): def file_create_form(location, anchor=None, helptext=""):
form=FORM( form=FORM(
LABEL(T("create file with filename:")), LABEL(T("create file with filename:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''), INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''),
TAG['SMALL'](helptext,_class="help-block"), TAG['SMALL'](helptext,_class="help-block"),
DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)), INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="id",_value=anchor), INPUT(_type="hidden",_name="id",_value=anchor),
_action=URL('create_file'), _action=URL('create_file'),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def upload_plugin_form(app, anchor=None): def upload_plugin_form(app, anchor=None):
form=FORM( form=FORM(
LABEL(T("upload plugin file:")), LABEL(T("upload plugin file:")),
INPUT(_type="file",_name="pluginfile"), INPUT(_type="file",_name="pluginfile"),
INPUT(_type="hidden",_name="id",_value=anchor), INPUT(_type="hidden",_name="id",_value=anchor),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def deletefile(arglist, vars={}): def deletefile(arglist, vars={}):
vars.update({'sender':request.function+'/'+app}) vars.update({'sender':request.function+'/'+app})
return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')), return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')),
SPAN(T('Delete this file (you will be asked to confirm deletion)'))), SPAN(T('Delete this file (you will be asked to confirm deletion)'))),
_href=URL('delete',args=arglist,vars=vars), _href=URL('delete',args=arglist,vars=vars),
_class='icon delete', _class='icon delete',
_rel="tooltip", _rel="tooltip",
**{'_data-placement':'right', **{'_data-placement':'right',
'_data-original-title':T('Delete this file (you will be asked to confirm deletion)')}) '_data-original-title':T('Delete this file (you will be asked to confirm deletion)')})
}} }}
{{block sectionclass}}design{{end}} {{block sectionclass}}design{{end}}
@@ -91,45 +91,45 @@ def deletefile(arglist, vars={}):
<!-- MODELS --> <!-- MODELS -->
<h3 id="_models" rel="pagebookmark"> <h3 id="_models" rel="pagebookmark">
<span class="component" onclick="collapse('models_inner');">{{=T("Models")}}</span> <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')}}"> <a href="#models" rel="tooltip" data-placement="right" data-original-title="{{=T('The data representation, define database tables and sets')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("The data representation, define database tables and sets")}}</span> <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> </a><span id="models" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="models_inner" class="component_contents"> <div id="models_inner" class="component_contents">
{{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}} {{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL(a=app,c='appadmin',f='index'), T('database administration'))}} {{=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):}} {{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')}} {{=button(URL('peek/%s/databases/sql.log'%app), 'sql.log')}}
{{pass}} {{pass}}
{{=button(URL(a=app, c='appadmin',f='graph_model'), T('graph model'))}} {{=button(URL(a=app, c='appadmin',f='graph_model'), T('graph model'))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for m in models:}} {{for m in models:}}
{{id="models__"+m.replace('.','__')}} {{id="models__"+m.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('models',m, dict(id=id))}} {{=editfile('models',m, dict(id=id))}}
{{=deletefile([app, 'models', m], dict(id=id, id2='models'))}} {{=deletefile([app, 'models', m], dict(id=id, id2='models'))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('models',m, dict(id=id))}} {{=peekfile('models',m, dict(id=id))}}
</span> </span>
<span class="extras"> <span class="extras">
{{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}} {{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form1').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form1').slideToggle()" class="btn btn-mini">Create</button>
<div id="form1" class="row-fluid" style="display:none"> <div id="form1" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div> <div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- FIND CONTROLLER FUNCTIONS --> <!-- FIND CONTROLLER FUNCTIONS -->
@@ -140,330 +140,330 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
<!-- CONTROLLERS --> <!-- CONTROLLERS -->
<h3 id="_controllers" rel="pagebookmark"> <h3 id="_controllers" rel="pagebookmark">
<span class="component" onclick="collapse('controllers_inner');">{{=T("Controllers")}}</span> <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')}}"> <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()}} {{=helpicon()}}
<span>{{=T("The application logic, each URL path is mapped in one exposed function in the controller")}}</span> <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> </a><span id="controllers" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="controllers_inner" class="component_contents"> <div id="controllers_inner" class="component_contents">
{{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}} {{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}} {{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}}
{{=button(URL('test',args=app), T("test"))}} {{=button(URL('test',args=app), T("test"))}}
{{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}} {{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for c in controllers:}} {{for c in controllers:}}
{{id="controllers__"+c.replace('.','__')}} {{id="controllers__"+c.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('controllers',c, dict(id=id))}} {{=editfile('controllers',c, dict(id=id))}}
{{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}} {{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}}
{{=testfile('controllers',c)}} {{=testfile('controllers',c)}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('controllers',c, dict(id=id))}} {{=peekfile('controllers',c, dict(id=id))}}
</span> </span>
<span class="extras celled"> <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]]))}} {{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> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form2').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form2').slideToggle()" class="btn btn-mini">Create</button>
<div id="form2" class="row-fluid" style="display:none"> <div id="form2" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div> <div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- VIEWS --> <!-- VIEWS -->
<h3 id="_views" rel="pagebookmark"> <h3 id="_views" rel="pagebookmark">
<span class="component" onclick="collapse('views_inner');">{{=T("Views")}}</span> <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')}}"> <a href="#views" rel="tooltip" data-placement="right" data-original-title="{{=T('The presentations layer, views are also known as templates')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("The presentations layer, views are also known as templates")}}</span> <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> </a><span id="views" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="views_inner" class="component_contents"> <div id="views_inner" class="component_contents">
{{if not views:}}<p><strong>{{=T("There are no views")}}</strong></p>{{else:}} {{if not views:}}<p><strong>{{=T("There are no views")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(LAYOUTS_APP, T("download layouts"))}} {{=button(LAYOUTS_APP, T("download layouts"))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for c in views:}} {{for c in views:}}
{{id="views__"+c.replace('/','__').replace('.','__')}} {{id="views__"+c.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('views',c, dict(id=id))}} {{=editfile('views',c, dict(id=id))}}
{{=deletefile([app, 'views', c], dict(id=id, id2='views'))}} {{=deletefile([app, 'views', c], dict(id=id, id2='views'))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('views',c, dict(id=id))}} {{=peekfile('views',c, dict(id=id))}}
</span> </span>
<span class="extras celled celled-one"> <span class="extras celled celled-one">
{{if extend.has_key(c):}}{{=T("extends")}} <b>{{=extend[c]}}</b> {{pass}} {{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]]))}} {{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form3').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form3').slideToggle()" class="btn btn-mini">Create</button>
<div id="form3" class="row-fluid" style="display:none"> <div id="form3" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div> <div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- LANGUAGES --> <!-- LANGUAGES -->
<h3 id="_languages" rel="pagebookmark"> <h3 id="_languages" rel="pagebookmark">
<span class="component" onclick="collapse('languages_inner');">{{=T("Languages")}}</span> <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')}}"> <a href="#languages" rel="tooltip" data-placement="right" data-original-title="{{=T('Translation strings for the application')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("Translation strings for the application")}}</span> <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> </a><span id="languages" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="languages_inner" class="component_contents"> <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:}} {{if not languages:}}<p><strong>{{=T("There are no translators, only default language is supported")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL('update_languages/'+app), T('update all languages'))}} {{=button(URL('update_languages/'+app), T('update all languages'))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for lang in sorted(languages): {{for lang in sorted(languages):
file = lang+'.py' file = lang+'.py'
id = "languages__"+file.replace('.','__')}} id = "languages__"+file.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="li-controls"> <span class="li-controls">
<span class="filetools controls"> <span class="filetools controls">
{{=editlanguagefile('languages',file)}} {{=editlanguagefile('languages',file)}}
{{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}} {{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}}
</span> </span>
<span class=""> <span class="">
{{=peekfile('languages',file, dict(id=id))}} {{=peekfile('languages',file, dict(id=id))}}
</span> </span>
</span> <!-- /li-row --> </span> <!-- /li-row -->
<span class="extras celled"> <span class="extras celled">
( (
{{=T("Plural-Forms:")}} {{=T("Plural-Forms:")}}
{{p=languages[lang][3:7]}} {{p=languages[lang][3:7]}}
{{if p[2] == 'default':}} {{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])}} <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:}} {{else:}}
{{if p[3] == 1:}} {{if p[3] == 1:}}
{{=B(T("are not used"))}} {{=B(T("are not used"))}}
{{else:}} {{else:}}
{{pfile=p[0]}} {{pfile=p[0]}}
{{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;"> {{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;">
<span class="filetools controls"> <span class="filetools controls">
{{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}} {{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('languages',pfile,dict(id=id))}} {{=peekfile('languages',pfile,dict(id=id))}}
</span></span> </span></span>
{{else:}} {{else:}}
<b>{{=T("are not used yet")}}</b> <b>{{=T("are not used yet")}}</b>
{{pass}} {{pass}}
{{pass}} {{pass}}
{{pass}} {{pass}}
) )
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form4').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form4').slideToggle()" class="btn btn-mini">Create</button>
<div id="form4" class="row-fluid" style="display:none"> <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 class="span3">{{=file_create_form('%s/languages/' % app, 'languages', T('(something like "it-it")'))}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- STATIC --> <!-- STATIC -->
<h3 id="_static" rel="pagebookmark"> <h3 id="_static" rel="pagebookmark">
<span class="component" onclick="collapse('static_inner');">{{=T("Static")}}</span> <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')}}"> <a href="#static" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are served without processing, your images go here')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("These files are served without processing, your images go here")}}</span> <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> </a><span id="static" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="static_inner" class="component_contents"> <div id="static_inner" class="component_contents">
{{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}} {{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{ {{
path=[] path=[]
for file in statics+['']: for file in statics+['']:
items=file.split('/') items=file.split('/')
file_path=items[:-1] file_path=items[:-1]
filename=items[-1] filename=items[-1]
while path!=file_path: while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(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)]) path.append(file_path[len(path)])
thispath='static__'+'__'.join(path) thispath='static__'+'__'.join(path)
}} }}
<li class="folder"><i>&nbsp;</i> <li class="folder"><i>&nbsp;</i>
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a> <a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{ <ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else: else:
path = path[:-1] path = path[:-1]
}} }}
</ul></li> </ul></li>
{{ {{
pass pass
pass pass
if filename: if filename:
}} }}
<li> <li>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}} {{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}}
</span> </span>
<span class="file"> <span class="file">
<a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a> <a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a>
</span> </span>
</li>{{ </li>{{
pass pass
pass pass
}} }}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form5" class="row-fluid" style="display:none"> <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_create_form('%s/static/' % app, 'static')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div> <div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- MODULES --> <!-- MODULES -->
<h3 id="_modules" rel="pagebookmark"> <h3 id="_modules" rel="pagebookmark">
<span class="component" onclick="collapse('modules_inner');">{{=T("Modules")}}</span> <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')}}"> <a href="#modules" rel="tooltip" data-placement="right" data-original-title="{{=T('Additional code for your application')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("Additional code for your application")}}</span> <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> </a><span id="modules" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="modules_inner" class="component_contents"> <div id="modules_inner" class="component_contents">
{{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}} {{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for m in modules:}} {{for m in modules:}}
{{id="modules__"+m.replace('/','__').replace('.','__')}} {{id="modules__"+m.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetols controls"> <span class="filetols controls">
{{=editfile('modules',m,dict(id=id))}} {{=editfile('modules',m,dict(id=id))}}
{{if m!='__init__.py':}} {{if m!='__init__.py':}}
{{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}} {{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}}
{{pass}} {{pass}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('modules',m, dict(id=id))}} {{=peekfile('modules',m, dict(id=id))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form6').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form5" class="row-fluid" style="display:none"> <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_create_form('%s/modules/' % app, 'modules')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div> <div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- PRIVATE --> <!-- PRIVATE -->
<h3 id="_private" rel="pagebookmark"> <h3 id="_private" rel="pagebookmark">
<span class="component" onclick="collapse('private_inner');">{{=T("Private files")}}</span> <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')}}"> <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()}} {{=helpicon()}}
<span>{{=T("These files are not served, they are only available from within your app")}}</span> <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> </a><span id="private" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="private_inner" class="component_contents"> <div id="private_inner" class="component_contents">
{{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}} {{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{ {{
path=[] path=[]
for file in privates+['']: for file in privates+['']:
items=file.split('/') items=file.split('/')
file_path=items[:-1] file_path=items[:-1]
filename=items[-1] filename=items[-1]
while path!=file_path: while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(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)]) path.append(file_path[len(path)])
thispath='private__'+'__'.join(path) thispath='private__'+'__'.join(path)
}} }}
<li class="folder"> <li class="folder">
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a> <a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{ <ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else: else:
path = path[:-1] path = path[:-1]
}} }}
</ul> </ul>
</li> </li>
{{ {{
pass pass
pass pass
if filename: if filename:
}} }}
<li> <li>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}} {{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('private',file, dict(id="private"))}} {{=peekfile('private',file, dict(id="private"))}}
</span> </span>
</li>{{ </li>{{
pass pass
pass pass
}} }}
{{pass}} {{pass}}
</ul> </ul>
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form6').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form6" class="row-fluid" style="display:none"> <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_create_form('%s/private/' % app, 'private')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div> <div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- PLUGINS --> <!-- PLUGINS -->
<h3 id="_plugins" rel="pagebookmark"> <h3 id="_plugins" rel="pagebookmark">
<span class="component" onclick="collapse('plugins_inner');">{{=T("Plugins")}}</span> <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]')}}"> <a href="#plugins" rel="tooltip" data-placement="right" data-original-title="{{=T('To create a plugin, name a file/folder plugin_[name]')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("To create a plugin, name a file/folder plugin_[name]")}}</span> <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> </a><span id="plugins" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="plugins_inner" class="component_contents"> <div id="plugins_inner" class="component_contents">
<div class="controls comptools"> <div class="controls comptools">
{{=button(PLUGINS_APP, T('download plugins'))}} {{=button(PLUGINS_APP, T('download plugins'))}}
</div> </div>
{{if plugins:}} {{if plugins:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for plugin in plugins:}} {{for plugin in plugins:}}
{{id="plugins__"+plugin.replace('/','__').replace('.','__')}} {{id="plugins__"+plugin.replace('/','__').replace('.','__')}}
<li id="{{=id}}"> <li id="{{=id}}">
{{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}} {{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}}
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{else:}} {{else:}}
<p><strong>{{=T('There are no plugins')}}</strong></p> <p><strong>{{=T('There are no plugins')}}</strong></p>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Upload</button> <button onclick="jQuery('#form8').slideToggle()" class="btn btn-mini">Upload</button>
<div id="form7" class="row-fluid" style="display:none"> <div id="form8" class="row-fluid" style="display:none">
<div class="row-fluid"> <div class="row-fluid">
<div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div> <div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
+1
View File
@@ -0,0 +1 @@
session.connect(request,response,cookie_key='yoursecret')
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
@@ -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 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="{{=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/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> </div>
</div> </div>
+72 -66
View File
@@ -4,8 +4,8 @@
'!langcode!': 'it', '!langcode!': 'it',
'!langname!': 'Italiano', '!langname!': 'Italiano',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ',
'%(nrows)s records found': '%(nrows)s records found', '%(nrows)s records found': '%(nrows)s record trovati',
'%d seconds ago': '%d seconds ago', '%d seconds ago': '%d secondi fa',
'%s %%{row} deleted': '%s righe ("record") cancellate', '%s %%{row} deleted': '%s righe ("record") cancellate',
'%s %%{row} updated': '%s righe ("record") modificate', '%s %%{row} updated': '%s righe ("record") modificate',
'%s selected': '%s selezionato', '%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', 'About': 'About',
'Access Control': 'Access Control', 'Access Control': 'Controllo Accessi',
'Add': 'Add', 'Add': 'Aggiungi',
'Administrative Interface': 'Administrative Interface', 'Administrative Interface': 'Interfaccia Amministrativa',
'Administrative interface': 'Interfaccia amministrativa', 'Administrative interface': 'Interfaccia amministrativa',
'Ajax Recipes': 'Ajax Recipes', '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', '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', 'Available Databases and Tables': 'Database e tabelle disponibili',
'Back': 'Back', 'Back': 'Indietro',
'Buy this book': 'Buy this book', 'Buy this book': 'Compra questo libro',
'cache': 'cache', 'cache': 'cache',
'Cache': 'Cache', 'Cache': 'Cache',
'Cache Keys': 'Cache Keys', 'Cache Keys': 'Cache Keys',
'Cannot be empty': 'Non può essere vuoto', 'Cannot be empty': 'Non può essere vuoto',
'Change password': 'Change password', 'Change password': 'Cambia Password',
'change password': 'Cambia password', 'change password': 'Cambia password',
'Check to delete': 'Seleziona per cancellare', 'Check to delete': 'Seleziona per cancellare',
'Clear': 'Clear', 'Clear': 'Resetta',
'Clear CACHE?': 'Clear CACHE?', 'Clear CACHE?': 'Resetta CACHE?',
'Clear DISK': 'Clear DISK', 'Clear DISK': 'Resetta DISK',
'Clear RAM': 'Clear RAM', 'Clear RAM': 'Resetta RAM',
'Client IP': 'Client IP', 'Client IP': 'Client IP',
'Close': 'Close', 'Close': 'Chiudi',
'Cognome': 'Cognome', 'Cognome': 'Cognome',
'Community': 'Community', 'Community': 'Community',
'Components and Plugins': 'Components and Plugins', 'Components and Plugins': 'Componenti and Plugin',
'contains': 'contains', 'contains': 'contiene',
'Controller': 'Controller', 'Controller': 'Controller',
'Copyright': 'Copyright', 'Copyright': 'Copyright',
'Created By': 'Created By', 'Created By': 'Creato Da',
'Created On': 'Created On', 'Created On': 'Creato Il',
'CSV': 'CSV', 'CSV': 'CSV',
'CSV (hidden cols)': 'CSV (hidden cols)', 'CSV (hidden cols)': 'CSV (hidden cols)',
'Current request': 'Richiesta (request) corrente', 'Current request': 'Richiesta (request) corrente',
@@ -61,7 +62,7 @@
'Database %s select': 'Database %s select', 'Database %s select': 'Database %s select',
'db': 'db', 'db': 'db',
'DB Model': 'Modello di DB', 'DB Model': 'Modello di DB',
'Delete': 'Delete', 'Delete': 'Cancella',
'Delete:': 'Cancella:', 'Delete:': 'Cancella:',
'Demo': 'Demo', 'Demo': 'Demo',
'Deployment Recipes': 'Deployment Recipes', 'Deployment Recipes': 'Deployment Recipes',
@@ -71,7 +72,7 @@
'Disk Cache Keys': 'Disk Cache Keys', 'Disk Cache Keys': 'Disk Cache Keys',
'Disk Cleared': 'Disk Cleared', 'Disk Cleared': 'Disk Cleared',
'Documentation': 'Documentazione', '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!', 'done!': 'fatto!',
'Download': 'Download', 'Download': 'Download',
'E-mail': 'E-mail', 'E-mail': 'E-mail',
@@ -79,18 +80,19 @@
'Edit current record': 'Modifica record corrente', 'Edit current record': 'Modifica record corrente',
'edit profile': 'modifica profilo', 'edit profile': 'modifica profilo',
'Edit This App': 'Modifica questa applicazione', 'Edit This App': 'Modifica questa applicazione',
'Email and SMS': 'Email and SMS', 'Email and SMS': 'Email e SMS',
'Email non valida': 'Email non valida', 'Email non valida': 'Email non valida',
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'inserisci un intero tra %(min)g e %(max)g',
'Errors': 'Errors', 'Errors': 'Errori',
'Errors in form, please check it out.': 'Errors in form, please check it out.', 'Errors in form, please check it out.': 'Errori nel form, ricontrollalo',
'export as csv file': 'esporta come file CSV', 'export as csv file': 'esporta come file CSV',
'Export:': 'Export:', 'Export:': 'Esporta:',
'FAQ': 'FAQ', 'FAQ': 'FAQ',
'First name': 'Nome', 'First name': 'Nome',
'Forgot username?': 'Forgot username?', 'Forgot username?': 'Dimenticato lo username?',
'Forms and Validators': 'Forms and Validators', 'Forms and Validators': 'Forms and Validators',
'Free Applications': 'Free Applications', 'Free Applications': 'Free Applications',
'Graph Model': 'Graph Model',
'Group %(group_id)s created': 'Group %(group_id)s created', 'Group %(group_id)s created': 'Group %(group_id)s created',
'Group ID': 'ID Gruppo', 'Group ID': 'ID Gruppo',
'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s',
@@ -100,69 +102,70 @@
'Hello World': 'Salve Mondo', 'Hello World': 'Salve Mondo',
'Hello World in a flash!': 'Salve Mondo in un flash!', 'Hello World in a flash!': 'Salve Mondo in un flash!',
'Home': 'Home', 'Home': 'Home',
'How did you get here?': 'How did you get here?', 'How did you get here?': 'Come sei arrivato qui?',
'HTML': 'HTML', 'HTML': 'HTML',
'import': 'import', 'import': 'importa',
'Import/Export': 'Importa/Esporta', 'Import/Export': 'Importa/Esporta',
'Index': 'Indice', 'Index': 'Indice',
'insert new': 'inserisci nuovo', 'insert new': 'inserisci nuovo',
'insert new %s': 'inserisci nuovo %s', 'insert new %s': 'inserisci nuovo %s',
'Internal State': 'Stato interno', 'Internal State': 'Stato interno',
'Introduction': 'Introduction', 'Introduction': 'Introduzione',
'Invalid email': 'Email non valida', 'Invalid email': 'Email non valida',
'Invalid login': 'Invalid login', 'Invalid login': 'Login non valido',
'Invalid Query': 'Richiesta (query) non valida', 'Invalid Query': 'Richiesta (query) non valida',
'invalid request': 'richiesta non valida', 'invalid request': 'richiesta non valida',
'Is Active': 'Is Active', 'Is Active': "E' attivo",
'Key': 'Key', 'Key': 'Chiave',
'Last name': 'Cognome', 'Last name': 'Cognome',
'Layout': 'Layout', 'Layout': 'Layout',
'Layout Plugins': 'Layout Plugins', 'Layout Plugins': 'Layout Plugins',
'Layouts': 'Layouts', 'Layouts': 'Layouts',
'Live Chat': 'Live Chat', 'Live Chat': 'Live Chat',
'Logged in': 'Logged in', 'Logged in': 'Loggato',
'Logged out': 'Logged out', 'Logged out': 'Disconnesso',
'login': 'accesso', 'login': 'accesso',
'Login': 'Login', 'Login': 'Login',
'logout': 'uscita', 'logout': 'uscita',
'Logout': 'Logout', 'Logout': 'Logout',
'Lost Password': 'Lost Password', 'Lost Password': 'Password Smarrita',
'Lost password?': 'Lost password?', 'Lost password?': 'Password smarrita?',
'lost password?': 'dimenticato la password?', 'lost password?': 'dimenticato la password?',
'Main Menu': 'Menu principale', 'Main Menu': 'Menu principale',
'Manage Cache': 'Manage Cache', 'Manage Cache': 'Manage Cache',
'Menu Model': 'Menu Modelli', 'Menu Model': 'Menu Modelli',
'Modified By': 'Modified By', 'Modified By': 'Modificato da',
'Modified On': 'Modified On', 'Modified On': 'Modificato il',
'My Sites': 'My Sites', 'My Sites': 'My Sites',
'Name': 'Nome', 'Name': 'Nome',
'New': 'New', 'New': 'Nuovo',
'New password': 'New password', 'New password': 'Nuova password',
'New Record': 'Nuovo elemento (record)', 'New Record': 'Nuovo elemento (record)',
'new record inserted': 'nuovo record inserito', 'new record inserted': 'nuovo record inserito',
'next 100 rows': 'prossime 100 righe', 'next 100 rows': 'prossime 100 righe',
'No databases in this application': 'Nessun database presente in questa applicazione', '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', 'Nome': 'Nome',
'Non può essere vuoto': 'Non può essere vuoto', 'Non può essere vuoto': 'Non può essere vuoto',
'not authorized': 'non autorizzato', 'not authorized': 'non autorizzato',
'Object or table name': 'Object or table name', 'Object or table name': 'Oggeto o nome tabella',
'Old password': 'Old password', 'Old password': 'Vecchia password',
'Online examples': 'Vedere gli esempi', 'Online examples': 'Vedere gli esempi',
'Or': 'Or', 'Or': 'O',
'or import from csv file': 'oppure importa da file CSV', 'or import from csv file': 'oppure importa da file CSV',
'Origin': 'Origine', 'Origin': 'Origine',
'Other Plugins': 'Other Plugins', 'Other Plugins': 'Other Plugins',
'Other Recipes': 'Other Recipes', 'Other Recipes': 'Other Recipes',
'Overview': 'Overview', 'Overview': 'Overview',
'Password': 'Password', 'Password': 'Password',
"Password fields don't match": "Password fields don't match", "Password fields don't match": 'I campi password non sono uguali',
'please input your password again': 'please input your password again', 'please input your password again': 'perfavore reimmeti la tua password',
'Plugins': 'Plugins', 'Plugins': 'Plugins',
'Powered by': 'Powered by', 'Powered by': 'Powered by',
'Preface': 'Preface', 'Preface': 'Preface',
'previous 100 rows': '100 righe precedenti', 'previous 100 rows': '100 righe precedenti',
'Profile': 'Profile', 'Profile': 'Profilo',
'pygraphviz library not found': 'pygraphviz library not found',
'Python': 'Python', 'Python': 'Python',
'Query:': 'Richiesta (query):', 'Query:': 'Richiesta (query):',
'Quick Examples': 'Quick Examples', 'Quick Examples': 'Quick Examples',
@@ -174,28 +177,30 @@
'record does not exist': 'il record non esiste', 'record does not exist': 'il record non esiste',
'Record ID': 'Record ID', 'Record ID': 'Record ID',
'Record id': 'Record id', 'Record id': 'Record id',
'Register': 'Register', 'Register': 'Registrati',
'register': 'registrazione', 'register': 'registrazione',
'Registration identifier': 'Registration identifier', 'Registration identifier': 'Registration identifier',
'Registration key': 'Chiave di Registazione', 'Registration key': 'Chiave di Registazione',
'Registration successful': 'Registration successful', 'Registration successful': 'Registrazione avvenuta',
'Remember me (for 30 days)': 'Remember me (for 30 days)', 'reload': 'reload',
'Request reset password': 'Request reset password', 'Remember me (for 30 days)': 'Ricordami (per 30 giorni)',
'Request reset password': 'Richiedi il reset della password',
'Reset Password key': 'Resetta chiave Password ', 'Reset Password key': 'Resetta chiave Password ',
'Role': 'Ruolo', 'Role': 'Ruolo',
'Rows in Table': 'Righe nella tabella', 'Rows in Table': 'Righe nella tabella',
'Rows selected': 'Righe selezionate', 'Rows selected': 'Righe selezionate',
'Save profile': 'Save profile', 'Save model as...': 'Salva modello come...',
'Search': 'Search', 'Save profile': 'Salva profilo',
'Search': 'Ricerca',
'Semantic': 'Semantic', 'Semantic': 'Semantic',
'Services': 'Services', 'Services': 'Servizi',
'Size of cache:': 'Size of cache:', 'Size of cache:': 'Size of cache:',
'starts with': 'starts with', 'starts with': 'comincia con',
'state': 'stato', 'state': 'stato',
'Statistics': 'Statistics', 'Statistics': 'Statistics',
'Stylesheet': 'Foglio di stile (stylesheet)', 'Stylesheet': 'Foglio di stile (stylesheet)',
'submit': 'submit', 'submit': 'Inviai',
'Submit': 'Submit', 'Submit': 'Invia',
'Support': 'Support', 'Support': 'Support',
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
'Table': 'tabella', 'Table': 'tabella',
@@ -208,12 +213,13 @@
'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", '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)', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)',
'Timestamp': 'Ora (timestamp)', 'Timestamp': 'Ora (timestamp)',
'too short': 'too short', 'too short': 'troppo corto',
'TSV (Excel compatible)': 'TSV (Excel compatible)', 'Traceback': 'Traceback',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatible, hidden cols)', 'TSV (Excel compatible)': 'TSV (Excel compatibile)',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatibile, hidden cols)',
'Twitter': 'Twitter', 'Twitter': 'Twitter',
'unable to parse csv file': 'non riesco a decodificare questo file CSV', 'unable to parse csv file': 'non riesco a decodificare questo file CSV',
'Update': 'Update', 'Update': 'Aggiorna',
'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).', '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', '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 Profile updated': 'User %(id)s Profile updated',
'User %(id)s Registered': 'User %(id)s Registered', 'User %(id)s Registered': 'User %(id)s Registered',
'User ID': 'ID Utente', 'User ID': 'ID Utente',
'value already in database or empty': 'value already in database or empty', 'value already in database or empty': 'valore già presente nel database o vuoto',
'Verify Password': 'Verify Password', 'Verify Password': 'Verifica Password',
'Videos': 'Videos', 'Videos': 'Videos',
'View': 'Vista', 'View': 'Vista',
'Welcome': 'Welcome', 'Welcome': 'Welcome',
'Welcome %s': 'Benvenuto %s', 'Welcome %s': 'Benvenuto %s',
'Welcome to web2py': 'Benvenuto su web2py', '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', 'Which called the function %s located in the file %s': 'che ha chiamato la funzione %s presente nel file %s',
'XML': 'XML', 'XML': 'XML',
'You are successfully running web2py': 'Stai eseguendo web2py con successo', 'You are successfully running web2py': 'Stai eseguendo web2py con successo',
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
+63 -30
View File
@@ -52,7 +52,7 @@ def set_global(var, val):
class FPDF(object): class FPDF(object):
"PDF Generation class" "PDF Generation class"
def __init__(self, orientation='P',unit='mm',format='A4'): def __init__(self, orientation='P',unit='mm',format='A4'):
# Some checks # Some checks
self._dochecks() self._dochecks()
@@ -356,7 +356,7 @@ class FPDF(object):
elif (self.current_font['desc']['MissingWidth']) : elif (self.current_font['desc']['MissingWidth']) :
w += self.current_font['desc']['MissingWidth'] w += self.current_font['desc']['MissingWidth']
#elif (isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; } #elif (isset($this->CurrentFont['MissingWidth'])) { $w += $this->CurrentFont['MissingWidth']; }
else: else:
w += 500 w += 500
else: else:
for i in xrange(0, l): for i in xrange(0, l):
@@ -373,6 +373,21 @@ class FPDF(object):
"Draw a line" "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)) 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=''): def rect(self, x,y,w,h,style=''):
"Draw a rectangle" "Draw a rectangle"
if(style=='F'): if(style=='F'):
@@ -401,10 +416,10 @@ class FPDF(object):
global SYSTEM_TTFONTS global SYSTEM_TTFONTS
if os.path.exists(fname): if os.path.exists(fname):
ttffilename = fname ttffilename = fname
elif (FPDF_FONT_DIR and elif (FPDF_FONT_DIR and
os.path.exists(os.path.join(FPDF_FONT_DIR, fname))): os.path.exists(os.path.join(FPDF_FONT_DIR, fname))):
ttffilename = 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))): os.path.exists(os.path.join(SYSTEM_TTFONTS, fname))):
ttffilename = os.path.join(SYSTEM_TTFONTS, fname) ttffilename = os.path.join(SYSTEM_TTFONTS, fname)
else: else:
@@ -450,7 +465,7 @@ class FPDF(object):
fh = open(unifilename, "w") fh = open(unifilename, "w")
pickle.dump(font_dict, fh) pickle.dump(font_dict, fh)
fh.close() fh.close()
except IOError as e: except IOError, e:
if not e.errno == errno.EACCES: if not e.errno == errno.EACCES:
raise # Not a permission error. raise # Not a permission error.
del ttf del ttf
@@ -459,11 +474,11 @@ class FPDF(object):
else: else:
sbarr = range(0,32) sbarr = range(0,32)
self.fonts[fontkey] = { self.fonts[fontkey] = {
'i': len(self.fonts)+1, 'type': font_dict['type'], 'i': len(self.fonts)+1, 'type': font_dict['type'],
'name': font_dict['name'], 'desc': font_dict['desc'], 'name': font_dict['name'], 'desc': font_dict['desc'],
'up': font_dict['up'], 'ut': font_dict['ut'], 'up': font_dict['up'], 'ut': font_dict['ut'],
'cw': font_dict['cw'], 'cw': font_dict['cw'],
'ttffile': font_dict['ttffile'], 'fontkey': fontkey, 'ttffile': font_dict['ttffile'], 'fontkey': fontkey,
'subset': sbarr, 'unifilename': unifilename, 'subset': sbarr, 'unifilename': unifilename,
} }
self.font_files[fontkey] = {'length1': font_dict['originalsize'], self.font_files[fontkey] = {'length1': font_dict['originalsize'],
@@ -494,7 +509,7 @@ class FPDF(object):
if (type == 'TrueType'): if (type == 'TrueType'):
self.font_files[filename]={'length1': originalsize} self.font_files[filename]={'length1': originalsize}
else: else:
self.font_files[filename]={'length1': size1, self.font_files[filename]={'length1': size1,
'length2': size2} 'length2': size2}
def set_font(self, family,style='',size=0): def set_font(self, family,style='',size=0):
@@ -660,7 +675,7 @@ class FPDF(object):
dx=self.c_margin dx=self.c_margin
if(self.color_flag): if(self.color_flag):
s+='q '+self.text_color+' ' s+='q '+self.text_color+' '
# If multibyte, Tw has no effect - do word spacing using an adjustment before each space # If multibyte, Tw has no effect - do word spacing using an adjustment before each space
if (self.ws and self.unifontsubset): if (self.ws and self.unifontsubset):
for uni in UTF8StringToArray(txt): for uni in UTF8StringToArray(txt):
@@ -686,7 +701,7 @@ class FPDF(object):
else: else:
txt2 = self._escape(txt) 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) 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): if(self.underline):
s+=' '+self._dounderline(self.x+dx,self.y+.5*h+.3*self.font_size,txt) s+=' '+self._dounderline(self.x+dx,self.y+.5*h+.3*self.font_size,txt)
if(self.color_flag): if(self.color_flag):
@@ -764,7 +779,7 @@ class FPDF(object):
sep=i sep=i
ls=l ls=l
ns+=1 ns+=1
if self.unifontsubset: if self.unifontsubset:
l += self.get_string_width(c) / self.font_size*1000.0 l += self.get_string_width(c) / self.font_size*1000.0
else: else:
l += cw.get(c,0) l += cw.get(c,0)
@@ -848,7 +863,7 @@ class FPDF(object):
continue continue
if(c==' '): if(c==' '):
sep=i sep=i
if self.unifontsubset: if self.unifontsubset:
l += self.get_string_width(c) / self.font_size*1000.0 l += self.get_string_width(c) / self.font_size*1000.0
else: else:
l += cw.get(c,0) l += cw.get(c,0)
@@ -900,6 +915,24 @@ class FPDF(object):
info=self._parsepng(name) info=self._parsepng(name)
else: else:
#Allow for additional formats #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 mtd='_parse'+type
if not hasattr(self,mtd): if not hasattr(self,mtd):
self.error('Unsupported image type: '+type) self.error('Unsupported image type: '+type)
@@ -1202,7 +1235,7 @@ class FPDF(object):
self._out('<</Type /Font'); self._out('<</Type /Font');
self._out('/Subtype /Type0'); self._out('/Subtype /Type0');
self._out('/BaseFont /' + fontname + ''); 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('/DescendantFonts [' + str(self.n + 1) + ' 0 R]')
self._out('/ToUnicode ' + str(self.n + 2) + ' 0 R') self._out('/ToUnicode ' + str(self.n + 2) + ' 0 R')
self._out('>>') self._out('>>')
@@ -1264,7 +1297,7 @@ class FPDF(object):
for kd in ('Ascent', 'Descent', 'CapHeight', 'Flags', 'FontBBox', 'ItalicAngle', 'StemV', 'MissingWidth'): for kd in ('Ascent', 'Descent', 'CapHeight', 'Flags', 'FontBBox', 'ItalicAngle', 'StemV', 'MissingWidth'):
v = font['desc'][kd] v = font['desc'][kd]
if (kd == 'Flags'): if (kd == 'Flags'):
v = v | 4; v = v | 4;
v = v & ~32; # SYMBOLIC font flag v = v & ~32; # SYMBOLIC font flag
self._out(' /%s %s' % (kd, v)) self._out(' /%s %s' % (kd, v))
self._out('/FontFile2 ' + str(self.n + 2) + ' 0 R') self._out('/FontFile2 ' + str(self.n + 2) + ' 0 R')
@@ -1286,7 +1319,7 @@ class FPDF(object):
self._putstream(cidtogidmap) self._putstream(cidtogidmap)
self._out('endobj') self._out('endobj')
#Font file #Font file
self._newobj() self._newobj()
self._out('<</Length ' + str(len(fontstream))) self._out('<</Length ' + str(len(fontstream)))
self._out('/Filter /FlateDecode') self._out('/Filter /FlateDecode')
@@ -1341,14 +1374,14 @@ class FPDF(object):
font_dict['range'] = range_ font_dict['range'] = range_
pickle.dump(font_dict, fh) pickle.dump(font_dict, fh)
fh.close() fh.close()
except IOError as e: except IOError, e:
if not e.errno == errno.EACCES: if not e.errno == errno.EACCES:
raise # Not a permission error. raise # Not a permission error.
if (font['cw'][cid] == 0): if (font['cw'][cid] == 0):
continue continue
width = font['cw'][cid] width = font['cw'][cid]
if (width == 65535): width = 0 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 continue
if ('dw' not in font or (font['dw'] and width != font['dw'])): if ('dw' not in font or (font['dw'] and width != font['dw'])):
if (cid == (prevcid + 1)): if (cid == (prevcid + 1)):
@@ -1400,7 +1433,7 @@ class FPDF(object):
if (len(set(ws)) == 1): if (len(set(ws)) == 1):
w.append(' %s %s %s' % (k, k + len(ws) - 1, ws[0])) w.append(' %s %s %s' % (k, k + len(ws) - 1, ws[0]))
else: 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)) self._out('/W [%s]' % ''.join(w))
def _putimages(self): def _putimages(self):
@@ -1412,7 +1445,7 @@ class FPDF(object):
del info['data'] del info['data']
if 'smask' in info: if 'smask' in info:
del info['smask'] del info['smask']
def _putimage(self, info): def _putimage(self, info):
if 'data' in info: if 'data' in info:
self._newobj() self._newobj()
@@ -1791,20 +1824,20 @@ class FPDF(object):
def interleaved2of5(self, txt, x, y, w=1.0, h=10.0): def interleaved2of5(self, txt, x, y, w=1.0, h=10.0):
"Barcode I2of5 (numeric), adds a 0 if odd lenght" "Barcode I2of5 (numeric), adds a 0 if odd lenght"
narrow = w / 3.0 narrow = w / 3.0
wide = w wide = w
# wide/narrow codes for the digits # wide/narrow codes for the digits
bar_char={'0': 'nnwwn', '1': 'wnnnw', '2': 'nwnnw', '3': 'wwnnn', bar_char={'0': 'nnwwn', '1': 'wnnnw', '2': 'nwnnw', '3': 'wwnnn',
'4': 'nnwnw', '5': 'wnwnn', '6': 'nwwnn', '7': 'nnnww', '4': 'nnwnw', '5': 'wnwnn', '6': 'nwwnn', '7': 'nnnww',
'8': 'wnnwn', '9': 'nwnwn', 'A': 'nn', 'Z': 'wn'} '8': 'wnnwn', '9': 'nwnwn', 'A': 'nn', 'Z': 'wn'}
self.set_fill_color(0) self.set_fill_color(0)
code = txt code = txt
# add leading zero if code-length is odd # add leading zero if code-length is odd
if len(code) % 2 != 0: if len(code) % 2 != 0:
code = '0' + code code = '0' + code
# add start and stop codes # add start and stop codes
code = 'AA' + code.lower() + 'ZA' code = 'AA' + code.lower() + 'ZA'
@@ -1843,7 +1876,7 @@ class FPDF(object):
narrow = w / 3.0 narrow = w / 3.0
gap = narrow gap = narrow
bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw', bar_char={'0': 'nnnwwnwnn', '1': 'wnnwnnnnw', '2': 'nnwwnnnnw',
'3': 'wnwwnnnnn', '4': 'nnnwwnnnw', '5': 'wnnwwnnnn', '3': 'wnwwnnnnn', '4': 'nnnwwnnnw', '5': 'wnnwwnnnn',
'6': 'nnwwwnnnn', '7': 'nnnwnnwnw', '8': 'wnnwnnwnn', '6': 'nnwwwnnnn', '7': 'nnnwnnwnw', '8': 'wnnwnnwnn',
'9': 'nnwwnnwnn', 'A': 'wnnnnwnnw', 'B': 'nnwnnwnnw', '9': 'nnwwnnwnn', 'A': 'wnnnnwnnw', 'B': 'nnwnnwnnw',
@@ -1860,8 +1893,8 @@ class FPDF(object):
'+': 'nwnnnwnwn', '%': 'nnnwnwnwn'} '+': 'nwnnnwnwn', '%': 'nnnwnwnwn'}
self.set_fill_color(0) self.set_fill_color(0)
code = txt code = txt
code = code.upper() code = code.upper()
for i in xrange (0, len(code), 2): for i in xrange (0, len(code), 2):
char_bar = code[i] char_bar = code[i]
@@ -1871,7 +1904,7 @@ class FPDF(object):
seq= '' seq= ''
for s in xrange(0, len(bar_char[char_bar])): 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)): for bar in xrange(0, len(seq)):
if seq[bar] == 'n': if seq[bar] == 'n':
+41 -14
View File
@@ -370,7 +370,7 @@ if not 'google' in DRIVERS:
try: try:
import fdb import fdb
DRIVERS.append('Firbird(fdb)') DRIVERS.append('Firebird(fdb)')
except ImportError: except ImportError:
LOGGER.debug('no Firebird driver fdb') LOGGER.debug('no Firebird driver fdb')
##### #####
@@ -633,6 +633,7 @@ class BaseAdapter(ConnectionPool):
TRUE = 'T' TRUE = 'T'
FALSE = 'F' FALSE = 'F'
T_SEP = ' '
types = { types = {
'boolean': 'CHAR(1)', 'boolean': 'CHAR(1)',
'string': 'CHAR(%(length)s)', 'string': 'CHAR(%(length)s)',
@@ -1810,7 +1811,7 @@ class BaseAdapter(ConnectionPool):
obj = str(obj) obj = str(obj)
elif fieldtype == 'datetime': elif fieldtype == 'datetime':
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
obj = obj.isoformat()[:19].replace('T',' ') obj = obj.isoformat(self.T_SEP)[:19]
elif isinstance(obj, datetime.date): elif isinstance(obj, datetime.date):
obj = obj.isoformat()[:10]+' 00:00:00' obj = obj.isoformat()[:10]+' 00:00:00'
else: else:
@@ -2418,6 +2419,9 @@ class MySQLAdapter(BaseAdapter):
return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table, return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table,
'SET FOREIGN_KEY_CHECKS=1;'] 'SET FOREIGN_KEY_CHECKS=1;']
def _insert_empty(self, table):
return 'INSERT INTO %s VALUES (DEFAULT);' % table
def distributed_transaction_begin(self,key): def distributed_transaction_begin(self,key):
self.execute('XA START;') self.execute('XA START;')
@@ -3033,6 +3037,7 @@ class OracleAdapter(BaseAdapter):
class MSSQLAdapter(BaseAdapter): class MSSQLAdapter(BaseAdapter):
drivers = ('pyodbc',) drivers = ('pyodbc',)
T_SEP = 'T'
types = { types = {
'boolean': 'BIT', 'boolean': 'BIT',
@@ -3935,7 +3940,7 @@ class IngresAdapter(BaseAdapter):
return self.driver.connect(cnxn,**driver_args) return self.driver.connect(cnxn,**driver_args)
self.connector = connector self.connector = connector
# TODO if version is >= 10, set types['id'] to Identity column, see http://community.actian.com/wiki/Using_Ingres_Identity_Columns # 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() if do_connect: self.reconnect()
@@ -4654,7 +4659,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
return self.expand(first) return self.expand(first)
def truncate(self,table,mode): 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): def select_raw(self,query,fields=None,attributes=None):
db = self.db db = self.db
@@ -7052,15 +7057,13 @@ class DAL(object):
Example:: Example::
db = DAL('sqlite://test.db') db = DAL('sqlite://test.db')
or
db = DAL({"uri": ..., "items": ...}) # experimental
db.define_table('tablename', Field('fieldname1'), db.define_table('tablename', Field('fieldname1'),
Field('fieldname2')) 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): 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. :uri: string that contains information for connecting to a database.
(default: 'sqlite://dummy.db') (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. :pool_size: How many open connections to make to the database object.
:folder: where .table files will be created. :folder: where .table files will be created.
automatically set within web2py automatically set within web2py
@@ -8817,6 +8842,8 @@ class Expression(object):
result_type = 'integer' result_type = 'integer'
elif self.type in ['date','time','datetime','double','float']: elif self.type in ['date','time','datetime','double','float']:
result_type = 'double' result_type = 'double'
elif self.type.startswith('decimal('):
result_type = self.type
else: else:
raise SyntaxError("subtraction operation not supported for type") raise SyntaxError("subtraction operation not supported for type")
return Expression(db,db._adapter.SUB,self,other,result_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.name, self.f) = (name, f) if f else ('unkown', name)
self.type = ftype self.type = ftype
self.label = label or self.name.capitalize().replace('_',' ') self.label = label or self.name.capitalize().replace('_',' ')
self.represent = IDENTITY self.represent = IDENTITY
self.formatter = IDENTITY self.formatter = IDENTITY
self.comment = None self.comment = None
self.readable = True self.readable = True
@@ -9237,7 +9264,7 @@ class Field(Expression):
def retrieve(self, name, path=None, nameonly=False): 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) (filename, stream)
""" """
self_uploadfield = self.uploadfield self_uploadfield = self.uploadfield
@@ -9399,7 +9426,7 @@ class Field(Expression):
if k == "other": if k == "other":
if isinstance(v, dict): if isinstance(v, dict):
otype, other = v.popitem() otype, other = v.popitem()
else: else:
otype = flatten(type(v)) otype = flatten(type(v))
other = v other = v
newr[k] = {otype: filter_requires(otype, other, newr[k] = {otype: filter_requires(otype, other,
+1 -1
View File
@@ -131,7 +131,7 @@ def get_client(env):
""" """
g = regex_client.search(env.get('http_x_forwarded_for', '')) g = regex_client.search(env.get('http_x_forwarded_for', ''))
client = (g.group() or '').split(',')[0] if g else None 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', '')) g = regex_client.search(env.get('remote_addr', ''))
if g: if g:
client = g.group() client = g.group()
+3 -2
View File
@@ -66,7 +66,7 @@ class XssCleaner(HTMLParser):
# The only schemes allowed in URLs (for href and src attributes). # The only schemes allowed in URLs (for href and src attributes).
# Adding "javascript" or "vbscript" to this list would not be smart. # 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? #to strip or escape disallowed tags?
self.strip_disallowed = strip_disallowed self.strip_disallowed = strip_disallowed
@@ -151,11 +151,12 @@ class XssCleaner(HTMLParser):
def url_is_acceptable(self, url): def url_is_acceptable(self, url):
""" """
Accepts relative and absolute urls Accepts relative, absolute, and mailto urls
""" """
parsed = urlparse(url) parsed = urlparse(url)
return (parsed[0] in self.allowed_schemes and '.' in parsed[1]) \ 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('/')) or (parsed[0] == '' and parsed[2].startswith('/'))
def strip(self, rawstring, escape=True): def strip(self, rawstring, escape=True):
+40 -2
View File
@@ -107,6 +107,7 @@ TERMINATE = 'TERMINATE'
DISABLED = 'DISABLED' DISABLED = 'DISABLED'
KILL = 'KILL' KILL = 'KILL'
PICK = 'PICK' PICK = 'PICK'
STOP_TASK = 'STOP_TASK'
EXPIRED = 'EXPIRED' EXPIRED = 'EXPIRED'
SECONDS = 1 SECONDS = 1
HEARTBEAT = 3 * SECONDS HEARTBEAT = 3 * SECONDS
@@ -398,7 +399,7 @@ class MetaScheduler(threading.Thread):
TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED) TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED)
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED) 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): class TYPE(object):
@@ -746,7 +747,7 @@ class Scheduler(MetaScheduler):
# keep sleeping # keep sleeping
self.worker_status[0] = DISABLED self.worker_status[0] = DISABLED
if self.worker_status[1] == MAXHIBERNATION: 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( db(sw.worker_name == self.worker_name).update(
last_heartbeat=now) last_heartbeat=now)
elif mybackedstatus == TERMINATE: elif mybackedstatus == TERMINATE:
@@ -758,6 +759,9 @@ class Scheduler(MetaScheduler):
self.worker_status[0] = KILL self.worker_status[0] = KILL
self.die() self.die()
else: 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]) logger.debug('........recording heartbeat (%s)', self.worker_status[0])
db(sw.worker_name == self.worker_name).update( db(sw.worker_name == self.worker_name).update(
last_heartbeat=now, status=ACTIVE) last_heartbeat=now, status=ACTIVE)
@@ -1007,6 +1011,40 @@ class Scheduler(MetaScheduler):
object_hook=_decode_dict) or None object_hook=_decode_dict) or None
return row 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(): def main():
""" """
+45 -2
View File
@@ -25,9 +25,52 @@ try:
except ImportError: except ImportError:
have_yaml = False 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 # 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): def custom_json(o):
if hasattr(o, 'custom_json') and callable(o.custom_json): if hasattr(o, 'custom_json') and callable(o.custom_json):
+2 -1
View File
@@ -1440,7 +1440,8 @@ class SQLFORM(FORM):
f = self.table[fieldname].default or '' f = self.table[fieldname].default or ''
fields[fieldname] = f fields[fieldname] = f
else: else:
fields[fieldname] = '' f = self.table[fieldname].default or ''
fields[fieldname] = f
self.vars[fieldname] = fields[fieldname] self.vars[fieldname] = fields[fieldname]
if not f: if not f:
continue continue
+304 -282
View File
@@ -6,6 +6,8 @@
import sys import sys
import os import os
import glob
if os.path.isdir('gluon'): if os.path.isdir('gluon'):
sys.path.append(os.path.realpath('gluon')) sys.path.append(os.path.realpath('gluon'))
else: else:
@@ -19,6 +21,11 @@ except:
from io import StringIO from io import StringIO
from dal import DAL, Field, Table, SQLALL 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 = [ ALLOWED_DATATYPES = [
'string', 'string',
'text', 'text',
@@ -41,6 +48,8 @@ def setUpModule():
def tearDownModule(): def tearDownModule():
if os.path.isfile('sql.log'): if os.path.isfile('sql.log'):
os.unlink('sql.log') os.unlink('sql.log')
for a in glob.glob('*.table'):
os.unlink(a)
class TestFields(unittest.TestCase): class TestFields(unittest.TestCase):
@@ -96,35 +105,35 @@ class TestFields(unittest.TestCase):
isinstance(f.formatter(datetime.datetime.now()), str) isinstance(f.formatter(datetime.datetime.now()), str)
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
for ft in ['string', 'text', 'password', 'upload', 'blob']: for ft in ['string', 'text', 'password', 'upload', 'blob']:
db.define_table('t', Field('a', ft, default='')) db.define_table('tt', Field('aa', ft, default=''))
self.assertEqual(db.t.insert(a='x'), 1) self.assertEqual(db.tt.insert(aa='x'), 1)
self.assertEqual(db().select(db.t.a)[0].a, 'x') self.assertEqual(db().select(db.tt.aa)[0].aa, 'x')
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'integer', default=1)) db.define_table('tt', Field('aa', 'integer', default=1))
self.assertEqual(db.t.insert(a=3), 1) self.assertEqual(db.tt.insert(aa=3), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3) self.assertEqual(db().select(db.tt.aa)[0].aa, 3)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'double', default=1)) db.define_table('tt', Field('aa', 'double', default=1))
self.assertEqual(db.t.insert(a=3.1), 1) self.assertEqual(db.tt.insert(aa=3.1), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3.1) self.assertEqual(db().select(db.tt.aa)[0].aa, 3.1)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'boolean', default=True)) db.define_table('tt', Field('aa', 'boolean', default=True))
self.assertEqual(db.t.insert(a=True), 1) self.assertEqual(db.tt.insert(aa=True), 1)
self.assertEqual(db().select(db.t.a)[0].a, True) self.assertEqual(db().select(db.tt.aa)[0].aa, True)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'json', default={})) db.define_table('tt', Field('aa', 'json', default={}))
self.assertEqual(db.t.insert(a={}), 1) self.assertEqual(db.tt.insert(aa={}), 1)
self.assertEqual(db().select(db.t.a)[0].a, {}) self.assertEqual(db().select(db.tt.aa)[0].aa, {})
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'date', db.define_table('tt', Field('aa', 'date',
default=datetime.date.today())) default=datetime.date.today()))
t0 = datetime.date.today() t0 = datetime.date.today()
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'datetime', db.define_table('tt', Field('aa', 'datetime',
default=datetime.datetime.today())) default=datetime.datetime.today()))
t0 = datetime.datetime( t0 = datetime.datetime(
1971, 1971,
@@ -135,34 +144,34 @@ class TestFields(unittest.TestCase):
55, 55,
0, 0,
) )
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
## Row APIs ## Row APIs
row = db().select(db.t.a)[0] row = db().select(db.tt.aa)[0]
self.assertEqual(db.t[1].a,t0) self.assertEqual(db.tt[1].aa,t0)
self.assertEqual(db.t['a'],db.t.a) self.assertEqual(db.tt['aa'],db.tt.aa)
self.assertEqual(db.t(1).a,t0) self.assertEqual(db.tt(1).aa,t0)
self.assertTrue(db.t(1,a=None)==None) self.assertTrue(db.tt(1,aa=None)==None)
self.assertFalse(db.t(1,a=t0)==None) self.assertFalse(db.tt(1,aa=t0)==None)
self.assertEqual(row.a,t0) self.assertEqual(row.aa,t0)
self.assertEqual(row['a'],t0) self.assertEqual(row['aa'],t0)
self.assertEqual(row['t.a'],t0) self.assertEqual(row['tt.aa'],t0)
self.assertEqual(row('t.a'),t0) self.assertEqual(row('tt.aa'),t0)
## Lazy and Virtual fields ## Lazy and Virtual fields
db.t.b = Field.Virtual(lambda row: row.t.a) db.tt.b = Field.Virtual(lambda row: row.tt.aa)
db.t.c = Field.Lazy(lambda row: row.t.a) db.tt.c = Field.Lazy(lambda row: row.tt.aa)
row = db().select(db.t.a)[0] row = db().select(db.tt.aa)[0]
self.assertEqual(row.b,t0) self.assertEqual(row.b,t0)
self.assertEqual(row.c(),t0) self.assertEqual(row.c(),t0)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'time', default='11:30')) db.define_table('tt', Field('aa', 'time', default='11:30'))
t0 = datetime.time(10, 30, 55) t0 = datetime.time(10, 30, 55)
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.t.drop() db.tt.drop()
class TestAll(unittest.TestCase): class TestAll(unittest.TestCase):
@@ -198,7 +207,7 @@ class TestTable(unittest.TestCase):
in str(persons.ALL)) in str(persons.ALL))
def testTableAlias(self): def testTableAlias(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
persons = Table(db, 'persons', Field('firstname', persons = Table(db, 'persons', Field('firstname',
'string'), Field('lastname', 'string')) 'string'), Field('lastname', 'string'))
aliens = persons.with_alias('aliens') aliens = persons.with_alias('aliens')
@@ -221,276 +230,278 @@ class TestTable(unittest.TestCase):
class TestInsert(unittest.TestCase): class TestInsert(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='1'), 2) self.assertEqual(db.tt.insert(aa='1'), 2)
self.assertEqual(db.t.insert(a='1'), 3) self.assertEqual(db.tt.insert(aa='1'), 3)
self.assertEqual(db(db.t.a == '1').count(), 3) self.assertEqual(db(db.tt.aa == '1').count(), 3)
self.assertEqual(db(db.t.a == '1').update(a='2'), 3) self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3)
self.assertEqual(db(db.t.a == '2').count(), 3) self.assertEqual(db(db.tt.aa == '2').count(), 3)
self.assertEqual(db(db.t.a == '2').delete(), 3) self.assertEqual(db(db.tt.aa == '2').delete(), 3)
db.t.drop() db.tt.drop()
class TestSelect(unittest.TestCase): class TestSelect(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2) self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3) self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.t.id > 0).select()), 3) self.assertEqual(len(db(db.tt.id > 0).select()), 3)
self.assertEqual(db(db.t.id > 0).select(orderby=~db.t.a self.assertEqual(db(db.tt.id > 0).select(orderby=~db.tt.aa
| db.t.id)[0].a, '3') | db.tt.id)[0].aa, '3')
self.assertEqual(len(db(db.t.id > 0).select(limitby=(1, 2))), 1) self.assertEqual(len(db(db.tt.id > 0).select(limitby=(1, 2))), 1)
self.assertEqual(db(db.t.id > 0).select(limitby=(1, 2))[0].a, self.assertEqual(db(db.tt.id > 0).select(limitby=(1, 2))[0].aa,
'2') '2')
self.assertEqual(len(db().select(db.t.ALL)), 3) self.assertEqual(len(db().select(db.tt.ALL)), 3)
self.assertEqual(len(db(db.t.a == None).select()), 0) self.assertEqual(len(db(db.tt.aa == None).select()), 0)
self.assertEqual(len(db(db.t.a != None).select()), 3) self.assertEqual(len(db(db.tt.aa != None).select()), 3)
self.assertEqual(len(db(db.t.a > '1').select()), 2) self.assertEqual(len(db(db.tt.aa > '1').select()), 2)
self.assertEqual(len(db(db.t.a >= '1').select()), 3) self.assertEqual(len(db(db.tt.aa >= '1').select()), 3)
self.assertEqual(len(db(db.t.a == '1').select()), 1) self.assertEqual(len(db(db.tt.aa == '1').select()), 1)
self.assertEqual(len(db(db.t.a != '1').select()), 2) self.assertEqual(len(db(db.tt.aa != '1').select()), 2)
self.assertEqual(len(db(db.t.a < '3').select()), 2) self.assertEqual(len(db(db.tt.aa < '3').select()), 2)
self.assertEqual(len(db(db.t.a <= '3').select()), 3) self.assertEqual(len(db(db.tt.aa <= '3').select()), 3)
self.assertEqual(len(db(db.t.a > '1')(db.t.a < '3').select()), 1) self.assertEqual(len(db(db.tt.aa > '1')(db.tt.aa < '3').select()), 1)
self.assertEqual(len(db((db.t.a > '1') & (db.t.a < '3')).select()), 1) self.assertEqual(len(db((db.tt.aa > '1') & (db.tt.aa < '3')).select()), 1)
self.assertEqual(len(db((db.t.a > '1') | (db.t.a < '3')).select()), 3) self.assertEqual(len(db((db.tt.aa > '1') | (db.tt.aa < '3')).select()), 3)
self.assertEqual(len(db((db.t.a > '1') & ~(db.t.a > '2')).select()), 1) self.assertEqual(len(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).select()), 1)
self.assertEqual(len(db(~(db.t.a > '1') & (db.t.a > '2')).select()), 0) self.assertEqual(len(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).select()), 0)
db.t.drop() db.tt.drop()
class TestBelongs(unittest.TestCase): class TestBelongs(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2) self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3) self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.t.a.belongs(('1', '3'))).select()), self.assertEqual(len(db(db.tt.aa.belongs(('1', '3'))).select()),
2) 2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.id self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.id
> 2)._select(db.t.a))).select()), 1) > 2)._select(db.tt.aa))).select()), 1)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(('1', self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
'3')))._select(db.t.a))).select()), 2) '3')))._select(db.tt.aa))).select()), 2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(db self.assertEqual(len(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
(db.t.a.belongs(('1', '3')))._select(db.t.a)))._select( (db.tt.aa.belongs(('1', '3')))._select(db.tt.aa)))._select(
db.t.a))).select()), db.tt.aa))).select()),
2) 2)
db.t.drop() db.tt.drop()
class TestContains(unittest.TestCase): class TestContains(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'list:string')) db.define_table('tt', Field('aa', 'list:string'))
self.assertEqual(db.t.insert(a=['aaa','bbb']), 1) self.assertEqual(db.tt.insert(aa=['aaa','bbb']), 1)
self.assertEqual(db.t.insert(a=['bbb','ddd']), 2) self.assertEqual(db.tt.insert(aa=['bbb','ddd']), 2)
self.assertEqual(db.t.insert(a=['eee','aaa']), 3) self.assertEqual(db.tt.insert(aa=['eee','aaa']), 3)
self.assertEqual(len(db(db.t.a.contains('aaa')).select()), self.assertEqual(len(db(db.tt.aa.contains('aaa')).select()),
2) 2)
self.assertEqual(len(db(db.t.a.contains('bbb')).select()), self.assertEqual(len(db(db.tt.aa.contains('bbb')).select()),
2) 2)
self.assertEqual(len(db(db.t.a.contains('aa')).select()), self.assertEqual(len(db(db.tt.aa.contains('aa')).select()),
0) 0)
db.t.drop() db.tt.drop()
class TestLike(unittest.TestCase): class TestLike(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='abc'), 1) self.assertEqual(db.tt.insert(aa='abc'), 1)
self.assertEqual(len(db(db.t.a.like('a%')).select()), 1) self.assertEqual(len(db(db.tt.aa.like('a%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%b%')).select()), 1) self.assertEqual(len(db(db.tt.aa.like('%b%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%c')).select()), 1) self.assertEqual(len(db(db.tt.aa.like('%c')).select()), 1)
self.assertEqual(len(db(db.t.a.like('%d%')).select()), 0) self.assertEqual(len(db(db.tt.aa.like('%d%')).select()), 0)
self.assertEqual(len(db(db.t.a.lower().like('A%')).select()), 1) self.assertEqual(len(db(db.tt.aa.lower().like('A%')).select()), 1)
self.assertEqual(len(db(db.t.a.lower().like('%B%')).select()), self.assertEqual(len(db(db.tt.aa.lower().like('%B%')).select()),
1) 1)
self.assertEqual(len(db(db.t.a.lower().like('%C')).select()), 1) self.assertEqual(len(db(db.tt.aa.lower().like('%C')).select()), 1)
self.assertEqual(len(db(db.t.a.upper().like('A%')).select()), 1) self.assertEqual(len(db(db.tt.aa.upper().like('A%')).select()), 1)
self.assertEqual(len(db(db.t.a.upper().like('%B%')).select()), self.assertEqual(len(db(db.tt.aa.upper().like('%B%')).select()),
1) 1)
self.assertEqual(len(db(db.t.a.upper().like('%C')).select()), 1) self.assertEqual(len(db(db.tt.aa.upper().like('%C')).select()), 1)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1111111111), 1) self.assertEqual(db.tt.insert(aa=1111111111), 1)
self.assertEqual(len(db(db.t.a.like('1%')).select()), 1) self.assertEqual(len(db(db.tt.aa.like('1%')).select()), 1)
self.assertEqual(len(db(db.t.a.like('2%')).select()), 0) self.assertEqual(len(db(db.tt.aa.like('2%')).select()), 0)
db.t.drop() db.tt.drop()
class TestDatetime(unittest.TestCase): class TestDatetime(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'datetime')) db.define_table('tt', Field('aa', 'datetime'))
self.assertEqual(db.t.insert(a=datetime.datetime(1971, 12, 21, self.assertEqual(db.tt.insert(aa=datetime.datetime(1971, 12, 21,
11, 30)), 1) 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) 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) 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) 21, 11, 30)).select()), 1)
self.assertEqual(db(db.t.a.year() == 1971).count(), 2) self.assertEqual(db(db.tt.aa.year() == 1971).count(), 2)
self.assertEqual(db(db.t.a.month() == 12).count(), 2) self.assertEqual(db(db.tt.aa.month() == 12).count(), 2)
self.assertEqual(db(db.t.a.day() == 21).count(), 3) self.assertEqual(db(db.tt.aa.day() == 21).count(), 3)
self.assertEqual(db(db.t.a.hour() == 11).count(), 1) self.assertEqual(db(db.tt.aa.hour() == 11).count(), 1)
self.assertEqual(db(db.t.a.minutes() == 30).count(), 3) self.assertEqual(db(db.tt.aa.minutes() == 30).count(), 3)
self.assertEqual(db(db.t.a.seconds() == 0).count(), 3) self.assertEqual(db(db.tt.aa.seconds() == 0).count(), 3)
self.assertEqual(db(db.t.a.epoch()<365*24*3600).count(),1) self.assertEqual(db(db.tt.aa.epoch()<365*24*3600).count(),1)
db.t.drop() db.tt.drop()
class TestExpressions(unittest.TestCase): class TestExpressions(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1), 1) self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.t.insert(a=2), 2) self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.t.insert(a=3), 3) self.assertEqual(db.tt.insert(aa=3), 3)
self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1) self.assertEqual(db(db.tt.aa == 3).update(aa=db.tt.aa + 1), 1)
self.assertEqual(db(db.t.a == 4).count(), 1) self.assertEqual(db(db.tt.aa == 4).count(), 1)
db.t.drop() db.tt.drop()
class TestJoin(unittest.TestCase): class TestJoin(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t1', Field('a')) db.define_table('t1', Field('aa'))
db.define_table('t2', Field('a'), Field('b', db.t1)) db.define_table('t2', Field('aa'), Field('b', db.t1))
i1 = db.t1.insert(a='1') i1 = db.t1.insert(aa='1')
i2 = db.t1.insert(a='2') i2 = db.t1.insert(aa='2')
i3 = db.t1.insert(a='3') i3 = db.t1.insert(aa='3')
db.t2.insert(a='4', b=i1) db.t2.insert(aa='4', b=i1)
db.t2.insert(a='5', b=i2) db.t2.insert(aa='5', b=i2)
db.t2.insert(a='6', b=i2) db.t2.insert(aa='6', b=i2)
self.assertEqual(len(db(db.t1.id self.assertEqual(len(db(db.t1.id
== db.t2.b).select(orderby=db.t1.a == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)), 3) | db.t2.aa)), 3)
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)[2].t1.a, '2') | db.t2.aa)[2].t1.aa, '2')
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)[2].t2.a, '6') | db.t2.aa)[2].t2.aa, '6')
self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), 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, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), 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, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), 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, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), 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, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[3].t2.a, None) orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
self.assertEqual(len(db().select(db.t1.ALL, db.t2.id.count(), self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b), 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) 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), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[0]._extra[db.t2.id.count()], groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
1) 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), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[1]._extra[db.t2.id.count()], groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
2) 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), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[2]._extra[db.t2.id.count()], groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
0) 0)
db.t1.drop()
db.t2.drop() db.t2.drop()
db.t1.drop()
db.define_table('person',Field('name')) db.define_table('person',Field('name'))
id = db.person.insert(name="max") id = db.person.insert(name="max")
self.assertEqual(id.name,'max') self.assertEqual(id.name,'max')
db.define_table('dog',Field('name'),Field('owner','reference person')) db.define_table('dog',Field('name'),Field('ownerperson','reference person'))
db.dog.insert(name='skipper',owner=1) db.dog.insert(name='skipper',ownerperson=1)
row = db(db.person.id==db.dog.owner).select().first() row = db(db.person.id==db.dog.ownerperson).select().first()
self.assertEqual(row[db.person.name],'max') self.assertEqual(row[db.person.name],'max')
self.assertEqual(row['person.name'],'max') self.assertEqual(row['person.name'],'max')
db.dog.drop() db.dog.drop()
self.assertEqual(len(db.person._referenced_by),0) self.assertEqual(len(db.person._referenced_by),0)
db.person.drop() db.person.drop()
class TestMinMaxSum(unittest.TestCase): class TestMinMaxSumAvg(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1), 1) self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.t.insert(a=2), 2) self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.t.insert(a=3), 3) self.assertEqual(db.tt.insert(aa=3), 3)
s = db.t.a.min() s = db.tt.aa.min()
self.assertEqual(db(db.t.id > 0).select(s)[0]._extra[s], 1) self.assertEqual(db(db.tt.id > 0).select(s)[0]._extra[s], 1)
self.assertEqual(db(db.t.id > 0).select(s).first()[s], 1) self.assertEqual(db(db.tt.id > 0).select(s).first()[s], 1)
self.assertEqual(db().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) 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) 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) 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): class TestCache(unittest.TestCase):
def testRun(self): def testRun(self):
from cache import CacheInRam from cache import CacheInRam
cache = CacheInRam() cache = CacheInRam()
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.t.insert(a='1') db.tt.insert(aa='1')
r0 = db().select(db.t.ALL) r0 = db().select(db.tt.ALL)
r1 = db().select(db.t.ALL, cache=(cache, 1000)) r1 = db().select(db.tt.ALL, cache=(cache, 1000))
self.assertEqual(len(r0),len(r1)) 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)) 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)) 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)) self.assertEqual(len(r0),len(r4))
db.t.drop() db.tt.drop()
class TestMigrations(unittest.TestCase): class TestMigrations(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), migrate='.storage.table') db.define_table('tt', Field('aa'), migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), Field('b'), db.define_table('tt', Field('aa'), Field('b'),
migrate='.storage.table') migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), Field('b', 'text'), db.define_table('tt', Field('aa'), Field('b', 'text'),
migrate='.storage.table') migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), migrate='.storage.table') db.define_table('tt', Field('aa'), migrate='.storage.table')
db.t.drop() db.tt.drop()
db.commit() db.commit()
db.close() db.close()
@@ -500,81 +511,81 @@ class TestMigrations(unittest.TestCase):
if os.path.exists('.storage.table'): if os.path.exists('.storage.table'):
os.unlink('.storage.table') os.unlink('.storage.table')
class TestReferece(unittest.TestCase): class TestReference(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('name'), Field('a','reference t')) db.define_table('tt', Field('name'), Field('aa','reference tt'))
db.commit() db.commit()
x = db.t.insert(name='max') x = db.tt.insert(name='max')
assert x.id == 1 assert x.id == 1
assert x['id'] == 1 assert x['id'] == 1
x.a = x x.aa = x
assert x.a == 1 assert x.aa == 1
x.update_record() x.update_record()
y = db.t[1] y = db.tt[1]
assert y.a == 1 assert y.aa == 1
assert y.a.a.a.a.a.a.name == 'max' assert y.aa.aa.aa.aa.aa.aa.name == 'max'
z=db.t.insert(name='xxx', a = y) z=db.tt.insert(name='xxx', aa = y)
assert z.a == y.id assert z.aa == y.id
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestClientLevelOps(unittest.TestCase): class TestClientLevelOps(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.commit() db.commit()
db.t.insert(a="test") db.tt.insert(aa="test")
rows1 = db(db.t.id>0).select() rows1 = db(db.tt.id>0).select()
rows2 = db(db.t.id>0).select() rows2 = db(db.tt.id>0).select()
rows3 = rows1 & rows2 rows3 = rows1 & rows2
assert len(rows3) == 2 assert len(rows3) == 2
rows4 = rows1 | rows2 rows4 = rows1 | rows2
assert len(rows4) == 1 assert len(rows4) == 1
rows5 = rows1.find(lambda row: row.a=="test") rows5 = rows1.find(lambda row: row.aa=="test")
assert len(rows5) == 1 assert len(rows5) == 1
rows6 = rows2.exclude(lambda row: row.a=="test") rows6 = rows2.exclude(lambda row: row.aa=="test")
assert len(rows6) == 1 assert len(rows6) == 1
rows7 = rows5.sort(lambda row: row.a) rows7 = rows5.sort(lambda row: row.aa)
assert len(rows7) == 1 assert len(rows7) == 1
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestVirtualFields(unittest.TestCase): class TestVirtualFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.commit() db.commit()
db.t.insert(a="test") db.tt.insert(aa="test")
class Compute: class Compute:
def a_upper(row): return row.t.a.upper() def a_upper(row): return row.tt.aa.upper()
db.t.virtualfields.append(Compute()) db.tt.virtualfields.append(Compute())
assert db(db.t.id>0).select().first().a_upper == 'TEST' assert db(db.tt.id>0).select().first().a_upper == 'TEST'
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestComputedFields(unittest.TestCase): class TestComputedFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', db.define_table('tt',
Field('a'), Field('aa'),
Field('b',default='x'), Field('bb',default='x'),
Field('c',compute=lambda r: r.a+r.b)) Field('cc',compute=lambda r: r.aa+r.bb))
db.commit() db.commit()
id = db.t.insert(a="z") id = db.tt.insert(aa="z")
self.assertEqual(db.t[id].c,'zx') self.assertEqual(db.tt[id].cc,'zx')
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestImportExportFields(unittest.TestCase): class TestImportExportFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name')) db.define_table('person', Field('name'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2): for n in range(2):
@@ -598,7 +609,7 @@ class TestImportExportFields(unittest.TestCase):
class TestImportExportUuidFields(unittest.TestCase): class TestImportExportUuidFields(unittest.TestCase):
def testRun(self): 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('person', Field('name'),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2): for n in range(2):
@@ -622,7 +633,7 @@ class TestImportExportUuidFields(unittest.TestCase):
class TestDALDictImportExport(unittest.TestCase): class TestDALDictImportExport(unittest.TestCase):
def testRun(self): 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('person', Field('name', default="Michael"),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
dbdict = db.as_dict(flat=True, sanitize=False) 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["items"]["person"]["items"]["name"]["default"] == db.person.name.default
assert dbdict assert dbdict
db2 = DAL(dbdict) db2 = DAL(dbdict, check_reserved=['all'])
assert len(db.tables) == len(db2.tables) assert len(db.tables) == len(db2.tables)
assert hasattr(db2, "pet") and isinstance(db2.pet, Table) assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field) assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
db.pet.drop()
db.commit()
db2.commit()
have_serializers = True have_serializers = True
try: try:
import serializers import serializers
dbjson = db.as_json(sanitize=False) dbjson = db.as_json(sanitize=False)
assert isinstance(dbjson, basestring) and len(dbjson) > 0 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\ assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
db3.person.uuid.type == db.person.uuid.type db3.person.uuid.type == db.person.uuid.type
db3.pet.drop()
db3.person.drop() db3.person.drop()
db3.commit() db3.commit()
except ImportError: except ImportError:
pass pass
mpfc = "Monty Python's Flying Circus" mpfc = "Monty Python's Flying Circus"
dbdict4 = {"uri": 'sqlite:memory:', dbdict4 = {"uri": DEFAULT_URI,
"items":{"staff":{"items": {"name": "items":{"staff":{"items": {"name":
{"default":"Michael"}, {"default":"Michael"},
"food": "food":
{"default":"Spam"}, {"default":"Spam"},
"show": "tvshow":
{"type": "reference show"} {"type": "reference tvshow"}
}}, }},
"show":{"items": {"name": "tvshow":{"items": {"name":
{"default":mpfc}, {"default":mpfc},
"rating": "rating":
{"type":"double"}}}}} {"type":"double"}}}}}
db4 = DAL(dbdict4) db4 = DAL(dbdict4, check_reserved=['all'])
assert "staff" in db4.tables assert "staff" in db4.tables
assert "name" in db4.staff assert "name" in db4.staff
assert db4.show.rating.type == "double" assert db4.tvshow.rating.type == "double"
assert (db4.show.insert(), db4.show.insert(name="Loriot"), assert (db4.tvshow.insert(), db4.tvshow.insert(name="Loriot"),
db4.show.insert(name="Il Mattatore")) == (1, 2, 3) db4.tvshow.insert(name="Il Mattatore")) == (1, 2, 3)
assert db4(db4.show).select().first().id == 1 assert db4(db4.tvshow).select().first().id == 1
assert db4(db4.show).select().first().name == mpfc assert db4(db4.tvshow).select().first().name == mpfc
dbdict5 = {"uri": 'sqlite:memory:'} db4.staff.drop()
db5 = DAL(dbdict5) db4.tvshow.drop()
db4.commit()
dbdict5 = {"uri": DEFAULT_URI}
db5 = DAL(dbdict5, check_reserved=['all'])
assert db5.tables in ([], None) assert db5.tables in ([], None)
assert not (str(db5) in ("", None)) assert not (str(db5) in ("", None))
dbdict6 = {"uri": 'sqlite:memory:', dbdict6 = {"uri": DEFAULT_URI,
"items":{"staff":{}, "items":{"staff":{},
"show":{"items": {"name": {}, "tvshow":{"items": {"name": {},
"rating": "rating":
{"type":"double"}}}}} {"type":"double"}
db6 = DAL(dbdict6) }
}
}
}
db6 = DAL(dbdict6, check_reserved=['all'])
assert len(db6["staff"].fields) == 1 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.staff.insert() is not None
assert db6(db6.staff).select().first().id == 1 assert db6(db6.staff).select().first().id == 1
db6.staff.drop() db6.staff.drop()
db6.show.drop() db6.tvshow.drop()
db6.commit() 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__': if __name__ == '__main__':
+57 -30
View File
@@ -4475,6 +4475,8 @@ class Service(object):
return return_response(id, s) return return_response(id, s)
else: else:
return '' return ''
except HTTP, e:
raise e
except Service.JsonRpcException, e: except Service.JsonRpcException, e:
return return_error(id, e.code, e.info) return return_error(id, e.code, e.info)
except BaseException: except BaseException:
@@ -4897,7 +4899,7 @@ class Wiki(object):
everybody = 'everybody' everybody = 'everybody'
rows_page = 25 rows_page = 25
def markmin_base(self,body): def markmin_base(self,body):
return MARKMIN(body, extra=self.extra, return MARKMIN(body, extra=self.settings.extra,
url=True, environment=self.env, url=True, environment=self.env,
autolinks=lambda link: expand_one(link, {})).xml() autolinks=lambda link: expand_one(link, {})).xml()
@@ -4931,29 +4933,43 @@ class Wiki(object):
controller, function, args = items[0], items[1], items[2:] controller, function, args = items[0], items[1], items[2:]
return LOAD(controller, function, args=args, ajax=True).xml() 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', def __init__(self, auth, env=None, render='markmin',
manage_permissions=False, force_prefix='', manage_permissions=False, force_prefix='',
restrict_search=False, extra=None, restrict_search=False, extra=None,
menu_groups=None, templates=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 db = auth.db
self.env = env or {} self.env = env or {}
self.env['component'] = Wiki.component 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.auth = auth
self.menu_groups = menu_groups
if self.auth.user: if self.auth.user:
self.force_prefix = force_prefix % self.auth.user self.settings.force_prefix = force_prefix % self.auth.user
else: else:
self.force_prefix = force_prefix self.settings.force_prefix = force_prefix
self.host = current.request.env.http_host 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 = [ table_definitions = [
('wiki_page', { ('wiki_page', {
@@ -4973,7 +4989,8 @@ class Wiki(object):
writable=perms, readable=perms, writable=perms, readable=perms,
default=[Wiki.everybody]), default=[Wiki.everybody]),
Field('changelog'), Field('changelog'),
Field('html', 'text', compute=render, Field('html', 'text',
compute=self.get_render(),
readable=False, writable=False), readable=False, writable=False),
auth.signature], auth.signature],
'vars':{'format':'%(title)s'}}), 'vars':{'format':'%(title)s'}}),
@@ -5006,8 +5023,9 @@ class Wiki(object):
args += value['args'] args += value['args']
db.define_table(key, *args, **value['vars']) db.define_table(key, *args, **value['vars'])
if self.templates is None and not self.manage_permissions: if self.settings.templates is None and not \
self.templates = db.wiki_page.tags.contains('template')&\ self.settings.manage_permissions:
self.settings.templates = db.wiki_page.tags.contains('template')&\
db.wiki_page.can_read.contains('everybody') db.wiki_page.can_read.contains('everybody')
def update_tags_insert(page, id, db=db): 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( gid = group.id if group else db.auth_group.insert(
role='wiki_editor') role='wiki_editor')
auth.add_membership(gid) auth.add_membership(gid)
settings.lock_keys = True
# WIKI ACCESS POLICY # WIKI ACCESS POLICY
def not_authorized(self, page=None): def not_authorized(self, page=None):
raise HTTP(401) raise HTTP(401)
def can_read(self, page): 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 return True
elif self.auth.user: elif self.auth.user:
groups = self.auth.user_groups.values() groups = self.auth.user_groups.values()
@@ -5067,11 +5089,11 @@ class Wiki(object):
return True return True
def can_see_menu(self): def can_see_menu(self):
if self.menu_groups is None: if self.settings.menu_groups is None:
return True return True
if self.auth.user: if self.auth.user:
groups = self.auth.user_groups.values() 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 True
return False return False
@@ -5108,7 +5130,7 @@ class Wiki(object):
elif zero == '_cloud': elif zero == '_cloud':
return self.cloud() return self.cloud()
elif zero == '_preview': elif zero == '_preview':
return self.preview(self.render) return self.preview(self.get_render())
def first_paragraph(self, page): def first_paragraph(self, page):
if not self.can_read(page): if not self.can_read(page):
@@ -5176,9 +5198,9 @@ class Wiki(object):
title_guess = ' '.join(c.capitalize() for c in slug.split('-')) title_guess = ' '.join(c.capitalize() for c in slug.split('-'))
if not page: if not page:
if not (self.can_manage() or 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' \ current.session.flash = 'slug must have "%s" prefix' \
% self.force_prefix % self.settings.force_prefix
redirect(URL(args=('_create'))) redirect(URL(args=('_create')))
db.wiki_page.can_read.default = [Wiki.everybody] db.wiki_page.can_read.default = [Wiki.everybody]
db.wiki_page.can_edit.default = [auth.user_group_role()] 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))) return dict(content=TAG[''](form, SCRIPT(script)))
def editmedia(self, slug): def editmedia(self, slug):
@@ -5280,14 +5305,15 @@ class Wiki(object):
options=[OPTION(row.slug,_value=row.id) for row in slugs] options=[OPTION(row.slug,_value=row.id) for row in slugs]
options.insert(0, OPTION('',_value='')) options.insert(0, OPTION('',_value=''))
fields = [Field("slug", default=current.request.args(1) or 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))),] requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),]
if self.templates: if self.settings.templates:
fields.append( fields.append(
Field("from_template", "reference wiki_page", Field("from_template", "reference wiki_page",
requires=IS_EMPTY_OR(IS_IN_DB(db(self.templates), requires=IS_EMPTY_OR(
db.wiki_page._id, IS_IN_DB(db(self.settings.templates),
'%(slug)s')), db.wiki_page._id,
'%(slug)s')),
comment=current.T( comment=current.T(
"Choose Template or empty for new Page"))) "Choose Template or empty for new Page")))
form = SQLFORM.factory(*fields, **dict(_class="well span6")) form = SQLFORM.factory(*fields, **dict(_class="well span6"))
@@ -5329,7 +5355,7 @@ class Wiki(object):
request, db = current.request, self.auth.db request, db = current.request, self.auth.db
media = db.wiki_media(id) media = db.wiki_media(id)
if media: if media:
if self.manage_permissions: if self.settings.manage_permissions:
page = db.wiki_page(media.wiki_page) page = db.wiki_page(media.wiki_page)
if not self.can_read(page): if not self.can_read(page):
return self.not_authorized(page) return self.not_authorized(page)
@@ -5428,7 +5454,7 @@ class Wiki(object):
query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\ query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\
(db.wiki_tag.name.belongs(tags)) (db.wiki_tag.name.belongs(tags))
query = query | db.wiki_page.title.contains(request.vars.q) 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) query = query & (db.wiki_page.created_by == self.auth.user_id)
pages = db(query).select(count, pages = db(query).select(count,
*fields, **dict(orderby=orderby or ~count, *fields, **dict(orderby=orderby or ~count,
@@ -5489,6 +5515,7 @@ class Wiki(object):
request = current.request request = current.request
return render(request.post_vars) return render(request.post_vars)
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()
+34 -12
View File
@@ -2219,22 +2219,29 @@ class IS_DATE(Validator):
""" """
def __init__(self, format='%Y-%m-%d', 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.format = translate(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.timezone = timezone
self.extremes = {} self.extremes = {}
def __call__(self, value): def __call__(self, value):
ovalue = value
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None) return (value, None)
try: try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \ (y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format)) time.strptime(value, str(self.format))
value = datetime.date(y, m, d) value = datetime.date(y, m, d)
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) 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): def formatter(self, value):
if value is None: if value is None:
@@ -2247,6 +2254,8 @@ class IS_DATE(Validator):
if year < 1900: if year < 1900:
year = 2000 year = 2000
d = datetime.date(year, value.month, value.day) 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) return d.strftime(format)
@@ -2279,22 +2288,27 @@ class IS_DATETIME(Validator):
return dict(format=format) return dict(format=format)
def __init__(self, format='%Y-%m-%d %H:%M:%S', 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.format = translate(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.extremes = {} self.extremes = {}
self.timezone = timezone
def __call__(self, value): def __call__(self, value):
ovalue = value
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
return (value, None) return (value, None)
try: try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \ (y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format)) time.strptime(value, str(self.format))
value = datetime.datetime(y, m, d, hh, mm, ss) 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) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) 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): def formatter(self, value):
if value is None: if value is None:
@@ -2308,6 +2322,8 @@ class IS_DATETIME(Validator):
year = 2000 year = 2000
d = datetime.datetime(year, value.month, value.day, d = datetime.datetime(year, value.month, value.day,
value.hour, value.minute, value.second) value.hour, value.minute, value.second)
if self.timezone is not None:
d = d + datetime.timedelta(seconds=self.timezone*3600)
return d.strftime(format) return d.strftime(format)
@@ -2336,7 +2352,8 @@ class IS_DATE_IN_RANGE(IS_DATE):
minimum=None, minimum=None,
maximum=None, maximum=None,
format='%Y-%m-%d', format='%Y-%m-%d',
error_message=None): error_message=None,
timezone=None):
self.minimum = minimum self.minimum = minimum
self.maximum = maximum self.maximum = maximum
if error_message is None: 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" error_message = "enter date in range %(min)s %(max)s"
IS_DATE.__init__(self, IS_DATE.__init__(self,
format=format, format=format,
error_message=error_message) error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum) self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value): def __call__(self, value):
ovalue = value
(value, msg) = IS_DATE.__call__(self, value) (value, msg) = IS_DATE.__call__(self, value)
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: 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: 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) return (value, None)
@@ -2386,7 +2405,8 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
minimum=None, minimum=None,
maximum=None, maximum=None,
format='%Y-%m-%d %H:%M:%S', format='%Y-%m-%d %H:%M:%S',
error_message=None): error_message=None,
timezone=None):
self.minimum = minimum self.minimum = minimum
self.maximum = maximum self.maximum = maximum
if error_message is None: 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" error_message = "enter date and time in range %(min)s %(max)s"
IS_DATETIME.__init__(self, IS_DATETIME.__init__(self,
format=format, format=format,
error_message=error_message) error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum) self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value): def __call__(self, value):
ovalue = value
(value, msg) = IS_DATETIME.__call__(self, value) (value, msg) = IS_DATETIME.__call__(self, value)
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: 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: 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) return (value, None)
-17
View File
@@ -1,17 +0,0 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
before_script:
- pip install unittest2
- mysql -e 'create database test_pymysql;'
- mysql -e 'create database test_pymysql2;'
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
irc:
channels: "irc.freenode.org#web2py"