Compare commits

..

121 Commits

Author SHA1 Message Date
2efa54a2d7 remove useless imports. 2019-12-12 18:03:10 +00:00
f5cdf17c48 fix wrong redis server host. 2019-12-12 15:07:23 +00:00
3272755fea better structure in the redis test class. 2019-12-12 15:00:37 +00:00
022ddd49c4 fixed convertions for compatibility with Python3. Add further testing of redis sessions functions. 2019-12-12 14:37:41 +00:00
8f84b5df34 fix lambda definition. 2019-12-11 19:09:51 +00:00
223755d894 fix missing pip install redis. 2019-12-11 19:06:19 +00:00
981254ec61 add test_redis to gluon init. 2019-12-11 18:55:14 +00:00
ce917feb7e Fix redis_session types for new redis client. Add testing in travis and appveyor.yml 2019-12-11 18:31:44 +00:00
bad0d0b26b Start new testing of redis_session and save redis session table information so we can recover data from redis in the native format. 2019-12-05 19:53:20 +00:00
517f88891f Add a test that simulates redis session usage. 2019-12-05 19:26:40 +00:00
mdipierro
c44c2b06d3 Merge branch 'master' of github.com:web2py/web2py 2019-11-02 23:09:04 -07:00
mdipierro
eda8277fbd removed unwanted import that breaks python3.8 2019-11-02 20:09:47 -07:00
mdipierro
21afb0933e Merge pull request #2270 from tomodachi/freeipa_auth
Add support for freeIPA ldap auth
2019-10-27 21:27:40 -07:00
mdipierro
90fc9ea622 Merge pull request #2269 from bmiklautz/spawn_v2
Fix scheduler issue on linux related to multiprocessing
2019-10-27 21:27:04 -07:00
mdipierro
61976e7c3a Merge pull request #2267 from nicozanf/master
Fix broken inframe Vimeo video
2019-10-27 21:25:20 -07:00
Mateusz Mojsiejuk
a0eac8fce7 Add support for freeIPA ldap auth 2019-10-25 15:52:11 +02:00
Bernhard Miklautz
7038758960 new [scheduler]: add parameter use_spawn
Add a new parameter to Scheduler to allow it to use spawn
multiprocessing method.

This is only available with python3 therefore the parameter is
ignored with python2.
2019-10-25 11:31:54 +02:00
Nico Zanferrari
77bcda3f3e Added Python 3 binaries to the download page +typo
It fixes issue #2027.

** IMPORTANT ** - before approving, you need to copy from https://github.com/nicozanf/web2py-pyinstaller to https://mdipierro.pythonanywhere.com/examples/static the files: 
- web2py_win.zip
- web2py_win_py2.zip
- web2py_osx.zip
- web2py_osx_py2.zip

