Merge github.com:web2py/web2py

Conflicts:
	gluon/contrib/login_methods/rpx_account.py
This commit is contained in:
Michele Comitini
2012-08-26 00:50:46 +02:00
136 changed files with 2898 additions and 1774 deletions
+34 -15
View File
@@ -2,25 +2,28 @@
### DAL Improvements ### DAL Improvements
- MongoDB support in DAL (experimental) - Support for DAL(lazy_tables=True) and db.define_table(on_define=lambda table:), thanks Jonathan
- MongoDB support in DAL (experimental), thanks Mark Breedveld
- geodal and spatialite, thanks Denes and Fran (experimental) - geodal and spatialite, thanks Denes and Fran (experimental)
- db.mytable._before_insert, _after_insert, _before_update, _after_update, _before_delete. _after_delete (list of callbacks) - db.mytable._before_insert, _after_insert, _before_update, _after_update, _before_delete. _after_delete (list of callbacks)
- db(...).update_naive(...) same as update but ignores table._before_update and table._after_update - db(...).update_naive(...) same as update but ignores table._before_update and table._after_update
- DAL BIGINT support and DAL(...,bigint_id=True) - DAL BIGINT support and DAL(...,bigint_id=True)
- IS_IN_DB(..., distinct=True) - IS_IN_DB(..., distinct=True)
- new syntax: db.mytable.insert(myuploadfield=open(....)) - new syntax: db.mytable.insert(myuploadfield=open(....)), thank you Iceberg
- db(...).select(db.mytable.myfield.count(distinct=True)) - db(...).select(db.mytable.myfield.count(distinct=True))
- db(db.a)._update(name=db(db.b.a==db.a.id).nested_select(db.b.id)) - db(db.a)._update(name=db(db.b.a==db.a.id).nested_select(db.b.id))
- db.mytable.myfield.filter_in, filter_out - db.mytable.myfield.filter_in, filter_out
- db.mytable._enable_record_versioning(db) adds verining to this table - db.mytable._enable_record_versioning(db) adds versioning to this table
- teradata adapter, thanks Andrew Willimott - teradata adapter, thanks Andrew Willimott
- experimental Sybase Adapter - experimental Sybase Adapter
- added db.table.field.avg() - added db.table.field.avg()
- Suport for Google App Engine projections - Support for Google App Engine projections, thanks Christian
- Field(... 'upload', default=path) now accepts a path to a local file as default value, if user does not upload a file. Relative path looks inside current application folder, thanks Marin
- executesql(...,fields=,columns=) allows parsing of results in Rows, thanks Anthony
### Auth improvements ### Auth improvements
- auth.enable_record_versioning(db) adds full versining to all tables - auth.enable_record_versioning(db) adds full versioning to all tables
- @auth.requires_login(otherwise=URL(...)) - @auth.requires_login(otherwise=URL(...))
- auth supports salt and compatible with third party data, thanks Dave Stoll - auth supports salt and compatible with third party data, thanks Dave Stoll
- CRYPT now defaults to pbkdf2(1000,20,sha1) - CRYPT now defaults to pbkdf2(1000,20,sha1)
@@ -33,14 +36,14 @@
- FORM.confirm('Are you sure?',{'Back':URL(...)}) - FORM.confirm('Are you sure?',{'Back':URL(...)})
- SQLFORM.smartdictform(dict) - SQLFORM.smartdictform(dict)
- form.add_button(value,link) - form.add_button(value,link)
- SQLFORM.grid(groupby=...') - SQLFORM.grid(groupby='...')
- fixed security issue with SQLFORM.grid and SQLFORM.smartgrid - fixed security issue with SQLFORM.grid and SQLFORM.smartgrid
- more export options in SQLFORM.grid and SQLFORM.smartgrid (html, xml, csv, ...) - more export options in SQLFORM.grid and SQLFORM.smartgrid (html, xml, csv, ...)
### Admin improvements ### Admin improvements
- new admin pages: manage_students, bulk_regsiter, and progress reports - new admin pages: manage_students, bulk_regsiter, and progress reports
- increased secure admin against CSRF - increased security in admin against CSRF
- experimental Git integration - experimental Git integration
- experimental OpenShift deployment - experimental OpenShift deployment
- multi-language pluralization engine - multi-language pluralization engine
@@ -49,17 +52,32 @@
- Romanian translation for welcome, thanks ionel - Romanian translation for welcome, thanks ionel
- support for mercurial 2.6, thanks Vlad - support for mercurial 2.6, thanks Vlad
### Scheduler Inprovements ### Scheduler Improvements (thanks to niphlod, ykessler, dhx, toomim)
- web2py.py -K myapp -X starts the myapp scheduler alongside the webserver - web2py.py -K myapp -X starts the myapp scheduler alongside the webserver
- tasks are marked EXPIRED (if stop_time passed)
- functions with no result don't end up in scheduler_run
- more options: web2py.py -E -b -L - more options: web2py.py -E -b -L
- scheduler can now handle 10k tasks with 20 concurrent workers and no known issues (thanks to niphlod, ykessler, dhx, toomim) - scheduler can now handle 10k tasks with 20 concurrent workers and with no issues
- tasks can be found in the environment (no need to define the tasks parameter) - new params:
- max_empty_runs kills the workers automatically if no new tasks are found in queue (nice for "spikes" of processing power), discard_results to completely discard the results (if you don't need the output of the task), utc_time enables datetime calculations with UTC time, task_name is no longer required (filled automatically with function_name if found empty), uuid makes easy to coordinate scheduler_task maintenance (filled automatically if not provided), stop_time has no default (previously was today+1), retry_failed to requeue automatically failed tasks, sync_output refreshes automatically the output (nice to report percentages) tasks can be found in the environment (no need to define the tasks parameter)
- tasks can be DISABLED (put to sleep and do nothing if not sending the heartbeat every 30 seconds), TERMINATE (complete the current task and then die), KILL (kill ASAP), EXPIRED (if stop_time passed) max_empty_runs kills the workers automatically if no new tasks are found in queue (nice for "spikes" of processing power)
discard_results to completely discard the results (if you don't need the output of the task)
utc_time enables datetime calculations with UTC time
- scheduler_task changes:
task_name is no longer required (filled automatically with function_name if found empty)
uuid makes easy to coordinate scheduler_task maintenance (filled automatically if not provided)
stop_time has no default (previously was today+1)
retry_failed to requeue automatically failed tasks
sync_output refreshes automatically the output (nice to report percentages)
- workers can be:
DISABLED (put to sleep and do nothing if not sending the heartbeat every 30 seconds)
TERMINATE (complete the current task and then die)
KILL (kill ASAP)
### Other Improvements ### Other Improvements
- DIV(..).elements(...replace=...), thanks Anthony
- new layout based on Twitter Bootstrap - new layout based on Twitter Bootstrap
- New generic views: generic.ics (Mac Mail Calendar) and generic.map (Google Maps) - New generic views: generic.ics (Mac Mail Calendar) and generic.map (Google Maps)
- request.args(0,default=0, cast=int, otherwise=URL(...)), thanks Anthony - request.args(0,default=0, cast=int, otherwise=URL(...)), thanks Anthony
@@ -67,18 +85,19 @@
- routes in can redirect outside with routes_in=[('/path','303->http://..')] - routes in can redirect outside with routes_in=[('/path','303->http://..')]
- better memcache support - better memcache support
- improved spreadsheet, thanks Alan - improved spreadsheet, thanks Alan
- new interantionalization engine, thanks Vladyslav - new internationalization engine, thanks Vladyslav
- pluralization engine, thanks Vladyslav - pluralization engine, thanks Vladyslav
- new makrmin with supports for nested lists, <i>, <em>, autolinks, thanks Vladyslav - new markmin with support for nested lists, <i>, <em>, autolinks, thanks Vladyslav
- new syntax: {{=BR()*5}} - new syntax: {{=BR()*5}}
- gluon.cache.lazy_cache decorator allows caching functions in modules - gluon.cache.lazy_cache decorator allows caching functions in modules
- .coffee and .less support in response.fields, thanks Sam Sheftel - .coffee and .less support in response.files, thanks Sam Sheftel
- ldap certificate support - ldap certificate support
- pg8000 postgresql driver support (experimental) - pg8000 postgresql driver support (experimental)
- @cache('%(name)s%(args)s%(vars)s',5) and cache.autokey - @cache('%(name)s%(args)s%(vars)s',5) and cache.autokey
- added tox.ini, thanks Marc - added tox.ini, thanks Marc
- web2py.py --run_system_tests, thanks Marc Abramowitz - web2py.py --run_system_tests, thanks Marc Abramowitz
- html.py (and web2py helpers) can be used without web2py dependencies - html.py (and web2py helpers) can be used without web2py dependencies
- new fpdf, thanks Mariano
## 1.99.5-1.99.7 ## 1.99.5-1.99.7
+4 -4
View File
@@ -1,3 +1,7 @@
all:
echo "The Makefile is used to build the distribution."
echo "In order to run web2py you do not need to make anything."
echo "just run web2py.py"
clean: clean:
rm -f httpserver.log rm -f httpserver.log
rm -f parameters*.py rm -f parameters*.py
@@ -13,10 +17,6 @@ clean:
find ./applications/examples/ -name '.*' -exec rm -f {} \; find ./applications/examples/ -name '.*' -exec rm -f {} \;
find ./applications/welcome/ -name '.*' -exec rm -f {} \; find ./applications/welcome/ -name '.*' -exec rm -f {} \;
find ./ -name '*.pyc' -exec rm -f {} \; find ./ -name '*.pyc' -exec rm -f {} \;
all:
echo "The Makefile is used to build the distribution."
echo "In order to run web2py you do not need to make anything."
echo "just run web2py.py"
epydoc: epydoc:
### build epydoc ### build epydoc
rm -f -r applications/examples/static/epydoc/ rm -f -r applications/examples/static/epydoc/
+1 -1
View File
@@ -1 +1 @@
Version 2.00.0 (2012-08-22 19:47:08) dev Version 2.00.0 (2012-08-25 16:19:46) dev
+2
View File
@@ -6,3 +6,5 @@
+1
View File
@@ -309,3 +309,4 @@ if __name__=='__main__':
+1
View File
@@ -6,3 +6,4 @@ def webapp_add_wsgi_middleware(app):
+11 -9
View File
@@ -20,7 +20,7 @@ try:
have_git = True have_git = True
except ImportError: except ImportError:
have_git = False have_git = False
GIT_MISSING = 'requires python-git module, but not installed' GIT_MISSING = 'requires python-git module, but not installed or incompatible version'
from gluon.languages import (regex_language, read_possible_languages, from gluon.languages import (regex_language, read_possible_languages,
read_possible_plurals, lang_sampling, read_possible_plurals, lang_sampling,
@@ -206,7 +206,8 @@ def site():
elif form_create.accepted: elif form_create.accepted:
# create a new application # create a new application
appname = cleanpath(form_create.vars.name) appname = cleanpath(form_create.vars.name)
if app_create(appname, request): created, error = app_create(appname, request,info=True)
if created:
if MULTI_USER_MODE: if MULTI_USER_MODE:
db.app.insert(name=appname,owner=auth.user.id) db.app.insert(name=appname,owner=auth.user.id)
log_progress(appname) log_progress(appname)
@@ -214,14 +215,15 @@ def site():
redirect(URL('design',args=appname)) redirect(URL('design',args=appname))
else: else:
session.flash = \ session.flash = \
T('unable to create application "%s" (it may exist already)', DIV(T('unable to create application "%s"' % appname),
form_create.vars.name) PRE(error))
redirect(URL(r=request)) redirect(URL(r=request))
elif form_update.accepted: elif form_update.accepted:
if (form_update.vars.url or '').endswith('.git'): if (form_update.vars.url or '').endswith('.git'):
if not have_git: if not have_git:
session.flash = GIT_MISSING session.flash = GIT_MISSING
redirect(URL(r=request))
target = os.path.join(apath(r=request),form_update.vars.name) target = os.path.join(apath(r=request),form_update.vars.name)
try: try:
new_repo = Repo.clone_from(form_update.vars.url,target) new_repo = Repo.clone_from(form_update.vars.url,target)
@@ -336,7 +338,7 @@ def pack_plugin():
redirect(URL('plugin',args=request.args)) redirect(URL('plugin',args=request.args))
def upgrade_web2py(): def upgrade_web2py():
dialog = FORM.confim(T('Upgrade'), dialog = FORM.confirm(T('Upgrade'),
{T('Cancel'):URL('site')}) {T('Cancel'):URL('site')})
if dialog.accepted: if dialog.accepted:
(success, error) = upgrade(request) (success, error) = upgrade(request)
@@ -350,7 +352,7 @@ def upgrade_web2py():
def uninstall(): def uninstall():
app = get_app() app = get_app()
dialog = FORM.confim(T('Uninstall'), dialog = FORM.confirm(T('Uninstall'),
{T('Cancel'):URL('site')}) {T('Cancel'):URL('site')})
if dialog.accepted: if dialog.accepted:
@@ -433,7 +435,7 @@ def delete():
if isinstance(sender, list): # ## fix a problem with Vista if isinstance(sender, list): # ## fix a problem with Vista
sender = sender[0] sender = sender[0]
dialog = FORM.confim(T('Delete'), dialog = FORM.confirm(T('Delete'),
{T('Cancel'):URL(sender, anchor=request.vars.id)}) {T('Cancel'):URL(sender, anchor=request.vars.id)})
if dialog.accepted: if dialog.accepted:
@@ -1003,7 +1005,7 @@ def delete_plugin():
plugin = request.args(1) plugin = request.args(1)
plugin_name='plugin_'+plugin plugin_name='plugin_'+plugin
dialog = FORM.confim( dialog = FORM.confirm(
T('Delete'), T('Delete'),
{T('Cancel'):URL('design', args=app)}) {T('Cancel'):URL('design', args=app)})
@@ -1652,7 +1654,7 @@ def git_pull():
if not have_git: if not have_git:
session.flash = GIT_MISSING session.flash = GIT_MISSING
redirect(URL('site')) redirect(URL('site'))
dialog = FORM.confim(T('Pull'), dialog = FORM.confirm(T('Pull'),
{T('Cancel'):URL('site')}) {T('Cancel'):URL('site')})
if dialog.accepted: if dialog.accepted:
try: try:
+5 -5
View File
@@ -274,13 +274,13 @@ ul.button li a {
display: block; display: block;
z-index: 2; z-index: 2;
} }
a.button { a.button, a.btn {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
/* 1.1. Normal status */ /* 1.1. Normal status */
a.button, a.button, a.btn,
ul.button li a { ul.button li a {
background-position: 100% 0; background-position: 100% 0;
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -313,14 +313,14 @@ ul.button li.select a span {
/* 2. EDITABLE STYLES */ /* 2. EDITABLE STYLES */
/* 2.1. Image background used */ /* 2.1. Image background used */
a.button, a.button, a.btn,
.button a, .button a,
.button span { .button span {
background-image: url(../images/button.png); background-image: url(../images/button.png);
} }
/* 2.2. Normal status (Example for padding 10px) */ /* 2.2. Normal status (Example for padding 10px) */
a.button, a.button, a.btn,
.button a { .button a {
padding: 0 10px 0 0; /* Padding-right: 10px */ padding: 0 10px 0 0; /* Padding-right: 10px */
margin: 0 1px 0 10px; /* Margin-left: 10px */ margin: 0 1px 0 10px; /* Margin-left: 10px */
@@ -342,7 +342,7 @@ a.button span {
_float: left; /* Only IE6 */ _float: left; /* Only IE6 */
_position: relative; /* Only IE6 */ _position: relative; /* Only IE6 */
} }
a.button { a.button, a.btn {
display: -moz-inline-box; /* FF<3 */ display: -moz-inline-box; /* FF<3 */
display: inline-block; /* FF<3 */ display: inline-block; /* FF<3 */
-moz-box-orient: vertical; /* FF<3 */ -moz-box-orient: vertical; /* FF<3 */
+9 -3
View File
@@ -80,11 +80,11 @@ fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4p
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
td.w2p_fl {text-align:right} td.w2p_fl {text-align:left}
td.w2p_fl, td.w2p_fw {padding-right:7px} td.w2p_fl, td.w2p_fw {padding-right:7px}
td.w2p_fl,td.w2p_fc {padding-top:4px} td.w2p_fl,td.w2p_fc {padding-top:4px}
div.w2p_export_menu {margin:5px 0} div.w2p_export_menu {margin:5px 0}
div.w2p_export_menu a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;} div.w2p_export_menu a, div.w2p_wiki_tags a, div.w2p_cloud a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;}
/* tr#submit_record__row {border-top:1px solid #E5E5E5} */ /* tr#submit_record__row {border-top:1px solid #E5E5E5} */
#submit_record__row td {padding-top:.5em} #submit_record__row td {padding-top:.5em}
@@ -294,8 +294,14 @@ div.error {
margin-bottom:18px; margin-bottom:18px;
} }
.web2py_breadcrumbs ul li { li.w2p_grid_breadcrumb_elem {
display:inline-block; display:inline-block;
} }
.ie9 #query_panel {padding-bottom:2px} .ie9 #query_panel {padding-bottom:2px}
#wiki_page_body {
width: 600px;
height: auto;
min-height: 400px;
}
@@ -78,6 +78,16 @@
</p> </p>
</div> </div>
{{pass}} {{pass}}
<!-- MULTI_USER_INTERFACE -->
{{if MULTI_USER_MODE and is_manager():}}
<div class="box">
<h3>{{=T("Multi User Mode")}}</h3>
<p>
{{=button(URL('bulk_register'),T('Bulk Register'))}}
{{=button(URL('manage_students'),T('Manage Students'))}}
</p>
</div>
{{pass}}
<!-- APP WIZARD --> <!-- APP WIZARD -->
<div class="box"> <div class="box">
<h3>{{=T("New application wizard")}}</h3> <h3>{{=T("New application wizard")}}</h3>
@@ -7,6 +7,7 @@
- [[User Voice http://web2py.uservoice.com/ popup]] - [[User Voice http://web2py.uservoice.com/ popup]]
#### Learning and Demos #### Learning and Demos
- [[Killer Web Development Tutorial http://killer-web-development.com/]]
- [[Admin Demo http://www.web2py.com/demo_admin popup]] (web-based IDE) - [[Admin Demo http://www.web2py.com/demo_admin popup]] (web-based IDE)
- [[Welcome App Demo http://www.web2py.com/welcome]] (scaffolding application) - [[Welcome App Demo http://www.web2py.com/welcome]] (scaffolding application)
- [[Videos http://www.web2py.com/examples/default/videos/]] - [[Videos http://www.web2py.com/examples/default/videos/]]
+9 -3
View File
@@ -80,11 +80,11 @@ fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4p
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
td.w2p_fl {text-align:right} td.w2p_fl {text-align:left}
td.w2p_fl, td.w2p_fw {padding-right:7px} td.w2p_fl, td.w2p_fw {padding-right:7px}
td.w2p_fl,td.w2p_fc {padding-top:4px} td.w2p_fl,td.w2p_fc {padding-top:4px}
div.w2p_export_menu {margin:5px 0} div.w2p_export_menu {margin:5px 0}
div.w2p_export_menu a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;} div.w2p_export_menu a, div.w2p_wiki_tags a, div.w2p_cloud a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;}
/* tr#submit_record__row {border-top:1px solid #E5E5E5} */ /* tr#submit_record__row {border-top:1px solid #E5E5E5} */
#submit_record__row td {padding-top:.5em} #submit_record__row td {padding-top:.5em}
@@ -294,8 +294,14 @@ div.error {
margin-bottom:18px; margin-bottom:18px;
} }
.web2py_breadcrumbs ul li { li.w2p_grid_breadcrumb_elem {
display:inline-block; display:inline-block;
} }
.ie9 #query_panel {padding-bottom:2px} .ie9 #query_panel {padding-bottom:2px}
#wiki_page_body {
width: 600px;
height: auto;
min-height: 400px;
}
+8 -2
View File
@@ -80,11 +80,11 @@ fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4p
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
td.w2p_fl {text-align:right} td.w2p_fl {text-align:left}
td.w2p_fl, td.w2p_fw {padding-right:7px} td.w2p_fl, td.w2p_fw {padding-right:7px}
td.w2p_fl,td.w2p_fc {padding-top:4px} td.w2p_fl,td.w2p_fc {padding-top:4px}
div.w2p_export_menu {margin:5px 0} div.w2p_export_menu {margin:5px 0}
div.w2p_export_menu a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;} div.w2p_export_menu a, div.w2p_wiki_tags a, div.w2p_cloud a {margin-left:5px; padding:2px 5px; background-color:#f1f1f1; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px;}
/* tr#submit_record__row {border-top:1px solid #E5E5E5} */ /* tr#submit_record__row {border-top:1px solid #E5E5E5} */
#submit_record__row td {padding-top:.5em} #submit_record__row td {padding-top:.5em}
@@ -299,3 +299,9 @@ li.w2p_grid_breadcrumb_elem {
} }
.ie9 #query_panel {padding-bottom:2px} .ie9 #query_panel {padding-bottom:2px}
#wiki_page_body {
width: 600px;
height: auto;
min-height: 400px;
}
@@ -1,4 +1,4 @@
{{left_sidebar_enabled,right_sidebar_enabled=False,True}} {{left_sidebar_enabled,right_sidebar_enabled=False,('message' in globals())}}
{{extend 'layout.html'}} {{extend 'layout.html'}}
{{if 'message' in globals():}} {{if 'message' in globals():}}
@@ -17,6 +17,8 @@
_href=URL('admin','default','peek',args=(request.application,'views',request.controller,'index.html')))))}}</li> _href=URL('admin','default','peek',args=(request.application,'views',request.controller,'index.html')))))}}</li>
<li>{{=T('You can modify this application and adapt it to your needs')}}</li> <li>{{=T('You can modify this application and adapt it to your needs')}}</li>
</ol> </ol>
{{elif 'content' in globals():}}
{{=content}}
{{else:}} {{else:}}
{{=BEAUTIFY(response._vars)}} {{=BEAUTIFY(response._vars)}}
{{pass}} {{pass}}
@@ -31,4 +33,3 @@
<li><a href="http://web2py.com/book">{{=T('Documentation')}}</a></li> <li><a href="http://web2py.com/book">{{=T('Documentation')}}</a></li>
</ul> </ul>
{{end}} {{end}}
+2 -7
View File
@@ -43,12 +43,8 @@
response.files.append(URL('static','css/bootstrap.min.css')) response.files.append(URL('static','css/bootstrap.min.css'))
response.files.append(URL('static','css/bootstrap-responsive.min.css')) response.files.append(URL('static','css/bootstrap-responsive.min.css'))
response.files.append(URL('static','css/web2py.css')) response.files.append(URL('static','css/web2py.css'))
response.files.append(URL('static','css/bootswatch.css')) response.files.append(URL('static','css/web2py_bootstrap.css'))
}} }}
<!-- Makes bootswatch working on IE 7/8 -->
<!--[if (lt IE 9)]>
<link rel="stylesheet" href="{{=URL('static','css/bootswatch_ie.css')}}">
<![endif]-->
{{include 'web2py_ajax.html'}} {{include 'web2py_ajax.html'}}
@@ -64,7 +60,7 @@
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" type="text/css" media="all" /> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" type="text/css" media="all" />
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script> <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js" type="text/javascript"></script>
uncomment to load jquery-ui //--> uncomment to load jquery-ui //-->
<noscript><link href="{{=URL('static', 'css/bootswatch_nojs.css')}}" rel="stylesheet" type="text/css" /></noscript> <noscript><link href="{{=URL('static', 'css/web2py_bootstrap_nojs.css')}}" rel="stylesheet" type="text/css" /></noscript>
{{block head}}{{end}} {{block head}}{{end}}
</head> </head>
@@ -160,7 +156,6 @@
if(jQuery(this).find('ul').length) if(jQuery(this).find('ul').length)
jQuery(this).children('a').contents().before('<i class="chevron-right"></i>'); jQuery(this).children('a').contents().before('<i class="chevron-right"></i>');
}); });
if(jQuery(document).width()>=980) { if(jQuery(document).width()>=980) {
jQuery('ul.nav li.dropdown').hover(function() { jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn(); jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
+1
View File
@@ -65,3 +65,4 @@ wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase)
+1
View File
@@ -55,3 +55,4 @@ fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run()
+1
View File
@@ -103,3 +103,4 @@ if __name__ == '__main__':
+1
View File
@@ -52,3 +52,4 @@ if 0:
+25 -9
View File
@@ -18,6 +18,7 @@ from fileutils import up, fix_newlines, abspath, recursive_unlink
from fileutils import read_file, write_file, parse_version from fileutils import read_file, write_file, parse_version
from restricted import RestrictedError from restricted import RestrictedError
from settings import global_settings from settings import global_settings
from http import HTTP
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
import site import site
@@ -126,7 +127,7 @@ def app_cleanup(app, request):
r = False r = False
# Remove cache files # Remove cache files
path = apath('%s/sessions/' % app, request) path = apath('%s/cache/' % app, request)
if os.path.exists(path): if os.path.exists(path):
for f in os.listdir(path): for f in os.listdir(path):
try: try:
@@ -157,7 +158,7 @@ def app_compile(app, request):
remove_compiled_application(folder) remove_compiled_application(folder)
return tb return tb
def app_create(app, request,force=False,key=None): def app_create(app, request,force=False,key=None,info=False):
""" """
Create a copy of welcome.w2p (scaffolding) app Create a copy of welcome.w2p (scaffolding) app
@@ -169,11 +170,19 @@ def app_create(app, request,force=False,key=None):
the global request object the global request object
""" """
try: path = apath(app, request)
path = apath(app, request) if not os.path.exists(path):
os.mkdir(path) try:
except: os.mkdir(path)
if not force: except:
if info:
return False, traceback.format_exc(sys.exc_info)
else:
return False
elif not force:
if info:
return False, "Application exists"
else:
return False return False
try: try:
w2p_unpack('welcome.w2p', path) w2p_unpack('welcome.w2p', path)
@@ -189,10 +198,16 @@ def app_create(app, request,force=False,key=None):
data = data.replace('<your secret key>', data = data.replace('<your secret key>',
'sha512:'+(key or web2py_uuid())) 'sha512:'+(key or web2py_uuid()))
write_file(db, data) write_file(db, data)
return True if info:
return True, None
else:
return True
except: except:
rmtree(path) rmtree(path)
return False if info:
return False, traceback.format_exc(sys.exc_info)
else:
return False
def app_install(app, fobj, request, filename, overwrite=None): def app_install(app, fobj, request, filename, overwrite=None):
@@ -465,3 +480,4 @@ def create_missing_app_folders(request):
+1
View File
@@ -498,3 +498,4 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'):
return decorator return decorator
+1
View File
@@ -56,3 +56,4 @@ def getcfs(key, filename, filter=None):
+1
View File
@@ -691,3 +691,4 @@ if __name__ == '__main__':
+1
View File
@@ -723,3 +723,4 @@ def contenttype(filename, default='text/plain'):
+1
View File
@@ -263,3 +263,4 @@ if __name__=='__main__':
+1
View File
@@ -243,3 +243,4 @@ if __name__=='__main__':
+1
View File
@@ -5,3 +5,4 @@
+1
View File
@@ -501,3 +501,4 @@ aes_Rcon = array('B',
'61c29f254a943366cc831d3a74e8cb'.decode('hex') '61c29f254a943366cc831d3a74e8cb'.decode('hex')
) )
+3 -2
View File
@@ -135,7 +135,7 @@ def oembed(url):
for k,v in EMBED_MAPS: for k,v in EMBED_MAPS:
if k.match(url): if k.match(url):
oembed = v+'?format=json&url='+cgi.escape(url) oembed = v+'?format=json&url='+cgi.escape(url)
try: try:
data = urllib.urlopen(oembed).read() data = urllib.urlopen(oembed).read()
print data print data
return loads(data) # json! return loads(data) # json!
@@ -152,7 +152,7 @@ def expand_one(url,cdict):
r = cdict[url] r = cdict[url]
else: else:
r = oembed(url) r = oembed(url)
if isinstance(cdict,dict): if isinstance(cdict,dict):
cdict[url] = r cdict[url] = r
# if oembed service # if oembed service
if 'html' in r: if 'html' in r:
@@ -204,3 +204,4 @@ if __name__=="__main__":
else: else:
print test() print test()
+1
View File
@@ -194,3 +194,4 @@ if __name__ == "__main__":
+1
View File
@@ -3910,3 +3910,4 @@ def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, refer
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"FPDF for python"
__license__ = "LGPL 3.0"
__version__ = "1.7"
from fpdf import *
try:
from html import HTMLMixin
except ImportError:
import warnings
warnings.warn("web2py gluon package not installed, required for html2pdf")
from template import Template
+157
View File
@@ -0,0 +1,157 @@
#!/usr/bin/env python
# -*- coding: latin-1 -*-
# Fonts:
fpdf_charwidths = {}
fpdf_charwidths['courier']={}
for i in xrange(0,256):
fpdf_charwidths['courier'][chr(i)]=600
fpdf_charwidths['courierB']=fpdf_charwidths['courier']
fpdf_charwidths['courierI']=fpdf_charwidths['courier']
fpdf_charwidths['courierBI']=fpdf_charwidths['courier']
fpdf_charwidths['helvetica']={
'\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278,
'\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584,
',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667,
'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944,
'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833,
'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556,
'\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333,
'\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667,
'\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556,
'\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500}
fpdf_charwidths['helveticaB']={
'\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278,
'\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584,
',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722,
'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944,
'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889,
'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556,
'\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333,
'\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722,
'\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611,
'\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556
}
fpdf_charwidths['helveticaBI']={
'\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278,
'\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':333,'"':474,'#':556,'$':556,'%':889,'&':722,'\'':238,'(':333,')':333,'*':389,'+':584,
',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':333,';':333,'<':584,'=':584,'>':584,'?':611,'@':975,'A':722,
'B':722,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':556,'K':722,'L':611,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944,
'X':667,'Y':667,'Z':611,'[':333,'\\':278,']':333,'^':584,'_':556,'`':333,'a':556,'b':611,'c':556,'d':611,'e':556,'f':333,'g':611,'h':611,'i':278,'j':278,'k':556,'l':278,'m':889,
'n':611,'o':611,'p':611,'q':611,'r':389,'s':556,'t':333,'u':611,'v':556,'w':778,'x':556,'y':556,'z':500,'{':389,'|':280,'}':389,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':278,'\x83':556,
'\x84':500,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':278,'\x92':278,'\x93':500,'\x94':500,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':556,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':280,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333,
'\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':611,'\xb6':556,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722,
'\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':556,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':611,'\xf1':611,
'\xf2':611,'\xf3':611,'\xf4':611,'\xf5':611,'\xf6':611,'\xf7':584,'\xf8':611,'\xf9':611,'\xfa':611,'\xfb':611,'\xfc':611,'\xfd':556,'\xfe':611,'\xff':556}
fpdf_charwidths['helveticaI']={
'\x00':278,'\x01':278,'\x02':278,'\x03':278,'\x04':278,'\x05':278,'\x06':278,'\x07':278,'\x08':278,'\t':278,'\n':278,'\x0b':278,'\x0c':278,'\r':278,'\x0e':278,'\x0f':278,'\x10':278,'\x11':278,'\x12':278,'\x13':278,'\x14':278,'\x15':278,
'\x16':278,'\x17':278,'\x18':278,'\x19':278,'\x1a':278,'\x1b':278,'\x1c':278,'\x1d':278,'\x1e':278,'\x1f':278,' ':278,'!':278,'"':355,'#':556,'$':556,'%':889,'&':667,'\'':191,'(':333,')':333,'*':389,'+':584,
',':278,'-':333,'.':278,'/':278,'0':556,'1':556,'2':556,'3':556,'4':556,'5':556,'6':556,'7':556,'8':556,'9':556,':':278,';':278,'<':584,'=':584,'>':584,'?':556,'@':1015,'A':667,
'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':722,'I':278,'J':500,'K':667,'L':556,'M':833,'N':722,'O':778,'P':667,'Q':778,'R':722,'S':667,'T':611,'U':722,'V':667,'W':944,
'X':667,'Y':667,'Z':611,'[':278,'\\':278,']':278,'^':469,'_':556,'`':333,'a':556,'b':556,'c':500,'d':556,'e':556,'f':278,'g':556,'h':556,'i':222,'j':222,'k':500,'l':222,'m':833,
'n':556,'o':556,'p':556,'q':556,'r':333,'s':500,'t':278,'u':556,'v':500,'w':722,'x':500,'y':500,'z':500,'{':334,'|':260,'}':334,'~':584,'\x7f':350,'\x80':556,'\x81':350,'\x82':222,'\x83':556,
'\x84':333,'\x85':1000,'\x86':556,'\x87':556,'\x88':333,'\x89':1000,'\x8a':667,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':222,'\x92':222,'\x93':333,'\x94':333,'\x95':350,'\x96':556,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':500,'\x9b':333,'\x9c':944,'\x9d':350,'\x9e':500,'\x9f':667,'\xa0':278,'\xa1':333,'\xa2':556,'\xa3':556,'\xa4':556,'\xa5':556,'\xa6':260,'\xa7':556,'\xa8':333,'\xa9':737,'\xaa':370,'\xab':556,'\xac':584,'\xad':333,'\xae':737,'\xaf':333,
'\xb0':400,'\xb1':584,'\xb2':333,'\xb3':333,'\xb4':333,'\xb5':556,'\xb6':537,'\xb7':278,'\xb8':333,'\xb9':333,'\xba':365,'\xbb':556,'\xbc':834,'\xbd':834,'\xbe':834,'\xbf':611,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667,
'\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':278,'\xcd':278,'\xce':278,'\xcf':278,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':584,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':667,'\xde':667,'\xdf':611,'\xe0':556,'\xe1':556,'\xe2':556,'\xe3':556,'\xe4':556,'\xe5':556,'\xe6':889,'\xe7':500,'\xe8':556,'\xe9':556,'\xea':556,'\xeb':556,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':556,'\xf1':556,
'\xf2':556,'\xf3':556,'\xf4':556,'\xf5':556,'\xf6':556,'\xf7':584,'\xf8':611,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500}
fpdf_charwidths['symbol']={
'\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250,
'\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':713,'#':500,'$':549,'%':833,'&':778,'\'':439,'(':333,')':333,'*':500,'+':549,
',':250,'-':549,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':549,'=':549,'>':549,'?':444,'@':549,'A':722,
'B':667,'C':722,'D':612,'E':611,'F':763,'G':603,'H':722,'I':333,'J':631,'K':722,'L':686,'M':889,'N':722,'O':722,'P':768,'Q':741,'R':556,'S':592,'T':611,'U':690,'V':439,'W':768,
'X':645,'Y':795,'Z':611,'[':333,'\\':863,']':333,'^':658,'_':500,'`':500,'a':631,'b':549,'c':549,'d':494,'e':439,'f':521,'g':411,'h':603,'i':329,'j':603,'k':549,'l':549,'m':576,
'n':521,'o':549,'p':549,'q':521,'r':549,'s':603,'t':439,'u':576,'v':713,'w':686,'x':493,'y':686,'z':494,'{':480,'|':200,'}':480,'~':549,'\x7f':0,'\x80':0,'\x81':0,'\x82':0,'\x83':0,
'\x84':0,'\x85':0,'\x86':0,'\x87':0,'\x88':0,'\x89':0,'\x8a':0,'\x8b':0,'\x8c':0,'\x8d':0,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0,
'\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':750,'\xa1':620,'\xa2':247,'\xa3':549,'\xa4':167,'\xa5':713,'\xa6':500,'\xa7':753,'\xa8':753,'\xa9':753,'\xaa':753,'\xab':1042,'\xac':987,'\xad':603,'\xae':987,'\xaf':603,
'\xb0':400,'\xb1':549,'\xb2':411,'\xb3':549,'\xb4':549,'\xb5':713,'\xb6':494,'\xb7':460,'\xb8':549,'\xb9':549,'\xba':549,'\xbb':549,'\xbc':1000,'\xbd':603,'\xbe':1000,'\xbf':658,'\xc0':823,'\xc1':686,'\xc2':795,'\xc3':987,'\xc4':768,'\xc5':768,
'\xc6':823,'\xc7':768,'\xc8':768,'\xc9':713,'\xca':713,'\xcb':713,'\xcc':713,'\xcd':713,'\xce':713,'\xcf':713,'\xd0':768,'\xd1':713,'\xd2':790,'\xd3':790,'\xd4':890,'\xd5':823,'\xd6':549,'\xd7':250,'\xd8':713,'\xd9':603,'\xda':603,'\xdb':1042,
'\xdc':987,'\xdd':603,'\xde':987,'\xdf':603,'\xe0':494,'\xe1':329,'\xe2':790,'\xe3':790,'\xe4':786,'\xe5':713,'\xe6':384,'\xe7':384,'\xe8':384,'\xe9':384,'\xea':384,'\xeb':384,'\xec':494,'\xed':494,'\xee':494,'\xef':494,'\xf0':0,'\xf1':329,
'\xf2':274,'\xf3':686,'\xf4':686,'\xf5':686,'\xf6':384,'\xf7':384,'\xf8':384,'\xf9':384,'\xfa':384,'\xfb':384,'\xfc':494,'\xfd':494,'\xfe':494,'\xff':0}
fpdf_charwidths['times']={
'\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250,
'\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':408,'#':500,'$':500,'%':833,'&':778,'\'':180,'(':333,')':333,'*':500,'+':564,
',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':278,';':278,'<':564,'=':564,'>':564,'?':444,'@':921,'A':722,
'B':667,'C':667,'D':722,'E':611,'F':556,'G':722,'H':722,'I':333,'J':389,'K':722,'L':611,'M':889,'N':722,'O':722,'P':556,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':722,'W':944,
'X':722,'Y':722,'Z':611,'[':333,'\\':278,']':333,'^':469,'_':500,'`':333,'a':444,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':500,'i':278,'j':278,'k':500,'l':278,'m':778,
'n':500,'o':500,'p':500,'q':500,'r':333,'s':389,'t':278,'u':500,'v':500,'w':722,'x':500,'y':500,'z':444,'{':480,'|':200,'}':480,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500,
'\x84':444,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':889,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':444,'\x94':444,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':980,
'\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':200,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':564,'\xad':333,'\xae':760,'\xaf':333,
'\xb0':400,'\xb1':564,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':453,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':444,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722,
'\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':564,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':722,'\xde':556,'\xdf':500,'\xe0':444,'\xe1':444,'\xe2':444,'\xe3':444,'\xe4':444,'\xe5':444,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500,
'\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':564,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':500,'\xfe':500,'\xff':500}
fpdf_charwidths['timesB']={
'\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250,
'\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':555,'#':500,'$':500,'%':1000,'&':833,'\'':278,'(':333,')':333,'*':500,'+':570,
',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':930,'A':722,
'B':667,'C':722,'D':722,'E':667,'F':611,'G':778,'H':778,'I':389,'J':500,'K':778,'L':667,'M':944,'N':722,'O':778,'P':611,'Q':778,'R':722,'S':556,'T':667,'U':722,'V':722,'W':1000,
'X':722,'Y':722,'Z':667,'[':333,'\\':278,']':333,'^':581,'_':500,'`':333,'a':500,'b':556,'c':444,'d':556,'e':444,'f':333,'g':500,'h':556,'i':278,'j':333,'k':556,'l':278,'m':833,
'n':556,'o':500,'p':556,'q':556,'r':444,'s':389,'t':333,'u':556,'v':500,'w':722,'x':500,'y':500,'z':444,'{':394,'|':220,'}':394,'~':520,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500,
'\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':1000,'\x8d':350,'\x8e':667,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':444,'\x9f':722,'\xa0':250,'\xa1':333,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':300,'\xab':500,'\xac':570,'\xad':333,'\xae':747,'\xaf':333,
'\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':556,'\xb6':540,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':330,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':722,'\xc1':722,'\xc2':722,'\xc3':722,'\xc4':722,'\xc5':722,
'\xc6':1000,'\xc7':722,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':778,'\xd3':778,'\xd4':778,'\xd5':778,'\xd6':778,'\xd7':570,'\xd8':778,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':722,'\xde':611,'\xdf':556,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556,
'\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':500,'\xfe':556,'\xff':500}
fpdf_charwidths['timesBI']={
'\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250,
'\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':389,'"':555,'#':500,'$':500,'%':833,'&':778,'\'':278,'(':333,')':333,'*':500,'+':570,
',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':570,'=':570,'>':570,'?':500,'@':832,'A':667,
'B':667,'C':667,'D':722,'E':667,'F':667,'G':722,'H':778,'I':389,'J':500,'K':667,'L':611,'M':889,'N':722,'O':722,'P':611,'Q':722,'R':667,'S':556,'T':611,'U':722,'V':667,'W':889,
'X':667,'Y':611,'Z':611,'[':333,'\\':278,']':333,'^':570,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':333,'g':500,'h':556,'i':278,'j':278,'k':500,'l':278,'m':778,
'n':556,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':556,'v':444,'w':667,'x':500,'y':444,'z':389,'{':348,'|':220,'}':348,'~':570,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500,
'\x84':500,'\x85':1000,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':556,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':611,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':500,'\x94':500,'\x95':350,'\x96':500,'\x97':1000,'\x98':333,'\x99':1000,
'\x9a':389,'\x9b':333,'\x9c':722,'\x9d':350,'\x9e':389,'\x9f':611,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':220,'\xa7':500,'\xa8':333,'\xa9':747,'\xaa':266,'\xab':500,'\xac':606,'\xad':333,'\xae':747,'\xaf':333,
'\xb0':400,'\xb1':570,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':576,'\xb6':500,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':300,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':667,'\xc1':667,'\xc2':667,'\xc3':667,'\xc4':667,'\xc5':667,
'\xc6':944,'\xc7':667,'\xc8':667,'\xc9':667,'\xca':667,'\xcb':667,'\xcc':389,'\xcd':389,'\xce':389,'\xcf':389,'\xd0':722,'\xd1':722,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':570,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':611,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':722,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':556,
'\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':570,'\xf8':500,'\xf9':556,'\xfa':556,'\xfb':556,'\xfc':556,'\xfd':444,'\xfe':500,'\xff':444}
fpdf_charwidths['timesI']={
'\x00':250,'\x01':250,'\x02':250,'\x03':250,'\x04':250,'\x05':250,'\x06':250,'\x07':250,'\x08':250,'\t':250,'\n':250,'\x0b':250,'\x0c':250,'\r':250,'\x0e':250,'\x0f':250,'\x10':250,'\x11':250,'\x12':250,'\x13':250,'\x14':250,'\x15':250,
'\x16':250,'\x17':250,'\x18':250,'\x19':250,'\x1a':250,'\x1b':250,'\x1c':250,'\x1d':250,'\x1e':250,'\x1f':250,' ':250,'!':333,'"':420,'#':500,'$':500,'%':833,'&':778,'\'':214,'(':333,')':333,'*':500,'+':675,
',':250,'-':333,'.':250,'/':278,'0':500,'1':500,'2':500,'3':500,'4':500,'5':500,'6':500,'7':500,'8':500,'9':500,':':333,';':333,'<':675,'=':675,'>':675,'?':500,'@':920,'A':611,
'B':611,'C':667,'D':722,'E':611,'F':611,'G':722,'H':722,'I':333,'J':444,'K':667,'L':556,'M':833,'N':667,'O':722,'P':611,'Q':722,'R':611,'S':500,'T':556,'U':722,'V':611,'W':833,
'X':611,'Y':556,'Z':556,'[':389,'\\':278,']':389,'^':422,'_':500,'`':333,'a':500,'b':500,'c':444,'d':500,'e':444,'f':278,'g':500,'h':500,'i':278,'j':278,'k':444,'l':278,'m':722,
'n':500,'o':500,'p':500,'q':500,'r':389,'s':389,'t':278,'u':500,'v':444,'w':667,'x':444,'y':444,'z':389,'{':400,'|':275,'}':400,'~':541,'\x7f':350,'\x80':500,'\x81':350,'\x82':333,'\x83':500,
'\x84':556,'\x85':889,'\x86':500,'\x87':500,'\x88':333,'\x89':1000,'\x8a':500,'\x8b':333,'\x8c':944,'\x8d':350,'\x8e':556,'\x8f':350,'\x90':350,'\x91':333,'\x92':333,'\x93':556,'\x94':556,'\x95':350,'\x96':500,'\x97':889,'\x98':333,'\x99':980,
'\x9a':389,'\x9b':333,'\x9c':667,'\x9d':350,'\x9e':389,'\x9f':556,'\xa0':250,'\xa1':389,'\xa2':500,'\xa3':500,'\xa4':500,'\xa5':500,'\xa6':275,'\xa7':500,'\xa8':333,'\xa9':760,'\xaa':276,'\xab':500,'\xac':675,'\xad':333,'\xae':760,'\xaf':333,
'\xb0':400,'\xb1':675,'\xb2':300,'\xb3':300,'\xb4':333,'\xb5':500,'\xb6':523,'\xb7':250,'\xb8':333,'\xb9':300,'\xba':310,'\xbb':500,'\xbc':750,'\xbd':750,'\xbe':750,'\xbf':500,'\xc0':611,'\xc1':611,'\xc2':611,'\xc3':611,'\xc4':611,'\xc5':611,
'\xc6':889,'\xc7':667,'\xc8':611,'\xc9':611,'\xca':611,'\xcb':611,'\xcc':333,'\xcd':333,'\xce':333,'\xcf':333,'\xd0':722,'\xd1':667,'\xd2':722,'\xd3':722,'\xd4':722,'\xd5':722,'\xd6':722,'\xd7':675,'\xd8':722,'\xd9':722,'\xda':722,'\xdb':722,
'\xdc':722,'\xdd':556,'\xde':611,'\xdf':500,'\xe0':500,'\xe1':500,'\xe2':500,'\xe3':500,'\xe4':500,'\xe5':500,'\xe6':667,'\xe7':444,'\xe8':444,'\xe9':444,'\xea':444,'\xeb':444,'\xec':278,'\xed':278,'\xee':278,'\xef':278,'\xf0':500,'\xf1':500,
'\xf2':500,'\xf3':500,'\xf4':500,'\xf5':500,'\xf6':500,'\xf7':675,'\xf8':500,'\xf9':500,'\xfa':500,'\xfb':500,'\xfc':500,'\xfd':444,'\xfe':500,'\xff':444}
fpdf_charwidths['zapfdingbats']={
'\x00':0,'\x01':0,'\x02':0,'\x03':0,'\x04':0,'\x05':0,'\x06':0,'\x07':0,'\x08':0,'\t':0,'\n':0,'\x0b':0,'\x0c':0,'\r':0,'\x0e':0,'\x0f':0,'\x10':0,'\x11':0,'\x12':0,'\x13':0,'\x14':0,'\x15':0,
'\x16':0,'\x17':0,'\x18':0,'\x19':0,'\x1a':0,'\x1b':0,'\x1c':0,'\x1d':0,'\x1e':0,'\x1f':0,' ':278,'!':974,'"':961,'#':974,'$':980,'%':719,'&':789,'\'':790,'(':791,')':690,'*':960,'+':939,
',':549,'-':855,'.':911,'/':933,'0':911,'1':945,'2':974,'3':755,'4':846,'5':762,'6':761,'7':571,'8':677,'9':763,':':760,';':759,'<':754,'=':494,'>':552,'?':537,'@':577,'A':692,
'B':786,'C':788,'D':788,'E':790,'F':793,'G':794,'H':816,'I':823,'J':789,'K':841,'L':823,'M':833,'N':816,'O':831,'P':923,'Q':744,'R':723,'S':749,'T':790,'U':792,'V':695,'W':776,
'X':768,'Y':792,'Z':759,'[':707,'\\':708,']':682,'^':701,'_':826,'`':815,'a':789,'b':789,'c':707,'d':687,'e':696,'f':689,'g':786,'h':787,'i':713,'j':791,'k':785,'l':791,'m':873,
'n':761,'o':762,'p':762,'q':759,'r':759,'s':892,'t':892,'u':788,'v':784,'w':438,'x':138,'y':277,'z':415,'{':392,'|':392,'}':668,'~':668,'\x7f':0,'\x80':390,'\x81':390,'\x82':317,'\x83':317,
'\x84':276,'\x85':276,'\x86':509,'\x87':509,'\x88':410,'\x89':410,'\x8a':234,'\x8b':234,'\x8c':334,'\x8d':334,'\x8e':0,'\x8f':0,'\x90':0,'\x91':0,'\x92':0,'\x93':0,'\x94':0,'\x95':0,'\x96':0,'\x97':0,'\x98':0,'\x99':0,
'\x9a':0,'\x9b':0,'\x9c':0,'\x9d':0,'\x9e':0,'\x9f':0,'\xa0':0,'\xa1':732,'\xa2':544,'\xa3':544,'\xa4':910,'\xa5':667,'\xa6':760,'\xa7':760,'\xa8':776,'\xa9':595,'\xaa':694,'\xab':626,'\xac':788,'\xad':788,'\xae':788,'\xaf':788,
'\xb0':788,'\xb1':788,'\xb2':788,'\xb3':788,'\xb4':788,'\xb5':788,'\xb6':788,'\xb7':788,'\xb8':788,'\xb9':788,'\xba':788,'\xbb':788,'\xbc':788,'\xbd':788,'\xbe':788,'\xbf':788,'\xc0':788,'\xc1':788,'\xc2':788,'\xc3':788,'\xc4':788,'\xc5':788,
'\xc6':788,'\xc7':788,'\xc8':788,'\xc9':788,'\xca':788,'\xcb':788,'\xcc':788,'\xcd':788,'\xce':788,'\xcf':788,'\xd0':788,'\xd1':788,'\xd2':788,'\xd3':788,'\xd4':894,'\xd5':838,'\xd6':1016,'\xd7':458,'\xd8':748,'\xd9':924,'\xda':748,'\xdb':918,
'\xdc':927,'\xdd':928,'\xde':928,'\xdf':834,'\xe0':873,'\xe1':828,'\xe2':924,'\xe3':924,'\xe4':917,'\xe5':930,'\xe6':931,'\xe7':463,'\xe8':883,'\xe9':836,'\xea':836,'\xeb':867,'\xec':867,'\xed':696,'\xee':696,'\xef':874,'\xf0':0,'\xf1':874,
'\xf2':760,'\xf3':946,'\xf4':771,'\xf5':865,'\xf6':771,'\xf7':888,'\xf8':967,'\xf9':888,'\xfa':831,'\xfb':873,'\xfc':927,'\xfd':970,'\xfe':918,'\xff':0}
File diff suppressed because it is too large Load Diff
@@ -26,22 +26,23 @@ def hex2dec(color = "#000000"):
class HTML2FPDF(HTMLParser): class HTML2FPDF(HTMLParser):
"Render basic HTML to FPDF" "Render basic HTML to FPDF"
def __init__(self, pdf, image_map, **kwargs): def __init__(self, pdf):
HTMLParser.__init__(self) HTMLParser.__init__(self)
self.image_map = image_map
self.style = {} self.style = {}
self.pre = False self.pre = False
self.href = '' self.href = ''
self.align = '' self.align = ''
self.page_links = {} self.page_links = {}
self.font_list = ("times","courier", "helvetica") self.font_list = ("times","courier", "helvetica")
self.font = None
self.font_stack = []
self.pdf = pdf self.pdf = pdf
self.r = self.g = self.b = 0 self.r = self.g = self.b = 0
self.indent = 0 self.indent = 0
self.bullet = [] self.bullet = []
self.font_face="times" # initialize font self.set_font("times", 12)
self.color=0 # initialize font color self.font_face = "times" # initialize font
self.set_font(kwargs.get("font","times"), kwargs.get("fontsize",12)) self.color = 0 #initialize font color
self.table = None # table attributes self.table = None # table attributes
self.table_col_width = None # column (header) widths self.table_col_width = None # column (header) widths
self.table_col_index = None # current column index self.table_col_index = None # current column index
@@ -66,7 +67,10 @@ class HTML2FPDF(HTMLParser):
def handle_data(self, txt): def handle_data(self, txt):
if self.td is not None: # drawing a table? if self.td is not None: # drawing a table?
if 'width' not in self.td and 'colspan' not in self.td: if 'width' not in self.td and 'colspan' not in self.td:
l = [self.table_col_width[self.table_col_index]] try:
l = [self.table_col_width[self.table_col_index]]
except IndexError:
raise RuntimeError("Table column/cell width not specified, unable to continue")
elif 'colspan' in self.td: elif 'colspan' in self.td:
i = self.table_col_index i = self.table_col_index
colspan = int(self.td['colspan']) colspan = int(self.td['colspan'])
@@ -83,7 +87,7 @@ class HTML2FPDF(HTMLParser):
else: else:
self.set_style('B',True) self.set_style('B',True)
border = border or 'B' border = border or 'B'
align = self.td.get('align', 'C')[0].upper() align = 'C'
bgcolor = hex2dec(self.td.get('bgcolor', self.tr.get('bgcolor', ''))) bgcolor = hex2dec(self.td.get('bgcolor', self.tr.get('bgcolor', '')))
# parsing table header/footer (drawn later): # parsing table header/footer (drawn later):
if self.thead is not None: if self.thead is not None:
@@ -173,7 +177,7 @@ class HTML2FPDF(HTMLParser):
if tag=='p': if tag=='p':
self.pdf.ln(5) self.pdf.ln(5)
if attrs: if attrs:
self.align=attrs['align'].lower() if attrs: self.align = attrs.get('align')
if tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'): if tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'h6'):
k = (2, 1.5, 1.17, 1, 0.83, 0.67)[int(tag[1])] k = (2, 1.5, 1.17, 1, 0.83, 0.67)[int(tag[1])]
self.pdf.ln(5*k) self.pdf.ln(5*k)
@@ -208,6 +212,8 @@ class HTML2FPDF(HTMLParser):
self.pdf.write(self.h,'%s%s ' % (' '*5*self.indent, bullet)) self.pdf.write(self.h,'%s%s ' % (' '*5*self.indent, bullet))
self.set_text_color() self.set_text_color()
if tag=='font': if tag=='font':
# save previous font state:
self.font_stack.append((self.font_face, self.font_size, self.color))
if 'color' in attrs: if 'color' in attrs:
self.color = hex2dec(attrs['color']) self.color = hex2dec(attrs['color'])
self.set_text_color(*color) self.set_text_color(*color)
@@ -234,6 +240,7 @@ class HTML2FPDF(HTMLParser):
self.tfooter = [] self.tfooter = []
self.thead = None self.thead = None
self.tfoot = None self.tfoot = None
self.table_h = 0
self.pdf.ln() self.pdf.ln()
if tag=='tr': if tag=='tr':
self.tr = dict([(k.lower(), v) for k,v in attrs.items()]) self.tr = dict([(k.lower(), v) for k,v in attrs.items()])
@@ -244,7 +251,7 @@ class HTML2FPDF(HTMLParser):
if tag=='th': if tag=='th':
self.td = dict([(k.lower(), v) for k,v in attrs.items()]) self.td = dict([(k.lower(), v) for k,v in attrs.items()])
self.th = True self.th = True
if self.td['width']: if 'width' in self.td:
self.table_col_width.append(self.td['width']) self.table_col_width.append(self.td['width'])
if tag=='thead': if tag=='thead':
self.thead = {} self.thead = {}
@@ -258,8 +265,7 @@ class HTML2FPDF(HTMLParser):
h = px2mm(attrs.get('height',0)) h = px2mm(attrs.get('height',0))
if self.align and self.align[0].upper() == 'C': if self.align and self.align[0].upper() == 'C':
x = (self.pdf.w-x)/2.0 - w/2.0 x = (self.pdf.w-x)/2.0 - w/2.0
self.pdf.image(self.image_map(attrs['src']), self.pdf.image(attrs['src'], x, y, w, h, link=self.href)
x, y, w, h, link=self.href)
self.pdf.set_x(x+w) self.pdf.set_x(x+w)
self.pdf.set_y(y+h) self.pdf.set_y(y+h)
if tag=='b' or tag=='i' or tag=='u': if tag=='b' or tag=='i' or tag=='u':
@@ -324,12 +330,13 @@ class HTML2FPDF(HTMLParser):
self.td = None self.td = None
self.th = False self.th = False
if tag=='font': if tag=='font':
if self.color: # recover last font state
face, size, color = self.font_stack.pop()
if face:
self.pdf.set_text_color(0,0,0) self.pdf.set_text_color(0,0,0)
self.color = None self.color = None
if self.font_face: self.set_font(face, size)
self.set_font('Times',12) self.font = None
if tag=='center': if tag=='center':
self.align = None self.align = None
@@ -381,79 +388,10 @@ class HTML2FPDF(HTMLParser):
self.pdf.line(self.pdf.get_x(),self.pdf.get_y(),self.pdf.get_x()+187,self.pdf.get_y()) self.pdf.line(self.pdf.get_x(),self.pdf.get_y(),self.pdf.get_x()+187,self.pdf.get_y())
self.pdf.ln(3) self.pdf.ln(3)
class HTMLMixin(): class HTMLMixin(object):
def write_html(self, text, image_map=lambda x:x, **kwargs): def write_html(self, text):
"Parse HTML and convert it to PDF" "Parse HTML and convert it to PDF"
h2p = HTML2FPDF(self,image_map=image_map,**kwargs) h2p = HTML2FPDF(self)
h2p.feed(text) h2p.feed(text)
if __name__=='__main__':
html="""
<H1 align="center">html2fpdf</H1>
<h2>Basic usage</h2>
<p>You can now easily print text mixing different
styles : <B>bold</B>, <I>italic</I>, <U>underlined</U>, or
<B><I><U>all at once</U></I></B>!<BR>You can also insert links
on text, such as <A HREF="http://www.fpdf.org">www.fpdf.org</A>,
or on an image: click on the logo.<br>
<center>
<A HREF="http://www.fpdf.org"><img src="tutorial/logo.png" width="104" height="71"></A>
</center>
<h3>Sample List</h3>
<ul><li>option 1</li>
<ol><li>option 2</li></ol>
<li>option 3</li></ul>
<table border="0" align="center" width="50%">
<thead><tr><th width="30%">Header 1</th><th width="70%">header 2</th></tr></thead>
<tbody>
<tr><td>cell 1</td><td>cell 2</td></tr>
<tr><td>cell 2</td><td>cell 3</td></tr>
</tbody>
</table>
<table border="1">
<thead><tr bgcolor="#A0A0A0"><th width="30%">Header 1</th><th width="70%">header 2</th></tr></thead>
<tfoot><tr bgcolor="#E0E0E0"><td>footer 1</td><td>footer 2</td></tr></tfoot>
<tbody>
<tr><td>cell 1</td><td>cell 2</td></tr>
<tr>
<td width="30%">cell 1</td><td width="70%" bgcolor="#D0D0FF" align='right'>cell 2</td>
</tr>
</tbody>
<tbody><tr><td colspan="2">cell spanned</td></tr></tbody>
<tbody>
""" + """<tr bgcolor="#F0F0F0">
<td>cell 3</td><td>cell 4</td>
</tr><tr bgcolor="#FFFFFF">
<td>cell 5</td><td>cell 6</td>
</tr>""" * 200 + """
</tbody>
</table>
"""
class MyFPDF(FPDF, HTMLMixin):
def header(self):
self.image('tutorial/logo_pb.png',10,8,33)
self.set_font('Arial','B',15)
self.cell(80)
self.cell(30,10,'Title',1,0,'C')
self.ln(20)
def footer(self):
self.set_y(-15)
self.set_font('Arial','I',8)
txt = 'Page %s of %s' % (self.page_no(), self.alias_nb_pages())
self.cell(0,10,txt,0,0,'C')
pdf=MyFPDF()
#First page
pdf.add_page()
pdf.write_html(html)
pdf.output('html.pdf','F')
import os
os.system("evince html.pdf")
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python
# -*- coding: latin-1 -*-
# fpdf php helpers:
def substr(s, start, length=-1):
if length < 0:
length=len(s)-start
return s[start:start+length]
def sprintf(fmt, *args): return fmt % args
def print_r(array):
if not isinstance(array, dict):
array = dict([(k, k) for k in array])
for k, v in array.items():
print "[%s] => %s" % (k, v),
def UTF8ToUTF16BE(instr, setbom=True):
"Converts UTF-8 strings to UTF16-BE."
outstr = ""
if (setbom):
outstr += "\xFE\xFF";
if not isinstance(instr, unicode):
instr = instr.decode('UTF-8')
outstr += instr.encode('UTF-16BE')
return outstr
def UTF8StringToArray(instr):
"Converts UTF-8 strings to codepoints array"
return [ord(c) for c in instr]
# ttfints php helpers:
def die(msg):
raise RuntimeError(msg)
def str_repeat(s, count):
return s * count
def str_pad(s, pad_length=0, pad_char= " ", pad_type= +1 ):
if pad_type<0: # pad left
return s.rjust(pad_length, pad_char)
elif pad_type>0: # pad right
return s.ljust(pad_length, pad_char)
else: # pad both
return s.center(pad_length, pad_char)
strlen = count = lambda s: len(s)
+52 -28
View File
@@ -16,7 +16,8 @@ class Template:
def __init__(self, infile=None, elements=None, format='A4', orientation='portrait', def __init__(self, infile=None, elements=None, format='A4', orientation='portrait',
title='', author='', subject='', creator='', keywords=''): title='', author='', subject='', creator='', keywords=''):
if elements: if elements:
self.elements = dict([(v['name'].lower(),v) for v in elements]) self.elements = elements
self.keys = [v['name'].lower() for v in self.elements]
self.handlers = {'T': self.text, 'L': self.line, 'I': self.image, self.handlers = {'T': self.text, 'L': self.line, 'I': self.image,
'B': self.rect, 'BC': self.barcode, } 'B': self.rect, 'BC': self.barcode, }
self.pg_no = 0 self.pg_no = 0
@@ -32,34 +33,33 @@ class Template:
"Parse template format csv file and create elements dict" "Parse template format csv file and create elements dict"
keys = ('name','type','x1','y1','x2','y2','font','size', keys = ('name','type','x1','y1','x2','y2','font','size',
'bold','italic','underline','foreground','background', 'bold','italic','underline','foreground','background',
'align','text','priority') 'align','text','priority', 'multiline')
self.elements = {} self.elements = []
f = open(infile, 'rb') for row in csv.reader(open(infile, 'rb'), delimiter=delimiter):
try: kargs = {}
for row in csv.reader(f, delimiter=delimiter): for i,v in enumerate(row):
kargs = {} if not v.startswith("'") and decimal_sep!=".":
for i,v in enumerate(row): v = v.replace(decimal_sep,".")
if not v.startswith("'") and decimal_sep!=".": else:
v = v.replace(decimal_sep,".") v = v
else: if v=='':
v = v v = None
if v=='': else:
v = None v = eval(v.strip())
else: kargs[keys[i]] = v
v = eval(v.strip()) self.elements.append(kargs)
kargs[keys[i]] = v self.keys = [v['name'].lower() for v in self.elements]
self.elements[kargs['name'].lower()] = kargs
finally:
f.close()
def add_page(self): def add_page(self):
self.pg_no += 1 self.pg_no += 1
self.texts[self.pg_no] = {} self.texts[self.pg_no] = {}
def __setitem__(self, name, value): def __setitem__(self, name, value):
if name.lower() in self.elements: if self.has_key(name):
if isinstance(value,unicode): if isinstance(value,unicode):
value = value.encode("latin1","ignore") value = value.encode("latin1","ignore")
elif value is None:
value = ""
else: else:
value = str(value) value = str(value)
self.texts[self.pg_no][name.lower()] = value self.texts[self.pg_no][name.lower()] = value
@@ -67,14 +67,27 @@ class Template:
# setitem shortcut (may be further extended) # setitem shortcut (may be further extended)
set = __setitem__ set = __setitem__
def has_key(self, name):
return name.lower() in self.keys
def __getitem__(self, name): def __getitem__(self, name):
if name.lower() in self.elements: if self.has_key(name):
return self.texts[self.pg_no].get(name.lower(), self.elements[name.lower()]['text']) key = name.lower()
if key in self.texts:
# text for this page:
return self.texts[self.pg_no][key]
else:
# find first element for default text:
elements = [element for element in self.elements
if element['name'].lower() == key]
if elements:
return elements[0]['text']
def split_multicell(self, text, element_name): def split_multicell(self, text, element_name):
"Divide (\n) a string using a given element width" "Divide (\n) a string using a given element width"
pdf = self.pdf pdf = self.pdf
element = self.elements[element_name.lower()] element = [element for element in self.elements
if element['name'].lower() == element_name.lower()][0]
style = "" style = ""
if element['bold']: style += "B" if element['bold']: style += "B"
if element['italic']: style += "I" if element['italic']: style += "I"
@@ -96,7 +109,7 @@ class Template:
pdf.set_font('Arial','B',16) pdf.set_font('Arial','B',16)
pdf.set_auto_page_break(False,margin=0) pdf.set_auto_page_break(False,margin=0)
for element in sorted(self.elements.values(),key=lambda x: x['priority']): for element in sorted(self.elements,key=lambda x: x['priority']):
#print "dib",element['type'], element['name'], element['x1'], element['y1'], element['x2'], element['y2'] #print "dib",element['type'], element['name'], element['x1'], element['y1'], element['x2'], element['y2']
element = element.copy() element = element.copy()
element['text'] = self.texts[pg].get(element['name'].lower(), element['text']) element['text'] = self.texts[pg].get(element['name'].lower(), element['text'])
@@ -110,7 +123,7 @@ class Template:
def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10, def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10,
bold=False, italic=False, underline=False, align="", bold=False, italic=False, underline=False, align="",
foreground=0, backgroud=65535, foreground=0, backgroud=65535, multiline=None,
*args, **kwargs): *args, **kwargs):
if text: if text:
if pdf.text_color!=rgb(foreground): if pdf.text_color!=rgb(foreground):
@@ -134,7 +147,19 @@ class Template:
##m_k = 72 / 2.54 ##m_k = 72 / 2.54
##h = (size/m_k) ##h = (size/m_k)
pdf.set_xy(x1,y1) pdf.set_xy(x1,y1)
pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align) if multiline is None:
# multiline==None: write without wrapping/trimming (default)
pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align)
elif multiline:
# multiline==True: automatic word - warp
pdf.multi_cell(w=x2-x1,h=y2-y1,txt=text,border=0,align=align)
else:
# multiline==False: trim to fit exactly the space defined
text = pdf.multi_cell(w=x2-x1, h=y2-y1,
txt=text, align=align, split_only=True)[0]
print "trimming: *%s*" % text
pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align)
#pdf.Text(x=x1,y=y1,txt=text) #pdf.Text(x=x1,y=y1,txt=text)
def line(self, pdf, x1=0, y1=0, x2=0, y2=0, size=0, foreground=0, *args, **kwargs): def line(self, pdf, x1=0, y1=0, x2=0, y2=0, size=0, foreground=0, *args, **kwargs):
@@ -275,4 +300,3 @@ if __name__ == "__main__":
else: else:
os.system("./invoice.pdf") os.system("./invoice.pdf")
File diff suppressed because it is too large Load Diff
+1
View File
@@ -55,3 +55,4 @@ class MemcacheClient(Client):
+1
View File
@@ -90,3 +90,4 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
+2 -1
View File
@@ -7,7 +7,7 @@ import gluon.serializers
from gluon import current, HTTP from gluon import current, HTTP
from gluon.html import markmin_serializer, TAG, HTML, BODY, UL, XML, H1 from gluon.html import markmin_serializer, TAG, HTML, BODY, UL, XML, H1
from gluon.contenttype import contenttype from gluon.contenttype import contenttype
from gluon.contrib.pyfpdf import FPDF, HTMLMixin from gluon.contrib.fpdf import FPDF, HTMLMixin
from gluon.sanitizer import sanitize from gluon.sanitizer import sanitize
from gluon.contrib.markmin.markmin2latex import markmin2latex from gluon.contrib.markmin.markmin2latex import markmin2latex
from gluon.contrib.markmin.markmin2pdf import markmin2pdf from gluon.contrib.markmin.markmin2pdf import markmin2pdf
@@ -67,3 +67,4 @@ def pdf_from_html(html):
+1
View File
@@ -19,3 +19,4 @@ def button(merchant_id="123456789012345",
+1
View File
@@ -9,3 +9,4 @@ from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, driv
+1
View File
@@ -58,3 +58,4 @@ def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
else: else:
return image return image
+1
View File
@@ -1,2 +1,3 @@
@@ -23,3 +23,4 @@ def basic_auth(server="http://127.0.0.1"):
return False return False
return basic_login_aux return basic_login_aux
@@ -6,18 +6,18 @@
developed by Madhukar R Pai (Copyright 2012) developed by Madhukar R Pai (Copyright 2012)
Email <madspai@gmail.com> Email <madspai@gmail.com>
License : LGPL License : LGPL
thanks and credits to the web2py community thanks and credits to the web2py community
This custom authenticator allows web2py to authenticate using browserid (https://browserid.org/) This custom authenticator allows web2py to authenticate using browserid (https://browserid.org/)
BrowserID is a project by Mozilla Labs (http://mozillalabs.com/) BrowserID is a project by Mozilla Labs (http://mozillalabs.com/)
to Know how browserid works please visit http://identity.mozilla.com/post/7616727542/introducing-browserid-a-better-way-to-sign-in to Know how browserid works please visit http://identity.mozilla.com/post/7616727542/introducing-browserid-a-better-way-to-sign-in
bottom line BrowserID provides a free, secure, de-centralized, easy to use(for users and developers) login solution. bottom line BrowserID provides a free, secure, de-centralized, easy to use(for users and developers) login solution.
You can use any email id as your login id. Browserid just verifys the email id and lets you login with that id. You can use any email id as your login id. Browserid just verifys the email id and lets you login with that id.
credits for the doPost jquery function - itsadok (http://stackoverflow.com/users/7581/itsadok) credits for the doPost jquery function - itsadok (http://stackoverflow.com/users/7581/itsadok)
""" """
import time import time
from gluon import * from gluon import *
@@ -32,7 +32,7 @@ class BrowserID(object):
audience = "http://127.0.0.1:8000" audience = "http://127.0.0.1:8000"
assertion_post_url = "http://127.0.0.1:8000/%s/default/user/login" % request.application) assertion_post_url = "http://127.0.0.1:8000/%s/default/user/login" % request.application)
""" """
def __init__(self, def __init__(self,
request, request,
audience = "", audience = "",
@@ -45,7 +45,7 @@ class BrowserID(object):
crypto_js = "https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js", crypto_js = "https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js",
on_login_failure = None, on_login_failure = None,
): ):
self.request = request self.request = request
self.audience = audience self.audience = audience
self.assertion_post_url = assertion_post_url self.assertion_post_url = assertion_post_url
@@ -64,13 +64,13 @@ class BrowserID(object):
def get_user(self): def get_user(self):
request = self.request request = self.request
if request.vars.assertion: if request.vars.assertion:
audience = self.audience audience = self.audience
issuer = self.issuer issuer = self.issuer
assertion = XML(request.vars.assertion,sanitize=True) assertion = XML(request.vars.assertion,sanitize=True)
verify_data = {'assertion':assertion,'audience':audience} verify_data = {'assertion':assertion,'audience':audience}
auth_info_json = fetch(self.verify_url,data=verify_data) auth_info_json = fetch(self.verify_url,data=verify_data)
j = json.loads(auth_info_json) j = json.loads(auth_info_json)
epoch_time = int(time.time()*1000) # we need 13 digit epoch time epoch_time = int(time.time()*1000) # we need 13 digit epoch time
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time: if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
return dict(email = j['email']) return dict(email = j['email'])
@@ -88,3 +88,4 @@ class BrowserID(object):
A(IMG(_src=self.browserid_button,_alt=self.prompt),_href="#",_onclick=onclick,_class="browserid",_title="Login With BrowserID"), A(IMG(_src=self.browserid_button,_alt=self.prompt),_href="#",_onclick=onclick,_class="browserid",_title="Login With BrowserID"),
SCRIPT(self.asertion_js)) SCRIPT(self.asertion_js))
return form return form
+1
View File
@@ -136,3 +136,4 @@ class CasAuth( object ):
import urllib import urllib
redirect("%s?service=%s" % (self.cas_logout_url,self.cas_my_url)) redirect("%s?service=%s" % (self.cas_logout_url,self.cas_my_url))
@@ -12,7 +12,7 @@
import os import os
import re import re
import urllib import urllib
from dropbox import client, rest, session from dropbox import client, rest, session
from gluon import * from gluon import *
from gluon.tools import fetch from gluon.tools import fetch
from gluon.storage import Storage from gluon.storage import Storage
@@ -50,15 +50,15 @@ class DropboxAccount(object):
self.sess = session.DropboxSession( self.sess = session.DropboxSession(
self.key,self.secret,self.access_type) self.key,self.secret,self.access_type)
def get_user(self): def get_user(self):
request = self.request request = self.request
token = current.session.dropbox_token token = current.session.dropbox_token
try: try:
access_token = self.sess.obtain_access_token(token) access_token = self.sess.obtain_access_token(token)
except: except:
access_token = None access_token = None
if access_token: if access_token:
user = Storage() user = Storage()
self.client = client.DropboxClient(self.sess) self.client = client.DropboxClient(self.sess)
data = self.client.account_info() data = self.client.account_info()
@@ -68,7 +68,7 @@ class DropboxAccount(object):
last_name = display_name[-1], last_name = display_name[-1],
registration_id = data.get('uid',None)) registration_id = data.get('uid',None))
if not user['registration_id'] and self.on_login_failure: if not user['registration_id'] and self.on_login_failure:
redirect(self.on_login_failure) redirect(self.on_login_failure)
return user return user
return None return None
@@ -107,3 +107,4 @@ def use_dropbox(auth,filename='private/dropbox.key',**kwargs):
auth.settings.login_form = DropboxAccount( auth.settings.login_form = DropboxAccount(
request,key=key,secret=secret,access_type=access_type, request,key=key,secret=secret,access_type=access_type,
login_url = login_url,**kwargs) login_url = login_url,**kwargs)
@@ -43,3 +43,4 @@ def email_auth(server="smtp.gmail.com:587",
pass pass
return False return False
return email_auth_aux return email_auth_aux
@@ -86,7 +86,7 @@ class ExtendedLoginForm(object):
Otherwise it will render the normal login form combined with Otherwise it will render the normal login form combined with
alt_login_form.login_form. alt_login_form.login_form.
""" """
request = current.request request = current.request
args = request.args args = request.args
@@ -102,3 +102,4 @@ class ExtendedLoginForm(object):
form.components.append(self.alt_login_form.login_form()) form.components.append(self.alt_login_form.login_form())
return form return form
@@ -36,3 +36,4 @@ class GaeGoogleAccount(object):
return dict(nickname=user.nickname(), email=user.email(), return dict(nickname=user.nickname(), email=user.email(),
user_id=user.user_id(), source="google account") user_id=user.user_id(), source="google account")
+1
View File
@@ -634,3 +634,4 @@ def ldap_auth(server='ldap', port=None,
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
filterstr = filterstr[1:-1] # parens added again where used filterstr = filterstr[1:-1] # parens added again where used
return ldap_auth_aux return ldap_auth_aux
@@ -49,3 +49,4 @@ class LinkedInAccount(object):
username = profile.id) username = profile.id)
+1
View File
@@ -110,3 +110,4 @@ class Loginza(object):
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript")) SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
return form return form
+17 -16
View File
@@ -6,24 +6,24 @@ from gluon.dal import DAL
def motp_auth(db=DAL('sqlite://storage.sqlite'), def motp_auth(db=DAL('sqlite://storage.sqlite'),
time_offset=60): time_offset=60):
""" """
motp allows you to login with a one time password(OTP) generated on a motp client, motp allows you to login with a one time password(OTP) generated on a motp client,
motp clients are available for practically all platforms. motp clients are available for practically all platforms.
to know more about OTP visit http://en.wikipedia.org/wiki/One-time_password to know more about OTP visit http://en.wikipedia.org/wiki/One-time_password
to know more visit http://motp.sourceforge.net to know more visit http://motp.sourceforge.net
Written by Madhukar R Pai (madspai@gmail.com) Written by Madhukar R Pai (madspai@gmail.com)
License : MIT or GPL v2 License : MIT or GPL v2
thanks and credits to the web2py community thanks and credits to the web2py community
to use motp_auth: to use motp_auth:
motp_auth.py has to be located in gluon/contrib/login_methods/ folder motp_auth.py has to be located in gluon/contrib/login_methods/ folder
first auth_user has to have 2 extra fields - motp_secret and motp_pin first auth_user has to have 2 extra fields - motp_secret and motp_pin
for that define auth like shown below: for that define auth like shown below:
## after auth = Auth(db) ## after auth = Auth(db)
db.define_table( db.define_table(
auth.settings.table_user_name, auth.settings.table_user_name,
@@ -42,7 +42,7 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
writable=False, readable=False, default=''), writable=False, readable=False, default=''),
Field('registration_id', length=512, # required Field('registration_id', length=512, # required
writable=False, readable=False, default='')) writable=False, readable=False, default=''))
##validators ##validators
custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table
custom_auth_table.first_name.requires = \ custom_auth_table.first_name.requires = \
@@ -53,22 +53,22 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
custom_auth_table.email.requires = [ custom_auth_table.email.requires = [
IS_EMAIL(error_message=auth.messages.invalid_email), IS_EMAIL(error_message=auth.messages.invalid_email),
IS_NOT_IN_DB(db, custom_auth_table.email)] IS_NOT_IN_DB(db, custom_auth_table.email)]
auth.settings.table_user = custom_auth_table # tell auth to use custom_auth_table auth.settings.table_user = custom_auth_table # tell auth to use custom_auth_table
## before auth.define_tables() ## before auth.define_tables()
##after that: ##after that:
from gluon.contrib.login_methods.motp_auth import motp_auth from gluon.contrib.login_methods.motp_auth import motp_auth
auth.settings.login_methods.append(motp_auth(db=db)) auth.settings.login_methods.append(motp_auth(db=db))
##Instructions for using MOTP ##Instructions for using MOTP
- after configuring motp for web2py, Install a MOTP client on your phone (android,IOS, java, windows phone, etc) - after configuring motp for web2py, Install a MOTP client on your phone (android,IOS, java, windows phone, etc)
- initialize the motp client (to reset a motp secret type in #**#), - initialize the motp client (to reset a motp secret type in #**#),
During user creation enter the secret generated during initialization into the motp_secret field in auth_user and During user creation enter the secret generated during initialization into the motp_secret field in auth_user and
similarly enter a pre-decided pin into the motp_pin similarly enter a pre-decided pin into the motp_pin
- done.. to login, just generate a fresh OTP by typing in the pin and use the OTP as password - done.. to login, just generate a fresh OTP by typing in the pin and use the OTP as password
###To Dos### ###To Dos###
- both motp_secret and pin are stored in plain text! need to have some way of encrypting - both motp_secret and pin are stored in plain text! need to have some way of encrypting
- web2py stores the password in db on successful login (should not happen) - web2py stores the password in db on successful login (should not happen)
@@ -85,7 +85,7 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
hash = md5(to_hash).hexdigest()[:6] hash = md5(to_hash).hexdigest()[:6]
if otp == hash: return True if otp == hash: return True
return False return False
def motp_auth_aux(email, def motp_auth_aux(email,
password, password,
db=db, db=db,
@@ -102,3 +102,4 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
else: return False else: return False
return False return False
return motp_auth_aux return motp_auth_aux
@@ -188,3 +188,4 @@ class OAuthAccount(object):
@@ -66,7 +66,7 @@ class OAuthAccount(object):
if not self.accessToken(): if not self.accessToken():
return None return None
if not self.graph: if not self.graph:
self.graph = GraphAPI((self.accessToken())) self.graph = GraphAPI((self.accessToken()))
@@ -117,7 +117,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
""" """
Build the url opener for managing HTTP Basic Athentication Build the url opener for managing HTTP Basic Athentication
""" """
# Create an OpenerDirector with support # Create an OpenerDirector with support
# for Basic HTTP Authentication... # for Basic HTTP Authentication...
auth_handler = urllib2.HTTPBasicAuthHandler() auth_handler = urllib2.HTTPBasicAuthHandler()
@@ -172,7 +172,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
if current.session.token.has_key('expires_in'): if current.session.token.has_key('expires_in'):
exps = 'expires_in' exps = 'expires_in'
elif current.session.token.has_key('expires'): elif current.session.token.has_key('expires'):
exps = 'expires' exps = 'expires'
else: else:
exps = None exps = None
current.session.token['expires'] = exps and \ current.session.token['expires'] = exps and \
@@ -185,7 +185,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
current.session.token = None current.session.token = None
return None return None
def __init__(self, g=None, def __init__(self, g=None,
client_id=None, client_secret=None, client_id=None, client_secret=None,
auth_url=None, token_url=None, **args): auth_url=None, token_url=None, **args):
""" """
@@ -215,7 +215,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
def get_user(self): def get_user(self):
""" """
Override this method by sublcassing the class. Override this method by sublcassing the class.
""" """
if not current.session.token: return None if not current.session.token: return None
return dict(first_name = 'Pinco', return dict(first_name = 'Pinco',
@@ -274,3 +274,4 @@ server for requests. It can be used for the optional"scope" parameters for Face
return current.session.code return current.session.code
return None return None
@@ -630,3 +630,4 @@ class Web2pyStore(OpenIDStore):
return self.cleanupNonces(), self.cleanupAssociations() return self.cleanupNonces(), self.cleanupAssociations()
+1
View File
@@ -20,3 +20,4 @@ def pam_auth():
return pam_auth_aux return pam_auth_aux
+4 -3
View File
@@ -53,12 +53,12 @@ class RPXAccount(object):
self.prompt = prompt self.prompt = prompt
self.on_login_failure = on_login_failure self.on_login_failure = on_login_failure
self.mappings = Storage() self.mappings = Storage()
dn = {'givenName':'','familyName':''} dn = {'givenName':'','familyName':''}
self.mappings.Facebook = lambda profile, dn=dn:\ self.mappings.Facebook = lambda profile, dn=dn:\
dict(registration_id = profile.get("identifier",""), dict(registration_id = profile.get("identifier",""),
username = profile.get("preferredUsername",""), username = profile.get("preferredUsername",""),
email = profile.get("email",""), email = profile.get("email",""),
first_name = profile.get("name",dn).get("givenName",""), first_name = profile.get("name",dn).get("givenName",""),
last_name = profile.get("name",dn).get("familyName","")) last_name = profile.get("name",dn).get("familyName",""))
self.mappings.Google = lambda profile, dn=dn:\ self.mappings.Google = lambda profile, dn=dn:\
@@ -120,8 +120,9 @@ def use_janrain(auth,filename='private/janrain.key',**kwargs):
request = current.request request = current.request
domain,key = open(path,'r').read().strip().split(':') domain,key = open(path,'r').read().strip().split(':')
host = current.request.env.http_host host = current.request.env.http_host
url = '%s' % URL(r=request, c='default', f='user', args=['login'], scheme=True) url = URL('default', 'user', args='login', scheme=True)
auth.settings.actions_disabled = \ auth.settings.actions_disabled = \
['register','change_password','request_reset_password'] ['register','change_password','request_reset_password']
auth.settings.login_form = RPXAccount( auth.settings.login_form = RPXAccount(
request, api_key=key,domain=domain, url = url,**kwargs) request, api_key=key,domain=domain, url = url,**kwargs)
+3 -2
View File
@@ -53,7 +53,7 @@ class X509Auth(object):
# cn = self.subject.cn # cn = self.subject.cn
self.subject = Storage(filter(None, self.subject = Storage(filter(None,
map(lambda x: map(lambda x:
(x,map(lambda y: (x,map(lambda y:
y.get_data().as_text(), y.get_data().as_text(),
subject.get_entries_by_nid(subject.nid[x]))), subject.get_entries_by_nid(subject.nid[x]))),
subject.nid.keys()))) subject.nid.keys())))
@@ -63,7 +63,7 @@ class X509Auth(object):
def login_form(self, **args): def login_form(self, **args):
raise HTTP(403,'Login not allowed. No valid x509 crentials') raise HTTP(403,'Login not allowed. No valid x509 crentials')
def login_url(self, next="/"): def login_url(self, next="/"):
raise HTTP(403,'Login not allowed. No valid x509 crentials') raise HTTP(403,'Login not allowed. No valid x509 crentials')
@@ -102,3 +102,4 @@ class X509Auth(object):
return profile return profile
+8 -8
View File
@@ -523,7 +523,7 @@ regex_list=re.compile('^(?:(#{1,6}|\.+ |\++ |\++\. |\-+ |\-+\. )\s*)?(.*)$')
regex_bq_headline=re.compile('^(?:(\.+|\++|\-+)(\.)?\s+)?(-{3}-*)$') regex_bq_headline=re.compile('^(?:(\.+|\++|\-+)(\.)?\s+)?(-{3}-*)$')
regex_tq=re.compile('^(-{3}-*)(?::(?P<c>[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P<p>[a-zA-Z][_a-zA-Z\-\d]*)\])?)?$') regex_tq=re.compile('^(-{3}-*)(?::(?P<c>[a-zA-Z][_a-zA-Z\-\d]*)(?:\[(?P<p>[a-zA-Z][_a-zA-Z\-\d]*)\])?)?$')
regex_proto = re.compile(r'(?<!["\w>/=])(?P<p>\w+):(?P<k>\w+://[\w\d\-+=?%&/:.]+)', re.M) regex_proto = re.compile(r'(?<!["\w>/=])(?P<p>\w+):(?P<k>\w+://[\w\d\-+=?%&/:.]+)', re.M)
regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[\w\d\-+_=?%&/:.]+)',re.M) regex_auto = re.compile(r'(?<!["\w>/=])(?P<k>\w+://[^\s\'\"\]\}\)]+)',re.M)
regex_link=re.compile(r'('+LINK+r')|\[\[(?P<s>.+?)\]\]') regex_link=re.compile(r'('+LINK+r')|\[\[(?P<s>.+?)\]\]')
regex_link_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+\[(?P<a>.+?)\])?(?:\s+(?P<k>\S+))?(?:\s+(?P<p>popup))?\s*$') regex_link_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+\[(?P<a>.+?)\])?(?:\s+(?P<k>\S+))?(?:\s+(?P<p>popup))?\s*$')
regex_media_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+\[(?P<a>.+?)\])?(?:\s+(?P<k>\S+))?\s+(?P<p>img|IMG|left|right|center|video|audio)(?:\s+(?P<w>\d+px))?\s*$') regex_media_level2=re.compile(r'^(?P<t>\S.*?)?(?:\s+\[(?P<a>.+?)\])?(?:\s+(?P<k>\S+))?\s+(?P<p>img|IMG|left|right|center|video|audio)(?:\s+(?P<w>\d+px))?\s*$')
@@ -598,7 +598,7 @@ def render(text,
- class_prefix is a prefix for ALL classes in markmin text. E.g. if class_prefix='my_' - class_prefix is a prefix for ALL classes in markmin text. E.g. if class_prefix='my_'
then for ``test``:cls class will be changed to "my_cls" (default value is '') then for ``test``:cls class will be changed to "my_cls" (default value is '')
- id_prefix is prefix for ALL ids in markmin text (default value is 'markmin_'). E.g.: - id_prefix is prefix for ALL ids in markmin text (default value is 'markmin_'). E.g.:
-- [[id]] will be converted to <a name="markmin_id"></a> -- [[id]] will be converted to <div class="anchor" id="markmin_id"></div>
-- [[link #id]] will be converted to <a href="#markmin_id">link</a> -- [[link #id]] will be converted to <a href="#markmin_id">link</a>
-- ``test``:cls[id] will be converted to <code class="cls" id="markmin_id">test</code> -- ``test``:cls[id] will be converted to <code class="cls" id="markmin_id">test</code>
@@ -729,19 +729,19 @@ def render(text,
'<p>[[probe]]</p>' '<p>[[probe]]</p>'
>>> render(r"\\\\[[probe]]") >>> render(r"\\\\[[probe]]")
'<p>\\\\<a name="markmin_probe"></a></p>' '<p>\\\\<div class="anchor" id="markmin_probe"></div></p>'
>>> render(r"\\\\\\[[probe]]") >>> render(r"\\\\\\[[probe]]")
'<p>\\\\[[probe]]</p>' '<p>\\\\[[probe]]</p>'
>>> render(r"\\\\\\\\[[probe]]") >>> render(r"\\\\\\\\[[probe]]")
'<p>\\\\\\\\<a name="markmin_probe"></a></p>' '<p>\\\\\\\\<div class="anchor" id="markmin_probe"></div></p>'
>>> render(r"\\\\\\\\\[[probe]]") >>> render(r"\\\\\\\\\[[probe]]")
'<p>\\\\\\\\[[probe]]</p>' '<p>\\\\\\\\[[probe]]</p>'
>>> render(r"\\\\\\\\\\\[[probe]]") >>> render(r"\\\\\\\\\\\[[probe]]")
'<p>\\\\\\\\\\\\<a name="markmin_probe"></a></p>' '<p>\\\\\\\\\\\\<div class="anchor" id="markmin_probe"></div></p>'
>>> render("``[[ [\\[[probe\]\\]] URL\\[x\\]]]``:red[dummy_params]") >>> render("``[[ [\\[[probe\]\\]] URL\\[x\\]]]``:red[dummy_params]")
'<span style="color: red"><a href="URL[x]" title="[[probe]]">URL[x]</a></span>' '<span style="color: red"><a href="URL[x]" title="[[probe]]">URL[x]</a></span>'
@@ -789,7 +789,7 @@ def render(text,
'<p><strong>test 1</strong></p>' '<p><strong>test 1</strong></p>'
>>> render('[[id1 [span **messag** in ''markmin''] ]] ... [[**link** to id [link\\\'s title] #mark1]]') >>> render('[[id1 [span **messag** in ''markmin''] ]] ... [[**link** to id [link\\\'s title] #mark1]]')
'<p><a name="markmin_id1">span <strong>messag</strong> in markmin</a> ... <a href="#markmin_mark1" title="link\\\'s title"><strong>link</strong> to id</a></p>' '<p><div class="anchor" id="markmin_id1">span <strong>messag</strong> in markmin</div> ... <a href="#markmin_mark1" title="link\\\'s title"><strong>link</strong> to id</a></p>'
""" """
if autolinks=="default": autolinks = autolinks_simple if autolinks=="default": autolinks = autolinks_simple
@@ -801,7 +801,7 @@ def render(text,
# this is experimental @{function/args} # this is experimental @{function/args}
# turns into a digitally signed URL # turns into a digitally signed URL
def u1(match,URL=URL): def u1(match,URL=URL):
a,c,f,args = match.group('a','c','f','args') a,c,f,args = match.group('a','c','f','args')
return URL(a=a or None,c=c or None,f = f or None, return URL(a=a or None,c=c or None,f = f or None,
args=args.split('/'), scheme=True, host=True) args=args.split('/'), scheme=True, host=True)
text = regex_URL.sub(u1,text) text = regex_URL.sub(u1,text)
@@ -1200,7 +1200,7 @@ def render(text,
protolinks, class_prefix, id_prefix) if t else k protolinks, class_prefix, id_prefix) if t else k
return '<a href="%(k)s"%(title)s%(target)s>%(t)s</a>' \ return '<a href="%(k)s"%(title)s%(target)s>%(t)s</a>' \
% dict(k=k, title=title, target=target, t=t) % dict(k=k, title=title, target=target, t=t)
return '<a name="%s">%s</a>' % (escape(id_prefix+t), return '<div class="anchor" id="%s">%s</div>' % (escape(id_prefix+t),
render(a, {},{},'br', URL, render(a, {},{},'br', URL,
environment, latex, autolinks, environment, latex, autolinks,
protolinks, class_prefix, id_prefix)) protolinks, class_prefix, id_prefix))
+1
View File
@@ -911,3 +911,4 @@ if __name__ == '__main__':
+1
View File
@@ -127,3 +127,4 @@ if __name__ == "__main__":
+1
View File
@@ -129,3 +129,4 @@ def test():
if __name__ == '__main__': if __name__ == '__main__':
test() test()
+1
View File
@@ -171,3 +171,4 @@ if __name__ == '__main__':
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"FPDF for python (a.k.a. pyfpdf)"
# Read more about this http://code.google.com/p/pyfpdf
# Please note that new package name is fpdf (to avoid some naming conflicts)
# import fpdf into pyfpdf for backward compatibility (prior web2py 2.0):
from fpdf import *
# import warnings
# warnings.warn("pyfpdf package name is deprecated, please use fpdf instead")
-1
View File
@@ -1 +0,0 @@
Read more about this http://code.google.com/p/pyfpdf
-5
View File
@@ -1,5 +0,0 @@
from fpdf import FPDF
from html import HTMLMixin
from template import Template
-736
View File
@@ -1,736 +0,0 @@
#!/usr/bin/python
# -*- coding: latin-1 -*-
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
"Visual Template designer for PyFPDF (using wxPython OGL library)"
__author__ = "Mariano Reingart <reingart@gmail.com>"
__copyright__ = "Copyright (C) 2011 Mariano Reingart"
__license__ = "GPL 3.0"
__version__ = "1.01a"
# Based on:
# * pySjetch.py wxPython sample application
# * OGL.py and other wxPython demo modules
import os, sys
import wx
import wx.lib.ogl as ogl
from wx.lib.wordwrap import wordwrap
DEBUG = True
class CustomDialog(wx.Dialog):
"A dinamyc dialog to ask user about arbitrary fields"
def __init__(
self, parent, ID, title, size=wx.DefaultSize, pos=wx.DefaultPosition,
style=wx.DEFAULT_DIALOG_STYLE, fields=None, data=None,
):
wx.Dialog.__init__ (self, parent, ID, title, pos, size, style)
sizer = wx.BoxSizer(wx.VERTICAL)
self.textctrls = {}
for field in fields:
box = wx.BoxSizer(wx.HORIZONTAL)
label = wx.StaticText(self, -1, field)
label.SetHelpText("This is the help text for the label")
box.Add(label, 1, wx.ALIGN_CENTRE|wx.ALL, 5)
text = wx.TextCtrl(self, -1, "", size=(80,-1))
text.SetHelpText("Here's some help text for field #1")
if field in data:
text.SetValue(repr(data[field]))
box.Add(text, 1, wx.ALIGN_CENTRE|wx.ALL, 1)
sizer.Add(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 1)
self.textctrls[field] = text
line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL)
sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5)
btnsizer = wx.StdDialogButtonSizer()
btn = wx.Button(self, wx.ID_OK)
btn.SetHelpText("The OK button completes the dialog")
btn.SetDefault()
btnsizer.AddButton(btn)
btn = wx.Button(self, wx.ID_CANCEL)
btn.SetHelpText("The Cancel button cancels the dialog. (Cool, huh?)")
btnsizer.AddButton(btn)
btnsizer.Realize()
sizer.Add(btnsizer, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
self.SetSizer(sizer)
sizer.Fit(self)
@classmethod
def do_input(Class, parent, title, fields, data):
dlg = Class(parent, -1, title, size=(350, 200),
style=wx.DEFAULT_DIALOG_STYLE, # & ~wx.CLOSE_BOX,
fields=fields, data=data
)
dlg.CenterOnScreen()
while 1:
val = dlg.ShowModal()
if val == wx.ID_OK:
values = {}
for field in fields:
try:
values[field] = eval(dlg.textctrls[field].GetValue())
except Exception, e:
msg = wx.MessageDialog(parent, unicode(e),
"Error in field %s" % field,
wx.OK | wx.ICON_INFORMATION
)
msg.ShowModal()
msg.Destroy()
break
else:
return dict([(field, values[field]) for field in fields])
else:
return None
class MyEvtHandler(ogl.ShapeEvtHandler):
"Custom Event Handler for Shapes"
def __init__(self, callback):
ogl.ShapeEvtHandler.__init__(self)
self.callback = callback
def OnLeftClick(self, x, y, keys=0, attachment=0):
shape = self.GetShape()
canvas = shape.GetCanvas()
dc = wx.ClientDC(canvas)
canvas.PrepareDC(dc)
if shape.Selected() and keys & ogl.KEY_SHIFT:
shape.Select(False, dc)
#canvas.Redraw(dc)
canvas.Refresh(False)
else:
redraw = False
shapeList = canvas.GetDiagram().GetShapeList()
toUnselect = []
for s in shapeList:
if s.Selected() and not keys & ogl.KEY_SHIFT:
# If we unselect it now then some of the objects in
# shapeList will become invalid (the control points are
# shapes too!) and bad things will happen...
toUnselect.append(s)
shape.Select(True, dc)
if toUnselect:
for s in toUnselect:
s.Select(False, dc)
##canvas.Redraw(dc)
canvas.Refresh(False)
self.callback()
def OnEndDragLeft(self, x, y, keys=0, attachment=0):
shape = self.GetShape()
ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
if not shape.Selected():
self.OnLeftClick(x, y, keys, attachment)
self.callback()
def OnSizingEndDragLeft(self, pt, x, y, keys, attch):
ogl.ShapeEvtHandler.OnSizingEndDragLeft(self, pt, x, y, keys, attch)
self.callback()
def OnMovePost(self, dc, x, y, oldX, oldY, display):
shape = self.GetShape()
ogl.ShapeEvtHandler.OnMovePost(self, dc, x, y, oldX, oldY, display)
self.callback()
if "wxMac" in wx.PlatformInfo:
shape.GetCanvas().Refresh(False)
def OnLeftDoubleClick(self, x, y, keys = 0, attachment = 0):
self.callback("LeftDoubleClick")
def OnRightClick(self, *dontcare):
self.callback("RightClick")
class Element(object):
"Visual class that represent a placeholder in the template"
fields = ['name', 'type',
'x1', 'y1', 'x2', 'y2',
'font', 'size',
'bold', 'italic', 'underline',
'foreground', 'background',
'align', 'text', 'priority',]
def __init__(self, canvas=None, frame=None, zoom=5.0, static=False, **kwargs):
self.kwargs = kwargs
self.zoom = zoom
self.frame = frame
self.canvas = canvas
self.static = static
name = kwargs['name']
kwargs['type']
type = kwargs['type']
x, y, w, h = self.set_coordinates(kwargs['x1'], kwargs['y1'], kwargs['x2'], kwargs['y2'])
text = kwargs['text']
shape = self.shape = ogl.RectangleShape(w, h)
if not static:
shape.SetDraggable(True, True)
shape.SetX(x)
shape.SetY(y)
#if pen: shape.SetPen(pen)
#if brush: shape.SetBrush(brush)
shape.SetBrush(wx.TRANSPARENT_BRUSH)
if type not in ('L', 'B', 'BC'):
if not static:
pen = wx.LIGHT_GREY_PEN
else:
pen = wx.RED_PEN
shape.SetPen(pen)
self.text = kwargs['text']
evthandler = MyEvtHandler(self.evt_callback)
evthandler.SetShape(shape)
evthandler.SetPreviousHandler(shape.GetEventHandler())
shape.SetEventHandler(evthandler)
shape.SetCentreResize(False)
shape.SetMaintainAspectRatio(False)
canvas.AddShape( shape )
@classmethod
def new(Class, parent):
data = dict(name='some_name', type='T',
x1=5.0, y1=5.0, x2=100.0, y2=10.0,
font="Arial", size=12,
bold=False, italic=False, underline=False,
foreground= 0x000000, background=0xFFFFFF,
align="L", text="", priority=0)
data = CustomDialog.do_input(parent, 'New element', Class.fields, data)
if data:
return Class(canvas=parent.canvas, frame=parent, **data)
def edit(self):
"Edit current element (show a dialog box with all fields)"
data = self.kwargs.copy()
x1, y1, x2, y2 = self.get_coordinates()
data.update(dict(name=self.name,
text=self.text,
x1=x1, y1=y1, x2=x2, y2=y2,
))
data = CustomDialog.do_input(self.frame, 'Edit element', self.fields, data)
if data:
self.kwargs.update(data)
self.name = data['name']
self.text = data['text']
x,y, w, h = self.set_coordinates(data['x1'], data['y1'], data['x2'], data['y2'])
self.shape.SetX(x)
self.shape.SetY(y)
self.shape.SetWidth(w)
self.shape.SetHeight(h)
self.canvas.Refresh(False)
self.canvas.GetDiagram().ShowAll(1)
def edit_text(self):
"Allow text edition (i.e. for doubleclick)"
dlg = wx.TextEntryDialog(
self.frame, 'Text for %s' % self.name,
'Edit Text', '')
if self.text:
dlg.SetValue(self.text)
if dlg.ShowModal() == wx.ID_OK:
self.text = dlg.GetValue().encode("latin1")
dlg.Destroy()
def copy(self):
"Return an identical duplicate"
kwargs = self.as_dict()
element = Element(canvas=self.canvas, frame=self.frame, zoom=self.zoom, static=self.static, **kwargs)
return element
def remove(self):
"Erases visual shape from OGL canvas (element must be deleted manually)"
self.canvas.RemoveShape(self.shape)
def move(self, dx, dy):
"Change pdf coordinates (converting to wx internal values)"
x1, y1, x2, y2 = self.get_coordinates()
x1 += dx
x2 += dx
y1 += dy
y2 += dy
x, y, w, h = self.set_coordinates(x1, y1, x2, y2)
self.shape.SetX(x)
self.shape.SetY(y)
def evt_callback(self, evt_type=None):
"Event dispatcher"
if evt_type=="LeftDoubleClick":
self.edit_text()
if evt_type=='RightClick':
self.edit()
# update the status bar
x1, y1, x2, y2 = self.get_coordinates()
self.frame.SetStatusText("%s (%0.2f, %0.2f) - (%0.2f, %0.2f)" %
(self.name, x1, y1, x2, y2))
def get_coordinates(self):
"Convert from wx to pdf coordinates"
x, y = self.shape.GetX(), self.shape.GetY()
w, h = self.shape.GetBoundingBoxMax()
w -= 1
h -= 1
x1 = x/self.zoom - w/self.zoom/2.0
x2 = x/self.zoom + w/self.zoom/2.0
y1 = y/self.zoom - h/self.zoom/2.0
y2 = y/self.zoom + h/self.zoom/2.0
return x1, y1, x2, y2
def set_coordinates(self, x1, y1, x2, y2):
"Convert from pdf to wx coordinates"
x1 = x1 * self.zoom
x2 = x2 * self.zoom
y1 = y1 * self.zoom
y2 = y2 * self.zoom
# shapes seems to be centred, pdf coord not
w = max(x1, x2) - min(x1, x2) + 1
h = max(y1, y2) - min(y1, y2) + 1
x = (min(x1, x2) + w/2.0)
y = (min(y1, y2) + h/2.0)
return x, y, w, h
def text(self, txt=None):
if txt is not None:
if not isinstance(txt,str):
txt = str(txt)
self.kwargs['text'] = txt
self.shape.ClearText()
for line in txt.split('\n'):
self.shape.AddText(unicode(line, "latin1"))
self.canvas.Refresh(False)
return self.kwargs['text']
text = property(text, text)
def set_x(self, x):
self.shape.SetX(x)
self.canvas.Refresh(False)
self.evt_callback()
def set_y(self, y):
self.shape.SetY(y)
self.canvas.Refresh(False)
self.evt_callback()
def get_x(self):
return self.shape.GetX()
def get_y(self):
return self.shape.GetY()
x = property(get_x, set_x)
y = property(get_y, set_y)
def selected(self, sel=None):
if sel is not None:
print "Setting Select(%s)" % sel
self.shape.Select(sel)
return self.shape.Selected()
selected = property(selected, selected)
def name(self, name=None):
if name is not None:
self.kwargs['name'] = name
return self.kwargs['name']
name = property(name, name)
def __contains__(self, k):
"Implement in keyword for searchs"
return k in self.name.lower() or self.text and k in self.text.lower()
def as_dict(self):
"Return a dictionary representation, used by pyfpdf"
d = self.kwargs
x1, y1, x2, y2 = self.get_coordinates()
d.update({
'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2,
'text': self.text})
return d
class AppFrame(wx.Frame):
"OGL Designer main window"
title = "PyFPDF Template Designer (wx OGL)"
def __init__(self):
wx.Frame.__init__( self,
None, -1, self.title,
size=(640,480),
style=wx.DEFAULT_FRAME_STYLE )
sys.excepthook = self.except_hook
self.filename = ""
# Create a toolbar:
tsize = (16,16)
self.toolbar = self.CreateToolBar(wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT)
artBmp = wx.ArtProvider.GetBitmap
self.toolbar.AddSimpleTool(
wx.ID_NEW, artBmp(wx.ART_NEW, wx.ART_TOOLBAR, tsize), "New")
self.toolbar.AddSimpleTool(
wx.ID_OPEN, artBmp(wx.ART_FILE_OPEN, wx.ART_TOOLBAR, tsize), "Open")
self.toolbar.AddSimpleTool(
wx.ID_SAVE, artBmp(wx.ART_FILE_SAVE, wx.ART_TOOLBAR, tsize), "Save")
self.toolbar.AddSimpleTool(
wx.ID_SAVEAS, artBmp(wx.ART_FILE_SAVE_AS, wx.ART_TOOLBAR, tsize),
"Save As...")
#-------
self.toolbar.AddSeparator()
self.toolbar.AddSimpleTool(
wx.ID_UNDO, artBmp(wx.ART_UNDO, wx.ART_TOOLBAR, tsize), "Undo")
self.toolbar.AddSimpleTool(
wx.ID_REDO, artBmp(wx.ART_REDO, wx.ART_TOOLBAR, tsize), "Redo")
self.toolbar.AddSeparator()
#-------
self.toolbar.AddSimpleTool(
wx.ID_CUT, artBmp(wx.ART_CUT, wx.ART_TOOLBAR, tsize), "Remove")
self.toolbar.AddSimpleTool(
wx.ID_COPY, artBmp(wx.ART_COPY, wx.ART_TOOLBAR, tsize), "Duplicate")
self.toolbar.AddSimpleTool(
wx.ID_PASTE, artBmp(wx.ART_PASTE, wx.ART_TOOLBAR, tsize), "Insert")
self.toolbar.AddSeparator()
self.toolbar.AddSimpleTool(
wx.ID_FIND, artBmp(wx.ART_FIND, wx.ART_TOOLBAR, tsize), "Find")
self.toolbar.AddSeparator()
self.toolbar.AddSimpleTool(
wx.ID_PRINT, artBmp(wx.ART_PRINT, wx.ART_TOOLBAR, tsize), "Print")
self.toolbar.AddSimpleTool(
wx.ID_ABOUT, artBmp(wx.ART_HELP, wx.ART_TOOLBAR, tsize), "About")
self.toolbar.Realize()
self.toolbar.EnableTool(wx.ID_SAVEAS, False)
self.toolbar.EnableTool(wx.ID_UNDO, False)
self.toolbar.EnableTool(wx.ID_REDO, False)
menu_handlers = [
(wx.ID_NEW, self.do_new),
(wx.ID_OPEN, self.do_open),
(wx.ID_SAVE, self.do_save),
(wx.ID_PRINT, self.do_print),
(wx.ID_FIND, self.do_find),
(wx.ID_CUT, self.do_cut),
(wx.ID_COPY, self.do_copy),
(wx.ID_PASTE, self.do_paste),
(wx.ID_ABOUT, self.do_about),
]
for menu_id, handler in menu_handlers:
self.Bind(wx.EVT_MENU, handler, id = menu_id)
sizer = wx.BoxSizer(wx.VERTICAL)
# put stuff into sizer
self.CreateStatusBar()
canvas = self.canvas = ogl.ShapeCanvas( self )
maxWidth = 1500
maxHeight = 2000
canvas.SetScrollbars(20, 20, maxWidth/20, maxHeight/20)
sizer.Add( canvas, 1, wx.GROW )
canvas.SetBackgroundColour("WHITE") #
diagram = self.diagram = ogl.Diagram()
canvas.SetDiagram( diagram )
diagram.SetCanvas( canvas )
diagram.SetSnapToGrid( False )
# apply sizer
self.SetSizer(sizer)
self.SetAutoLayout(1)
self.Show(1)
self.Bind(wx.EVT_CHAR_HOOK, self.on_key_event)
self.elements = []
def on_key_event(self, event):
""" Respond to a keypress event.
We make the arrow keys move the selected object(s) by one pixel in
the given direction.
"""
step = 1
if event.ControlDown():
step = 20
if event.GetKeyCode() == wx.WXK_UP:
self.move_elements(0, -step)
elif event.GetKeyCode() == wx.WXK_DOWN:
self.move_elements(0, step)
elif event.GetKeyCode() == wx.WXK_LEFT:
self.move_elements(-step, 0)
elif event.GetKeyCode() == wx.WXK_RIGHT:
self.move_elements(step, 0)
elif event.GetKeyCode() == wx.WXK_DELETE:
self.do_cut()
else:
event.Skip()
def do_new(self, evt=None):
for element in self.elements:
element.remove()
self.elements = []
# draw paper size guides
for k, (w, h) in [('legal', (216, 356)), ('A4', (210, 297)), ('letter', (216, 279))]:
self.create_elements(
k, 'R', 0, 0, w, h,
size=70, foreground=0x808080, priority=-100,
canvas=self.canvas, frame=self, static=True)
self.diagram.ShowAll( 1 )
def do_open(self, evt):
dlg = wx.FileDialog(
self, message="Choose a file",
defaultDir=os.getcwd(),
defaultFile="invoice.csv",
wildcard="CSV Files (*.csv)|*.csv",
style=wx.OPEN
)
if dlg.ShowModal() == wx.ID_OK:
# This returns a Python list of files that were selected.
self.filename = dlg.GetPaths()[0]
dlg.Destroy()
self.SetTitle(self.filename + " - " + self.title)
self.do_new()
tmp = []
f = open(self.filename)
try:
filedata = f.readlines()
finally:
f.close()
for lno, linea in enumerate(filedata):
if DEBUG: print "processing line", lno, linea
args = []
for i,v in enumerate(linea.split(";")):
if not v.startswith("'"):
v = v.replace(",",".")
else:
v = v#.decode('latin1')
if v.strip()=='':
v = None
else:
v = eval(v.strip())
args.append(v)
tmp.append(args)
# sort by z-order (priority)
for args in sorted(tmp, key=lambda t: t[-1]):
if DEBUG: print args
self.create_elements(*args)
self.diagram.ShowAll( 1 ) #
return True
def do_save(self, evt, filename=None):
try:
from time import gmtime, strftime
ts = strftime("%Y%m%d%H%M%S", gmtime())
os.rename(self.filename, self.filename + ts + ".bak")
except Exception, e:
if DEBUG: print e
pass
def csv_repr(v, decimal_sep="."):
if isinstance(v, float):
return ("%0.2f" % v).replace(".", decimal_sep)
else:
return repr(v)
f = open(self.filename, "w")
try:
for element in sorted(self.elements, key=lambda e:e.name):
if element.static:
continue
d = element.as_dict()
l = [d['name'], d['type'],
d['x1'], d['y1'], d['x2'], d['y2'],
d['font'], d['size'],
d['bold'], d['italic'], d['underline'],
d['foreground'], d['background'],
d['align'], d['text'], d['priority'],
]
f.write(";".join([csv_repr(v) for v in l]))
f.write("\n")
finally:
f.close()
def do_print(self, evt):
# genero el renderizador con propiedades del PDF
from template import Template
t = Template(elements=[e.as_dict() for e in self.elements if not e.static])
t.add_page()
if not t['logo'] or not os.path.exists(t['logo']):
# put a default logo so it doesn't trow an exception
logo = os.path.join(os.path.dirname(__file__), 'tutorial','logo.png')
t.set('logo', logo)
try:
t.render(self.filename +".pdf")
except:
if DEBUG and False:
import pdb;
pdb.pm()
else:
raise
if sys.platform=="linux2":
os.system("evince ""%s""" % self.filename +".pdf")
else:
os.startfile(self.filename +".pdf")
def do_find(self, evt):
# busco nombre o texto
dlg = wx.TextEntryDialog(
self, 'Enter text to search for',
'Find Text', '')
if dlg.ShowModal() == wx.ID_OK:
txt = dlg.GetValue().encode("latin1").lower()
for element in self.elements:
if txt in element:
element.selected = True
print "Found:", element.name
self.canvas.Refresh(False)
dlg.Destroy()
def do_cut(self, evt=None):
"Delete selected elements"
new_elements = []
for element in self.elements:
if element.selected:
print "Erasing:", element.name
element.selected = False
self.canvas.Refresh(False)
element.remove()
else:
new_elements.append(element)
self.elements = new_elements
self.canvas.Refresh(False)
self.diagram.ShowAll( 1 )
def do_copy(self, evt):
"Duplicate selected elements"
fields = ['qty', 'dx', 'dy']
data = {'qty': 1, 'dx': 0.0, 'dy': 5.0}
data = CustomDialog.do_input(self, 'Copy elements', fields, data)
if data:
new_elements = []
for i in range(1, data['qty']+1):
for element in self.elements:
if element.selected:
print "Copying:", element.name
new_element = element.copy()
name = new_element.name
if len(name)>2 and name[-2:].isdigit():
new_element.name = name[:-2] + "%02d" % (int(name[-2:])+i)
else:
new_element.name = new_element.name + "_copy"
new_element.selected = False
new_element.move(data['dx']*i, data['dy']*i)
new_elements.append(new_element)
self.elements.extend(new_elements)
self.canvas.Refresh(False)
self.diagram.ShowAll( 1 )
def do_paste(self, evt):
"Insert new elements"
element = Element.new(self)
if element:
self.canvas.Refresh(False)
self.elements.append(element)
self.diagram.ShowAll( 1 )
def create_elements(self, name, type, x1, y1, x2, y2,
font="Arial", size=12,
bold=False, italic=False, underline=False,
foreground= 0x000000, background=0xFFFFFF,
align="L", text="", priority=0, canvas=None, frame=None, static=False,
**kwargs):
element = Element(name=name, type=type, x1=x1, y1=y1, x2=x2, y2=y2,
font=font, size=size,
bold=bold, italic=italic, underline=underline,
foreground= foreground, background=background,
align=align, text=text, priority=priority,
canvas=canvas or self.canvas, frame=frame or self,
static=static)
self.elements.append(element)
def move_elements(self, x, y):
for element in self.elements:
if element.selected:
print "moving", element.name, x, y
element.x = element.x + x
element.y = element.y + y
def do_about(self, evt):
info = wx.AboutDialogInfo()
info.Name = self.title
info.Version = __version__
info.Copyright = __copyright__
info.Description = (
"Visual Template designer for PyFPDF (using wxPython OGL library)\n"
"Input files are CSV format describing the layout, separated by ;\n"
"Use toolbar buttons to open, save, print (preview) your template, "
"and there are buttons to find, add, remove or duplicate elements.\n"
"Over an element, a double left click opens edit text dialog, "
"and a right click opens edit properties dialog. \n"
"Multiple element can be selected with shift left click. \n"
"Use arrow keys or drag-and-drop to move elements.\n"
"For further information see project webpage:"
)
info.WebSite = ("http://code.google.com/p/pyfpdf/wiki/Templates",
"pyfpdf Google Code Project")
info.Developers = [ __author__, ]
info.License = wordwrap(__license__, 500, wx.ClientDC(self))
# Then we call wx.AboutBox giving it that info object
wx.AboutBox(info)
def except_hook(self, type, value, trace):
import traceback
exc = traceback.format_exception(type, value, trace)
for e in exc: wx.LogError(e)
wx.LogError('Unhandled Error: %s: %s'%(str(type), str(value)))
app = wx.PySimpleApp()
ogl.OGLInitialize()
frame = AppFrame()
app.MainLoop()
app.Destroy()
+1
View File
@@ -964,3 +964,4 @@ if __name__ == '__main__':
+1
View File
@@ -167,3 +167,4 @@ class RedisClient(object):
+1
View File
@@ -592,3 +592,4 @@ if __name__ == '__main__':
+1
View File
@@ -270,3 +270,4 @@ if __name__=='__main__':
+1
View File
@@ -151,3 +151,4 @@ if __name__ == "__main__":
+1
View File
@@ -116,3 +116,4 @@ def sms_email(number,provider):
+1
View File
@@ -879,3 +879,4 @@ if __name__ == '__main__':
print s['c'].computed_value print s['c'].computed_value
+1
View File
@@ -66,3 +66,4 @@ if __name__=='__main__':
+1
View File
@@ -247,3 +247,4 @@ class TaskBarIcon:
+1
View File
@@ -95,3 +95,4 @@ if __name__=='__main__':
print(t.getReportText(orderByCost=False)) print(t.getReportText(orderByCost=False))
+1
View File
@@ -520,3 +520,4 @@ class mobilize(object):
+1
View File
@@ -329,3 +329,4 @@ class _Web2pyDateTrackerImporter(_Web2pyImporter, _DateTrackerImporter):
+83 -38
View File
@@ -174,7 +174,7 @@ import platform
CALLABLETYPES = (types.LambdaType, types.FunctionType, CALLABLETYPES = (types.LambdaType, types.FunctionType,
types.BuiltinFunctionType, types.BuiltinFunctionType,
types.MethodType, types.BuiltinMethodType) types.MethodType, types.BuiltinMethodType)
TABLE_ARGS = ('migrate','primarykey','fake_migrate','format','singular','plural','trigger_name','sequence_name','common_filter','polymodel','table_class') TABLE_ARGS = ('migrate','primarykey','fake_migrate','format','singular','plural','trigger_name','sequence_name','common_filter','polymodel','table_class','on_define')
################################################################################### ###################################################################################
# following checks allow the use of dal without web2py, as a standalone module # following checks allow the use of dal without web2py, as a standalone module
@@ -1317,7 +1317,7 @@ class BaseAdapter(ConnectionPool):
tablename,fieldname = item.split('.') tablename,fieldname = item.split('.')
new_fields.append(self.db[tablename][fieldname]) new_fields.append(self.db[tablename][fieldname])
else: else:
new_fields.append(Expression(self.db,item)) new_fields.append(Expression(self.db,lambda:item))
else: else:
new_fields.append(item) new_fields.append(item)
# ## if no fields specified take them all from the requested tables # ## if no fields specified take them all from the requested tables
@@ -2735,6 +2735,14 @@ class OracleAdapter(BaseAdapter):
self.execute('SELECT %s.currval FROM dual;' % sequence_name) self.execute('SELECT %s.currval FROM dual;' % sequence_name)
return int(self.cursor.fetchone()[0]) return int(self.cursor.fetchone()[0])
def parse_value(self, value, field_type, blob_decode=True):
if blob_decode and isinstance(value, cx_Oracle.LOB):
try:
value = value.read()
except cx_Oracle.ProgrammingError:
# After a subsequent fetch the LOB value is not valid anymore
pass
return BaseAdapter.parse_value(self, value, field_type, blob_decode)
class MSSQLAdapter(BaseAdapter): class MSSQLAdapter(BaseAdapter):
@@ -6281,11 +6289,9 @@ class Row(dict):
this is only used to store a Row this is only used to store a Row
""" """
def __getattr__(self, key): __setattr__ = dict.__setitem__
return self[key] __getattr__ = dict.__getitem__
__delattr__ = dict.__delitem__
def __setattr__(self, key, value):
self[key] = value
def __getitem__(self, key): def __getitem__(self, key):
key=str(key) key=str(key)
@@ -6299,8 +6305,10 @@ class Row(dict):
key = m.group(2) key = m.group(2)
return dict.__getitem__(self, key) return dict.__getitem__(self, key)
def __call__(self,key): __call__ = __getitem__
return self.__getitem__(key)
#def __call__(self,key):
# return self.__getitem__(key)
def __setitem__(self, key, value): def __setitem__(self, key, value):
dict.__setitem__(self, str(key), value) dict.__setitem__(self, str(key), value)
@@ -6924,7 +6932,7 @@ def index():
tablename, tablename,
*fields, *fields,
**args **args
): ):
if not isinstance(tablename,str): if not isinstance(tablename,str):
raise SyntaxError, "missing table name" raise SyntaxError, "missing table name"
elif tablename.startswith('_') or hasattr(self,tablename) or \ elif tablename.startswith('_') or hasattr(self,tablename) or \
@@ -6938,7 +6946,7 @@ def index():
invalid_args = [key for key in args if not key in TABLE_ARGS] invalid_args = [key for key in args if not key in TABLE_ARGS]
if invalid_args: if invalid_args:
raise SyntaxError, 'invalid table "%s" attributes: %s' \ raise SyntaxError, 'invalid table "%s" attributes: %s' \
% (tablename,invalid_args) % (tablename,invalid_args)
if self._lazy_tables and not tablename in self._LAZY_TABLES: if self._lazy_tables and not tablename in self._LAZY_TABLES:
self._LAZY_TABLES[tablename] = (tablename,fields,args) self._LAZY_TABLES[tablename] = (tablename,fields,args)
table = None table = None
@@ -6977,6 +6985,8 @@ def index():
sql_locker.release() sql_locker.release()
else: else:
table._dbt = None table._dbt = None
on_define = args.get('on_define',None)
if on_define: on_define(table)
return table return table
def __iter__(self): def __iter__(self):
@@ -7025,9 +7035,10 @@ def index():
thread.instances.remove(self._adapter) thread.instances.remove(self._adapter)
self._adapter.close() self._adapter.close()
def executesql(self, query, placeholders=None, as_dict=False): def executesql(self, query, placeholders=None, as_dict=False,
fields=None, colnames=None):
""" """
placeholders is optional and will always be None when using DAL. placeholders is optional and will always be None.
If using raw SQL with placeholders, placeholders may be If using raw SQL with placeholders, placeholders may be
a sequence of values to be substituted in a sequence of values to be substituted in
or, (if supported by the DB driver), a dictionary with keys or, (if supported by the DB driver), a dictionary with keys
@@ -7044,7 +7055,21 @@ def index():
[{field1: value1, field2: value2}, {field1: value1b, field2: value2b}] [{field1: value1, field2: value2}, {field1: value1b, field2: value2b}]
--bmeredyk Added 2012-08-24 "fields" optional argument. If not None, the
results cursor returned by the DB driver will be converted to a
DAL Rows object using the db._adapter.parse() method. This requires
specifying the "fields" argument as a list of DAL Field objects
that match the fields returned from the DB. The Field objects should
be part of one or more Table objects defined on the DAL object.
The "fields" list can include one or more DAL Table objects in addition
to or instead of including Field objects, or it can be just a single
table (not in a list). In that case, the Field objects will be
extracted from the table(s).
The field names will be extracted from the Field objects, or optionally,
a list of field names can be provided (in tablename.fieldname format)
via the "colnames" argument. Note, the fields and colnames must be in
the same order as the fields in the results cursor returned from the DB.
""" """
if placeholders: if placeholders:
self._adapter.execute(query, placeholders) self._adapter.execute(query, placeholders)
@@ -7064,11 +7089,22 @@ def index():
# convert the list for each row into a dictionary so it's # convert the list for each row into a dictionary so it's
# easier to work with. row['field_name'] rather than row[0] # easier to work with. row['field_name'] rather than row[0]
return [dict(zip(fields,row)) for row in data] return [dict(zip(fields,row)) for row in data]
# see if any results returned from database data = self._adapter.cursor.fetchall()
try: if fields:
return self._adapter.cursor.fetchall() if not isinstance(fields, list):
except: fields = [fields]
return None extracted_fields = []
for field in fields:
if isinstance(field, Table):
extracted_fields.extend([f for f in field])
else:
extracted_fields.append(field)
if not colnames:
colnames = ['%s.%s' % (f.tablename, f.name)
for f in extracted_fields]
data = self._adapter.parse(
data, fields=extracted_fields, colnames=colnames)
return data
def _update_referenced_by(self, other): def _update_referenced_by(self, other):
for tablename in self.tables: for tablename in self.tables:
@@ -7462,15 +7498,15 @@ class Table(dict):
'value must be a dictionary: %s' % value 'value must be a dictionary: %s' % value
dict.__setitem__(self, str(key), value) dict.__setitem__(self, str(key), value)
def __getattr__(self, key): __getattr__ = __getitem__
return self[key]
def __delitem__(self, key): def __delitem__(self, key):
if isinstance(key, dict): if isinstance(key, dict):
query = self._build_query(key) query = self._build_query(key)
if not self._db(query).delete(): if not self._db(query).delete():
raise SyntaxError, 'No such record: %s' % key raise SyntaxError, 'No such record: %s' % key
elif not str(key).isdigit() or not self._db(self._id == key).delete(): elif not str(key).isdigit() or \
not self._db(self._id == key).delete():
raise SyntaxError, 'No such record: %s' % key raise SyntaxError, 'No such record: %s' % key
def __setattr__(self, key, value): def __setattr__(self, key, value):
@@ -7499,33 +7535,38 @@ class Table(dict):
return self._db._adapter.drop(self,mode) return self._db._adapter.drop(self,mode)
def _listify(self,fields,update=False): def _listify(self,fields,update=False):
new_fields = [] new_fields = {} # format: new_fields[name] = (field,value)
new_fields_names = [] # store all fields passed as input in new_fields
for name in fields: for name in fields:
if not name in self.fields: if not name in self.fields:
if name != 'id': if name != 'id':
raise SyntaxError, 'Field %s does not belong to the table' % name raise SyntaxError, \
'Field %s does not belong to the table' % name
else: else:
field = self[name] field = self[name]
value = fields[name] value = fields[name]
if field.filter_in: value = field.filter_in(value) if field.filter_in:
new_fields.append((field,value)) value = field.filter_in(value)
new_fields_names.append(name) new_fields[name] = (field,value)
for ofield in self: # check all fields that should be in the self table
if not ofield.name in new_fields_names:
if not update and not ofield.default is None:
new_fields.append((ofield,ofield.default))
elif update and not ofield.update is None:
new_fields.append((ofield,ofield.update))
for ofield in self: for ofield in self:
name = ofield.name
# if field is supposed to be computed, compute it!
if ofield.compute: if ofield.compute:
try: try:
new_fields.append((ofield,ofield.compute(Row(fields)))) new_fields[name] = (ofield,ofield.compute(Row(fields)))
except KeyError: except KeyError:
pass pass
if not update and ofield.required and not ofield.name in new_fields_names: # if field is required, check its default value
raise SyntaxError,'Table: missing required field: %s' % ofield.name elif not name in new_fields:
return new_fields if not update and not ofield.default is None:
new_fields[name] = (ofield,ofield.default)
elif update and not ofield.update is None:
new_fields[name] = (ofield,ofield.update)
# error if field if required, record to be create and field missing
if not update and ofield.required and not name in new_fields:
raise SyntaxError, 'Table: missing required field: %s' % name
return new_fields.values()
def _attempt_upload(self, fields): def _attempt_upload(self, fields):
for field in self: for field in self:
@@ -8149,6 +8190,9 @@ class Field(Expression):
self.label = label if label!=None else fieldname.replace('_',' ').title() self.label = label if label!=None else fieldname.replace('_',' ').title()
self.requires = requires if requires!=None else [] self.requires = requires if requires!=None else []
def set_attributes(self,*args,**attributes):
self.__dict__.update(*args,**attributes)
def clone(self,point_self_references_to=False,**args): def clone(self,point_self_references_to=False,**args):
field = copy.copy(self) field = copy.copy(self)
if point_self_references_to and \ if point_self_references_to and \
@@ -9109,3 +9153,4 @@ if __name__ == '__main__':
+1
View File
@@ -192,3 +192,4 @@ gluon.main.global_settings.debugging = True
+1
View File
@@ -78,3 +78,4 @@ def decoder(buffer):
+1
View File
@@ -397,3 +397,4 @@ def abspath(*relpath, **base):
return os.path.join(global_settings.applications_parent, path) return os.path.join(global_settings.applications_parent, path)
+2 -1
View File
@@ -101,7 +101,7 @@ class Request(Storage):
self.is_https = False self.is_https = False
self.is_local = False self.is_local = False
self.global_settings = settings.global_settings self.global_settings = settings.global_settings
def compute_uuid(self): def compute_uuid(self):
self.uuid = '%s/%s.%s.%s' % ( self.uuid = '%s/%s.%s.%s' % (
self.application, self.application,
@@ -691,3 +691,4 @@ class Session(Storage):
+1
View File
@@ -348,3 +348,4 @@ if __name__ == '__main__':
+96 -29
View File
@@ -484,6 +484,19 @@ class XmlComponent(object):
components += [other] components += [other]
return CAT(*components) return CAT(*components)
def add_class(self, name):
""" add a class to _class attribute """
classes = set(self['_class'].split())|set(name.split())
self['_class'] = ' '.join(classes) if classes else None
return self
def remove_class(self, name):
""" remove a class from _class attribute """
classes = set(self['_class'].split())-set(name.split())
self['_class'] = ' '.join(classes) if classes else None
return self
class XML(XmlComponent): class XML(XmlComponent):
""" """
use it to wrap a string that contains XML/HTML so that it will not be use it to wrap a string that contains XML/HTML so that it will not be
@@ -949,6 +962,48 @@ class DIV(XmlComponent):
>>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled' >>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled'
>>> a.xml() >>> a.xml()
'<form action="" enctype="multipart/form-data" method="post"><input disabled="disabled" type="text" /><select disabled="disabled"><option value="0">0</option></select><textarea cols="40" disabled="disabled" rows="10"></textarea></form>' '<form action="" enctype="multipart/form-data" method="post"><input disabled="disabled" type="text" /><select disabled="disabled"><option value="0">0</option></select><textarea cols="40" disabled="disabled" rows="10"></textarea></form>'
Elements that are matched can also be replaced or removed by specifying
a "replace" argument (note, a list of the original matching elements
is still returned as usual).
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=P('x', _class='xyz'))
>>> print a
<div><div><p class="xyz">x</p><div><p class="xyz">x</p><p class="xyz">x</p></div></div></div>
"replace" can be a callable, which will be passed the original element and
should return a new element to replace it.
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz'))
>>> print a
<div><div><p class="xyz">x</p><div><p class="xyz">y</p><p class="xyz">z</p></div></div></div>
If replace=None, matching elements will be removed completely.
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span', find='y', replace=None)
>>> print a
<div><div><span class="abc">x</span><div><span class="abc">z</span></div></div></div>
If a "find_text" argument is specified, elements will be searched for text
components that match find_text, and any matching text components will be
replaced (find_text is ignored if "replace" is not also specified).
Like the "find" argument, "find_text" can be a string or a compiled regex.
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements(find_text=re.compile('x|y|z'), replace='hello')
>>> print a
<div><div><span class="abc">hello</span><div><span class="abc">hello</span><span class="abc">hello</span></div></div></div>
If other attributes are specified along with find_text, then only components
that match the specified attributes will be searched for find_text.
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='efg'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello')
>>> print a
<div><div><span class="abc">x</span><div><span class="efg">hello</span><span class="abc">z</span></div></div></div>
""" """
if len(args)==1: if len(args)==1:
args = [a.strip() for a in args[0].split(',')] args = [a.strip() for a in args[0].split(',')]
@@ -977,49 +1032,60 @@ class DIV(XmlComponent):
return self.elements(*args,**kargs) return self.elements(*args,**kargs)
# make a copy of the components # make a copy of the components
matches = [] matches = []
first_only = False
if kargs.has_key("first_only"):
first_only = kargs["first_only"]
del kargs["first_only"]
# check if the component has an attribute with the same # check if the component has an attribute with the same
# value as provided # value as provided
check = True check = True
tag = getattr(self,'tag').replace("/","") tag = getattr(self,'tag').replace('/', '')
if args and tag not in args: if args and tag not in args:
check = False check = False
for (key, value) in kargs.items(): for (key, value) in kargs.items():
if isinstance(value,(str,int)): if key not in ['first_only', 'replace', 'find_text']:
if self[key] != str(value): if isinstance(value, (str, int)):
if self[key] != str(value):
check = False
elif key in self.attributes:
if not value.search(str(self[key])):
check = False
else:
check = False check = False
elif key in self.attributes:
if not value.search(str(self[key])):
check = False
else:
check = False
if 'find' in kargs: if 'find' in kargs:
find = kargs['find'] find = kargs['find']
is_regex = not isinstance(find, (str, int))
for c in self.components: for c in self.components:
if isinstance(find,(str,int)): if (isinstance(c, str) and ((is_regex and find.search(c)) or
if isinstance(c,str) and str(find) in c: (str(find) in c))):
check = True check = True
else:
if isinstance(c,str) and find.search(c):
check = True
# if found, return the component # if found, return the component
if check: if check:
matches.append(self) matches.append(self)
if first_only:
return matches
# loop the copy
for c in self.components:
if isinstance(c, XmlComponent):
kargs['first_only'] = first_only
child_matches = c.elements( *args, **kargs )
if first_only and len(child_matches) != 0:
return child_matches
matches.extend( child_matches )
return matches
first_only = kargs.get('first_only', False)
replace = kargs.get('replace', False)
find_text = replace is not False and kargs.get('find_text', False)
is_regex = not isinstance(find_text, (str, int, bool))
find_components = not (check and first_only)
def replace_component(i):
if replace is None:
del self[i]
elif callable(replace):
self[i] = replace(self[i])
else:
self[i] = replace
# loop the components
if find_text or find_components:
for i, c in enumerate(self.components):
if check and find_text and isinstance(c, str) and \
((is_regex and find_text.search(c)) or (str(find_text) in c)):
replace_component(i)
if find_components and isinstance(c, XmlComponent):
child_matches = c.elements(*args, **kargs)
if len(child_matches):
if not find_text and replace is not False and child_matches[0] is c:
replace_component(i)
if first_only:
return child_matches
matches.extend(child_matches)
return matches
def element(self, *args, **kargs): def element(self, *args, **kargs):
""" """
@@ -2437,3 +2503,4 @@ if __name__ == '__main__':
+1
View File
@@ -142,3 +142,4 @@ def redirect(location, how=303, client_side=False):
+1
View File
@@ -113,3 +113,4 @@ for module in base_modules + contributed_modules:
+1
View File
@@ -939,3 +939,4 @@ if __name__ == '__main__':
doctest.testmod() doctest.testmod()
+1
View File
@@ -854,3 +854,4 @@ class HttpServer(object):
+1
View File
@@ -14,3 +14,4 @@ class MessageBoxHandler(logging.Handler):
msg = self.format(record) msg = self.format(record)
tkMessageBox.showinfo('info1', msg) tkMessageBox.showinfo('info1', msg)
+1
View File
@@ -32,3 +32,4 @@ regex_extend = re.compile(\
+1
View File
@@ -343,3 +343,4 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
+1
View File
@@ -151,3 +151,4 @@ if __name__=='__main__':

Some files were not shown because too many files have changed in this diff Show More