Compare commits

..

58 Commits

Author SHA1 Message Date
179edb659a Load all models when using --force_migrate (same behaviour as appadmin). 2019-08-13 18:04:30 +01:00
b4c2ee8304 Allow easily switching fake_migrate on to allow easier fixes of the database migrations 2019-07-25 17:15:19 +01:00
cb0c2e6edd Merge remote-tracking branch 'upstream/master' 2019-07-25 17:07:24 +01:00
mdipierro
2c364c2079 Merge pull request #2233 from rayluo/patch-1
Better https detection
2019-07-23 00:28:17 -07:00
Ray Luo
8faa5e2a82 Better https detection
Switch to request.is_https which provides better https detection
2019-07-22 23:41:23 -07:00
mdipierro
0557fe9c58 sync 2019-07-15 22:02:57 -07:00
mdipierro
5945edebfd Merge pull request #2230 from bjones1/master
Allow specification of server's encoding in webclient.
2019-07-15 22:00:33 -07:00
mdipierro
558afd886c Merge pull request #2229 from leonelcamara/patch-32
__lt__ and __gt__ for lazyT
2019-07-15 22:00:13 -07:00
mdipierro
1bc8ea6838 Merge pull request #2227 from nicozanf/bookfix
fix for py 3.6+ compatibility
2019-07-15 21:59:56 -07:00
mdipierro
24d970a943 Merge pull request #2223 from gonguinguen/patch-6
Added followlinks=True when calling listdir
2019-07-15 21:58:06 -07:00
mdipierro
e66dda8641 Merge pull request #2222 from gonguinguen/patch-5
Added followlinks argument to listdir
2019-07-15 21:57:36 -07:00
mdipierro
320a28d564 Merge pull request #2216 from web-py/patch-3
Update fa.py
2019-07-15 21:57:12 -07:00
Bryan A. Jones
9db1c6b0b0 Allow specification of server's encoding in webclient. 2019-07-12 10:23:53 -05:00
Leonel Câmara
f307fe7d56 __lt__ and __gt__ for lazyT
Fixes #2228
2019-07-10 12:25:40 +01:00
Nico Zanferrari
f845497479 fix for py 3.6+ compatibility
py 3.6+ gives errors and need more escapes - I've tested the patch on the full web2py book's latex output and it's backward compatible with 2.7. For technical details see https://bugs.python.org/issue28450 :
"Deprecated since version 3.5, will be removed in version 3.6: Unknown escapes consist of '\' and ASCII letter now raise a deprecation warning and will be forbidden in Python 3.6"
2019-07-07 00:55:39 +02:00
Dinis
f1f65275f7 Merge pull request #1 from web2py/master
Refresh master
2019-06-19 14:20:22 +01:00
Lisandro
2989beae02 Added followlinks=True when calling listdir
In order to solve the issue #2221, I've added followlinks=True when calling listdir() from functions compile_views(), compile_models() and compile_controllers()
2019-06-07 19:24:59 -03:00
Lisandro
2a7b16d61f Added followlinks argument to listdir
The followlinks argument is then used by the function when it calls os.walk(path...
Notice the "followlinks" argument of os.walk() is False by default, so this change won't make any difference. Every actual call to listdir() function won't need to be changed.
This change is needed to resolve issue #2221
2019-06-07 19:21:10 -03:00
mdipierro
14c1b3e400 fixed typo 2019-06-06 22:39:38 -07:00
mdipierro
8823cde350 simpler populate 2019-06-06 21:52:20 -07:00
mdipierro
3e09add351 submit->Submit, thanks Paolo Pastori 2019-06-06 21:28:56 -07:00
mdipierro
7385904f97 --force_migrate and --cron_threads, thanks Paolo Pastori 2019-06-06 21:23:31 -07:00
web-py
c82f0ba619 Update fa.py
edit plural-forms for persian agian
2019-06-02 16:07:58 +04:30
mdipierro
409a499dd0 Merge pull request #2215 from yamandu/pt_br_translation
Better Brazilian Portuguese translation
2019-06-01 11:37:17 -07:00
mdipierro
9d52f5498e Merge pull request #2214 from yamandu/issue2179
Issue2179
2019-06-01 11:36:52 -07:00
mdipierro
0fe9d0aa34 Merge pull request #2213 from web-py/patch-2
create fa.py
2019-06-01 11:36:06 -07:00
mdipierro
89a4da0594 better scheduler logic to prevent deadlocks, thanks Paolo Pastori 2019-06-01 11:34:58 -07:00
mdipierro
f2e95d1bb6 no need to create folders in shell 2019-06-01 11:31:27 -07:00
Carlos Costa
273ebb9a70 Better Brazilian Portuguese translation
Including SQLFORM.grid, auth and other strings.
2019-05-31 12:04:32 -03:00
Carlos Costa
1bfffc4f12 issue #2179
fixing tab size setting that breaks the editor configuration
2019-05-31 11:20:39 -03:00
Carlos José da Costa
6d7900be82 Merge pull request #1 from web2py/master
Sync with original
2019-05-31 10:28:05 -03:00
Ankidroid-net
dadf363122 create fa.py
Hello. I intend to prepare a Persian translation of the Web 2 Pie and provide a good source for Persian speakers.
2019-05-26 17:20:25 +04:30
mdipierro
ce25d8dc31 description % should not be escaped, reverting 2019-05-25 01:01:32 -07:00
mdipierro
1c5b2d7fce chmod a-x, thanks Paolo 2019-05-25 00:30:34 -07:00
mdipierro
9b831a64c0 Merge pull request #2159 from nicozanf/master
Change binary scripts to use PyInstaller with PY3
2019-05-25 00:24:16 -07:00
mdipierro
e6e92b43c9 Merge pull request #2212 from gonguinguen/patch-4
solves #2211
2019-05-25 00:21:44 -07:00
mdipierro
c02ee6a5c0 Merge pull request #2210 from timnyborg/patch-5
prevent open redirects with no protocol specified
2019-05-25 00:21:20 -07:00
mdipierro
22f95677d9 Merge pull request #2208 from dlage/fix-missing-folders-in-script
Refactor methods from gluon/admin to gluon/fileutils
2019-05-25 00:20:52 -07:00
mdipierro
4fd44ff682 Merge pull request #2207 from dlage/migrator-pr
Migrator - add a command line option to ensure that all tables are migrated
2019-05-25 00:20:21 -07:00
mdipierro
da253f7ba8 Merge pull request #2202 from bmiklautz/shell_controller
fix: controller function invocation via shell
2019-05-25 00:18:55 -07:00
mdipierro
a24926ad6f Merge pull request #2199 from timnyborg/patch-4
correct SAML authorization request binding
2019-05-25 00:18:27 -07:00
c6837a9e73 Merge branch 'master' into migrator-pr 2019-05-23 14:32:24 +01:00
Lisandro
bd892556e1 solves #2211
The change I propose solves issue #2211 
I hope this is the correct approach.
2019-05-20 11:58:42 -03:00
Tim Nyborg
99d3d1d465 prevent open redirects with no protocol specified
prevent_open_redirect doesn't currently handle a 'next' with a // but no protocol, e.g.: .../user/login?_next=//google.com
2019-05-20 11:27:21 +01:00
b365d12a2e Merge branch 'master' into migrator-pr 2019-05-16 16:49:15 +01:00
4455fa48c4 Add a script to force migration of lazy tables and fix missing options in DAL init. 2019-05-16 15:33:19 +01:00
96534ccf30 First attempt to force migration of the tables even with migrate option set to false. 2019-05-15 20:07:36 +01:00
Bernhard Miklautz
7a225da44e fix: controller function invocation via shell
If only a compiled version is available and a controller function is invoked with -S app/c/f an
IOError is raised as '_' is used as delimiter instead of '.'.

web2py.py  -S test/test/test -M would lead to:

IOError: [Errno 2] No such file or directory: 'applications/test/compiled/controllers_test_test.pyc'
2019-05-11 17:33:42 +02:00
Tim Nyborg
3f15d1ceb8 correct SAML authorization request binding
AuthnRequest cannot use BINDING_HTTP_REDIRECT, according to the SAML v2 specifications.  See:
https://github.com/IdentityPython/pysaml2/issues/163
2019-05-07 09:09:56 +01:00
Nico Zanferrari
9769314f01 PY2 compatibility 2019-04-24 23:38:06 +02:00
Nico Zanferrari
b79b5951f8 Update docs 2019-04-24 23:35:44 +02:00
Nico Zanferrari
7b9d2c87c2 PY2 compatibility 2019-04-24 23:28:48 +02:00
Nico Zanferrari
c03c962778 Rename README to README.md 2019-03-22 15:37:44 +01:00
Nico Zanferrari
ca7b676591 Update README 2019-03-22 15:37:14 +01:00
Nico Zanferrari
7a0e113a5f new PyInstaller script 2019-03-22 15:34:50 +01:00
Nico Zanferrari
7e30be377d cleanup 2019-03-22 15:33:44 +01:00
Nico Zanferrari
f9db6a8306 cleanup 2019-03-22 15:33:29 +01:00
Nico Zanferrari
50878f33bd cleanup 2019-03-22 15:33:14 +01:00
106 changed files with 1267 additions and 1166 deletions

View File

@@ -30,7 +30,7 @@ except:
if request.is_https:
session.secure()
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \
elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \
(request.function != 'manage'):
raise HTTP(200, T('appadmin is disabled because insecure channel'))
@@ -46,7 +46,7 @@ if request.function == 'manage':
auth.table_permission()])
manager_role = manager_action.get('role', None) if manager_action else None
if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)):
raise HTTP(403, "Not authorized")
raise HTTP(403, 'Not authorized')
menu = False
elif (request.application == 'admin' and not session.authorized) or \
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
@@ -182,7 +182,7 @@ def select():
db = get_database(request)
dbname = request.args[0]
try:
is_imap = db._uri.startswith("imap://")
is_imap = db._uri.startswith('imap://')
except (KeyError, AttributeError, TypeError):
is_imap = False
regex = re.compile(r'(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
@@ -224,15 +224,15 @@ def select():
session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class="form-control",
_name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
error_message=T('Cannot be empty')))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields
or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check',
or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))),
TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))),
_action=URL(r=request, args=request.args))
tb = None
@@ -254,8 +254,8 @@ def select():
if is_imap:
fields = [db[table][name] for name in
("id", "uid", "created", "to",
"sender", "subject")]
('id', 'uid', 'created', 'to',
'sender', 'subject')]
if orderby:
rows = db(query, ignore_common_filters=True).select(
*fields, limitby=(start, stop),
@@ -271,10 +271,10 @@ def select():
# begin handle upload csv
csv_table = table or request.vars.table
if csv_table:
formcsv = FORM(str(T('or import from csv file')) + " ",
formcsv = FORM(str(T('or import from csv file')) + ' ',
INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit', _value=T('import'), _class="btn btn-primary"))
INPUT(_type='submit', _value=T('import'), _class='btn btn-primary'))
else:
formcsv = None
if formcsv and formcsv.process().accepted:
@@ -356,26 +356,26 @@ def state():
def ccache():
if is_gae:
form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")))
P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')))
else:
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')),
P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
T('Clear RAM'), _type='submit', _name='ram', _value='ram')),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
T('Clear DISK'), _type='submit', _name='disk', _value='disk')),
)
if form.accepts(request.vars, session):
session.flash = ""
session.flash = ''
if is_gae:
if request.vars.yes:
cache.ram.clear()
session.flash += T("Cache Cleared")
session.flash += T('Cache Cleared')
else:
clear_ram = False
clear_disk = False
@@ -387,10 +387,10 @@ def ccache():
clear_disk = True
if clear_ram:
cache.ram.clear()
session.flash += T("Ram Cleared")
session.flash += T('Ram Cleared')
if clear_disk:
cache.disk.clear()
session.flash += T("Disk Cleared")
session.flash += T('Disk Cleared')
redirect(URL(r=request))
try:
@@ -436,7 +436,7 @@ def ccache():
gae_stats['ratio'] = ((gae_stats['hits'] * 100) /
(gae_stats['hits'] + gae_stats['misses']))
except ZeroDivisionError:
gae_stats['ratio'] = T("?")
gae_stats['ratio'] = T('?')
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
@@ -502,7 +502,7 @@ def ccache():
TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))),
*[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys],
**dict(_class='cache-keys',
_style="border-collapse: separate; border-spacing: .5em;"))
_style='border-collapse: separate; border-spacing: .5em;'))
if not is_gae:
ram['keys'] = key_table(ram['keys'])
@@ -536,26 +536,26 @@ def table_template(table):
# This is horribe HTML but the only one graphiz understands
rows = []
cellpadding = 4
color = "#000000"
bgcolor = "#FFFFFF"
face = "Helvetica"
face_bold = "Helvetica Bold"
color = '#000000'
bgcolor = '#FFFFFF'
face = 'Helvetica'
face_bold = 'Helvetica Bold'
border = 0
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
_colspan=3, _cellpadding=cellpadding,
_align="center", _bgcolor=color)))
_align='center', _bgcolor=color)))
for row in db[table]:
rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(row.type, _color=color, _face=face),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(types(row), _color=color, _face=face),
_align="center", _cellpadding=cellpadding,
_align='center', _cellpadding=cellpadding,
_border=border)))
return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
_cellborder=0, _cellspacing=0)
).xml()
@@ -632,15 +632,15 @@ def hooks():
if len(functions):
method_hooks.append({'name': op, 'functions':functions})
if len(method_hooks):
tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks})
tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks})
# Render
ul_main = UL(_class='nav nav-list')
for t in tables:
ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug']))
ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none')
ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none')
for op in t['method_hooks']:
ul_t.append(LI(op['name']))
ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_main.append(ul_t)
return ul_main
@@ -650,11 +650,11 @@ def hooks():
# ###########################################################
def d3_graph_model():
""" See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
and also the app_admin bg_graph_model function
Create a list of table dicts, called "nodes"
"""
Create a list of table dicts, called 'nodes'
'''
nodes = []
links = []
@@ -670,10 +670,10 @@ def d3_graph_model():
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
disp = 'PK'
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
disp = 'FK'
else:
disp = ' '
fields.append(dict(name=field.name, type=field.type, disp=disp))
@@ -685,7 +685,7 @@ def d3_graph_model():
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type='table', fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

0
applications/admin/languages/af.py Executable file → Normal file
View File

0
applications/admin/languages/bg.py Executable file → Normal file
View File

0
applications/admin/languages/cs.py Executable file → Normal file
View File

0
applications/admin/languages/en.py Executable file → Normal file
View File

0
applications/admin/languages/es.py Executable file → Normal file
View File

0
applications/admin/languages/fr.py Executable file → Normal file
View File

0
applications/admin/languages/he.py Executable file → Normal file
View File

0
applications/admin/languages/it.py Executable file → Normal file
View File

0
applications/admin/languages/ja.py Executable file → Normal file
View File

0
applications/admin/languages/my-mm.py Executable file → Normal file
View File

0
applications/admin/languages/nl.py Executable file → Normal file
View File

0
applications/admin/languages/pl.py Executable file → Normal file
View File

0
applications/admin/languages/plural-en.py Executable file → Normal file
View File

0
applications/admin/languages/plural-ru.py Executable file → Normal file
View File

0
applications/admin/languages/plural-uk.py Executable file → Normal file
View File

0
applications/admin/languages/pt-br.py Executable file → Normal file
View File

0
applications/admin/languages/pt.py Executable file → Normal file
View File

0
applications/admin/languages/ro.py Executable file → Normal file
View File

0
applications/admin/languages/ru.py Executable file → Normal file
View File

0
applications/admin/languages/sl.py Executable file → Normal file
View File

0
applications/admin/languages/sr-cr.py Executable file → Normal file
View File

0
applications/admin/languages/sr-lt.py Executable file → Normal file
View File

0
applications/admin/languages/tr.py Executable file → Normal file
View File

0
applications/admin/languages/uk.py Executable file → Normal file
View File

0
applications/admin/languages/zh-tw.py Executable file → Normal file
View File

0
applications/admin/languages/zh.py Executable file → Normal file
View File

View File

@@ -1,9 +1,12 @@
[DEFAULT]
[editor]
theme = web2py
editor = default
theme = twilight
editor = sublime
closetag = true
tabwidth = 4
highlightline = true
linenumbers = true
codefolding = false
indentwithtabs = false
[editor_sessions]
welcome = welcome/models/db.py,welcome/controllers/default.py,welcome/views/default/index.html

0
applications/admin/static/css/jqueryMultiSelect.css Executable file → Normal file
View File

0
applications/admin/static/js/jqueryMultiSelect.js vendored Executable file → Normal file
View File

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

View File

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -38,19 +38,19 @@ def file_upload_form(location):
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY()),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)),
INPUT(_type="submit",_value=T("submit")),_action=URL('upload_file'))
INPUT(_type="submit",_value=T("Submit")),_action=URL('upload_file'))
return form
def file_create_form(location):
form=FORM(T("create file with filename:")," ",
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY()),
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)),
INPUT(_type="submit",_value=T("submit")),_action=URL('create_file'))
INPUT(_type="submit",_value=T("Submit")),_action=URL('create_file'))
return form
def upload_plugin_form(app):
form=FORM(T("upload plugin file:")," ",
INPUT(_type="file",_name="pluginfile"),
INPUT(_type="submit",_value=T("submit")))
INPUT(_type="submit",_value=T("Submit")))
return form
def deletefile(arglist):
return A_delete(SPAN(T('Delete')), _href=URL('delete',args=arglist,vars=dict(sender=request.function+'/'+app)))

View File

@@ -6,7 +6,7 @@
return files
}}
{{themes = [f[:-4] for f in listfiles('admin', "static/codemirror/theme", regexp='.*\.css$' )]}}
{{editors = ['default', 'vim', 'emacs']}}
{{editors = ['default', 'vim', 'emacs', 'sublime']}}
<form id="editor_settings_form" class="form-horizontal" action="">
<div class="control-group">
@@ -27,7 +27,7 @@
</div>
<div class="control-group">
<label class="control-label" for="tabwidth">{{=T('Tab width (# characters)')}}</label>
<div class="controls">{{=SELECT(range(1,9, 1), value=editor_settings['tabwidth'], _name="tabwidth" )}}</div>
<div class="controls">{{=SELECT(list(range(1,9, 1)), value=editor_settings['tabwidth'], _name="tabwidth" )}}</div>
</div>
<div class="control-group">
<label class="control-label" for="indentwithtabs">{{=T('Indent with tabs')}}</label>

View File

@@ -24,7 +24,7 @@ def file_upload_form(location):
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)),
INPUT(_type="submit",_value=T("submit")),_action=URL('upload_file'))
INPUT(_type="submit",_value=T("Submit")),_action=URL('upload_file'))
return form
def file_create_form(location):
form=FORM(T("create file with filename:")," ",
@@ -32,7 +32,7 @@ def file_create_form(location):
INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design/'+app)),
INPUT(_type="submit",_value=T("submit")),_action=URL('create_file'))
INPUT(_type="submit",_value=T("Submit")),_action=URL('create_file'))
return form
def deletefile(arglist):
return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')),

View File

@@ -30,7 +30,7 @@ except:
if request.is_https:
session.secure()
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \
elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \
(request.function != 'manage'):
raise HTTP(200, T('appadmin is disabled because insecure channel'))
@@ -46,7 +46,7 @@ if request.function == 'manage':
auth.table_permission()])
manager_role = manager_action.get('role', None) if manager_action else None
if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)):
raise HTTP(403, "Not authorized")
raise HTTP(403, 'Not authorized')
menu = False
elif (request.application == 'admin' and not session.authorized) or \
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
@@ -182,7 +182,7 @@ def select():
db = get_database(request)
dbname = request.args[0]
try:
is_imap = db._uri.startswith("imap://")
is_imap = db._uri.startswith('imap://')
except (KeyError, AttributeError, TypeError):
is_imap = False
regex = re.compile(r'(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
@@ -224,15 +224,15 @@ def select():
session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class="form-control",
_name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
error_message=T('Cannot be empty')))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields
or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check',
or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))),
TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))),
_action=URL(r=request, args=request.args))
tb = None
@@ -254,8 +254,8 @@ def select():
if is_imap:
fields = [db[table][name] for name in
("id", "uid", "created", "to",
"sender", "subject")]
('id', 'uid', 'created', 'to',
'sender', 'subject')]
if orderby:
rows = db(query, ignore_common_filters=True).select(
*fields, limitby=(start, stop),
@@ -271,10 +271,10 @@ def select():
# begin handle upload csv
csv_table = table or request.vars.table
if csv_table:
formcsv = FORM(str(T('or import from csv file')) + " ",
formcsv = FORM(str(T('or import from csv file')) + ' ',
INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit', _value=T('import'), _class="btn btn-primary"))
INPUT(_type='submit', _value=T('import'), _class='btn btn-primary'))
else:
formcsv = None
if formcsv and formcsv.process().accepted:
@@ -356,26 +356,26 @@ def state():
def ccache():
if is_gae:
form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")))
P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')))
else:
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')),
P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
T('Clear RAM'), _type='submit', _name='ram', _value='ram')),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
T('Clear DISK'), _type='submit', _name='disk', _value='disk')),
)
if form.accepts(request.vars, session):
session.flash = ""
session.flash = ''
if is_gae:
if request.vars.yes:
cache.ram.clear()
session.flash += T("Cache Cleared")
session.flash += T('Cache Cleared')
else:
clear_ram = False
clear_disk = False
@@ -387,10 +387,10 @@ def ccache():
clear_disk = True
if clear_ram:
cache.ram.clear()
session.flash += T("Ram Cleared")
session.flash += T('Ram Cleared')
if clear_disk:
cache.disk.clear()
session.flash += T("Disk Cleared")
session.flash += T('Disk Cleared')
redirect(URL(r=request))
try:
@@ -436,7 +436,7 @@ def ccache():
gae_stats['ratio'] = ((gae_stats['hits'] * 100) /
(gae_stats['hits'] + gae_stats['misses']))
except ZeroDivisionError:
gae_stats['ratio'] = T("?")
gae_stats['ratio'] = T('?')
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
@@ -502,7 +502,7 @@ def ccache():
TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))),
*[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys],
**dict(_class='cache-keys',
_style="border-collapse: separate; border-spacing: .5em;"))
_style='border-collapse: separate; border-spacing: .5em;'))
if not is_gae:
ram['keys'] = key_table(ram['keys'])
@@ -536,26 +536,26 @@ def table_template(table):
# This is horribe HTML but the only one graphiz understands
rows = []
cellpadding = 4
color = "#000000"
bgcolor = "#FFFFFF"
face = "Helvetica"
face_bold = "Helvetica Bold"
color = '#000000'
bgcolor = '#FFFFFF'
face = 'Helvetica'
face_bold = 'Helvetica Bold'
border = 0
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
_colspan=3, _cellpadding=cellpadding,
_align="center", _bgcolor=color)))
_align='center', _bgcolor=color)))
for row in db[table]:
rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(row.type, _color=color, _face=face),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(types(row), _color=color, _face=face),
_align="center", _cellpadding=cellpadding,
_align='center', _cellpadding=cellpadding,
_border=border)))
return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
_cellborder=0, _cellspacing=0)
).xml()
@@ -632,15 +632,15 @@ def hooks():
if len(functions):
method_hooks.append({'name': op, 'functions':functions})
if len(method_hooks):
tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks})
tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks})
# Render
ul_main = UL(_class='nav nav-list')
for t in tables:
ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug']))
ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none')
ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none')
for op in t['method_hooks']:
ul_t.append(LI(op['name']))
ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_main.append(ul_t)
return ul_main
@@ -650,11 +650,11 @@ def hooks():
# ###########################################################
def d3_graph_model():
""" See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
and also the app_admin bg_graph_model function
Create a list of table dicts, called "nodes"
"""
Create a list of table dicts, called 'nodes'
'''
nodes = []
links = []
@@ -670,10 +670,10 @@ def d3_graph_model():
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
disp = 'PK'
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
disp = 'FK'
else:
disp = ' '
fields.append(dict(name=field.name, type=field.type, disp=disp))
@@ -685,7 +685,7 @@ def d3_graph_model():
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type='table', fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