And also modify the procedures for the "For Testers" versions on https://mdipierro.pythonanywhere.com/examples/static, that should use the previous ones as binaries.
2019-10-23 22:41:19 +02:00
Nico Zanferrari
92bb2c2241 Added Python 3 binaries to the download page 2019-10-23 22:05:12 +02:00
Nico Zanferrari
f7b3abdc89 Fix broken inframe Vimeo video
It fixes Issue #2068
2019-10-22 21:54:01 +02:00
mdipierro
8f98994423 fixed validate for good 2019-10-20 22:04:11 -07:00
mdipierro
cf91145db0 made validators compliant with new dal 2019-10-20 21:48:49 -07:00
mdipierro
dce5fbb472 fixed validate maybe 2019-10-20 21:43:01 -07:00
mdipierro
40485ecd88 Merge pull request #2265 from vinyldarkscratch/patch-1
Add events for form upload progress
2019-10-20 21:39:50 -07:00
mdipierro
78b529bb92 Merge pull request #2263 from jicho/master
No redirect when url ends with #
2019-10-20 21:38:36 -07:00
mdipierro
32fc729cdc Merge pull request #2225 from nicozanf/pyinstaller
Full docs for obtaining Win and Mac binaries with PyInstaller
2019-10-20 21:35:47 -07:00
Queen Vinyl Darkscratch
5bcf34aba5 Add events for form upload progress 2019-10-15 16:32:43 -07:00
jicho
e4845aeef6 No redirect when url ends with #
When a redirect url ends with a hash (#) the browser (Chrome) does not redirect/refreshes the browser.

This is happening with this setting:
```
form = SQLFORM.grid(
        query,
        client_side_delete=True
    )
```
2019-10-09 09:33:18 +02:00
mdipierro
1ce316609a Merge branch 'master' of github.com:web2py/web2py 2019-09-30 20:47:15 -07:00
mdipierro
c95b4e43d7 isostring->isoformat 2019-09-30 20:47:06 -07:00
mdipierro
501d0b8a9b Merge pull request #2260 from timnyborg/patch-11
no sleep when redis_cache set to fail gracefully
2019-09-29 19:47:13 -07:00
Tim Nyborg
2b30157cce Update redis_cache.py
moving logging as well
2019-09-25 11:06:23 +01:00
mdipierro
cbbf793841 Merge pull request #2259 from timnyborg/patch-8
correcting indentation
2019-09-21 12:00:09 -07:00
Tim Nyborg
89c392d224 correcting indentation 2019-09-17 14:48:15 +01:00
mdipierro
91fd094790 Merge branch 'master' of github.com:web2py/web2py 2019-09-09 21:13:20 -07:00
mdipierro
0f638f9cdf possibly fixed the Redis problem, thanks Rastafarian 2019-09-09 21:13:01 -07:00
mdipierro
d84cbf8763 Merge pull request #2255 from vinyldarkscratch/patch-1
Don't set form in trap_form as ajax_page element
2019-09-09 20:52:15 -07:00
mdipierro
6af98ac7bc Merge pull request #2252 from misl6/fix-user-registration-key-error
Fix user registration key error
2019-09-09 20:51:24 -07:00
mdipierro
f91eaa84e8 Merge pull request #2249 from timnyborg/patch-7
python3 compatibility for JSONRPCError
2019-09-09 20:50:47 -07:00
Queen Vinyl Darkscratch
d17572fb10 Don't set form in trap_form as ajax_page element 2019-09-05 00:02:19 -07:00
Mirko Galimberti
1dec1b4358 Checks for user availability before accessing the registration key 2019-09-02 13:50:39 +02:00
Mirko
4191d4c48c Merge pull request #2 from web2py/master
Merge w/ current master
2019-09-02 13:43:56 +02:00
Tim Nyborg
75491fb273 python3 compatibility for JSONRPCError
Trying this again, now that I learned about gluon._compat
2019-08-23 12:25:11 +01:00
mdipierro
b7202df0b9 do not test 3.5 on bionic 2019-08-18 01:11:04 -07:00
mdipierro
529444cda7 fixed a test for Decimal serializer 2019-08-18 00:47:25 -07:00
mdipierro
12d1ca739d reverting test 3.5 2019-08-18 00:21:31 -07:00
mdipierro
af151783c6 test 2019-08-18 00:20:24 -07:00
mdipierro
4a838c2c14 biopic web2py tests 2019-08-17 23:43:25 -07:00
mdipierro
42181ca263 Merge branch 'master' of github.com:web2py/web2py 2019-08-17 22:46:56 -07:00
mdipierro
8b55025cb3 serializing decimal as float, thanks villas 2019-08-17 22:46:31 -07:00
mdipierro
e0fc657b8e Merge pull request #2237 from dlage/fake_migrate
Fake migrate
2019-08-17 21:59:28 -07:00
mdipierro
619af453a6 Merge pull request #2234 from jvanbraekel/Fix-encoding-of-pdflatex-log-file
Fix encoding of pdflatex log file
2019-08-17 21:58:30 -07:00
179edb659a Load all models when using --force_migrate (same behaviour as appadmin). 2019-08-13 18:04:30 +01:00
mdipierro
019295e1d1 reverting 2c364c2079 which incorrecly assumes the scheme can only be http or https 2019-08-11 10:58:22 -07:00
mdipierro
60c68164f3 Merge pull request #2231 from dlage/patch-1
Force page reload with anchors
2019-08-11 10:45:37 -07:00
b4c2ee8304 Allow easily switching fake_migrate on to allow easier fixes of the database migrations 2019-07-25 17:15:19 +01:00
cb0c2e6edd Merge remote-tracking branch 'upstream/master' 2019-07-25 17:07:24 +01:00
jvanbraekel
37d1fca32c Enable greek symbols support
Pakage was missing form the template, leading to errors when greeks characters were used
2019-07-25 11:28:37 +02:00
jvanbraekel
251314ceb8 Only return get_vars after a failing login
Since the redirect is a GET action, there is no reason to return post vars from the failing attempt.
2019-07-24 09:43:03 +02:00
jvanbraekel
a23a068d40 Add utf-8 encoding in latex log opening
By default, open use ascii encoding leading to crash when non ascii caracters are present . Fix it by enforcing UTF-8  encoding
2019-07-23 15:52:21 +02:00
jvanbraekel
726d664292 Merge pull request #3 from web2py/master
merge web2py master changes
2019-07-23 15:38:15 +02:00
mdipierro
2c364c2079 Merge pull request #2233 from rayluo/patch-1
Better https detection
2019-07-23 00:28:17 -07:00
Ray Luo
8faa5e2a82 Better https detection
Switch to request.is_https which provides better https detection
2019-07-22 23:41:23 -07:00
Dinis
6fddca4e4f Force page reload with anchors
Force page to reload even when the location contains anchors, while using "web2py-redirect-location".
Otherwise, when the location is the same and the url has an anchor it won't redirect.
2019-07-17 15:18:44 +01:00
mdipierro
0557fe9c58 sync 2019-07-15 22:02:57 -07:00
mdipierro
5945edebfd Merge pull request #2230 from bjones1/master
Allow specification of server's encoding in webclient.
2019-07-15 22:00:33 -07:00
mdipierro
558afd886c Merge pull request #2229 from leonelcamara/patch-32
__lt__ and __gt__ for lazyT
2019-07-15 22:00:13 -07:00
mdipierro
1bc8ea6838 Merge pull request #2227 from nicozanf/bookfix
fix for py 3.6+ compatibility
2019-07-15 21:59:56 -07:00
mdipierro
24d970a943 Merge pull request #2223 from gonguinguen/patch-6
Added followlinks=True when calling listdir
2019-07-15 21:58:06 -07:00
mdipierro
e66dda8641 Merge pull request #2222 from gonguinguen/patch-5
Added followlinks argument to listdir
2019-07-15 21:57:36 -07:00
mdipierro
320a28d564 Merge pull request #2216 from web-py/patch-3
Update fa.py
2019-07-15 21:57:12 -07:00
Bryan A. Jones
9db1c6b0b0 Allow specification of server's encoding in webclient. 2019-07-12 10:23:53 -05:00
Leonel Câmara
f307fe7d56 __lt__ and __gt__ for lazyT
Fixes #2228
2019-07-10 12:25:40 +01:00
Nico Zanferrari
f845497479 fix for py 3.6+ compatibility
py 3.6+ gives errors and need more escapes - I've tested the patch on the full web2py book's latex output and it's backward compatible with 2.7. For technical details see https://bugs.python.org/issue28450 :
"Deprecated since version 3.5, will be removed in version 3.6: Unknown escapes consist of '\' and ASCII letter now raise a deprecation warning and will be forbidden in Python 3.6"
2019-07-07 00:55:39 +02:00
Nico Zanferrari
ab257031b5 Modified fake site.py from PyInstaller - for PY3 2019-07-01 22:51:25 +02:00
Nico Zanferrari
64c66000fa Modified fake site.py from PyInstaller - for PY2 2019-07-01 22:50:37 +02:00
Nico Zanferrari
c247e740a2 full docs 2019-07-01 22:49:09 +02:00
Nico Zanferrari
3fe797ce60 full docs 2019-07-01 22:42:27 +02:00
Dinis
f1f65275f7 Merge pull request #1 from web2py/master
Refresh master
2019-06-19 14:20:22 +01:00
Lisandro
2989beae02 Added followlinks=True when calling listdir
In order to solve the issue #2221, I've added followlinks=True when calling listdir() from functions compile_views(), compile_models() and compile_controllers()
2019-06-07 19:24:59 -03:00
Lisandro
2a7b16d61f Added followlinks argument to listdir
The followlinks argument is then used by the function when it calls os.walk(path...
Notice the "followlinks" argument of os.walk() is False by default, so this change won't make any difference. Every actual call to listdir() function won't need to be changed.
This change is needed to resolve issue #2221
2019-06-07 19:21:10 -03:00
mdipierro
14c1b3e400 fixed typo 2019-06-06 22:39:38 -07:00
mdipierro
8823cde350 simpler populate 2019-06-06 21:52:20 -07:00
mdipierro
3e09add351 submit->Submit, thanks Paolo Pastori 2019-06-06 21:28:56 -07:00
mdipierro
7385904f97 --force_migrate and --cron_threads, thanks Paolo Pastori 2019-06-06 21:23:31 -07:00
web-py
c82f0ba619 Update fa.py
edit plural-forms for persian agian
2019-06-02 16:07:58 +04:30
mdipierro
409a499dd0 Merge pull request #2215 from yamandu/pt_br_translation
Better Brazilian Portuguese translation
2019-06-01 11:37:17 -07:00
mdipierro
9d52f5498e Merge pull request #2214 from yamandu/issue2179
Issue2179
2019-06-01 11:36:52 -07:00
mdipierro
0fe9d0aa34 Merge pull request #2213 from web-py/patch-2
create fa.py
2019-06-01 11:36:06 -07:00
mdipierro
89a4da0594 better scheduler logic to prevent deadlocks, thanks Paolo Pastori 2019-06-01 11:34:58 -07:00
mdipierro
f2e95d1bb6 no need to create folders in shell 2019-06-01 11:31:27 -07:00
Carlos Costa
273ebb9a70 Better Brazilian Portuguese translation
Including SQLFORM.grid, auth and other strings.
2019-05-31 12:04:32 -03:00
Carlos Costa
1bfffc4f12 issue #2179
fixing tab size setting that breaks the editor configuration
2019-05-31 11:20:39 -03:00
Carlos José da Costa
6d7900be82 Merge pull request #1 from web2py/master
Sync with original
2019-05-31 10:28:05 -03:00
Ankidroid-net
dadf363122 create fa.py
Hello. I intend to prepare a Persian translation of the Web 2 Pie and provide a good source for Persian speakers.
2019-05-26 17:20:25 +04:30
mdipierro
ce25d8dc31 description % should not be escaped, reverting 2019-05-25 01:01:32 -07:00
mdipierro
1c5b2d7fce chmod a-x, thanks Paolo 2019-05-25 00:30:34 -07:00
mdipierro
9b831a64c0 Merge pull request #2159 from nicozanf/master
Change binary scripts to use PyInstaller with PY3
2019-05-25 00:24:16 -07:00
mdipierro
e6e92b43c9 Merge pull request #2212 from gonguinguen/patch-4
solves #2211
2019-05-25 00:21:44 -07:00
mdipierro
c02ee6a5c0 Merge pull request #2210 from timnyborg/patch-5
prevent open redirects with no protocol specified
2019-05-25 00:21:20 -07:00
mdipierro
22f95677d9 Merge pull request #2208 from dlage/fix-missing-folders-in-script
Refactor methods from gluon/admin to gluon/fileutils
2019-05-25 00:20:52 -07:00
mdipierro
4fd44ff682 Merge pull request #2207 from dlage/migrator-pr
Migrator - add a command line option to ensure that all tables are migrated
2019-05-25 00:20:21 -07:00
mdipierro
da253f7ba8 Merge pull request #2202 from bmiklautz/shell_controller
fix: controller function invocation via shell
2019-05-25 00:18:55 -07:00
mdipierro
a24926ad6f Merge pull request #2199 from timnyborg/patch-4
correct SAML authorization request binding
2019-05-25 00:18:27 -07:00
c6837a9e73 Merge branch 'master' into migrator-pr 2019-05-23 14:32:24 +01:00
Lisandro
bd892556e1 solves #2211
The change I propose solves issue #2211 
I hope this is the correct approach.
2019-05-20 11:58:42 -03:00
Tim Nyborg
99d3d1d465 prevent open redirects with no protocol specified
prevent_open_redirect doesn't currently handle a 'next' with a // but no protocol, e.g.: .../user/login?_next=//google.com
2019-05-20 11:27:21 +01:00
b365d12a2e Merge branch 'master' into migrator-pr 2019-05-16 16:49:15 +01:00
4455fa48c4 Add a script to force migration of lazy tables and fix missing options in DAL init. 2019-05-16 15:33:19 +01:00
96534ccf30 First attempt to force migration of the tables even with migrate option set to false. 2019-05-15 20:07:36 +01:00
Bernhard Miklautz
7a225da44e fix: controller function invocation via shell
If only a compiled version is available and a controller function is invoked with -S app/c/f an
IOError is raised as '_' is used as delimiter instead of '.'.

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

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

View File

@@ -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;'

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

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

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

View File

@@ -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) {

View File

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 366 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

View File

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

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

View File

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

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
<div>
{{=get_content('main')}}
<center>
<iframe src="//player.vimeo.com/hubnut/album/3016728?color=ff6600&amp;background=ffffff&amp;slideshow=1&amp;video_title=1&amp;video_byline=1" width="400" height="300" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
<iframe src="https://player.vimeo.com/video/104800778?color=ff6600&amp;background=ffffff&amp;slideshow=1&amp;video_title=1&amp;video_byline=1" width="400" height="300" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>
</center>
{{=get_content('official')}}
{{=get_content('community')}}

View File

@@ -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')}}

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -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);

View File

@@ -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"

View File

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

View File

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

View File

@@ -0,0 +1,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 .

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

12
fabfile.py vendored
View File

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

View File

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

View File

@@ -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)

View File

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

View File

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

View File

@@ -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

View File

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

View File

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

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