Compare commits
121 Commits
fix-missin
...
issue-2274
| Author | SHA1 | Date | |
|---|---|---|---|
| 2efa54a2d7 | |||
| f5cdf17c48 | |||
| 3272755fea | |||
| 022ddd49c4 | |||
| 8f84b5df34 | |||
| 223755d894 | |||
| 981254ec61 | |||
| ce917feb7e | |||
| bad0d0b26b | |||
| 517f88891f | |||
|
|
c44c2b06d3 | ||
|
|
eda8277fbd | ||
|
|
21afb0933e | ||
|
|
90fc9ea622 | ||
|
|
61976e7c3a | ||
|
|
a0eac8fce7 | ||
|
|
7038758960 | ||
|
|
77bcda3f3e | ||
|
|
92bb2c2241 | ||
|
|
f7b3abdc89 | ||
|
|
8f98994423 | ||
|
|
cf91145db0 | ||
|
|
dce5fbb472 | ||
|
|
40485ecd88 | ||
|
|
78b529bb92 | ||
|
|
32fc729cdc | ||
|
|
5bcf34aba5 | ||
|
|
e4845aeef6 | ||
|
|
1ce316609a | ||
|
|
c95b4e43d7 | ||
|
|
501d0b8a9b | ||
|
|
2b30157cce | ||
|
|
cbbf793841 | ||
|
|
89c392d224 | ||
|
|
91fd094790 | ||
|
|
0f638f9cdf | ||
|
|
d84cbf8763 | ||
|
|
6af98ac7bc | ||
|
|
f91eaa84e8 | ||
|
|
d17572fb10 | ||
|
|
1dec1b4358 | ||
|
|
4191d4c48c | ||
|
|
75491fb273 | ||
|
|
b7202df0b9 | ||
|
|
529444cda7 | ||
|
|
12d1ca739d | ||
|
|
af151783c6 | ||
|
|
4a838c2c14 | ||
|
|
42181ca263 | ||
|
|
8b55025cb3 | ||
|
|
e0fc657b8e | ||
|
|
619af453a6 | ||
| 179edb659a | |||
|
|
019295e1d1 | ||
|
|
60c68164f3 | ||
| b4c2ee8304 | |||
| cb0c2e6edd | |||
|
|
37d1fca32c | ||
|
|
251314ceb8 | ||
|
|
a23a068d40 | ||
|
|
726d664292 | ||
|
|
2c364c2079 | ||
|
|
8faa5e2a82 | ||
|
|
6fddca4e4f | ||
|
|
0557fe9c58 | ||
|
|
5945edebfd | ||
|
|
558afd886c | ||
|
|
1bc8ea6838 | ||
|
|
24d970a943 | ||
|
|
e66dda8641 | ||
|
|
320a28d564 | ||
|
|
9db1c6b0b0 | ||
|
|
f307fe7d56 | ||
|
|
f845497479 | ||
|
|
ab257031b5 | ||
|
|
64c66000fa | ||
|
|
c247e740a2 | ||
|
|
3fe797ce60 | ||
|
|
f1f65275f7 | ||
|
|
2989beae02 | ||
|
|
2a7b16d61f | ||
|
|
14c1b3e400 | ||
|
|
8823cde350 | ||
|
|
3e09add351 | ||
|
|
7385904f97 | ||
|
|
c82f0ba619 | ||
|
|
409a499dd0 | ||
|
|
9d52f5498e | ||
|
|
0fe9d0aa34 | ||
|
|
89a4da0594 | ||
|
|
f2e95d1bb6 | ||
|
|
273ebb9a70 | ||
|
|
1bfffc4f12 | ||
|
|
6d7900be82 | ||
|
|
dadf363122 | ||
|
|
ce25d8dc31 | ||
|
|
1c5b2d7fce | ||
|
|
9b831a64c0 | ||
|
|
e6e92b43c9 | ||
|
|
c02ee6a5c0 | ||
|
|
22f95677d9 | ||
|
|
4fd44ff682 | ||
|
|
da253f7ba8 | ||
|
|
a24926ad6f | ||
| c6837a9e73 | |||
|
|
bd892556e1 | ||
|
|
99d3d1d465 | ||
| b365d12a2e | |||
| 4455fa48c4 | |||
| 96534ccf30 | |||
|
|
7a225da44e | ||
|
|
3f15d1ceb8 | ||
|
|
9769314f01 | ||
|
|
b79b5951f8 | ||
|
|
7b9d2c87c2 | ||
|
|
c03c962778 | ||
|
|
ca7b676591 | ||
|
|
7a0e113a5f | ||
|
|
7e30be377d | ||
|
|
f9db6a8306 | ||
|
|
50878f33bd |
12
.travis.yml
@@ -1,27 +1,22 @@
|
||||
language: python
|
||||
|
||||
sudo: required
|
||||
|
||||
cache: pip
|
||||
|
||||
dist: "xenial"
|
||||
dist: "bionic"
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- redis-server
|
||||
|
||||
python:
|
||||
- '2.7'
|
||||
- '3.5'
|
||||
- '3.6'
|
||||
- '3.6-dev'
|
||||
- '3.7'
|
||||
- '3.7-dev'
|
||||
- 'pypy3.5'
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: '3.6-dev'
|
||||
- python: '3.7-dev'
|
||||
- python: 'pypy3.5'
|
||||
|
||||
install:
|
||||
- pip install -e .
|
||||
@@ -29,6 +24,7 @@ install:
|
||||
before_script:
|
||||
- pip install coverage
|
||||
- pip install codecov
|
||||
- pip install redis
|
||||
|
||||
before_install:
|
||||
- mysql -e 'create database pydal;'
|
||||
|
||||
@@ -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
0
applications/admin/languages/bg.py
Executable file → Normal file
0
applications/admin/languages/cs.py
Executable file → Normal file
0
applications/admin/languages/en.py
Executable file → Normal file
0
applications/admin/languages/es.py
Executable file → Normal file
0
applications/admin/languages/fr.py
Executable file → Normal file
0
applications/admin/languages/he.py
Executable file → Normal file
0
applications/admin/languages/it.py
Executable file → Normal file
0
applications/admin/languages/ja.py
Executable file → Normal file
0
applications/admin/languages/my-mm.py
Executable file → Normal file
0
applications/admin/languages/nl.py
Executable file → Normal file
0
applications/admin/languages/pl.py
Executable file → Normal file
0
applications/admin/languages/plural-en.py
Executable file → Normal file
0
applications/admin/languages/plural-ru.py
Executable file → Normal file
0
applications/admin/languages/plural-uk.py
Executable file → Normal file
0
applications/admin/languages/pt-br.py
Executable file → Normal file
0
applications/admin/languages/pt.py
Executable file → Normal file
0
applications/admin/languages/ro.py
Executable file → Normal file
0
applications/admin/languages/ru.py
Executable file → Normal file
0
applications/admin/languages/sl.py
Executable file → Normal file
0
applications/admin/languages/sr-cr.py
Executable file → Normal file
0
applications/admin/languages/sr-lt.py
Executable file → Normal file
0
applications/admin/languages/tr.py
Executable file → Normal file
0
applications/admin/languages/uk.py
Executable file → Normal file
0
applications/admin/languages/zh-tw.py
Executable file → Normal file
0
applications/admin/languages/zh.py
Executable file → Normal 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
0
applications/admin/static/js/jqueryMultiSelect.js
vendored
Executable file → Normal file
@@ -284,6 +284,7 @@
|
||||
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
||||
if (redirect !== null) {
|
||||
window.location = redirect;
|
||||
window.location.reload(); // Force reload even with anchors
|
||||
}
|
||||
/* run this here only if this Ajax request is NOT for a web2py component. */
|
||||
if (xhr.getResponseHeader('web2py-component-content') === null) {
|
||||
|
||||
0
applications/admin/static/plugin_jqmobile/images/ajax-loader.png
Executable file → Normal file
|
Before Width: | Height: | Size: 366 B After Width: | Height: | Size: 366 B |
0
applications/admin/static/plugin_jqmobile/images/icons-18-black.png
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
0
applications/admin/static/plugin_jqmobile/images/icons-18-white.png
Executable file → Normal file
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
0
applications/admin/static/plugin_jqmobile/images/icons-36-black.png
Executable file → Normal file
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
0
applications/admin/static/plugin_jqmobile/images/icons-36-white.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
0
applications/admin/static/plugin_multiselect/jquery.multi-select.js
Executable file → Normal file
0
applications/admin/static/plugin_multiselect/multi-select.css
Executable file → Normal file
0
applications/admin/static/plugin_multiselect/switch.png
Executable file → Normal file
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -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)))
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')),
|
||||
|
||||
@@ -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'))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div>
|
||||
{{=get_content('main')}}
|
||||
<center>
|
||||
<iframe src="//player.vimeo.com/hubnut/album/3016728?color=ff6600&background=ffffff&slideshow=1&video_title=1&video_byline=1" width="400" height="300" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
|
||||
<iframe src="https://player.vimeo.com/video/104800778?color=ff6600&background=ffffff&slideshow=1&video_title=1&video_byline=1" width="400" height="300" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
|
||||
</center>
|
||||
{{=get_content('official')}}
|
||||
{{=get_content('community')}}
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
<table class="twothirds">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>For Normal Users</th>
|
||||
<th>For Normal Users (Py3)</th>
|
||||
<th>For Legacy Users (Py2)</th>
|
||||
<th>For Testers</th>
|
||||
<th>For Developers</th>
|
||||
</tr>
|
||||
@@ -17,10 +18,13 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win.zip">For Windows</a>
|
||||
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win.zip">Windows binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_win.zip">For Windows</a>
|
||||
<a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win_py2.zip">Windows binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_win.zip">Windows binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded red" href="http://github.com/web2py/web2py/">Git Repository</a>
|
||||
@@ -28,17 +32,25 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx.zip">For Mac</a>
|
||||
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx.zip">Mac binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_osx.zip">For Mac</a>
|
||||
<a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx_py2.zip">Mac binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_osx.zip">Mac binaries</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded" href="http://mdipierro.github.io/web2py/web2py_manual_5th.pdf">Manual</a>
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_src.zip">Source Code</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_src.zip">Source Code</a>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_src.zip">Source Code</a>
|
||||
</td>
|
||||
@@ -48,7 +60,8 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a class="btn btn180 rounded green" href="http://mdipierro.github.io/web2py/web2py_manual_5th.pdf">Manual</a>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn180 rounded" href="https://github.com/web2py/web2py/releases">Change Log</a>
|
||||
@@ -62,13 +75,19 @@
|
||||
</center>
|
||||
|
||||
<p style="text-align:left;">
|
||||
The source code version works on Windows and most Unix systems, including <b>Linux</b>, <b>BSD</b> and <b>Mac</b> . It requires Python 2.6 (no more supported), Python 2.7 (stable) or Python 3.5+ (recommended for new projects) already installed on your system.
|
||||
There are also binary packages for Windows and Mac OS X. They include the Python 2.7 interpreter so you do not need to have it pre-installed.
|
||||
The source code version works on Windows and most Unix systems, including <b>Linux</b>, <b>BSD</b> and <b>Mac</b> . It requires Python 3.5+ (recommended for new projects)
|
||||
or Python 2.7+ (stable, for use with legacy apps) already installed on your system.
|
||||
</p>
|
||||
<p style="text-align:left;">
|
||||
There are also binary packages for Windows and MacOs. They include the Python interpreter version 3.7.4 or 2.7.16, so you do not need to have it pre-installed.
|
||||
</p>
|
||||
|
||||
<h3>Instructions</h3>
|
||||
<p>With the binary packages, after download, just unzip it and then click on web2py.exe (windows) or web2py.app (osx).
|
||||
If you prefer to run it from source with your own Python interpreter alreay installed, type:</p>
|
||||
<p>With the binary packages, after download, just unzip it and then click on web2py.exe (Windows) or web2py (MacOs).</p>
|
||||
<p>Note that on recent MacOs versions (10.12+) you could face problems in running the binary App program, due to the last changes to the security settings.
|
||||
In this case, press the 'control' key + click on downloaded file and then 'open' it (confirm the warnings). Finally move the program in Applications and run it from there.
|
||||
</p>
|
||||
<p> If you prefer to run it from source with your own Python interpreter already installed, type:</p>
|
||||
{{=CODE("python web2py.py", language=None, counter='>', _class='boxCode')}}
|
||||
<p>or for more info type:</p>
|
||||
{{=CODE("python web2py.py -h", language=None, counter='>', _class='boxCode')}}
|
||||
|
||||
@@ -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
0
applications/welcome/languages/ca.py
Executable file → Normal file
0
applications/welcome/languages/cs.py
Executable file → Normal file
0
applications/welcome/languages/en.py
Executable file → Normal file
0
applications/welcome/languages/es.py
Executable file → Normal file
0
applications/welcome/languages/fr-ca.py
Executable file → Normal file
0
applications/welcome/languages/fr.py
Executable file → Normal file
0
applications/welcome/languages/hi.py
Executable file → Normal file
0
applications/welcome/languages/hu.py
Executable file → Normal file
0
applications/welcome/languages/id.py
Executable file → Normal file
0
applications/welcome/languages/it.py
Executable file → Normal file
0
applications/welcome/languages/my-mm.py
Executable file → Normal file
0
applications/welcome/languages/my.py
Executable file → Normal file
0
applications/welcome/languages/nl.py
Executable file → Normal file
0
applications/welcome/languages/pl.py
Executable file → Normal file
0
applications/welcome/languages/plural-cs.py
Executable file → Normal file
0
applications/welcome/languages/plural-en.py
Executable file → Normal file
0
applications/welcome/languages/plural-es.py
Executable file → Normal file
0
applications/welcome/languages/plural-ru.py
Executable file → Normal file
0
applications/welcome/languages/plural-uk.py
Executable file → Normal file
208
applications/welcome/languages/pt-br.py
Executable file → Normal 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
0
applications/welcome/languages/ro.py
Executable file → Normal file
0
applications/welcome/languages/ru.py
Executable file → Normal file
0
applications/welcome/languages/sk.py
Executable file → Normal file
0
applications/welcome/languages/tr.py
Executable file → Normal file
0
applications/welcome/languages/uk.py
Executable file → Normal file
0
applications/welcome/languages/zh-cn.py
Executable file → Normal file
0
applications/welcome/languages/zh-tw.py
Executable file → Normal file
0
applications/welcome/languages/zh.py
Executable file → Normal file
@@ -283,7 +283,11 @@
|
||||
doc.ajaxSuccess(function (e, xhr) {
|
||||
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
||||
if (redirect !== null) {
|
||||
window.location = redirect;
|
||||
if (!redirect.endsWith('#')) {
|
||||
window.location.href = redirect;
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
/* run this here only if this Ajax request is NOT for a web2py component. */
|
||||
if (xhr.getResponseHeader('web2py-component-content') === null) {
|
||||
@@ -335,7 +339,7 @@
|
||||
} else {
|
||||
formData = form.serialize(); // Fallback for older browsers.
|
||||
}
|
||||
web2py.ajax_page('post', url, formData, target, form);
|
||||
web2py.ajax_page('post', url, formData, target);
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
@@ -367,6 +371,24 @@
|
||||
'data': data,
|
||||
'processData': !isFormData,
|
||||
'contentType': contentType,
|
||||
'xhr': function() {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
|
||||
xhr.upload.addEventListener("progress", function(evt) {
|
||||
if (evt.lengthComputable) {
|
||||
var percentComplete = evt.loaded / evt.total;
|
||||
percentComplete = parseInt(percentComplete * 100);
|
||||
web2py.fire(element, 'w2p:uploadProgress', [percentComplete], target);
|
||||
|
||||
if (percentComplete === 100) {
|
||||
web2py.fire(element, 'w2p:uploadComplete', [], target);
|
||||
}
|
||||
|
||||
}
|
||||
}, false);
|
||||
|
||||
return xhr;
|
||||
},
|
||||
'beforeSend': function (xhr, settings) {
|
||||
xhr.setRequestHeader('web2py-component-location', document.location);
|
||||
xhr.setRequestHeader('web2py-component-element', target);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
build: false
|
||||
before_build:
|
||||
- choco install redis-64
|
||||
- redis-server --service-install
|
||||
- redis-server --service-start
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
@@ -26,7 +30,7 @@ init:
|
||||
|
||||
install:
|
||||
- python -m ensurepip
|
||||
- pip install codecov
|
||||
- pip install codecov redis
|
||||
- git submodule update --init --recursive
|
||||
# Check that we have the expected version and architecture for Python
|
||||
- "python --version"
|
||||
|
||||
@@ -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.
|
||||
4
extras/build_web2py/README.md
Normal 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.
|
||||
50
extras/build_web2py/README_mac.md
Normal file
@@ -0,0 +1,50 @@
|
||||
## MacOS binaries
|
||||
|
||||
The MacOS binaries contain Python 3.7.3 (or 2.7.16) 64 bit with all the needed modules and the web2py in the specified version:
|
||||
you don't need anything else to run them on MacOS! After uncompressing the zip file, you just need to click on the web2py icon inside.
|
||||
|
||||
They were produced on MacOS Sierra 10.12.6 + security update 2019.001.
|
||||
|
||||
## Full MacOS build recipe
|
||||
|
||||
1. grab and install the official Python program: we've got version 3.7.3 or 2.7.16 (64 bit). If you've chosen python 2, change pip3
|
||||
with pip, and python3 with python in the following instructions...
|
||||
|
||||
2. Open a terminal, update tools with:
|
||||
|
||||
"python3 -m pip install --upgrade pip"
|
||||
"pip3 install --upgrade setuptools"
|
||||
|
||||
|
||||
3. install PyInstaller with:
|
||||
sudo -H pip3 install pyinstaller (we've got PyInstaller-3.4 )
|
||||
|
||||
4. additional (but not required) packages:
|
||||
(only for python 2: install Homebrew from https://brew.sh/#install , then 'brew install unixodbc' )
|
||||
pip3 install psycopg2-binary = psycopg2-2.7.7
|
||||
pip3 install pyodbc = pyodbc-4.0.26-cp37-cp37m
|
||||
pip3 install python-ldap (on the windows message, accept to install the "Command line developer tools"). Rerun:
|
||||
pip3 install python-ldap
|
||||
|
||||
5. grab latest web2py source from https://mdipierro.pythonanywhere.com/examples/static/web2py_src.zip
|
||||
(you need at least 2.18.3 for needed changes in gluon\admin.py). Open it to uncompress, in this example on Desktop/web2py
|
||||
|
||||
|
||||
6. take the file build_web2py.py and web2py.mac.spec from this folder and place it on the Desktop/web2py folder
|
||||
|
||||
7. edit the file /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/PyInstaller/hooks/hook-_tkinter.py
|
||||
and change one of its line according to https://github.com/pyinstaller/pyinstaller/pull/3830
|
||||
|
||||
8. (optional, for having a full working interactive shell) change the fake site.py module included within the PyInstaller installation
|
||||
with the content of the files web2py.site_37.py or web2py.site_27.py from this folder - see comments inside these files for details
|
||||
|
||||
9. open a terminal, goto Desktop/web2py and run:
|
||||
|
||||
python3 build_web2py.py
|
||||
|
||||
10. if everything is fine, you'll obtain web2py_macos.zip on the Desktop/web2py folder. Inside it, there is the web2py program with
|
||||
both the CMD version and the APP version.
|
||||
|
||||
## Gothca
|
||||
|
||||
Unfortunately, the APP version is still not working - see https://github.com/pyinstaller/pyinstaller/issues/3820 .
|
||||
47
extras/build_web2py/README_win.md
Normal file
@@ -0,0 +1,47 @@
|
||||
## Windows binaries
|
||||
|
||||
The windows binaries contain Python 64 bit version 3.7.3 or 2.7.16 with all the needed modules and the web2py in the specified version.
|
||||
You don't need anything else to run them on Windows.
|
||||
At least on Windows 7, if you get an error stating that "api-ms-win-crt-runtime-l1-1-0.dll is missing" you have only to install the
|
||||
free and official "Visual C++ Redistributable for Visual Studio" as described later
|
||||
|
||||
|
||||
## Full Windows build recipe
|
||||
|
||||
1. get a clean Windows 10 (Windows 10 Professional English build 1809 64 bit, under Virtualbox in our case)
|
||||
2. grab and install the official Python program: we've got version 3.7.3 or 2.7.16, 64 bit
|
||||
(https://www.python.org/ftp/python/3.7.2/python-3.7.2-amd64.exe ) + select "add Python 3.7 to PATH" during its setup if Python 3.
|
||||
For Python 2 you need to manually add the folders for python27 and python27\Scripts to the system path.
|
||||
3. update tools with
|
||||
"python -m pip install --upgrade pip"
|
||||
"pip install --upgrade setuptools"
|
||||
4. download and install python-win32, which is needed for web2py to work with all features enabled
|
||||
(https://github.com/mhammond/pywin32/releases/download/b224/pywin32-224.win-amd64-py3.7.exe)
|
||||
5. grab latest web2py source from https://mdipierro.pythonanywhere.com/examples/static/web2py_src.zip (you need at least 2.18.3 for
|
||||
needed changes in gluon\admin.py). Unzip it in a dedicated folder, in this example C:\web2py - so that you have
|
||||
C:\web2py\web2py.py inside)
|
||||
6. install PyInstaller with:
|
||||
pip install pyinstaller (we've got PyInstaller-3.4.tar.gz )
|
||||
7. download and install the free Microsoft Visual C++ Redistributable per Visual Studio 2017, 64 bit version, from
|
||||
https://aka.ms/vs/15/release/vc_redist.x64.exe
|
||||
8. additional (but not required) packages to work better in the Windows world:
|
||||
pip install psycopg2 = psycopg2-2.7.7-cp37-cp37m-win_amd64.whl
|
||||
pip install pyodbc = pyodbc-4.0.26-cp37-cp37m-win_amd64.whl
|
||||
download the file python_ldap-3.1.0-cp37-cp37m-win_amd64.whl from https://www.lfd.uci.edu/~gohlke/pythonlibs/ and install it from that
|
||||
folder with the command 'pip install python_ldap-3.1.0-cp37-cp37m-win_amd64.whl'
|
||||
|
||||
9. copy build_web2py.py, web2py.win.spec and web2py.win_no_console.spec from this folder to C:\web2py\
|
||||
10. (only for python 2) - due to a PyInstaller bug, you need to manually change the file gluon\rocket.py, line 26, from IS_JYTHON
|
||||
= platform.system() == 'Java' to IS_JYTHON = False
|
||||
11. (optional, for having a full working interactive shell) change the fake site.py module included within the PyInstaller installation
|
||||
with the content of the files web2py.site_37.py or web2py.site_27.py from this folder - see comments inside these files for details
|
||||
12. open a CMD and go to C:\web2py. Run:
|
||||
|
||||
python build_web2py.py
|
||||
|
||||
If everything goes fine, you'll obtain the 64 bit binary build zipped as C:\web2py\web2py_win.zip.
|
||||
If you try to run it in a 32 bit Windows system, you'll correctly get a 'web2py.exe not a valid Win32 application' error message.
|
||||
|
||||
## Gothca:
|
||||
- at least on Windows 7, you can get an error stating that "api-ms-win-crt-runtime-l1-1-0.dll is missing". You can easily resolve it by
|
||||
installing "Visual C++ Redistributable for Visual Studio" described earlier
|
||||
164
extras/build_web2py/build_web2py.py
Normal 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")
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
52
extras/build_web2py/web2py.mac.spec
Normal 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})
|
||||
1980
extras/build_web2py/web2py.site_27.py
Normal file
1653
extras/build_web2py/web2py.site_37.py
Normal file
44
extras/build_web2py/web2py.win.spec
Normal 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')
|
||||
44
extras/build_web2py/web2py.win_no_console.spec
Normal 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
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -803,7 +803,7 @@ class AuthAPI(object):
|
||||
|
||||
# Finally verify the password
|
||||
passfield = settings.password_field
|
||||
password = table_user[passfield].validate(kwargs.get(passfield, ''))[0]
|
||||
password = table_user[passfield].validate(kwargs.get(passfield, ''), None)[0]
|
||||
|
||||
if password == user[passfield]:
|
||||
self.login_user(user)
|
||||
|
||||
@@ -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:
|
||||
@@ -565,7 +565,7 @@ def run_models_in(environment):
|
||||
|
||||
TEST_CODE = r"""
|
||||
def _TEST():
|
||||
import doctest, sys, cStringIO, types, cgi, gluon.fileutils
|
||||
import doctest, sys, cStringIO, types, gluon.fileutils
|
||||
if not gluon.fileutils.check_credentials(request):
|
||||
raise HTTP(401, web2py_error='invalid credentials')
|
||||
stdout = sys.stdout
|
||||
|
||||
@@ -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
|
||||
|
||||
59
gluon/contrib/login_methods/freeipa_auth.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import sys
|
||||
import logging
|
||||
|
||||
try:
|
||||
import ldap
|
||||
|
||||
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
|
||||
except Exception as detail:
|
||||
logging.error('missing ldap, try "pip install python-ldap"')
|
||||
raise detail
|
||||
|
||||
|
||||
def freeipa_auth(server, basedn, group):
|
||||
"""
|
||||
custom module for freeIPA auth in web2py
|
||||
server: freeipa ip
|
||||
base_dn: root of ldap tree containing user & groups
|
||||
group: group authing user has to be a member of
|
||||
|
||||
"""
|
||||
logger = logging.getLogger("web2py.auth.freeipa_auth")
|
||||
|
||||
def freeipa_auth_aux(username, password):
|
||||
if password == "" or username == "":
|
||||
logger.warning("blank username / password not allowed")
|
||||
return False
|
||||
|
||||
bind_user_base = "uid=" + username + ",cn=users," + basedn
|
||||
ldap_filter = "memberof=cn=" + group + ",cn=groups," + basedn
|
||||
|
||||
session = ldap.initialize("ldaps://" + server + ":636")
|
||||
try:
|
||||
session.bind_s(bind_user_base, password)
|
||||
except ldap.LDAPError:
|
||||
import traceback
|
||||
|
||||
logger.warning("[%s] Error in ldap bind" % str(username))
|
||||
logger.debug(traceback.format_exc())
|
||||
return False
|
||||
|
||||
try:
|
||||
result = session.search_s(
|
||||
bind_user_base, ldap.SCOPE_SUBTREE, ldap_filter, ["member"]
|
||||
)
|
||||
session.unbind()
|
||||
except ldap.LDAPError as detail:
|
||||
logger.warning(
|
||||
"ldap_auth: searc %s for %s resulted in %s: %s\n"
|
||||
% (bind_user_base, ldap_filter, exc_type, exc_value)
|
||||
)
|
||||
|
||||
try:
|
||||
if result == list():
|
||||
return False
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
return freeipa_auth_aux
|
||||
@@ -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
|
||||
@@ -150,14 +150,14 @@ class Saml2Auth(object):
|
||||
self.config_file = config_file
|
||||
self.maps = maps
|
||||
|
||||
# URL for redirecting users to when they sign out
|
||||
# URL for redirecting users to when they sign out
|
||||
self.saml_logout_url = logout_url
|
||||
|
||||
# URL to let users change their password in the IDP system
|
||||
self.saml_change_password_url = change_password_url
|
||||
|
||||
# URL to specify an IDP if using federation metadata or an MDQ
|
||||
self.entityid = entityid
|
||||
# URL to specify an IDP if using federation metadata or an MDQ
|
||||
self.entityid = entityid
|
||||
|
||||
def login_url(self, next="/"):
|
||||
d = saml2_handler(current.session, current.request, entityid=self.entityid)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -237,6 +237,7 @@ WRAPPER = """
|
||||
\\usepackage{graphicx}
|
||||
\\usepackage{grffile}
|
||||
\\usepackage[utf8x]{inputenc}
|
||||
\\usepackage{textgreek}
|
||||
\\definecolor{lg}{rgb}{0.9,0.9,0.9}
|
||||
\\definecolor{dg}{rgb}{0.3,0.3,0.3}
|
||||
\\def\\ft{\\small\\tt}
|
||||
|
||||