Compare commits

...
88 Commits
Author SHA1 Message Date
mdipierro aca3d43f0a R-2.4.6 2013-04-06 12:12:32 -05:00
mdipierro 5a898cc80a fixed Issue 1406:Table scrollbar problem in IE, thanks czhang2000 2013-04-06 11:13:31 -05:00
mdipierro bfb3a320bd comment about incompatibility of GAE static mapping and parametric router syntax, thanks Jonathan 2013-04-06 10:14:38 -05:00
mdipierro c0b1e387b1 fixed Issue 1409:Mail.send defaults for subject, message appear to be wrong 2013-04-06 10:10:37 -05:00
mdipierro c175e6340d Issue 1415:Untranslated label for SQLFORM 'Check to delete' checkbox, thanks Dmitry 2013-04-06 10:06:27 -05:00
mdipierro 3a5f7b05df attempt to address issue 1424 2013-04-06 10:02:55 -05:00
mdipierro 26babd7e96 fixed Issue 1416:auth.wiki login loop unless the user is admin, thanks Alan 2013-04-06 09:52:42 -05:00
mdipierro 349088a96e fixed Issue 1428:backslash in edit controller URL, thanks Frank 2013-04-06 09:48:51 -05:00
mdipierro a4ff77bdb9 fixed Issue 1430:options_std.py ssl_certificate and ssl_private_key default value broken on web2py 2.4.5, thanks wprins 2013-04-06 09:44:16 -05:00
mdipierro 8f70d3a802 fixed Issue 1431:Typo in web2py documentation, thanks dabweather.FBI 2013-04-06 09:42:37 -05:00
mdipierro 89021b6409 fixed Issue 1432:Oracle: invalid SQL generated when requires = IS_NOT_IN_DB, thanks jphillips 2013-04-06 09:40:07 -05:00
mdipierro 4a078705b3 fixed issue 1246 with a nested select 2013-04-06 09:38:16 -05:00
Massimo 6eea834777 fixed bug in scheduler context switch, thanks Niphlod 2013-04-05 16:01:13 -05:00
mdipierro 0f0a62ef7f possible fix for Issue 1426:Count(*) query is incorrect when SQLFORM.grid() contains groupby parameter, thanks daveshih01 2013-04-04 21:11:19 -05:00
mdipierro cc8a21aedf document better the -X option, thanks Niphlod 2013-04-03 16:49:15 -05:00
mdipierro 38fff6fb91 better reporting in scheduler, thanks Niphlod 2013-04-03 16:42:27 -05:00
mdipierro e87a2bf0d5 populate(...contents), thanks Vinicius 2013-04-03 14:46:13 -05:00
mdipierro 6b3cdb5a99 fixed string concatenation in MySQL 2013-04-03 11:32:17 -05:00
mdipierro e2191175da smarter CONTAINS and REPLACE 2013-04-03 09:50:28 -05:00
Massimo 4a7f15bb04 fixed typo in examples page 2013-04-02 16:15:26 -05:00
mdipierro e6c58c3c6f Issue 1425:Check NoSQL, not GAE or Mongo, thanks Alan 2013-04-02 11:22:53 -05:00
mdipierro bb2407b468 interfear->interfere, thanks Ramos 2013-04-02 11:21:15 -05:00
mdipierro 07e809acb3 app pack custom 2013-04-02 11:12:40 -05:00
mdipierro 7714b5dc7b remove un-necessary checks 2013-04-01 19:18:56 -05:00
mdipierro ca85d9adab Issue 1418:Enhancement - SQLFORM.grid param. to change selectable option's submit button text, thanks bpeterso2013 2013-04-01 14:39:20 -05:00
mdipierro 41a9f12024 fixed Issue 1419:Using a recursive select but encountered a broken reference (versioning and auth.signature) 2013-04-01 14:36:14 -05:00
mdipierro 669691bbea Issue 1423:smartgrid multiple links to related table - how to suppress all but one, thanks cjkske 2013-04-01 14:14:41 -05:00
mdipierro 6ac38e7c56 fixed Issue 1414:Error with ldap_auth and using allowed_groups against AD 2008, thnaks Peter Gastinger 2013-04-01 12:45:09 -05:00
mdipierro 359758804b fixed contains(field) in postgresq for list:string 2013-04-01 09:32:44 -05:00
mdipierro 9400a0c7a1 fixed spanish translation, thanks neoecos 2013-03-31 19:14:17 -05:00
mdipierro ea34d1b3a0 fixed issue 1422, rss serializer encoding, thanks neoecos 2013-03-31 19:11:31 -05:00
mdipierro 72e9921901 fixed problem with hideerror, thanks Ricardo 2013-03-31 19:09:27 -05:00
mdipierro d6af8279bc Merge pull request #68 from mictee/any_of_validator
Added an ANY_OF validator
2013-03-31 16:17:48 -07:00
mdipierro cd005b01c0 Merge pull request #69 from grutz/IS_IPV6
Adds IS_IPV6() validator using Google ipaddr.py library
2013-03-31 16:16:50 -07:00
mdipierro fa04c23bbf Merge branch 'master' of github.com:web2py/web2py 2013-03-31 18:15:41 -05:00
mdipierro 6ecc09f286 Merge pull request #70 from gokceneraslan/patch-1
Render form content instead
2013-03-31 16:14:53 -07:00
mdipierro 4b0f6aff73 better Expose 2013-03-31 15:32:15 -05:00
Gökçen Eraslan c451f57d4e Render form content instead 2013-03-29 22:25:39 +01:00
Kurt Grutzmacher 721cb2b90a Adds IS_IPV6() validator using Google ipaddr.py library
ipaddr.py library rev/3144 has been approved as a standard python
library for python3. IS_IPV4() was not modified to use the library.
2013-03-29 08:55:06 -07:00
mdipierro 01d5302a2d no more bzr 2013-03-29 02:31:41 -05:00
mdipierro 706324b9c6 cleanup of whitespaces in scripts 2013-03-29 02:30:15 -05:00
mdipierro 244d3c68eb fixed newlines 2013-03-29 02:13:35 -05:00
mdipierro fcfa8aa917 added common_filter tests 2013-03-29 02:08:41 -05:00
mdipierro 8438a432e9 fixed issue 1405, ORDERBY and mssql, thanks Niphlod 2013-03-28 15:18:44 -05:00
mdipierro 9d5e0b24f7 Merge pull request #66 from mictee/arch_run_script
Improved process handling an error handling in web2py.archlinux.sh
2013-03-28 13:09:40 -07:00
mdipierro 4480acd6f7 Merge pull request #67 from michele-comitini/master
avoid ordering on limitby
2013-03-28 13:05:09 -07:00
mdipierro cd1ce28639 encoding in mail.send, thanks Jonathan 2013-03-28 14:59:06 -05:00
mdipierro bf5fbd8b09 fixed issue 1411, list of vars when using radiowidget, thanks Dmitry Mosin 2013-03-28 14:46:31 -05:00
mdipierro 515e284c22 issue 1412, common_filters and joins, thanks Felipe Merelles 2013-03-28 14:44:32 -05:00
mictee f98548827a Added an ANY_OF validator that allows a value to be valid according to any of a list of validators. 2013-03-26 17:24:19 +01:00
Michele Comitini b67e57f275 avoid ordering on limitby 2013-03-26 15:30:53 +01:00
mictee 7005fb655c Improved process handling an error handling in web2py.archlinux.sh 2013-03-26 13:47:41 +01:00
mdipierro b9189771d0 fixed mongodb issue 1401 and improvements, thanks Alan 2013-03-25 15:27:45 -05:00
mdipierro b15211064d fixed NoSQL Json, thanks Alan 2013-03-25 15:25:29 -05:00
Massimo 30ea9ac56b fixed cache and it.py, thanks Niphlod 2013-03-22 15:38:05 -05:00
Massimo dab4950f14 fixed cs BOM hopefully 2013-03-22 15:37:00 -05:00
Massimo 0fce3655d5 enabling test_web tests in travis, thanks Niphlod 2013-03-21 15:10:32 -05:00
Massimo 91504fbc2c better test_web.py, thanks Niphlod 2013-03-21 12:04:39 -05:00
Massimo 4b17684980 fixed issue 1397, fixed CHAR_LENGTH in Firebird thanks Villas 2013-03-21 11:29:31 -05:00
Massimo 675245eb15 fixed issue 1398, brackets in libspatialite load extension, thanks rmdpedroso 2013-03-21 11:21:53 -05:00
mdipierro 3494fa2d0d changed the logic behind tz 2013-03-18 22:48:52 -05:00
mdipierro 87a593845c added a test, thanks Jonathan 2013-03-18 22:03:39 -05:00
Massimo b9d80fcdc7 R-2.4.5 2013-03-18 17:37:49 -05:00
Massimo 3f7d085f73 fixed timezone and textarea default width 2013-03-18 17:34:06 -05:00
Massimo d57dd72780 upgraded fpdf, better tests, passes travis.ci tests, thanks Niphlod 2013-03-18 16:59:57 -05:00
Massimo a8d1d5cfcf upgraded fpdf, better tests, passes travis.ci tests, thanks Niphlod 2013-03-18 16:52:47 -05:00
mdipierro 8857e3d521 IS_DATE/DATETIME/etc. (timezone=user_timezone in hrs) 2013-03-17 18:53:19 -05:00
mdipierro 3b1a5be1be fixed (possibly) truncate for GAE 2013-03-17 15:27:47 -05:00
mdipierro c9a63a8524 fixed issue 1393, cast_keys in dict for python 2.5 support, thanks Alan and Niphlod 2013-03-17 12:11:10 -05:00
mdipierro cd967d2551 new travis.ci 2013-03-16 20:53:24 -05:00
mdipierro 92b5247f9f typo Fir(e)bird, thanks Jonathan 2013-03-15 21:27:52 -05:00
mdipierro 45a5b436c8 patched fpdf for 2.5, thanks Niphlod 2013-03-15 15:28:50 -05:00
mdipierro bfd385f969 travis badge in README, thanks niphlod 2013-03-15 15:12:01 -05:00
mdipierro f693fe6b2a added .travis.yml 2013-03-15 14:55:36 -05:00
mdipierro b040159a9b fixed issue 1385, thanks Alan 2013-03-15 14:14:40 -05:00
mdipierro f3af2a1999 fixed issue 1382, sanitizer accepts mailto, thanks lightdot 2013-03-15 10:17:33 -05:00
mdipierro f613a4cc99 wiki.settings, thanks Alan 2013-03-15 10:14:33 -05:00
mdipierro e4a96125a6 fixed issue 1390, dateTtime separator in MSSQL, thanks score2000 2013-03-15 10:05:52 -05:00
mdipierro e48074ff54 fixed issue 1391, sub for decimal 2013-03-15 10:00:45 -05:00
mdipierro 2ed122a534 fixed typo, thank you Philipp Storz 2013-03-15 09:49:46 -05:00
mdipierro 65c0d9b18b italian patch 2013-03-15 09:46:55 -05:00
mdipierro 6702694590 fixed slidetoggle boxes in new admin, thanks Annet and Niphlod 2013-03-15 09:44:22 -05:00
mdipierro 6f0d4d039e fixed upload default values, thanks Marin Pranjic 2013-03-15 09:38:47 -05:00
mdipierro 1325b0e48f Merge pull request #65 from michele-comitini/master
HTTP exceptions in jsonrpc2
2013-03-15 07:25:55 -07:00
Michele Comitini 7421eb8068 avoid treating HTTP redirections or status codes generated in jsonrpc2 functions as error exceptions 2013-03-14 21:54:38 +01:00
Michele Comitini 6c7a9a4030 avoid treating HTTP redirections or status codes generated in jsonrpc2 functions as error exceptions 2013-03-14 21:49:05 +01:00
Massimo ba0a143717 scheduler patch allows termination of tasks, thanks niphlod 2013-03-12 10:40:57 -05:00
mdipierro 28bcb5ed6c added sessions in cookie for examples 2013-03-11 18:12:01 -05:00
72 changed files with 7218 additions and 4320 deletions
+25
View File
@@ -0,0 +1,25 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
env:
- DB=sqlite:memory
- DB=mysql://root:@localhost/test_w2p
- DB=postgres://postgres:@localhost/test_w2p
before_script:
- pip install unittest2
- if [[ $DB == postgres* ]]; then pip install --use-mirrors psycopg2; fi
- if [[ $TRAVIS_PYTHON_VERSION == '2.5' ]]; then pip install --use-mirrors pysqlite; fi
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
#Temporal solution to travis issue #155
- sudo chmod 777 /dev/shm
- sudo rm -rf /dev/shm && sudo ln -s /run/shm /dev/shm
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
email: false
irc:
channels: "irc.freenode.org#web2py"
+12
View File
@@ -1,3 +1,15 @@
## 2.4.6
- better tests
- new ANY_OF and IS_IPV6 validators
- new custom save option
- many small bug fixes
## 2.4.5
- travis.ci integration (thanks Marc Abramowitz and Niphlod). Passes all tests (thanks Niplod).
- IS_DATE and IS_DATETIME can specify timezone
## 2.4.1- 2.4.3 ## 2.4.1- 2.4.3
- 2D GEO API: geoPoint, getLine, geoPolygon - 2D GEO API: geoPoint, getLine, geoPolygon
+1 -2
View File
@@ -30,7 +30,7 @@ update:
echo "remember that pymysql was tweaked" echo "remember that pymysql was tweaked"
src: src:
### Use semantic versioning ### Use semantic versioning
echo 'Version 2.4.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION echo 'Version 2.4.6-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files ### rm -f all junk files
make clean make clean
### clean up baisc apps ### clean up baisc apps
@@ -114,7 +114,6 @@ commit:
make src make src
echo '' > NEWINSTALL echo '' > NEWINSTALL
hg commit -m "$(S)" hg commit -m "$(S)"
#bzr commit -m "$(S)"
git commit -a -m "$(S)" git commit -a -m "$(S)"
push: push:
hg push hg push
-1
View File
@@ -1 +0,0 @@
+5
View File
@@ -6,6 +6,11 @@ It is written and programmable in Python. LGPLv3 License
Learn more at http://web2py.com Learn more at http://web2py.com
## Tests
[![Build Status](https://travis-ci.org/web2py/web2py.png)](https://travis-ci.org/web2py/web2py)
## Installation Instructions ## Installation Instructions
To start web2py there is NO NEED to install it. Just unzip and do: To start web2py there is NO NEED to install it. Just unzip and do:
+1 -1
View File
@@ -1 +1 @@
Version 2.4.4-stable+timestamp.2013.03.11.15.25.57 Version 2.4.6-stable+timestamp.2013.04.06.12.11.51
+4
View File
@@ -21,6 +21,10 @@ default_expiration: "24h" # for static files
handlers: handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(?P<a>.+?)/static/(?P<b>.+) - url: /(?P<a>.+?)/static/(?P<b>.+)
static_files: applications/\1/static/\2 static_files: applications/\1/static/\2
upload: applications/(.+?)/static/(.+) upload: applications/(.+?)/static/(.+)
@@ -54,6 +54,10 @@ response.menu = [[T('design'), False, URL('admin', 'default', 'design',
# ## auxiliary functions # ## auxiliary functions
# ########################################################### # ###########################################################
if False and request.tickets_db:
from gluon.restricted import TicketStorage
ts = TicketStorage()
ts._get_table(request.tickets_db, ts.tablename, request.application)
def get_databases(request): def get_databases(request):
dbs = {} dbs = {}
+30 -4
View File
@@ -27,7 +27,7 @@ from gluon.languages import (read_possible_languages, read_dict, write_dict,
read_plural_dict, write_plural_dict) read_plural_dict, write_plural_dict)
if DEMO_MODE and request.function in ['change_password', 'pack', 'pack_plugin', 'upgrade_web2py', 'uninstall', 'cleanup', 'compile_app', 'remove_compiled_app', 'delete', 'delete_plugin', 'create_file', 'upload_file', 'update_languages', 'reload_routes', 'git_push', 'git_pull']: if DEMO_MODE and request.function in ['change_password', 'pack', 'pack_custom','pack_plugin', 'upgrade_web2py', 'uninstall', 'cleanup', 'compile_app', 'remove_compiled_app', 'delete', 'delete_plugin', 'create_file', 'upload_file', 'update_languages', 'reload_routes', 'git_push', 'git_pull']:
session.flash = T('disabled in demo mode') session.flash = T('disabled in demo mode')
redirect(URL('site')) redirect(URL('site'))
@@ -146,7 +146,7 @@ def check_version():
elif new_version != True: elif new_version != True:
return A(T('web2py is up to date'), _href=WEB2PY_URL) return A(T('web2py is up to date'), _href=WEB2PY_URL)
elif platform.system().lower() in ('windows', 'win32', 'win64') and os.path.exists("web2py.exe"): elif platform.system().lower() in ('windows', 'win32', 'win64') and os.path.exists("web2py.exe"):
return SPAN('You should upgrade to version %s.%s.%s' % version_number[:3]) return SPAN('You should upgrade to version %s' % version_number)
else: else:
return sp_button(URL('upgrade_web2py'), T('upgrade now to %s') % version_number.split('-')[0]) return sp_button(URL('upgrade_web2py'), T('upgrade now to %s') % version_number.split('-')[0])
@@ -341,7 +341,6 @@ def pack():
session.flash = T('internal error: %s' % e) session.flash = T('internal error: %s' % e)
redirect(URL('site')) redirect(URL('site'))
def pack_plugin(): def pack_plugin():
app = get_app() app = get_app()
if len(request.args) == 2: if len(request.args) == 2:
@@ -356,6 +355,33 @@ def pack_plugin():
session.flash = T('internal error') session.flash = T('internal error')
redirect(URL('plugin', args=request.args)) redirect(URL('plugin', args=request.args))
def pack_custom():
app = get_app()
base = apath(app, r=request)
if request.post_vars.file:
files = request.post_vars.file
files = [files] if not isinstance(files,list) else files
fname = 'web2py.app.%s.w2p' % app
try:
filename = app_pack(app, request, raise_ex=True, filenames=files)
except Exception, e:
filename = None
if filename:
response.headers['Content-Type'] = 'application/w2p'
disposition = 'attachment; filename=%s' % fname
response.headers['Content-Disposition'] = disposition
return safe_read(filename, 'rb')
else:
session.flash = T('internal error: %s' % e)
redirect(URL(args=request.args))
def ignore(fs):
return [f for f in fs if not (
f[:1] in '#' or f.endswith('~') or f.endswith('.bak'))]
files = {}
for (r,d,f) in os.walk(base):
files[r] = {'folders':ignore(d),'files':ignore(f)}
return locals()
def upgrade_web2py(): def upgrade_web2py():
dialog = FORM.confirm(T('Upgrade'), dialog = FORM.confirm(T('Upgrade'),
@@ -682,7 +708,7 @@ def edit():
cfilename = os.path.join(request.args[0], 'controllers', cfilename = os.path.join(request.args[0], 'controllers',
request.args[2] + '.py') request.args[2] + '.py')
if os.path.exists(apath(cfilename, r=request)): if os.path.exists(apath(cfilename, r=request)):
edit_controller = URL('edit', args=[cfilename]) edit_controller = URL('edit',args=[cfilename.replace(os.sep, "/")])
view = request.args[3].replace('.html', '') view = request.args[3].replace('.html', '')
view_link = URL(request.args[0], request.args[2], view) view_link = URL(request.args[0], request.args[2], view)
elif filetype == 'python' and request.args[1] == 'controllers': elif filetype == 'python' and request.args[1] == 'controllers':
+362 -272
View File
@@ -1,390 +1,480 @@
# coding: utf8 # coding: utf8
{ {
'!langcode!': 'cs-cz', '!langcode!': 'cs-cz',
'!langname!': '?e?tina', '!langname!': 'čeština',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinn? v?raz, nap??klad "pole1=\'nov?hodnota\'". V?sledky datab?zov?ho JOINu nem??ete mazat ani upravovat.', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinný výraz, například "pole1=\'nováhodnota\'". Výsledky databázového JOINu nemůžete mazat ani upravovat.',
'%%{Row} in Table': '%%{??dek} v tabulce', '"User Exception" debug mode. An error ticket could be issued!': '"User Exception" debug mode. An error ticket could be issued!',
'%%{Row} selected': 'ozna?en?ch %%{??dek}', '%%{Row} in Table': '%%{řádek} v tabulce',
'%s %%{row} deleted': '%s smazan?ch %%{z?znam}', '%%{Row} selected': 'označených %%{řádek}',
'%s %%{row} updated': '%s upraven?ch %%{z?znam}', '%s %%{row} deleted': '%s smazaných %%{záznam}',
'%s selected': '%s ozna?en?ch', '%s %%{row} updated': '%s upravených %%{záznam}',
'%s selected': '%s označených',
'%Y-%m-%d': '%d.%m.%Y', '%Y-%m-%d': '%d.%m.%Y',
'%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S', '%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S',
'(requires internet access)': '(vy?aduje p?ipojen? k internetu)', '(requires internet access)': '(vyžaduje připojení k internetu)',
'(something like "it-it")': '(nap??klad "cs-cs")', '(requires internet access, experimental)': '(requires internet access, experimental)',
'(something like "it-it")': '(například "cs-cs")',
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)',
'@markmin\x01Searching: **%s** %%{file}': 'Hled?n?: **%s** %%{soubor}', '@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}',
'About': 'O programu', 'About': 'O programu',
'About application': 'O aplikaci', 'About application': 'O aplikaci',
'Access Control': '??zen? p??stupu', 'Access Control': 'Řízení přístupu',
'Add breakpoint': 'P?idat bod p?eru?en?', 'Add breakpoint': 'Přidat bod přerušení',
'Additional code for your application': 'Dal?? k?d pro Va?i aplikaci', 'Additional code for your application': 'Další kód pro Vaši aplikaci',
'Admin language': 'jazyk rozhran?', 'Admin design page': 'Admin design page',
'Administrative interface': 'pro administr?torsk? rozhran? klikn?te sem', 'Admin language': 'jazyk rozhraní',
'Administrative Interface': 'Administr?torsk? rozhran?', 'Administrative interface': 'pro administrátorské rozhraní klikněte sem',
'administrative interface': 'rozhran? pro spr?vu', 'Administrative Interface': 'Administrátorské rozhraní',
'Administrator Password:': 'Administr?torsk? heslo:', 'administrative interface': 'rozhraní pro správu',
'Administrator Password:': 'Administrátorské heslo:',
'Ajax Recipes': 'Recepty s ajaxem', 'Ajax Recipes': 'Recepty s ajaxem',
'and rename it:': 'a p?ejmenovat na:', 'An error occured, please %s the page': 'An error occured, please %s the page',
'and rename it:': 'a přejmenovat na:',
'appadmin': 'appadmin', 'appadmin': 'appadmin',
'appadmin is disabled because insecure channel': 'appadmin je zak?zan? bez zabezpe?en?ho spojen?', 'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení',
'application "%s" uninstalled': 'application "%s" odinstalov?na', 'Application': 'Application',
'application compiled': 'aplikace zkompilov?na', 'application "%s" uninstalled': 'application "%s" odinstalována',
'Application name:': 'N?zev aplikace:', 'application compiled': 'aplikace zkompilována',
'are not used': 'nepou?ita', 'Application name:': 'Název aplikace:',
'are not used yet': 'je?t? nepou?ita', 'are not used': 'nepoužita',
'are not used yet': 'ještě nepoužita',
'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?', 'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?',
'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?', 'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?',
'Available Databases and Tables': 'Dostupn? datab?ze a tabulky', 'arguments': 'arguments',
'back': 'zp?t', 'at char %s': 'at char %s',
'at line %s': 'at line %s',
'ATTENTION:': 'ATTENTION:',
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.',
'Available Databases and Tables': 'Dostupné databáze a tabulky',
'back': 'zpět',
'Back to wizard': 'Back to wizard',
'Basics': 'Basics', 'Basics': 'Basics',
'Begin': 'Za??t', 'Begin': 'Začít',
'breakpoint': 'bod p?eru?en?', 'breakpoint': 'bod přerušení',
'Breakpoints': 'Body p?eru?en?', 'Breakpoints': 'Body přerušení',
'breakpoints': 'body p?eru?en?', 'breakpoints': 'body přerušení',
'Buy this book': 'Koupit web2py knihu', 'Buy this book': 'Koupit web2py knihu',
'Cache': 'Cache', 'Cache': 'Cache',
'cache': 'cache', 'cache': 'cache',
'Cache Keys': 'Kl??e cache', 'Cache Keys': 'Klíče cache',
'cache, errors and sessions cleaned': 'cache, chyby a relace byly pro?i?t?ny', 'cache, errors and sessions cleaned': 'cache, chyby a relace byly pročištěny',
'can be a git repo': 'm??e to b?t git repo', 'can be a git repo': 'může to být git repo',
'Cancel': 'Storno', 'Cancel': 'Storno',
'Cannot be empty': 'Nem??e b?t pr?zdn?', 'Cannot be empty': 'Nemůže být prázdné',
'Change admin password': 'Zm?nit heslo pro str?vu aplikac?', 'Change Admin Password': 'Změnit heslo pro správu',
'Change password': 'Zm?na hesla', 'Change admin password': 'Změnit heslo pro správu aplikací',
'check all': 'v?e ozna?it', 'Change password': 'Změna hesla',
'check all': 'vše označit',
'Check for upgrades': 'Zkusit aktualizovat', 'Check for upgrades': 'Zkusit aktualizovat',
'Check to delete': 'Ozna?it ke smaz?n?', 'Check to delete': 'Označit ke smazání',
'Check to delete:': 'Ozna?it ke smaz?n?:', 'Check to delete:': 'Označit ke smazání:',
'Checking for upgrades...': 'Zji??uji, zda jsou k dispozici aktualizace...', 'Checking for upgrades...': 'Zjišťuji, zda jsou k dispozici aktualizace...',
'Clean': 'Pro?istit', 'Clean': 'Pročistit',
'Clear CACHE?': 'Vymazat CACHE?', 'Clear CACHE?': 'Vymazat CACHE?',
'Clear DISK': 'Vymazat DISK', 'Clear DISK': 'Vymazat DISK',
'Clear RAM': 'Vymazat RAM', 'Clear RAM': 'Vymazat RAM',
'Click row to expand traceback': 'Pro rozbalen? stopy, klikn?te na ??dek', 'Click row to expand traceback': 'Pro rozbalení stopy, klikněte na řádek',
'Click row to view a ticket': 'Pro zobrazen? chyby (ticketu), klikn?te na ??dku...', 'Click row to view a ticket': 'Pro zobrazení chyby (ticketu), klikněte na řádku...',
'Client IP': 'IP adresa klienta', 'Client IP': 'IP adresa klienta',
'collapse/expand all': 'v?e sbalit/rozbalit', 'code': 'code',
'Code listing': 'Code listing',
'collapse/expand all': 'vše sbalit/rozbalit',
'Community': 'Komunita', 'Community': 'Komunita',
'Compile': 'Zkompilovat', 'Compile': 'Zkompilovat',
'compiled application removed': 'zkompilovan? aplikace smaz?na', 'compiled application removed': 'zkompilovaná aplikace smazána',
'Components and Plugins': 'Komponenty a z?suvn? moduly', 'Components and Plugins': 'Komponenty a zásuvné moduly',
'Condition': 'Podm?nka', 'Condition': 'Podmínka',
'Controller': 'Kontrol?r (Controller)', 'continue': 'continue',
'Controllers': 'Kontrol?ry', 'Controller': 'Kontrolér (Controller)',
'controllers': 'kontrol?ry', 'Controllers': 'Kontroléry',
'controllers': 'kontroléry',
'Copyright': 'Copyright', 'Copyright': 'Copyright',
'Count': 'Po?et', 'Count': 'Počet',
'Create': 'Vytvo?it', 'Create': 'Vytvořit',
'create file with filename:': 'vytvo?it soubor s n?zvem:', 'create file with filename:': 'vytvořit soubor s názvem:',
'created by': 'vytvo?il', 'created by': 'vytvořil',
'Created By': 'Vytvo?eno - k?m', 'Created By': 'Vytvořeno - kým',
'Created On': 'Vytvo?eno - kdy', 'Created On': 'Vytvořeno - kdy',
'crontab': 'crontab', 'crontab': 'crontab',
'Current request': 'Aktu?ln? po?adavek', 'Current request': 'Aktuální požadavek',
'Current response': 'Aktu?ln? odpov??', 'Current response': 'Aktuální odpověď',
'Current session': 'Aktu?ln? relace', 'Current session': 'Aktuální relace',
'currently running': 'pr?v? b???', 'currently running': 'právě běží',
'currently saved or': 'ulo?eno nebo', 'currently saved or': 'uloženo nebo',
'customize me!': 'upravte m?!', 'customize me!': 'upravte mě!',
'data uploaded': 'data nahr?na', 'data uploaded': 'data nahrána',
'Database': 'Rozhran? datab?ze', 'Database': 'Rozhraní databáze',
'Database %s select': 'datab?ze %s v?b?r', 'Database %s select': 'databáze %s výběr',
'database administration': 'spr?va datab?ze', 'Database administration': 'Database administration',
'Date and Time': 'Datum a ?as', 'database administration': 'správa databáze',
'Date and Time': 'Datum a čas',
'day': 'den', 'day': 'den',
'db': 'db', 'db': 'db',
'DB Model': 'Datab?zov? model', 'DB Model': 'Databázový model',
'Debug': 'Lad?n?', 'Debug': 'Ladění',
'defines tables': 'defines tables',
'Delete': 'Smazat', 'Delete': 'Smazat',
'delete': 'smazat', 'delete': 'smazat',
'delete all checked': 'smazat v?e ozna?en?', 'delete all checked': 'smazat vše označené',
'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete po??d?n o potvrzen? maz?n?)', 'delete plugin': 'delete plugin',
'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete požádán o potvrzení mazání)',
'Delete:': 'Smazat:', 'Delete:': 'Smazat:',
'deleted after first hit': 'smazat po prvn?m dosa?en?', 'deleted after first hit': 'smazat po prvním dosažení',
'Demo': 'Demo', 'Demo': 'Demo',
'Deploy': 'Nahr?t', 'Deploy': 'Nahrát',
'Deploy on Google App Engine': 'Nahr?t na Google App Engine', 'Deploy on Google App Engine': 'Nahrát na Google App Engine',
'Deploy to OpenShift': 'Nahr?t na OpenShift', 'Deploy to OpenShift': 'Nahrát na OpenShift',
'Deployment Recipes': 'Postupy pro deployment', 'Deployment Recipes': 'Postupy pro deployment',
'Description': 'Popis', 'Description': 'Popis',
'design': 'n?vrh', 'design': 'návrh',
'direction: ltr': 'direction: ltr', 'Detailed traceback description': 'Podrobný výpis prostředí',
'details': 'podrobnosti',
'direction: ltr': 'směr: ltr',
'Disable': 'Zablokovat', 'Disable': 'Zablokovat',
'DISK': 'DISK', 'DISK': 'DISK',
'Disk Cache Keys': 'Kl??e diskov? cache', 'Disk Cache Keys': 'Klíče diskové cache',
'Disk Cleared': 'Disk smaz?n', 'Disk Cleared': 'Disk smazán',
'docs': 'dokumentace', 'docs': 'dokumentace',
'Documentation': 'Dokumentace', 'Documentation': 'Dokumentace',
"Don't know what to do?": 'Nev?te kudy kam?', "Don't know what to do?": 'Nevíte kudy kam?',
'done!': 'hotovo!', 'done!': 'hotovo!',
'Download': 'St?hnout', 'Download': 'Stáhnout',
'download layouts': 'st?hnout moduly rozvr?en? str?nky', 'download layouts': 'stáhnout moduly rozvržení stránky',
'download plugins': 'st?hnout z?suvn? moduly', 'download plugins': 'stáhnout zásuvné moduly',
'E-mail': 'E-mail', 'E-mail': 'E-mail',
'Edit': 'Upravit', 'Edit': 'Upravit',
'edit all': 'edit all', 'edit all': 'edit all',
'Edit application': 'Spr?va aplikace', 'Edit application': 'Správa aplikace',
'Edit current record': 'Upravit aktu?ln? z?znam', 'edit controller': 'edit controller',
'Edit current record': 'Upravit aktuální záznam',
'Edit Profile': 'Upravit profil', 'Edit Profile': 'Upravit profil',
'Editing file "%s"': '?prava souboru "%s"', 'edit views:': 'upravit pohled:',
'Editing Language file': '?prava jazykov?ho souboru', 'Editing file "%s"': 'Úprava souboru "%s"',
'Editing Language file': 'Úprava jazykového souboru',
'Editing Plural Forms File': 'Editing Plural Forms File',
'Email and SMS': 'Email a SMS', 'Email and SMS': 'Email a SMS',
'Enable': 'Odblokovat', 'Enable': 'Odblokovat',
'enter a number between %(min)g and %(max)g': 'zadejte ??slo mezi %(min)g a %(max)g', 'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g',
'enter an integer between %(min)g and %(max)g': 'zadejte cel? ??slo mezi %(min)g a %(max)g', 'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g',
'Error': 'Chyba', 'Error': 'Chyba',
'Error logs for "%(app)s"': 'Seznam v?skytu chyb pro aplikaci "%(app)s"', 'Error logs for "%(app)s"': 'Seznam výskytu chyb pro aplikaci "%(app)s"',
'Error snapshot': 'Snapshot chyby',
'Error ticket': 'Ticket chyby',
'Errors': 'Chyby', 'Errors': 'Chyby',
'Exception %(extype)s: %(exvalue)s': 'Exception %(extype)s: %(exvalue)s',
'Exception %s': 'Exception %s',
'Exception instance attributes': 'Prvky instance výjimky',
'Expand Abbreviation': 'Expand Abbreviation',
'export as csv file': 'exportovat do .csv souboru', 'export as csv file': 'exportovat do .csv souboru',
'exposes': 'vystavuje', 'exposes': 'vystavuje',
'extends': 'roz?i?uje', 'exposes:': 'vystavuje funkce:',
'FAQ': '?asto kladen? dotazy', 'extends': 'rozšiřuje',
'failed to compile file because:': 'soubor se nepodařilo zkompilovat, protože:',
'FAQ': 'Často kladené dotazy',
'File': 'Soubor', 'File': 'Soubor',
'file': 'soubor', 'file': 'soubor',
'file saved on %(time)s': 'soubor ulo?en %(time)s', 'file "%(filename)s" created': 'file "%(filename)s" created',
'Filename': 'N?zev souboru', 'file saved on %(time)s': 'soubor uložen %(time)s',
'file saved on %s': 'soubor uložen %s',
'Filename': 'Název souboru',
'filter': 'filtr', 'filter': 'filtr',
'Find Next': 'Naj?t dal??', 'Find Next': 'Najít další',
'Find Previous': 'Naj?t p?edchoz?', 'Find Previous': 'Najít předchozí',
'First name': 'K?estn? jm?no', 'First name': 'Křestní jméno',
'Forgot username?': 'Zapomn?l jste svoje p?ihla?ovac? jm?no?', 'Forgot username?': 'Zapomněl jste svoje přihlašovací jméno?',
'forgot username?': 'zapomn?l jste svoje p?ihla?ovac? jm?no?', 'forgot username?': 'zapomněl jste svoje přihlašovací jméno?',
'Forms and Validators': 'Formul??e a valid?tory', 'Forms and Validators': 'Formuláře a validátory',
'Frames': 'Frames',
'Free Applications': 'Aplikace zdarma', 'Free Applications': 'Aplikace zdarma',
'Generate': 'Vytvo?it', 'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.',
'Get from URL:': 'St?hnout z internetu:', 'Generate': 'Vytvořit',
'Get from URL:': 'Stáhnout z internetu:',
'Git Pull': 'Git Pull', 'Git Pull': 'Git Pull',
'Git Push': 'Git Push', 'Git Push': 'Git Push',
'Globals##debug': 'Glob?ln? prom?nn?', 'Globals##debug': 'Globální proměnné',
'go!': 'OK!', 'go!': 'OK!',
'Group %(group_id)s created': 'Skupina %(group_id)s vytvo?ena', 'Goto': 'Goto',
'graph model': 'graph model',
'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena',
'Group ID': 'ID skupiny', 'Group ID': 'ID skupiny',
'Groups': 'Skupiny', 'Groups': 'Skupiny',
'Hello World': 'Ahoj sv?te', 'Hello World': 'Ahoj světe',
'Help': 'N?pov?da', 'Help': 'Nápověda',
'Hide/Show Translated strings': 'Skr?t/Zobrazit p?elo?en? texty', 'Hide/Show Translated strings': 'Skrýt/Zobrazit přeložené texty',
'Hits': 'Kolikr?t dosa?eno', 'Hits': 'Kolikrát dosaženo',
'Home': 'Domovsk? str?nka', 'Home': 'Domovská stránka',
'honored only if the expression evaluates to true': 'br?t v potaz jen kdy? se tato podm?nka vyhodnot? kladn?', 'honored only if the expression evaluates to true': 'brát v potaz jen když se tato podmínka vyhodnotí kladně',
'How did you get here?': 'Jak jste se sem vlastn? dostal?', 'How did you get here?': 'Jak jste se sem vlastně dostal?',
'If start the upgrade, be patient, it may take a while to download': 'If start the upgrade, be patient, it may take a while to download',
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
'import': 'import', 'import': 'import',
'Import/Export': 'Import/Export', 'Import/Export': 'Import/Export',
'includes': 'zahrnuje', 'includes': 'zahrnuje',
'Index': 'Index', 'Index': 'Index',
'insert new': 'vlo?it nov? z?znam ', 'insert new': 'vložit nový záznam ',
'insert new %s': 'vlo?it nov? z?znam %s', 'insert new %s': 'vložit nový záznam %s',
'inspect attributes': 'inspect attributes',
'Install': 'Instalovat', 'Install': 'Instalovat',
'Installed applications': 'Nainstalovan? aplikace', 'Installed applications': 'Nainstalované aplikace',
'Internal State': 'Vnit?n? stav', 'Interaction at %s line %s': 'Interakce v %s, na řádce %s',
'Introduction': '?vod', 'Interactive console': 'Interaktivní příkazová řádka',
'Invalid email': 'Neplatn? email', 'Internal State': 'Vnitřní stav',
'Invalid password': 'Nespr?vn? heslo', 'Introduction': 'Úvod',
'invalid password.': 'neplatn? heslo', 'Invalid email': 'Neplatný email',
'Invalid Query': 'Neplatn? dotaz', 'Invalid password': 'Nesprávné heslo',
'invalid request': 'Neplatn? po?adavek', 'invalid password.': 'neplatné heslo',
'Is Active': 'Je aktivn?', 'Invalid Query': 'Neplatný dotaz',
'invalid request': 'Neplatný požadavek',
'Is Active': 'Je aktivní',
'It is %s %%{day} today.': 'Dnes je to %s %%{den}.', 'It is %s %%{day} today.': 'Dnes je to %s %%{den}.',
'Key': 'Kl??', 'Key': 'Klíč',
'Key bindings': 'Vazby kl???', 'Key bindings': 'Vazby klíčů',
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
'languages': 'jazyky', 'languages': 'jazyky',
'Languages': 'Jazyky', 'Languages': 'Jazyky',
'Last name': 'P??jmen?', 'Last name': 'Příjmení',
'Last saved on:': 'Naposledy ulo?eno:', 'Last saved on:': 'Naposledy uloženo:',
'Layout': 'Rozvr?en? str?nky (layout)', 'Layout': 'Rozvržení stránky (layout)',
'Layout Plugins': 'Moduly rozvr?en? str?nky (Layout Plugins)', 'Layout Plugins': 'Moduly rozvržení stránky (Layout Plugins)',
'Layouts': 'Rozvr?en? str?nek', 'Layouts': 'Rozvržení stránek',
'License for': 'Licence pro', 'License for': 'Licence pro',
'Line number': '??slo ??dku', 'Line number': 'Číslo řádku',
'LineNo': '?.??dku', 'LineNo': 'Č.řádku',
'Live Chat': 'Online pokec', 'Live Chat': 'Online pokec',
'loading...': 'nahr?v?m...', 'loading...': 'nahrávám...',
'Locals##debug': 'Lok?ln? prom?nn?', 'locals': 'locals',
'Logged in': 'P?ihl??en? prob?hlo ?sp??n?', 'Locals##debug': 'Lokální proměnné',
'Logged out': 'Odhl??en? prob?hlo ?sp??n?', 'Logged in': 'Přihlášení proběhlo úspěšně',
'Login': 'P?ihl?sit se', 'Logged out': 'Odhlášení proběhlo úspěšně',
'login': 'p?ihl?sit se', 'Login': 'Přihlásit se',
'Login to the Administrative Interface': 'P?ihl?sit se do Spr?vce aplikac?', 'login': 'přihlásit se',
'logout': 'odhl?sit se', 'Login to the Administrative Interface': 'Přihlásit se do Správce aplikací',
'Logout': 'Odhl?sit se', 'logout': 'odhlásit se',
'Lost Password': 'Zapomn?l jste heslo', 'Logout': 'Odhlásit se',
'Lost password?': 'Zapomn?l jste heslo?', 'Lost Password': 'Zapomněl jste heslo',
'lost password?': 'zapomn?l jste heslo?', 'Lost password?': 'Zapomněl jste heslo?',
'lost password?': 'zapomněl jste heslo?',
'Manage': 'Manage',
'Manage Cache': 'Manage Cache', 'Manage Cache': 'Manage Cache',
'Menu Model': 'Model rozbalovac? nab?dky', 'Menu Model': 'Model rozbalovací nabídky',
'Models': 'Modely', 'Models': 'Modely',
'models': 'modely', 'models': 'modely',
'Modified By': 'Zm?n?no - k?m', 'Modified By': 'Změněno - kým',
'Modified On': 'Zm?n?no - kdy', 'Modified On': 'Změněno - kdy',
'Modules': 'Moduly', 'Modules': 'Moduly',
'modules': 'moduly', 'modules': 'moduly',
'My Sites': 'Spr?va aplikac?', 'My Sites': 'Správa aplikací',
'Name': 'Jm?no', 'Name': 'Jméno',
'New Application Wizard': 'Nov? pr?vodce aplikac?', 'new application "%s" created': 'nová aplikace "%s" vytvořena',
'New application wizard': 'Nov? pr?vodce aplikac?', 'New Application Wizard': 'Nový průvodce aplikací',
'New password': 'Nov? heslo', 'New application wizard': 'Nový průvodce aplikací',
'New Record': 'Nov? z?znam', 'New password': 'Nové heslo',
'new record inserted': 'nov? z?znam byl zalo?en', 'New Record': 'Nový záznam',
'New simple application': 'Vytvo?it primitivn? aplikaci', 'new record inserted': 'nový záznam byl založen',
'next 100 rows': 'dal??ch 100 ??dk?', 'New simple application': 'Vytvořit primitivní aplikaci',
'No databases in this application': 'V t?to aplik?ci nejsou ??dn? datab?ze', 'next': 'next',
'No Interaction yet': 'Je?t? ??dn? interakce nenastala', 'next 100 rows': 'dalších 100 řádků',
'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adres??i /private nenalezen', 'No databases in this application': 'V této aplikaci nejsou žádné databáze',
'Object or table name': 'Objekt ?i tabulka', 'No Interaction yet': 'Ještě žádná interakce nenastala',
'Old password': 'P?vodn? heslo', 'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adresáři /private nenalezen',
'online designer': 'online n?vrh??', 'Object or table name': 'Objekt či tabulka',
'Online examples': 'P??klady online', 'Old password': 'Původní heslo',
'online designer': 'online návrhář',
'Online examples': 'Příklady online',
'Open new app in new window': 'Open new app in new window',
'or alternatively': 'or alternatively',
'Or Get from URL:': 'Or Get from URL:',
'or import from csv file': 'nebo importovat z .csv souboru', 'or import from csv file': 'nebo importovat z .csv souboru',
'Origin': 'P?vod', 'Origin': 'Původ',
'Original/Translation': 'Origin?l/P?eklad', 'Original/Translation': 'Originál/Překlad',
'Other Plugins': 'Ostatn? moduly', 'Other Plugins': 'Ostatní moduly',
'Other Recipes': 'Ostatn? z?suvn? moduly', 'Other Recipes': 'Ostatní zásuvné moduly',
'Overview': 'P?ehled', 'Overview': 'Přehled',
'Overwrite installed app': 'P?epsat instalovanou aplikaci', 'Overwrite installed app': 'Přepsat instalovanou aplikaci',
'Pack all': 'Zabalit', 'Pack all': 'Zabalit',
'Pack compiled': 'Zabalit zkompilovan?', 'Pack compiled': 'Zabalit zkompilované',
'pack plugin': 'pack plugin',
'password': 'heslo', 'password': 'heslo',
'Password': 'Heslo', 'Password': 'Heslo',
"Password fields don't match": 'Hesla se neshoduj?', "Password fields don't match": 'Hesla se neshodují',
'Please': 'Pros?m', 'Peeking at file': 'Peeking at file',
'plugins': 'z?suvn? moduly', 'Please': 'Prosím',
'Plugins': 'Z?suvn? moduly', 'Plugin "%s" in application': 'Plugin "%s" in application',
'Plural-Forms:': 'Mno?n? ??sla:', 'plugins': 'zásuvné moduly',
'Powered by': 'Poh?n?no', 'Plugins': 'Zásuvné moduly',
'Preface': 'P?edmluva', 'Plural Form #%s': 'Plural Form #%s',
'previous 100 rows': 'p?edchoz?ch 100 ??dk?', 'Plural-Forms:': 'Množná čísla:',
'Private files': 'Soukrom? soubory', 'Powered by': 'Poháněno',
'private files': 'soukrom? soubory', 'Preface': 'Předmluva',
'previous 100 rows': 'předchozích 100 řádků',
'Private files': 'Soukromé soubory',
'private files': 'soukromé soubory',
'profile': 'profil', 'profile': 'profil',
'Project Progress': 'V?voj projektu', 'Project Progress': 'Vývoj projektu',
'Python': 'Python', 'Python': 'Python',
'Query:': 'Dotaz:', 'Query:': 'Dotaz:',
'Quick Examples': 'Kr?tk? p??klady', 'Quick Examples': 'Krátké příklady',
'RAM': 'RAM', 'RAM': 'RAM',
'RAM Cache Keys': 'Kl??e RAM Cache', 'RAM Cache Keys': 'Klíče RAM Cache',
'Ram Cleared': 'RAM smaz?na', 'Ram Cleared': 'RAM smazána',
'Readme': 'N?pov?da', 'Readme': 'Nápověda',
'Recipes': 'Postupy jak na to', 'Recipes': 'Postupy jak na to',
'Record': 'Z?znam', 'Record': 'Záznam',
'record does not exist': 'z?znam neexistuje', 'record does not exist': 'záznam neexistuje',
'Record ID': 'ID z?znamu', 'Record ID': 'ID záznamu',
'Record id': 'id z?znamu', 'Record id': 'id záznamu',
'refresh': 'obnovte', 'refresh': 'obnovte',
'register': 'registrovat', 'register': 'registrovat',
'Register': 'Zaregistrovat se', 'Register': 'Zaregistrovat se',
'Registration identifier': 'Registra?n? identifik?tor', 'Registration identifier': 'Registrační identifikátor',
'Registration key': 'Registra?n? kl??', 'Registration key': 'Registrační klíč',
'Reload routes': 'Znovu nahr?t cesty', 'reload': 'reload',
'Remember me (for 30 days)': 'Zapamatovat na 30 dn?', 'Reload routes': 'Znovu nahrát cesty',
'Remove compiled': 'Odstranit zkompilovan?', 'Remember me (for 30 days)': 'Zapamatovat na 30 dní',
'Removed Breakpoint on %s at line %s': 'Bod p?eru?en? smaz?n - soubor %s na ??dce %s', 'Remove compiled': 'Odstranit zkompilované',
'Replace': 'Zam?nit', 'Removed Breakpoint on %s at line %s': 'Bod přerušení smazán - soubor %s na řádce %s',
'Replace All': 'Zam?nit v?e', 'Replace': 'Zaměnit',
'Reset Password key': 'Reset registra?n?ho kl??e', 'Replace All': 'Zaměnit vše',
'request': 'request',
'Reset Password key': 'Reset registračního klíče',
'response': 'response',
'restart': 'restart', 'restart': 'restart',
'restore': 'obnovit', 'restore': 'obnovit',
'Retrieve username': 'Z?skat p?ihla?ovac? jm?no', 'Retrieve username': 'Získat přihlašovací jméno',
'revert': 'vr?tit se k p?vodn?mu', 'return': 'return',
'revert': 'vrátit se k původnímu',
'Role': 'Role', 'Role': 'Role',
'Rows in Table': 'Z?znamy v tabulce', 'Rows in Table': 'Záznamy v tabulce',
'Rows selected': 'Z?znam? zobrazeno', 'Rows selected': 'Záznamů zobrazeno',
'rules are not defined': 'pravidla nejsou definov?na', 'rules are not defined': 'pravidla nejsou definována',
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spust? testy v tomto souboru (ke spu?t?n? v?ech test?, pou?ijte tla??tko 'test')", "Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spustí testy v tomto souboru (ke spuštění všech testů, použijte tlačítko 'test')",
'Running on %s': 'B??? na %s', 'Running on %s': 'Běží na %s',
'Save': 'Ulo?it', 'Save': 'Uložit',
'Save via Ajax': 'Ulo?it pomoc? Ajaxu', 'Save file:': 'Save file:',
'Saved file hash:': 'hash ulo?en?ho souboru:', 'Save via Ajax': 'Uložit pomocí Ajaxu',
'Saved file hash:': 'hash uloženého souboru:',
'Semantic': 'Modul semantic', 'Semantic': 'Modul semantic',
'Services': 'Slu?by', 'Services': 'Služby',
'Set Breakpoint on %s at line %s: %s': 'Bod p?eru?en? nastaven v souboru %s na ??dce %s: %s', 'session': 'session',
'shell': 'p??kazov? ??dka', 'session expired': 'session expired',
'Site': 'Spr?va aplikac?', 'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s',
'shell': 'příkazová řádka',
'Singular Form': 'Singular Form',
'Site': 'Správa aplikací',
'Size of cache:': 'Velikost cache:', 'Size of cache:': 'Velikost cache:',
'skip to generate': 'skip to generate', 'skip to generate': 'skip to generate',
'Sorry, could not find mercurial installed': 'Bohu?el mercurial nen? nainstalov?n.', 'Sorry, could not find mercurial installed': 'Bohužel mercurial není nainstalován.',
'Start a new app': 'Vytvo?it novou aplikaci', 'Start a new app': 'Vytvořit novou aplikaci',
'Start searching': 'Za??t hled?n?', 'Start searching': 'Začít hledání',
'Start wizard': 'Spustit pr?vodce', 'Start wizard': 'Spustit průvodce',
'state': 'stav', 'state': 'stav',
'static': 'statick? soubory', 'Static': 'Static',
'Static files': 'Statick? soubory', 'static': 'statické soubory',
'Static files': 'Statické soubory',
'Statistics': 'Statistika', 'Statistics': 'Statistika',
'Step': 'Step', 'Step': 'Step',
'step': 'step',
'stop': 'stop',
'Stylesheet': 'CSS styly', 'Stylesheet': 'CSS styly',
'submit': 'odeslat', 'submit': 'odeslat',
'Submit': 'Odeslat', 'Submit': 'Odeslat',
'successful': '?sp??n?', 'successful': 'úspěšně',
'Support': 'Podpora', 'Support': 'Podpora',
'Sure you want to delete this object?': 'Opravdu chcete smazat tento objekt?', 'Sure you want to delete this object?': 'Opravdu chcete smazat tento objekt?',
'Table': 'tabulka', 'Table': 'tabulka',
'Table name': 'N?zev tabulky', 'Table name': 'Název tabulky',
'Temporary': 'Do?asn?', 'Temporary': 'Dočasný',
'test': 'test', 'test': 'test',
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podm?nka, nap??klad "db.tabulka1.pole1==\'hodnota\'". Podm?nka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvo?? SQL JOIN.', 'Testing application': 'Testing application',
'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: ka?d? URL je mapov?na na funkci vystavovanou kontrol?rem.', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podmínka, například "db.tabulka1.pole1==\'hodnota\'". Podmínka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvoří SQL JOIN.',
'The Core': 'J?dro (The Core)', 'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: každá URL je mapována na funkci vystavovanou kontrolérem.',
'The data representation, define database tables and sets': 'Reprezentace dat: definovat tabulky datab?ze a z?znamy', 'The Core': 'Jádro (The Core)',
'The output of the file is a dictionary that was rendered by the view %s': 'V?stup ze souboru je slovn?k, kter? se zobrazil v pohledu %s.', 'The data representation, define database tables and sets': 'Reprezentace dat: definovat tabulky databáze a záznamy',
'The presentations layer, views are also known as templates': 'Prezenta?n? vrstva: pohledy ?i templaty (?ablony)', 'The output of the file is a dictionary that was rendered by the view %s': 'Výstup ze souboru je slovník, který se zobrazil v pohledu %s.',
'The presentations layer, views are also known as templates': 'Prezentační vrstva: pohledy či templaty (šablony)',
'The Views': 'Pohledy (The Views)', 'The Views': 'Pohledy (The Views)',
'There are no plugins': '??dn? moduly nejsou instalov?ny.', 'There are no controllers': 'There are no controllers',
'There are no private files': '??dn? soukrom? soubory neexistuj?.', 'There are no modules': 'There are no modules',
'These files are not served, they are only available from within your app': 'Tyto soubory jsou klient?m nep??stupn?. K dispozici jsou pouze v r?mci aplikace.', 'There are no plugins': 'Žádné moduly nejsou instalovány.',
'These files are served without processing, your images go here': 'Tyto soubory jsou serv?rov?ny bez p??davn? logiky, sem pat?? nap?. obr?zky.', 'There are no private files': 'Žádné soukromé soubory neexistují.',
'There are no static files': 'There are no static files',
'There are no translators, only default language is supported': 'There are no translators, only default language is supported',
'There are no views': 'There are no views',
'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.',
'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.',
'This App': 'Tato aplikace', 'This App': 'Tato aplikace',
'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.', 'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.',
'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto str?nku, abyste uvid?li, zda se dos?hlo bodu p?eru?en?.', 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk',
'This is the %(filename)s template': 'This is the %(filename)s template',
'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto stránku, abyste uviděli, zda se dosáhlo bodu přerušení.',
'Ticket': 'Ticket', 'Ticket': 'Ticket',
'Time in Cache (h:m:s)': '?as v Cache (h:m:s)', 'Ticket ID': 'Ticket ID',
'Timestamp': '?asov? raz?tko', 'Time in Cache (h:m:s)': 'Čas v Cache (h:m:s)',
'to previous version.': 'k p?edchoz? verzi.', 'Timestamp': 'Časové razítko',
'To create a plugin, name a file/folder plugin_[name]': 'Z?suvn? modul vytvo??te tak, ?e pojmenujete soubor/adres?? plugin_[jm?no modulu]', 'to previous version.': 'k předchozí verzi.',
'To emulate a breakpoint programatically, write:': 'K nastaven? bodu p?eru?en? v k?du programu, napi?te:', 'To create a plugin, name a file/folder plugin_[name]': 'Zásuvný modul vytvoříte tak, že pojmenujete soubor/adresář plugin_[jméno modulu]',
'to use the debugger!': ', abyste mohli lad?c? program pou??vat!', 'To emulate a breakpoint programatically, write:': 'K nastavení bodu přerušení v kódu programu, napište:',
'toggle breakpoint': 'vyp./zap. bod p?eru?en?', 'to use the debugger!': ', abyste mohli ladící program používat!',
'Toggle Fullscreen': 'Na celou obrazovku a zp?t', 'toggle breakpoint': 'vyp./zap. bod přerušení',
'too short': 'P??li? kr?tk?', 'Toggle Fullscreen': 'Na celou obrazovku a zpět',
'Translation strings for the application': 'P?eklad text? pro aplikaci', 'too short': 'Příliš krátké',
'Try the mobile interface': 'Zkuste rozhran? pro mobiln? za??zen?', 'Traceback': 'Traceback',
'Translation strings for the application': 'Překlad textů pro aplikaci',
'try something like': 'try something like',
'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení',
'try view': 'try view',
'Twitter': 'Twitter', 'Twitter': 'Twitter',
'unable to parse csv file': 'csv soubor ned? sa zpracovat', 'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.',
'uncheck all': 'v?e odzna?it', 'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.',
'Unable to check for upgrades': 'Unable to check for upgrades',
'unable to parse csv file': 'csv soubor nedá sa zpracovat',
'uncheck all': 'vše odznačit',
'Uninstall': 'Odinstalovat', 'Uninstall': 'Odinstalovat',
'update': 'aktualizovat', 'update': 'aktualizovat',
'update all languages': 'aktualizovat v?echny jazyky', 'update all languages': 'aktualizovat všechny jazyky',
'Update:': 'Upravit:', 'Update:': 'Upravit:',
'upload': 'nahr?t', 'Upgrade': 'Upgrade',
'Upload a package:': 'Nahr?t bal?k:', 'upgrade now': 'upgrade now',
'Upload and install packed application': 'Nahr?t a instalovat zabalenou aplikaci', 'upgrade now to %s': 'upgrade now to %s',
'upload file:': 'nahr?t soubor:', 'upload': 'nahrát',
'upload plugin file:': 'nahr?t soubor modulu:', 'Upload': 'Upload',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Pou?ijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestaven? slo?it?j??ch dotaz?.', 'Upload a package:': 'Nahrát balík:',
'User %(id)s Logged-in': 'U?ivatel %(id)s p?ihl??en', 'Upload and install packed application': 'Nahrát a instalovat zabalenou aplikaci',
'User %(id)s Logged-out': 'U?ivatel %(id)s odhl??en', 'upload file:': 'nahrát soubor:',
'User %(id)s Password changed': 'U?ivatel %(id)s zm?nil heslo', 'upload plugin file:': 'nahrát soubor modulu:',
'User %(id)s Profile updated': 'U?ivatel %(id)s upravil profil', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Použijte (...)&(...) pro AND, (...)|(...) pro OR a ~(...) pro NOT pro sestavení složitějších dotazů.',
'User %(id)s Registered': 'U?ivatel %(id)s se zaregistroval', 'User %(id)s Logged-in': 'Uživatel %(id)s přihlášen',
'User %(id)s Username retrieved': 'U?ivatel %(id)s si nachal zaslat p?ihla?ovac? jm?no', 'User %(id)s Logged-out': 'Uživatel %(id)s odhlášen',
'User ID': 'ID u?ivatele', 'User %(id)s Password changed': 'Uživatel %(id)s změnil heslo',
'Username': 'P?ihla?ovac? jm?no', 'User %(id)s Profile updated': 'Uživatel %(id)s upravil profil',
'User %(id)s Registered': 'Uživatel %(id)s se zaregistroval',
'User %(id)s Username retrieved': 'Uživatel %(id)s si nachal zaslat přihlašovací jméno',
'User ID': 'ID uživatele',
'Username': 'Přihlašovací jméno',
'variables': 'variables',
'Verify Password': 'Zopakujte heslo', 'Verify Password': 'Zopakujte heslo',
'Version': 'Verze',
'Version %s.%s.%s (%s) %s': 'Verze %s.%s.%s (%s) %s', 'Version %s.%s.%s (%s) %s': 'Verze %s.%s.%s (%s) %s',
'Versioning': 'Verzov?n?', 'Versioning': 'Verzování',
'Videos': 'Videa', 'Videos': 'Videa',
'View': 'Pohled (View)', 'View': 'Pohled (View)',
'Views': 'Pohledy', 'Views': 'Pohledy',
'views': 'pohledy', 'views': 'pohledy',
'Web Framework': 'Web Framework', 'Web Framework': 'Web Framework',
'web2py is up to date': 'M?te aktu?ln? verzi web2py.', 'web2py is up to date': 'Máte aktuální verzi web2py.',
'web2py online debugger': 'Lad?c? online web2py program', 'web2py online debugger': 'Ladící online web2py program',
'web2py Recent Tweets': '?t?bet?n? na Twitteru o web2py', 'web2py Recent Tweets': 'Štěbetání na Twitteru o web2py',
'Welcome': 'V?tejte', 'web2py upgrade': 'web2py upgrade',
'web2py upgraded; please restart it': 'web2py upgraded; please restart it',
'Welcome': 'Vítejte',
'Welcome to web2py': 'Vitejte ve web2py', 'Welcome to web2py': 'Vitejte ve web2py',
'Welcome to web2py!': 'V?tejte ve web2py!', 'Welcome to web2py!': 'Vítejte ve web2py!',
'Which called the function %s located in the file %s': 'kter? zavolala funkci %s v souboru (kontrol?ru) %s.', 'Which called the function %s located in the file %s': 'která zavolala funkci %s v souboru (kontroléru) %s.',
'You are successfully running web2py': '?sp??n? jste spustili web2py.', 'You are successfully running web2py': 'Úspěšně jste spustili web2py.',
'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Nastavovat a mazat body p?eru?en? je t?? mo?no v r?mci editov?n? zdrojov?ho souboru p?es tla??tko Vyp./Zap. bod p?eru?en?', 'You can also set and remove breakpoint in the edit window, using the Toggle Breakpoint button': 'Nastavovat a mazat body přerušení je též možno v rámci editování zdrojového souboru přes tlačítko Vyp./Zap. bod přerušení',
'You can modify this application and adapt it to your needs': 'Tuto aplikaci si m??ete upravit a p?izp?sobit ji sv?m pot?eb?m.', 'You can modify this application and adapt it to your needs': 'Tuto aplikaci si můžete upravit a přizpůsobit ji svým potřebám.',
'You need to set up and reach a': 'Je t?eba nejprve nastavit a doj?t a? na', 'You need to set up and reach a': 'Je třeba nejprve nastavit a dojít až na',
'You visited the url %s': 'Nav?t?vili jste str?nku %s,', 'You visited the url %s': 'Navštívili jste stránku %s,',
'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Aplikace bude blokována než se klikne na jedno z tlačítek (další, krok, pokračovat, atd.)',
'Your can inspect variables using the console bellow': 'Níže pomocí příkazové řádky si můžete prohlédnout proměnné',
} }
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
+375 -375
View File
@@ -1,69 +1,69 @@
{{extend 'layout.html'}} {{extend 'layout.html'}}
{{ {{
def all(items): def all(items):
return reduce(lambda a,b:a and b,items,True) return reduce(lambda a,b:a and b,items,True)
def peekfile(path,file,vars={},title=None): def peekfile(path,file,vars={},title=None):
args=(path,file) if 'app' in vars else (app,path,file) args=(path,file) if 'app' in vars else (app,path,file)
return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars)) return A(file.replace('\\\\','/'),_title=title,_href=URL('peek', args=args, vars=vars))
def editfile(path,file,vars={}): def editfile(path,file,vars={}):
args=(path,file) if 'app' in vars else (app,path,file) args=(path,file) if 'app' in vars else (app,path,file)
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit', args=args, vars=vars)) return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit', args=args, vars=vars))
def testfile(path,file): def testfile(path,file):
return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('test')), return A(TAG[''](IMG(_src=URL('static', 'images/test_icon.png'), _alt=T('test')),
SPAN(T("Run tests in this file (to run all files, you may also use the button labelled 'test')"))), SPAN(T("Run tests in this file (to run all files, you may also use the button labelled 'test')"))),
_class='icon test', _class='icon test',
_href=URL('test', args=(app, file)), _href=URL('test', args=(app, file)),
_rel="tooltip", _rel="tooltip",
**{'_data-placement':'right', **{'_data-placement':'right',
'_data-original-title':T("Run tests in this file (to run all files, you may also use the button labelled 'test')")}) '_data-original-title':T("Run tests in this file (to run all files, you may also use the button labelled 'test')")})
def editlanguagefile(path,file,vars={}): def editlanguagefile(path,file,vars={}):
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_language', args=(app, path, file), vars=vars)) return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_language', args=(app, path, file), vars=vars))
def editpluralsfile(path,file,vars={}): def editpluralsfile(path,file,vars={}):
return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_plurals', args=(app, path, file), vars=vars)) return A(SPAN(T('Edit')),_class='button editbutton btn btn-mini',_href=URL('edit_plurals', args=(app, path, file), vars=vars))
def file_upload_form(location, anchor=None): def file_upload_form(location, anchor=None):
form=FORM( form=FORM(
LABEL(T("upload file:")), LABEL(T("upload file:")),
INPUT(_type="file",_name="file"), INPUT(_type="file",_name="file"),
LABEL(T("and rename it:")), LABEL(T("and rename it:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""), INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=""),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)), INPUT(_type="hidden",_name="sender",_value=URL('design',args=app, anchor=anchor)),
_action=URL('upload_file'), _action=URL('upload_file'),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def file_create_form(location, anchor=None, helptext=""): def file_create_form(location, anchor=None, helptext=""):
form=FORM( form=FORM(
LABEL(T("create file with filename:")), LABEL(T("create file with filename:")),
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''), INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY,_class=''),
TAG['SMALL'](helptext,_class="help-block"), TAG['SMALL'](helptext,_class="help-block"),
DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Create"),_type="submit",_class="btn"),_class="controls"),
INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)), INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
INPUT(_type="hidden",_name="id",_value=anchor), INPUT(_type="hidden",_name="id",_value=anchor),
_action=URL('create_file'), _action=URL('create_file'),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def upload_plugin_form(app, anchor=None): def upload_plugin_form(app, anchor=None):
form=FORM( form=FORM(
LABEL(T("upload plugin file:")), LABEL(T("upload plugin file:")),
INPUT(_type="file",_name="pluginfile"), INPUT(_type="file",_name="pluginfile"),
INPUT(_type="hidden",_name="id",_value=anchor), INPUT(_type="hidden",_name="id",_value=anchor),
INPUT(_type="hidden",_name="token",_value=session.token), INPUT(_type="hidden",_name="token",_value=session.token),
DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"), DIV(TAG['BUTTON'](T("Upload"),_type="submit",_class="btn"),_class="controls"),
_class="generatedbyw2p well well-small") _class="generatedbyw2p well well-small")
return form return form
def deletefile(arglist, vars={}): def deletefile(arglist, vars={}):
vars.update({'sender':request.function+'/'+app}) vars.update({'sender':request.function+'/'+app})
return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')), return A(TAG[''](IMG(_src=URL('static', 'images/delete_icon.png')),
SPAN(T('Delete this file (you will be asked to confirm deletion)'))), SPAN(T('Delete this file (you will be asked to confirm deletion)'))),
_href=URL('delete',args=arglist,vars=vars), _href=URL('delete',args=arglist,vars=vars),
_class='icon delete', _class='icon delete',
_rel="tooltip", _rel="tooltip",
**{'_data-placement':'right', **{'_data-placement':'right',
'_data-original-title':T('Delete this file (you will be asked to confirm deletion)')}) '_data-original-title':T('Delete this file (you will be asked to confirm deletion)')})
}} }}
{{block sectionclass}}design{{end}} {{block sectionclass}}design{{end}}
@@ -91,45 +91,45 @@ def deletefile(arglist, vars={}):
<!-- MODELS --> <!-- MODELS -->
<h3 id="_models" rel="pagebookmark"> <h3 id="_models" rel="pagebookmark">
<span class="component" onclick="collapse('models_inner');">{{=T("Models")}}</span> <span class="component" onclick="collapse('models_inner');">{{=T("Models")}}</span>
<a href="#models" rel="tooltip" data-placement="right" data-original-title="{{=T('The data representation, define database tables and sets')}}"> <a href="#models" rel="tooltip" data-placement="right" data-original-title="{{=T('The data representation, define database tables and sets')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("The data representation, define database tables and sets")}}</span> <span>{{=T("The data representation, define database tables and sets")}}</span>
</a><span id="models" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="models" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="models_inner" class="component_contents"> <div id="models_inner" class="component_contents">
{{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}} {{if not models:}}<p><strong>{{=T("There are no models")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL(a=app,c='appadmin',f='index'), T('database administration'))}} {{=button(URL(a=app,c='appadmin',f='index'), T('database administration'))}}
{{if os.access(os.path.join(request.folder,'..',app,'databases','sql.log'),os.R_OK):}} {{if os.access(os.path.join(request.folder,'..',app,'databases','sql.log'),os.R_OK):}}
{{=button(URL('peek/%s/databases/sql.log'%app), 'sql.log')}} {{=button(URL('peek/%s/databases/sql.log'%app), 'sql.log')}}
{{pass}} {{pass}}
{{=button(URL(a=app, c='appadmin',f='graph_model'), T('graph model'))}} {{=button(URL(a=app, c='appadmin',f='graph_model'), T('graph model'))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for m in models:}} {{for m in models:}}
{{id="models__"+m.replace('.','__')}} {{id="models__"+m.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('models',m, dict(id=id))}} {{=editfile('models',m, dict(id=id))}}
{{=deletefile([app, 'models', m], dict(id=id, id2='models'))}} {{=deletefile([app, 'models', m], dict(id=id, id2='models'))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('models',m, dict(id=id))}} {{=peekfile('models',m, dict(id=id))}}
</span> </span>
<span class="extras"> <span class="extras">
{{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}} {{if len(defines[m]):}}{{=T("defines tables")}} {{pass}}{{=XML(', '.join([B(table).xml() for table in defines[m]]))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form1').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form1').slideToggle()" class="btn btn-mini">Create</button>
<div id="form1" class="row-fluid" style="display:none"> <div id="form1" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div> <div class="span3">{{=file_create_form('%s/models/' % app, 'models')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- FIND CONTROLLER FUNCTIONS --> <!-- FIND CONTROLLER FUNCTIONS -->
@@ -140,330 +140,330 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
<!-- CONTROLLERS --> <!-- CONTROLLERS -->
<h3 id="_controllers" rel="pagebookmark"> <h3 id="_controllers" rel="pagebookmark">
<span class="component" onclick="collapse('controllers_inner');">{{=T("Controllers")}}</span> <span class="component" onclick="collapse('controllers_inner');">{{=T("Controllers")}}</span>
<a href="#controllers" rel="tooltip" data-placement="right" data-original-title="{{=T('The application logic, each URL path is mapped in one exposed function in the controller')}}"> <a href="#controllers" rel="tooltip" data-placement="right" data-original-title="{{=T('The application logic, each URL path is mapped in one exposed function in the controller')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("The application logic, each URL path is mapped in one exposed function in the controller")}}</span> <span>{{=T("The application logic, each URL path is mapped in one exposed function in the controller")}}</span>
</a><span id="controllers" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="controllers" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="controllers_inner" class="component_contents"> <div id="controllers_inner" class="component_contents">
{{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}} {{if not controllers:}}<p><strong>{{=T("There are no controllers")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}} {{=button(URL(r=request,c='shell',f='index',args=app), T("shell"))}}
{{=button(URL('test',args=app), T("test"))}} {{=button(URL('test',args=app), T("test"))}}
{{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}} {{=button(URL('edit',args=[app,'cron','crontab']), T("crontab"))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for c in controllers:}} {{for c in controllers:}}
{{id="controllers__"+c.replace('.','__')}} {{id="controllers__"+c.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('controllers',c, dict(id=id))}} {{=editfile('controllers',c, dict(id=id))}}
{{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}} {{=deletefile([app, 'controllers', c], dict(id=id, id2='controllers'))}}
{{=testfile('controllers',c)}} {{=testfile('controllers',c)}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('controllers',c, dict(id=id))}} {{=peekfile('controllers',c, dict(id=id))}}
</span> </span>
<span class="extras celled"> <span class="extras celled">
{{if functions[c]:}}{{=T("exposes")}}{{pass}} {{=XML(', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}} {{if functions[c]:}}{{=T("exposes")}}{{pass}} {{=XML(', '.join([A(f,_href=URL(a=app,c=c[:-3],f=f)).xml() for f in functions[c]]))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form2').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form2').slideToggle()" class="btn btn-mini">Create</button>
<div id="form2" class="row-fluid" style="display:none"> <div id="form2" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div> <div class="span3">{{=file_create_form('%s/controllers/' % app, 'controllers')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- VIEWS --> <!-- VIEWS -->
<h3 id="_views" rel="pagebookmark"> <h3 id="_views" rel="pagebookmark">
<span class="component" onclick="collapse('views_inner');">{{=T("Views")}}</span> <span class="component" onclick="collapse('views_inner');">{{=T("Views")}}</span>
<a href="#views" rel="tooltip" data-placement="right" data-original-title="{{=T('The presentations layer, views are also known as templates')}}"> <a href="#views" rel="tooltip" data-placement="right" data-original-title="{{=T('The presentations layer, views are also known as templates')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("The presentations layer, views are also known as templates")}}</span> <span>{{=T("The presentations layer, views are also known as templates")}}</span>
</a><span id="views" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="views" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="views_inner" class="component_contents"> <div id="views_inner" class="component_contents">
{{if not views:}}<p><strong>{{=T("There are no views")}}</strong></p>{{else:}} {{if not views:}}<p><strong>{{=T("There are no views")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(LAYOUTS_APP, T("download layouts"))}} {{=button(LAYOUTS_APP, T("download layouts"))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for c in views:}} {{for c in views:}}
{{id="views__"+c.replace('/','__').replace('.','__')}} {{id="views__"+c.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('views',c, dict(id=id))}} {{=editfile('views',c, dict(id=id))}}
{{=deletefile([app, 'views', c], dict(id=id, id2='views'))}} {{=deletefile([app, 'views', c], dict(id=id, id2='views'))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('views',c, dict(id=id))}} {{=peekfile('views',c, dict(id=id))}}
</span> </span>
<span class="extras celled celled-one"> <span class="extras celled celled-one">
{{if extend.has_key(c):}}{{=T("extends")}} <b>{{=extend[c]}}</b> {{pass}} {{if extend.has_key(c):}}{{=T("extends")}} <b>{{=extend[c]}}</b> {{pass}}
{{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}} {{if include[c]:}}{{=T("includes")}} {{pass}}{{=XML(', '.join([B(f).xml() for f in include[c]]))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form3').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form3').slideToggle()" class="btn btn-mini">Create</button>
<div id="form3" class="row-fluid" style="display:none"> <div id="form3" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div> <div class="span3">{{=file_create_form('%s/views/' % app, 'views')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- LANGUAGES --> <!-- LANGUAGES -->
<h3 id="_languages" rel="pagebookmark"> <h3 id="_languages" rel="pagebookmark">
<span class="component" onclick="collapse('languages_inner');">{{=T("Languages")}}</span> <span class="component" onclick="collapse('languages_inner');">{{=T("Languages")}}</span>
<a href="#languages" rel="tooltip" data-placement="right" data-original-title="{{=T('Translation strings for the application')}}"> <a href="#languages" rel="tooltip" data-placement="right" data-original-title="{{=T('Translation strings for the application')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("Translation strings for the application")}}</span> <span>{{=T("Translation strings for the application")}}</span>
</a><span id="languages" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="languages" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="languages_inner" class="component_contents"> <div id="languages_inner" class="component_contents">
{{if not languages:}}<p><strong>{{=T("There are no translators, only default language is supported")}}</strong></p>{{else:}} {{if not languages:}}<p><strong>{{=T("There are no translators, only default language is supported")}}</strong></p>{{else:}}
<div class="controls comptools"> <div class="controls comptools">
{{=button(URL('update_languages/'+app), T('update all languages'))}} {{=button(URL('update_languages/'+app), T('update all languages'))}}
</div> </div>
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for lang in sorted(languages): {{for lang in sorted(languages):
file = lang+'.py' file = lang+'.py'
id = "languages__"+file.replace('.','__')}} id = "languages__"+file.replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark" class="li-row"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="li-controls"> <span class="li-controls">
<span class="filetools controls"> <span class="filetools controls">
{{=editlanguagefile('languages',file)}} {{=editlanguagefile('languages',file)}}
{{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}} {{=deletefile([app, 'languages', file], dict(id=id, id2='languages'))}}
</span> </span>
<span class=""> <span class="">
{{=peekfile('languages',file, dict(id=id))}} {{=peekfile('languages',file, dict(id=id))}}
</span> </span>
</span> <!-- /li-row --> </span> <!-- /li-row -->
<span class="extras celled"> <span class="extras celled">
( (
{{=T("Plural-Forms:")}} {{=T("Plural-Forms:")}}
{{p=languages[lang][3:7]}} {{p=languages[lang][3:7]}}
{{if p[2] == 'default':}} {{if p[2] == 'default':}}
<span class='error text-error'>{{=T("rules are not defined")}}</span> {{=T.M("(file **gluon/contrib/plural_rules/%s.py** is not found)",lang[:2])}} <span class='error text-error'>{{=T("rules are not defined")}}</span> {{=T.M("(file **gluon/contrib/plural_rules/%s.py** is not found)",lang[:2])}}
{{else:}} {{else:}}
{{if p[3] == 1:}} {{if p[3] == 1:}}
{{=B(T("are not used"))}} {{=B(T("are not used"))}}
{{else:}} {{else:}}
{{pfile=p[0]}} {{pfile=p[0]}}
{{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;"> {{if p[1]!=0:}}<span style="display:inline-block;margin-top:-10px;">
<span class="filetools controls"> <span class="filetools controls">
{{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}} {{=editpluralsfile('languages',pfile,dict(nplurals=p[3]))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('languages',pfile,dict(id=id))}} {{=peekfile('languages',pfile,dict(id=id))}}
</span></span> </span></span>
{{else:}} {{else:}}
<b>{{=T("are not used yet")}}</b> <b>{{=T("are not used yet")}}</b>
{{pass}} {{pass}}
{{pass}} {{pass}}
{{pass}} {{pass}}
) )
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form4').slideToggle()" class="btn btn-mini">Create</button> <button onclick="jQuery('#form4').slideToggle()" class="btn btn-mini">Create</button>
<div id="form4" class="row-fluid" style="display:none"> <div id="form4" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/languages/' % app, 'languages', T('(something like "it-it")'))}}</div> <div class="span3">{{=file_create_form('%s/languages/' % app, 'languages', T('(something like "it-it")'))}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- STATIC --> <!-- STATIC -->
<h3 id="_static" rel="pagebookmark"> <h3 id="_static" rel="pagebookmark">
<span class="component" onclick="collapse('static_inner');">{{=T("Static")}}</span> <span class="component" onclick="collapse('static_inner');">{{=T("Static")}}</span>
<a href="#static" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are served without processing, your images go here')}}"> <a href="#static" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are served without processing, your images go here')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("These files are served without processing, your images go here")}}</span> <span>{{=T("These files are served without processing, your images go here")}}</span>
</a><span id="static" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="static" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="static_inner" class="component_contents"> <div id="static_inner" class="component_contents">
{{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}} {{if not statics:}}<p><strong>{{=T("There are no static files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{ {{
path=[] path=[]
for file in statics+['']: for file in statics+['']:
items=file.split('/') items=file.split('/')
file_path=items[:-1] file_path=items[:-1]
filename=items[-1] filename=items[-1]
while path!=file_path: while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]): if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)]) path.append(file_path[len(path)])
thispath='static__'+'__'.join(path) thispath='static__'+'__'.join(path)
}} }}
<li class="folder"><i>&nbsp;</i> <li class="folder"><i>&nbsp;</i>
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a> <a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{ <ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else: else:
path = path[:-1] path = path[:-1]
}} }}
</ul></li> </ul></li>
{{ {{
pass pass
pass pass
if filename: if filename:
}} }}
<li> <li>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}} {{=editfile('static',file, dict(id="static"))}} {{=deletefile([app,'static',file], dict(id="static",id2="static"))}}
</span> </span>
<span class="file"> <span class="file">
<a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a> <a href="{{=URL(a=app,c='static',f=file)}}">{{=filename}}</a>
</span> </span>
</li>{{ </li>{{
pass pass
pass pass
}} }}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form5" class="row-fluid" style="display:none"> <div id="form5" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/static/' % app, 'static')}}<em>{{=T('or alternatively')}}</em></div> <div class="span3">{{=file_create_form('%s/static/' % app, 'static')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div> <div class="span3">{{=file_upload_form('%s/static/' % app, 'static')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- MODULES --> <!-- MODULES -->
<h3 id="_modules" rel="pagebookmark"> <h3 id="_modules" rel="pagebookmark">
<span class="component" onclick="collapse('modules_inner');">{{=T("Modules")}}</span> <span class="component" onclick="collapse('modules_inner');">{{=T("Modules")}}</span>
<a href="#modules" rel="tooltip" data-placement="right" data-original-title="{{=T('Additional code for your application')}}"> <a href="#modules" rel="tooltip" data-placement="right" data-original-title="{{=T('Additional code for your application')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("Additional code for your application")}}</span> <span>{{=T("Additional code for your application")}}</span>
</a><span id="modules" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="modules" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="modules_inner" class="component_contents"> <div id="modules_inner" class="component_contents">
{{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}} {{if not modules:}}<p><strong>{{=T("There are no modules")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for m in modules:}} {{for m in modules:}}
{{id="modules__"+m.replace('/','__').replace('.','__')}} {{id="modules__"+m.replace('/','__').replace('.','__')}}
<li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span> <li id="{{='_'+id}}" rel="pagebookmark"><span id="{{=id}}" class="hashstick">&nbsp;</span>
<span class="filetols controls"> <span class="filetols controls">
{{=editfile('modules',m,dict(id=id))}} {{=editfile('modules',m,dict(id=id))}}
{{if m!='__init__.py':}} {{if m!='__init__.py':}}
{{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}} {{=deletefile([app, 'modules', m], dict(id=id, id2='modules'))}}
{{pass}} {{pass}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('modules',m, dict(id=id))}} {{=peekfile('modules',m, dict(id=id))}}
</span> </span>
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form5').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form6').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form5" class="row-fluid" style="display:none"> <div id="form6" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/modules/' % app, 'modules')}}<em>{{=T('or alternatively')}}</em></div> <div class="span3">{{=file_create_form('%s/modules/' % app, 'modules')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div> <div class="span3">{{=file_upload_form('%s/modules/' % app, 'modules')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- PRIVATE --> <!-- PRIVATE -->
<h3 id="_private" rel="pagebookmark"> <h3 id="_private" rel="pagebookmark">
<span class="component" onclick="collapse('private_inner');">{{=T("Private files")}}</span> <span class="component" onclick="collapse('private_inner');">{{=T("Private files")}}</span>
<a href="#private" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are not served, they are only available from within your app')}}"> <a href="#private" rel="tooltip" data-placement="right" data-original-title="{{=T('These files are not served, they are only available from within your app')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("These files are not served, they are only available from within your app")}}</span> <span>{{=T("These files are not served, they are only available from within your app")}}</span>
</a><span id="private" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="private" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="private_inner" class="component_contents"> <div id="private_inner" class="component_contents">
{{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}} {{if not privates:}}<p><strong>{{=T("There are no private files")}}</strong></p>{{else:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{ {{
path=[] path=[]
for file in privates+['']: for file in privates+['']:
items=file.split('/') items=file.split('/')
file_path=items[:-1] file_path=items[:-1]
filename=items[-1] filename=items[-1]
while path!=file_path: while path!=file_path:
if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]): if len(file_path)>=len(path) and all([v==file_path[k] for k,v in enumerate(path)]):
path.append(file_path[len(path)]) path.append(file_path[len(path)])
thispath='private__'+'__'.join(path) thispath='private__'+'__'.join(path)
}} }}
<li class="folder"> <li class="folder">
<a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a> <a href="javascript:collapse('{{=thispath}}');" class="file">{{=path[-1]}}/</a>
<ul id="{{=thispath}}" style="display: none;" class="sublist">{{ <ul id="{{=thispath}}" style="display: none;" class="sublist">{{
else: else:
path = path[:-1] path = path[:-1]
}} }}
</ul> </ul>
</li> </li>
{{ {{
pass pass
pass pass
if filename: if filename:
}} }}
<li> <li>
<span class="filetools controls"> <span class="filetools controls">
{{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}} {{=editfile('private',file, dict(id="private"))}} {{=deletefile([app,'private',file], dict(id="private",id2="private"))}}
</span> </span>
<span class="file"> <span class="file">
{{=peekfile('private',file, dict(id="private"))}} {{=peekfile('private',file, dict(id="private"))}}
</span> </span>
</li>{{ </li>{{
pass pass
pass pass
}} }}
{{pass}} {{pass}}
</ul> </ul>
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form6').slideToggle()" class="btn btn-mini">Create/Upload</button> <button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Create/Upload</button>
<div id="form6" class="row-fluid" style="display:none"> <div id="form7" class="row-fluid" style="display:none">
<div class="span3">{{=file_create_form('%s/private/' % app, 'private')}}<em>{{=T('or alternatively')}}</em></div> <div class="span3">{{=file_create_form('%s/private/' % app, 'private')}}<em>{{=T('or alternatively')}}</em></div>
<div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div> <div class="span3">{{=file_upload_form('%s/private/' % app, 'private')}}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- PLUGINS --> <!-- PLUGINS -->
<h3 id="_plugins" rel="pagebookmark"> <h3 id="_plugins" rel="pagebookmark">
<span class="component" onclick="collapse('plugins_inner');">{{=T("Plugins")}}</span> <span class="component" onclick="collapse('plugins_inner');">{{=T("Plugins")}}</span>
<a href="#plugins" rel="tooltip" data-placement="right" data-original-title="{{=T('To create a plugin, name a file/folder plugin_[name]')}}"> <a href="#plugins" rel="tooltip" data-placement="right" data-original-title="{{=T('To create a plugin, name a file/folder plugin_[name]')}}">
{{=helpicon()}} {{=helpicon()}}
<span>{{=T("To create a plugin, name a file/folder plugin_[name]")}}</span> <span>{{=T("To create a plugin, name a file/folder plugin_[name]")}}</span>
</a><span id="plugins" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a> </a><span id="plugins" class="hashstick">&nbsp;</span><a href="#" class="tophashlink btn btn-mini btn-warning"><span>top</span></a>
</h3> </h3>
<div id="plugins_inner" class="component_contents"> <div id="plugins_inner" class="component_contents">
<div class="controls comptools"> <div class="controls comptools">
{{=button(PLUGINS_APP, T('download plugins'))}} {{=button(PLUGINS_APP, T('download plugins'))}}
</div> </div>
{{if plugins:}} {{if plugins:}}
<ul class="unstyled act_edit"> <ul class="unstyled act_edit">
{{for plugin in plugins:}} {{for plugin in plugins:}}
{{id="plugins__"+plugin.replace('/','__').replace('.','__')}} {{id="plugins__"+plugin.replace('/','__').replace('.','__')}}
<li id="{{=id}}"> <li id="{{=id}}">
{{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}} {{=A('plugin_%s' % plugin, _class='file', _href=URL('plugin', args=[app, plugin], vars=dict(id=id, id2='plugins')))}}
</li> </li>
{{pass}} {{pass}}
</ul> </ul>
{{else:}} {{else:}}
<p><strong>{{=T('There are no plugins')}}</strong></p> <p><strong>{{=T('There are no plugins')}}</strong></p>
{{pass}} {{pass}}
<div class="controls formfield"> <div class="controls formfield">
<button onclick="jQuery('#form7').slideToggle()" class="btn btn-mini">Upload</button> <button onclick="jQuery('#form8').slideToggle()" class="btn btn-mini">Upload</button>
<div id="form7" class="row-fluid" style="display:none"> <div id="form8" class="row-fluid" style="display:none">
<div class="row-fluid"> <div class="row-fluid">
<div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div> <div class="span3">{{=upload_plugin_form(app, 'plugins')}}</div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
@@ -0,0 +1,32 @@
{{extend 'layout.html'}}
{{import os}}
{{def tree(path):}}
<input type="checkbox" onclick="jQuery(this).parent().find(':checkbox').attr('checked',this.checked)" checked="checked"/>
<a href="#" onclick="jQuery(this).next().slideToggle();return false;">
{{=path[len(base):] or '/%s' % app}}
</a>
<ul class="collapsible">
{{for file in files[path]['files']:}}
<li style="list-style-type: none;">
{{p = os.path.relpath(os.path.join(path,file),base)}}
<input type="checkbox" value="{{=p}}" name="file" checked="checked"/>
{{=file}}
</li>
{{pass}}
{{for dir in files[path]['folders']:}}
<li style="list-style-type: none;">
{{tree(os.path.join(path,dir))}}
</li>
{{pass}}
</ul>
{{return}}
<form action="{{=URL(args=request.args)}}" method="POST">
<h2>{{=T('Select Files to Package')}}</h2>
<input type="submit" value="{{=T('Download .w2p')}}" class="btn"/>
<div style="margin-top:20px">
{{tree(base)}}
</div>
</form>
<script>jQuery(function(){jQuery('.collapsible').hide();});</script>
@@ -25,6 +25,7 @@
{{buttons.append((URL('errors',args=a), T("Errors")))}} {{buttons.append((URL('errors',args=a), T("Errors")))}}
{{buttons.append((URL('cleanup',args=a), T("Clean")))}} {{buttons.append((URL('cleanup',args=a), T("Clean")))}}
{{buttons.append((URL('pack',args=a), T("Pack all")))}} {{buttons.append((URL('pack',args=a), T("Pack all")))}}
{{buttons.append((URL('pack_custom',args=a), T("Pack custom")))}}
{{if not os.path.exists('applications/%s/compiled' % a):}} {{if not os.path.exists('applications/%s/compiled' % a):}}
{{buttons.append((URL('compile_app',args=a), T("Compile")))}} {{buttons.append((URL('compile_app',args=a), T("Compile")))}}
{{else:}} {{else:}}
@@ -54,6 +54,10 @@ response.menu = [[T('design'), False, URL('admin', 'default', 'design',
# ## auxiliary functions # ## auxiliary functions
# ########################################################### # ###########################################################
if False and request.tickets_db:
from gluon.restricted import TicketStorage
ts = TicketStorage()
ts._get_table(request.tickets_db, ts.tablename, request.application)
def get_databases(request): def get_databases(request):
dbs = {} dbs = {}
+1
View File
@@ -0,0 +1 @@
session.connect(request,response,cookie_key='yoursecret')
@@ -47,7 +47,7 @@ Here is a more complex complete application that lets the visitor upload images
`` ``
db=DAL('sqlite://storage.db') db=DAL('sqlite://storage.db')
db.define_table('image', db.define_table('image',
Field('name'), Field('name', notnull=True),
Field('file','upload')) Field('file','upload'))
``:code_python ``:code_python
@@ -55,10 +55,10 @@ db.define_table('image',
`` ``
def index(): def index():
form = SQLFORM(db.image) form = SQLFORM(db.image).process()
if form.process().accepted: if form.accepted:
response.flash = 'image uploaded' response.flash = 'image uploaded'
return dict(form = form)""",counter=None,_class='boxCode')}} return locals()
``:code_python ``:code_python
### In View ### In View
@@ -66,7 +66,7 @@ def index():
`` ``
{{extend 'layout.html'}} {{extend 'layout.html'}}
<h1>Image upload form</h1> <h1>Image upload form</h1>
{{form}} {{=form}}
``:code_python ``:code_python
Uploaded images are safely renamed to avoid directory traversal vulnerabilities, stored on the filesystem (or database) and a corresponding entry is inserted in the database, linking the file. A built-in mechanism prevents involuntary double form submission. All DB IO is transaction safe by default. Any exception in the code causes the transaction to rollback. Uploaded images are safely renamed to avoid directory traversal vulnerabilities, stored on the filesystem (or database) and a corresponding entry is inserted in the database, linking the file. A built-in mechanism prevents involuntary double form submission. All DB IO is transaction safe by default. Any exception in the code causes the transaction to rollback.
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
@@ -43,7 +43,7 @@ random.shuffle(quotes)
<a href="http://www.infoworld.com/slideshow/24605/infoworlds-2012-technology-of-the-year-award-winners-183313#slide23"><img src="{{=URL('static','images/infoworld2012.jpeg')}}" width="200px"/></a><br/> <a href="http://www.infoworld.com/slideshow/24605/infoworlds-2012-technology-of-the-year-award-winners-183313#slide23"><img src="{{=URL('static','images/infoworld2012.jpeg')}}" width="200px"/></a><br/>
<a class="btn btn-danger" href="{{=URL('download')}}" style="margin-top:10px; width:180px; color:white">Download Now</a><br/> <a class="btn btn-danger" href="{{=URL('download')}}" style="margin-top:10px; width:180px; color:white">Download Now</a><br/>
<a class="btn btn-danger" href="http://web2py.com/demo_admin" style="margin-top:10px; width:180px; color:white">Online Demo</a><br/> <a class="btn btn-danger" href="http://web2py.com/demo_admin" style="margin-top:10px; width:180px; color:white">Online Demo</a><br/>
<a class="btn btn-danger" href="http://web2py.com/poweredby" style="margin-top:10px; width:180px; color:white">Sites Powereb by web2py</a> <a class="btn btn-danger" href="http://web2py.com/poweredby" style="margin-top:10px; width:180px; color:white">Sites Powered by web2py</a>
</div> </div>
</div> </div>
</div> </div>
@@ -54,6 +54,10 @@ response.menu = [[T('design'), False, URL('admin', 'default', 'design',
# ## auxiliary functions # ## auxiliary functions
# ########################################################### # ###########################################################
if False and request.tickets_db:
from gluon.restricted import TicketStorage
ts = TicketStorage()
ts._get_table(request.tickets_db, ts.tablename, request.application)
def get_databases(request): def get_databases(request):
dbs = {} dbs = {}
+67 -10
View File
@@ -3,6 +3,7 @@
'!langcode!': 'cs-cz', '!langcode!': 'cs-cz',
'!langname!': 'čeština', '!langname!': 'čeština',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinný výraz, například "pole1=\'nováhodnota\'". Výsledky databázového JOINu nemůžete mazat ani upravovat.', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': 'Kolonka "Upravit" je nepovinný výraz, například "pole1=\'nováhodnota\'". Výsledky databázového JOINu nemůžete mazat ani upravovat.',
'"User Exception" debug mode. An error ticket could be issued!': '"User Exception" debug mode. An error ticket could be issued!',
'%%{Row} in Table': '%%{řádek} v tabulce', '%%{Row} in Table': '%%{řádek} v tabulce',
'%%{Row} selected': 'označených %%{řádek}', '%%{Row} selected': 'označených %%{řádek}',
'%s %%{row} deleted': '%s smazaných %%{záznam}', '%s %%{row} deleted': '%s smazaných %%{záznam}',
@@ -11,6 +12,7 @@
'%Y-%m-%d': '%d.%m.%Y', '%Y-%m-%d': '%d.%m.%Y',
'%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S', '%Y-%m-%d %H:%M:%S': '%d.%m.%Y %H:%M:%S',
'(requires internet access)': '(vyžaduje připojení k internetu)', '(requires internet access)': '(vyžaduje připojení k internetu)',
'(requires internet access, experimental)': '(requires internet access, experimental)',
'(something like "it-it")': '(například "cs-cs")', '(something like "it-it")': '(například "cs-cs")',
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)', '@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(soubor **gluon/contrib/plural_rules/%s.py** nenalezen)',
'@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}', '@markmin\x01Searching: **%s** %%{file}': 'Hledání: **%s** %%{soubor}',
@@ -19,15 +21,18 @@
'Access Control': 'Řízení přístupu', 'Access Control': 'Řízení přístupu',
'Add breakpoint': 'Přidat bod přerušení', 'Add breakpoint': 'Přidat bod přerušení',
'Additional code for your application': 'Další kód pro Vaši aplikaci', 'Additional code for your application': 'Další kód pro Vaši aplikaci',
'Admin design page': 'Admin design page',
'Admin language': 'jazyk rozhraní', 'Admin language': 'jazyk rozhraní',
'Administrative interface': 'pro administrátorské rozhraní klikněte sem', 'Administrative interface': 'pro administrátorské rozhraní klikněte sem',
'Administrative Interface': 'Administrátorské rozhraní', 'Administrative Interface': 'Administrátorské rozhraní',
'administrative interface': 'rozhraní pro správu', 'administrative interface': 'rozhraní pro správu',
'Administrator Password:': 'Administrátorské heslo:', 'Administrator Password:': 'Administrátorské heslo:',
'Ajax Recipes': 'Recepty s ajaxem', 'Ajax Recipes': 'Recepty s ajaxem',
'An error occured, please %s the page': 'An error occured, please %s the page',
'and rename it:': 'a přejmenovat na:', 'and rename it:': 'a přejmenovat na:',
'appadmin': 'appadmin', 'appadmin': 'appadmin',
'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení', 'appadmin is disabled because insecure channel': 'appadmin je zakázaná bez zabezpečeného spojení',
'Application': 'Application',
'application "%s" uninstalled': 'application "%s" odinstalována', 'application "%s" uninstalled': 'application "%s" odinstalována',
'application compiled': 'aplikace zkompilována', 'application compiled': 'aplikace zkompilována',
'Application name:': 'Název aplikace:', 'Application name:': 'Název aplikace:',
@@ -36,8 +41,13 @@
'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?', 'Are you sure you want to delete this object?': 'Opravdu chcete odstranit tento objekt?',
'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?', 'Are you sure you want to uninstall application "%s"?': 'Opravdu chcete odinstalovat aplikaci "%s"?',
'arguments': 'arguments', 'arguments': 'arguments',
'at char %s': 'at char %s',
'at line %s': 'at line %s',
'ATTENTION:': 'ATTENTION:',
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.',
'Available Databases and Tables': 'Dostupné databáze a tabulky', 'Available Databases and Tables': 'Dostupné databáze a tabulky',
'back': 'zpět', 'back': 'zpět',
'Back to wizard': 'Back to wizard',
'Basics': 'Basics', 'Basics': 'Basics',
'Begin': 'Začít', 'Begin': 'Začít',
'breakpoint': 'bod přerušení', 'breakpoint': 'bod přerušení',
@@ -51,7 +61,8 @@
'can be a git repo': 'může to být git repo', 'can be a git repo': 'může to být git repo',
'Cancel': 'Storno', 'Cancel': 'Storno',
'Cannot be empty': 'Nemůže být prázdné', 'Cannot be empty': 'Nemůže být prázdné',
'Change admin password': 'Změnit heslo pro strávu aplikací', 'Change Admin Password': 'Změnit heslo pro správu',
'Change admin password': 'Změnit heslo pro správu aplikací',
'Change password': 'Změna hesla', 'Change password': 'Změna hesla',
'check all': 'vše označit', 'check all': 'vše označit',
'Check for upgrades': 'Zkusit aktualizovat', 'Check for upgrades': 'Zkusit aktualizovat',
@@ -94,6 +105,7 @@
'data uploaded': 'data nahrána', 'data uploaded': 'data nahrána',
'Database': 'Rozhraní databáze', 'Database': 'Rozhraní databáze',
'Database %s select': 'databáze %s výběr', 'Database %s select': 'databáze %s výběr',
'Database administration': 'Database administration',
'database administration': 'správa databáze', 'database administration': 'správa databáze',
'Date and Time': 'Datum a čas', 'Date and Time': 'Datum a čas',
'day': 'den', 'day': 'den',
@@ -104,6 +116,7 @@
'Delete': 'Smazat', 'Delete': 'Smazat',
'delete': 'smazat', 'delete': 'smazat',
'delete all checked': 'smazat vše označené', 'delete all checked': 'smazat vše označené',
'delete plugin': 'delete plugin',
'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete požádán o potvrzení mazání)', 'Delete this file (you will be asked to confirm deletion)': 'Smazat tento soubor (budete požádán o potvrzení mazání)',
'Delete:': 'Smazat:', 'Delete:': 'Smazat:',
'deleted after first hit': 'smazat po prvním dosažení', 'deleted after first hit': 'smazat po prvním dosažení',
@@ -114,9 +127,9 @@
'Deployment Recipes': 'Postupy pro deployment', 'Deployment Recipes': 'Postupy pro deployment',
'Description': 'Popis', 'Description': 'Popis',
'design': 'návrh', 'design': 'návrh',
'Detailed traceback description': 'Detailed traceback description', 'Detailed traceback description': 'Podrobný výpis prostředí',
'details': 'details', 'details': 'podrobnosti',
'direction: ltr': 'direction: ltr', 'direction: ltr': 'směr: ltr',
'Disable': 'Zablokovat', 'Disable': 'Zablokovat',
'DISK': 'DISK', 'DISK': 'DISK',
'Disk Cache Keys': 'Klíče diskové cache', 'Disk Cache Keys': 'Klíče diskové cache',
@@ -132,29 +145,37 @@
'Edit': 'Upravit', 'Edit': 'Upravit',
'edit all': 'edit all', 'edit all': 'edit all',
'Edit application': 'Správa aplikace', 'Edit application': 'Správa aplikace',
'edit controller': 'edit controller',
'Edit current record': 'Upravit aktuální záznam', 'Edit current record': 'Upravit aktuální záznam',
'Edit Profile': 'Upravit profil', 'Edit Profile': 'Upravit profil',
'edit views:': 'upravit pohled:', 'edit views:': 'upravit pohled:',
'Editing file "%s"': 'Úprava souboru "%s"', 'Editing file "%s"': 'Úprava souboru "%s"',
'Editing Language file': 'Úprava jazykového souboru', 'Editing Language file': 'Úprava jazykového souboru',
'Editing Plural Forms File': 'Editing Plural Forms File',
'Email and SMS': 'Email a SMS', 'Email and SMS': 'Email a SMS',
'Enable': 'Odblokovat', 'Enable': 'Odblokovat',
'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g', 'enter a number between %(min)g and %(max)g': 'zadejte číslo mezi %(min)g a %(max)g',
'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g', 'enter an integer between %(min)g and %(max)g': 'zadejte celé číslo mezi %(min)g a %(max)g',
'Error': 'Chyba', 'Error': 'Chyba',
'Error logs for "%(app)s"': 'Seznam výskytu chyb pro aplikaci "%(app)s"', 'Error logs for "%(app)s"': 'Seznam výskytu chyb pro aplikaci "%(app)s"',
'Error snapshot': 'Error snapshot', 'Error snapshot': 'Snapshot chyby',
'Error ticket': 'Error ticket', 'Error ticket': 'Ticket chyby',
'Errors': 'Chyby', 'Errors': 'Chyby',
'Exception instance attributes': 'Exception instance attributes', 'Exception %(extype)s: %(exvalue)s': 'Exception %(extype)s: %(exvalue)s',
'Exception %s': 'Exception %s',
'Exception instance attributes': 'Prvky instance výjimky',
'Expand Abbreviation': 'Expand Abbreviation',
'export as csv file': 'exportovat do .csv souboru', 'export as csv file': 'exportovat do .csv souboru',
'exposes': 'vystavuje', 'exposes': 'vystavuje',
'exposes:': 'vystavuje funkce:', 'exposes:': 'vystavuje funkce:',
'extends': 'rozšiřuje', 'extends': 'rozšiřuje',
'failed to compile file because:': 'soubor se nepodařilo zkompilovat, protože:',
'FAQ': 'Často kladené dotazy', 'FAQ': 'Často kladené dotazy',
'File': 'Soubor', 'File': 'Soubor',
'file': 'soubor', 'file': 'soubor',
'file "%(filename)s" created': 'file "%(filename)s" created',
'file saved on %(time)s': 'soubor uložen %(time)s', 'file saved on %(time)s': 'soubor uložen %(time)s',
'file saved on %s': 'soubor uložen %s',
'Filename': 'Název souboru', 'Filename': 'Název souboru',
'filter': 'filtr', 'filter': 'filtr',
'Find Next': 'Najít další', 'Find Next': 'Najít další',
@@ -165,12 +186,15 @@
'Forms and Validators': 'Formuláře a validátory', 'Forms and Validators': 'Formuláře a validátory',
'Frames': 'Frames', 'Frames': 'Frames',
'Free Applications': 'Aplikace zdarma', 'Free Applications': 'Aplikace zdarma',
'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.',
'Generate': 'Vytvořit', 'Generate': 'Vytvořit',
'Get from URL:': 'Stáhnout z internetu:', 'Get from URL:': 'Stáhnout z internetu:',
'Git Pull': 'Git Pull', 'Git Pull': 'Git Pull',
'Git Push': 'Git Push', 'Git Push': 'Git Push',
'Globals##debug': 'Globální proměnné', 'Globals##debug': 'Globální proměnné',
'go!': 'OK!', 'go!': 'OK!',
'Goto': 'Goto',
'graph model': 'graph model',
'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena', 'Group %(group_id)s created': 'Skupina %(group_id)s vytvořena',
'Group ID': 'ID skupiny', 'Group ID': 'ID skupiny',
'Groups': 'Skupiny', 'Groups': 'Skupiny',
@@ -181,6 +205,8 @@
'Home': 'Domovská stránka', 'Home': 'Domovská stránka',
'honored only if the expression evaluates to true': 'brát v potaz jen když se tato podmínka vyhodnotí kladně', 'honored only if the expression evaluates to true': 'brát v potaz jen když se tato podmínka vyhodnotí kladně',
'How did you get here?': 'Jak jste se sem vlastně dostal?', 'How did you get here?': 'Jak jste se sem vlastně dostal?',
'If start the upgrade, be patient, it may take a while to download': 'If start the upgrade, be patient, it may take a while to download',
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
'import': 'import', 'import': 'import',
'Import/Export': 'Import/Export', 'Import/Export': 'Import/Export',
'includes': 'zahrnuje', 'includes': 'zahrnuje',
@@ -190,8 +216,8 @@
'inspect attributes': 'inspect attributes', 'inspect attributes': 'inspect attributes',
'Install': 'Instalovat', 'Install': 'Instalovat',
'Installed applications': 'Nainstalované aplikace', 'Installed applications': 'Nainstalované aplikace',
'Interaction at %s line %s': 'Interaction at %s line %s', 'Interaction at %s line %s': 'Interakce v %s, na řádce %s',
'Interactive console': 'Interactive console', 'Interactive console': 'Interaktivní příkazová řádka',
'Internal State': 'Vnitřní stav', 'Internal State': 'Vnitřní stav',
'Introduction': 'Úvod', 'Introduction': 'Úvod',
'Invalid email': 'Neplatný email', 'Invalid email': 'Neplatný email',
@@ -203,6 +229,7 @@
'It is %s %%{day} today.': 'Dnes je to %s %%{den}.', 'It is %s %%{day} today.': 'Dnes je to %s %%{den}.',
'Key': 'Klíč', 'Key': 'Klíč',
'Key bindings': 'Vazby klíčů', 'Key bindings': 'Vazby klíčů',
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
'languages': 'jazyky', 'languages': 'jazyky',
'Languages': 'Jazyky', 'Languages': 'Jazyky',
'Last name': 'Příjmení', 'Last name': 'Příjmení',
@@ -227,6 +254,7 @@
'Lost Password': 'Zapomněl jste heslo', 'Lost Password': 'Zapomněl jste heslo',
'Lost password?': 'Zapomněl jste heslo?', 'Lost password?': 'Zapomněl jste heslo?',
'lost password?': 'zapomněl jste heslo?', 'lost password?': 'zapomněl jste heslo?',
'Manage': 'Manage',
'Manage Cache': 'Manage Cache', 'Manage Cache': 'Manage Cache',
'Menu Model': 'Model rozbalovací nabídky', 'Menu Model': 'Model rozbalovací nabídky',
'Models': 'Modely', 'Models': 'Modely',
@@ -237,6 +265,7 @@
'modules': 'moduly', 'modules': 'moduly',
'My Sites': 'Správa aplikací', 'My Sites': 'Správa aplikací',
'Name': 'Jméno', 'Name': 'Jméno',
'new application "%s" created': 'nová aplikace "%s" vytvořena',
'New Application Wizard': 'Nový průvodce aplikací', 'New Application Wizard': 'Nový průvodce aplikací',
'New application wizard': 'Nový průvodce aplikací', 'New application wizard': 'Nový průvodce aplikací',
'New password': 'Nové heslo', 'New password': 'Nové heslo',
@@ -245,13 +274,16 @@
'New simple application': 'Vytvořit primitivní aplikaci', 'New simple application': 'Vytvořit primitivní aplikaci',
'next': 'next', 'next': 'next',
'next 100 rows': 'dalších 100 řádků', 'next 100 rows': 'dalších 100 řádků',
'No databases in this application': 'V této aplikáci nejsou žádné databáze', 'No databases in this application': 'V této aplikaci nejsou žádné databáze',
'No Interaction yet': 'Ještě žádná interakce nenastala', 'No Interaction yet': 'Ještě žádná interakce nenastala',
'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adresáři /private nenalezen', 'No ticket_storage.txt found under /private folder': 'Soubor ticket_storage.txt v adresáři /private nenalezen',
'Object or table name': 'Objekt či tabulka', 'Object or table name': 'Objekt či tabulka',
'Old password': 'Původní heslo', 'Old password': 'Původní heslo',
'online designer': 'online návrhář', 'online designer': 'online návrhář',
'Online examples': 'Příklady online', 'Online examples': 'Příklady online',
'Open new app in new window': 'Open new app in new window',
'or alternatively': 'or alternatively',
'Or Get from URL:': 'Or Get from URL:',
'or import from csv file': 'nebo importovat z .csv souboru', 'or import from csv file': 'nebo importovat z .csv souboru',
'Origin': 'Původ', 'Origin': 'Původ',
'Original/Translation': 'Originál/Překlad', 'Original/Translation': 'Originál/Překlad',
@@ -261,12 +293,16 @@
'Overwrite installed app': 'Přepsat instalovanou aplikaci', 'Overwrite installed app': 'Přepsat instalovanou aplikaci',
'Pack all': 'Zabalit', 'Pack all': 'Zabalit',
'Pack compiled': 'Zabalit zkompilované', 'Pack compiled': 'Zabalit zkompilované',
'pack plugin': 'pack plugin',
'password': 'heslo', 'password': 'heslo',
'Password': 'Heslo', 'Password': 'Heslo',
"Password fields don't match": 'Hesla se neshodují', "Password fields don't match": 'Hesla se neshodují',
'Peeking at file': 'Peeking at file',
'Please': 'Prosím', 'Please': 'Prosím',
'Plugin "%s" in application': 'Plugin "%s" in application',
'plugins': 'zásuvné moduly', 'plugins': 'zásuvné moduly',
'Plugins': 'Zásuvné moduly', 'Plugins': 'Zásuvné moduly',
'Plural Form #%s': 'Plural Form #%s',
'Plural-Forms:': 'Množná čísla:', 'Plural-Forms:': 'Množná čísla:',
'Powered by': 'Poháněno', 'Powered by': 'Poháněno',
'Preface': 'Předmluva', 'Preface': 'Předmluva',
@@ -292,6 +328,7 @@
'Register': 'Zaregistrovat se', 'Register': 'Zaregistrovat se',
'Registration identifier': 'Registrační identifikátor', 'Registration identifier': 'Registrační identifikátor',
'Registration key': 'Registrační klíč', 'Registration key': 'Registrační klíč',
'reload': 'reload',
'Reload routes': 'Znovu nahrát cesty', 'Reload routes': 'Znovu nahrát cesty',
'Remember me (for 30 days)': 'Zapamatovat na 30 dní', 'Remember me (for 30 days)': 'Zapamatovat na 30 dní',
'Remove compiled': 'Odstranit zkompilované', 'Remove compiled': 'Odstranit zkompilované',
@@ -313,6 +350,7 @@
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spustí testy v tomto souboru (ke spuštění všech testů, použijte tlačítko 'test')", "Run tests in this file (to run all files, you may also use the button labelled 'test')": "Spustí testy v tomto souboru (ke spuštění všech testů, použijte tlačítko 'test')",
'Running on %s': 'Běží na %s', 'Running on %s': 'Běží na %s',
'Save': 'Uložit', 'Save': 'Uložit',
'Save file:': 'Save file:',
'Save via Ajax': 'Uložit pomocí Ajaxu', 'Save via Ajax': 'Uložit pomocí Ajaxu',
'Saved file hash:': 'hash uloženého souboru:', 'Saved file hash:': 'hash uloženého souboru:',
'Semantic': 'Modul semantic', 'Semantic': 'Modul semantic',
@@ -321,6 +359,7 @@
'session expired': 'session expired', 'session expired': 'session expired',
'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s', 'Set Breakpoint on %s at line %s: %s': 'Bod přerušení nastaven v souboru %s na řádce %s: %s',
'shell': 'příkazová řádka', 'shell': 'příkazová řádka',
'Singular Form': 'Singular Form',
'Site': 'Správa aplikací', 'Site': 'Správa aplikací',
'Size of cache:': 'Velikost cache:', 'Size of cache:': 'Velikost cache:',
'skip to generate': 'skip to generate', 'skip to generate': 'skip to generate',
@@ -329,6 +368,7 @@
'Start searching': 'Začít hledání', 'Start searching': 'Začít hledání',
'Start wizard': 'Spustit průvodce', 'Start wizard': 'Spustit průvodce',
'state': 'stav', 'state': 'stav',
'Static': 'Static',
'static': 'statické soubory', 'static': 'statické soubory',
'Static files': 'Statické soubory', 'Static files': 'Statické soubory',
'Statistics': 'Statistika', 'Statistics': 'Statistika',
@@ -345,6 +385,7 @@
'Table name': 'Název tabulky', 'Table name': 'Název tabulky',
'Temporary': 'Dočasný', 'Temporary': 'Dočasný',
'test': 'test', 'test': 'test',
'Testing application': 'Testing application',
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podmínka, například "db.tabulka1.pole1==\'hodnota\'". Podmínka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvoří SQL JOIN.', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': '"Dotaz" je podmínka, například "db.tabulka1.pole1==\'hodnota\'". Podmínka "db.tabulka1.pole1==db.tabulka2.pole2" pak vytvoří SQL JOIN.',
'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: každá URL je mapována na funkci vystavovanou kontrolérem.', 'The application logic, each URL path is mapped in one exposed function in the controller': 'Logika aplikace: každá URL je mapována na funkci vystavovanou kontrolérem.',
'The Core': 'Jádro (The Core)', 'The Core': 'Jádro (The Core)',
@@ -352,13 +393,19 @@
'The output of the file is a dictionary that was rendered by the view %s': 'Výstup ze souboru je slovník, který se zobrazil v pohledu %s.', 'The output of the file is a dictionary that was rendered by the view %s': 'Výstup ze souboru je slovník, který se zobrazil v pohledu %s.',
'The presentations layer, views are also known as templates': 'Prezentační vrstva: pohledy či templaty (šablony)', 'The presentations layer, views are also known as templates': 'Prezentační vrstva: pohledy či templaty (šablony)',
'The Views': 'Pohledy (The Views)', 'The Views': 'Pohledy (The Views)',
'There are no controllers': 'There are no controllers',
'There are no modules': 'There are no modules',
'There are no plugins': 'Žádné moduly nejsou instalovány.', 'There are no plugins': 'Žádné moduly nejsou instalovány.',
'There are no private files': 'Žádné soukromé soubory neexistují.', 'There are no private files': 'Žádné soukromé soubory neexistují.',
'There are no static files': 'There are no static files', 'There are no static files': 'There are no static files',
'There are no translators, only default language is supported': 'There are no translators, only default language is supported',
'There are no views': 'There are no views',
'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.', 'These files are not served, they are only available from within your app': 'Tyto soubory jsou klientům nepřístupné. K dispozici jsou pouze v rámci aplikace.',
'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.', 'These files are served without processing, your images go here': 'Tyto soubory jsou servírovány bez přídavné logiky, sem patří např. obrázky.',
'This App': 'Tato aplikace', 'This App': 'Tato aplikace',
'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.', 'This is a copy of the scaffolding application': 'Toto je kopie aplikace skelet.',
'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk': 'This is an experimental feature and it needs more testing. If you decide to upgrade you do it at your own risk',
'This is the %(filename)s template': 'This is the %(filename)s template',
'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto stránku, abyste uviděli, zda se dosáhlo bodu přerušení.', 'this page to see if a breakpoint was hit and debug interaction is required.': 'tuto stránku, abyste uviděli, zda se dosáhlo bodu přerušení.',
'Ticket': 'Ticket', 'Ticket': 'Ticket',
'Ticket ID': 'Ticket ID', 'Ticket ID': 'Ticket ID',
@@ -373,16 +420,24 @@
'too short': 'Příliš krátké', 'too short': 'Příliš krátké',
'Traceback': 'Traceback', 'Traceback': 'Traceback',
'Translation strings for the application': 'Překlad textů pro aplikaci', 'Translation strings for the application': 'Překlad textů pro aplikaci',
'try something like': 'try something like',
'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení', 'Try the mobile interface': 'Zkuste rozhraní pro mobilní zařízení',
'try view': 'try view',
'Twitter': 'Twitter', 'Twitter': 'Twitter',
'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Type python statement in here and hit Return (Enter) to execute it.',
'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.',
'Unable to check for upgrades': 'Unable to check for upgrades',
'unable to parse csv file': 'csv soubor nedá sa zpracovat', 'unable to parse csv file': 'csv soubor nedá sa zpracovat',
'uncheck all': 'vše odznačit', 'uncheck all': 'vše odznačit',
'Uninstall': 'Odinstalovat', 'Uninstall': 'Odinstalovat',
'update': 'aktualizovat', 'update': 'aktualizovat',
'update all languages': 'aktualizovat všechny jazyky', 'update all languages': 'aktualizovat všechny jazyky',
'Update:': 'Upravit:', 'Update:': 'Upravit:',
'Upgrade': 'Upgrade',
'upgrade now': 'upgrade now',
'upgrade now to %s': 'upgrade now to %s',
'upload': 'nahrát', 'upload': 'nahrát',
'Upload': 'Upload',
'Upload a package:': 'Nahrát balík:', 'Upload a package:': 'Nahrát balík:',
'Upload and install packed application': 'Nahrát a instalovat zabalenou aplikaci', 'Upload and install packed application': 'Nahrát a instalovat zabalenou aplikaci',
'upload file:': 'nahrát soubor:', 'upload file:': 'nahrát soubor:',
@@ -409,6 +464,8 @@
'web2py is up to date': 'Máte aktuální verzi web2py.', 'web2py is up to date': 'Máte aktuální verzi web2py.',
'web2py online debugger': 'Ladící online web2py program', 'web2py online debugger': 'Ladící online web2py program',
'web2py Recent Tweets': 'Štěbetání na Twitteru o web2py', 'web2py Recent Tweets': 'Štěbetání na Twitteru o web2py',
'web2py upgrade': 'web2py upgrade',
'web2py upgraded; please restart it': 'web2py upgraded; please restart it',
'Welcome': 'Vítejte', 'Welcome': 'Vítejte',
'Welcome to web2py': 'Vitejte ve web2py', 'Welcome to web2py': 'Vitejte ve web2py',
'Welcome to web2py!': 'Vítejte ve web2py!', 'Welcome to web2py!': 'Vítejte ve web2py!',
+65 -65
View File
@@ -8,23 +8,23 @@
'%s selected': '%s seleccionado(s)', '%s selected': '%s seleccionado(s)',
'%Y-%m-%d': '%Y-%m-%d', '%Y-%m-%d': '%Y-%m-%d',
'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S',
'(something like "it-it")': '(algo como "it-it")', '(something like "it-it")': '(algo como "eso-eso")',
'A new version of web2py is available': 'Hay una nueva versión de web2py disponible', 'A new version of web2py is available': 'Hay una nueva versión de web2py disponible',
'A new version of web2py is available: %s': 'Hay una nueva versión de web2py disponible: %s', 'A new version of web2py is available: %s': 'Hay una nueva versión de web2py disponible: %s',
'about': 'acerca de', 'about': 'acerca de',
'About': 'Acerca de', 'About': 'Acerca de',
'About application': 'Acerca de la aplicación', 'About application': 'Acerca de la aplicación',
'Access Control': 'Access Control', 'Access Control': 'Control de Acceso',
'additional code for your application': 'código adicional para su aplicación', 'additional code for your application': 'código adicional para su aplicación',
'admin disabled because no admin password': ' por falta de contraseña', 'admin disabled because no admin password': ' por falta de contraseña',
'admin disabled because not supported on google app engine': 'admin deshabilitado, no es soportado en GAE', 'admin disabled because not supported on google app engine': 'admin deshabilitado, no es soportado en GAE',
'admin disabled because unable to access password file': 'admin deshabilitado, imposible acceder al archivo con la contraseña', 'admin disabled because unable to access password file': 'admin deshabilitado, imposible acceder al archivo con la contraseña',
'Admin is disabled because insecure channel': 'Admin deshabilitado, el canal no es seguro', 'Admin is disabled because insecure channel': 'Admin deshabilitado, el canal no es seguro',
'Admin is disabled because unsecure channel': 'Admin deshabilitado, el canal no es seguro', 'Admin is disabled because unsecure channel': 'Admin deshabilitado, el canal no es seguro',
'Administrative Interface': 'Administrative Interface', 'Administrative Interface': 'Interfaz Administrativa',
'Administrative interface': 'Interfaz administrativa', 'Administrative interface': 'Interfaz administrativa',
'Administrator Password:': 'Contraseña del Administrador:', 'Administrator Password:': 'Contraseña del Administrador:',
'Ajax Recipes': 'Ajax Recipes', 'Ajax Recipes': 'Recetas AJAX',
'and rename it (required):': 'y renombrela (requerido):', 'and rename it (required):': 'y renombrela (requerido):',
'and rename it:': ' y renombrelo:', 'and rename it:': ' y renombrelo:',
'appadmin': 'appadmin', 'appadmin': 'appadmin',
@@ -33,37 +33,37 @@
'application compiled': 'aplicación compilada', 'application compiled': 'aplicación compilada',
'application is compiled and cannot be designed': 'la aplicación está compilada y no puede ser modificada', 'application is compiled and cannot be designed': 'la aplicación está compilada y no puede ser modificada',
'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?', 'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', 'Are you sure you want to delete this object?': '¿Está seguro que desea borrar este objeto?',
'Are you sure you want to uninstall application "%s"': '¿Está seguro que desea desinstalar la aplicación "%s"', 'Are you sure you want to uninstall application "%s"': '¿Está seguro que desea desinstalar la aplicación "%s"',
'Are you sure you want to uninstall application "%s"?': '¿Está seguro que desea desinstalar la aplicación "%s"?', 'Are you sure you want to uninstall application "%s"?': '¿Está seguro que desea desinstalar la aplicación "%s"?',
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCION: Inicio de sesión requiere una conexión segura (HTTPS) o localhost.', 'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCION: Inicio de sesión requiere una conexión segura (HTTPS) o localhost.',
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.',
'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que se ejecuta!', 'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que está ejecutandose!',
'Authentication': 'Autenticación', 'Authentication': 'Autenticación',
'Available Databases and Tables': 'Bases de datos y tablas disponibles', 'Available Databases and Tables': 'Bases de datos y tablas disponibles',
'Buy this book': 'Buy this book', 'Buy this book': 'Compra este libro',
'cache': 'cache', 'cache': 'cache',
'Cache': 'Cache', 'Cache': 'Cache',
'Cache Keys': 'Cache Keys', 'Cache Keys': 'Llaves de la Cache',
'cache, errors and sessions cleaned': 'cache, errores y sesiones eliminados', 'cache, errors and sessions cleaned': 'cache, errores y sesiones eliminados',
'Cannot be empty': 'No puede estar vacío', 'Cannot be empty': 'No puede estar vacío',
'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.', 'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.',
'cannot create file': 'no es posible crear archivo', 'cannot create file': 'no es posible crear archivo',
'cannot upload file "%(filename)s"': 'no es posible subir archivo "%(filename)s"', 'cannot upload file "%(filename)s"': 'no es posible subir archivo "%(filename)s"',
'Change Password': 'Cambie Contraseña', 'Change Password': 'Cambie la contraseña',
'change password': 'cambie contraseña', 'change password': 'cambie la contraseña',
'check all': 'marcar todos', 'check all': 'marcar todos',
'Check to delete': 'Marque para eliminar', 'Check to delete': 'Marque para eliminar',
'clean': 'limpiar', 'clean': 'limpiar',
'Clear CACHE?': 'Clear CACHE?', 'Clear CACHE?': '¿Limpiar CACHE?',
'Clear DISK': 'Clear DISK', 'Clear DISK': '¿Limpiar DISCO',
'Clear RAM': 'Clear RAM', 'Clear RAM': '¿Limpiar RAM',
'click to check for upgrades': 'haga clic para buscar actualizaciones', 'click to check for upgrades': 'haga clic para buscar actualizaciones',
'Client IP': 'IP del Cliente', 'Client IP': 'IP del Cliente',
'Community': 'Community', 'Community': 'Comunidad',
'compile': 'compilar', 'compile': 'compilar',
'compiled application removed': 'aplicación compilada removida', 'compiled application removed': 'aplicación compilada removida',
'Components and Plugins': 'Components and Plugins', 'Components and Plugins': 'Componentes y Plugins',
'Controller': 'Controlador', 'Controller': 'Controlador',
'Controllers': 'Controladores', 'Controllers': 'Controladores',
'controllers': 'controladores', 'controllers': 'controladores',
@@ -83,24 +83,24 @@
'database administration': 'administración base de datos', 'database administration': 'administración base de datos',
'Date and Time': 'Fecha y Hora', 'Date and Time': 'Fecha y Hora',
'db': 'db', 'db': 'db',
'DB Model': 'Modelo "db"', 'DB Model': 'Modelo "DB"',
'defines tables': 'define tablas', 'defines tables': 'define tablas',
'Delete': 'Elimine', 'Delete': 'Eliminar',
'delete': 'eliminar', 'delete': 'eliminar',
'delete all checked': 'eliminar marcados', 'delete all checked': 'eliminar marcados',
'Delete:': 'Elimine:', 'Delete:': 'Eliminar:',
'Demo': 'Demo', 'Demo': 'Demo',
'Deploy on Google App Engine': 'Instale en Google App Engine', 'Deploy on Google App Engine': 'Despliegue en Google App Engine',
'Deployment Recipes': 'Deployment Recipes', 'Deployment Recipes': 'Recetas de despliegue',
'Description': 'Descripción', 'Description': 'Descripción',
'DESIGN': 'DISEÑO', 'DESIGN': 'DISEÑO',
'design': 'modificar', 'design': 'modificar',
'Design for': 'Diseño para', 'Design for': 'Diseño por',
'DISK': 'DISK', 'DISK': 'DISK',
'Disk Cache Keys': 'Disk Cache Keys', 'Disk Cache Keys': 'Llaves de Cache en Disco',
'Disk Cleared': 'Disk Cleared', 'Disk Cleared': 'Disco limpiado',
'Documentation': 'Documentación', 'Documentation': 'Documentación',
"Don't know what to do?": "Don't know what to do?", "Don't know what to do?": "¿No sabe que hacer?",
'done!': 'listo!', 'done!': 'listo!',
'Download': 'Download', 'Download': 'Download',
'E-mail': 'Correo electrónico', 'E-mail': 'Correo electrónico',
@@ -115,9 +115,9 @@
'Edit This App': 'Edite esta App', 'Edit This App': 'Edite esta App',
'Editing file': 'Editando archivo', 'Editing file': 'Editando archivo',
'Editing file "%s"': 'Editando archivo "%s"', 'Editing file "%s"': 'Editando archivo "%s"',
'Email and SMS': 'Email and SMS', 'Email and SMS': 'Correo electrónico y SMS',
'Error logs for "%(app)s"': 'Bitácora de errores en "%(app)s"', 'Error logs for "%(app)s"': 'Bitácora de errores en "%(app)s"',
'Errors': 'Errors', 'Errors': 'Errores',
'errors': 'errores', 'errors': 'errores',
'export as csv file': 'exportar como archivo CSV', 'export as csv file': 'exportar como archivo CSV',
'exposes': 'expone', 'exposes': 'expone',
@@ -134,17 +134,17 @@
'file saved on %(time)s': 'archivo guardado %(time)s', 'file saved on %(time)s': 'archivo guardado %(time)s',
'file saved on %s': 'archivo guardado %s', 'file saved on %s': 'archivo guardado %s',
'First name': 'Nombre', 'First name': 'Nombre',
'Forms and Validators': 'Forms and Validators', 'Forms and Validators': 'Formularios y validadores',
'Free Applications': 'Free Applications', 'Free Applications': 'Aplicaciones Libres',
'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].', 'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].',
'Group ID': 'ID de Grupo', 'Group ID': 'ID de Grupo',
'Groups': 'Groups', 'Groups': 'Groupos',
'Hello World': 'Hola Mundo', 'Hello World': 'Hola Mundo',
'help': 'ayuda', 'help': 'ayuda',
'Home': 'Home', 'Home': 'Home',
'How did you get here?': 'How did you get here?', 'How did you get here?': '¿Cómo llegasta hasta allá?',
'htmledit': 'htmledit', 'htmledit': 'htmledit',
'import': 'import', 'import': 'importar',
'Import/Export': 'Importar/Exportar', 'Import/Export': 'Importar/Exportar',
'includes': 'incluye', 'includes': 'incluye',
'Index': 'Indice', 'Index': 'Indice',
@@ -153,14 +153,14 @@
'Installed applications': 'Aplicaciones instaladas', 'Installed applications': 'Aplicaciones instaladas',
'internal error': 'error interno', 'internal error': 'error interno',
'Internal State': 'Estado Interno', 'Internal State': 'Estado Interno',
'Introduction': 'Introduction', 'Introduction': 'Introducción',
'Invalid action': 'Acción inválida', 'Invalid action': 'Acción inválida',
'Invalid email': 'Correo inválido', 'Invalid email': 'Correo electrónico inválido',
'invalid password': 'contraseña inválida', 'invalid password': 'contraseña inválida',
'Invalid Query': 'Consulta inválida', 'Invalid Query': 'Consulta inválida',
'invalid request': 'solicitud inválida', 'invalid request': 'solicitud inválida',
'invalid ticket': 'tiquete inválido', 'invalid ticket': 'tiquete inválido',
'Key': 'Key', 'Key': 'Llave',
'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado', 'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado',
'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados', 'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados',
'languages': 'lenguajes', 'languages': 'lenguajes',
@@ -169,10 +169,10 @@
'Last name': 'Apellido', 'Last name': 'Apellido',
'Last saved on:': 'Guardado en:', 'Last saved on:': 'Guardado en:',
'Layout': 'Diseño de página', 'Layout': 'Diseño de página',
'Layout Plugins': 'Layout Plugins', 'Layout Plugins': 'Plugins de diseño',
'Layouts': 'Layouts', 'Layouts': 'Diseños de páginas',
'License for': 'Licencia para', 'License for': 'Licencia para',
'Live Chat': 'Live Chat', 'Live Chat': 'Chat en vivo',
'loading...': 'cargando...', 'loading...': 'cargando...',
'login': 'inicio de sesión', 'login': 'inicio de sesión',
'Login': 'Inicio de sesión', 'Login': 'Inicio de sesión',
@@ -180,16 +180,16 @@
'logout': 'fin de sesión', 'logout': 'fin de sesión',
'Logout': 'Fin de sesión', 'Logout': 'Fin de sesión',
'Lost Password': 'Contraseña perdida', 'Lost Password': 'Contraseña perdida',
'lost password?': '¿olvido la contraseña?', 'lost password?': '¿Olvido la contraseña?',
'Main Menu': 'Menú principal', 'Main Menu': 'Menú principal',
'Manage Cache': 'Manage Cache', 'Manage Cache': 'Manejar la Cache',
'Menu Model': 'Modelo "menu"', 'Menu Model': 'Modelo "menu"',
'merge': 'combinar', 'merge': 'combinar',
'Models': 'Modelos', 'Models': 'Modelos',
'models': 'modelos', 'models': 'modelos',
'Modules': 'Módulos', 'Modules': 'Módulos',
'modules': 'módulos', 'modules': 'módulos',
'My Sites': 'My Sites', 'My Sites': 'Mis Sitios',
'Name': 'Nombre', 'Name': 'Nombre',
'new application "%s" created': 'nueva aplicación "%s" creada', 'new application "%s" created': 'nueva aplicación "%s" creada',
'New Record': 'Registro nuevo', 'New Record': 'Registro nuevo',
@@ -202,9 +202,9 @@
'or provide application url:': 'o provea URL de la aplicación:', 'or provide application url:': 'o provea URL de la aplicación:',
'Origin': 'Origen', 'Origin': 'Origen',
'Original/Translation': 'Original/Traducción', 'Original/Translation': 'Original/Traducción',
'Other Plugins': 'Other Plugins', 'Other Plugins': 'Otros Plugins',
'Other Recipes': 'Other Recipes', 'Other Recipes': 'Otas Recetas',
'Overview': 'Overview', 'Overview': 'Resumen',
'pack all': 'empaquetar todo', 'pack all': 'empaquetar todo',
'pack compiled': 'empaquete compiladas', 'pack compiled': 'empaquete compiladas',
'Password': 'Contraseña', 'Password': 'Contraseña',
@@ -215,20 +215,20 @@
'previous 100 rows': '100 filas anteriores', 'previous 100 rows': '100 filas anteriores',
'Python': 'Python', 'Python': 'Python',
'Query:': 'Consulta:', 'Query:': 'Consulta:',
'Quick Examples': 'Quick Examples', 'Quick Examples': 'Ejemplos Rápidos',
'RAM': 'RAM', 'RAM': 'RAM',
'RAM Cache Keys': 'RAM Cache Keys', 'RAM Cache Keys':'Llaves de la RAM Cache',
'Ram Cleared': 'Ram Cleared', 'Ram Cleared': 'Ram Limpiada',
'Recipes': 'Recipes', 'Recipes': 'Recetas',
'Record': 'registro', 'Record': 'registro',
'record does not exist': 'el registro no existe', 'record does not exist': 'el registro no existe',
'Record ID': 'ID de Registro', 'Record ID': 'ID de Registro',
'Record id': 'id de registro', 'Record id': 'id de registro',
'Register': 'Registrese', 'Register': 'Registrese',
'register': 'registrese', 'register': 'registrese',
'Registration key': 'Contraseña de Registro', 'Registration key': 'Llave de Registro',
'remove compiled': 'eliminar compiladas', 'remove compiled': 'eliminar compiladas',
'Reset Password key': 'Reset Password key', 'Reset Password key': 'Restaurar LLavel de la Contraseña',
'Resolve Conflict file': 'archivo Resolución de Conflicto', 'Resolve Conflict file': 'archivo Resolución de Conflicto',
'restore': 'restaurar', 'restore': 'restaurar',
'revert': 'revertir', 'revert': 'revertir',
@@ -237,20 +237,20 @@
'Rows selected': 'Filas seleccionadas', 'Rows selected': 'Filas seleccionadas',
'save': 'guardar', 'save': 'guardar',
'Saved file hash:': 'Hash del archivo guardado:', 'Saved file hash:': 'Hash del archivo guardado:',
'Semantic': 'Semantic', 'Semantic': 'Semantica',
'Services': 'Services', 'Services': 'Servicios',
'session expired': 'sesión expirada', 'session expired': 'sesión expirada',
'shell': 'shell', 'shell': 'terminal',
'site': 'sitio', 'site': 'sitio',
'Size of cache:': 'Size of cache:', 'Size of cache:': 'Tamaño del Cache:',
'some files could not be removed': 'algunos archivos no pudieron ser removidos', 'some files could not be removed': 'algunos archivos no pudieron ser removidos',
'state': 'estado', 'state': 'estado',
'static': 'estáticos', 'static': 'estáticos',
'Static files': 'Archivos estáticos', 'Static files': 'Archivos estáticos',
'Statistics': 'Statistics', 'Statistics': 'Estadísticas',
'Stylesheet': 'Hoja de estilo', 'Stylesheet': 'Hoja de estilo',
'submit': 'submit', 'submit': 'enviar',
'Support': 'Support', 'Support': 'Soporte',
'Sure you want to delete this object?': '¿Está seguro que desea eliminar este objeto?', 'Sure you want to delete this object?': '¿Está seguro que desea eliminar este objeto?',
'Table': 'tabla', 'Table': 'tabla',
'Table name': 'Nombre de la tabla', 'Table name': 'Nombre de la tabla',
@@ -258,11 +258,11 @@
'Testing application': 'Probando aplicación', 'Testing application': 'Probando aplicación',
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "consulta" es una condición como "db.tabla1.campo1==\'valor\'". Algo como "db.tabla1.campo1==db.tabla2.campo2" resulta en un JOIN SQL.', 'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "consulta" es una condición como "db.tabla1.campo1==\'valor\'". Algo como "db.tabla1.campo1==db.tabla2.campo2" resulta en un JOIN SQL.',
'the application logic, each URL path is mapped in one exposed function in the controller': 'la lógica de la aplicación, cada ruta URL se mapea en una función expuesta en el controlador', 'the application logic, each URL path is mapped in one exposed function in the controller': 'la lógica de la aplicación, cada ruta URL se mapea en una función expuesta en el controlador',
'The Core': 'The Core', 'The Core': 'El Núcleo',
'the data representation, define database tables and sets': 'la representación de datos, define tablas y conjuntos de base de datos', 'the data representation, define database tables and sets': 'la representación de datos, define tablas y conjuntos de base de datos',
'The output of the file is a dictionary that was rendered by the view %s': 'La salida del archivo es un diccionario escenificado por la vista %s', 'The output of the file is a dictionary that was rendered by the view %s': 'La salida del archivo es un diccionario escenificado por la vista %s',
'the presentations layer, views are also known as templates': 'la capa de presentación, las vistas también son llamadas plantillas', 'the presentations layer, views are also known as templates': 'la capa de presentación, las vistas también son llamadas plantillas',
'The Views': 'The Views', 'The Views': 'Las Vistas',
'There are no controllers': 'No hay controladores', 'There are no controllers': 'No hay controladores',
'There are no models': 'No hay modelos', 'There are no models': 'No hay modelos',
'There are no modules': 'No hay módulos', 'There are no modules': 'No hay módulos',
@@ -270,14 +270,14 @@
'There are no translators, only default language is supported': 'No hay traductores, sólo el lenguaje por defecto es soportado', 'There are no translators, only default language is supported': 'No hay traductores, sólo el lenguaje por defecto es soportado',
'There are no views': 'No hay vistas', 'There are no views': 'No hay vistas',
'these files are served without processing, your images go here': 'estos archivos son servidos sin procesar, sus imágenes van aquí', 'these files are served without processing, your images go here': 'estos archivos son servidos sin procesar, sus imágenes van aquí',
'This App': 'This App', 'This App': 'Esta Aplicación',
'This is a copy of the scaffolding application': 'Esta es una copia de la aplicación de andamiaje', 'This is a copy of the scaffolding application': 'Esta es una copia de la aplicación de andamiaje',
'This is the %(filename)s template': 'Esta es la plantilla %(filename)s', 'This is the %(filename)s template': 'Esta es la plantilla %(filename)s',
'Ticket': 'Tiquete', 'Ticket': 'Tiquete',
'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Time in Cache (h:m:s)': 'Tiempo en Cache (h:m:s)',
'Timestamp': 'Timestamp', 'Timestamp': 'Marca de tiempo',
'to previous version.': 'a la versión previa.', 'to previous version.': 'a la versión previa.',
'translation strings for the application': 'cadenas de caracteres de traducción para la aplicación', 'translation strings for the application': 'cadenas de carácteres de traducción para la aplicación',
'try': 'intente', 'try': 'intente',
'try something like': 'intente algo como', 'try something like': 'intente algo como',
'Twitter': 'Twitter', 'Twitter': 'Twitter',
@@ -306,12 +306,12 @@
'views': 'vistas', 'views': 'vistas',
'web2py is up to date': 'web2py está actualizado', 'web2py is up to date': 'web2py está actualizado',
'web2py Recent Tweets': 'Tweets Recientes de web2py', 'web2py Recent Tweets': 'Tweets Recientes de web2py',
'Welcome': 'Welcome', 'Welcome': 'Bienvenido',
'Welcome %s': 'Bienvenido %s', 'Welcome %s': 'Bienvenido %s',
'Welcome to web2py': 'Bienvenido a web2py', 'Welcome to web2py': 'Bienvenido a web2py',
'Welcome to web2py!': 'Welcome to web2py!', 'Welcome to web2py!': '¡Bienvenido to web2py!',
'Which called the function %s located in the file %s': 'La cual llamó la función %s localizada en el archivo %s', 'Which called the function %s located in the file %s': 'La cual llamó la función %s localizada en el archivo %s',
'YES': 'SI', 'YES': 'SÍ',
'You are successfully running web2py': 'Usted está ejecutando web2py exitosamente', 'You are successfully running web2py': 'Usted está ejecutando web2py exitosamente',
'You can modify this application and adapt it to your needs': 'Usted puede modificar esta aplicación y adaptarla a sus necesidades', 'You can modify this application and adapt it to your needs': 'Usted puede modificar esta aplicación y adaptarla a sus necesidades',
'You visited the url %s': 'Usted visitó la url %s', 'You visited the url %s': 'Usted visitó la url %s',
+72 -66
View File
@@ -4,8 +4,8 @@
'!langcode!': 'it', '!langcode!': 'it',
'!langname!': 'Italiano', '!langname!': 'Italiano',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ',
'%(nrows)s records found': '%(nrows)s records found', '%(nrows)s records found': '%(nrows)s record trovati',
'%d seconds ago': '%d seconds ago', '%d seconds ago': '%d secondi fa',
'%s %%{row} deleted': '%s righe ("record") cancellate', '%s %%{row} deleted': '%s righe ("record") cancellate',
'%s %%{row} updated': '%s righe ("record") modificate', '%s %%{row} updated': '%s righe ("record") modificate',
'%s selected': '%s selezionato', '%s selected': '%s selezionato',
@@ -16,40 +16,41 @@
'=': '=', '=': '=',
'>': '>', '>': '>',
'>=': '>=', '>=': '>=',
'@markmin\x01Number of entries: **%s**': 'Number of entries: **%s**', '@markmin\x01Number of entries: **%s**': 'Numero di entità: **%s**',
'About': 'About', 'About': 'About',
'Access Control': 'Access Control', 'Access Control': 'Controllo Accessi',
'Add': 'Add', 'Add': 'Aggiungi',
'Administrative Interface': 'Administrative Interface', 'Administrative Interface': 'Interfaccia Amministrativa',
'Administrative interface': 'Interfaccia amministrativa', 'Administrative interface': 'Interfaccia amministrativa',
'Ajax Recipes': 'Ajax Recipes', 'Ajax Recipes': 'Ajax Recipes',
'And': 'And', 'An error occured, please %s the page': "È stato rilevato un errore, prego %s la pagina",
'And': 'E',
'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura', 'appadmin is disabled because insecure channel': 'Amministrazione (appadmin) disabilitata: comunicazione non sicura',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', 'Are you sure you want to delete this object?': 'Sicuro di voler cancellare questo oggetto ?',
'Available Databases and Tables': 'Database e tabelle disponibili', 'Available Databases and Tables': 'Database e tabelle disponibili',
'Back': 'Back', 'Back': 'Indietro',
'Buy this book': 'Buy this book', 'Buy this book': 'Compra questo libro',
'cache': 'cache', 'cache': 'cache',
'Cache': 'Cache', 'Cache': 'Cache',
'Cache Keys': 'Cache Keys', 'Cache Keys': 'Cache Keys',
'Cannot be empty': 'Non può essere vuoto', 'Cannot be empty': 'Non può essere vuoto',
'Change password': 'Change password', 'Change password': 'Cambia Password',
'change password': 'Cambia password', 'change password': 'Cambia password',
'Check to delete': 'Seleziona per cancellare', 'Check to delete': 'Seleziona per cancellare',
'Clear': 'Clear', 'Clear': 'Resetta',
'Clear CACHE?': 'Clear CACHE?', 'Clear CACHE?': 'Resetta CACHE?',
'Clear DISK': 'Clear DISK', 'Clear DISK': 'Resetta DISK',
'Clear RAM': 'Clear RAM', 'Clear RAM': 'Resetta RAM',
'Client IP': 'Client IP', 'Client IP': 'Client IP',
'Close': 'Close', 'Close': 'Chiudi',
'Cognome': 'Cognome', 'Cognome': 'Cognome',
'Community': 'Community', 'Community': 'Community',
'Components and Plugins': 'Components and Plugins', 'Components and Plugins': 'Componenti and Plugin',
'contains': 'contains', 'contains': 'contiene',
'Controller': 'Controller', 'Controller': 'Controller',
'Copyright': 'Copyright', 'Copyright': 'Copyright',
'Created By': 'Created By', 'Created By': 'Creato Da',
'Created On': 'Created On', 'Created On': 'Creato Il',
'CSV': 'CSV', 'CSV': 'CSV',
'CSV (hidden cols)': 'CSV (hidden cols)', 'CSV (hidden cols)': 'CSV (hidden cols)',
'Current request': 'Richiesta (request) corrente', 'Current request': 'Richiesta (request) corrente',
@@ -61,7 +62,7 @@
'Database %s select': 'Database %s select', 'Database %s select': 'Database %s select',
'db': 'db', 'db': 'db',
'DB Model': 'Modello di DB', 'DB Model': 'Modello di DB',
'Delete': 'Delete', 'Delete': 'Cancella',
'Delete:': 'Cancella:', 'Delete:': 'Cancella:',
'Demo': 'Demo', 'Demo': 'Demo',
'Deployment Recipes': 'Deployment Recipes', 'Deployment Recipes': 'Deployment Recipes',
@@ -71,7 +72,7 @@
'Disk Cache Keys': 'Disk Cache Keys', 'Disk Cache Keys': 'Disk Cache Keys',
'Disk Cleared': 'Disk Cleared', 'Disk Cleared': 'Disk Cleared',
'Documentation': 'Documentazione', 'Documentation': 'Documentazione',
"Don't know what to do?": "Don't know what to do?", "Don't know what to do?": 'Non sai cosa fare?',
'done!': 'fatto!', 'done!': 'fatto!',
'Download': 'Download', 'Download': 'Download',
'E-mail': 'E-mail', 'E-mail': 'E-mail',
@@ -79,18 +80,19 @@
'Edit current record': 'Modifica record corrente', 'Edit current record': 'Modifica record corrente',
'edit profile': 'modifica profilo', 'edit profile': 'modifica profilo',
'Edit This App': 'Modifica questa applicazione', 'Edit This App': 'Modifica questa applicazione',
'Email and SMS': 'Email and SMS', 'Email and SMS': 'Email e SMS',
'Email non valida': 'Email non valida', 'Email non valida': 'Email non valida',
'enter an integer between %(min)g and %(max)g': 'enter an integer between %(min)g and %(max)g', 'enter an integer between %(min)g and %(max)g': 'inserisci un intero tra %(min)g e %(max)g',
'Errors': 'Errors', 'Errors': 'Errori',
'Errors in form, please check it out.': 'Errors in form, please check it out.', 'Errors in form, please check it out.': 'Errori nel form, ricontrollalo',
'export as csv file': 'esporta come file CSV', 'export as csv file': 'esporta come file CSV',
'Export:': 'Export:', 'Export:': 'Esporta:',
'FAQ': 'FAQ', 'FAQ': 'FAQ',
'First name': 'Nome', 'First name': 'Nome',
'Forgot username?': 'Forgot username?', 'Forgot username?': 'Dimenticato lo username?',
'Forms and Validators': 'Forms and Validators', 'Forms and Validators': 'Forms and Validators',
'Free Applications': 'Free Applications', 'Free Applications': 'Free Applications',
'Graph Model': 'Graph Model',
'Group %(group_id)s created': 'Group %(group_id)s created', 'Group %(group_id)s created': 'Group %(group_id)s created',
'Group ID': 'ID Gruppo', 'Group ID': 'ID Gruppo',
'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s', 'Group uniquely assigned to user %(id)s': 'Group uniquely assigned to user %(id)s',
@@ -100,69 +102,70 @@
'Hello World': 'Salve Mondo', 'Hello World': 'Salve Mondo',
'Hello World in a flash!': 'Salve Mondo in un flash!', 'Hello World in a flash!': 'Salve Mondo in un flash!',
'Home': 'Home', 'Home': 'Home',
'How did you get here?': 'How did you get here?', 'How did you get here?': 'Come sei arrivato qui?',
'HTML': 'HTML', 'HTML': 'HTML',
'import': 'import', 'import': 'importa',
'Import/Export': 'Importa/Esporta', 'Import/Export': 'Importa/Esporta',
'Index': 'Indice', 'Index': 'Indice',
'insert new': 'inserisci nuovo', 'insert new': 'inserisci nuovo',
'insert new %s': 'inserisci nuovo %s', 'insert new %s': 'inserisci nuovo %s',
'Internal State': 'Stato interno', 'Internal State': 'Stato interno',
'Introduction': 'Introduction', 'Introduction': 'Introduzione',
'Invalid email': 'Email non valida', 'Invalid email': 'Email non valida',
'Invalid login': 'Invalid login', 'Invalid login': 'Login non valido',
'Invalid Query': 'Richiesta (query) non valida', 'Invalid Query': 'Richiesta (query) non valida',
'invalid request': 'richiesta non valida', 'invalid request': 'richiesta non valida',
'Is Active': 'Is Active', 'Is Active': "E' attivo",
'Key': 'Key', 'Key': 'Chiave',
'Last name': 'Cognome', 'Last name': 'Cognome',
'Layout': 'Layout', 'Layout': 'Layout',
'Layout Plugins': 'Layout Plugins', 'Layout Plugins': 'Layout Plugins',
'Layouts': 'Layouts', 'Layouts': 'Layouts',
'Live Chat': 'Live Chat', 'Live Chat': 'Live Chat',
'Logged in': 'Logged in', 'Logged in': 'Loggato',
'Logged out': 'Logged out', 'Logged out': 'Disconnesso',
'login': 'accesso', 'login': 'accesso',
'Login': 'Login', 'Login': 'Login',
'logout': 'uscita', 'logout': 'uscita',
'Logout': 'Logout', 'Logout': 'Logout',
'Lost Password': 'Lost Password', 'Lost Password': 'Password Smarrita',
'Lost password?': 'Lost password?', 'Lost password?': 'Password smarrita?',
'lost password?': 'dimenticato la password?', 'lost password?': 'dimenticato la password?',
'Main Menu': 'Menu principale', 'Main Menu': 'Menu principale',
'Manage Cache': 'Manage Cache', 'Manage Cache': 'Manage Cache',
'Menu Model': 'Menu Modelli', 'Menu Model': 'Menu Modelli',
'Modified By': 'Modified By', 'Modified By': 'Modificato da',
'Modified On': 'Modified On', 'Modified On': 'Modificato il',
'My Sites': 'My Sites', 'My Sites': 'My Sites',
'Name': 'Nome', 'Name': 'Nome',
'New': 'New', 'New': 'Nuovo',
'New password': 'New password', 'New password': 'Nuova password',
'New Record': 'Nuovo elemento (record)', 'New Record': 'Nuovo elemento (record)',
'new record inserted': 'nuovo record inserito', 'new record inserted': 'nuovo record inserito',
'next 100 rows': 'prossime 100 righe', 'next 100 rows': 'prossime 100 righe',
'No databases in this application': 'Nessun database presente in questa applicazione', 'No databases in this application': 'Nessun database presente in questa applicazione',
'No records found': 'No records found', 'No records found': 'Nessun record trovato',
'Nome': 'Nome', 'Nome': 'Nome',
'Non può essere vuoto': 'Non può essere vuoto', 'Non può essere vuoto': 'Non può essere vuoto',
'not authorized': 'non autorizzato', 'not authorized': 'non autorizzato',
'Object or table name': 'Object or table name', 'Object or table name': 'Oggeto o nome tabella',
'Old password': 'Old password', 'Old password': 'Vecchia password',
'Online examples': 'Vedere gli esempi', 'Online examples': 'Vedere gli esempi',
'Or': 'Or', 'Or': 'O',
'or import from csv file': 'oppure importa da file CSV', 'or import from csv file': 'oppure importa da file CSV',
'Origin': 'Origine', 'Origin': 'Origine',
'Other Plugins': 'Other Plugins', 'Other Plugins': 'Other Plugins',
'Other Recipes': 'Other Recipes', 'Other Recipes': 'Other Recipes',
'Overview': 'Overview', 'Overview': 'Overview',
'Password': 'Password', 'Password': 'Password',
"Password fields don't match": "Password fields don't match", "Password fields don't match": 'I campi password non sono uguali',
'please input your password again': 'please input your password again', 'please input your password again': 'perfavore reimmeti la tua password',
'Plugins': 'Plugins', 'Plugins': 'Plugins',
'Powered by': 'Powered by', 'Powered by': 'Powered by',
'Preface': 'Preface', 'Preface': 'Preface',
'previous 100 rows': '100 righe precedenti', 'previous 100 rows': '100 righe precedenti',
'Profile': 'Profile', 'Profile': 'Profilo',
'pygraphviz library not found': 'pygraphviz library not found',
'Python': 'Python', 'Python': 'Python',
'Query:': 'Richiesta (query):', 'Query:': 'Richiesta (query):',
'Quick Examples': 'Quick Examples', 'Quick Examples': 'Quick Examples',
@@ -174,28 +177,30 @@
'record does not exist': 'il record non esiste', 'record does not exist': 'il record non esiste',
'Record ID': 'Record ID', 'Record ID': 'Record ID',
'Record id': 'Record id', 'Record id': 'Record id',
'Register': 'Register', 'Register': 'Registrati',
'register': 'registrazione', 'register': 'registrazione',
'Registration identifier': 'Registration identifier', 'Registration identifier': 'Registration identifier',
'Registration key': 'Chiave di Registazione', 'Registration key': 'Chiave di Registazione',
'Registration successful': 'Registration successful', 'Registration successful': 'Registrazione avvenuta',
'Remember me (for 30 days)': 'Remember me (for 30 days)', 'reload': 'reload',
'Request reset password': 'Request reset password', 'Remember me (for 30 days)': 'Ricordami (per 30 giorni)',
'Request reset password': 'Richiedi il reset della password',
'Reset Password key': 'Resetta chiave Password ', 'Reset Password key': 'Resetta chiave Password ',
'Role': 'Ruolo', 'Role': 'Ruolo',
'Rows in Table': 'Righe nella tabella', 'Rows in Table': 'Righe nella tabella',
'Rows selected': 'Righe selezionate', 'Rows selected': 'Righe selezionate',
'Save profile': 'Save profile', 'Save model as...': 'Salva modello come...',
'Search': 'Search', 'Save profile': 'Salva profilo',
'Search': 'Ricerca',
'Semantic': 'Semantic', 'Semantic': 'Semantic',
'Services': 'Services', 'Services': 'Servizi',
'Size of cache:': 'Size of cache:', 'Size of cache:': 'Size of cache:',
'starts with': 'starts with', 'starts with': 'comincia con',
'state': 'stato', 'state': 'stato',
'Statistics': 'Statistics', 'Statistics': 'Statistics',
'Stylesheet': 'Foglio di stile (stylesheet)', 'Stylesheet': 'Foglio di stile (stylesheet)',
'submit': 'submit', 'submit': 'Inviai',
'Submit': 'Submit', 'Submit': 'Invia',
'Support': 'Support', 'Support': 'Support',
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?', 'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
'Table': 'tabella', 'Table': 'tabella',
@@ -208,12 +213,13 @@
'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)", 'This is a copy of the scaffolding application': "Questa è una copia dell'applicazione di base (scaffold)",
'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)', 'Time in Cache (h:m:s)': 'Time in Cache (h:m:s)',
'Timestamp': 'Ora (timestamp)', 'Timestamp': 'Ora (timestamp)',
'too short': 'too short', 'too short': 'troppo corto',
'TSV (Excel compatible)': 'TSV (Excel compatible)', 'Traceback': 'Traceback',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatible, hidden cols)', 'TSV (Excel compatible)': 'TSV (Excel compatibile)',
'TSV (Excel compatible, hidden cols)': 'TSV (Excel compatibile, hidden cols)',
'Twitter': 'Twitter', 'Twitter': 'Twitter',
'unable to parse csv file': 'non riesco a decodificare questo file CSV', 'unable to parse csv file': 'non riesco a decodificare questo file CSV',
'Update': 'Update', 'Update': 'Aggiorna',
'Update:': 'Aggiorna:', 'Update:': 'Aggiorna:',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).', 'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
'User %(id)s Logged-in': 'User %(id)s Logged-in', 'User %(id)s Logged-in': 'User %(id)s Logged-in',
@@ -223,14 +229,14 @@
'User %(id)s Profile updated': 'User %(id)s Profile updated', 'User %(id)s Profile updated': 'User %(id)s Profile updated',
'User %(id)s Registered': 'User %(id)s Registered', 'User %(id)s Registered': 'User %(id)s Registered',
'User ID': 'ID Utente', 'User ID': 'ID Utente',
'value already in database or empty': 'value already in database or empty', 'value already in database or empty': 'valore già presente nel database o vuoto',
'Verify Password': 'Verify Password', 'Verify Password': 'Verifica Password',
'Videos': 'Videos', 'Videos': 'Videos',
'View': 'Vista', 'View': 'Vista',
'Welcome': 'Welcome', 'Welcome': 'Welcome',
'Welcome %s': 'Benvenuto %s', 'Welcome %s': 'Benvenuto %s',
'Welcome to web2py': 'Benvenuto su web2py', 'Welcome to web2py': 'Benvenuto su web2py',
'Welcome to web2py!': 'Welcome to web2py!', 'Welcome to web2py!': 'Benvenuto in web2py!',
'Which called the function %s located in the file %s': 'che ha chiamato la funzione %s presente nel file %s', 'Which called the function %s located in the file %s': 'che ha chiamato la funzione %s presente nel file %s',
'XML': 'XML', 'XML': 'XML',
'You are successfully running web2py': 'Stai eseguendo web2py con successo', 'You are successfully running web2py': 'Stai eseguendo web2py con successo',
+1 -2
View File
@@ -7,8 +7,7 @@
response.logo = A(B('web',SPAN(2),'py'),XML('&trade;&nbsp;'), response.logo = A(B('web',SPAN(2),'py'),XML('&trade;&nbsp;'),
_class="brand",_href="http://www.web2py.com/") _class="brand",_href="http://www.web2py.com/")
response.title = ' '.join( response.title = request.application.replace('_',' ').title()
word.capitalize() for word in request.application.split('_'))
response.subtitle = T('customize me!') response.subtitle = T('customize me!')
## read more at http://dev.w3.org/html5/markup/meta.name.html ## read more at http://dev.w3.org/html5/markup/meta.name.html
@@ -102,6 +102,7 @@ td.w2p_fl,td.w2p_fc {padding:0;}
==============================================================*/ ==============================================================*/
/* because web2py handles this via js */ /* because web2py handles this via js */
textarea { width:90%}
.hidden{visibility:visible;} .hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */ /* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{ [class^="icon-"],[class*=" icon-"]{
+3 -1
View File
@@ -91,16 +91,18 @@
<div class="container"> <div class="container">
<!-- Masthead ================================================== --> <!-- Masthead ================================================== -->
{{if response.title:}}
<header class="mastheader row" id="header"> <header class="mastheader row" id="header">
<div class="span12"> <div class="span12">
<div class="page-header"> <div class="page-header">
<h1> <h1>
{{=response.title or request.application}} {{=response.title}}
<small>{{=response.subtitle or ''}}</small> <small>{{=response.subtitle or ''}}</small>
</h1> </h1>
</div> </div>
</div> </div>
</header> </header>
{{pass}}
<section id="main" class="main row"> <section id="main" class="main row">
{{if left_sidebar_enabled:}} {{if left_sidebar_enabled:}}
+3 -3
View File
@@ -43,7 +43,7 @@ def apath(path='', r=None):
return os.path.join(opath, path).replace('\\', '/') return os.path.join(opath, path).replace('\\', '/')
def app_pack(app, request, raise_ex=False): def app_pack(app, request, raise_ex=False, filenames=None):
""" """
Builds a w2p package for the application Builds a w2p package for the application
@@ -60,9 +60,9 @@ def app_pack(app, request, raise_ex=False):
filename of the w2p file or None on error filename of the w2p file or None on error
""" """
try: try:
app_cleanup(app, request) if filenames is None: app_cleanup(app, request)
filename = apath('../deposit/web2py.app.%s.w2p' % app, request) filename = apath('../deposit/web2py.app.%s.w2p' % app, request)
w2p_pack(filename, apath(app, request)) w2p_pack(filename, apath(app, request), filenames=filenames)
return filename return filename
except Exception, e: except Exception, e:
if raise_ex: if raise_ex:
+2 -2
View File
@@ -457,7 +457,7 @@ class Cache(object):
public: if False forces the Cache-Control to be 'private' public: if False forces the Cache-Control to be 'private'
valid_statuses: by default only status codes starting with 1,2,3 will be cached. valid_statuses: by default only status codes starting with 1,2,3 will be cached.
pass an explicit list of statuses on which turn the cache on pass an explicit list of statuses on which turn the cache on
quick: Session,Vars,Lang,User-agent,Public,duration: quick: Session,Vars,Lang,User-agent,Public:
fast overrides with initial strings, e.g. 'SVLP' or 'VLP', or 'VLP' fast overrides with initial strings, e.g. 'SVLP' or 'VLP', or 'VLP'
""" """
from gluon import current from gluon import current
@@ -512,7 +512,7 @@ class Cache(object):
current.response.headers['Expires'] = expires current.response.headers['Expires'] = expires
current.response.headers['Cache-Control'] = cache_control current.response.headers['Cache-Control'] = cache_control
if cache_model and not send_headers: if cache_model and not send_headers:
cache_model.clear(cache_key) cache_model(cache_key, None)
return rtn return rtn
return func() return func()
wrapped_f.__name__ = func.__name__ wrapped_f.__name__ = func.__name__
+35 -2
View File
@@ -373,6 +373,21 @@ class FPDF(object):
"Draw a line" "Draw a line"
self._out(sprintf('%.2f %.2f m %.2f %.2f l S',x1*self.k,(self.h-y1)*self.k,x2*self.k,(self.h-y2)*self.k)) self._out(sprintf('%.2f %.2f m %.2f %.2f l S',x1*self.k,(self.h-y1)*self.k,x2*self.k,(self.h-y2)*self.k))
def _set_dash(self, dash_length=False, space_length=False):
if(dash_length and space_length):
s = sprintf('[%.3f %.3f] 0 d', dash_length*self.k, space_length*self.k)
else:
s = '[] 0 d'
self._out(s)
def dashed_line(self, x1,y1,x2,y2, dash_length=1, space_length=1):
"""Draw a dashed line. Same interface as line() except:
- dash_length: Length of the dash
- space_length: Length of the space between dashes"""
self._set_dash(dash_length, space_length)
self.line(x1, y1, x2, y2)
self._set_dash()
def rect(self, x,y,w,h,style=''): def rect(self, x,y,w,h,style=''):
"Draw a rectangle" "Draw a rectangle"
if(style=='F'): if(style=='F'):
@@ -450,7 +465,7 @@ class FPDF(object):
fh = open(unifilename, "w") fh = open(unifilename, "w")
pickle.dump(font_dict, fh) pickle.dump(font_dict, fh)
fh.close() fh.close()
except IOError as e: except IOError, e:
if not e.errno == errno.EACCES: if not e.errno == errno.EACCES:
raise # Not a permission error. raise # Not a permission error.
del ttf del ttf
@@ -900,6 +915,24 @@ class FPDF(object):
info=self._parsepng(name) info=self._parsepng(name)
else: else:
#Allow for additional formats #Allow for additional formats
#maybe the image is not showing the correct extension,
#but the header is OK,
succeed_parsing = False
#try all the parsing functions
parsing_functions = [self._parsejpg,self._parsepng,self._parsegif]
for pf in parsing_functions:
try:
info = pf(name)
succeed_parsing = True
break;
except:
pass
#last resource
if not succeed_parsing:
mtd='_parse'+type
if not hasattr(self,mtd):
self.error('Unsupported image type: '+type)
info=getattr(self, mtd)(name)
mtd='_parse'+type mtd='_parse'+type
if not hasattr(self,mtd): if not hasattr(self,mtd):
self.error('Unsupported image type: '+type) self.error('Unsupported image type: '+type)
@@ -1341,7 +1374,7 @@ class FPDF(object):
font_dict['range'] = range_ font_dict['range'] = range_
pickle.dump(font_dict, fh) pickle.dump(font_dict, fh)
fh.close() fh.close()
except IOError as e: except IOError, e:
if not e.errno == errno.EACCES: if not e.errno == errno.EACCES:
raise # Not a permission error. raise # Not a permission error.
if (font['cw'][cid] == 0): if (font['cw'][cid] == 0):
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
+2 -2
View File
@@ -653,7 +653,8 @@ def ldap_auth(server='ldap', port=None,
ldap_groups_of_the_user = list() ldap_groups_of_the_user = list()
for group_row in group_search_result: for group_row in group_search_result:
group = group_row[1] group = group_row[1]
ldap_groups_of_the_user.extend(group[group_name_attrib]) if type(group) == dict and group.has_key(group_name_attrib):
ldap_groups_of_the_user.extend(group[group_name_attrib])
con.unbind() con.unbind()
logger.debug('User groups: %s' % ldap_groups_of_the_user) logger.debug('User groups: %s' % ldap_groups_of_the_user)
@@ -662,4 +663,3 @@ def ldap_auth(server='ldap', port=None,
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
filterstr = filterstr[1:-1] # parens added again where used filterstr = filterstr[1:-1] # parens added again where used
return ldap_auth_aux return ldap_auth_aux
@@ -1,4 +1,3 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
+8 -1
View File
@@ -75,7 +75,7 @@ def da_du_ma(n=4):
[random.randint(0, 11)] for i in range(n)]) [random.randint(0, 11)] for i in range(n)])
def populate(table, n, default=True, compute=False): def populate(table, n, default=True, compute=False, contents={}):
ell = Learner() ell = Learner()
#ell.learn(open('20417.txt','r').read()) #ell.learn(open('20417.txt','r').read())
#ell.save('frequencies.pickle') #ell.save('frequencies.pickle')
@@ -84,7 +84,14 @@ def populate(table, n, default=True, compute=False):
ids = {} ids = {}
for i in range(n): for i in range(n):
record = {} record = {}
record.update(contents) # load user supplied contents.
for fieldname in table.fields: for fieldname in table.fields:
if record.get(fieldname) is not None:
continue # if user supplied it, let it be.
field = table[fieldname] field = table[fieldname]
if not isinstance(field.type, (str, unicode)): if not isinstance(field.type, (str, unicode)):
continue continue
+6 -2
View File
@@ -15,12 +15,12 @@ mostly for testing purposes
Some examples at the bottom. Some examples at the bottom.
""" """
import re import re
import time import time
import urllib import urllib
import urllib2 import urllib2
DEFAULT_HEADERS = { DEFAULT_HEADERS = {
'user-agent': 'Mozilla/4.0', # some servers are picky 'user-agent': 'Mozilla/4.0', # some servers are picky
'accept-language': 'en', 'accept-language': 'en',
@@ -126,7 +126,11 @@ class WebClient(object):
self.time = time.time() - t0 self.time = time.time() - t0
self.response = error self.response = error
self.status = self.response.getcode() if hasattr(self.response, 'getcode'):
self.status = self.response.getcode()
else:#python2.5
self.status = None
self.text = self.response.read() self.text = self.response.read()
self.headers = dict(self.response.headers) self.headers = dict(self.response.headers)
+156 -79
View File
@@ -122,6 +122,7 @@ Supported DAL URI strings:
'google:sql' # for google app engine with sql (mysql compatible) 'google:sql' # for google app engine with sql (mysql compatible)
'teradata://DSN=dsn;UID=user;PWD=pass; DATABASE=database' # experimental 'teradata://DSN=dsn;UID=user;PWD=pass; DATABASE=database' # experimental
'imap://user:password@server:port' # experimental 'imap://user:password@server:port' # experimental
'mongodb://user:password@server:port/database' # experimental
For more info: For more info:
help(DAL) help(DAL)
@@ -199,7 +200,7 @@ TABLE_ARGS = set(
SELECT_ARGS = set( SELECT_ARGS = set(
('orderby', 'groupby', 'limitby','required', 'cache', 'left', ('orderby', 'groupby', 'limitby','required', 'cache', 'left',
'distinct', 'having', 'join','for_update', 'processor','cacheable')) 'distinct', 'having', 'join','for_update', 'processor','cacheable', 'orderby_on_limitby'))
ogetattr = object.__getattribute__ ogetattr = object.__getattribute__
osetattr = object.__setattr__ osetattr = object.__setattr__
@@ -370,7 +371,7 @@ if not 'google' in DRIVERS:
try: try:
import fdb import fdb
DRIVERS.append('Firbird(fdb)') DRIVERS.append('Firebird(fdb)')
except ImportError: except ImportError:
LOGGER.debug('no Firebird driver fdb') LOGGER.debug('no Firebird driver fdb')
##### #####
@@ -633,6 +634,7 @@ class BaseAdapter(ConnectionPool):
TRUE = 'T' TRUE = 'T'
FALSE = 'F' FALSE = 'F'
T_SEP = ' '
types = { types = {
'boolean': 'CHAR(1)', 'boolean': 'CHAR(1)',
'string': 'CHAR(%(length)s)', 'string': 'CHAR(%(length)s)',
@@ -1125,6 +1127,9 @@ class BaseAdapter(ConnectionPool):
def EPOCH(self, first): def EPOCH(self, first):
return self.EXTRACT(first, 'epoch') return self.EXTRACT(first, 'epoch')
def LENGTH(self, first):
return "LENGTH(%s)" % self.expand(first)
def AGGREGATE(self, first, what): def AGGREGATE(self, first, what):
return "%s(%s)" % (what, self.expand(first)) return "%s(%s)" % (what, self.expand(first))
@@ -1250,24 +1255,15 @@ class BaseAdapter(ConnectionPool):
return '(%s LIKE %s)' % (self.expand(first), return '(%s LIKE %s)' % (self.expand(first),
self.expand('%'+second, 'string')) self.expand('%'+second, 'string'))
def CONTAINS(self, first, second, case_sensitive=False): def CONTAINS(self,first,second,case_sensitive=False):
if isinstance(second,Expression): if first.type in ('string','text', 'json'):
field = self.expand(first) second = Expression(None,self.CONCAT('%',Expression(
expr = self.expand(second,'string') None,self.REPLACE(second,('%','%%'))),'%'))
if first.type.startswith('list:'): elif first.type.startswith('list:'):
expr = 'CONCAT("|", %s, "|")' % expr second = Expression(None,self.CONCAT('%|',Expression(None,self.REPLACE(
elif not first.type in ('string', 'text', 'json'): Expression(None,self.REPLACE(second,('%','%%'))),('|','||'))),'|%'))
raise RuntimeError("Expression Not Supported") op = case_sensitive and self.LIKE or self.ILIKE
return 'INSTR(%s,%s)' % (field, expr) return op(first,second)
else:
if first.type in ('string', 'text', 'json'):
key = '%'+str(second).replace('%','%%')+'%'
elif first.type.startswith('list:'):
key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
else:
raise RuntimeError("Expression Not Supported")
op = case_sensitive and self.LIKE or self.ILIKE
return op(first,key)
def EQ(self, first, second=None): def EQ(self, first, second=None):
if second is None: if second is None:
@@ -1305,9 +1301,24 @@ class BaseAdapter(ConnectionPool):
return '(%s >= %s)' % (self.expand(first), return '(%s >= %s)' % (self.expand(first),
self.expand(second,first.type)) self.expand(second,first.type))
def is_numerical_type(self, ftype):
return ftype in ('integer','boolean','double','bigint') or \
ftype.startswith('decimal')
def REPLACE(self, first, (second, third)):
return 'REPLACE(%s,%s,%s)' % (self.expand(first,'string'),
self.expand(second,'string'),
self.expand(third,'string'))
def CONCAT(self, *items):
return '(%s)' % ' || '.join(self.expand(x,'string') for x in items)
def ADD(self, first, second): def ADD(self, first, second):
return '(%s + %s)' % (self.expand(first), if self.is_numerical_type(first.type):
self.expand(second, first.type)) return '(%s + %s)' % (self.expand(first),
self.expand(second, first.type))
else:
return self.CONCAT(first, second)
def SUB(self, first, second): def SUB(self, first, second):
return '(%s - %s)' % (self.expand(first), return '(%s - %s)' % (self.expand(first),
@@ -1498,6 +1509,7 @@ class BaseAdapter(ConnectionPool):
raise SyntaxError('invalid select attribute: %s' % key) raise SyntaxError('invalid select attribute: %s' % key)
args_get = attributes.get args_get = attributes.get
tablenames = tables(query) tablenames = tables(query)
tablenames_for_common_filters = tablenames
for field in fields: for field in fields:
if isinstance(field, basestring) \ if isinstance(field, basestring) \
and REGEX_TABLE_DOT_FIELD.match(field): and REGEX_TABLE_DOT_FIELD.match(field):
@@ -1524,6 +1536,7 @@ class BaseAdapter(ConnectionPool):
orderby = args_get('orderby', False) orderby = args_get('orderby', False)
having = args_get('having', False) having = args_get('having', False)
limitby = args_get('limitby', False) limitby = args_get('limitby', False)
orderby_on_limitby = args_get('orderby_on_limitby', True)
for_update = args_get('for_update', False) for_update = args_get('for_update', False)
if self.can_select_for_update is False and for_update is True: if self.can_select_for_update is False and for_update is True:
raise SyntaxError('invalid select attribute: for_update') raise SyntaxError('invalid select attribute: for_update')
@@ -1561,6 +1574,8 @@ class BaseAdapter(ConnectionPool):
dict.fromkeys(tables(t))) for t in joinon] dict.fromkeys(tables(t))) for t in joinon]
joinont = [t.first._tablename for t in joinon] joinont = [t.first._tablename for t in joinon]
[tables_to_merge.pop(t) for t in joinont if t in tables_to_merge] [tables_to_merge.pop(t) for t in joinont if t in tables_to_merge]
tablenames_for_common_filters = [t for t in tablenames
if not t in joinont ]
important_tablenames = joint + joinont + tables_to_merge.keys() important_tablenames = joint + joinont + tables_to_merge.keys()
excluded = [t for t in tablenames excluded = [t for t in tablenames
if not t in important_tablenames ] if not t in important_tablenames ]
@@ -1568,7 +1583,7 @@ class BaseAdapter(ConnectionPool):
excluded = tablenames excluded = tablenames
if use_common_filters(query): if use_common_filters(query):
query = self.common_filter(query,excluded) query = self.common_filter(query,tablenames_for_common_filters)
sql_w = ' WHERE ' + self.expand(query) if query else '' sql_w = ' WHERE ' + self.expand(query) if query else ''
def alias(t): def alias(t):
@@ -1615,7 +1630,7 @@ class BaseAdapter(ConnectionPool):
else: else:
sql_o += ' ORDER BY %s' % self.expand(orderby) sql_o += ' ORDER BY %s' % self.expand(orderby)
if limitby: if limitby:
if not orderby and tablenames: if orderby_on_limitby and not orderby and tablenames:
sql_o += ' ORDER BY %s' % ', '.join(['%s.%s'%(t,x) for t in tablenames for x in (hasattr(self.db[t],'_primarykey') and self.db[t]._primarykey or [self.db[t]._id.name])]) sql_o += ' ORDER BY %s' % ', '.join(['%s.%s'%(t,x) for t in tablenames for x in (hasattr(self.db[t],'_primarykey') and self.db[t]._primarykey or [self.db[t]._id.name])])
# oracle does not support limitby # oracle does not support limitby
sql = self.select_limitby(sql_s, sql_f, sql_t, sql_w, sql_o, limitby) sql = self.select_limitby(sql_s, sql_f, sql_t, sql_w, sql_o, limitby)
@@ -1810,7 +1825,7 @@ class BaseAdapter(ConnectionPool):
obj = str(obj) obj = str(obj)
elif fieldtype == 'datetime': elif fieldtype == 'datetime':
if isinstance(obj, datetime.datetime): if isinstance(obj, datetime.datetime):
obj = obj.isoformat()[:19].replace('T',' ') obj = obj.isoformat(self.T_SEP)[:19]
elif isinstance(obj, datetime.date): elif isinstance(obj, datetime.date):
obj = obj.isoformat()[:10]+' 00:00:00' obj = obj.isoformat()[:10]+' 00:00:00'
else: else:
@@ -1938,17 +1953,17 @@ class BaseAdapter(ConnectionPool):
return value return value
def parse_list_integers(self, value, field_type): def parse_list_integers(self, value, field_type):
if not self.dbengine=='google:datastore': if not isinstance(self, NoSQLAdapter):
value = bar_decode_integer(value) value = bar_decode_integer(value)
return value return value
def parse_list_references(self, value, field_type): def parse_list_references(self, value, field_type):
if not self.dbengine=='google:datastore': if not isinstance(self, NoSQLAdapter):
value = bar_decode_integer(value) value = bar_decode_integer(value)
return [self.parse_reference(r, field_type[5:]) for r in value] return [self.parse_reference(r, field_type[5:]) for r in value]
def parse_list_strings(self, value, field_type): def parse_list_strings(self, value, field_type):
if not self.dbengine=='google:datastore': if not isinstance(self, NoSQLAdapter):
value = bar_decode_string(value) value = bar_decode_string(value)
return value return value
@@ -2259,7 +2274,7 @@ class SpatiaLiteAdapter(SQLiteAdapter):
# Linux uses libspatialite.so # Linux uses libspatialite.so
# Mac OS X uses libspatialite.dylib # Mac OS X uses libspatialite.dylib
libspatialite = SPATIALLIBS[platform.system()] libspatialite = SPATIALLIBS[platform.system()]
self.execute(r'SELECT load_extension("%s");') % libspatialite self.execute(r'SELECT load_extension("%s");' % libspatialite)
self.connection.create_function('web2py_extract', 2, self.connection.create_function('web2py_extract', 2,
SQLiteAdapter.web2py_extract) SQLiteAdapter.web2py_extract)
@@ -2409,6 +2424,9 @@ class MySQLAdapter(BaseAdapter):
def EPOCH(self, first): def EPOCH(self, first):
return "UNIX_TIMESTAMP(%s)" % self.expand(first) return "UNIX_TIMESTAMP(%s)" % self.expand(first)
def CONCAT(self, *items):
return 'CONCAT(%s)' % ','.join(self.expand(x,'string') for x in items)
def REGEXP(self,first,second): def REGEXP(self,first,second):
return '(%s REGEXP %s)' % (self.expand(first), return '(%s REGEXP %s)' % (self.expand(first),
self.expand(second,'string')) self.expand(second,'string'))
@@ -2418,6 +2436,9 @@ class MySQLAdapter(BaseAdapter):
return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table, return ['SET FOREIGN_KEY_CHECKS=0;','DROP TABLE %s;' % table,
'SET FOREIGN_KEY_CHECKS=1;'] 'SET FOREIGN_KEY_CHECKS=1;']
def _insert_empty(self, table):
return 'INSERT INTO %s VALUES (DEFAULT);' % table
def distributed_transaction_begin(self,key): def distributed_transaction_begin(self,key):
self.execute('XA START;') self.execute('XA START;')
@@ -2602,7 +2623,11 @@ class PostgreSQLAdapter(BaseAdapter):
"port=%s password='%s'") \ "port=%s password='%s'") \
% (db, user, host, port, password) % (db, user, host, port, password)
# choose diver according uri # choose diver according uri
self.__version__ = "%s %s" % (self.driver.__name__, self.driver.__version__) if self.driver:
self.__version__ = "%s %s" % (self.driver.__name__,
self.driver.__version__)
else:
self.__version__ = None
def connector(msg=msg,driver_args=driver_args): def connector(msg=msg,driver_args=driver_args):
return self.driver.connect(msg,**driver_args) return self.driver.connect(msg,**driver_args)
self.connector = connector self.connector = connector
@@ -2657,14 +2682,6 @@ class PostgreSQLAdapter(BaseAdapter):
return '(%s ILIKE %s)' % (self.expand(first), return '(%s ILIKE %s)' % (self.expand(first),
self.expand('%'+second,'string')) self.expand('%'+second,'string'))
def CONTAINS(self,first,second,case_sensitive=False):
if first.type in ('string','text', 'json'):
second = '%'+str(second).replace('%','%%')+'%'
elif first.type.startswith('list:'):
second = '%|'+str(second).replace('|','||').replace('%','%%')+'|%'
op = case_sensitive and self.LIKE or self.ILIKE
return op(first,second)
# GIS functions # GIS functions
def ST_ASGEOJSON(self, first, second): def ST_ASGEOJSON(self, first, second):
@@ -3033,6 +3050,7 @@ class OracleAdapter(BaseAdapter):
class MSSQLAdapter(BaseAdapter): class MSSQLAdapter(BaseAdapter):
drivers = ('pyodbc',) drivers = ('pyodbc',)
T_SEP = 'T'
types = { types = {
'boolean': 'BIT', 'boolean': 'BIT',
@@ -3098,7 +3116,9 @@ class MSSQLAdapter(BaseAdapter):
(lmin, lmax) = limitby (lmin, lmax) = limitby
sql_s += ' TOP %i' % lmax sql_s += ' TOP %i' % lmax
if 'GROUP BY' in sql_o: if 'GROUP BY' in sql_o:
sql_o = sql_o[:sql_o.find('ORDER BY ')] orderfound = sql_o.find('ORDER BY ')
if orderfound >= 0:
sql_o = sql_o[:orderfound]
return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o) return 'SELECT %s %s FROM %s%s%s;' % (sql_s, sql_f, sql_t, sql_w, sql_o)
TRUE = 1 TRUE = 1
@@ -3188,6 +3208,9 @@ class MSSQLAdapter(BaseAdapter):
def EPOCH(self, first): def EPOCH(self, first):
return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first) return "DATEDIFF(second, '1970-01-01 00:00:00', %s)" % self.expand(first)
def CONCAT(self, *items):
return '(%s)' % ' + '.join(self.expand(x,'string') for x in items)
# GIS Spatial Extensions # GIS Spatial Extensions
# No STAsGeoJSON in MSSQL # No STAsGeoJSON in MSSQL
@@ -3443,18 +3466,16 @@ class FireBirdAdapter(BaseAdapter):
def SUBSTRING(self,field,parameters): def SUBSTRING(self,field,parameters):
return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1]) return 'SUBSTRING(%s from %s for %s)' % (self.expand(field), parameters[0], parameters[1])
def CONTAINING(self,first,second): def LENGTH(self, first):
"case in-sensitive like operator" return "CHAR_LENGTH(%s)" % self.expand(first)
def CONTAINS(self,first,second,case_sensitive=False):
if first.type.startswith('list:'):
second = Expression(None,self.CONCAT('|',Expression(
None,self.REPLACE(second,('|','||'))),'|'))
return '(%s CONTAINING %s)' % (self.expand(first), return '(%s CONTAINING %s)' % (self.expand(first),
self.expand(second, 'string')) self.expand(second, 'string'))
def CONTAINS(self, first, second, case_sensitive=False):
if first.type in ('string','text'):
second = str(second).replace('%','%%')
elif first.type.startswith('list:'):
second = '|'+str(second).replace('|','||').replace('%','%%')+'|'
return self.CONTAINING(first,second)
def _drop(self,table,mode): def _drop(self,table,mode):
sequence_name = table._sequence_name sequence_name = table._sequence_name
return ['DROP TABLE %s %s;' % (table, mode), 'DROP GENERATOR %s;' % sequence_name] return ['DROP TABLE %s %s;' % (table, mode), 'DROP GENERATOR %s;' % sequence_name]
@@ -4353,13 +4374,14 @@ class NoSQLAdapter(BaseAdapter):
elif fieldtype == 'blob': elif fieldtype == 'blob':
pass pass
elif fieldtype == 'json': elif fieldtype == 'json':
obj = self.to_unicode(obj) if isinstance(obj, basestring):
if have_serializers: obj = self.to_unicode(obj)
obj = serializers.loads_json(obj) if have_serializers:
elif simplejson: obj = serializers.loads_json(obj)
obj = simplejson.loads(obj) elif simplejson:
else: obj = simplejson.loads(obj)
raise RuntimeError("missing simplejson") else:
raise RuntimeError("missing simplejson")
elif is_string and field_is_type('list:string'): elif is_string and field_is_type('list:string'):
return map(self.to_unicode,obj) return map(self.to_unicode,obj)
elif is_list: elif is_list:
@@ -4417,6 +4439,7 @@ class NoSQLAdapter(BaseAdapter):
def LOWER(self,first): raise SyntaxError("Not supported") def LOWER(self,first): raise SyntaxError("Not supported")
def UPPER(self,first): raise SyntaxError("Not supported") def UPPER(self,first): raise SyntaxError("Not supported")
def EXTRACT(self,first,what): raise SyntaxError("Not supported") def EXTRACT(self,first,what): raise SyntaxError("Not supported")
def LENGTH(self, first): raise SyntaxError("Not supported")
def AGGREGATE(self,first,what): raise SyntaxError("Not supported") def AGGREGATE(self,first,what): raise SyntaxError("Not supported")
def LEFT_JOIN(self): raise SyntaxError("Not supported") def LEFT_JOIN(self): raise SyntaxError("Not supported")
def RANDOM(self): raise SyntaxError("Not supported") def RANDOM(self): raise SyntaxError("Not supported")
@@ -4654,7 +4677,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
return self.expand(first) return self.expand(first)
def truncate(self,table,mode): def truncate(self,table,mode):
self.db(table._id).delete() self.db(self.db._adapter.id_query(table)).delete()
def select_raw(self,query,fields=None,attributes=None): def select_raw(self,query,fields=None,attributes=None):
db = self.db db = self.db
@@ -5176,6 +5199,7 @@ class MongoDBAdapter(NoSQLAdapter):
"Requires an integer or base 16 value") "Requires an integer or base 16 value")
elif isinstance(arg, self.ObjectId): elif isinstance(arg, self.ObjectId):
return arg return arg
if not isinstance(arg, (int, long)): if not isinstance(arg, (int, long)):
raise TypeError("object_id argument must be of type " + raise TypeError("object_id argument must be of type " +
"ObjectId or an objectid representable integer") "ObjectId or an objectid representable integer")
@@ -5185,8 +5209,26 @@ class MongoDBAdapter(NoSQLAdapter):
hexvalue = hex(arg)[2:].replace("L", "") hexvalue = hex(arg)[2:].replace("L", "")
return self.ObjectId(hexvalue) return self.ObjectId(hexvalue)
def parse_reference(self, value, field_type):
# here we have to check for ObjectID before base parse
if isinstance(value, self.ObjectId):
value = int(str(value), 16)
return super(MongoDBAdapter,
self).parse_reference(value, field_type)
def parse_id(self, value, field_type):
if isinstance(value, self.ObjectId):
value = int(str(value), 16)
return super(MongoDBAdapter,
self).parse_id(value, field_type)
def represent(self, obj, fieldtype): def represent(self, obj, fieldtype):
value = NoSQLAdapter.represent(self, obj, fieldtype) # the base adatpter does not support MongoDB ObjectId
if isinstance(obj, self.ObjectId):
value = obj
else:
value = NoSQLAdapter.represent(self, obj, fieldtype)
# reference types must be convert to ObjectID
if fieldtype =='date': if fieldtype =='date':
if value == None: if value == None:
return value return value
@@ -5203,15 +5245,24 @@ class MongoDBAdapter(NoSQLAdapter):
# mongodb doesn't has a time object and so it must datetime, # mongodb doesn't has a time object and so it must datetime,
# string or integer # string or integer
return datetime.datetime.combine(d, value) return datetime.datetime.combine(d, value)
elif fieldtype == 'list:string' or \ elif (isinstance(fieldtype, basestring) and
fieldtype == 'list:integer' or \ fieldtype.startswith('list:')):
fieldtype == 'list:reference': if fieldtype.startswith('list:reference'):
newval = []
for v in value:
newval.append(self.object_id(v))
return newval
return value return value
elif ((isinstance(fieldtype, basestring) and
fieldtype.startswith("reference")) or
(isinstance(fieldtype, Table))):
value = self.object_id(value)
return value return value
# Safe determines whether a asynchronious request is done or a # Safe determines whether a asynchronious request is done or a
# synchronious action is done # synchronious action is done
# For safety, we use by default synchronious requests # For safety, we use by default synchronous requests
def insert(self, table, fields, safe=None): def insert(self, table, fields, safe=None):
if safe==None: if safe==None:
safe = self.safe safe = self.safe
@@ -5259,14 +5310,18 @@ class MongoDBAdapter(NoSQLAdapter):
if expression.first.type == 'id': if expression.first.type == 'id':
expression.first.name = '_id' expression.first.name = '_id'
# cast to Mongo ObjectId # cast to Mongo ObjectId
expression.second = self.object_id(expression.second) if isinstance(expression.second, (tuple, list, set)):
expression.second = [self.object_id(item) for
item in expression.second]
else:
expression.second = self.object_id(expression.second)
result = expression.op(expression.first, expression.second) result = expression.op(expression.first, expression.second)
if isinstance(expression, Field): if isinstance(expression, Field):
if expression.type=='id': if expression.type=='id':
result = "_id" result = "_id"
else: else:
result = expression.name result = expression.name
elif isinstance(expression, (Expression, Query)): elif isinstance(expression, (Expression, Query)):
if not expression.second is None: if not expression.second is None:
result = expression.op(expression.first, expression.second) result = expression.op(expression.first, expression.second)
@@ -5277,7 +5332,7 @@ class MongoDBAdapter(NoSQLAdapter):
else: else:
result = expression.op result = expression.op
elif field_type: elif field_type:
result = str(self.represent(expression,field_type)) result = self.represent(expression,field_type)
elif isinstance(expression,(list,tuple)): elif isinstance(expression,(list,tuple)):
result = ','.join(self.represent(item,field_type) for result = ','.join(self.represent(item,field_type) for
item in expression) item in expression)
@@ -5331,7 +5386,6 @@ class MongoDBAdapter(NoSQLAdapter):
else: else:
raise SyntaxError("The table name could not be found in " + raise SyntaxError("The table name could not be found in " +
"the query nor from the select statement.") "the query nor from the select statement.")
mongoqry_dict = self.expand(query) mongoqry_dict = self.expand(query)
fields = fields or self.db[tablename] fields = fields or self.db[tablename]
for field in fields: for field in fields:
@@ -5381,15 +5435,12 @@ class MongoDBAdapter(NoSQLAdapter):
# record id's # record id's
if fieldname == "id": fieldname = "_id" if fieldname == "id": fieldname = "_id"
if fieldname in record: if fieldname in record:
if isinstance(record[fieldname], value = record[fieldname]
self.ObjectId):
value = int(str(record[fieldname]), 16)
else:
value = record[fieldname]
else: else:
value = None value = None
row.append(value) row.append(value)
rows.append(row) rows.append(row)
processor = attributes.get('processor', self.parse) processor = attributes.get('processor', self.parse)
result = processor(rows, fields, newnames, False) result = processor(rows, fields, newnames, False)
return result return result
@@ -5489,7 +5540,7 @@ class MongoDBAdapter(NoSQLAdapter):
def BELONGS(self, first, second): def BELONGS(self, first, second):
if isinstance(second, str): if isinstance(second, str):
return {self.expand(first) : {"$in" : [ second[:-1]]} } return {self.expand(first) : {"$in" : [ second[:-1]]} }
elif second==[] or second==(): elif second==[] or second==() or second==set():
return {1:0} return {1:0}
items = [self.expand(item, first.type) for item in second] items = [self.expand(item, first.type) for item in second]
return {self.expand(first) : {"$in" : items} } return {self.expand(first) : {"$in" : items} }
@@ -7052,15 +7103,13 @@ class DAL(object):
Example:: Example::
db = DAL('sqlite://test.db') db = DAL('sqlite://test.db')
or
db = DAL({"uri": ..., "items": ...}) # experimental
db.define_table('tablename', Field('fieldname1'), db.define_table('tablename', Field('fieldname1'),
Field('fieldname2')) Field('fieldname2'))
(experimental)
you can pass a dict object as uri with the uri string
and table/field definitions. For an example of valid data check
the output of:
>>> db.as_dict(flat=True, sanitize=False)
""" """
def __new__(cls, uri='sqlite://dummy.db', *args, **kwargs): def __new__(cls, uri='sqlite://dummy.db', *args, **kwargs):
@@ -7181,6 +7230,28 @@ class DAL(object):
:uri: string that contains information for connecting to a database. :uri: string that contains information for connecting to a database.
(default: 'sqlite://dummy.db') (default: 'sqlite://dummy.db')
experimental: you can specify a dictionary as uri
parameter i.e. with
db = DAL({"uri": "sqlite://storage.sqlite",
"items": {...}, ...})
for an example of dict input you can check the output
of the scaffolding db model with
db.as_dict()
Note that for compatibility with Python older than
version 2.6.5 you should cast your dict input keys
to str due to a syntax limitation on kwarg names.
for proper DAL dictionary input you can use one of:
obj = serializers.cast_keys(dict, [encoding="utf-8"])
or else (for parsing json input)
obj = serializers.loads_json(data, unicode_keys=False)
:pool_size: How many open connections to make to the database object. :pool_size: How many open connections to make to the database object.
:folder: where .table files will be created. :folder: where .table files will be created.
automatically set within web2py automatically set within web2py
@@ -8721,7 +8792,7 @@ class Expression(object):
def len(self): def len(self):
db = self.db db = self.db
return Expression(db, db._adapter.AGGREGATE, self, 'LENGTH', 'integer') return Expression(db, db._adapter.LENGTH, self, None, 'integer')
def avg(self): def avg(self):
db = self.db db = self.db
@@ -8739,6 +8810,10 @@ class Expression(object):
db = self.db db = self.db
return Expression(db, db._adapter.UPPER, self, None, self.type) return Expression(db, db._adapter.UPPER, self, None, self.type)
def replace(self,a,b):
db = self.db
return Expression(db, db._adapter.REPLACE, self, (a,b), self.type)
def year(self): def year(self):
db = self.db db = self.db
return Expression(db, db._adapter.EXTRACT, self, 'year', 'integer') return Expression(db, db._adapter.EXTRACT, self, 'year', 'integer')
@@ -8817,6 +8892,8 @@ class Expression(object):
result_type = 'integer' result_type = 'integer'
elif self.type in ['date','time','datetime','double','float']: elif self.type in ['date','time','datetime','double','float']:
result_type = 'double' result_type = 'double'
elif self.type.startswith('decimal('):
result_type = self.type
else: else:
raise SyntaxError("subtraction operation not supported for type") raise SyntaxError("subtraction operation not supported for type")
return Expression(db,db._adapter.SUB,self,other,result_type) return Expression(db,db._adapter.SUB,self,other,result_type)
+11 -7
View File
@@ -223,19 +223,20 @@ def _extractall(filename, path='.', members=None):
return ret return ret
def tar(file, dir, expression='^.+$'): def tar(file, dir, expression='^.+$', filenames=None):
""" """
tars dir into file, only tars file that match expression tars dir into file, only tars file that match expression
""" """
tar = tarfile.TarFile(file, 'w') tar = tarfile.TarFile(file, 'w')
try: try:
for file in listdir(dir, expression, add_dirs=True): if filenames is None:
filenames = listdir(dir, expression, add_dirs=True)
for file in filenames:
tar.add(os.path.join(dir, file), file, False) tar.add(os.path.join(dir, file), file, False)
finally: finally:
tar.close() tar.close()
def untar(file, dir): def untar(file, dir):
""" """
untar file into dir untar file into dir
@@ -244,14 +245,14 @@ def untar(file, dir):
_extractall(file, dir) _extractall(file, dir)
def w2p_pack(filename, path, compiled=False): def w2p_pack(filename, path, compiled=False, filenames=None):
filename = abspath(filename) filename = abspath(filename)
path = abspath(path) path = abspath(path)
tarname = filename + '.tar' tarname = filename + '.tar'
if compiled: if compiled:
tar_compiled(tarname, path, '^[\w\.\-]+$') tar_compiled(tarname, path, '^[\w\.\-]+$')
else: else:
tar(tarname, path, '^[\w\.\-]+$') tar(tarname, path, '^[\w\.\-]+$', filenames=filenames)
w2pfp = gzopen(filename, 'wb') w2pfp = gzopen(filename, 'wb')
tarfp = open(tarname, 'rb') tarfp = open(tarname, 'rb')
w2pfp.write(tarfp.read()) w2pfp.write(tarfp.read())
@@ -366,16 +367,19 @@ def get_session(request, other_application='admin'):
return osession return osession
def check_credentials(request, other_application='admin', expiration=60 * 60): def check_credentials(request, other_application='admin',
expiration=60 * 60, gae_login=True):
""" checks that user is authorized to access other_application""" """ checks that user is authorized to access other_application"""
if request.env.web2py_runtime_gae: if request.env.web2py_runtime_gae:
from google.appengine.api import users from google.appengine.api import users
if users.is_current_user_admin(): if users.is_current_user_admin():
return True return True
else: elif gae_login:
login_html = '<a href="%s">Sign in with your google account</a>.' \ login_html = '<a href="%s">Sign in with your google account</a>.' \
% users.create_login_url(request.env.path_info) % users.create_login_url(request.env.path_info)
raise HTTP(200, '<html><body>%s</body></html>' % login_html) raise HTTP(200, '<html><body>%s</body></html>' % login_html)
else:
return False
else: else:
dt = time.time() - expiration dt = time.time() - expiration
s = get_session(request, other_application) s = get_session(request, other_application)
+2 -3
View File
@@ -840,9 +840,8 @@ class DIV(XmlComponent):
c.latest = self.latest c.latest = self.latest
c.session = self.session c.session = self.session
c.formname = self.formname c.formname = self.formname
if hideerror and not \ c['hideerror'] = hideerror or \
self.attributes.get('hideerror', False): self.attributes.get('hideerror', False)
c['hideerror'] = hideerror
newstatus = c._traverse(status, hideerror) and newstatus newstatus = c._traverse(status, hideerror) and newstatus
# for input, textarea, select, option # for input, textarea, select, option
+1 -1
View File
@@ -131,7 +131,7 @@ def get_client(env):
""" """
g = regex_client.search(env.get('http_x_forwarded_for', '')) g = regex_client.search(env.get('http_x_forwarded_for', ''))
client = (g.group() or '').split(',')[0] if g else None client = (g.group() or '').split(',')[0] if g else None
if client in (None, '', 'unkown'): if client in (None, '', 'unknown'):
g = regex_client.search(env.get('remote_addr', '')) g = regex_client.search(env.get('remote_addr', ''))
if g: if g:
client = g.group() client = g.group()
+3 -3
View File
@@ -66,7 +66,7 @@ class XssCleaner(HTMLParser):
# The only schemes allowed in URLs (for href and src attributes). # The only schemes allowed in URLs (for href and src attributes).
# Adding "javascript" or "vbscript" to this list would not be smart. # Adding "javascript" or "vbscript" to this list would not be smart.
self.allowed_schemes = ['http', 'https', 'ftp'] self.allowed_schemes = ['http', 'https', 'ftp', 'mailto']
#to strip or escape disallowed tags? #to strip or escape disallowed tags?
self.strip_disallowed = strip_disallowed self.strip_disallowed = strip_disallowed
@@ -151,11 +151,12 @@ class XssCleaner(HTMLParser):
def url_is_acceptable(self, url): def url_is_acceptable(self, url):
""" """
Accepts relative and absolute urls Accepts relative, absolute, and mailto urls
""" """
parsed = urlparse(url) parsed = urlparse(url)
return (parsed[0] in self.allowed_schemes and '.' in parsed[1]) \ return (parsed[0] in self.allowed_schemes and '.' in parsed[1]) \
or (parsed[0] in self.allowed_schemes and '@' in parsed[2]) \
or (parsed[0] == '' and parsed[2].startswith('/')) or (parsed[0] == '' and parsed[2].startswith('/'))
def strip(self, rawstring, escape=True): def strip(self, rawstring, escape=True):
@@ -225,4 +226,3 @@ def sanitize(text, permitted_tags=[
return str(text) return str(text)
return XssCleaner(permitted_tags=permitted_tags, return XssCleaner(permitted_tags=permitted_tags,
allowed_attributes=allowed_attributes).strip(text, escape) allowed_attributes=allowed_attributes).strip(text, escape)
+55 -9
View File
@@ -107,6 +107,7 @@ TERMINATE = 'TERMINATE'
DISABLED = 'DISABLED' DISABLED = 'DISABLED'
KILL = 'KILL' KILL = 'KILL'
PICK = 'PICK' PICK = 'PICK'
STOP_TASK = 'STOP_TASK'
EXPIRED = 'EXPIRED' EXPIRED = 'EXPIRED'
SECONDS = 1 SECONDS = 1
HEARTBEAT = 3 * SECONDS HEARTBEAT = 3 * SECONDS
@@ -398,7 +399,7 @@ class MetaScheduler(threading.Thread):
TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED) TASK_STATUS = (QUEUED, RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED, EXPIRED)
RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED) RUN_STATUS = (RUNNING, COMPLETED, FAILED, TIMEOUT, STOPPED)
WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL) WORKER_STATUS = (ACTIVE, PICK, DISABLED, TERMINATE, KILL, STOP_TASK)
class TYPE(object): class TYPE(object):
@@ -562,16 +563,18 @@ class Scheduler(MetaScheduler):
self.die() self.die()
def wrapped_assign_tasks(self, db): def wrapped_assign_tasks(self, db):
logger.debug('Assigning tasks...')
db.commit() #db.commit() only for Mysql db.commit() #db.commit() only for Mysql
x = 0 x = 0
while x < 10: while x < 10:
try: try:
self.assign_tasks(db) self.assign_tasks(db)
db.commit() db.commit()
logger.debug('Tasks assigned...')
break break
except: except:
db.rollback() db.rollback()
logger.error('TICKER: error assigning tasks') logger.error('TICKER: error assigning tasks (%s)', x)
x += 1 x += 1
time.sleep(0.5) time.sleep(0.5)
@@ -746,7 +749,7 @@ class Scheduler(MetaScheduler):
# keep sleeping # keep sleeping
self.worker_status[0] = DISABLED self.worker_status[0] = DISABLED
if self.worker_status[1] == MAXHIBERNATION: if self.worker_status[1] == MAXHIBERNATION:
logger.debug('........recording heartbeat') logger.debug('........recording heartbeat (%s)', self.worker_status[0])
db(sw.worker_name == self.worker_name).update( db(sw.worker_name == self.worker_name).update(
last_heartbeat=now) last_heartbeat=now)
elif mybackedstatus == TERMINATE: elif mybackedstatus == TERMINATE:
@@ -758,11 +761,14 @@ class Scheduler(MetaScheduler):
self.worker_status[0] = KILL self.worker_status[0] = KILL
self.die() self.die()
else: else:
if mybackedstatus == STOP_TASK:
logger.info('Asked to kill the current task')
self.terminate_process()
logger.debug('........recording heartbeat (%s)', self.worker_status[0]) logger.debug('........recording heartbeat (%s)', self.worker_status[0])
db(sw.worker_name == self.worker_name).update( db(sw.worker_name == self.worker_name).update(
last_heartbeat=now, status=ACTIVE) last_heartbeat=now, status=ACTIVE)
self.worker_status[1] = 1 # re-activating the process self.worker_status[1] = 1 # re-activating the process
if self.worker_status[0] <> RUNNING: if self.worker_status[0] != RUNNING:
self.worker_status[0] = ACTIVE self.worker_status[0] = ACTIVE
self.do_assign_tasks = False self.do_assign_tasks = False
@@ -782,13 +788,17 @@ class Scheduler(MetaScheduler):
inactive_workers._select(sw.worker_name)))(st.status == RUNNING)\ inactive_workers._select(sw.worker_name)))(st.status == RUNNING)\
.update(assigned_worker_name='', status=QUEUED) .update(assigned_worker_name='', status=QUEUED)
inactive_workers.delete() inactive_workers.delete()
self.is_a_ticker = self.being_a_ticker() try:
self.is_a_ticker = self.being_a_ticker()
except:
logger.error('Error coordinating TICKER')
if self.worker_status[0] == ACTIVE: if self.worker_status[0] == ACTIVE:
self.do_assign_tasks = True self.do_assign_tasks = True
except: except:
pass logger.error('Error cleaning up')
db.commit() db.commit()
except: except:
logger.error('Error retrieving status')
db.rollback() db.rollback()
self.adj_hibernation() self.adj_hibernation()
self.sleep() self.sleep()
@@ -802,14 +812,16 @@ class Scheduler(MetaScheduler):
ticker = all_active.find(lambda row: row.is_ticker is True).first() ticker = all_active.find(lambda row: row.is_ticker is True).first()
not_busy = self.worker_status[0] == ACTIVE not_busy = self.worker_status[0] == ACTIVE
if not ticker: if not ticker:
#if no other tickers are around
if not_busy: if not_busy:
#only if this worker isn't busy, otherwise wait for a free one #only if I'm not busy
db(sw.worker_name == self.worker_name).update(is_ticker=True) db(sw.worker_name == self.worker_name).update(is_ticker=True)
db(sw.worker_name != self.worker_name).update(is_ticker=False) db(sw.worker_name != self.worker_name).update(is_ticker=False)
logger.info("TICKER: I'm a ticker") logger.info("TICKER: I'm a ticker")
else: else:
#giving up, only if I'm not alone #I'm busy
if len(all_active) > 1: if len(all_active) >= 1:
#so I'll "downgrade" myself to a "poor worker"
db(sw.worker_name == self.worker_name).update(is_ticker=False) db(sw.worker_name == self.worker_name).update(is_ticker=False)
else: else:
not_busy = True not_busy = True
@@ -1007,6 +1019,40 @@ class Scheduler(MetaScheduler):
object_hook=_decode_dict) or None object_hook=_decode_dict) or None
return row return row
def stop_task(self, ref):
"""
Experimental!!!
Shortcut for task termination.
If the task is RUNNING it will terminate it --> execution will be set as FAILED
If the task is QUEUED, its stop_time will be set as to "now",
the enabled flag will be set to False, status to STOPPED
:param ref: can be
- integer --> lookup will be done by scheduler_task.id
- string --> lookup will be done by scheduler_task.uuid
Returns:
- 1 if task was stopped (meaning an update has been done)
- None if task was not found, or if task was not RUNNING or QUEUED
"""
from gluon.dal import Query
st, sw = self.db.scheduler_task, self.db.scheduler_worker
if isinstance(ref, int):
q = st.id == ref
elif isinstance(ref, str):
q = st.uuid == ref
else:
raise SyntaxError(
"You can retrieve results only by id or uuid")
task = self.db(q).select(st.id, st.status, st.assigned_worker_name).first()
rtn = None
if not task:
return rtn
if task.status == 'RUNNING':
rtn = self.db(sw.worker_name == task.assigned_worker_name).update(status=STOP_TASK)
elif task.status == 'QUEUED':
rtn = self.db(q).update(stop_time=self.now(), enabled=False, status=STOPPED)
return rtn
def main(): def main():
""" """
+52 -9
View File
@@ -25,9 +25,52 @@ try:
except ImportError: except ImportError:
have_yaml = False have_yaml = False
def loads_json(o): def cast_keys(o, cast=str, encoding="utf-8"):
""" Builds a new object with <cast> type keys
Arguments:
o is the object input
cast (defaults to str) is an object type or function
which supports conversion such as:
>>> converted = cast(o)
encoding (defaults to utf-8) is the encoding for unicode
keys. This is not used for custom cast functions
Use this funcion if you are in Python < 2.6.5
This avoids syntax errors when unpacking dictionary arguments.
"""
if isinstance(o, (dict, Storage)):
if isinstance(o, dict):
newobj = dict()
else:
newobj = Storage()
for k, v in o.items():
if (cast == str) and isinstance(k, unicode):
key = k.encode(encoding)
else:
key = cast(k)
if isinstance(v, (dict, Storage)):
value = cast_keys(v, cast=cast, encoding=encoding)
else:
value = v
newobj[key] = value
else:
raise TypeError("Cannot cast keys: %s is not supported" % \
type(o))
return newobj
def loads_json(o, unicode_keys=True, **kwargs):
# deserialize a json string # deserialize a json string
return json_parser.loads(o) result = json_parser.loads(o, **kwargs)
if not unicode_keys:
# filter non-str keys in dictionary objects
result = cast_keys(result,
encoding=kwargs.get("encoding", "utf-8"))
return result
def custom_json(o): def custom_json(o):
if hasattr(o, 'custom_json') and callable(o.custom_json): if hasattr(o, 'custom_json') and callable(o.custom_json):
@@ -117,18 +160,19 @@ def rss(feed):
if not 'entries' in feed and 'items' in feed: if not 'entries' in feed and 'items' in feed:
feed['entries'] = feed['items'] feed['entries'] = feed['items']
now = datetime.datetime.now() now = datetime.datetime.now()
rss = rss2.RSS2(title=str(feed.get('title', '(notitle)')), rss = rss2.RSS2(title=str(feed.get('title', '(notitle)').encode('utf-8', 'replace')),
link=str(feed.get('link', None)), link=str(feed.get('link', None).encode('utf-8', 'replace')),
description=str(feed.get('description', '')), description=str(feed.get('description', '').encode('utf-8', 'replace')),
lastBuildDate=feed.get('created_on', now), lastBuildDate=feed.get('created_on', now),
items=[rss2.RSSItem( items=[rss2.RSSItem(
title=str(entry.get('title', '(notitle)')), title=str(entry.get('title', '(notitle)').encode('utf-8', 'replace')),
link=str(entry.get('link', None)), link=str(entry.get('link', None).encode('utf-8', 'replace')),
description=str(entry.get('description', '')), description=str(entry.get('description', '').encode('utf-8', 'replace')),
pubDate=entry.get('created_on', now) pubDate=entry.get('created_on', now)
) for entry in feed.get('entries', [])]) ) for entry in feed.get('entries', [])])
return rss.to_xml(encoding='utf-8') return rss.to_xml(encoding='utf-8')
def yaml(data): def yaml(data):
if have_yaml: if have_yaml:
return yamlib.dump(data) return yamlib.dump(data)
@@ -138,4 +182,3 @@ def loads_yaml(data):
if have_yaml: if have_yaml:
return yamlib.load(data) return yamlib.load(data)
else: raise ImportError("No YAML serializer available") else: raise ImportError("No YAML serializer available")
+78 -32
View File
@@ -19,6 +19,7 @@ except ImportError:
from cgi import parse_qs as psq from cgi import parse_qs as psq
import os import os
from http import HTTP from http import HTTP
from html import XmlComponent
from html import XML, SPAN, TAG, A, DIV, CAT, UL, LI, TEXTAREA, BR, IMG, SCRIPT from html import XML, SPAN, TAG, A, DIV, CAT, UL, LI, TEXTAREA, BR, IMG, SCRIPT
from html import FORM, INPUT, LABEL, OPTION, SELECT, BUTTON from html import FORM, INPUT, LABEL, OPTION, SELECT, BUTTON
from html import TABLE, THEAD, TBODY, TR, TD, TH, STYLE from html import TABLE, THEAD, TBODY, TR, TD, TH, STYLE
@@ -349,6 +350,12 @@ class RadioWidget(OptionsWidget):
see also: :meth:`FormWidget.widget` see also: :meth:`FormWidget.widget`
""" """
if isinstance(value, (list,tuple)):
value = str(value[0])
else:
value = str(value)
attr = cls._attributes(field, {}, **attributes) attr = cls._attributes(field, {}, **attributes)
attr['_class'] = attr.get('_class', 'web2py_radiowidget') attr['_class'] = attr.get('_class', 'web2py_radiowidget')
@@ -1174,9 +1181,10 @@ class SQLFORM(FORM):
xfields.append( xfields.append(
(self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX, (self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_ROW_SUFFIX,
LABEL( LABEL(
delete_label, separator, T(delete_label), separator,
_for=self.FIELDKEY_DELETE_RECORD, _for=self.FIELDKEY_DELETE_RECORD,
_id=self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_LABEL_SUFFIX), _id=self.FIELDKEY_DELETE_RECORD + \
SQLFORM.ID_LABEL_SUFFIX),
widget, widget,
col3.get(self.FIELDKEY_DELETE_RECORD, ''))) col3.get(self.FIELDKEY_DELETE_RECORD, '')))
self.custom.delete = self.custom.deletable = widget self.custom.delete = self.custom.deletable = widget
@@ -1223,7 +1231,8 @@ class SQLFORM(FORM):
table = TABLE() table = TABLE()
for id, a, b, c in xfields: for id, a, b, c in xfields:
newrows = formstyle(id, a, b, c) newrows = formstyle(id, a, b, c)
self.field_parent[id] = getattr(b, 'parent', None) self.field_parent[id] = getattr(b, 'parent', None) \
if isinstance(b,XmlComponent) else None
if type(newrows).__name__ != "tuple": if type(newrows).__name__ != "tuple":
newrows = [newrows] newrows = [newrows]
for newrow in newrows: for newrow in newrows:
@@ -1231,7 +1240,8 @@ class SQLFORM(FORM):
else: else:
table = formstyle(self, xfields) table = formstyle(self, xfields)
for id, a, b, c in xfields: for id, a, b, c in xfields:
self.field_parent[id] = getattr(b, 'parent', None) self.field_parent[id] = getattr(b, 'parent', None) \
if isinstance(b,XmlComponent) else None
else: else:
raise RuntimeError('formstyle not supported') raise RuntimeError('formstyle not supported')
return table return table
@@ -1440,7 +1450,8 @@ class SQLFORM(FORM):
f = self.table[fieldname].default or '' f = self.table[fieldname].default or ''
fields[fieldname] = f fields[fieldname] = f
else: else:
fields[fieldname] = '' f = self.table[fieldname].default or ''
fields[fieldname] = f
self.vars[fieldname] = fields[fieldname] self.vars[fieldname] = fields[fieldname]
if not f: if not f:
continue continue
@@ -1580,7 +1591,7 @@ class SQLFORM(FORM):
table_name = attributes.get('table_name', 'no_table') table_name = attributes.get('table_name', 'no_table')
# So it won't interfear with SQLDB.define_table # So it won't interfere with SQLDB.define_table
if 'table_name' in attributes: if 'table_name' in attributes:
del attributes['table_name'] del attributes['table_name']
@@ -1759,6 +1770,7 @@ class SQLFORM(FORM):
createargs={}, createargs={},
editargs={}, editargs={},
viewargs={}, viewargs={},
selectable_submit_button='Submit',
buttons_placement = 'right', buttons_placement = 'right',
links_placement = 'right', links_placement = 'right',
noconfirm=False noconfirm=False
@@ -1891,7 +1903,7 @@ class SQLFORM(FORM):
tablename = table._tablename tablename = table._tablename
if upload == '<default>': if upload == '<default>':
upload = lambda filename: url(args=['download', filename]) upload = lambda filename: url(args=['download', filename])
if len(request.args) > 1 and request.args[-2] == 'download': if request.args(-2) == 'download':
stream = response.download(request, db) stream = response.download(request, db)
raise HTTP(200, stream, **response.headers) raise HTTP(200, stream, **response.headers)
@@ -1928,7 +1940,7 @@ class SQLFORM(FORM):
create_form = update_form = view_form = search_form = None create_form = update_form = view_form = search_form = None
sqlformargs = dict(formargs) sqlformargs = dict(formargs)
if create and len(request.args) > 1 and request.args[-2] == 'new': if create and request.args(-2) == 'new':
table = db[request.args[-1]] table = db[request.args[-1]]
sqlformargs.update(createargs) sqlformargs.update(createargs)
create_form = SQLFORM( create_form = SQLFORM(
@@ -1947,7 +1959,7 @@ class SQLFORM(FORM):
res.search_form = search_form res.search_form = search_form
return res return res
elif details and len(request.args) > 2 and request.args[-3] == 'view': elif details and request.args(-3) == 'view':
table = db[request.args[-2]] table = db[request.args[-2]]
record = table(request.args[-1]) or redirect(referrer) record = table(request.args[-1]) or redirect(referrer)
sqlformargs.update(viewargs) sqlformargs.update(viewargs)
@@ -1962,7 +1974,7 @@ class SQLFORM(FORM):
res.view_form = view_form res.view_form = view_form
res.search_form = search_form res.search_form = search_form
return res return res
elif editable and len(request.args) > 2 and request.args[-3] == 'edit': elif editable and request.args(-3) == 'edit':
table = db[request.args[-2]] table = db[request.args[-2]]
record = table(request.args[-1]) or redirect(URL('error')) record = table(request.args[-1]) or redirect(URL('error'))
sqlformargs.update(editargs) sqlformargs.update(editargs)
@@ -1987,7 +1999,7 @@ class SQLFORM(FORM):
res.view_form = view_form res.view_form = view_form
res.search_form = search_form res.search_form = search_form
return res return res
elif deletable and len(request.args) > 2 and request.args[-3] == 'delete': elif deletable and request.args(-3) == 'delete':
table = db[request.args[-2]] table = db[request.args[-2]]
if ondelete: if ondelete:
ondelete(table, request.args[-1]) ondelete(table, request.args[-1])
@@ -2110,10 +2122,15 @@ class SQLFORM(FORM):
if subquery: if subquery:
dbset = dbset(subquery) dbset = dbset(subquery)
try: try:
if left or groupby: if groupby:
c = 'count(*)' c = 'count(*)'
nrows = dbset.select(c, left=left, cacheable=True, nrows = db.executesql(
groupby=groupby).first()[c] 'select count(*) from (%s);' %
dbset._select(c, left=left, cacheable=True,
groupby=groupby)[:-1])[0][0]
elif left:
c = 'count(*)'
nrows = dbset.select(c, left=left, cacheable=True).first()[c]
elif dbset._db._adapter.dbengine=='google:datastore': elif dbset._db._adapter.dbengine=='google:datastore':
#if we don't set a limit, this can timeout for a large table #if we don't set a limit, this can timeout for a large table
nrows = dbset.db._adapter.count(dbset.query, limit=1000) nrows = dbset.db._adapter.count(dbset.query, limit=1000)
@@ -2367,9 +2384,12 @@ class SQLFORM(FORM):
tr = TR(*trcols, **dict(_class=classtr)) tr = TR(*trcols, **dict(_class=classtr))
tbody.append(tr) tbody.append(tr)
htmltable.append(tbody) htmltable.append(tbody)
htmltable = DIV(htmltable, _style='width:100%;overflow-x:auto') htmltable = DIV(
htmltable, _class='web2py_htmltable',
_style='width:100%;overflow-x:auto;-ms-overflow-x:scroll')
if selectable: if selectable:
htmltable = FORM(htmltable, INPUT(_type="submit")) htmltable = FORM(htmltable, INPUT(
_type="submit", _value=T(selectable_submit_button)))
if htmltable.process(formname=formname).accepted: if htmltable.process(formname=formname).accepted:
htmltable.vars.records = htmltable.vars.records or [] htmltable.vars.records = htmltable.vars.records or []
htmltable.vars.records = htmltable.vars.records if type(htmltable.vars.records) == list else [htmltable.vars.records] htmltable.vars.records = htmltable.vars.records if type(htmltable.vars.records) == list else [htmltable.vars.records]
@@ -2517,8 +2537,9 @@ class SQLFORM(FORM):
break break
if nargs > len(args) + 1: if nargs > len(args) + 1:
query = (field == id) query = (field == id)
if isinstance(linked_tables, dict): # cjk
linked_tables = linked_tables.get(table._tablename, []) # if isinstance(linked_tables, dict):
# linked_tables = linked_tables.get(table._tablename, [])
if linked_tables is None or referee in linked_tables: if linked_tables is None or referee in linked_tables:
field.represent = lambda id, r=None, referee=referee, rep=field.represent: A(callable(rep) and rep(id) or id, _class=trap_class(), _href=url(args=['view', referee, id])) field.represent = lambda id, r=None, referee=referee, rep=field.represent: A(callable(rep) and rep(id) or id, _class=trap_class(), _href=url(args=['view', referee, id]))
except (KeyError, ValueError, TypeError): except (KeyError, ValueError, TypeError):
@@ -2543,20 +2564,45 @@ class SQLFORM(FORM):
check[rfield.tablename] = \ check[rfield.tablename] = \
check.get(rfield.tablename, []) + [rfield.name] check.get(rfield.tablename, []) + [rfield.name]
if isinstance(linked_tables, dict): if isinstance(linked_tables, dict):
linked_tables = linked_tables.get(table._tablename, []) for tbl in linked_tables.keys():
for tablename in sorted(check): tb = db[tbl]
linked_fieldnames = check[tablename] if isinstance(linked_tables[tbl], list):
tb = db[tablename] if len(linked_tables[tbl]) > 1:
multiple_links = len(linked_fieldnames) > 1 t = T('%s(%s)' %(tbl, fld))
for fieldname in linked_fieldnames: else:
if linked_tables is None or tablename in linked_tables: t = T(tb._plural)
t = T(tb._plural) if not multiple_links else \ for fld in linked_tables[tbl]:
T(tb._plural + '(' + fieldname + ')') if fld not in db[tbl].fields:
args0 = tablename + '.' + fieldname raise ValueError('Field %s not in table' %fld)
args0 = tbl + '.' + fld
links.append(
lambda row, t=t, nargs=nargs, args0=args0:
A(SPAN(t), _class=trap_class(), _href=url(
args=[args0, row[id_field_name]])))
else:
t = T(tb._plural)
fld = linked_tables[tbl]
if fld not in db[tbl].fields:
raise ValueError('Field %s not in table' %fld)
args0 = tbl + '.' + fld
links.append( links.append(
lambda row, t=t, nargs=nargs, args0=args0: lambda row, t=t, nargs=nargs, args0=args0:
A(SPAN(t), _class=trap_class(), _href=url( A(SPAN(t), _class=trap_class(), _href=url(
args=[args0, row[id_field_name]]))) args=[args0, row[id_field_name]])))
else:
for tablename in sorted(check):
linked_fieldnames = check[tablename]
tb = db[tablename]
multiple_links = len(linked_fieldnames) > 1
for fieldname in linked_fieldnames:
if linked_tables is None or tablename in linked_tables:
t = T(tb._plural) if not multiple_links else \
T(tb._plural + '(' + fieldname + ')')
args0 = tablename + '.' + fieldname
links.append(
lambda row, t=t, nargs=nargs, args0=args0:
A(SPAN(t), _class=trap_class(), _href=url(
args=[args0, row[id_field_name]])))
grid = SQLFORM.grid(query, args=request.args[:nargs], links=links, grid = SQLFORM.grid(query, args=request.args[:nargs], links=links,
links_in_grid=links_in_grid, links_in_grid=links_in_grid,
@@ -2634,7 +2680,7 @@ class SQLTABLE(TABLE):
:param selectid: The id you want to select :param selectid: The id you want to select
:param renderstyle: Boolean render the style with the table :param renderstyle: Boolean render the style with the table
:param extracolums = [{'label':A('Extra',_href='#'), :param extracolumns = [{'label':A('Extra',_href='#'),
'class': '', #class name of the header 'class': '', #class name of the header
'width':'', #width in pixels or % 'width':'', #width in pixels or %
'content':lambda row, rc: A('Edit',_href='edit/%s'%row.id), 'content':lambda row, rc: A('Edit',_href='edit/%s'%row.id),
@@ -2656,8 +2702,8 @@ class SQLTABLE(TABLE):
}, },
} }
table = SQLTABLE(rows, headers=headers, extracolums=extracolums) table = SQLTABLE(rows, headers=headers, extracolumns=extracolumns)
`<
""" """
+1 -1
View File
@@ -10,4 +10,4 @@ from test_template import *
from test_utils import * from test_utils import *
from test_contribs import * from test_contribs import *
from test_markmin import * from test_markmin import *
# from test_web import * from test_web import *
+335 -286
View File
@@ -6,6 +6,8 @@
import sys import sys
import os import os
import glob
if os.path.isdir('gluon'): if os.path.isdir('gluon'):
sys.path.append(os.path.realpath('gluon')) sys.path.append(os.path.realpath('gluon'))
else: else:
@@ -19,6 +21,11 @@ except:
from io import StringIO from io import StringIO
from dal import DAL, Field, Table, SQLALL from dal import DAL, Field, Table, SQLALL
#for travis-ci
DEFAULT_URI = os.environ.get('DB', 'sqlite:memory')
print 'Testing against %s engine (%s)' % (DEFAULT_URI.partition(':')[0], DEFAULT_URI)
ALLOWED_DATATYPES = [ ALLOWED_DATATYPES = [
'string', 'string',
'text', 'text',
@@ -41,6 +48,8 @@ def setUpModule():
def tearDownModule(): def tearDownModule():
if os.path.isfile('sql.log'): if os.path.isfile('sql.log'):
os.unlink('sql.log') os.unlink('sql.log')
for a in glob.glob('*.table'):
os.unlink(a)
class TestFields(unittest.TestCase): class TestFields(unittest.TestCase):
@@ -96,35 +105,35 @@ class TestFields(unittest.TestCase):
isinstance(f.formatter(datetime.datetime.now()), str) isinstance(f.formatter(datetime.datetime.now()), str)
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
for ft in ['string', 'text', 'password', 'upload', 'blob']: for ft in ['string', 'text', 'password', 'upload', 'blob']:
db.define_table('t', Field('a', ft, default='')) db.define_table('tt', Field('aa', ft, default=''))
self.assertEqual(db.t.insert(a='x'), 1) self.assertEqual(db.tt.insert(aa='x'), 1)
self.assertEqual(db().select(db.t.a)[0].a, 'x') self.assertEqual(db().select(db.tt.aa)[0].aa, 'x')
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'integer', default=1)) db.define_table('tt', Field('aa', 'integer', default=1))
self.assertEqual(db.t.insert(a=3), 1) self.assertEqual(db.tt.insert(aa=3), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3) self.assertEqual(db().select(db.tt.aa)[0].aa, 3)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'double', default=1)) db.define_table('tt', Field('aa', 'double', default=1))
self.assertEqual(db.t.insert(a=3.1), 1) self.assertEqual(db.tt.insert(aa=3.1), 1)
self.assertEqual(db().select(db.t.a)[0].a, 3.1) self.assertEqual(db().select(db.tt.aa)[0].aa, 3.1)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'boolean', default=True)) db.define_table('tt', Field('aa', 'boolean', default=True))
self.assertEqual(db.t.insert(a=True), 1) self.assertEqual(db.tt.insert(aa=True), 1)
self.assertEqual(db().select(db.t.a)[0].a, True) self.assertEqual(db().select(db.tt.aa)[0].aa, True)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'json', default={})) db.define_table('tt', Field('aa', 'json', default={}))
self.assertEqual(db.t.insert(a={}), 1) self.assertEqual(db.tt.insert(aa={}), 1)
self.assertEqual(db().select(db.t.a)[0].a, {}) self.assertEqual(db().select(db.tt.aa)[0].aa, {})
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'date', db.define_table('tt', Field('aa', 'date',
default=datetime.date.today())) default=datetime.date.today()))
t0 = datetime.date.today() t0 = datetime.date.today()
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'datetime', db.define_table('tt', Field('aa', 'datetime',
default=datetime.datetime.today())) default=datetime.datetime.today()))
t0 = datetime.datetime( t0 = datetime.datetime(
1971, 1971,
@@ -135,34 +144,34 @@ class TestFields(unittest.TestCase):
55, 55,
0, 0,
) )
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
## Row APIs ## Row APIs
row = db().select(db.t.a)[0] row = db().select(db.tt.aa)[0]
self.assertEqual(db.t[1].a,t0) self.assertEqual(db.tt[1].aa,t0)
self.assertEqual(db.t['a'],db.t.a) self.assertEqual(db.tt['aa'],db.tt.aa)
self.assertEqual(db.t(1).a,t0) self.assertEqual(db.tt(1).aa,t0)
self.assertTrue(db.t(1,a=None)==None) self.assertTrue(db.tt(1,aa=None)==None)
self.assertFalse(db.t(1,a=t0)==None) self.assertFalse(db.tt(1,aa=t0)==None)
self.assertEqual(row.a,t0) self.assertEqual(row.aa,t0)
self.assertEqual(row['a'],t0) self.assertEqual(row['aa'],t0)
self.assertEqual(row['t.a'],t0) self.assertEqual(row['tt.aa'],t0)
self.assertEqual(row('t.a'),t0) self.assertEqual(row('tt.aa'),t0)
## Lazy and Virtual fields ## Lazy and Virtual fields
db.t.b = Field.Virtual(lambda row: row.t.a) db.tt.b = Field.Virtual(lambda row: row.tt.aa)
db.t.c = Field.Lazy(lambda row: row.t.a) db.tt.c = Field.Lazy(lambda row: row.tt.aa)
row = db().select(db.t.a)[0] row = db().select(db.tt.aa)[0]
self.assertEqual(row.b,t0) self.assertEqual(row.b,t0)
self.assertEqual(row.c(),t0) self.assertEqual(row.c(),t0)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'time', default='11:30')) db.define_table('tt', Field('aa', 'time', default='11:30'))
t0 = datetime.time(10, 30, 55) t0 = datetime.time(10, 30, 55)
self.assertEqual(db.t.insert(a=t0), 1) self.assertEqual(db.tt.insert(aa=t0), 1)
self.assertEqual(db().select(db.t.a)[0].a, t0) self.assertEqual(db().select(db.tt.aa)[0].aa, t0)
db.t.drop() db.tt.drop()
class TestAll(unittest.TestCase): class TestAll(unittest.TestCase):
@@ -198,7 +207,7 @@ class TestTable(unittest.TestCase):
in str(persons.ALL)) in str(persons.ALL))
def testTableAlias(self): def testTableAlias(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
persons = Table(db, 'persons', Field('firstname', persons = Table(db, 'persons', Field('firstname',
'string'), Field('lastname', 'string')) 'string'), Field('lastname', 'string'))
aliens = persons.with_alias('aliens') aliens = persons.with_alias('aliens')
@@ -221,276 +230,279 @@ class TestTable(unittest.TestCase):
class TestInsert(unittest.TestCase): class TestInsert(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='1'), 2) self.assertEqual(db.tt.insert(aa='1'), 2)
self.assertEqual(db.t.insert(a='1'), 3) self.assertEqual(db.tt.insert(aa='1'), 3)
self.assertEqual(db(db.t.a == '1').count(), 3) self.assertEqual(db(db.tt.aa == '1').count(), 3)
self.assertEqual(db(db.t.a == '1').update(a='2'), 3) self.assertEqual(db(db.tt.aa == '1').update(aa='2'), 3)
self.assertEqual(db(db.t.a == '2').count(), 3) self.assertEqual(db(db.tt.aa == '2').count(), 3)
self.assertEqual(db(db.t.a == '2').delete(), 3) self.assertEqual(db(db.tt.aa == '2').delete(), 3)
db.t.drop() db.tt.drop()
class TestSelect(unittest.TestCase): class TestSelect(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2) self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3) self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.t.id > 0).select()), 3) self.assertEqual(db(db.tt.id > 0).count(), 3)
self.assertEqual(db(db.t.id > 0).select(orderby=~db.t.a self.assertEqual(db(db.tt.id > 0).select(orderby=~db.tt.aa
| db.t.id)[0].a, '3') | db.tt.id)[0].aa, '3')
self.assertEqual(len(db(db.t.id > 0).select(limitby=(1, 2))), 1) self.assertEqual(len(db(db.tt.id > 0).select(limitby=(1, 2))), 1)
self.assertEqual(db(db.t.id > 0).select(limitby=(1, 2))[0].a, self.assertEqual(db(db.tt.id > 0).select(limitby=(1, 2))[0].aa,
'2') '2')
self.assertEqual(len(db().select(db.t.ALL)), 3) self.assertEqual(len(db().select(db.tt.ALL)), 3)
self.assertEqual(len(db(db.t.a == None).select()), 0) self.assertEqual(db(db.tt.aa == None).count(), 0)
self.assertEqual(len(db(db.t.a != None).select()), 3) self.assertEqual(db(db.tt.aa != None).count(), 3)
self.assertEqual(len(db(db.t.a > '1').select()), 2) self.assertEqual(db(db.tt.aa > '1').count(), 2)
self.assertEqual(len(db(db.t.a >= '1').select()), 3) self.assertEqual(db(db.tt.aa >= '1').count(), 3)
self.assertEqual(len(db(db.t.a == '1').select()), 1) self.assertEqual(db(db.tt.aa == '1').count(), 1)
self.assertEqual(len(db(db.t.a != '1').select()), 2) self.assertEqual(db(db.tt.aa != '1').count(), 2)
self.assertEqual(len(db(db.t.a < '3').select()), 2) self.assertEqual(db(db.tt.aa < '3').count(), 2)
self.assertEqual(len(db(db.t.a <= '3').select()), 3) self.assertEqual(db(db.tt.aa <= '3').count(), 3)
self.assertEqual(len(db(db.t.a > '1')(db.t.a < '3').select()), 1) self.assertEqual(db(db.tt.aa > '1')(db.tt.aa < '3').count(), 1)
self.assertEqual(len(db((db.t.a > '1') & (db.t.a < '3')).select()), 1) self.assertEqual(db((db.tt.aa > '1') & (db.tt.aa < '3')).count(), 1)
self.assertEqual(len(db((db.t.a > '1') | (db.t.a < '3')).select()), 3) self.assertEqual(db((db.tt.aa > '1') | (db.tt.aa < '3')).count(), 3)
self.assertEqual(len(db((db.t.a > '1') & ~(db.t.a > '2')).select()), 1) self.assertEqual(db((db.tt.aa > '1') & ~(db.tt.aa > '2')).count(), 1)
self.assertEqual(len(db(~(db.t.a > '1') & (db.t.a > '2')).select()), 0) self.assertEqual(db(~(db.tt.aa > '1') & (db.tt.aa > '2')).count(), 0)
db.t.drop() db.tt.drop()
class TestBelongs(unittest.TestCase): class TestBelongs(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='1'), 1) self.assertEqual(db.tt.insert(aa='1'), 1)
self.assertEqual(db.t.insert(a='2'), 2) self.assertEqual(db.tt.insert(aa='2'), 2)
self.assertEqual(db.t.insert(a='3'), 3) self.assertEqual(db.tt.insert(aa='3'), 3)
self.assertEqual(len(db(db.t.a.belongs(('1', '3'))).select()), self.assertEqual(db(db.tt.aa.belongs(('1', '3'))).count(),
2) 2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.id self.assertEqual(db(db.tt.aa.belongs(db(db.tt.id
> 2)._select(db.t.a))).select()), 1) > 2)._select(db.tt.aa))).count(), 1)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(('1', self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(('1',
'3')))._select(db.t.a))).select()), 2) '3')))._select(db.tt.aa))).count(), 2)
self.assertEqual(len(db(db.t.a.belongs(db(db.t.a.belongs(db self.assertEqual(db(db.tt.aa.belongs(db(db.tt.aa.belongs(db
(db.t.a.belongs(('1', '3')))._select(db.t.a)))._select( (db.tt.aa.belongs(('1', '3')))._select(db.tt.aa)))._select(
db.t.a))).select()), db.tt.aa))).count(),
2) 2)
db.t.drop() db.tt.drop()
class TestContains(unittest.TestCase): class TestContains(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'list:string')) db.define_table('tt', Field('aa', 'list:string'), Field('bb','string'))
self.assertEqual(db.t.insert(a=['aaa','bbb']), 1) self.assertEqual(db.tt.insert(aa=['aaa','bbb'],bb='aaa'), 1)
self.assertEqual(db.t.insert(a=['bbb','ddd']), 2) self.assertEqual(db.tt.insert(aa=['bbb','ddd'],bb='abb'), 2)
self.assertEqual(db.t.insert(a=['eee','aaa']), 3) self.assertEqual(db.tt.insert(aa=['eee','aaa'],bb='acc'), 3)
self.assertEqual(len(db(db.t.a.contains('aaa')).select()), self.assertEqual(db(db.tt.aa.contains('aaa')).count(), 2)
2) self.assertEqual(db(db.tt.aa.contains('bbb')).count(), 2)
self.assertEqual(len(db(db.t.a.contains('bbb')).select()), self.assertEqual(db(db.tt.aa.contains('aa')).count(), 0)
2) self.assertEqual(db(db.tt.bb.contains('a')).count(), 3)
self.assertEqual(len(db(db.t.a.contains('aa')).select()), self.assertEqual(db(db.tt.bb.contains('b')).count(), 1)
0) self.assertEqual(db(db.tt.bb.contains('d')).count(), 0)
db.t.drop() self.assertEqual(db(db.tt.aa.contains(db.tt.bb)).count(), 1)
db.tt.drop()
class TestLike(unittest.TestCase): class TestLike(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
self.assertEqual(db.t.insert(a='abc'), 1) self.assertEqual(db.tt.insert(aa='abc'), 1)
self.assertEqual(len(db(db.t.a.like('a%')).select()), 1) self.assertEqual(db(db.tt.aa.like('a%')).count(), 1)
self.assertEqual(len(db(db.t.a.like('%b%')).select()), 1) self.assertEqual(db(db.tt.aa.like('%b%')).count(), 1)
self.assertEqual(len(db(db.t.a.like('%c')).select()), 1) self.assertEqual(db(db.tt.aa.like('%c')).count(), 1)
self.assertEqual(len(db(db.t.a.like('%d%')).select()), 0) self.assertEqual(db(db.tt.aa.like('%d%')).count(), 0)
self.assertEqual(len(db(db.t.a.lower().like('A%')).select()), 1) self.assertEqual(db(db.tt.aa.lower().like('A%')).count(), 1)
self.assertEqual(len(db(db.t.a.lower().like('%B%')).select()), self.assertEqual(db(db.tt.aa.lower().like('%B%')).count(),
1) 1)
self.assertEqual(len(db(db.t.a.lower().like('%C')).select()), 1) self.assertEqual(db(db.tt.aa.lower().like('%C')).count(), 1)
self.assertEqual(len(db(db.t.a.upper().like('A%')).select()), 1) self.assertEqual(db(db.tt.aa.upper().like('A%')).count(), 1)
self.assertEqual(len(db(db.t.a.upper().like('%B%')).select()), self.assertEqual(db(db.tt.aa.upper().like('%B%')).count(),
1) 1)
self.assertEqual(len(db(db.t.a.upper().like('%C')).select()), 1) self.assertEqual(db(db.tt.aa.upper().like('%C')).count(), 1)
db.t.drop() db.tt.drop()
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1111111111), 1) self.assertEqual(db.tt.insert(aa=1111111111), 1)
self.assertEqual(len(db(db.t.a.like('1%')).select()), 1) self.assertEqual(db(db.tt.aa.like('1%')).count(), 1)
self.assertEqual(len(db(db.t.a.like('2%')).select()), 0) self.assertEqual(db(db.tt.aa.like('2%')).count(), 0)
db.t.drop() db.tt.drop()
class TestDatetime(unittest.TestCase): class TestDatetime(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'datetime')) db.define_table('tt', Field('aa', 'datetime'))
self.assertEqual(db.t.insert(a=datetime.datetime(1971, 12, 21, self.assertEqual(db.tt.insert(aa=datetime.datetime(1971, 12, 21,
11, 30)), 1) 11, 30)), 1)
self.assertEqual(db.t.insert(a=datetime.datetime(1971, 11, 21, self.assertEqual(db.tt.insert(aa=datetime.datetime(1971, 11, 21,
10, 30)), 2) 10, 30)), 2)
self.assertEqual(db.t.insert(a=datetime.datetime(1970, 12, 21, self.assertEqual(db.tt.insert(aa=datetime.datetime(1970, 12, 21,
9, 30)), 3) 9, 30)), 3)
self.assertEqual(len(db(db.t.a == datetime.datetime(1971, 12, self.assertEqual(db(db.tt.aa == datetime.datetime(1971, 12,
21, 11, 30)).select()), 1) 21, 11, 30)).count(), 1)
self.assertEqual(db(db.t.a.year() == 1971).count(), 2) self.assertEqual(db(db.tt.aa.year() == 1971).count(), 2)
self.assertEqual(db(db.t.a.month() == 12).count(), 2) self.assertEqual(db(db.tt.aa.month() == 12).count(), 2)
self.assertEqual(db(db.t.a.day() == 21).count(), 3) self.assertEqual(db(db.tt.aa.day() == 21).count(), 3)
self.assertEqual(db(db.t.a.hour() == 11).count(), 1) self.assertEqual(db(db.tt.aa.hour() == 11).count(), 1)
self.assertEqual(db(db.t.a.minutes() == 30).count(), 3) self.assertEqual(db(db.tt.aa.minutes() == 30).count(), 3)
self.assertEqual(db(db.t.a.seconds() == 0).count(), 3) self.assertEqual(db(db.tt.aa.seconds() == 0).count(), 3)
self.assertEqual(db(db.t.a.epoch()<365*24*3600).count(),1) self.assertEqual(db(db.tt.aa.epoch()<365*24*3600).count(),1)
db.t.drop() db.tt.drop()
class TestExpressions(unittest.TestCase): class TestExpressions(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1), 1) self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.t.insert(a=2), 2) self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.t.insert(a=3), 3) self.assertEqual(db.tt.insert(aa=3), 3)
self.assertEqual(db(db.t.a == 3).update(a=db.t.a + 1), 1) self.assertEqual(db(db.tt.aa == 3).update(aa=db.tt.aa + 1), 1)
self.assertEqual(db(db.t.a == 4).count(), 1) self.assertEqual(db(db.tt.aa == 4).count(), 1)
db.t.drop() db.tt.drop()
class TestJoin(unittest.TestCase): class TestJoin(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t1', Field('a')) db.define_table('t1', Field('aa'))
db.define_table('t2', Field('a'), Field('b', db.t1)) db.define_table('t2', Field('aa'), Field('b', db.t1))
i1 = db.t1.insert(a='1') i1 = db.t1.insert(aa='1')
i2 = db.t1.insert(a='2') i2 = db.t1.insert(aa='2')
i3 = db.t1.insert(a='3') i3 = db.t1.insert(aa='3')
db.t2.insert(a='4', b=i1) db.t2.insert(aa='4', b=i1)
db.t2.insert(a='5', b=i2) db.t2.insert(aa='5', b=i2)
db.t2.insert(a='6', b=i2) db.t2.insert(aa='6', b=i2)
self.assertEqual(len(db(db.t1.id self.assertEqual(len(db(db.t1.id
== db.t2.b).select(orderby=db.t1.a == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)), 3) | db.t2.aa)), 3)
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)[2].t1.a, '2') | db.t2.aa)[2].t1.aa, '2')
self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.a self.assertEqual(db(db.t1.id == db.t2.b).select(orderby=db.t1.aa
| db.t2.a)[2].t2.a, '6') | db.t2.aa)[2].t2.aa, '6')
self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(len(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)), 4) orderby=db.t1.aa | db.t2.aa)), 4)
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[2].t1.a, '2') orderby=db.t1.aa | db.t2.aa)[2].t1.aa, '2')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[2].t2.a, '6') orderby=db.t1.aa | db.t2.aa)[2].t2.aa, '6')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[3].t1.a, '3') orderby=db.t1.aa | db.t2.aa)[3].t1.aa, '3')
self.assertEqual(db().select(db.t1.ALL, db.t2.ALL, self.assertEqual(db().select(db.t1.ALL, db.t2.ALL,
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a)[3].t2.a, None) orderby=db.t1.aa | db.t2.aa)[3].t2.aa, None)
self.assertEqual(len(db().select(db.t1.ALL, db.t2.id.count(), self.assertEqual(len(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, groupby=db.t1.a)), orderby=db.t1.aa, groupby=db.t1.aa)),
3) 3)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(), self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[0]._extra[db.t2.id.count()], groupby=db.t1.aa)[0]._extra[db.t2.id.count()],
1) 1)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(), self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[1]._extra[db.t2.id.count()], groupby=db.t1.aa)[1]._extra[db.t2.id.count()],
2) 2)
self.assertEqual(db().select(db.t1.ALL, db.t2.id.count(), self.assertEqual(db().select(db.t1.aa, db.t2.id.count(),
left=db.t2.on(db.t1.id == db.t2.b), left=db.t2.on(db.t1.id == db.t2.b),
orderby=db.t1.a | db.t2.a, orderby=db.t1.aa,
groupby=db.t1.a)[2]._extra[db.t2.id.count()], groupby=db.t1.aa)[2]._extra[db.t2.id.count()],
0) 0)
db.t1.drop()
db.t2.drop() db.t2.drop()
db.t1.drop()
db.define_table('person',Field('name')) db.define_table('person',Field('name'))
id = db.person.insert(name="max") id = db.person.insert(name="max")
self.assertEqual(id.name,'max') self.assertEqual(id.name,'max')
db.define_table('dog',Field('name'),Field('owner','reference person')) db.define_table('dog',Field('name'),Field('ownerperson','reference person'))
db.dog.insert(name='skipper',owner=1) db.dog.insert(name='skipper',ownerperson=1)
row = db(db.person.id==db.dog.owner).select().first() row = db(db.person.id==db.dog.ownerperson).select().first()
self.assertEqual(row[db.person.name],'max') self.assertEqual(row[db.person.name],'max')
self.assertEqual(row['person.name'],'max') self.assertEqual(row['person.name'],'max')
db.dog.drop() db.dog.drop()
self.assertEqual(len(db.person._referenced_by),0) self.assertEqual(len(db.person._referenced_by),0)
db.person.drop() db.person.drop()
class TestMinMaxSum(unittest.TestCase): class TestMinMaxSumAvg(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a', 'integer')) db.define_table('tt', Field('aa', 'integer'))
self.assertEqual(db.t.insert(a=1), 1) self.assertEqual(db.tt.insert(aa=1), 1)
self.assertEqual(db.t.insert(a=2), 2) self.assertEqual(db.tt.insert(aa=2), 2)
self.assertEqual(db.t.insert(a=3), 3) self.assertEqual(db.tt.insert(aa=3), 3)
s = db.t.a.min() s = db.tt.aa.min()
self.assertEqual(db(db.t.id > 0).select(s)[0]._extra[s], 1) self.assertEqual(db(db.tt.id > 0).select(s)[0]._extra[s], 1)
self.assertEqual(db(db.t.id > 0).select(s).first()[s], 1) self.assertEqual(db(db.tt.id > 0).select(s).first()[s], 1)
self.assertEqual(db().select(s).first()[s], 1) self.assertEqual(db().select(s).first()[s], 1)
s = db.t.a.max() s = db.tt.aa.max()
self.assertEqual(db().select(s).first()[s], 3) self.assertEqual(db().select(s).first()[s], 3)
s = db.t.a.sum() s = db.tt.aa.sum()
self.assertEqual(db().select(s).first()[s], 6) self.assertEqual(db().select(s).first()[s], 6)
s = db.t.a.count() s = db.tt.aa.count()
self.assertEqual(db().select(s).first()[s], 3) self.assertEqual(db().select(s).first()[s], 3)
db.t.drop() s = db.tt.aa.avg()
self.assertEqual(db().select(s).first()[s], 2)
db.tt.drop()
class TestCache(unittest.TestCase): class TestCache(unittest.TestCase):
def testRun(self): def testRun(self):
from cache import CacheInRam from cache import CacheInRam
cache = CacheInRam() cache = CacheInRam()
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.t.insert(a='1') db.tt.insert(aa='1')
r0 = db().select(db.t.ALL) r0 = db().select(db.tt.ALL)
r1 = db().select(db.t.ALL, cache=(cache, 1000)) r1 = db().select(db.tt.ALL, cache=(cache, 1000))
self.assertEqual(len(r0),len(r1)) self.assertEqual(len(r0),len(r1))
r2 = db().select(db.t.ALL, cache=(cache, 1000)) r2 = db().select(db.tt.ALL, cache=(cache, 1000))
self.assertEqual(len(r0),len(r2)) self.assertEqual(len(r0),len(r2))
r3 = db().select(db.t.ALL, cache=(cache, 1000), cacheable=True) r3 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
self.assertEqual(len(r0),len(r3)) self.assertEqual(len(r0),len(r3))
r4 = db().select(db.t.ALL, cache=(cache, 1000), cacheable=True) r4 = db().select(db.tt.ALL, cache=(cache, 1000), cacheable=True)
self.assertEqual(len(r0),len(r4)) self.assertEqual(len(r0),len(r4))
db.t.drop() db.tt.drop()
class TestMigrations(unittest.TestCase): class TestMigrations(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), migrate='.storage.table') db.define_table('tt', Field('aa'), migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), Field('b'), db.define_table('tt', Field('aa'), Field('b'),
migrate='.storage.table') migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), Field('b', 'text'), db.define_table('tt', Field('aa'), Field('b', 'text'),
migrate='.storage.table') migrate='.storage.table')
db.commit() db.commit()
db.close() db.close()
db = DAL('sqlite://.storage.db') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a'), migrate='.storage.table') db.define_table('tt', Field('aa'), migrate='.storage.table')
db.t.drop() db.tt.drop()
db.commit() db.commit()
db.close() db.close()
@@ -500,81 +512,107 @@ class TestMigrations(unittest.TestCase):
if os.path.exists('.storage.table'): if os.path.exists('.storage.table'):
os.unlink('.storage.table') os.unlink('.storage.table')
class TestReferece(unittest.TestCase): class TestReference(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('name'), Field('a','reference t')) db.define_table('tt', Field('name'), Field('aa','reference tt'))
db.commit() db.commit()
x = db.t.insert(name='max') x = db.tt.insert(name='max')
assert x.id == 1 assert x.id == 1
assert x['id'] == 1 assert x['id'] == 1
x.a = x x.aa = x
assert x.a == 1 assert x.aa == 1
x.update_record() x.update_record()
y = db.t[1] y = db.tt[1]
assert y.a == 1 assert y.aa == 1
assert y.a.a.a.a.a.a.name == 'max' assert y.aa.aa.aa.aa.aa.aa.name == 'max'
z=db.t.insert(name='xxx', a = y) z=db.tt.insert(name='xxx', aa = y)
assert z.a == y.id assert z.aa == y.id
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestClientLevelOps(unittest.TestCase): class TestClientLevelOps(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.commit() db.commit()
db.t.insert(a="test") db.tt.insert(aa="test")
rows1 = db(db.t.id>0).select() rows1 = db(db.tt.id>0).select()
rows2 = db(db.t.id>0).select() rows2 = db(db.tt.id>0).select()
rows3 = rows1 & rows2 rows3 = rows1 & rows2
assert len(rows3) == 2 assert len(rows3) == 2
rows4 = rows1 | rows2 rows4 = rows1 | rows2
assert len(rows4) == 1 assert len(rows4) == 1
rows5 = rows1.find(lambda row: row.a=="test") rows5 = rows1.find(lambda row: row.aa=="test")
assert len(rows5) == 1 assert len(rows5) == 1
rows6 = rows2.exclude(lambda row: row.a=="test") rows6 = rows2.exclude(lambda row: row.aa=="test")
assert len(rows6) == 1 assert len(rows6) == 1
rows7 = rows5.sort(lambda row: row.a) rows7 = rows5.sort(lambda row: row.aa)
assert len(rows7) == 1 assert len(rows7) == 1
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestVirtualFields(unittest.TestCase): class TestVirtualFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', Field('a')) db.define_table('tt', Field('aa'))
db.commit() db.commit()
db.t.insert(a="test") db.tt.insert(aa="test")
class Compute: class Compute:
def a_upper(row): return row.t.a.upper() def a_upper(row): return row.tt.aa.upper()
db.t.virtualfields.append(Compute()) db.tt.virtualfields.append(Compute())
assert db(db.t.id>0).select().first().a_upper == 'TEST' assert db(db.tt.id>0).select().first().a_upper == 'TEST'
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestComputedFields(unittest.TestCase): class TestComputedFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t', db.define_table('tt',
Field('a'), Field('aa'),
Field('b',default='x'), Field('bb',default='x'),
Field('c',compute=lambda r: r.a+r.b)) Field('cc',compute=lambda r: r.aa+r.bb))
db.commit() db.commit()
id = db.t.insert(a="z") id = db.tt.insert(aa="z")
self.assertEqual(db.t[id].c,'zx') self.assertEqual(db.tt[id].cc,'zx')
db.t.drop() db.tt.drop()
db.commit() db.commit()
class TestCommonFilters(unittest.TestCase):
def testRun(self):
db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('t1', Field('aa'))
db.define_table('t2', Field('aa'), Field('b', db.t1))
i1 = db.t1.insert(aa='1')
i2 = db.t1.insert(aa='2')
i3 = db.t1.insert(aa='3')
db.t2.insert(aa='4', b=i1)
db.t2.insert(aa='5', b=i2)
db.t2.insert(aa='6', b=i2)
db.t1._common_filter = lambda q: db.t1.aa>1
self.assertEqual(db(db.t1).count(),2)
self.assertEqual(db(db.t1).count(),2)
q = db.t2.b==db.t1.id
self.assertEqual(db(q).count(),2)
self.assertEqual(db(q).count(),2)
self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),3)
db.t2._common_filter = lambda q: db.t2.aa<6
self.assertEqual(db(q).count(),1)
self.assertEqual(db(q).count(),1)
self.assertEqual(len(db(db.t1).select(left=db.t2.on(q))),2)
db.t2.drop()
db.t1.drop()
class TestImportExportFields(unittest.TestCase): class TestImportExportFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name')) db.define_table('person', Field('name'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2): for n in range(2):
@@ -598,7 +636,7 @@ class TestImportExportFields(unittest.TestCase):
class TestImportExportUuidFields(unittest.TestCase): class TestImportExportUuidFields(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name'),Field('uuid')) db.define_table('person', Field('name'),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
for n in range(2): for n in range(2):
@@ -622,7 +660,7 @@ class TestImportExportUuidFields(unittest.TestCase):
class TestDALDictImportExport(unittest.TestCase): class TestDALDictImportExport(unittest.TestCase):
def testRun(self): def testRun(self):
db = DAL('sqlite:memory:') db = DAL(DEFAULT_URI, check_reserved=['all'])
db.define_table('person', Field('name', default="Michael"),Field('uuid')) db.define_table('person', Field('name', default="Michael"),Field('uuid'))
db.define_table('pet',Field('friend',db.person),Field('name')) db.define_table('pet',Field('friend',db.person),Field('name'))
dbdict = db.as_dict(flat=True, sanitize=False) dbdict = db.as_dict(flat=True, sanitize=False)
@@ -635,77 +673,88 @@ class TestDALDictImportExport(unittest.TestCase):
assert dbdict["items"]["person"]["items"]["name"]["default"] == db.person.name.default assert dbdict["items"]["person"]["items"]["name"]["default"] == db.person.name.default
assert dbdict assert dbdict
db2 = DAL(dbdict) db2 = DAL(dbdict, check_reserved=['all'])
assert len(db.tables) == len(db2.tables) assert len(db.tables) == len(db2.tables)
assert hasattr(db2, "pet") and isinstance(db2.pet, Table) assert hasattr(db2, "pet") and isinstance(db2.pet, Table)
assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field) assert hasattr(db2.pet, "friend") and isinstance(db2.pet.friend, Field)
db.pet.drop()
db.commit()
db2.commit()
have_serializers = True have_serializers = True
try: try:
import serializers import serializers
dbjson = db.as_json(sanitize=False) dbjson = db.as_json(sanitize=False)
assert isinstance(dbjson, basestring) and len(dbjson) > 0 assert isinstance(dbjson, basestring) and len(dbjson) > 0
db3 = DAL(serializers.loads_json(dbjson))
unicode_keys = True
if sys.version < "2.6.5":
unicode_keys = False
db3 = DAL(serializers.loads_json(dbjson,
unicode_keys=unicode_keys))
assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\ assert hasattr(db3, "person") and hasattr(db3.person, "uuid") and\
db3.person.uuid.type == db.person.uuid.type db3.person.uuid.type == db.person.uuid.type
db3.pet.drop()
db3.person.drop() db3.person.drop()
db3.commit() db3.commit()
except ImportError: except ImportError:
pass pass
mpfc = "Monty Python's Flying Circus" mpfc = "Monty Python's Flying Circus"
dbdict4 = {"uri": 'sqlite:memory:', dbdict4 = {"uri": DEFAULT_URI,
"items":{"staff":{"items": {"name": "items":{"staff":{"items": {"name":
{"default":"Michael"}, {"default":"Michael"},
"food": "food":
{"default":"Spam"}, {"default":"Spam"},
"show": "tvshow":
{"type": "reference show"} {"type": "reference tvshow"}
}}, }},
"show":{"items": {"name": "tvshow":{"items": {"name":
{"default":mpfc}, {"default":mpfc},
"rating": "rating":
{"type":"double"}}}}} {"type":"double"}}}}}
db4 = DAL(dbdict4) db4 = DAL(dbdict4, check_reserved=['all'])
assert "staff" in db4.tables assert "staff" in db4.tables
assert "name" in db4.staff assert "name" in db4.staff
assert db4.show.rating.type == "double" assert db4.tvshow.rating.type == "double"
assert (db4.show.insert(), db4.show.insert(name="Loriot"), assert (db4.tvshow.insert(), db4.tvshow.insert(name="Loriot"),
db4.show.insert(name="Il Mattatore")) == (1, 2, 3) db4.tvshow.insert(name="Il Mattatore")) == (1, 2, 3)
assert db4(db4.show).select().first().id == 1 assert db4(db4.tvshow).select().first().id == 1
assert db4(db4.show).select().first().name == mpfc assert db4(db4.tvshow).select().first().name == mpfc
dbdict5 = {"uri": 'sqlite:memory:'} db4.staff.drop()
db5 = DAL(dbdict5) db4.tvshow.drop()
db4.commit()
dbdict5 = {"uri": DEFAULT_URI}
db5 = DAL(dbdict5, check_reserved=['all'])
assert db5.tables in ([], None) assert db5.tables in ([], None)
assert not (str(db5) in ("", None)) assert not (str(db5) in ("", None))
dbdict6 = {"uri": 'sqlite:memory:', dbdict6 = {"uri": DEFAULT_URI,
"items":{"staff":{}, "items":{"staff":{},
"show":{"items": {"name": {}, "tvshow":{"items": {"name": {},
"rating": "rating":
{"type":"double"}}}}} {"type":"double"}
db6 = DAL(dbdict6) }
}
}
}
db6 = DAL(dbdict6, check_reserved=['all'])
assert len(db6["staff"].fields) == 1 assert len(db6["staff"].fields) == 1
assert "name" in db6["show"].fields assert "name" in db6["tvshow"].fields
assert db6.staff.insert() is not None assert db6.staff.insert() is not None
assert db6(db6.staff).select().first().id == 1 assert db6(db6.staff).select().first().id == 1
db6.staff.drop() db6.staff.drop()
db6.show.drop() db6.tvshow.drop()
db6.commit() db6.commit()
db4.staff.drop()
db4.show.drop()
db4.commit()
db2.pet.drop()
db2.person.drop()
db2.commit()
db.pet.drop()
db.person.drop()
db.commit()
if __name__ == '__main__': if __name__ == '__main__':
+6
View File
@@ -206,6 +206,12 @@ class TestBareHelpers(unittest.TestCase):
self.assertEqual(UL('<>', _a='1', _b='2').xml(), self.assertEqual(UL('<>', _a='1', _b='2').xml(),
'<ul a="1" b="2"><li>&lt;&gt;</li></ul>') '<ul a="1" b="2"><li>&lt;&gt;</li></ul>')
class TestData(unittest.TestCase):
def testAdata(self):
self.assertEqual(A('<>', data=dict(abc='<def?asd>', cde='standard'), _a='1', _b='2').xml(),
'<a a="1" b="2" data-abc="&lt;def?asd&gt;" data-cde="standard">&lt;&gt;</a>')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+52 -6
View File
@@ -11,11 +11,58 @@ else:
sys.path.append(os.path.realpath('../')) sys.path.append(os.path.realpath('../'))
import unittest import unittest
import subprocess
import time
import signal
from contrib.webclient import WebClient from contrib.webclient import WebClient
webserverprocess = None
class TestWeb(unittest.TestCase): def startwebserver():
def testWebClient(self): global webserverprocess
webserverprocess = subprocess.Popen([sys.executable, 'web2py.py', '-a', 'testpass'])
print 'Sleeping before web2py starts...'
for a in range(1,11):
time.sleep(1)
print a, '...'
print ''
def terminate_process(pid):
#Taken from http://stackoverflow.com/questions/1064335/in-python-2-5-how-do-i-kill-a-subprocess
# all this shit is because we are stuck with Python 2.5 and \
#we cannot use Popen.terminate()
if sys.platform.startswith('win'):
import ctypes
PROCESS_TERMINATE = 1
handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.CloseHandle(handle)
else:
os.kill(pid, signal.SIGKILL)
def stopwebserver():
global webserverprocess
print 'Killing webserver'
if sys.version_info < (2,6):
terminate_process(webserverprocess.pid)
else:
webserverprocess.terminate()
class LiveTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
startwebserver()
@classmethod
def tearDownClass(cls):
stopwebserver()
class TestWeb(LiveTest):
def testRegisterAndLogin(self):
client = WebClient('http://127.0.0.1:8000/welcome/default/') client = WebClient('http://127.0.0.1:8000/welcome/default/')
client.get('index') client.get('index')
@@ -40,7 +87,7 @@ class TestWeb(unittest.TestCase):
# check registration and login were successful # check registration and login were successful
client.get('index') client.get('index')
print client.text
self.assertTrue('Welcome Homer' in client.text) self.assertTrue('Welcome Homer' in client.text)
client = WebClient('http://127.0.0.1:8000/admin/default/') client = WebClient('http://127.0.0.1:8000/admin/default/')
@@ -48,9 +95,7 @@ class TestWeb(unittest.TestCase):
client.get('site') client.get('site')
client.get('design/welcome') client.get('design/welcome')
def testStaticCache(self):
class TestStaticCacheControl(unittest.TestCase):
def testWebClient(self):
s = WebClient('http://127.0.0.1:8000/welcome/') s = WebClient('http://127.0.0.1:8000/welcome/')
s.get('static/js/web2py.js') s.get('static/js/web2py.js')
assert('expires' not in s.headers) assert('expires' not in s.headers)
@@ -61,5 +106,6 @@ class TestStaticCacheControl(unittest.TestCase):
assert('expires' in s.headers) assert('expires' in s.headers)
assert(s.headers['cache-control'].startswith('max-age')) assert(s.headers['cache-control'].startswith('max-age'))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+108 -49
View File
@@ -266,8 +266,8 @@ class Mail(object):
def send( def send(
self, self,
to, to,
subject='None', subject = '[no subject]',
message='None', message = '[no message]',
attachments=None, attachments=None,
cc=None, cc=None,
bcc=None, bcc=None,
@@ -374,10 +374,14 @@ class Mail(object):
payload_in = MIMEMultipart.MIMEMultipart('mixed') payload_in = MIMEMultipart.MIMEMultipart('mixed')
else: else:
# no encoding configuration for raw messages # no encoding configuration for raw messages
if isinstance(message, basestring): if not isinstance(message, basestring):
message = message.read()
if isinstance(message, unicode):
text = message.encode('utf-8')
elif not encoding=='utf-8':
text = message.decode(encoding).encode('utf-8') text = message.decode(encoding).encode('utf-8')
else: else:
text = message.read().decode(encoding).encode('utf-8') text = message
# No charset passed to avoid transport encoding # No charset passed to avoid transport encoding
# NOTE: some unicode encoded strings will produce # NOTE: some unicode encoded strings will produce
# unreadable mail contents. # unreadable mail contents.
@@ -397,7 +401,8 @@ class Mail(object):
text = html = None text = html = None
elif isinstance(message, (list, tuple)): elif isinstance(message, (list, tuple)):
text, html = message text, html = message
elif message.strip().startswith('<html') and message.strip().endswith('</html>'): elif message.strip().startswith('<html') and \
message.strip().endswith('</html>'):
text = self.settings.server == 'gae' and message or None text = self.settings.server == 'gae' and message or None
html = message html = message
else: else:
@@ -4325,7 +4330,7 @@ class Service(object):
# jsonrpc 2.0 error types. records the following structure {code: (message,meaning)} # jsonrpc 2.0 error types. records the following structure {code: (message,meaning)}
jsonrpc_errors = { jsonrpc_errors = {
-32700: ("Parse error. Invalid JSON was received by the server.", "An error occurred on the server while parsing the JSON text."), -32700: ("Parse error. Invalid JSON was received by the server.", "An error occurred on the server while parsing the JSON text."),
-32600: ("Invalid Request", "The JSON sent is not a valid Request object."), -32600: ("Invalid Request", "The JSON sent is not a valid Request object."),
-32601: ("Method not found", "The method does not exist / is not available."), -32601: ("Method not found", "The method does not exist / is not available."),
-32602: ("Invalid params", "Invalid method parameter(s)."), -32602: ("Invalid params", "Invalid method parameter(s)."),
@@ -4475,6 +4480,8 @@ class Service(object):
return return_response(id, s) return return_response(id, s)
else: else:
return '' return ''
except HTTP, e:
raise e
except Service.JsonRpcException, e: except Service.JsonRpcException, e:
return return_error(id, e.code, e.info) return return_error(id, e.code, e.info)
except BaseException: except BaseException:
@@ -4826,14 +4833,27 @@ class PluginManager(object):
class Expose(object): class Expose(object):
def __init__(self, base=None, basename='base', extensions=None, allow_download=True): def __init__(self, base=None, basename=None, extensions=None, allow_download=True):
""" """
extensions: an optional list of file extensions for filtering displayed files: Usage:
def static():
return dict(files=Expose())
or
def static():
path = os.path.join(request.folder,'static','public')
return dict(files=Expose(path,basename='public'))
extensions:
an optional list of file extensions for filtering displayed files:
['.py', '.jpg'] ['.py', '.jpg']
allow_download: whether to allow downloading selected files allow_download: whether to allow downloading selected files
""" """
current.session.forget() current.session.forget()
base = base or os.path.join(current.request.folder, 'static') base = base or os.path.join(current.request.folder, 'static')
basename = basename or current.request.function
self.basename = basename self.basename = basename
self.args = current.request.raw_args and \ self.args = current.request.raw_args and \
[arg for arg in current.request.raw_args.split('/') if arg] or [] [arg for arg in current.request.raw_args.split('/') if arg] or []
@@ -4850,8 +4870,14 @@ class Expose(object):
if os.path.isdir(f) and not self.isprivate(f)] if os.path.isdir(f) and not self.isprivate(f)]
self.filenames = [f[len(path) - 1:] for f in sorted(glob.glob(path)) self.filenames = [f[len(path) - 1:] for f in sorted(glob.glob(path))
if not os.path.isdir(f) and not self.isprivate(f)] if not os.path.isdir(f) and not self.isprivate(f)]
if 'README' in self.filenames:
readme = open(os.path.join(filename,'README')).read()
self.paragraph = MARKMIN(readme)
else:
self.paragraph = None
if extensions: if extensions:
self.filenames = [f for f in self.filenames if os.path.splitext(f)[-1] in extensions] self.filenames = [f for f in self.filenames
if os.path.splitext(f)[-1] in extensions]
def breadcrumbs(self, basename): def breadcrumbs(self, basename):
path = [] path = []
@@ -4865,8 +4891,10 @@ class Expose(object):
def table_folders(self): def table_folders(self):
if self.folders: if self.folders:
return SPAN(H3('Folders'), TABLE(*[TR(TD(A(folder, _href=URL(args=self.args + [folder])))) return SPAN(H3('Folders'), TABLE(
for folder in self.folders])) *[TR(TD(A(folder, _href=URL(args=self.args + [folder]))))
for folder in self.folders],
**dict(_class="table")))
return '' return ''
@staticmethod @staticmethod
@@ -4875,20 +4903,24 @@ class Expose(object):
@staticmethod @staticmethod
def isimage(f): def isimage(f):
return os.path.splitext(f)[-1].lower() in ('.png', '.jpg', '.jpeg', '.gif', '.tiff') return os.path.splitext(f)[-1].lower() in (
'.png', '.jpg', '.jpeg', '.gif', '.tiff')
def table_files(self, width=160): def table_files(self, width=160):
if self.filenames: if self.filenames:
return SPAN(H3('Files'), TABLE(*[TR(TD(A(f, _href=URL(args=self.args + [f]))), return SPAN(H3('Files'),
TD(IMG(_src=URL(args=self.args + [f]), TABLE(*[TR(TD(A(f, _href=URL(args=self.args + [f]))),
_style='max-width:%spx' % width) TD(IMG(_src=URL(args=self.args + [f]),
if width and self.isimage(f) else '')) _style='max-width:%spx' % width)
for f in self.filenames])) if width and self.isimage(f) else ''))
for f in self.filenames],
**dict(_class="table")))
return '' return ''
def xml(self): def xml(self):
return DIV( return DIV(
H2(self.breadcrumbs(self.basename)), H2(self.breadcrumbs(self.basename)),
self.paragraph or '',
self.table_folders(), self.table_folders(),
self.table_files()).xml() self.table_files()).xml()
@@ -4897,7 +4929,7 @@ class Wiki(object):
everybody = 'everybody' everybody = 'everybody'
rows_page = 25 rows_page = 25
def markmin_base(self,body): def markmin_base(self,body):
return MARKMIN(body, extra=self.extra, return MARKMIN(body, extra=self.settings.extra,
url=True, environment=self.env, url=True, environment=self.env,
autolinks=lambda link: expand_one(link, {})).xml() autolinks=lambda link: expand_one(link, {})).xml()
@@ -4931,29 +4963,43 @@ class Wiki(object):
controller, function, args = items[0], items[1], items[2:] controller, function, args = items[0], items[1], items[2:]
return LOAD(controller, function, args=args, ajax=True).xml() return LOAD(controller, function, args=args, ajax=True).xml()
def get_render(self):
if isinstance(self.settings.render, basestring):
r = getattr(self, "%s_render" % self.settings.render)
elif callable(self.settings.render):
r = self.settings.render
else:
raise ValueError("Invalid render type %s" % type(render))
return r
def __init__(self, auth, env=None, render='markmin', def __init__(self, auth, env=None, render='markmin',
manage_permissions=False, force_prefix='', manage_permissions=False, force_prefix='',
restrict_search=False, extra=None, restrict_search=False, extra=None,
menu_groups=None, templates=None): menu_groups=None, templates=None):
settings = self.settings = Settings()
# render: "markmin", "html", ..., <function>
settings.render = render
perms = settings.manage_permissions = manage_permissions
settings.force_prefix = force_prefix
settings.restrict_search = restrict_search
settings.extra = extra or {}
settings.menu_groups = menu_groups
settings.templates = templates
db = auth.db db = auth.db
self.env = env or {} self.env = env or {}
self.env['component'] = Wiki.component self.env['component'] = Wiki.component
if render == 'markmin':
render = self.markmin_render
elif render == 'html':
render = self.html_render
self.render = render
self.auth = auth self.auth = auth
self.menu_groups = menu_groups
if self.auth.user: if self.auth.user:
self.force_prefix = force_prefix % self.auth.user self.settings.force_prefix = force_prefix % self.auth.user
else: else:
self.force_prefix = force_prefix self.settings.force_prefix = force_prefix
self.host = current.request.env.http_host self.host = current.request.env.http_host
perms = self.manage_permissions = manage_permissions
self.restrict_search = restrict_search
self.extra = extra or {}
self.templates = templates
table_definitions = [ table_definitions = [
('wiki_page', { ('wiki_page', {
@@ -4973,7 +5019,8 @@ class Wiki(object):
writable=perms, readable=perms, writable=perms, readable=perms,
default=[Wiki.everybody]), default=[Wiki.everybody]),
Field('changelog'), Field('changelog'),
Field('html', 'text', compute=render, Field('html', 'text',
compute=self.get_render(),
readable=False, writable=False), readable=False, writable=False),
auth.signature], auth.signature],
'vars':{'format':'%(title)s'}}), 'vars':{'format':'%(title)s'}}),
@@ -5006,8 +5053,9 @@ class Wiki(object):
args += value['args'] args += value['args']
db.define_table(key, *args, **value['vars']) db.define_table(key, *args, **value['vars'])
if self.templates is None and not self.manage_permissions: if self.settings.templates is None and not \
self.templates = db.wiki_page.tags.contains('template')&\ self.settings.manage_permissions:
self.settings.templates = db.wiki_page.tags.contains('template')&\
db.wiki_page.can_read.contains('everybody') db.wiki_page.can_read.contains('everybody')
def update_tags_insert(page, id, db=db): def update_tags_insert(page, id, db=db):
@@ -5025,19 +5073,25 @@ class Wiki(object):
db.wiki_tag.insert(name=tag, wiki_page=page.id) db.wiki_tag.insert(name=tag, wiki_page=page.id)
db.wiki_page._after_insert.append(update_tags_insert) db.wiki_page._after_insert.append(update_tags_insert)
db.wiki_page._after_update.append(update_tags_update) db.wiki_page._after_update.append(update_tags_update)
if auth.user and check_credentials(current.request) and \
not 'wiki_editor' in auth.user_groups.values(): if (auth.user and
check_credentials(current.request, gae_login=False) and
not 'wiki_editor' in auth.user_groups.values()):
group = db.auth_group(role='wiki_editor') group = db.auth_group(role='wiki_editor')
gid = group.id if group else db.auth_group.insert( gid = group.id if group else db.auth_group.insert(
role='wiki_editor') role='wiki_editor')
auth.add_membership(gid) auth.add_membership(gid)
settings.lock_keys = True
# WIKI ACCESS POLICY # WIKI ACCESS POLICY
def not_authorized(self, page=None): def not_authorized(self, page=None):
raise HTTP(401) raise HTTP(401)
def can_read(self, page): def can_read(self, page):
if 'everybody' in page.can_read or not self.manage_permissions: if 'everybody' in page.can_read or not \
self.settings.manage_permissions:
return True return True
elif self.auth.user: elif self.auth.user:
groups = self.auth.user_groups.values() groups = self.auth.user_groups.values()
@@ -5067,11 +5121,11 @@ class Wiki(object):
return True return True
def can_see_menu(self): def can_see_menu(self):
if self.menu_groups is None: if self.settings.menu_groups is None:
return True return True
if self.auth.user: if self.auth.user:
groups = self.auth.user_groups.values() groups = self.auth.user_groups.values()
if any(t in self.menu_groups for t in groups): if any(t in self.settings.menu_groups for t in groups):
return True return True
return False return False
@@ -5108,7 +5162,7 @@ class Wiki(object):
elif zero == '_cloud': elif zero == '_cloud':
return self.cloud() return self.cloud()
elif zero == '_preview': elif zero == '_preview':
return self.preview(self.render) return self.preview(self.get_render())
def first_paragraph(self, page): def first_paragraph(self, page):
if not self.can_read(page): if not self.can_read(page):
@@ -5176,9 +5230,9 @@ class Wiki(object):
title_guess = ' '.join(c.capitalize() for c in slug.split('-')) title_guess = ' '.join(c.capitalize() for c in slug.split('-'))
if not page: if not page:
if not (self.can_manage() or if not (self.can_manage() or
slug.startswith(self.force_prefix)): slug.startswith(self.settings.force_prefix)):
current.session.flash = 'slug must have "%s" prefix' \ current.session.flash = 'slug must have "%s" prefix' \
% self.force_prefix % self.settings.force_prefix
redirect(URL(args=('_create'))) redirect(URL(args=('_create')))
db.wiki_page.can_read.default = [Wiki.everybody] db.wiki_page.can_read.default = [Wiki.everybody]
db.wiki_page.can_edit.default = [auth.user_group_role()] db.wiki_page.can_edit.default = [auth.user_group_role()]
@@ -5234,7 +5288,10 @@ class Wiki(object):
} }
}) })
}) })
""" % dict(url=URL(args=('_preview')), urlmedia=URL(extension='load',args=('_editmedia'),vars=dict(embedded=1))) """ % dict(url=URL(args=('_preview', slug)),
urlmedia=URL(extension='load',
args=('_editmedia',slug),
vars=dict(embedded=1)))
return dict(content=TAG[''](form, SCRIPT(script))) return dict(content=TAG[''](form, SCRIPT(script)))
def editmedia(self, slug): def editmedia(self, slug):
@@ -5280,14 +5337,15 @@ class Wiki(object):
options=[OPTION(row.slug,_value=row.id) for row in slugs] options=[OPTION(row.slug,_value=row.id) for row in slugs]
options.insert(0, OPTION('',_value='')) options.insert(0, OPTION('',_value=''))
fields = [Field("slug", default=current.request.args(1) or fields = [Field("slug", default=current.request.args(1) or
self.force_prefix, self.settings.force_prefix,
requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),] requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),]
if self.templates: if self.settings.templates:
fields.append( fields.append(
Field("from_template", "reference wiki_page", Field("from_template", "reference wiki_page",
requires=IS_EMPTY_OR(IS_IN_DB(db(self.templates), requires=IS_EMPTY_OR(
db.wiki_page._id, IS_IN_DB(db(self.settings.templates),
'%(slug)s')), db.wiki_page._id,
'%(slug)s')),
comment=current.T( comment=current.T(
"Choose Template or empty for new Page"))) "Choose Template or empty for new Page")))
form = SQLFORM.factory(*fields, **dict(_class="well span6")) form = SQLFORM.factory(*fields, **dict(_class="well span6"))
@@ -5329,7 +5387,7 @@ class Wiki(object):
request, db = current.request, self.auth.db request, db = current.request, self.auth.db
media = db.wiki_media(id) media = db.wiki_media(id)
if media: if media:
if self.manage_permissions: if self.settings.manage_permissions:
page = db.wiki_page(media.wiki_page) page = db.wiki_page(media.wiki_page)
if not self.can_read(page): if not self.can_read(page):
return self.not_authorized(page) return self.not_authorized(page)
@@ -5428,7 +5486,7 @@ class Wiki(object):
query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\ query = (db.wiki_page.id == db.wiki_tag.wiki_page) &\
(db.wiki_tag.name.belongs(tags)) (db.wiki_tag.name.belongs(tags))
query = query | db.wiki_page.title.contains(request.vars.q) query = query | db.wiki_page.title.contains(request.vars.q)
if self.restrict_search and not self.manage(): if self.settings.restrict_search and not self.manage():
query = query & (db.wiki_page.created_by == self.auth.user_id) query = query & (db.wiki_page.created_by == self.auth.user_id)
pages = db(query).select(count, pages = db(query).select(count,
*fields, **dict(orderby=orderby or ~count, *fields, **dict(orderby=orderby or ~count,
@@ -5489,6 +5547,7 @@ class Wiki(object):
request = current.request request = current.request
return render(request.post_vars) return render(request.post_vars)
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()
+230 -17
View File
@@ -31,8 +31,8 @@ except ImportError:
from gluon.contrib.simplejson.decoder import JSONDecodeError from gluon.contrib.simplejson.decoder import JSONDecodeError
JSONErrors += (JSONDecodeError,) JSONErrors += (JSONDecodeError,)
__all__ = [ __all__ = [
'ANY_OF',
'CLEANUP', 'CLEANUP',
'CRYPT', 'CRYPT',
'IS_ALPHANUMERIC', 'IS_ALPHANUMERIC',
@@ -50,6 +50,7 @@ __all__ = [
'IS_IN_SET', 'IS_IN_SET',
'IS_INT_IN_RANGE', 'IS_INT_IN_RANGE',
'IS_IPV4', 'IS_IPV4',
'IS_IPV6',
'IS_LENGTH', 'IS_LENGTH',
'IS_LIST_OF', 'IS_LIST_OF',
'IS_LOWER', 'IS_LOWER',
@@ -332,7 +333,7 @@ class IS_JSON(Validator):
requires=IS_JSON(error_message="This is not a valid json input") requires=IS_JSON(error_message="This is not a valid json input")
>>> IS_JSON()('{"a": 100}') >>> IS_JSON()('{"a": 100}')
('{"a": 100}', None) ({u'a': 100}, None)
>>> IS_JSON()('spam1234') >>> IS_JSON()('spam1234')
('spam1234', 'invalid json') ('spam1234', 'invalid json')
@@ -651,7 +652,7 @@ class IS_NOT_IN_DB(Validator):
if row and any(str(row[f]) != str(id[f]) for f in id): if row and any(str(row[f]) != str(id[f]) for f in id):
return (value, translate(self.error_message)) return (value, translate(self.error_message))
else: else:
row = subset.select(table._id, limitby=(0, 1)).first() row = subset.select(table._id, field, limitby=(0, 1)).first()
if row and str(row.id) != str(id): if row and str(row.id) != str(id):
return (value, translate(self.error_message)) return (value, translate(self.error_message))
return (value, None) return (value, None)
@@ -2208,6 +2209,17 @@ class IS_TIME(Validator):
pass pass
return (ivalue, translate(self.error_message)) return (ivalue, translate(self.error_message))
# A UTC class.
class UTC(datetime.tzinfo):
"""UTC"""
ZERO = datetime.timedelta(0)
def utcoffset(self, dt):
return UTC.ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return UTC.ZERO
utc = UTC()
class IS_DATE(Validator): class IS_DATE(Validator):
""" """
@@ -2219,22 +2231,32 @@ class IS_DATE(Validator):
""" """
def __init__(self, format='%Y-%m-%d', def __init__(self, format='%Y-%m-%d',
error_message='enter date as %(format)s'): error_message='enter date as %(format)s',
timezone = None):
"""
timezome must be None or a pytz.timezone("America/Chicago") object
"""
self.format = translate(format) self.format = translate(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.timezone = timezone
self.extremes = {} self.extremes = {}
def __call__(self, value): def __call__(self, value):
ovalue = value
if isinstance(value, datetime.date): if isinstance(value, datetime.date):
if self.timezone is not None:
value = value - datetime.timedelta(seconds=self.timezone*3600)
return (value, None) return (value, None)
try: try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \ (y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format)) time.strptime(value, str(self.format))
value = datetime.date(y, m, d) value = datetime.date(y, m, d)
if self.timezone is not None:
value = self.timezone.localize(value).astimezone(utc)
return (value, None) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) self.extremes.update(IS_DATETIME.nice(self.format))
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None: if value is None:
@@ -2247,6 +2269,8 @@ class IS_DATE(Validator):
if year < 1900: if year < 1900:
year = 2000 year = 2000
d = datetime.date(year, value.month, value.day) d = datetime.date(year, value.month, value.day)
if self.timezone is not None:
d = d.replace(tzinfo=utc).astimezone(self.timezone)
return d.strftime(format) return d.strftime(format)
@@ -2279,22 +2303,30 @@ class IS_DATETIME(Validator):
return dict(format=format) return dict(format=format)
def __init__(self, format='%Y-%m-%d %H:%M:%S', def __init__(self, format='%Y-%m-%d %H:%M:%S',
error_message='enter date and time as %(format)s'): error_message='enter date and time as %(format)s',
timezone=None):
"""
timezome must be None or a pytz.timezone("America/Chicago") object
"""
self.format = translate(format) self.format = translate(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.extremes = {} self.extremes = {}
self.timezone = timezone
def __call__(self, value): def __call__(self, value):
ovalue = value
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
return (value, None) return (value, None)
try: try:
(y, m, d, hh, mm, ss, t0, t1, t2) = \ (y, m, d, hh, mm, ss, t0, t1, t2) = \
time.strptime(value, str(self.format)) time.strptime(value, str(self.format))
value = datetime.datetime(y, m, d, hh, mm, ss) value = datetime.datetime(y, m, d, hh, mm, ss)
if self.timezone is not None:
value = self.timezone.localize(value).astimezone(utc)
return (value, None) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) self.extremes.update(IS_DATETIME.nice(self.format))
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None: if value is None:
@@ -2308,6 +2340,8 @@ class IS_DATETIME(Validator):
year = 2000 year = 2000
d = datetime.datetime(year, value.month, value.day, d = datetime.datetime(year, value.month, value.day,
value.hour, value.minute, value.second) value.hour, value.minute, value.second)
if self.timezone is not None:
d = d.replace(tzinfo=utc).astimezone(self.timezone)
return d.strftime(format) return d.strftime(format)
@@ -2323,7 +2357,7 @@ class IS_DATE_IN_RANGE(IS_DATE):
(datetime.date(2008, 3, 3), None) (datetime.date(2008, 3, 3), None)
>>> v('03/03/2010') >>> v('03/03/2010')
(datetime.date(2010, 3, 3), 'oops') ('03/03/2010', 'oops')
>>> v(datetime.date(2008,3,3)) >>> v(datetime.date(2008,3,3))
(datetime.date(2008, 3, 3), None) (datetime.date(2008, 3, 3), None)
@@ -2336,7 +2370,8 @@ class IS_DATE_IN_RANGE(IS_DATE):
minimum=None, minimum=None,
maximum=None, maximum=None,
format='%Y-%m-%d', format='%Y-%m-%d',
error_message=None): error_message=None,
timezone=None):
self.minimum = minimum self.minimum = minimum
self.maximum = maximum self.maximum = maximum
if error_message is None: if error_message is None:
@@ -2348,17 +2383,19 @@ class IS_DATE_IN_RANGE(IS_DATE):
error_message = "enter date in range %(min)s %(max)s" error_message = "enter date in range %(min)s %(max)s"
IS_DATE.__init__(self, IS_DATE.__init__(self,
format=format, format=format,
error_message=error_message) error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum) self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value): def __call__(self, value):
ovalue = value
(value, msg) = IS_DATE.__call__(self, value) (value, msg) = IS_DATE.__call__(self, value)
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: if self.minimum and self.minimum > value:
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
if self.maximum and value > self.maximum: if self.maximum and value > self.maximum:
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
return (value, None) return (value, None)
@@ -2374,7 +2411,7 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
(datetime.datetime(2008, 3, 3, 12, 40), None) (datetime.datetime(2008, 3, 3, 12, 40), None)
>>> v('03/03/2010 10:34') >>> v('03/03/2010 10:34')
(datetime.datetime(2010, 3, 3, 10, 34), 'oops') ('03/03/2010 10:34', 'oops')
>>> v(datetime.datetime(2008,3,3,0,0)) >>> v(datetime.datetime(2008,3,3,0,0))
(datetime.datetime(2008, 3, 3, 0, 0), None) (datetime.datetime(2008, 3, 3, 0, 0), None)
@@ -2386,7 +2423,8 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
minimum=None, minimum=None,
maximum=None, maximum=None,
format='%Y-%m-%d %H:%M:%S', format='%Y-%m-%d %H:%M:%S',
error_message=None): error_message=None,
timezone=None):
self.minimum = minimum self.minimum = minimum
self.maximum = maximum self.maximum = maximum
if error_message is None: if error_message is None:
@@ -2398,17 +2436,19 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
error_message = "enter date and time in range %(min)s %(max)s" error_message = "enter date and time in range %(min)s %(max)s"
IS_DATETIME.__init__(self, IS_DATETIME.__init__(self,
format=format, format=format,
error_message=error_message) error_message=error_message,
timezone=timezone)
self.extremes = dict(min=minimum, max=maximum) self.extremes = dict(min=minimum, max=maximum)
def __call__(self, value): def __call__(self, value):
ovalue = value
(value, msg) = IS_DATETIME.__call__(self, value) (value, msg) = IS_DATETIME.__call__(self, value)
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: if self.minimum and self.minimum > value:
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
if self.maximum and value > self.maximum: if self.maximum and value > self.maximum:
return (value, translate(self.error_message) % self.extremes) return (ovalue, translate(self.error_message) % self.extremes)
return (value, None) return (value, None)
@@ -2553,6 +2593,38 @@ class IS_SLUG(Validator):
return (urlify(value, self.maxlen, self.keep_underscores), None) return (urlify(value, self.maxlen, self.keep_underscores), None)
class ANY_OF(Validator):
"""
test if any of the validators in a list return successfully
>>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('a@b.co')
('a@b.co', None)
>>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('abco')
('abco', None)
>>> ANY_OF([IS_EMAIL(),IS_ALPHANUMERIC()])('@ab.co')
('@ab.co', 'enter only letters, numbers, and underscore')
>>> ANY_OF([IS_ALPHANUMERIC(),IS_EMAIL()])('@ab.co')
('@ab.co', 'enter a valid email address')
"""
def __init__(self, subs):
self.subs = subs
def __call__(self, value):
for validator in self.subs:
value, error = validator(value)
if error == None:
break
return value, error
def formatter(self, value):
# Use the formatter of the first subvalidator
# that validates the value and has a formatter
for validator in self.subs:
if hasattr(validator, 'formatter') and validator(value)[1] != None:
return validator.formatter(value)
class IS_EMPTY_OR(Validator): class IS_EMPTY_OR(Validator):
""" """
dummy class for testing IS_EMPTY_OR dummy class for testing IS_EMPTY_OR
@@ -3349,6 +3421,147 @@ class IS_IPV4(Validator):
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translate(self.error_message))
class IS_IPV6(Validator):
"""
Checks if field's value is an IP version 6 address. First attempts to
use the ipaddress library and falls back to contrib/ipaddr.py from Google
(https://code.google.com/p/ipaddr-py/)
Arguments:
is_private: None (default): indifferent
True (enforce): address must be in fc00::/7 range
False (forbid): address must NOT be in fc00::/7 range
is_link_local: Same as above but uses fe80::/10 range
is_reserved: Same as above but uses IETF reserved range
is_mulicast: Same as above but uses ff00::/8 range
is_routeable: Similar to above but enforces not private, link_local,
reserved or multicast
is_6to4: Same as above but uses 2002::/16 range
is_teredo: Same as above but uses 2001::/32 range
subnets: value must be a member of at least one from list of subnets
Examples:
#Check for valid IPv6 address:
INPUT(_type='text', _name='name', requires=IS_IPV6())
#Check for valid IPv6 address is a link_local address:
INPUT(_type='text', _name='name', requires=IS_IPV6(is_link_local=True))
#Check for valid IPv6 address that is Internet routeable:
INPUT(_type='text', _name='name', requires=IS_IPV6(is_routeable=True))
#Check for valid IPv6 address in specified subnet:
INPUT(_type='text', _name='name', requires=IS_IPV6(subnets=['2001::/32'])
>>> IS_IPV6()('fe80::126c:8ffa:fe22:b3af')
('fe80::126c:8ffa:fe22:b3af', None)
>>> IS_IPV6()('192.168.1.1')
('192.168.1.1', 'enter valid IPv6 address')
>>> IS_IPV6(error_message='bad ip')('192.168.1.1')
('192.168.1.1', 'bad ip')
>>> IS_IPV6(is_link_local=True)('fe80::126c:8ffa:fe22:b3af')
('fe80::126c:8ffa:fe22:b3af', None)
>>> IS_IPV6(is_link_local=False)('fe80::126c:8ffa:fe22:b3af')
('fe80::126c:8ffa:fe22:b3af', 'enter valid IPv6 address')
>>> IS_IPV6(is_link_local=True)('2001::126c:8ffa:fe22:b3af')
('2001::126c:8ffa:fe22:b3af', 'enter valid IPv6 address')
>>> IS_IPV6(is_multicast=True)('2001::126c:8ffa:fe22:b3af')
('2001::126c:8ffa:fe22:b3af', 'enter valid IPv6 address')
>>> IS_IPV6(is_multicast=True)('ff00::126c:8ffa:fe22:b3af')
('ff00::126c:8ffa:fe22:b3af', None)
>>> IS_IPV6(is_routeable=True)('2001::126c:8ffa:fe22:b3af')
('2001::126c:8ffa:fe22:b3af', None)
>>> IS_IPV6(is_routeable=True)('ff00::126c:8ffa:fe22:b3af')
('ff00::126c:8ffa:fe22:b3af', 'enter valid IPv6 address')
>>> IS_IPV6(subnets='2001::/32')('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', None)
>>> IS_IPV6(subnets='fb00::/8')('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', 'enter valid IPv6 address')
>>> IS_IPV6(subnets=['fc00::/8','2001::/32'])('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', None)
>>> IS_IPV6(subnets='invalidsubnet')('2001::8ffa:fe22:b3af')
('2001::8ffa:fe22:b3af', 'invalid subnet provided')
"""
def __init__(
self,
is_private=None,
is_link_local=None,
is_reserved=None,
is_multicast=None,
is_routeable=None,
is_6to4=None,
is_teredo=None,
subnets=None,
error_message='enter valid IPv6 address'):
self.is_private = is_private
self.is_link_local = is_link_local
self.is_reserved = is_reserved
self.is_multicast = is_multicast
self.is_routeable = is_routeable
self.is_6to4 = is_6to4
self.is_teredo = is_teredo
self.subnets = subnets
self.error_message = error_message
def __call__(self, value):
try:
import ipaddress
except ImportError:
from contrib import ipaddr as ipaddress
try:
ip = ipaddress.IPv6Address(value)
ok = True
except ipaddress.AddressValueError:
return (value, translate(self.error_message))
if self.subnets:
# iterate through self.subnets to see if value is a member
ok = False
if isinstance(self.subnets, str):
self.subnets = [self.subnets]
for network in self.subnets:
try:
ipnet = ipaddress.IPv6Network(network)
except (ipaddress.NetmaskValueError, ipaddress.AddressValueError):
return (value, translate('invalid subnet provided'))
if ip in ipnet:
ok = True
if self.is_routeable:
self.is_private = False
self.is_link_local = False
self.is_reserved = False
self.is_multicast = False
if not (self.is_private is None or self.is_private ==
ip.is_private):
ok = False
if not (self.is_link_local is None or self.is_link_local ==
ip.is_link_local):
ok = False
if not (self.is_reserved is None or self.is_reserved ==
ip.is_reserved):
ok = False
if not (self.is_multicast is None or self.is_multicast ==
ip.is_multicast):
ok = False
if not (self.is_6to4 is None or self.is_6to4 ==
ip.is_6to4):
ok = False
if not (self.is_teredo is None or self.is_teredo ==
ip.is_teredo):
ok = False
if ok:
return (value, None)
return (value, translate(self.error_message))
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod( doctest.testmod(
+1 -1
View File
@@ -793,7 +793,7 @@ def console():
default=None, default=None,
help=msg) help=msg)
msg = 'run schedulers alongside webserver' msg = 'run schedulers alongside webserver, needs -K app1 and -a too'
parser.add_option('-X', parser.add_option('-X',
'--with-scheduler', '--with-scheduler',
action='store_true', action='store_true',
+2 -2
View File
@@ -19,8 +19,8 @@ password = '<recycle>' # ## <recycle> means use the previous password
pid_filename = 'httpserver.pid' pid_filename = 'httpserver.pid'
log_filename = 'httpserver.log' log_filename = 'httpserver.log'
profiler_filename = None profiler_filename = None
ssl_certificate = None # 'ssl_certificate.pem' # ## path to certificate file ssl_certificate = '' # 'ssl_certificate.pem' # ## path to certificate file
ssl_private_key = None # 'ssl_private_key.pem' # ## path to private key file ssl_private_key = '' # 'ssl_private_key.pem' # ## path to private key file
#numthreads = 50 # ## deprecated; remove #numthreads = 50 # ## deprecated; remove
minthreads = None minthreads = None
maxthreads = None maxthreads = None
+4 -5
View File
@@ -46,9 +46,9 @@ def allow_access(environ,host):
filename = os.path.join(os.path.dirname(__file__),'access.wsgi.log') filename = os.path.join(os.path.dirname(__file__),'access.wsgi.log')
f = open(filename,'a') f = open(filename,'a')
try: try:
f.write('\n'+header+'\n'+pprint+'\n') f.write('\n'+header+'\n'+pprint+'\n')
finally: finally:
f.close() f.close()
app = environ['REQUEST_URI'].split('/')[1] app = environ['REQUEST_URI'].split('/')[1]
keys = [key for key in environ if key.startswith('HTTP_')] keys = [key for key in environ if key.startswith('HTTP_')]
headers = {} headers = {}
@@ -58,8 +58,7 @@ def allow_access(environ,host):
try: try:
data = urllib.urlencode({'request_uri':environ['REQUEST_URI']}) data = urllib.urlencode({'request_uri':environ['REQUEST_URI']})
request = urllib2.Request(URL_CHECK_ACCESS % dict(app=app),data,headers) request = urllib2.Request(URL_CHECK_ACCESS % dict(app=app),data,headers)
response = urllib2.urlopen(request).read().strip().lower() response = urllib2.urlopen(request).read().strip().lower()
if response.startswith('true'): return True if response.startswith('true'): return True
except: pass except: pass
return False return False
+80 -80
View File
@@ -4,112 +4,112 @@ worker_processes 1;
error_log /var/log/nginx/error_log info; error_log /var/log/nginx/error_log info;
events { events {
worker_connections 1024; worker_connections 1024;
use epoll; use epoll;
} }
http { http {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
log_format main log_format main
'$remote_addr - $remote_user [$time_local] ' '$remote_addr - $remote_user [$time_local] '
'"$request" $status $bytes_sent ' '"$request" $status $bytes_sent '
'"$http_referer" "$http_user_agent" ' '"$http_referer" "$http_user_agent" '
'"$gzip_ratio"'; '"$gzip_ratio"';
client_header_timeout 10m; client_header_timeout 10m;
client_body_timeout 10m; client_body_timeout 10m;
send_timeout 10m; send_timeout 10m;
connection_pool_size 256; connection_pool_size 256;
client_header_buffer_size 1k; client_header_buffer_size 1k;
large_client_header_buffers 4 2k; large_client_header_buffers 4 2k;
request_pool_size 4k; request_pool_size 4k;
gzip on; gzip on;
gzip_min_length 1100; gzip_min_length 1100;
gzip_buffers 4 8k; gzip_buffers 4 8k;
gzip_types text/plain; gzip_types text/plain;
output_buffers 1 32k; output_buffers 1 32k;
postpone_output 1460; postpone_output 1460;
sendfile on; sendfile on;
tcp_nopush on; tcp_nopush on;
tcp_nodelay on; tcp_nodelay on;
keepalive_timeout 75 20; keepalive_timeout 75 20;
ignore_invalid_headers on; ignore_invalid_headers on;
ssl_session_cache shared:SSL:10m; ssl_session_cache shared:SSL:10m;
index index.html; index index.html;
server { server {
listen 127.0.0.1; listen 127.0.0.1;
server_name localhost; server_name localhost;
access_log /var/log/nginx/localhost.access_log main; access_log /var/log/nginx/localhost.access_log main;
error_log /var/log/nginx/localhost.error_log info; error_log /var/log/nginx/localhost.error_log info;
root /var/www/localhost/htdocs; root /var/www/localhost/htdocs;
} }
# SSL example # SSL example
server { server {
listen 127.0.0.1:443; listen 127.0.0.1:443;
server_name localhost; server_name localhost;
ssl on; ssl on;
ssl_certificate /etc/ssl/nginx/nginx-server.pem; ssl_certificate /etc/ssl/nginx/nginx-server.pem;
ssl_client_certificate /etc/ssl/nginx/cacert.pem; ssl_client_certificate /etc/ssl/nginx/cacert.pem;
ssl_certificate_key /etc/ssl/nginx/nginx.key; ssl_certificate_key /etc/ssl/nginx/nginx.key;
ssl_verify_client optional; ssl_verify_client optional;
access_log /var/log/nginx/localhost.ssl_access_log main; access_log /var/log/nginx/localhost.ssl_access_log main;
error_log /var/log/nginx/localhost.ssl_error_log info; error_log /var/log/nginx/localhost.ssl_error_log info;
root /var/www/localhost/htdocs; root /var/www/localhost/htdocs;
set $web2pyroot /home/Desktop/source/michelecomitini-facebookaccess; set $web2pyroot /home/Desktop/source/michelecomitini-facebookaccess;
location /pki/ { location /pki/ {
root /var/www/localhost/html; root /var/www/localhost/html;
} }
location ^/(.*)/static/(.*) { location ^/(.*)/static/(.*) {
alias $web2pyroot/applications/$1/static/$2; alias $web2pyroot/applications/$1/static/$2;
} }
location / { location / {
include /etc/nginx/scgi_params; include /etc/nginx/scgi_params;
scgi_pass 127.0.0.1:4000; scgi_pass 127.0.0.1:4000;
#Module ngx_http_ssl_module supports the following built-in variables: #Module ngx_http_ssl_module supports the following built-in variables:
#$ssl_cipher returns the cipher suite being used for the currently established SSL/TLS connection #$ssl_cipher returns the cipher suite being used for the currently established SSL/TLS connection
#$ssl_client_serial returns the serial number of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection #$ssl_client_serial returns the serial number of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
#$ssl_client_s_dn returns the subject Distinguished Name (DN) of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection #$ssl_client_s_dn returns the subject Distinguished Name (DN) of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
#$ssl_client_i_dn returns the issuer DN of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection #$ssl_client_i_dn returns the issuer DN of the client certificate for the currently established SSL/TLS connection — if applicable, i.e., if client authentication is activated in the connection
#$ssl_protocol returns the protocol of the currently established SSL/TLS connection — depending on the configuration and client available options it's one of SSLv2, SSLv3 or TLSv1 #$ssl_protocol returns the protocol of the currently established SSL/TLS connection — depending on the configuration and client available options it's one of SSLv2, SSLv3 or TLSv1
#$ssl_session_id the Session ID of the established secure connection — requires Nginx version greater or equal to 0.8.20 #$ssl_session_id the Session ID of the established secure connection — requires Nginx version greater or equal to 0.8.20
#$ssl_client_cert #$ssl_client_cert
#$ssl_client_raw_cert #$ssl_client_raw_cert
#$ssl_client_verify takes the value "SUCCESS" when the client certificate is successfully verified #$ssl_client_verify takes the value "SUCCESS" when the client certificate is successfully verified
scgi_param SSL_PROTOCOL $ssl_protocol; scgi_param SSL_PROTOCOL $ssl_protocol;
scgi_param HTTPS on; scgi_param HTTPS on;
scgi_param SSL_CIPHER $ssl_cipher; scgi_param SSL_CIPHER $ssl_cipher;
scgi_param SSL_CLIENT_SERIAL $ssl_client_serial; scgi_param SSL_CLIENT_SERIAL $ssl_client_serial;
scgi_param SSL_CLIENT_S_DN $ssl_client_s_dn; scgi_param SSL_CLIENT_S_DN $ssl_client_s_dn;
scgi_param SSL_CLIENT_I_DN $ssl_client_i_dn; scgi_param SSL_CLIENT_I_DN $ssl_client_i_dn;
scgi_param SSL_SESSION_ID $ssl_session_id; scgi_param SSL_SESSION_ID $ssl_session_id;
scgi_param SSL_CLIENT_CERT $ssl_client_cert; scgi_param SSL_CLIENT_CERT $ssl_client_cert;
scgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert; scgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert;
scgi_param SSL_CLIENT_VERIFY $ssl_client_verify; scgi_param SSL_CLIENT_VERIFY $ssl_client_verify;
} }
} }
} }
-1
View File
@@ -64,4 +64,3 @@ case "$1" in
;; ;;
esac esac
exit 0 exit 0
-1
View File
@@ -9,4 +9,3 @@ unzip web2py_src.zip
cd web2py cd web2py
# start web2py using command-line script # start web2py using command-line script
w2env/bin/python web2py.py -i 0.0.0.0 -p 8123 -a 'adminpasswd' w2env/bin/python web2py.py -i 0.0.0.0 -p 8123 -a 'adminpasswd'
@@ -58,8 +58,8 @@ wget http://rubyforge.org/frs/download.php/76628/redmine-2.2.0.tar.gz.md5
md5sum --check redmine-2.2.0.tar.gz.md5 > redmine_md5_checked_successfully md5sum --check redmine-2.2.0.tar.gz.md5 > redmine_md5_checked_successfully
if [ -f redmine_md5_checked_successfully ] if [ -f redmine_md5_checked_successfully ]
then then
tar xvfz redmine-2.2.0.tar.gz tar xvfz redmine-2.2.0.tar.gz
rm redmine_md5_checked_successfully rm redmine_md5_checked_successfully
else else
echo "Redmine md5 check sum failed..." echo "Redmine md5 check sum failed..."
exit 1 exit 1
@@ -195,18 +195,18 @@ SCRIPTNAME=/etc/init.d/redmine
# #
do_start() do_start()
{ {
# Return # Return
# 0 if daemon has been started # 0 if daemon has been started
# 1 if daemon was already running # 1 if daemon was already running
# 2 if daemon could not be started # 2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1 || return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \ $DAEMON_ARGS \
|| return 2 || return 2
# Add code here, if necessary, that waits for the process to be ready # Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend # to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time. # on this one. As a last resort, sleep for some time.
} }
# #
@@ -214,96 +214,96 @@ do_start()
# #
do_stop() do_stop()
{ {
# Return # Return
# 0 if daemon has been stopped # 0 if daemon has been stopped
# 1 if daemon was already stopped # 1 if daemon was already stopped
# 2 if daemon could not be stopped # 2 if daemon could not be stopped
# other if a failure occurred # other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?" RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2 [ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks # Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript. # and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code # If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be # that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to # needed by services started subsequently. A last resort is to
# sleep for some time. # sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2 [ "$?" = 2 ] && return 2
# Many daemons don'\''t delete their pidfiles when they exit. # Many daemons don'\''t delete their pidfiles when they exit.
rm -f $PIDFILE rm -f $PIDFILE
return "$RETVAL" return "$RETVAL"
} }
# #
# Function that sends a SIGHUP to the daemon/service # Function that sends a SIGHUP to the daemon/service
# #
do_reload() { do_reload() {
# #
# If the daemon can reload its configuration without # If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP), # restarting (for example, when it is sent a SIGHUP),
# then implement that here. # then implement that here.
# #
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0 return 0
} }
case "$1" in case "$1" in
start) start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start do_start
case "$?" in case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac esac
;; ;;
stop) stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop do_stop
case "$?" in case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac esac
;; ;;
status) status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;; ;;
#reload|force-reload) #reload|force-reload)
# #
# If do_reload() is not implemented then leave this commented out # If do_reload() is not implemented then leave this commented out
# and leave '\''force-reload'\'' as an alias for '\''restart'\''. # and leave '\''force-reload'\'' as an alias for '\''restart'\''.
# #
#log_daemon_msg "Reloading $DESC" "$NAME" #log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload #do_reload
#log_end_msg $? #log_end_msg $?
#;; #;;
restart|force-reload) restart|force-reload)
# #
# If the "reload" option is implemented then remove the # If the "reload" option is implemented then remove the
# '\''force-reload'\'' alias # '\''force-reload'\'' alias
# #
log_daemon_msg "Restarting $DESC" "$NAME" log_daemon_msg "Restarting $DESC" "$NAME"
do_stop do_stop
case "$?" in case "$?" in
0|1) 0|1)
do_start do_start
case "$?" in case "$?" in
0) log_end_msg 0 ;; 0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running 1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start *) log_end_msg 1 ;; # Failed to start
esac esac
;; ;;
*) *)
# Failed to stop # Failed to stop
log_end_msg 1 log_end_msg 1
;; ;;
esac esac
;; ;;
*) *)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3 exit 3
;; ;;
esac esac
:' > /etc/init.d/redmine :' > /etc/init.d/redmine
@@ -360,12 +360,12 @@ server {
root /var/www; root /var/www;
ssl_certificate /etc/nginx/ssl/self_signed.cert; ssl_certificate /etc/nginx/ssl/self_signed.cert;
ssl_certificate_key /etc/nginx/ssl/self_signed.key; ssl_certificate_key /etc/nginx/ssl/self_signed.key;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m; ssl_session_timeout 10m;
ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA; ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA;
ssl_protocols SSLv3 TLSv1; ssl_protocols SSLv3 TLSv1;
keepalive_timeout 70; keepalive_timeout 70;
location ~^\/(?!redmine(.*)) { location ~^\/(?!redmine(.*)) {
#uwsgi_pass 127.0.0.1:9001; #uwsgi_pass 127.0.0.1:9001;
uwsgi_pass unix:///tmp/web2py.socket; uwsgi_pass unix:///tmp/web2py.socket;
@@ -392,8 +392,8 @@ server {
# We copy ssl files we previously created # We copy ssl files we previously created
if [ -f /etc/nginx/ssl ] if [ -f /etc/nginx/ssl ]
then then
cp ~/self_signed.* /etc/nginx/ssl/ cp ~/self_signed.* /etc/nginx/ssl/
rm ~/self_signed.* rm ~/self_signed.*
else else
mkdir /etc/nginx/ssl mkdir /etc/nginx/ssl
cp ~/self_signed.* /etc/nginx/ssl/ cp ~/self_signed.* /etc/nginx/ssl/
+20 -25
View File
@@ -1,33 +1,33 @@
# #
# Author: Christopher Steel # Author: Christopher Steel
# Organization: Voice of Access # Organization: Voice of Access
# Date: 2010-11-24 # Date: 2010-11-24
# License: Same as Web2py, MIT / GNU # License: Same as Web2py, MIT / GNU
# Email: Christopher DOT Steel AT Voice of Access DOT org # Email: Christopher DOT Steel AT Voice of Access DOT org
# #
# This script will : # This script will :
# download and install virtualenv # download and install virtualenv
# start a virtual environment # start a virtual environment
# move into the virtual environment # move into the virtual environment
# download and install latest stable version of web2py # download and install latest stable version of web2py
# start web2py in the virtual environment # start web2py in the virtual environment
# #
# To disactivate the virtual environment, shut down web2py # To disactivate the virtual environment, shut down web2py
# and type 'disactivate' at the command line. # and type 'disactivate' at the command line.
# #
# Testing: # Testing:
# OS X # OS X
# should work on POSIX systems # should work on POSIX systems
# #
# Usage: # Usage:
# create a directory to hold your virtual environments, for example # create a directory to hold your virtual environments, for example
# /home/user_name/virtual_environments # /home/user_name/virtual_environments
# place this script in the directory and make it executable # place this script in the directory and make it executable
# chmod +x web2py-install-virtualenv.sh # chmod +x web2py-install-virtualenv.sh
customize the variables below to meet your needs customize the variables below to meet your needs
# execute from terminal # execute from terminal
# ./web2py-install-virtualenv.sh # ./web2py-install-virtualenv.sh
# relax... # relax...
################ VARIABLES ################ VARIABLES
# Change to reflect version changes etc. # Change to reflect version changes etc.
@@ -94,8 +94,3 @@ read -p "Press any key to start web2py…"
echo 'starting web2py' echo 'starting web2py'
echo '===============' echo '==============='
../bin/python2.5 web2py.py ../bin/python2.5 web2py.py
-1
View File
@@ -398,4 +398,3 @@ cd ${current_directory}
echo " - Complete!" echo " - Complete!"
echo echo
@@ -346,5 +346,3 @@ reboot
as superuser as superuser
" "
-1
View File
@@ -175,4 +175,3 @@ cd /home/www-data/web2py
sudo -u www-data python -c "from gluon.widget import console; console();" sudo -u www-data python -c "from gluon.widget import console; console();"
sudo -u www-data python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)" sudo -u www-data python -c "from gluon.main import save_password; save_password(raw_input('admin password: '),443)"
echo "done!" echo "done!"
+14 -15
View File
@@ -13,13 +13,13 @@
TARGET=web2py TARGET=web2py
if [ ! -d $TARGET ]; then if [ ! -d $TARGET ]; then
# in case we're in web2py/ # in case we're in web2py/
if [ -f ../$TARGET/VERSION ]; then if [ -f ../$TARGET/VERSION ]; then
cd .. cd ..
# in case we're in web2py/scripts # in case we're in web2py/scripts
elif [ -f ../../$TARGET/VERSION ]; then elif [ -f ../../$TARGET/VERSION ]; then
cd ../.. cd ../..
fi fi
fi fi
read a VERSION c < $TARGET/VERSION read a VERSION c < $TARGET/VERSION
SAVE=$TARGET-$VERSION SAVE=$TARGET-$VERSION
@@ -32,14 +32,14 @@ SAVED=""
# but don't overwrite a previous save of the same version. # but don't overwrite a previous save of the same version.
# #
if [ -f $SAVE.zip ]; then if [ -f $SAVE.zip ]; then
echo "Remove or rename $SAVE.zip first" >&2 echo "Remove or rename $SAVE.zip first" >&2
exit 1 exit 1
fi fi
if [ -d $TARGET ]; then if [ -d $TARGET ]; then
echo -n ">>Save old version: " >&2 echo -n ">>Save old version: " >&2
cat $TARGET/VERSION >&2 cat $TARGET/VERSION >&2
zip -q -r $SAVE.zip $TARGET zip -q -r $SAVE.zip $TARGET
SAVED=$SAVE.zip SAVED=$SAVE.zip
fi fi
# #
# Download the new version. # Download the new version.
@@ -54,6 +54,5 @@ rm $ZIP
echo -n ">>New version: " >&2 echo -n ">>New version: " >&2
cat $TARGET/VERSION >&2 cat $TARGET/VERSION >&2
if [ "$SAVED" != "" ]; then if [ "$SAVED" != "" ]; then
echo ">>Old version saved as $SAVED" echo ">>Old version saved as $SAVED"
fi fi
+26 -13
View File
@@ -2,25 +2,39 @@
# the script should be run # the script should be run
# from WEB2PY root directory # from WEB2PY root directory
prog=`basename $0` prog="web2py.py"
cd `pwd`
chmod +x $prog chmod +x $prog
function web2py_start { function web2py_start {
nohup ./$prog -a "<recycle>" 2>/dev/null & nohup python2 ./$prog -a "<recycle>" >>/dev/null 2>/dev/null &
pid=`pgrep $prog | tail -1` pid=`pgrep -f $prog | tail -1`
if [ $pid -ne $$ ] if [ "x$pid" != "x$$" ]
then then
echo "WEB2PY has been started." echo "WEB2PY has been started (pid $pid). Stop it with '$0 stop'"
else
echo "Failed to start WEB2PY."
fi fi
} }
function web2py_stop { function web2py_stop {
kill -15 `pgrep $prog | grep -v $$` 2>/dev/null pid="`pgrep -f $prog | grep -v $$`"
pid=`pgrep $prog | head -1` if [ "x$pid" == "x" ]
if [ $pid -ne $$ ] then
then echo "No WEB2PY processes to stop."
echo "WEB2PY has been stopped." else
kill -15 $pid
# Wait for web2py to shut down gracefully.
sleep 2
pid="`pgrep -f $prog | head -1`"
if [ "x$pid" == "x" ]
then
echo "WEB2PY has been stopped."
else
echo "Failed to stop WEB2PY. (Possibly, only one of several web2py processes was killed.)"
echo "Still running:"
pgrep -af $prog
fi
fi fi
} }
@@ -36,9 +50,8 @@ case "$1" in
web2py_start web2py_start
;; ;;
*) *)
echo "Usage: $prog [start|stop|restart]" echo "Usage: $0 [start|stop|restart]"
;; ;;
esac esac
exit 0 exit 0
+36 -37
View File
@@ -28,55 +28,54 @@ PYTHON=python
cd $DAEMON_DIR cd $DAEMON_DIR
start() { start() {
echo -n $"Starting $DESC ($NAME): " echo -n $"Starting $DESC ($NAME): "
daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --nogui -a $ADMINPASS -d $PIDFILE -p $PORT & daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --nogui -a $ADMINPASS -d $PIDFILE -p $PORT &
RETVAL=$? RETVAL=$?
if [ $RETVAL -eq 0 ]; then if [ $RETVAL -eq 0 ]; then
touch /var/lock/subsys/$NAME touch /var/lock/subsys/$NAME
fi fi
echo echo
return $RETVAL return $RETVAL
} }
stop() { stop() {
echo -n $"Shutting down $DESC ($NAME): " echo -n $"Shutting down $DESC ($NAME): "
killproc -p "$PIDFILE" -d 3 "$NAME" killproc -p "$PIDFILE" -d 3 "$NAME"
echo echo
if [ $RETVAL -eq 0 ]; then if [ $RETVAL -eq 0 ]; then
rm -f /var/lock/subsys/$NAME rm -f /var/lock/subsys/$NAME
rm -f $PIDFILE rm -f $PIDFILE
fi fi
return $RETVAL return $RETVAL
} }
restart() { restart() {
stop stop
start start
} }
status() { status() {
if [ -r "$PIDFILE" ]; then if [ -r "$PIDFILE" ]; then
pid=`cat $PIDFILE` pid=`cat $PIDFILE`
fi fi
if [ $pid ]; then if [ $pid ]; then
echo "$NAME (pid $pid) is running..." echo "$NAME (pid $pid) is running..."
else else
echo "$NAME is stopped" echo "$NAME is stopped"
fi fi
} }
case "$1" in case "$1" in
start) start;; start) start;;
stop) stop;; stop) stop;;
status) status;; status) status;;
restart) restart;; restart) restart;;
condrestart) [ -e /var/lock/subsys/$NAME ] && restart condrestart) [ -e /var/lock/subsys/$NAME ] && restart
RETVAL=$? RETVAL=$?
;; ;;
*) echo $"Usage: $0 {start|stop|restart|condrestart|status}" *) echo $"Usage: $0 {start|stop|restart|condrestart|status}"
RETVAL=1 RETVAL=1
;; ;;
esac esac
exit $RETVAL exit $RETVAL
+108 -109
View File
@@ -48,26 +48,26 @@ DAEMON_USER=web2py
# #
do_start() do_start()
{ {
# Return # Return
# 0 if daemon has been started # 0 if daemon has been started
# 1 if daemon was already running # 1 if daemon was already running
# 2 if daemon could not be started # 2 if daemon could not be started
# The PIDDIR should normally be created during installation. This # The PIDDIR should normally be created during installation. This
# fixes things just in case. # fixes things just in case.
[ -d $PIDDIR ] || mkdir -p $PIDDIR [ -d $PIDDIR ] || mkdir -p $PIDDIR
[ -n "$DAEMON_USER" ] && chown --recursive $DAEMON_USER $PIDDIR [ -n "$DAEMON_USER" ] && chown --recursive $DAEMON_USER $PIDDIR
# Check to see if the daemon is already running. # Check to see if the daemon is already running.
start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \ start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \
&& return 1 && return 1
start-stop-daemon --start --quiet --pidfile $PIDFILE \ start-stop-daemon --start --quiet --pidfile $PIDFILE \
${DAEMON_USER:+--chuid $DAEMON_USER} --chdir $DAEMON_DIR \ ${DAEMON_USER:+--chuid $DAEMON_USER} --chdir $DAEMON_DIR \
--background --exec $DAEMON -- $DAEMON_ARGS \ --background --exec $DAEMON -- $DAEMON_ARGS \
|| return 2 || return 2
return 0; return 0;
} }
# #
@@ -75,17 +75,17 @@ do_start()
# #
do_stop() do_stop()
{ {
# Return # Return
# 0 if daemon has been stopped # 0 if daemon has been stopped
# 1 if daemon was already stopped # 1 if daemon was already stopped
# 2 if daemon could not be stopped # 2 if daemon could not be stopped
# other if a failure occurred # other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE
RETVAL=$? RETVAL=$?
# Many daemons don't delete their pidfiles when they exit. # Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE rm -f $PIDFILE
return "$RETVAL" return "$RETVAL"
} }
# #
@@ -93,37 +93,37 @@ do_stop()
# #
do_restart() do_restart()
{ {
# Return # Return
# 0 if daemon was (re-)started # 0 if daemon was (re-)started
# 1 if daemon was not strated or re-started # 1 if daemon was not strated or re-started
do_stop do_stop
case "$?" in case "$?" in
0|1) 0|1)
do_start do_start
case "$?" in case "$?" in
0) RETVAL=0 ;; 0) RETVAL=0 ;;
1) RETVAL=1 ;; # Old process is still running 1) RETVAL=1 ;; # Old process is still running
*) RETVAL=1 ;; # Failed to start *) RETVAL=1 ;; # Failed to start
esac esac
;; ;;
*) RETVAL=1 ;; # Failed to stop *) RETVAL=1 ;; # Failed to stop
esac esac
return "$RETVAL" return "$RETVAL"
} }
# #
# Function that sends a SIGHUP to the daemon/service # Function that sends a SIGHUP to the daemon/service
# #
do_reload() { do_reload() {
# #
# If the daemon can reload its configuration without # If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP), # restarting (for example, when it is sent a SIGHUP),
# then implement that here. # then implement that here.
# #
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE
return 0 return 0
} }
# #
@@ -131,79 +131,79 @@ do_reload() {
# #
do_status() do_status()
{ {
# Return # Return
# 0 if daemon is responding and OK # 0 if daemon is responding and OK
# 1 if daemon is not responding, but PIDFILE exists # 1 if daemon is not responding, but PIDFILE exists
# 2 if daemon is not responding, but LOCKFILE exists # 2 if daemon is not responding, but LOCKFILE exists
# 3 if deamon is not running # 3 if deamon is not running
# 4 if daemon status is unknown # 4 if daemon status is unknown
# Check to see if the daemon is already running. # Check to see if the daemon is already running.
start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \ start-stop-daemon --stop --test --quiet --pidfile $PIDFILE \
&& return 0 && return 0
[ -f $PIDFILE ] && return 1 [ -f $PIDFILE ] && return 1
return 3 return 3
} }
case "$1" in case "$1" in
start) start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start do_start
RETVAL=$? RETVAL=$?
[ "$VERBOSE" != no ] && [ "$VERBOSE" != no ] &&
case "$RETVAL" in case "$RETVAL" in
0|1) log_end_msg 0 ;; 0|1) log_end_msg 0 ;;
*) log_end_msg 1 ;; *) log_end_msg 1 ;;
esac esac
exit "$RETVAL" exit "$RETVAL"
;; ;;
stop) stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop do_stop
RETVAL=$? RETVAL=$?
[ "$VERBOSE" != no ] && [ "$VERBOSE" != no ] &&
case "$RETVAL" in case "$RETVAL" in
0|1) log_end_msg 0 ;; 0|1) log_end_msg 0 ;;
*) log_end_msg 1 ;; *) log_end_msg 1 ;;
esac esac
exit "$RETVAL" exit "$RETVAL"
;; ;;
#reload|force-reload) #reload|force-reload)
# #
# If do_reload() is not implemented then leave this commented out # If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'. # and leave 'force-reload' as an alias for 'restart'.
# #
#[ "$VERBOSE" != no ] && log_daemon_msg "Reloading $DESC" "$NAME" #[ "$VERBOSE" != no ] && log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload #do_reload
#RETVAL=$? #RETVAL=$?
#[ "$VERBOSE" != no ] && log_end_msg $? #[ "$VERBOSE" != no ] && log_end_msg $?
#exit "$RETVAL" #exit "$RETVAL"
#;; #;;
restart|force-reload) restart|force-reload)
# #
# If the "reload" option is implemented then remove the # If the "reload" option is implemented then remove the
# 'force-reload' alias # 'force-reload' alias
# #
[ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME" [ "$VERBOSE" != no ] && log_daemon_msg "Restarting $DESC" "$NAME"
do_restart do_restart
RETVAL=$? RETVAL=$?
[ "$VERBOSE" != no ] && log_end_msg "$RETVAL" [ "$VERBOSE" != no ] && log_end_msg "$RETVAL"
exit "$RETVAL" exit "$RETVAL"
;; ;;
status) status)
do_status do_status
RETVAL=$? RETVAL=$?
[ "$VERBOSE" != no ] && [ "$VERBOSE" != no ] &&
case "$RETVAL" in case "$RETVAL" in
0) log_success_msg "$NAME is running" ;; 0) log_success_msg "$NAME is running" ;;
*) log_failure_msg "$NAME is not running" ;; *) log_failure_msg "$NAME is not running" ;;
esac esac
exit "$RETVAL" exit "$RETVAL"
;; ;;
*) *)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2 echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload|status}" >&2
exit 3 exit 3
;; ;;
esac esac
: :
@@ -220,4 +220,3 @@ esac
# http://refspecs.linux-foundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic.pdf # http://refspecs.linux-foundation.org/LSB_3.2.0/LSB-Core-generic/LSB-Core-generic.pdf
# Debian Policy SysV init: http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit # Debian Policy SysV init: http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit
# Examine files in /usr/share/doc/sysv-rc/ # Examine files in /usr/share/doc/sysv-rc/
-17
View File
@@ -1,17 +0,0 @@
language: python
python:
- '2.5'
- '2.6'
- '2.7'
before_script:
- pip install unittest2
- mysql -e 'create database test_pymysql;'
- mysql -e 'create database test_pymysql2;'
script: PYTHONPATH=. unit2 -v gluon.tests
notifications:
irc:
channels: "irc.freenode.org#web2py"