View File

@@ -30,7 +30,7 @@ except:
if request.is_https:
session.secure()
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1") and \
elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \
(request.function != 'manage'):
raise HTTP(200, T('appadmin is disabled because insecure channel'))
@@ -46,7 +46,7 @@ if request.function == 'manage':
auth.table_permission()])
manager_role = manager_action.get('role', None) if manager_action else None
if not (gluon.fileutils.check_credentials(request) or auth.has_membership(manager_role)):
raise HTTP(403, "Not authorized")
raise HTTP(403, 'Not authorized')
menu = False
elif (request.application == 'admin' and not session.authorized) or \
(request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
@@ -182,7 +182,7 @@ def select():
db = get_database(request)
dbname = request.args[0]
try:
is_imap = db._uri.startswith("imap://")
is_imap = db._uri.startswith('imap://')
except (KeyError, AttributeError, TypeError):
is_imap = False
regex = re.compile(r'(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
@@ -224,15 +224,15 @@ def select():
session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class="form-control",
_name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
error_message=T('Cannot be empty')))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields
or '', _class="form-control")), TR(T('Delete:'), INPUT(_name='delete_check',
or '', _class='form-control')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit'), _class="btn btn-primary"))),
TR('', '', INPUT(_type='submit', _value=T('Submit'), _class='btn btn-primary'))),
_action=URL(r=request, args=request.args))
tb = None
@@ -254,8 +254,8 @@ def select():
if is_imap:
fields = [db[table][name] for name in
("id", "uid", "created", "to",
"sender", "subject")]
('id', 'uid', 'created', 'to',
'sender', 'subject')]
if orderby:
rows = db(query, ignore_common_filters=True).select(
*fields, limitby=(start, stop),
@@ -271,10 +271,10 @@ def select():
# begin handle upload csv
csv_table = table or request.vars.table
if csv_table:
formcsv = FORM(str(T('or import from csv file')) + " ",
formcsv = FORM(str(T('or import from csv file')) + ' ',
INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit', _value=T('import'), _class="btn btn-primary"))
INPUT(_type='submit', _value=T('import'), _class='btn btn-primary'))
else:
formcsv = None
if formcsv and formcsv.process().accepted:
@@ -356,26 +356,26 @@ def state():
def ccache():
if is_gae:
form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")))
P(TAG.BUTTON(T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')))
else:
cache.ram.initialize()
cache.disk.initialize()
form = FORM(
P(TAG.BUTTON(
T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
T('Clear CACHE?'), _type='submit', _name='yes', _value='yes')),
P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
T('Clear RAM'), _type='submit', _name='ram', _value='ram')),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
T('Clear DISK'), _type='submit', _name='disk', _value='disk')),
)
if form.accepts(request.vars, session):
session.flash = ""
session.flash = ''
if is_gae:
if request.vars.yes:
cache.ram.clear()
session.flash += T("Cache Cleared")
session.flash += T('Cache Cleared')
else:
clear_ram = False
clear_disk = False
@@ -387,10 +387,10 @@ def ccache():
clear_disk = True
if clear_ram:
cache.ram.clear()
session.flash += T("Ram Cleared")
session.flash += T('Ram Cleared')
if clear_disk:
cache.disk.clear()
session.flash += T("Disk Cleared")
session.flash += T('Disk Cleared')
redirect(URL(r=request))
try:
@@ -436,7 +436,7 @@ def ccache():
gae_stats['ratio'] = ((gae_stats['hits'] * 100) /
(gae_stats['hits'] + gae_stats['misses']))
except ZeroDivisionError:
gae_stats['ratio'] = T("?")
gae_stats['ratio'] = T('?')
gae_stats['oldest'] = GetInHMS(time.time() - gae_stats['oldest_item_age'])
total.update(gae_stats)
else:
@@ -502,7 +502,7 @@ def ccache():
TR(TD(B(T('Key'))), TD(B(T('Time in Cache (h:m:s)')))),
*[TR(TD(k[0]), TD('%02d:%02d:%02d' % k[1])) for k in keys],
**dict(_class='cache-keys',
_style="border-collapse: separate; border-spacing: .5em;"))
_style='border-collapse: separate; border-spacing: .5em;'))
if not is_gae:
ram['keys'] = key_table(ram['keys'])
@@ -536,26 +536,26 @@ def table_template(table):
# This is horribe HTML but the only one graphiz understands
rows = []
cellpadding = 4
color = "#000000"
bgcolor = "#FFFFFF"
face = "Helvetica"
face_bold = "Helvetica Bold"
color = '#000000'
bgcolor = '#FFFFFF'
face = 'Helvetica'
face_bold = 'Helvetica Bold'
border = 0
rows.append(TR(TD(FONT(table, _face=face_bold, _color=bgcolor),
_colspan=3, _cellpadding=cellpadding,
_align="center", _bgcolor=color)))
_align='center', _bgcolor=color)))
for row in db[table]:
rows.append(TR(TD(FONT(row.name, _color=color, _face=face_bold),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(row.type, _color=color, _face=face),
_align="left", _cellpadding=cellpadding,
_align='left', _cellpadding=cellpadding,
_border=border),
TD(FONT(types(row), _color=color, _face=face),
_align="center", _cellpadding=cellpadding,
_align='center', _cellpadding=cellpadding,
_border=border)))
return "< %s >" % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
return '< %s >' % TABLE(*rows, **dict(_bgcolor=bgcolor, _border=1,
_cellborder=0, _cellspacing=0)
).xml()
@@ -632,15 +632,15 @@ def hooks():
if len(functions):
method_hooks.append({'name': op, 'functions':functions})
if len(method_hooks):
tables.append({'name': "%s.%s" % (db_str, t), 'slug': IS_SLUG()("%s.%s" % (db_str,t))[0], 'method_hooks':method_hooks})
tables.append({'name': '%s.%s' % (db_str, t), 'slug': IS_SLUG()('%s.%s' % (db_str,t))[0], 'method_hooks':method_hooks})
# Render
ul_main = UL(_class='nav nav-list')
for t in tables:
ul_main.append(A(t['name'], _onclick="collapse('a_%s')" % t['slug']))
ul_t = UL(_class='nav nav-list', _id="a_%s" % t['slug'], _style='display:none')
ul_t = UL(_class='nav nav-list', _id='a_%s' % t['slug'], _style='display:none')
for op in t['method_hooks']:
ul_t.append(LI(op['name']))
ul_t.append(UL([LI(A(f['funcname'], _class="editor_filelink", _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_t.append(UL([LI(A(f['funcname'], _class='editor_filelink', _href=f['url']if 'url' in f else None, **{'_data-lineno':f['lineno']-1})) for f in op['functions']]))
ul_main.append(ul_t)
return ul_main
@@ -650,11 +650,11 @@ def hooks():
# ###########################################################
def d3_graph_model():
""" See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
''' See https://www.facebook.com/web2py/posts/145613995589010 from Bruno Rocha
and also the app_admin bg_graph_model function
Create a list of table dicts, called "nodes"
"""
Create a list of table dicts, called 'nodes'
'''
nodes = []
links = []
@@ -670,10 +670,10 @@ def d3_graph_model():
elif f_type == 'string':
disp = field.length
elif f_type == 'id':
disp = "PK"
disp = 'PK'
elif f_type.startswith('reference') or \
f_type.startswith('list:reference'):
disp = "FK"
disp = 'FK'
else:
disp = ' '
fields.append(dict(name=field.name, type=field.type, disp=disp))
@@ -685,7 +685,7 @@ def d3_graph_model():
links.append(dict(source=tablename, target = referenced_table))
nodes.append(dict(name=tablename, type="table", fields = fields))
nodes.append(dict(name=tablename, type='table', fields = fields))
# d3 v4 allows individual modules to be specified. The complete d3 library is included below.
response.files.append(URL('admin','static','js/d3.min.js'))

0
applications/welcome/languages/ar.py Executable file → Normal file
View File

0
applications/welcome/languages/ca.py Executable file → Normal file
View File

0
applications/welcome/languages/cs.py Executable file → Normal file
View File

0
applications/welcome/languages/en.py Executable file → Normal file
View File

0
applications/welcome/languages/es.py Executable file → Normal file
View File

0
applications/welcome/languages/fr-ca.py Executable file → Normal file
View File

0
applications/welcome/languages/fr.py Executable file → Normal file
View File

0
applications/welcome/languages/hi.py Executable file → Normal file
View File

0
applications/welcome/languages/hu.py Executable file → Normal file
View File

0
applications/welcome/languages/id.py Executable file → Normal file
View File

0
applications/welcome/languages/it.py Executable file → Normal file
View File

0
applications/welcome/languages/my-mm.py Executable file → Normal file
View File

0
applications/welcome/languages/my.py Executable file → Normal file
View File

0
applications/welcome/languages/nl.py Executable file → Normal file
View File

0
applications/welcome/languages/pl.py Executable file → Normal file
View File

0
applications/welcome/languages/plural-cs.py Executable file → Normal file
View File

0
applications/welcome/languages/plural-en.py Executable file → Normal file
View File

0
applications/welcome/languages/plural-es.py Executable file → Normal file
View File

0
applications/welcome/languages/plural-ru.py Executable file → Normal file
View File

0
applications/welcome/languages/plural-uk.py Executable file → Normal file
View File

208
applications/welcome/languages/pt-br.py Executable file → Normal file
View File

@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
{
'!=': '!=',
'!langcode!': 'pt-br',
'!langname!': 'Português (do Brasil)',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" é uma expressão opcional como "campo1=\'novovalor\'". Você não pode atualizar ou apagar os resultados de um JOIN',
'%(nrows)s records found': '%(nrows)s registros encontrados',
'%s %%{row} deleted': '%s linha apagadas',
'%s %%{row} updated': '%s linha atualizadas',
'%s selected': '%s selecionado',
@@ -12,6 +14,13 @@
'**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}',
'**%(items)s** items, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** items, **%(bytes)s** %%{byte(bytes)}',
'**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '**not available** (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)',
'+ And': '+ E',
'+ Or': '+ Ou',
'<': '<',
'<=': '<=',
'=': '=',
'>': '>',
'>=': '>=',
'?': '?',
'@markmin\x01(**%.0d MB**)': '(**%.0d MB**)',
'@markmin\x01**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}': '**%(items)s** %%{item(items)}, **%(bytes)s** %%{byte(bytes)}',
@@ -22,23 +31,28 @@
'@markmin\x01Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'Cache contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.',
'@markmin\x01DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'DISK contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.',
'@markmin\x01Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})',
'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**',
'@markmin\x01Number of entries: **%s**': 'Número de entradas: **%s**',
'@markmin\x01RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.',
'``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)': '``**not available**``:red (requires the Python [[guppy http://pypi.python.org/pypi/guppy/ popup]] library)',
'A new password was emailed to you': 'A new password was emailed to you',
'A new password was emailed to you': 'Uma nova senha foi enviada por email para você',
'About': 'Sobre',
'Access Control': 'Controle de Acesso',
'Add Record': 'Novo Registro',
'Add record to database': 'Adicionar registro ao banco de dados',
'Add this to the search as an AND term': 'Adicionar à pesquisa como um termo E',
'Add this to the search as an OR term': 'Adicionar à pesquisa como um termo OU',
'admin': 'admin',
'Administrative Interface': 'Interface Administrativa',
'Administrative interface': 'Interface administrativa',
'Ajax Recipes': 'Receitas de Ajax',
'An error occured, please [[reload %s]] the page': 'An error occured, please [[reload %s]] the page',
'API Example': 'API Example',
'API Example': 'Exmplo de API',
'appadmin is disabled because insecure channel': 'Administração desativada porque o canal não é seguro',
'Apply changes': 'Apply changes',
'Apply changes': 'Aplicar Mudanças',
'Are you sure you want to delete this object?': 'Você tem certeza que quer apagar este objeto?',
'Authentication code': 'Authentication code',
'Authentication code': 'Código de autenticação',
'Available Databases and Tables': 'Bancos de dados e tabelas disponíveis',
'Back': 'Voltar',
'Buy this book': 'Compre o livro',
"Buy web2py's book": 'Compre o livro do web2py',
'cache': 'cache',
@@ -48,19 +62,27 @@
'Cache Keys': 'Chaves de cache',
'Cannot be empty': 'Não pode estar vazio',
'change password': 'mudar senha',
'Change Password': 'Change Password',
'Change password': 'Change password',
'Change Password': 'Trocar Senhar',
'Change password': 'Trocar senha',
'Check to delete': 'Marque para apagar',
'Clear': 'Limpar',
'Clear CACHE?': 'Limpar CACHE?',
'Clear DISK': 'Limpar DISCO',
'Clear RAM': 'Limpar memória RAM',
'Click on the link %(link)s to reset your password': 'Click on the link %(link)s to reset your password',
'Client IP': 'IP do cliente',
'Close': 'Fechar',
'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export': 'Comma-separated export including columns not shown; fields from other tables are exported as raw values for faster export',
'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows': 'Comma-separated export of visible columns. Fields from other tables are exported as they appear on-screen but this may be slow for many rows',
'Community': 'Comunidade',
'Components and Plugins': 'Componentes e Plugins',
'Config.ini': 'Config.ini',
'Confirm Password': 'Confirme a Senha',
'contains': 'contém',
'Controller': 'Controlador',
'Copyright': 'Copyright',
'CSV': 'CSV',
'CSV (hidden cols)': 'CSV (col. ocultas)',
'Current request': 'Requisição atual',
'Current response': 'Resposta atual',
'Current session': 'Sessão atual',
@@ -68,9 +90,10 @@
'data uploaded': 'dados enviados',
'Database': 'banco de dados',
'Database %s select': 'Selecionar banco de dados %s',
'Database Administration (appadmin)': 'Database Administration (appadmin)',
'Database Administration (appadmin)': 'Administração de Banco de Dados (appadmin)',
'db': 'bd',
'DB Model': 'Modelo BD',
'Delete': 'Excluir',
'Delete:': 'Apagar:',
'Demo': 'Demo',
'Deployment Recipes': 'Receitas de deploy',
@@ -91,50 +114,57 @@
'edit profile': 'editar perfil',
'Edit This App': 'Editar esta aplicação',
'Email and SMS': 'Email e SMS',
'Email sent': 'Email sent',
'Email verification': 'Email verification',
'Email verified': 'Email verified',
'Email sent': 'Email enviado',
'Email verification': 'Verificação de email',
'Email verified': 'Email verificado',
'Enter an integer between %(min)g and %(max)g': 'Informe um valor inteiro entre %(min)g e %(max)g',
'Errors': 'Erros',
'export as csv file': 'exportar como um arquivo csv',
'Export:': 'Exportar:',
'FAQ': 'Perguntas frequentes',
'First name': 'Nome',
'Forms and Validators': 'Formulários e Validadores',
'Free Applications': 'Aplicações gratuitas',
'Function disabled': 'Function disabled',
'Graph Model': 'Graph Model',
'Function disabled': 'Função desabilitada',
'Graph Model': 'Modelo em Grafo',
'Grid Example': 'Exemplo de Grade',
'Group %(group_id)s created': 'Group %(group_id)s created',
'Group %(group_id)s deleted': 'Group %(group_id)s deleted',
'Group %(group_id)s created': 'Grupo %(group_id)s criado',
'Group %(group_id)s deleted': 'Grupo %(group_id)s excluído',
'Group ID': 'ID do Grupo',
'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s',
'Group uniquely assigned to user %(id)s': 'Gurpo unicamente atribuído ao usuário %(id)s',
'Groups': 'Grupos',
'Hello World': 'Olá Mundo',
'Helping web2py': 'Helping web2py',
'Helping web2py': 'Ajudando web2py',
'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})': 'Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})',
'Home': 'Principal',
'How did you get here?': 'Como você chegou aqui?',
'HTML': 'HTML',
'HTML export of visible columns': 'HTML exportar colunas visíveis',
'Id': 'Id',
'import': 'importar',
'Import/Export': 'Importar/Exportar',
'in': 'em',
'Incorrect code. {0} more attempt(s) remaining.': 'Incorrect code. {0} more attempt(s) remaining.',
'Index': 'Início',
'insert new': 'inserir novo',
'insert new %s': 'inserir novo %s',
'Insufficient privileges': 'Insufficient privileges',
'Insufficient privileges': 'Privilégios insuficientes',
'Internal State': 'Estado Interno',
'Introduction': 'Introdução',
'Invalid email': 'Email inválido',
'Invalid key': 'Invalid key',
'Invalid login': 'Invalid login',
'Invalid password': 'Invalid password',
'Invalid key': 'Chave inválida',
'Invalid login': 'Login Inválido',
'Invalid password': 'Senha inválida',
'Invalid Query': 'Consulta Inválida',
'invalid request': 'requisição inválida',
'Invalid reset password': 'Invalid reset password',
'Invalid user': 'Invalid user',
'Invalid username': 'Invalid username',
'Invitation to join %(site)s': 'Invitation to join %(site)s',
'Invalid reset password': 'Recriação de senha inválida',
'Invalid user': 'Usuário inválido',
'Invalid username': 'Nome de usuário inválido',
'Invitation to join %(site)s': 'Convite para entrar %(site)s',
'JSON': 'JSON',
'JSON export of visible columns': 'JSON exportar colunas visíveis',
'Key': 'Chave',
'Key verified': 'Key verified',
'Key verified': 'Chave verificada',
'Last name': 'Sobrenome',
'Layout': 'Layout',
'Layout Plugins': 'Plugins de Layout',
@@ -142,13 +172,13 @@
'Live chat': 'Chat ao vivo',
'Live Chat': 'Chat ao vivo',
'Log In': 'Entrar',
'Logged in': 'Logged in',
'Logged out': 'Logged out',
'Logged in': 'Conectado',
'Logged out': 'Desconectado',
'login': 'Entrar',
'Login': 'Entrar',
'Login disabled by administrator': 'Login disabled by administrator',
'Login disabled by administrator': 'Login desabilitado pelo administrador',
'logout': 'Sair',
'Logout': 'Logout',
'Logout': 'Sair',
'Lost Password': 'Esqueceu sua senha?',
'lost password?': 'esqueceu sua senha?',
'Lost your password?': 'Esqueceu sua senha?',
@@ -160,16 +190,19 @@
'Menu Model': 'Modelo de Menu',
'My Sites': 'Meus sites',
'Name': 'Nome',
'New password': 'New password',
'New password': 'Nova senha',
'New Record': 'Novo Registro',
'new record inserted': 'novo registro inserido',
'New Search': 'Nova pesquisa',
'next %s rows': 'próximas %s ´linhas',
'next 100 rows': 'próximas 100 linhas',
'No databases in this application': 'Não há bancos de dados nesta aplicação',
'No records found': 'Não foram encontrados registros',
'not in': 'não está em',
'Number of entries: **%s**': 'Número de entradas: **%s**',
'Object or table name': 'Nome do objeto do da tabela',
'Old password': 'Old password',
'Online book': 'Online book',
'Old password': 'Senha antiga',
'Online book': 'Livro online',
'Online examples': 'Exemplos online',
'or import from csv file': 'ou importar de um arquivo csv',
'Origin': 'Origem',
@@ -177,66 +210,71 @@
'Other Recipes': 'Outras Receitas',
'Overview': 'Visão Geral',
'Password': 'Senha',
'Password changed': 'Password changed',
"Password fields don't match": "Password fields don't match",
'Password reset': 'Password reset',
'Password retrieve': 'Password retrieve',
'Permission': 'Permission',
'Permissions': 'Permissions',
'please input your password again': 'please input your password again',
'Password changed': 'Senha trocada',
"Password fields don't match": 'Senhas não conferem',
'Password reset': 'Recriar senha',
'Password retrieve': 'Recuperar senha',
'Permission': 'Permissão',
'Permissions': 'Permissões',
'please input your password again': 'por favor digite a senha novamente',
'Plugins': 'Plugins',
'Powered by': 'Desenvolvido com',
'Preface': 'Prefácio',
'previous %s rows': '%s linhas anteriores',
'previous 100 rows': '100 linhas anteriores',
'Profile': 'Profile',
'Profile updated': 'Profile updated',
'Profile': 'Perfil',
'Profile updated': 'Perfil atualizado',
'pygraphviz library not found': 'biblioteca pygraphviz não encontrada',
'Python': 'Python',
'Query:': 'Consulta:',
'Quick Examples': 'Exemplos rápidos',
'RAM': 'RAM',
'RAM Cache Keys': 'RAM Cache Keys',
'RAM Cache Keys': 'Chaves de Cache RAM ',
'Ram Cleared': 'Ram Limpa',
'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.': 'RAM contains items up to **%(hours)02d** %%{hour(hours)} **%(min)02d** %%{minute(min)} **%(sec)02d** %%{second(sec)} old.',
'Recipes': 'Receitas',
'Record': 'Registro',
'Record %(id)s created': 'Record %(id)s created',
'Record %(id)s deleted': 'Record %(id)s deleted',
'Record %(id)s read': 'Record %(id)s read',
'Record %(id)s updated': 'Record %(id)s updated',
'Record Created': 'Record Created',
'Record Deleted': 'Record Deleted',
'Record %(id)s created': 'Registro %(id)s criado',
'Record %(id)s deleted': 'Registro %(id)s excluído',
'Record %(id)s read': 'Registro %(id)s lido',
'Record %(id)s updated': 'Registro %(id)s atualizado',
'Record Created': 'Registro Criado',
'Record Deleted': 'Registro Excluído',
'record does not exist': 'registro não existe',
'Record ID': 'ID do Registro',
'Record id': 'id do registro',
'Record Updated': 'Record Updated',
'register': 'Registre-se',
'Register': 'Registre-se',
'Registration identifier': 'Idenficador de registro',
'Registration is pending approval': 'Registration is pending approval',
'Registration key': 'Chave de registro',
'Registration needs verification': 'Registration needs verification',
'Registration successful': 'Registration successful',
'Record Updated': 'Registro Atualizado',
'register': 'Cadastre-se',
'Register': 'Cadastre-se',
'Registration identifier': 'Idenficador de Cadastro',
'Registration is pending approval': 'Aprovação de cadastro pendente',
'Registration key': 'Chave de cadastro',
'Registration needs verification': 'Cadastro necessita verficação',
'Registration successful': 'Cadastro finalizado',
'Remember me (for 30 days)': 'Mantenha-me logado (por 30 dias)',
'Request reset password': 'Request reset password',
'Request reset password': 'Requerer recriação de senha',
'Reset Password key': 'Resetar chave de senha',
'Resources': 'Recursos',
'Role': 'Papel',
'Roles': 'Roles',
'Roles': 'Papéis',
'Rows in Table': 'Linhas na tabela',
'Rows selected': 'Linhas selecionadas',
'Save model as...': 'Salvar modelo como...',
'Search': 'Pesquisar',
'Semantic': 'Semântico',
'Services': 'Serviço',
'Sign Up': 'Cadastrar',
'Sign up': 'Cadastrar',
'Size of cache:': 'Tamanho do cache:',
'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow': 'Spreadsheet-optimised export of tab-separated content including hidden columns. May be slow',
'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.': 'Spreadsheet-optimised export of tab-separated content, visible columns only. May be slow.',
'Start building a new search': 'Comerçar um nova pesquisa',
'starts with': 'começa com',
'state': 'estado',
'Statistics': 'Estatísticas',
'Stylesheet': 'Folha de estilo',
'submit': 'enviar',
'Submit': 'Submit',
'Submit': 'Enviar',
'Support': 'Suporte',
'Sure you want to delete this object?': 'Está certo(a) que deseja apagar este objeto?',
'Table': 'Tabela',
@@ -252,31 +290,33 @@
'Time in Cache (h:m:s)': 'Tempo em Cache (h:m:s)',
'Timestamp': 'Timestamp',
'Traceback': 'Traceback',
'TSV (Spreadsheets)': 'TSV (Planilhas)',
'TSV (Spreadsheets, hidden cols)': 'TSV (Planilhas, col. ocultas)',
'Twitter': 'Twitter',
'Two-step Login Authentication Code': 'Two-step Login Authentication Code',
'Two-step Login Authentication Code': 'Código de Autenticação de Login em Dois Fatores',
'unable to parse csv file': 'não foi possível analisar arquivo csv',
'Unable to send email': 'Unable to send email',
'Unable to send email': 'Não foi possível enviar email',
'Update:': 'Atualizar:',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, e ~(...) para NOT para construir consultas mais complexas.',
'User': 'User',
'User': 'Usuário',
'User %(id)s is impersonating %(other_id)s': 'User %(id)s is impersonating %(other_id)s',
'User %(id)s Logged-in': 'User %(id)s Logged-in',
'User %(id)s Logged-out': 'User %(id)s Logged-out',
'User %(id)s Password changed': 'User %(id)s Password changed',
'User %(id)s Password reset': 'User %(id)s Password reset',
'User %(id)s Password retrieved': 'User %(id)s Password retrieved',
'User %(id)s Profile updated': 'User %(id)s Profile updated',
'User %(id)s Registered': 'User %(id)s Registered',
'User %(id)s Username retrieved': 'User %(id)s Username retrieved',
'User %(id)s Verification email sent': 'User %(id)s Verification email sent',
'User %(id)s verified registration key': 'User %(id)s verified registration key',
'User %(id)s Logged-in': 'Usuário %(id)s entrou',
'User %(id)s Logged-out': 'Usuário %(id)s saiu',
'User %(id)s Password changed': 'Usuário %(id)s trocou a senha',
'User %(id)s Password reset': 'Usuário %(id)s recirar a senha',
'User %(id)s Password retrieved': 'Usuário %(id)s Recuperou a senha',
'User %(id)s Profile updated': 'Usuário %(id)s Atualizou perfil',
'User %(id)s Registered': 'Usuário %(id)s Cadastrou-se',
'User %(id)s Username retrieved': 'Usuário %(id)s Recuperou nome de usuário',
'User %(id)s Verification email sent': 'Usuário %(id)s Email de verificação enviado',
'User %(id)s verified registration key': 'Usuário %(id)s chave de cadastro verificada',
'User ID': 'ID do Usuário',
'User Voice': 'Opinião dos usuários',
'Username': 'Username',
'Username already taken': 'Username already taken',
'Username retrieve': 'Username retrieve',
'Users': 'Users',
'Verify Password': 'Verify Password',
'Username': 'Nome de Usuário',
'Username already taken': 'Nome de usuário já existe',
'Username retrieve': 'Recuperar nome de usuário',
'Users': 'Usuários',
'Verify Password': 'Verificar Senha',
'Videos': 'Vídeos',
'View': 'Visualização',
'Web2py': 'Web2py',
@@ -286,15 +326,17 @@
'Welcome to web2py': 'Bem-vindo ao web2py',
'Welcome to web2py!': 'Bem-vindo ao web2py!',
'Which called the function %s located in the file %s': 'Que chamou a função %s localizada no arquivo %s',
'Wiki Example': 'Wiki Example',
'Wiki Example': 'Exmplo de Wiki',
'Working...': 'Trabalhando...',
'XML': 'XML',
'XML export of columns shown': 'XML exportar colunas visíveis',
'You are successfully running web2py': 'Você está executando o web2py com sucesso',
'You are successfully running web2py.': 'Você está executando o web2py com sucesso.',
'You can modify this application and adapt it to your needs': 'Você pode modificar esta aplicação e adaptá-la às suas necessidades',
'You have been invited to join %(site)s, click %(link)s to complete the process': 'You have been invited to join %(site)s, click %(link)s to complete the process',
'You visited the url %s': 'Você acessou a url %s',
'Your password is: %(password)s': 'Your password is: %(password)s',
'Your temporary login code is {0}': 'Your temporary login code is {0}',
'Your username is: %(username)s': 'Your username is: %(username)s',
'Your username was emailed to you': 'Your username was emailed to you',
'Your password is: %(password)s': 'Sua senha é: %(password)s',
'Your temporary login code is {0}': 'Seu código temporário de login é {0}',
'Your username is: %(username)s': 'Seu nome de usuário é: %(username)s',
'Your username was emailed to you': 'Seu nome de usuário foi enviado por email para você',
}

0
applications/welcome/languages/pt.py Executable file → Normal file
View File

0
applications/welcome/languages/ro.py Executable file → Normal file
View File

0
applications/welcome/languages/ru.py Executable file → Normal file
View File

0
applications/welcome/languages/sk.py Executable file → Normal file
View File

0
applications/welcome/languages/tr.py Executable file → Normal file
View File

0
applications/welcome/languages/uk.py Executable file → Normal file
View File

0
applications/welcome/languages/zh-cn.py Executable file → Normal file
View File

0
applications/welcome/languages/zh-tw.py Executable file → Normal file
View File

0
applications/welcome/languages/zh.py Executable file → Normal file
View File

View File

@@ -1,2 +0,0 @@
The files in this folder must be run from the main web2py folder.
They are for building windows and osx binary distribution and not meant for the end user.

View File

@@ -0,0 +1,4 @@
# build-web2py
The files in this folder must be run from the main web2py folder.
They are for building windows and osx binary distribution using PyInstaller and not meant for the end user.

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# up to 2019, we have used py2applet, py2exe and bbfreeze for building web2py binaries
# The original scripts can be found on GitHub for web2py up to version 2.18.4
# See also Niphlod's work on http://www.web2pyslices.com/slice/show/1726/build-windows-binaries
# Then we switched to Pyinstaller in order to fully support Python 3
from distutils.core import setup
from gluon.import_all import base_modules, contributed_modules
from gluon.fileutils import readlines_file
from glob import glob
import os
import shutil
import sys
import re
import zipfile
import subprocess
import platform
USAGE = """
build_web2py - make web2py Windows and MacOS binaries with pyinstaller
Usage:
Install the pyinstaller program, copy this file (plus web2py.*.spec files)
to web2py root folder and run:
python build_py3.py
(tested with python 3.7.3 and 2.7.16 with PyInstaller 3.4)
"""
BUILD_DEBUG = False
"""
If BUILD_DEBUG is set to False, no gluon modules will be embedded inside the binary web2py.exe.
Thus, you can easily update the build version by changing the gluon folder inside the resulting ZIP file.
In case of problem , set BUILD_DEBUG to True. Then all the gluon modules will be analyzed and embedded, too.
You can later analyze the .exe with 'pyi-archive_viewer web2py.exe' and then 'o PYZ-00.pyz'
in order to check for missing system modules to be manually inserted in the SPEC file
"""
if len(sys.argv) != 1 or not os.path.isfile('web2py.py'):
print(USAGE)
sys.exit(1)
os_version = platform.system()
if os_version not in ('Windows', 'Darwin'):
print('Unsupported system: %s' % os_version)
sys.exit(1)
def unzip(source_filename, dest_dir):
with zipfile.ZipFile(source_filename) as zf:
zf.extractall(dest_dir)
# borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
def recursive_zip(zipf, directory, folder=""):
for item in os.listdir(directory):
if os.path.isfile(os.path.join(directory, item)):
zipf.write(os.path.join(directory, item), folder + os.sep + item)
elif os.path.isdir(os.path.join(directory, item)):
recursive_zip(
zipf, os.path.join(directory, item), folder + os.sep + item)
# read web2py version from VERSION file
web2py_version_line = readlines_file('VERSION')[0]
# use regular expression to get just the version number
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
web2py_version = v_re.search(web2py_version_line).group(0)
# Python base version
python_version = sys.version_info[:3]
if os_version == 'Windows':
print("\nBuilding binary web2py for Windows\n")
if BUILD_DEBUG: # debug only
subprocess.call('pyinstaller --clean --icon=extras/icons/web2py.ico \
--hidden-import=site-packages --hidden-import=gluon.packages.dal.pydal \
--hidden-import=gluon.packages.yatl.yatl web2py.py')
zip_filename = 'web2py_win_debug'
else: # normal run
subprocess.call('pyinstaller --clean web2py.win.spec')
subprocess.call('pyinstaller --clean web2py.win_no_console.spec')
source_no_console = 'dist/web2py_no_console/'
files = 'web2py_no_console.exe'
shutil.move(os.path.join(source_no_console, files), 'dist')
shutil.rmtree(source_no_console)
shutil.rmtree('build')
zip_filename = 'web2py_win'
source = 'dist/web2py/'
for files in os.listdir(source):
shutil.move(os.path.join(source, files), 'dist')
shutil.rmtree(source)
os.unlink('dist/web2py.exe.manifest')
bin_folders = ['dist',]
elif os_version == 'Darwin':
print("\nBuilding binary web2py for MacOS\n")
if BUILD_DEBUG: #debug only
subprocess.call("pyinstaller --clean --icon=extras/icons/web2py.icns --hidden-import=gluon.packages.dal.pydal --hidden-import=gluon.packages.yatl.yatl \
--hidden-import=site-packages --windowed web2py.py", shell=True)
zip_filename = 'web2py_osx_debug'
else: # normal run
subprocess.call("pyinstaller --clean web2py.mac.spec", shell=True)
# cleanup + move binary files to dist folder
#shutil.rmtree(os.path.join('dist', 'web2py'))
shutil.rmtree('build')
zip_filename = 'web2py_osx'
shutil.move((os.path.join('dist', 'web2py')),(os.path.join('dist', 'web2py_cmd')))
bin_folders = [(os.path.join('dist', 'web2py.app/Contents/MacOS')), (os.path.join('dist', 'web2py_cmd'))]
print("\nWeb2py binary successfully built!\n")
# add data_files
for req in ['CHANGELOG', 'LICENSE', 'VERSION']:
for bin_folder in bin_folders:
shutil.copy(req, os.path.join(bin_folder, req))
# cleanup unuseful binary cache
for dirpath, dirnames, files in os.walk('.'):
if dirpath.endswith('__pycache__'):
print('Deleting cached binary directory : %s' % dirpath)
shutil.rmtree(dirpath)
for dirpath, dirnames, files in os.walk('.'):
for file in files:
if file.endswith('.pyc'):
print('Deleting cached binary file : %s' % file)
os.unlink(os.path.join(dirpath, file))
print("\nPreparing package ...")
# misc
for folders in ['gluon', 'extras', 'site-packages', 'scripts', 'applications', 'examples', 'handlers']:
for bin_folder in bin_folders:
shutil.copytree(folders, os.path.join(bin_folder, folders))
if not os.path.exists(os.path.join(bin_folder, 'logs')):
os.mkdir(os.path.join(bin_folder, 'logs'))
# create a web2py folder & copy dist's files into it
shutil.copytree('dist', 'zip_temp/web2py')
# create zip file
zipf = zipfile.ZipFile(zip_filename + ".zip",
"w", compression=zipfile.ZIP_DEFLATED)
# just temp so the web2py directory is included in our zip file
path = 'zip_temp'
# leave the first folder as None, as path is root.
recursive_zip(zipf, path)
zipf.close()
shutil.rmtree('zip_temp')
shutil.rmtree('dist')
print("Your binary version of web2py can be found in " + \
zip_filename + ".zip")
print("You may extract the archive anywhere and then run web2py without worrying about dependency")
print("\nEnjoy binary web2py " + web2py_version_line + "\n with embedded Python " + sys.version + "\n")

View File

@@ -1,160 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This is a setup.py script generated by py2applet
Usage:
python setup.py py2app
"""
copy_apps = False
copy_scripts = True
copy_site_packages = True
remove_build_files = True
make_zip = True
zip_filename = "web2py_osx"
from setuptools import setup
from gluon.import_all import base_modules, contributed_modules
from gluon.fileutils import readlines_file
import os
import fnmatch
import shutil
import sys
import re
import zipfile
#read web2py version from VERSION file
web2py_version_line = readlines_file('VERSION')[0]
#use regular expression to get just the version number
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
web2py_version = v_re.search(web2py_version_line).group(0)
class reglob:
def __init__(self, directory, pattern="*"):
self.stack = [directory]
self.pattern = pattern
self.files = []
self.index = 0
def __getitem__(self, index):
while 1:
try:
file = self.files[self.index]
self.index = self.index + 1
except IndexError:
self.index = 0
self.directory = self.stack.pop()
self.files = os.listdir(self.directory)
else:
fullname = os.path.join(self.directory, file)
if os.path.isdir(fullname) and not os.path.islink(fullname):
self.stack.append(fullname)
if not (file.startswith('.') or file.startswith('#') or file.endswith('~')) \
and fnmatch.fnmatch(file, self.pattern):
return fullname
setup(app=['web2py.py'],
version=web2py_version,
description="web2py web framework",
author="Massimo DiPierro",
license="LGPL v3",
data_files=[
'NEWINSTALL',
'ABOUT',
'LICENSE',
'VERSION',
'splashlogo.gif',
'logging.example.conf',
'options_std.py',
],
options={'py2app': {
'argv_emulation': True,
'includes': base_modules,
}},
setup_requires=['py2app'])
def copy_folders(source, destination):
"""Copy files & folders from source to destination (within dist/)"""
print 'copying %s -> %s' % (source, destination)
base = 'dist/web2py.app/Contents/Resources/'
if os.path.exists(os.path.join(base, destination)):
shutil.rmtree(os.path.join(base, destination))
shutil.copytree(os.path.join(source), os.path.join(base, destination))
#Should we include applications?
copy_folders('gluon','gluon')
if copy_apps:
copy_folders('applications', 'applications')
print "Your application(s) have been added"
else:
#only copy web2py's default applications
copy_folders('applications/admin', 'applications/admin')
copy_folders('applications/welcome', 'applications/welcome')
copy_folders('applications/examples', 'applications/examples')
print "Only web2py's admin, examples & welcome applications have been added"
#should we copy project's site-packages into dist/site-packages
if copy_site_packages:
#copy site-packages
copy_folders('site-packages', 'site-packages')
else:
#no worries, web2py will create the (empty) folder first run
print "Skipping site-packages"
pass
#should we copy project's scripts into dist/scripts
if copy_scripts:
#copy scripts
copy_folders('scripts', 'scripts')
else:
#no worries, web2py will create the (empty) folder first run
print "Skipping scripts"
pass
#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
def recursive_zip(zipf, directory, folder=""):
for item in os.listdir(directory):
if os.path.isfile(os.path.join(directory, item)):
zipf.write(os.path.join(directory, item), folder + os.sep + item)
elif os.path.isdir(os.path.join(directory, item)):
recursive_zip(
zipf, os.path.join(directory, item), folder + os.sep + item)
#should we create a zip file of the build?
if make_zip:
#to keep consistent with how official web2py windows zip file is setup,
#create a web2py folder & copy dist's files into it
shutil.copytree('dist', 'zip_temp/web2py')
#create zip file
#use filename specified via command line
zipf = zipfile.ZipFile(
zip_filename + ".zip", "w", compression=zipfile.ZIP_DEFLATED)
path = 'zip_temp' # just temp so the web2py directory is included in our zip file
recursive_zip(
zipf, path) # leave the first folder as None, as path is root.
zipf.close()
shutil.rmtree('zip_temp')
print "Your Windows binary version of web2py can be found in " + \
zip_filename + ".zip"
print "You may extract the archive anywhere and then run web2py/web2py.exe"
#should py2exe build files be removed?
if remove_build_files:
shutil.rmtree('build')
shutil.rmtree('deposit')
shutil.rmtree('dist')
print "py2exe build files removed"
#final info
if not make_zip and not remove_build_files:
print "Your Windows binary & associated files can also be found in /dist"
print "Finished!"
print "Enjoy web2py " + web2py_version_line

View File

@@ -1,27 +0,0 @@
[Setup]
#py2exe often includes DLLS from windows which aren't licensed for
#open source distribution. Should they be removed?
remove_microsoft_dlls: Yes
#copy all web2py apps currently installed?
#If no, only the default admin, welcome & example apps will be included
copy_apps: No
#include the web2py\site-packages directory?
copy_site_packages: Yes
#include the web2py\scripts directory?
copy_scripts: Yes
#create a zip file of the build for easy distribution?
make_zip: Yes
#what should the zip file be named? (leave off the .zip extension)
zip_filename = web2py_win
#should the build, deposit & dist directories used by py2exe be removed?
#if you created a zip file you likely don't need these directories anymore
remove_build_files = Yes
#should the build include the gevented webserver (needs gevent)
include_gevent = Yes

View File

@@ -1,232 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py
USAGE = """
Usage:
Copy this and setup_exe.conf to web2py root folder
To build with py2exe:
Install py2exe: http://sourceforge.net/projects/py2exe/files/
run python setup_exe.py py2exe
To build with bbfreeze:
Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/
run python setup_exe.py bbfreeze
"""
from distutils.core import setup
from gluon.import_all import base_modules, contributed_modules
from gluon.fileutils import readlines_file
from glob import glob
import fnmatch
import os
import shutil
import sys
import re
import zipfile
if len(sys.argv) != 2 or not os.path.isfile('web2py.py'):
print USAGE
sys.exit(1)
BUILD_MODE = sys.argv[1]
if not BUILD_MODE in ('py2exe', 'bbfreeze'):
print USAGE
sys.exit(1)
def unzip(source_filename, dest_dir):
with zipfile.ZipFile(source_filename) as zf:
zf.extractall(dest_dir)
#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
def recursive_zip(zipf, directory, folder=""):
for item in os.listdir(directory):
if os.path.isfile(os.path.join(directory, item)):
zipf.write(os.path.join(directory, item), folder + os.sep + item)
elif os.path.isdir(os.path.join(directory, item)):
recursive_zip(
zipf, os.path.join(directory, item), folder + os.sep + item)
#read web2py version from VERSION file
web2py_version_line = readlines_file('VERSION')[0]
#use regular expression to get just the version number
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
web2py_version = v_re.search(web2py_version_line).group(0)
#pull in preferences from config file
import ConfigParser
Config = ConfigParser.ConfigParser()
Config.read('setup_exe.conf')
remove_msft_dlls = Config.getboolean("Setup", "remove_microsoft_dlls")
copy_apps = Config.getboolean("Setup", "copy_apps")
copy_site_packages = Config.getboolean("Setup", "copy_site_packages")
copy_scripts = Config.getboolean("Setup", "copy_scripts")
make_zip = Config.getboolean("Setup", "make_zip")
zip_filename = Config.get("Setup", "zip_filename")
remove_build_files = Config.getboolean("Setup", "remove_build_files")
include_gevent = Config.getboolean("Setup", "include_gevent")
# Python base version
python_version = sys.version_info[:3]
if BUILD_MODE == 'py2exe':
import py2exe
setup(
console=[{'script':'web2py.py',
'icon_resources': [(0, 'extras/icons/web2py.ico')]
}],
windows=[{'script':'web2py.py',
'icon_resources': [(1, 'extras/icons/web2py.ico')],
'dest_base':'web2py_no_console' # MUST NOT be just 'web2py' otherwise it overrides the standard web2py.exe
}],
name="web2py",
version=web2py_version,
description="web2py web framework",
author="Massimo DiPierro",
license="LGPL v3",
data_files=[
'ABOUT',
'LICENSE',
'VERSION'
],
options={'py2exe': {
'packages': contributed_modules,
'includes': base_modules,
}},
)
#py2exe packages lots of duplicates in the library.zip, let's save some space
library_temp_dir = os.path.join('dist', 'library_temp')
library_zip_archive = os.path.join('dist', 'library.zip')
os.makedirs(library_temp_dir)
unzip(library_zip_archive, library_temp_dir)
os.unlink(library_zip_archive)
zipl = zipfile.ZipFile(library_zip_archive, "w", compression=zipfile.ZIP_DEFLATED)
recursive_zip(zipl, library_temp_dir)
zipl.close()
shutil.rmtree(library_temp_dir)
print "web2py binary successfully built"
elif BUILD_MODE == 'bbfreeze':
modules = base_modules + contributed_modules
from bbfreeze import Freezer
f = Freezer(distdir="dist", includes=(modules))
f.addScript("web2py.py")
#to make executable without GUI we need this trick
shutil.copy("web2py.py", "web2py_no_console.py")
f.addScript("web2py_no_console.py", gui_only=True)
if include_gevent:
#fetch the gevented webserver script and copy to root
gevented_webserver = os.path.join("handlers", "web2py_on_gevent.py")
shutil.copy(gevented_webserver, "web2py_on_gevent.py")
f.addScript("web2py_on_gevent.py")
f.setIcon('extras/icons/web2py.ico')
f() # starts the freezing process
os.unlink("web2py_no_console.py")
if include_gevent:
os.unlink("web2py_on_gevent.py")
#add data_files
for req in ['ABOUT', 'LICENSE', 'VERSION']:
shutil.copy(req, os.path.join('dist', req))
print "web2py binary successfully built"
try:
os.unlink('storage.sqlite')
except:
pass
#This need to happen after bbfreeze is run because Freezer() deletes distdir before starting!
if python_version > (2,5):
# Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52
try:
shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/Microsoft.VC90.CRT/')
except:
print "You MUST copy Microsoft.VC90.CRT folder into the archive"
def copy_folders(source, destination):
"""Copy files & folders from source to destination (within dist/)"""
if os.path.exists(os.path.join('dist', destination)):
shutil.rmtree(os.path.join('dist', destination))
shutil.copytree(os.path.join(source), os.path.join('dist', destination))
#should we remove Windows OS dlls user is unlikely to be able to distribute
if remove_msft_dlls:
print "Deleted Microsoft files not licensed for open source distribution"
print "You are still responsible for making sure you have the rights to distribute any other included files!"
#delete the API-MS-Win-Core DLLs
for f in glob('dist/API-MS-Win-*.dll'):
os.unlink(f)
#then delete some other files belonging to Microsoft
other_ms_files = ['KERNELBASE.dll', 'MPR.dll', 'MSWSOCK.dll',
'POWRPROF.dll']
for f in other_ms_files:
try:
os.unlink(os.path.join('dist', f))
except:
print "unable to delete dist/" + f
#Should we include applications?
if copy_apps:
copy_folders('applications', 'applications')
print "Your application(s) have been added"
else:
#only copy web2py's default applications
copy_folders('applications/admin', 'applications/admin')
copy_folders('applications/welcome', 'applications/welcome')
copy_folders('applications/examples', 'applications/examples')
print "Only web2py's admin, examples & welcome applications have been added"
copy_folders('extras', 'extras')
copy_folders('examples', 'examples')
copy_folders('handlers', 'handlers')
#should we copy project's site-packages into dist/site-packages
if copy_site_packages:
#copy site-packages
copy_folders('site-packages', 'site-packages')
else:
#no worries, web2py will create the (empty) folder first run
print "Skipping site-packages"
#should we copy project's scripts into dist/scripts
if copy_scripts:
#copy scripts
copy_folders('scripts', 'scripts')
else:
#no worries, web2py will create the (empty) folder first run
print "Skipping scripts"
#should we create a zip file of the build?
if make_zip:
#create a web2py folder & copy dist's files into it
shutil.copytree('dist', 'zip_temp/web2py')
#create zip file
zipf = zipfile.ZipFile(zip_filename + ".zip",
"w", compression=zipfile.ZIP_DEFLATED)
# just temp so the web2py directory is included in our zip file
path = 'zip_temp'
# leave the first folder as None, as path is root.
recursive_zip(zipf, path)
zipf.close()
shutil.rmtree('zip_temp')
print "Your Windows binary version of web2py can be found in " + \
zip_filename + ".zip"
print "You may extract the archive anywhere and then run web2py/web2py.exe"
#should py2exe build files be removed?
if remove_build_files:
if BUILD_MODE == 'py2exe':
shutil.rmtree('build')
shutil.rmtree('deposit')
shutil.rmtree('dist')
print "build files removed"
#final info
if not make_zip and not remove_build_files:
print "Your Windows binary & associated files can also be found in /dist"
print "Finished!"
print "Enjoy web2py " + web2py_version_line

View File

@@ -0,0 +1,52 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['web2py.py'],
pathex=['.'],
binaries=[('/System/Library/Frameworks/Tk.framework/Tk', 'tk'), ('/System/Library/Frameworks/Tcl.framework/Tcl', 'tcl')],
datas=[],
hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'cProfile', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imaplib', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
'psycopg2', 'psycopg2._ipaddress', 'psycopg2._json', 'psycopg2._range', 'psycopg2.extensions', 'psycopg2.extras', 'psycopg2.sql',
'psycopg2.tz', 'pyodbc', 'python-ldap', 'rlcompleter', 'sched', 'site', 'smtplib', 'sqlite3', 'sqlite3.dbapi2', 'sqlite3.dump', 'timeit', 'tkinter',
'tkinter.commondialog', 'tkinter.constants', 'tkinter.messagebox', 'uuid', 'win32evtlogutil', 'wsgiref',
'wsgiref.handlers', 'wsgiref.headers', 'wsgiref.simple_server', 'wsgiref.util', 'xml.dom', 'xml.dom.NodeFilter', 'xml.dom.domreg',
'xml.dom.expatbuilder', 'xml.dom.minicompat', 'xml.dom.minidom', 'xml.dom.pulldom', 'xml.dom.xmlbuilder', 'xmlrpc.server'],
hookspath=[],
runtime_hooks=[],
excludes=['gluon'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='web2py',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False,
icon='extras/icons/web2py.icns')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='web2py')
app = BUNDLE(coll,
name='web2py.app',
icon='extras/icons/web2py.icns',
bundle_identifier=None,
info_plist={
'NSPrincipleClass': 'NSApplication',
'NSAppleScriptEnabled': False})

View File

@@ -0,0 +1,44 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['web2py.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
'psycopg2', 'psycopg2._ipaddress', 'psycopg2._json', 'psycopg2._range', 'psycopg2.extensions', 'psycopg2.extras', 'psycopg2.sql',
'psycopg2.tz', 'pyodbc', 'python-ldap', 'rlcompleter', 'sched', 'site', 'smtplib', 'sqlite3', 'sqlite3.dbapi2', 'sqlite3.dump', 'timeit', 'tkinter',
'tkinter.commondialog', 'tkinter.constants', 'tkinter.messagebox', 'uuid', 'win32con', 'win32evtlogutil', 'winerror', 'wsgiref',
'wsgiref.handlers', 'wsgiref.headers', 'wsgiref.simple_server', 'wsgiref.util', 'xml.dom', 'xml.dom.NodeFilter', 'xml.dom.domreg',
'xml.dom.expatbuilder', 'xml.dom.minicompat', 'xml.dom.minidom', 'xml.dom.pulldom', 'xml.dom.xmlbuilder', 'xmlrpc.server'],
hookspath=[],
runtime_hooks=[],
excludes=['gluon'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='web2py',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True , icon='extras\\icons\\web2py.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='web2py')

View File

@@ -0,0 +1,44 @@
# -*- mode: python -*-
block_cipher = None
a = Analysis(['web2py.py'],
pathex=['.'],
binaries=[],
datas=[],
hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
'psycopg2', 'psycopg2._ipaddress', 'psycopg2._json', 'psycopg2._range', 'psycopg2.extensions', 'psycopg2.extras', 'psycopg2.sql',
'psycopg2.tz', 'pyodbc', 'python-ldap', 'rlcompleter', 'sched', 'site', 'smtplib', 'sqlite3', 'sqlite3.dbapi2', 'sqlite3.dump', 'timeit', 'tkinter',
'tkinter.commondialog', 'tkinter.constants', 'tkinter.messagebox', 'uuid', 'win32con', 'win32evtlogutil', 'winerror', 'wsgiref',
'wsgiref.handlers', 'wsgiref.headers', 'wsgiref.simple_server', 'wsgiref.util', 'xml.dom', 'xml.dom.NodeFilter', 'xml.dom.domreg',
'xml.dom.expatbuilder', 'xml.dom.minicompat', 'xml.dom.minidom', 'xml.dom.pulldom', 'xml.dom.xmlbuilder', 'xmlrpc.server'],
hookspath=[],
runtime_hooks=[],
excludes=['gluon'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='web2py_no_console',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False , icon='extras\\icons\\web2py.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='web2py_no_console')

12
fabfile.py vendored
View File

@@ -27,7 +27,7 @@ def create_user(username):
run('EDITOR="cp /tmp/sudoers.new" visudo')
uncomment('~%s/.bashrc' % username, '#force_color_prompt=yes')
def install_web2py():
def install_web2py():
"""fab -H username@host install_web2py"""
sudo('wget https://raw.githubusercontent.com/web2py/web2py/master/scripts/%s' % INSTALL_SCRIPT)
sudo('chmod +x %s' % INSTALL_SCRIPT)
@@ -119,7 +119,7 @@ def deploy(appname=None, all=False):
os.unlink(zipfile)
backup = mkdir_or_backup(appname)
if all=='all' or not backup:
local('zip -r _update.zip * -x *~ -x .* -x \#* -x *.bak -x *.bak2')
else:
@@ -131,10 +131,10 @@ def deploy(appname=None, all=False):
sudo('unzip -o /tmp/_update.zip')
sudo('chown -R www-data:www-data *')
sudo('echo "%s" > DATE_DEPLOYMENT' % now)
finally:
sudo('rm /tmp/_update.zip')
if backup:
print 'TO RESTORE: fab restore:%s' % backup
@@ -154,10 +154,10 @@ def deploynobackup(appname=None):
sudo('unzip -o /tmp/_update.zip')
sudo('chown -R www-data:www-data *')
sudo('echo "%s" > DATE_DEPLOYMENT' % now)
finally:
sudo('rm /tmp/_update.zip')
def restore(backup):
"""fab -H username@host restore:backupfilename"""
appname = backup.split('/')[-1].split('.')[0]

View File

@@ -36,7 +36,7 @@ REGEX_EXTEND = r"""^\s*(?P<all>\{\{\s*extend\s+['"](?P<name>[^'"]+)['"]\s*\}\})"
REGEX_INCLUDE = r"""(?P<all>\{\{\s*include\s+['"](?P<name>[^'"]+)['"]\s*\}\})"""
# TODO: swap arguments, let first ('r' or whatever) be mandatory
# TODO: swap arguments, let first ('r' or whatever) be mandatory
def apath(path='', r=None):
"""Builds a path inside an application folder

View File

@@ -437,7 +437,7 @@ def compile_views(folder, skip_failed_views=False):
"""
path = pjoin(folder, 'views')
failed_views = []
for fname in listdir(path, REGEX_VIEW_PATH):
for fname in listdir(path, REGEX_VIEW_PATH, followlinks=True):
try:
data = parse_template(fname, path)
except Exception as e:
@@ -462,7 +462,7 @@ def compile_models(folder):
Compiles all the models in the application specified by `folder`
"""
path = pjoin(folder, 'models')
for fname in listdir(path, REGEX_MODEL_PATH):
for fname in listdir(path, REGEX_MODEL_PATH, followlinks=True):
data = read_file(pjoin(path, fname))
modelfile = 'models.'+fname.replace(os.sep, '.')
filename = pjoin(folder, 'compiled', modelfile)
@@ -487,7 +487,7 @@ def compile_controllers(folder):
Compiles all the controllers in the application specified by `folder`
"""
path = pjoin(folder, 'controllers')
for fname in listdir(path, REGEX_CONTROLLER):
for fname in listdir(path, REGEX_CONTROLLER, followlinks=True):
data = read_file(pjoin(path, fname))
exposed = find_exposed_functions(data)
for function in exposed:

View File

@@ -23,9 +23,15 @@ for the benefit of code maintainers/developers:
'--run_system_tests')
- remember to allow the '-' too as word separator (e.g.
'--run-system-tests') but do not use this form on help
(add the minus version of the option to _omitted_opts
to hide it in usage help)
- prefer short names on help messages, instead use
all options names in warning/error messages (e.g.
'-R/--run requires -S/--shell')
Notice that options must be included into opt_map dictionary
(defined in parse_args function) to be available in
configuration file.
"""
from __future__ import print_function
@@ -90,11 +96,12 @@ def console(version):
# most of the options but do not show both versions on help
_omitted_opts = ('--add-options', '--errors-to-console',
'--no-banner', '--log-level', '--no-gui', '--import-models',
'--force-migrate',
'--server-name', '--server-key', '--server-cert', '--ca-cert',
'--pid-filename', '--log-filename', '--min-threads',
'--max-threads', '--request-queue-size', '--socket-timeout',
'--profiler-dir', '--with-scheduler', '--with-cron',
'--soft-cron', '--cron-run',
'--cron-threads', '--soft-cron', '--cron-run',
'--run-doctests', '--run-system-tests', '--with-coverage')
_hidden_options = _omitted_opts + tuple(deprecated_opts.keys())
@@ -259,6 +266,17 @@ web2py will attempt to run a GUI to ask for it when starting the web server
'(default is %(default)s), see -S above. NOTE: when the APP_ENV '
'argument of -S include a controller c automatic import of '
'models is always enabled')
g.add_argument('--fake_migrate',
default=False,
action='store_true',
help=
'force DAL to fake migrate all tables; '
'monkeypatch in the DAL class to force _fake_migrate=True')
g.add_argument('--force_migrate', '--force-migrate',
default=False,
action='store_true', help=
'force DAL to migrate all tables that should be migrated when enabled; '
'monkeypatch in the DAL class to force _migrate_enabled=True')
g.add_argument('-R', '--run',
type=existing_file,
metavar='PYTHON_FILE', help=
@@ -446,6 +464,19 @@ web2py will attempt to run a GUI to ask for it when starting the web server
'only, the default behaviour is to read the crontab for all of the '
'installed applications. NOTE: this option can be used multiple '
'times to build the list of crontabs to be processed by cron')
def positive_int(v, err_label='value'):
try:
iv = int(v)
if iv <= 0: raise ValueError()
return iv
except ValueError:
pass
raise argparse.ArgumentTypeError("bad %s %s" % (err_label, v))
def cron_threads(v):
return positive_int(v, err_label='cron_threads')
g.add_argument('--cron_threads', '--cron-threads',
type=cron_threads, metavar='NUM',
help='maximum number of cron threads (5)')
g.add_argument('--soft_cron', '--soft-cron',
'--softcron', # deprecated
default=False,
@@ -658,6 +689,7 @@ def parse_args(parser, cli_args, deprecated_opts, integer_log_level,
'bpython': store_true,
'plain': store_true,
'import_models': store_true,
'force_migrate': store_true,
'run': str_or_default,
'args': list_or_default,
# web server options
@@ -682,6 +714,7 @@ def parse_args(parser, cli_args, deprecated_opts, integer_log_level,
# cron options
'with_cron': store_true,
'crontab': list_or_default,
'cron_threads': str_or_default,
'soft_cron': store_true,
'cron_run': store_true,
# test options

View File

@@ -118,7 +118,7 @@ def saml2_handler(session, request, config_filename = None, entityid = None):
elif request.env.request_method == 'POST':
binding = BINDING_HTTP_POST
if not request.vars.SAMLResponse:
req_id, req = client.create_authn_request(destination, binding=binding)
req_id, req = client.create_authn_request(destination, binding=BINDING_HTTP_POST)
relay_state = web2py_uuid().replace('-','')
session.saml_outstanding_queries = {req_id: request.url}
session.saml_req_id = req_id

View File

@@ -18,14 +18,14 @@ regex_title = re.compile('^#{1} (?P<t>[^\n]+)', re.M)
regex_maps = [
(re.compile('[ \t\r]+\n'), '\n'),
(re.compile('\*\*(?P<t>[^\s\*]+( +[^\s\*]+)*)\*\*'), '{\\\\bf \g<t>}'),
(re.compile("''(?P<t>[^\s']+( +[^\s']+)*)''"), '{\\it \g<t>}'),
(re.compile("''(?P<t>[^\s']+( +[^\s']+)*)''"), '{\\\it \g<t>}'),
(re.compile('^#{5,6}\s*(?P<t>[^\n]+)', re.M), '\n\n{\\\\bf \g<t>}\n'),
(re.compile('^#{4}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\subsubsection{\g<t>}\n'),
(re.compile('^#{3}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\subsection{\g<t>}\n'),
(re.compile('^#{2}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\section{\g<t>}\n'),
(re.compile('^#{4}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\\subsubsection{\g<t>}\n'),
(re.compile('^#{3}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\\subsection{\g<t>}\n'),
(re.compile('^#{2}\s*(?P<t>[^\n]+)', re.M), '\n\n\\\\goodbreak\\\section{\g<t>}\n'),
(re.compile('^#{1}\s*(?P<t>[^\n]+)', re.M), ''),
(re.compile('^\- +(?P<t>.*)', re.M), '\\\\begin{itemize}\n\\item \g<t>\n\\end{itemize}'),
(re.compile('^\+ +(?P<t>.*)', re.M), '\\\\begin{itemize}\n\\item \g<t>\n\\end{itemize}'),
(re.compile('^\- +(?P<t>.*)', re.M), '\\\\begin{itemize}\n\\\item \g<t>\n\\\end{itemize}'),
(re.compile('^\+ +(?P<t>.*)', re.M), '\\\\begin{itemize}\n\\\item \g<t>\n\\\end{itemize}'),
(re.compile('\\\\end\{itemize\}\s+\\\\begin\{itemize\}'), '\n'),
(re.compile('\n\s+\n'), '\n\n')]
regex_table = re.compile('^\-{4,}\n(?P<t>.*?)\n\-{4,}(:(?P<c>\w+))?\n', re.M | re.S)
@@ -97,7 +97,7 @@ def render(text,
text = latex_escape(text, pound=False)
texts = text.split('## References', 1)
text = regex_anchor.sub('\\label{\g<t>}', texts[0])
text = regex_anchor.sub('\\\label{\g<t>}', texts[0])
if len(texts) == 2:
text += '\n\\begin{thebibliography}{999}\n'
text += regex_bibitem.sub('\n\\\\bibitem{\g<t>}', texts[1])
@@ -145,7 +145,7 @@ def render(text,
text = regex_image_width.sub(sub, text)
text = regex_image.sub(sub, text)
text = regex_link.sub('{\\\\footnotesize\\href{\g<k>}{\g<t>}}', text)
text = regex_link.sub('{\\\\footnotesize\\\href{\g<k>}{\g<t>}}', text)
text = regex_commas.sub('\g<t>', text)
text = regex_noindent.sub('\n\\\\noindent \g<t>', text)

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf8 -*-
# Plural-Forms for fa (Persian)

File diff suppressed because one or more lines are too long

0
gluon/contrib/pymysql/README.rst Executable file → Normal file
View File

View File

@@ -64,7 +64,7 @@ class WebClient(object):
headers=headers, method='GET')
def post(self, url, data=None, cookies=None,
headers=None, auth=None, method='auto'):
headers=None, auth=None, method='auto', charset='utf-8'):
self.url = self.app + url
# if this POST form requires a postback do it
@@ -147,7 +147,11 @@ class WebClient(object):
else:#python2.5
self.status = None
self.text = to_native(self.response.read())
self.text = self.response.read()
if charset:
if charset == 'auto':
charset = self.response.headers.getparam('charset')
self.text = to_native(self.text, charset)
# In PY3 self.response.headers are case sensitive
self.headers = dict()
for h in self.response.headers:
@@ -173,9 +177,10 @@ class WebClient(object):
self.sessions[name] = value
# find all forms and formkeys in page
self.forms = {}
for match in FORM_REGEX.finditer(to_native(self.text)):
self.forms[match.group('formname')] = match.group('formkey')
if charset:
self.forms = {}
for match in FORM_REGEX.finditer(self.text):
self.forms[match.group('formname')] = match.group('formkey')
# log this request
self.history.append((self.method, self.url, self.status, self.time))

View File

@@ -148,7 +148,8 @@ def listdir(path,
add_dirs=False,
sort=True,
maxnum=None,
exclude_content_from=None
exclude_content_from=None,
followlinks=False
):
"""
Like `os.listdir()` but you can specify a regex pattern to filter files.
@@ -164,7 +165,7 @@ def listdir(path,
n = 0
regex = re.compile(expression)
items = []
for (root, dirs, files) in os.walk(path, topdown=True):
for (root, dirs, files) in os.walk(path, topdown=True, followlinks=followlinks):
for dir in dirs[:]:
if dir.startswith('.'):
dirs.remove(dir)

View File

@@ -392,6 +392,12 @@ class lazyT(object):
def __eq__(self, other):
return str(self) == str(other)
def __lt__(self, other):
return str(self) < str(other)
def __gt__(self, other):
return str(self) > str(other)
def __ne__(self, other):
return str(self) != str(other)

View File

@@ -334,7 +334,7 @@ class SimplePool(object):
self.running = set()
def grow(self, size):
if size > self.size:
if size and size > self.size:
self.size = size
def start(self, t):
@@ -380,8 +380,14 @@ class SimplePool(object):
_dancer = SimplePool(5, worker_cls=SoftWorker)
def dancer_size(size):
_dancer.grow(size)
_launcher = SimplePool(5)
def launcher_size(size):
_launcher.grow(size)
def crondance(applications_parent, ctype='hard', startup=False, apps=None):
"""
Does the periodic job of cron service: read the crontab(s) and launch

View File

@@ -204,7 +204,7 @@ def url_out(request, environ, application, controller, function,
if host is True or (host is None and (scheme or port is not None)):
host = request.env.http_host
if not scheme or scheme is True:
scheme = request.env.get('wsgi_url_scheme', 'http').lower() if request else 'http'
scheme = 'https' if request and request.is_https else 'http'
if host:
host_port = host if not port else host.split(':', 1)[0] + ':%s' % port
url = '%s://%s%s' % (scheme, host_port, url)

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,6 @@ from gluon.restricted import RestrictedError
from gluon.globals import Request, Response, Session
from gluon.storage import Storage, List
from gluon.admin import w2p_unpack
from gluon.fileutils import create_missing_app_folders
from pydal.base import BaseAdapter
from gluon._compat import iteritems, ClassType, PY2
@@ -167,9 +166,6 @@ def env(
path_info = '%s?%s' % (path_info, '&'.join(vars))
request.env.path_info = path_info
# Ensure necessary folders are created
create_missing_app_folders(request)
# Monkey patch so credentials checks pass.
def check_credentials(request, other_application='admin'):
@@ -220,7 +216,9 @@ def run(
bpython=False,
python_code=None,
cron_job=False,
scheduler_job=False):
scheduler_job=False,
force_migrate=False,
fake_migrate=False):
"""
Start interactive shell or run Python script (startfile) in web2py
controller environment. appname is formatted like:
@@ -250,6 +248,21 @@ def run(
os.mkdir(adir)
fileutils.create_app(adir)
if force_migrate:
c = 'appadmin' # Load all models (hack already used for appadmin controller)
import_models = True
from gluon.dal import DAL
orig_init = DAL.__init__
def custom_init(*args, **kwargs):
kwargs['migrate_enabled'] = True
kwargs['migrate'] = True
kwargs['fake_migrate'] = fake_migrate
logger.info('Forcing migrate_enabled=True')
orig_init(*args, **kwargs)
DAL.__init__ = custom_init
if c:
import_models = True
extra_request = {}
@@ -265,7 +278,7 @@ def run(
if c:
pyfile = os.path.join('applications', a, 'controllers', c + '.py')
pycfile = os.path.join('applications', a, 'compiled',
"controllers_%s_%s.pyc" % (c, f))
"controllers.%s.%s.pyc" % (c, f))
if ((cron_job and os.path.isfile(pycfile))
or not os.path.isfile(pyfile)):
exec(read_pyc(pycfile), _env)
@@ -303,6 +316,15 @@ def run(
print(traceback.format_exc())
if import_models:
BaseAdapter.close_all_instances('rollback')
elif force_migrate:
try:
execfile("scripts/migrator.py", _env)
if import_models:
BaseAdapter.close_all_instances('commit')
except:
print(traceback.format_exc())
if import_models:
BaseAdapter.close_all_instances('rollback')
else:
if not plain:
if bpython:

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