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