Compare commits

...
232 Commits
Author SHA1 Message Date
mdipierro 4599dcd988 R-2.3.2 2012-12-17 09:00:46 -06:00
mdipierro 14bdfd3321 fixed issue 1227, entropy check with IS_STRONG, thanks Clavin Sim 2012-12-17 08:50:08 -06:00
mdipierro e7d14c028b fixed issue 1225, NEWINSTALL ignored 2012-12-17 08:46:33 -06:00
mdipierro 0207a17afa cursor point in share.js, issue 1123, thanks Nathan 2012-12-16 12:36:28 -06:00
mdipierro 73b049f361 added oneall login support, issue 1124, thanks Nathan 2012-12-16 12:34:08 -06:00
mdipierro 5695fe34f0 fixed example in examples from pull-request, thanks tpng 2012-12-14 14:28:31 -06:00
mdipierro f2e44f96d6 R-2.3.1 2012-12-14 09:22:09 -06:00
mdipierro 2ccc707059 modernizr 2.6.2 2012-12-14 09:21:00 -06:00
mdipierro 30490a0c5c fixed possible issue with share.js 2012-12-14 09:17:30 -06:00
mdipierro 4c1102026d upgraded bootstrap, jquery, and analytics 2012-12-14 08:24:58 -06:00
mdipierro 63b589170c fixed paths in admin debug.py, thanks Mariano 2012-12-13 18:09:11 -06:00
mdipierro 2b0d098027 CHANGELOG edits 2012-12-13 12:54:39 -06:00
mdipierro fcfaa63f33 edited CHANGELOG, 2.3.0 rc2 2012-12-13 11:53:30 -06:00
mdipierro f497aa7fcf fixed aes 2012-12-13 11:28:52 -06:00
mdipierro d91f7381e1 allow web shell working when https is required 2012-12-13 11:10:59 -06:00
mdipierro bad0af15f6 removed debug print statement, thanks Niphlod 2012-12-13 08:22:10 -06:00
mdipierro d7cc15c85d partially fixed issue 832, auth.requires raises 401 on ajax, thanks Paolo Valleri 2012-12-12 22:07:20 -06:00
mdipierro 1f1e65dcb5 fixed formstyle_bootstrap, thanks Anthony 2012-12-12 22:03:39 -06:00
mdipierro 930d4dcdd0 added NEWINSTALL to git, issue 1217, thanks dev.ldhughes 2012-12-12 21:49:27 -06:00
mdipierro b640820052 fixed ip4 client validation, issue 1218, thanks Joachim Breitsprecher 2012-12-12 21:47:55 -06:00
mdipierro be0003927a fixed css issue 1219, thanks Paolo 2012-12-12 21:46:09 -06:00
mdipierro 0015a265a9 analytics.js 2012-12-12 21:30:57 -06:00
mdipierro 12ff451f4b allow LOADing multiple grids, thanks Niphlod 2012-12-12 21:14:25 -06:00
mdipierro 63cefe0862 fixed typo in docstring, issue 1210, thanks francois.delpierre 2012-12-11 20:50:10 -06:00
mdipierro bf3a24e515 revised setup-web2py-nginx-uwsgi-ubuntu.sh, thanks Vinicious 2012-12-11 20:15:15 -06:00
mdipierro 4378a8a792 fixed typo in globals, issue 1213, thanks jkellas 2012-12-11 15:54:57 -06:00
mdipierro 1532cecb25 Added intro tutorial, thanks Michael Herman 2012-12-11 10:53:31 -06:00
mdipierro 22af4db51d sorting importing models was broken, now fixed, thanks Joel Samuelsson 2012-12-11 08:25:48 -06:00
mdipierro 523cb34119 fixed winservice problem 2012-12-10 21:32:36 -06:00
mdipierro 24b037c307 fixed problem with clear and session_hash 2012-12-09 14:29:37 -06:00
mdipierro bd933ca43d fixed issue 1208 (auth on GAE) by disabling virtual fields in auth.user on GAE 2012-12-08 18:31:41 -06:00
mdipierro 676b2ef54b fixed issue 1208 (auth on GAE) by disabling virtual fields in auth.user on GAE 2012-12-08 18:25:04 -06:00
mdipierro 353a66f44c fixed issue 1207, IS_IN_SET(list_of_ints), thanks Alan 2012-12-07 10:58:43 -06:00
mdipierro 689dfcd83e fixed problem CRYPT password comparsion, issue 1203, thanks Alan 2012-12-06 23:04:29 -06:00
mdipierro 8f7b82fdb3 fixed escaping of response.js, issue 1205, thanks max.morais.dmm 2012-12-06 23:02:09 -06:00
mdipierro c574a97cad regex simplification 2012-12-06 16:56:29 -06:00
mdipierro 5e7de996e0 fixed issue 1202, commas in attachments, thanks simonm3 2012-12-05 16:49:24 -06:00
mdipierro 52ba4bb0ea fixed issue 1187, custom widget hadnling error in case for nonzero formatter, thanks Maria Mitica 2012-12-05 16:34:55 -06:00
mdipierro c43aa3545f himBH>jQuery, thanks Niphlod 2012-12-05 15:33:30 -06:00
mdipierro 8e48a816f8 better paymenttech support, BSD license 2012-12-05 15:31:38 -06:00
mdipierro afef829571 fixed IS_STRONG **** issue, issue 1098 2012-12-05 15:24:15 -06:00
mdipierro f4e5de995c lazy sqlhtml_validators, fixed issue 931 2012-12-05 14:18:00 -06:00
mdipierro 70a4e06f18 fixed web2py_bootstrap.css, thanks Paolo 2012-12-05 13:53:46 -06:00
mdipierro f241c008d4 allow more customization of db._adapter.types 2012-12-05 11:57:18 -06:00
mdipierro 9920b7592d fixed issue 440, reference to primary keys 2012-12-05 11:35:23 -06:00
mdipierro fee2c667b3 db.define_table('person',Field('name'),Field.Virtual('namey',lambda row: row.person.name+'y')), fixed issue 374 2012-12-05 11:23:11 -06:00
mdipierro ebc9d98a87 fixed issue 265 2012-12-05 11:03:16 -06:00
mdipierro 06f5a32c48 db.thing(name='Cohen',_orderby=db.thing.name), thanks Yair 2012-12-05 09:01:03 -06:00
mdipierro 35cb9ee529 experimental support for MSSQL pagination 2012-12-04 18:30:36 -06:00
mdipierro f0e59ca617 paymentech.py 2012-12-04 10:56:30 -06:00
mdipierro 1bbfd80c24 2.3.0 rc1 2012-12-04 09:02:41 -06:00
mdipierro a9600faa9f uri='informi-se://... 2012-12-04 08:48:27 -06:00
mdipierro 6f39f96799 added interact.html to git 2012-12-04 08:32:26 -06:00
mdipierro b06df4740b fixed filename in dal storage, thanks Marin 2012-12-03 18:02:45 -06:00
mdipierro d354e4f767 possibly fixed issue 1169 2012-12-03 17:39:17 -06:00
mdipierro caef44d4f9 fixed escape->encodeURIComponent in autocomplete, issue 1198, thanks Dmitry 2012-12-03 17:11:18 -06:00
mdipierro f9eb2c5a0a check_reserved in welcome, thanks Marin 2012-12-03 16:30:13 -06:00
mdipierro 3a05be49f6 closeflash patch, thanks Paolo, issue 1191 2012-12-03 16:06:27 -06:00
mdipierro b92e169b69 MENU(li_first, li_last..), fixed issue 1200, thanks dev to null 2012-12-03 14:42:37 -06:00
mdipierro 882f42de8f better grid errors, issue 1135 2012-12-03 14:28:00 -06:00
mdipierro c8e64ac4f7 fixed problem with raw_args 2012-12-03 14:04:17 -06:00
mdipierro da1751dc1b fixed cronjobs broken in 4278, fixed issue 1190 2012-12-03 13:16:14 -06:00
mdipierro 304ae277fe improved heroku script 2012-12-03 11:01:11 -06:00
mdipierro 0e93162080 support for in and not in smart_query 2012-12-03 08:15:58 -06:00
mdipierro 6ced9dd235 auth.settings.login_onfail, thanks Yair 2012-12-02 08:27:17 -06:00
mdipierro e2da57f448 added missing form.html to examples 2012-12-01 16:39:31 -06:00
mdipierro d0047e6e5b removed test, lazy table do not suport self references 2012-11-30 14:30:49 -06:00
mdipierro 90955bdcab wiki(menugroups=['wiki_editor']) 2012-11-30 09:02:35 -06:00
mdipierro d2b4099cdb fixed error reporting in dal 2012-11-30 08:34:11 -06:00
mdipierro 1e7315d4a8 fixed problem with lazy tables and self references 2012-11-29 23:54:29 -06:00
mdipierro d7ace92c04 Allow for Field Validators to handle None values, fixed issue 1183, thanks Joe Barnhart 2012-11-29 22:35:20 -06:00
mdipierro 568c33d7d6 allow -R ...pyc, fixed issue 1186, thanks Corne.Dickens 2012-11-29 18:21:06 -06:00
mdipierro ef8f10c42d added missing fileutils, fixed issue 1193, thanks Yager 2012-11-29 17:13:30 -06:00
mdipierro fc7c4432e9 fixed issue 1197, Virtual Fields are stored in session 2012-11-29 17:05:27 -06:00
mdipierro 04a7d9a92d do not redirect after auth.logout() if next is None 2012-11-29 09:52:48 -06:00
mdipierro b83673f1c8 new router patch fixes runaway vulnerability, thanks Jonathan 2012-11-27 11:29:36 -06:00
mdipierro ba21ce887a fixed bug in getlist thanks roger 2012-11-27 10:17:22 -06:00
mdipierro 5764482acd style upload button 2012-11-27 10:11:52 -06:00
mdipierro 1b0e08cfa0 fixed issue 1174, cache folder in examples 2012-11-24 11:12:20 -06:00
mdipierro e59cb43b0b fixed issue 1174, cache folder in examples 2012-11-24 11:07:07 -06:00
mdipierro 2cbca7a43d fixed issue 1176, truncate on GAE+SQL 2012-11-24 10:58:44 -06:00
mdipierro d2dd3d0ac6 fixed issue 1179, welcome.w2p created if not exists 2012-11-24 10:48:27 -06:00
mdipierro 6324ed7a9c added .woff contenttype, fixed issue 1181, thanks Chris 2012-11-24 10:35:47 -06:00
mdipierro 23595c0608 rocket support for IPV6, thanks Chirs Winebrinner 2012-11-24 10:15:37 -06:00
mdipierro eebc3418f9 Merge branch 'master' of github.com:web2py/web2py 2012-11-24 10:09:48 -06:00
mdipierro 867caae3da Merge pull request #44 from cwinebrinner/rocket_ipv6_and_cleanup
explorigin/Rocket merged and IPv6 support added
2012-11-24 08:06:54 -08:00
mdipierro d0098a878a fixed router regex runaway, thanks Jonathan 2012-11-23 11:58:36 -06:00
mdipierro ee7db631ee fixed ldap security issue, thanks demetrio 2012-11-23 08:02:01 -06:00
Charles Winebrinner b388f41f80 Added IPv6 support for Rocket, refactored some code, and fixed some bugs. 2012-11-18 01:03:04 -06:00
Charles Winebrinner 9f5391bbc0 Merged explorigin/Rocket and brought web2py's rocket more in line with upstream. 2012-11-18 00:32:24 -06:00
Charles Winebrinner 22ca52a1cb Fixed a typo in utils.py. 2012-11-18 00:32:24 -06:00
Charles Winebrinner b56cfb372d PEP8'd rocket.py.footer (to be removed from rocket.py). 2012-11-18 00:32:24 -06:00
mdipierro c8367e257f better heroku script 2012-11-17 22:28:51 -06:00
mdipierro 62d37702a9 better heroku support 2012-11-17 22:14:42 -06:00
mdipierro 74fc2a2d85 added heroku support 2012-11-17 21:38:28 -06:00
mdipierro 0ac1298501 fixed broken session in recent trunk 2012-11-17 13:28:50 -06:00
mdipierro faf76ac715 fixed issue 1173, request.requires_https, thanks Maciej 2012-11-17 12:14:28 -06:00
mdipierro 3ece96ae08 fixed typo in response.updated, issue 1167, thanks dedirock 2012-11-17 12:06:39 -06:00
mdipierro e91cf7f6ac better list widget, thanks Howesc 2012-11-17 12:04:29 -06:00
mdipierro 019d3e07b5 fixed issue 1152, gae sql varchar limit, and fixed typo in fpdf 2012-11-17 11:46:46 -06:00
mdipierro e3046845bf fixed image_map in gluon/contrib/fpdf/html.py 2012-11-17 09:16:07 -06:00
mdipierro b8f52ed76a fixed broken HTT_HOST in routes_in 2012-11-17 08:59:55 -06:00
mdipierro 662abd29cb extra or {}, thanks villas 2012-11-16 16:24:13 -06:00
mdipierro 3560337b40 auth.wiki(extra) 2012-11-15 14:34:20 -06:00
mdipierro c7985e8881 patches for @service.jsonrpc and exportclasses, thanks niphlod 2012-11-15 13:40:04 -06:00
mdipierro a69bc44314 fixed generic.xml 2012-11-15 12:29:28 -06:00
mdipierro 651c92c175 do not create a new session if nothing in session file 2012-11-14 15:33:18 -06:00
mdipierro 7319574f30 fixed issue 1160, show button for linked tables even if referenced column is not readable 2012-11-14 15:00:04 -06:00
mdipierro b9821f1c21 fixed issue 1158, redirect client_side in auth forms embedded via LOAD, thanks PyCon support 2012-11-14 14:12:32 -06:00
mdipierro 52ca6af024 possible solution to issue 1154, onvalidation = dict(onsuccess= [...]) 2012-11-14 13:47:56 -06:00
mdipierro cc4d27c6d2 jQuery 1.8.2 2012-11-14 08:44:11 -06:00
mdipierro b687977663 fixed 2.5 issues with scheduler 2012-11-13 16:27:26 -06:00
mdipierro 3e85f5bf39 fixed CAS login for extra url fields, thanks efaisal 2012-11-09 14:11:37 -06:00
mdipierro 23165d9382 fixed issue 1144, start webservert at 0.0.0.0 2012-11-08 16:19:44 -06:00
mdipierro 1abc31895e HTML(doctype=''), issue 1140 2012-11-08 11:29:47 -06:00
mdipierro 6fc70fbc55 fixed issue 1143, grid/smartgrid GAE improvements, thanks Howesc 2012-11-08 10:05:46 -06:00
mdipierro bf86becc5e fies typo in dal, issue 1146, thanks guruyaya 2012-11-08 10:04:24 -06:00
mdipierro 510142ad2d typo in compileapp, issue 1149, thanks Corne 2012-11-08 10:02:46 -06:00
mdipierro 6eb361d8f7 possibly fixed issue 1151 2012-11-08 09:59:10 -06:00
mdipierro e56f588dea fixed issue 1147, belongs(None), thanks Marin 2012-11-06 14:26:28 -06:00
mdipierro 0ee551de71 grid buttons_placement and links_placement , thanks Niphlod 2012-11-06 10:36:45 -06:00
mdipierro 7dc40f68db changed in custom_import 2012-11-06 09:47:02 -06:00
mdipierro adb0c08933 improved scheduler, thanks Niphlod 2012-11-05 12:39:17 -06:00
mdipierro 2fea9495b3 fixed rss serializer again, thanks Charles Winebrinner 2012-11-04 20:45:25 -06:00
mdipierro 1f767c3497 fixed rss serializer again, thanks Charles Winebrinner 2012-11-04 19:55:15 -06:00
mdipierro 09bae08dfa fixed rss2 again 2012-11-04 19:29:18 -06:00
mdipierro 8a137691ff fixed problem in rss2.py 2012-11-04 18:12:41 -06:00
mdipierro 7de90f18cc adding missing views 2012-11-04 09:14:47 -06:00
mdipierro 859636e6e0 fixed issue 1136, prevent redirection loop, better form, usability as embedded widgets, thanks Alan 2012-11-03 11:54:46 -05:00
mdipierro a511682ce1 better google wallet, checks parameters 2012-11-01 22:01:57 -05:00
mdipierro 6a81420fc0 many scheduler improvements, thanks Niphlod 2012-11-01 21:35:53 -05:00
mdipierro fa019e8960 fixed issue 1124, create new auth.wiki page from slug model, thanks Nico 2012-11-01 21:33:06 -05:00
mdipierro b17358e761 better gluon.tools.Expose, thanks Richard 2012-11-01 21:09:09 -05:00
mdipierro c9b41a4343 fixed issue 1132, unicode in IS_IN_DB validator 2012-11-01 21:07:47 -05:00
mdipierro 8affdbdc86 fixed issue 1134, gae dal delete, thanks Howesc 2012-11-01 21:01:46 -05:00
mdipierro a61e388498 fixed issue 1130, prevent exception for virtual fields in forms, thanks hi21alt 2012-10-31 11:43:11 -05:00
mdipierro d72752f453 scripts/extract_sqlite_models.py, thanks Michele 2012-10-31 10:23:56 -05:00
mdipierro d67af48f29 allows to customize the A in a MENU, thanks ilvalle 2012-10-31 10:22:20 -05:00
mdipierro b18a2a7aa3 fixed issue 1117, snitize of unicode, thanks Bill 2012-10-31 10:02:08 -05:00
mdipierro 693dce0e6c fixed issue 1127, SQLTABLE column with virtual fields, thanks hi21alt 2012-10-31 09:52:43 -05:00
mdipierro e1cd36771e fixed issue 1129, recapcha message error, thanks Friedrich 2012-10-31 09:50:37 -05:00
mdipierro 12107da9bd allow to pass hidden to crud.update 2012-10-31 09:14:58 -05:00
Massimo 96eee74f56 fixed sync languages, thanks Yair 2012-10-30 11:09:50 -05:00
Massimo 2f881085ef fixed validator recent typo 2012-10-30 09:42:03 -05:00
mdipierro 7b2afa109e reverted changes about formatting None 2012-10-29 22:25:19 -05:00
mdipierro 8dae3f832d gluon/contrib/login_methods/dropbox_account.py, get client 2012-10-29 22:22:36 -05:00
mdipierro c61e0e11bb page and media preview in wiki, thanks Niphlod 2012-10-29 21:38:09 -05:00
mdipierro 04caa5f4e4 moved toolbar logic to dal, thanks Niphlod 2012-10-29 18:13:58 -05:00
mdipierro 6a13c33c70 added informix to list, thanks Christopher 2012-10-29 18:11:52 -05:00
mdipierro 4357edb1d4 if user_group_name is None, user_group_role is also None 2012-10-29 14:00:29 -05:00
mdipierro 9d7e009c0f csv can return a generator, thanks Jonathan 2012-10-29 11:36:04 -05:00
mdipierro 7549f77edc validator.formatter checks None 2012-10-29 10:09:07 -05:00
mdipierro 5ee2ab9b6b fixed redirection after change password, issue 1112, thanks Friedrich 2012-10-29 09:31:51 -05:00
mdipierro 54b98b4e05 fixed issue 1118, reload page when called by component, thanks Paolo 2012-10-29 09:28:19 -05:00
mdipierro 6e0e48e150 fixed toolbar issue 1111, thanks niphlod 2012-10-29 09:06:52 -05:00
mdipierro fe5a1c6a8d fixed issue 1120, bug introduced in latest utils.py import. thanks Yair 2012-10-29 08:59:54 -05:00
mdipierro 8332106754 fixed issue 1121, appadmin interface to cache, thanks Paolo 2012-10-29 08:57:43 -05:00
mdipierro aa149d2e7b fixed bug in sessions2trach.py, issue 1111, thanks Szimszon 2012-10-29 08:53:50 -05:00
mdipierro a5f883c2f0 following Joe and Jonathan's advice, None value is formatted 2012-10-28 12:01:09 -05:00
mdipierro 5953377060 better utils.py, thanks Niphlod 2012-10-27 14:37:40 -05:00
mdipierro 5a478302f4 removed redundant assignment, thanks Niphlod 2012-10-26 15:51:17 -05:00
mdipierro 1621825166 fixed scheduler bug, thanks Niphlod 2012-10-26 15:49:47 -05:00
mdipierro 6af2e859ac fixed commit mistake, thanks Nico 2012-10-25 17:23:05 -05:00
mdipierro fb22a8843e fixed issue 1096, thanks Howesc 2012-10-25 10:23:50 -05:00
mdipierro 529dda0e6d fixed issue 1104, thanks Paolo Valleri 2012-10-25 10:19:50 -05:00
mdipierro 35c893d47a fixed issue 1109 2012-10-25 10:11:51 -05:00
mdipierro 99e2397981 fixed issue 1113, thanks Nico 2012-10-25 10:07:39 -05:00
mdipierro b6d68f97d6 fixed issue 1114, thanks Friedrich 2012-10-25 10:04:04 -05:00
mdipierro 74024301a5 operators in grid are not translated, thanks Friedrich Weber 2012-10-25 10:02:40 -05:00
mdipierro 1f100bbe88 better pep8 in ldap_auth.py, thanks Gyuris 2012-10-25 08:47:36 -05:00
mdipierro c94c192e17 wiki edit menu pages conditional to login 2012-10-25 08:22:34 -05:00
mdipierro 0d9e5985e4 better sessions2trash.py, thanks Jim 2012-10-25 08:17:30 -05:00
Massimo b31bfdaaf5 fixed missing sys in languages 2012-10-24 16:04:32 -05:00
mdipierro 5f67edbf87 dal almost runs with python 3.3 but fails a test on 2.5 2012-10-23 11:30:02 -05:00
mdipierro 093e8b356e dal compiles with python 3.3 2012-10-23 11:13:29 -05:00
mdipierro 43d4c2831d template compiles with python 3.0 2012-10-23 10:45:17 -05:00
mdipierro 55a0dbeb86 languages.py closer to python 3.0 support 2012-10-23 10:28:46 -05:00
mdipierro 2f1f2dccc1 utils compiles with python 3 2012-10-23 10:17:23 -05:00
mdipierro a4cef60c49 reverted changs to utils 2012-10-23 10:13:17 -05:00
mdipierro 96ee377279 utils compiles with python 3 2012-10-23 10:11:33 -05:00
mdipierro 4152c72de5 aes python 3 compiles (but does work well?) 2012-10-23 09:54:07 -05:00
mdipierro ab066397b2 aes new raise compliant 2012-10-23 09:44:34 -05:00
mdipierro 8c514df120 reverted previous change but still problems with concurency multiple dbs in different folders in same app 2012-10-22 22:52:40 -05:00
mdipierro 0eab35842d THREAD_LOCAL folder not reset in dal 2012-10-22 22:49:35 -05:00
mdipierro 752bb7e048 portable languages 2012-10-22 22:21:11 -05:00
mdipierro 9e897dcc46 language changes 2012-10-22 22:14:40 -05:00
mdipierro 2bb3485827 minor fix in dal for rare condition 2012-10-22 18:50:18 -05:00
mdipierro 0be4abe9c2 minor fix in dal for rare condition 2012-10-22 15:07:38 -05:00
mdipierro 37bc09169f R-2.2.1 2012-10-21 10:54:13 -05:00
mdipierro 1cc2decfdd assign_task takes better care for group_names, thanks Niphlod 2012-10-21 09:23:26 -05:00
mdipierro e9eb1689e2 fixed a problem with capitalization in wiki 2012-10-20 19:38:54 -05:00
mdipierro 0f446833c0 assign_task takes better care for group_names, thanks Niphlod 2012-10-20 15:27:13 -05:00
mdipierro cb9c2a8ef9 fixed issue 1102, thanks howesc 2012-10-20 10:12:04 -05:00
mdipierro b37ed1c1e0 fixed issue 1109, thanks Niphlod 2012-10-20 10:11:08 -05:00
mdipierro b484955005 fixed issue 1100, thanks sherdim 2012-10-20 10:08:58 -05:00
mdipierro 654626d519 removed print statements from scheduler, thanks Niphlod 2012-10-20 10:03:57 -05:00
mdipierro ca0313c514 back button in mobile admin demo 2012-10-19 18:43:14 -05:00
mdipierro fb357eb241 fixed problem with new importer and custom import 2012-10-19 18:36:39 -05:00
mdipierro 1a83487221 populate fix and pep8 2012-10-19 14:59:30 -05:00
mdipierro 6cda7a29fc pep8 in scripts/*.py 2012-10-19 14:22:42 -05:00
mdipierro 17f495e9c5 fixed pep8 in apps 2012-10-19 14:13:37 -05:00
mdipierro dd32a90844 upgraded feedparer and PyRSS2Gen 2012-10-19 12:59:58 -05:00
mdipierro 4b2ba185ae many pep8 improvements 2012-10-19 12:33:53 -05:00
mdipierro b716df1a05 some pep8 changes (fixed spacing at end) 2012-10-19 10:37:07 -05:00
mdipierro d552eb2eeb fixed some pep8 stuff 2012-10-19 09:40:17 -05:00
mdipierro de2337dfe3 scheduler.queue_task, thank Niphlod 2012-10-18 18:20:51 -05:00
mdipierro e707cfc67c codemirror autoresize 2012-10-18 13:42:49 -05:00
mdipierro c3c5df6394 scheduler.queue_task(...), thanks Niphlod 2012-10-18 13:02:19 -05:00
mdipierro 72739ebb97 better openshift error 2012-10-17 17:00:50 -05:00
mdipierro 39c94b8dcf faster scheduler, thanks Niphlod 2012-10-17 16:01:29 -05:00
mdipierro 5af380223a remove excessive session saving with caching of mobile_agent 2012-10-17 14:33:52 -05:00
mdipierro 1ea379180a fixed appadmin bug and excessive session saving 2012-10-17 14:30:02 -05:00
mdipierro ca3d050a0b fixed problem with session in cookie, thanks Niphlod 2012-10-17 12:04:19 -05:00
mdipierro 161b2271b3 T.is_writable, thanks Fran 2012-10-17 09:13:48 -05:00
Massimo 013c94cdf6 inlined sluggify 2012-10-16 11:47:16 -05:00
Massimo addbb78151 customizable session cookie expriration 2012-10-16 11:23:41 -05:00
Massimo f6789eaf39 session cookie data no expires by default 2012-10-16 11:14:52 -05:00
Massimo c13b4f18f7 fixed auth.impersonating again, thanks Ricardo 2012-10-16 11:08:42 -05:00
Massimo 67840e9481 current.T.is_writable=False no languages write, thanks Fran 2012-10-16 10:54:33 -05:00
Massimo 41c40974a3 fixed issue 1092, thanks nicozanf 2012-10-16 10:34:36 -05:00
Massimo 9a6ce11f09 users can switch between sessions storage types 2012-10-16 10:25:28 -05:00
mdipierro d404e40508 improved session data in cookie 2012-10-16 07:44:22 -05:00
mdipierro b6a496aae5 improved session data in cookie 2012-10-16 07:10:33 -05:00
mdipierro ecaed07a3d fixed bug with auth.impersonate(0) 2012-10-16 06:19:39 -05:00
mdipierro 30ec8e645b sessions in secure cookies, session.connect(cookie_key='mypassword') 2012-10-16 06:15:45 -05:00
mdipierro 3a1ba53a4b reverting some test but session.__hash needs work 2012-10-15 21:49:43 -05:00
mdipierro ab95dfa7cd fixed problem with session __hash 2012-10-15 20:43:51 -05:00
mdipierro 6b9ebb6dc5 fixed issue with storing Row in session 2012-10-15 19:39:25 -05:00
mdipierro e2e843d2ed more languages patches, thanks Vladyslav 2012-10-15 16:28:51 -05:00
mdipierro d5381d7b36 fixed make pip 2012-10-15 14:48:45 -05:00
mdipierro f7c0f0341b who page 2012-10-15 14:44:04 -05:00
mdipierro 9f7fd68728 better markmin with links in links, thanks Vladyslav 2012-10-15 07:43:23 -05:00
236 changed files with 14056 additions and 10104 deletions
+37
View File
@@ -1,3 +1,40 @@
## 2.3.1 - 2.3.2
- new virtual fields syntax:
``db.define_table('person',Field('name'),Field.Virtual('namey',lambda row: row.person.name+'y'))``
- db.thing(name='Cohen',_orderby=db.thing.name), thanks Yair
- made many modules Python 3.3 friendly (compile but not tested)
- better welcome css, thanks Paolo
- jQuery 1.8.3
- Bootstrap 2.2.2
- Modernizr 2.6.2 (custom full options)
- integration with analyitics.js (0.2.0)
- better scheduler, thanks Niphlod
- page and media preview in wiki, thanks Niphlod
- create new auth.wiki page from slug model, thanks Nico
- conditional menus with auth.wiki(menugroups=['wiki_editor'])
- better security in grid/smartgrid
- allow LOADing multiple grids, thanks Niphlod
- auth.settings.login_onfail, thanks Yair
- better handling of session files for speed
- added heroku support (experimental)
- added rocket support for IPV6, thanks Chirs Winebrinner
- more customizable menus with MENU(li_first, li_last..)
- added support for paymentech (gluon/contrib/paymentech.py)
- fixed broken cron
- fixed possible xss with share.js
- many bug fixes. Closed more than 50 tickets since 2.2.1
## 2.2.1
- session.connect(cookie_key='secret', compression_level=9) stores sessions in cookies
- T.is_writable = False prevents T from dynamically updating langauge files
- all code is more PEP8 compliant
- better custom_importer behaviour (now works per app, is smalled and faster)
- fixed some bugs
- upgraded feedparser.py and rss2.py
- codemirror has autoresize
## 2.1.0 ## 2.1.0
- overall faster web2py - overall faster web2py
+5 -4
View File
@@ -29,7 +29,7 @@ update:
wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py wget -O gluon/contrib/simplejsonrpc.py http://rad2py.googlecode.com/hg/ide2py/simplejsonrpc.py
echo "remember that pymysql was tweaked" echo "remember that pymysql was tweaked"
src: src:
echo 'Version 2.1.1 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION echo 'Version 2.3.2 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION
### rm -f all junk files ### rm -f all junk files
make clean make clean
### clean up baisc apps ### clean up baisc apps
@@ -43,6 +43,8 @@ src:
rm -f applications/admin/uploads/* rm -f applications/admin/uploads/*
rm -f applications/welcome/uploads/* rm -f applications/welcome/uploads/*
rm -f applications/examples/uploads/* rm -f applications/examples/uploads/*
### make epydoc
make epydoc
### make welcome layout and appadmin the default ### make welcome layout and appadmin the default
cp applications/welcome/views/appadmin.html applications/admin/views cp applications/welcome/views/appadmin.html applications/admin/views
cp applications/welcome/views/appadmin.html applications/examples/views cp applications/welcome/views/appadmin.html applications/examples/views
@@ -54,7 +56,6 @@ src:
cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py_exe.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run startweb2py web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py cd ..; zip -r web2py/web2py_src.zip web2py/gluon/*.py web2py/gluon/contrib/* web2py/splashlogo.gif web2py/*.py web2py/README.markdown web2py/LICENSE web2py/CHANGELOG web2py/NEWINSTALL web2py/VERSION web2py/Makefile web2py/epydoc.css web2py/epydoc.conf web2py/app.example.yaml web2py/logging.example.conf web2py_exe.conf web2py/queue.example.yaml MANIFEST.in w2p_apps w2p_clone w2p_run startweb2py web2py/scripts/*.sh web2py/scripts/*.py web2py/applications/admin web2py/applications/examples/ web2py/applications/welcome web2py/applications/__init__.py web2py/site-packages/__init__.py web2py/gluon/tests/*.sh web2py/gluon/tests/*.py
mdp: mdp:
make epydoc
make src make src
make app make app
make win make win
@@ -130,5 +131,5 @@ pip:
# after upload clean Web2py sources with rm -R ./dist # after upload clean Web2py sources with rm -R ./dist
# http://guide.python-distribute.org/creation.html # http://guide.python-distribute.org/creation.html
python setup.py sdist python setup.py sdist
python setup.py register sudo python setup.py register
python setup.py sdist upload sudo python setup.py sdist upload
+1
View File
@@ -0,0 +1 @@
+1 -1
View File
@@ -1 +1 @@
Version 2.1.1 (2012-10-15 06:40:52) stable Version 2.3.2 (2012-12-17 08:59:58) stable
-10
View File
@@ -1,10 +0,0 @@
+72 -53
View File
@@ -9,47 +9,54 @@ License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
This file is based, although a rewrite, on MIT-licensed code from the Bottle web framework. This file is based, although a rewrite, on MIT-licensed code from the Bottle web framework.
""" """
import os, sys, optparse, urllib import os
import sys
import optparse
import urllib
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
os.chdir(path) os.chdir(path)
sys.path = [path]+[p for p in sys.path if not p==path] sys.path = [path] + [p for p in sys.path if not p == path]
import gluon.main import gluon.main
from gluon.fileutils import read_file, write_file from gluon.fileutils import read_file, write_file
class Servers: class Servers:
@staticmethod @staticmethod
def cgi(app, address=None, **options): def cgi(app, address=None, **options):
from wsgiref.handlers import CGIHandler from wsgiref.handlers import CGIHandler
CGIHandler().run(app) # Just ignore host and port here CGIHandler().run(app) # Just ignore host and port here
@staticmethod @staticmethod
def flup(app,address, **options): def flup(app, address, **options):
import flup.server.fcgi import flup.server.fcgi
flup.server.fcgi.WSGIServer(app, bindAddress=address).run() flup.server.fcgi.WSGIServer(app, bindAddress=address).run()
@staticmethod @staticmethod
def wsgiref(app,address,**options): # pragma: no cover def wsgiref(app, address, **options): # pragma: no cover
from wsgiref.simple_server import make_server, WSGIRequestHandler from wsgiref.simple_server import make_server, WSGIRequestHandler
class QuietHandler(WSGIRequestHandler): class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass def log_request(*args, **kw):
pass
options['handler_class'] = QuietHandler options['handler_class'] = QuietHandler
srv = make_server(address[0],address[1],app,**options) srv = make_server(address[0], address[1], app, **options)
srv.serve_forever() srv.serve_forever()
@staticmethod @staticmethod
def cherrypy(app,address, **options): def cherrypy(app, address, **options):
from cherrypy import wsgiserver from cherrypy import wsgiserver
server = wsgiserver.CherryPyWSGIServer(address, app) server = wsgiserver.CherryPyWSGIServer(address, app)
server.start() server.start()
@staticmethod @staticmethod
def rocket(app,address, **options): def rocket(app, address, **options):
from gluon.rocket import CherryPyWSGIServer from gluon.rocket import CherryPyWSGIServer
server = CherryPyWSGIServer(address, app) server = CherryPyWSGIServer(address, app)
server.start() server.start()
@staticmethod @staticmethod
def rocket_with_repoze_profiler(app,address, **options): def rocket_with_repoze_profiler(app, address, **options):
from gluon.rocket import CherryPyWSGIServer from gluon.rocket import CherryPyWSGIServer
from repoze.profile.profiler import AccumulatingProfileMiddleware from repoze.profile.profiler import AccumulatingProfileMiddleware
from gluon.settings import global_settings from gluon.settings import global_settings
@@ -59,44 +66,46 @@ class Servers:
log_filename='wsgi.prof', log_filename='wsgi.prof',
discard_first_request=True, discard_first_request=True,
flush_at_shutdown=True, flush_at_shutdown=True,
path = '/__profile__' path='/__profile__'
) )
server = CherryPyWSGIServer(address, wrapped) server = CherryPyWSGIServer(address, wrapped)
server.start() server.start()
@staticmethod @staticmethod
def paste(app,address,**options): def paste(app, address, **options):
from paste import httpserver from paste import httpserver
from paste.translogger import TransLogger from paste.translogger import TransLogger
httpserver.serve(app, host=address[0], port=address[1], **options) httpserver.serve(app, host=address[0], port=address[1], **options)
@staticmethod @staticmethod
def fapws(app,address, **options): def fapws(app, address, **options):
import fapws._evwsgi as evwsgi import fapws._evwsgi as evwsgi
from fapws import base from fapws import base
evwsgi.start(address[0],str(address[1])) evwsgi.start(address[0], str(address[1]))
evwsgi.set_base_module(base) evwsgi.set_base_module(base)
def app(environ, start_response): def app(environ, start_response):
environ['wsgi.multiprocess'] = False environ['wsgi.multiprocess'] = False
return app(environ, start_response) return app(environ, start_response)
evwsgi.wsgi_cb(('',app)) evwsgi.wsgi_cb(('', app))
evwsgi.run() evwsgi.run()
@staticmethod @staticmethod
def gevent(app,address, **options): def gevent(app, address, **options):
from gevent import monkey; monkey.patch_all() from gevent import monkey
monkey.patch_all()
from gevent import pywsgi from gevent import pywsgi
from gevent.pool import Pool from gevent.pool import Pool
pywsgi.WSGIServer(address, app, spawn = 'workers' in options and Pool(int(options.workers)) or 'default').serve_forever() pywsgi.WSGIServer(address, app, spawn='workers' in options and Pool(
int(options.workers)) or 'default').serve_forever()
@staticmethod @staticmethod
def bjoern(app,address, **options): def bjoern(app, address, **options):
import bjoern import bjoern
bjoern.run(app, *address) bjoern.run(app, *address)
@staticmethod @staticmethod
def tornado(app,address, **options): def tornado(app, address, **options):
import tornado.wsgi import tornado.wsgi
import tornado.httpserver import tornado.httpserver
import tornado.ioloop import tornado.ioloop
@@ -106,7 +115,7 @@ class Servers:
tornado.ioloop.IOLoop.instance().start() tornado.ioloop.IOLoop.instance().start()
@staticmethod @staticmethod
def twisted(app,address, **options): def twisted(app, address, **options):
from twisted.web import server, wsgi from twisted.web import server, wsgi
from twisted.python.threadpool import ThreadPool from twisted.python.threadpool import ThreadPool
from twisted.internet import reactor from twisted.internet import reactor
@@ -118,42 +127,44 @@ class Servers:
reactor.run() reactor.run()
@staticmethod @staticmethod
def diesel(app,address, **options): def diesel(app, address, **options):
from diesel.protocols.wsgi import WSGIApplication from diesel.protocols.wsgi import WSGIApplication
app = WSGIApplication(app, port=address[1]) app = WSGIApplication(app, port=address[1])
app.run() app.run()
@staticmethod @staticmethod
def gunicorn(app,address, **options): def gunicorn(app, address, **options):
from gunicorn.app.base import Application from gunicorn.app.base import Application
config = {'bind': "%s:%d" % address} config = {'bind': "%s:%d" % address}
config.update(options) config.update(options)
sys.argv = ['anyserver.py'] sys.argv = ['anyserver.py']
class GunicornApplication(Application): class GunicornApplication(Application):
def init(self, parser, opts, args): def init(self, parser, opts, args):
return config return config
def load(self): def load(self):
return app return app
g = GunicornApplication() g = GunicornApplication()
g.run() g.run()
@staticmethod @staticmethod
def eventlet(app,address, **options): def eventlet(app, address, **options):
from eventlet import wsgi, listen from eventlet import wsgi, listen
wsgi.server(listen(address), app) wsgi.server(listen(address), app)
@staticmethod @staticmethod
def mongrel2(app,address,**options): def mongrel2(app, address, **options):
import uuid import uuid
sys.path.append(os.path.abspath(os.path.dirname(__file__))) sys.path.append(os.path.abspath(os.path.dirname(__file__)))
from mongrel2 import handler from mongrel2 import handler
conn = handler.Connection(str(uuid.uuid4()), conn = handler.Connection(str(uuid.uuid4()),
"tcp://127.0.0.1:9997", "tcp://127.0.0.1:9997",
"tcp://127.0.0.1:9996") "tcp://127.0.0.1:9996")
mongrel2_handler(app,conn,debug=False) mongrel2_handler(app, conn, debug=False)
def run(servername,ip,port,softcron=True,logging=False,profiler=None): def run(servername, ip, port, softcron=True, logging=False, profiler=None):
if logging: if logging:
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase, application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
logfilename='httpserver.log', logfilename='httpserver.log',
@@ -163,9 +174,10 @@ def run(servername,ip,port,softcron=True,logging=False,profiler=None):
if softcron: if softcron:
from gluon.settings import global_settings from gluon.settings import global_settings
global_settings.web2py_crontype = 'soft' global_settings.web2py_crontype = 'soft'
getattr(Servers,servername)(application,(ip,int(port))) getattr(Servers, servername)(application, (ip, int(port)))
def mongrel2_handler(application,conn,debug=False):
def mongrel2_handler(application, conn, debug=False):
""" """
Based on : Based on :
https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py https://github.com/berry/Mongrel2-WSGI-Handler/blob/master/wsgi-handler.py
@@ -190,20 +202,23 @@ def mongrel2_handler(application,conn,debug=False):
# and responses. Unless I have missed something. # and responses. Unless I have missed something.
while True: while True:
if debug: print "WAITING FOR REQUEST" if debug:
print "WAITING FOR REQUEST"
# receive a request # receive a request
req = conn.recv() req = conn.recv()
if debug: print "REQUEST BODY: %r\n" % req.body if debug:
print "REQUEST BODY: %r\n" % req.body
if req.is_disconnect(): if req.is_disconnect():
if debug: print "DISCONNECT" if debug:
continue #effectively ignore the disconnect from the client print "DISCONNECT"
continue # effectively ignore the disconnect from the client
# Set a couple of environment attributes a.k.a. header attributes # Set a couple of environment attributes a.k.a. header attributes
# that are a must according to PEP 333 # that are a must according to PEP 333
environ = req.headers environ = req.headers
environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1 environ['SERVER_PROTOCOL'] = 'HTTP/1.1' # SimpleHandler expects a server_protocol, lets assume it is HTTP 1.1
environ['REQUEST_METHOD'] = environ['METHOD'] environ['REQUEST_METHOD'] = environ['METHOD']
if ':' in environ['Host']: if ':' in environ['Host']:
environ['SERVER_NAME'] = environ['Host'].split(':')[0] environ['SERVER_NAME'] = environ['Host'].split(':')[0]
@@ -211,17 +226,19 @@ def mongrel2_handler(application,conn,debug=False):
else: else:
environ['SERVER_NAME'] = environ['Host'] environ['SERVER_NAME'] = environ['Host']
environ['SERVER_PORT'] = '' environ['SERVER_PORT'] = ''
environ['SCRIPT_NAME'] = '' # empty for now environ['SCRIPT_NAME'] = '' # empty for now
environ['PATH_INFO'] = urllib.unquote(environ['PATH']) environ['PATH_INFO'] = urllib.unquote(environ['PATH'])
if '?' in environ['URI']: if '?' in environ['URI']:
environ['QUERY_STRING'] = environ['URI'].split('?')[1] environ['QUERY_STRING'] = environ['URI'].split('?')[1]
else: else:
environ['QUERY_STRING'] = '' environ['QUERY_STRING'] = ''
if environ.has_key('Content-Length'): if 'Content-Length' in environ:
environ['CONTENT_LENGTH'] = environ['Content-Length'] # necessary for POST to work with Django environ['CONTENT_LENGTH'] = environ[
'Content-Length'] # necessary for POST to work with Django
environ['wsgi.input'] = req.body environ['wsgi.input'] = req.body
if debug: print "ENVIRON: %r\n" % environ if debug:
print "ENVIRON: %r\n" % environ
# SimpleHandler needs file-like stream objects for # SimpleHandler needs file-like stream objects for
# requests, errors and responses # requests, errors and responses
@@ -230,7 +247,8 @@ def mongrel2_handler(application,conn,debug=False):
respIO = StringIO.StringIO() respIO = StringIO.StringIO()
# execute the application # execute the application
handler = SimpleHandler(reqIO, respIO, errIO, environ, multithread = False, multiprocess = False) handler = SimpleHandler(reqIO, respIO, errIO, environ,
multithread=False, multiprocess=False)
handler.run(application) handler.run(application)
# Get the response and filter out the response (=data) itself, # Get the response and filter out the response (=data) itself,
@@ -254,11 +272,15 @@ def mongrel2_handler(application,conn,debug=False):
errors = errIO.getvalue() errors = errIO.getvalue()
# return the response # return the response
if debug: print "RESPONSE: %r\n" % response if debug:
print "RESPONSE: %r\n" % response
if errors: if errors:
if debug: print "ERRORS: %r" % errors if debug:
print "ERRORS: %r" % errors
data = "%s\r\n\r\n%s" % (data, errors) data = "%s\r\n\r\n%s" % (data, errors)
conn.reply_http(req, data, code = code, status = status, headers = headers) conn.reply_http(
req, data, code=code, status=status, headers=headers)
def main(): def main():
usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P" usage = "python anyserver.py -s tornado -i 127.0.0.1 -p 8000 -l -P"
@@ -278,7 +300,7 @@ def main():
default=False, default=False,
dest='profiler', dest='profiler',
help='profiler filename') help='profiler filename')
servers = ', '.join(x for x in dir(Servers) if not x[0]=='_') servers = ', '.join(x for x in dir(Servers) if not x[0] == '_')
parser.add_option('-s', parser.add_option('-s',
'--server', '--server',
default='rocket', default='rocket',
@@ -300,13 +322,10 @@ def main():
dest='workers', dest='workers',
help='number of workers number') help='number of workers number')
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
print 'starting %s on %s:%s...' % (options.server,options.ip,options.port) print 'starting %s on %s:%s...' % (
run(options.server,options.ip,options.port,logging=options.logging,profiler=options.profiler) options.server, options.ip, options.port)
run(options.server, options.ip, options.port,
logging=options.logging, profiler=options.profiler)
if __name__=='__main__': if __name__ == '__main__':
main() main()
@@ -2,8 +2,3 @@ def webapp_add_wsgi_middleware(app):
from google.appengine.ext.appstats import recording from google.appengine.ext.appstats import recording
app = recording.appstats_wsgi_middleware(app) app = recording.appstats_wsgi_middleware(app)
return app return app
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
+71 -54
View File
@@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr
try: try:
hosts = (http_host, socket.gethostname(), hosts = (http_host, socket.gethostname(),
socket.gethostbyname(http_host), socket.gethostbyname(http_host),
'::1','127.0.0.1','::ffff:127.0.0.1') '::1', '127.0.0.1', '::ffff:127.0.0.1')
except: except:
hosts = (http_host, ) hosts = (http_host, )
@@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https:
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
raise HTTP(200, T('appadmin is disabled because insecure channel')) raise HTTP(200, T('appadmin is disabled because insecure channel'))
if (request.application=='admin' and not session.authorized) or \ if (request.application == 'admin' and not session.authorized) or \
(request.application!='admin' and not gluon.fileutils.check_credentials(request)): (request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
redirect(URL('admin', 'default', 'index', redirect(URL('admin', 'default', 'index',
vars=dict(send=URL(args=request.args,vars=request.vars)))) vars=dict(send=URL(args=request.args, vars=request.vars))))
ignore_rw = True ignore_rw = True
response.view = 'appadmin.html' response.view = 'appadmin.html'
@@ -95,24 +95,23 @@ def get_query(request):
return None return None
def query_by_table_type(tablename,db,request=request): def query_by_table_type(tablename, db, request=request):
keyed = hasattr(db[tablename],'_primarykey') keyed = hasattr(db[tablename], '_primarykey')
if keyed: if keyed:
firstkey = db[tablename][db[tablename]._primarykey[0]] firstkey = db[tablename][db[tablename]._primarykey[0]]
cond = '>0' cond = '>0'
if firstkey.type in ['string', 'text']: if firstkey.type in ['string', 'text']:
cond = '!=""' cond = '!=""'
qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) qry = '%s.%s.%s%s' % (
request.args[0], request.args[1], firstkey.name, cond)
else: else:
qry = '%s.%s.id>0' % tuple(request.args[:2]) qry = '%s.%s.id>0' % tuple(request.args[:2])
return qry return qry
# ########################################################## # ##########################################################
# ## list all databases and tables # ## list all databases and tables
# ########################################################### # ###########################################################
def index(): def index():
return dict(databases=databases) return dict(databases=databases)
@@ -127,7 +126,7 @@ def insert():
form = SQLFORM(db[table], ignore_rw=ignore_rw) form = SQLFORM(db[table], ignore_rw=ignore_rw)
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
response.flash = T('new record inserted') response.flash = T('new record inserted')
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -138,7 +137,8 @@ def insert():
def download(): def download():
import os import os
db = get_database(request) db = get_database(request)
return response.download(request,db) return response.download(request, db)
def csv(): def csv():
import gluon.contenttype import gluon.contenttype
@@ -149,26 +149,27 @@ def csv():
if not query: if not query:
return None return None
response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\
% tuple(request.vars.query.split('.')[:2]) % tuple(request.vars.query.split('.')[:2])
return str(db(query,ignore_common_filters=True).select()) return str(db(query, ignore_common_filters=True).select())
def import_csv(table, file): def import_csv(table, file):
table.import_from_csv_file(file) table.import_from_csv_file(file)
def select(): def select():
import re import re
db = get_database(request) db = get_database(request)
dbname = request.args[0] dbname = request.args[0]
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'):
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)')
if request.vars.query: if request.vars.query:
match = regex.match(request.vars.query) match = regex.match(request.vars.query)
if match: if match:
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else: else:
request.vars.query = session.last_query request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
@@ -192,46 +193,50 @@ def select():
session.last_query = request.vars.query session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _name='query', _value=request.vars.query or '',
requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox', INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px', value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields _name='update_fields', _value=request.vars.update_fields
or '')), TR(T('Delete:'), INPUT(_name='delete_check', or '')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''), _class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit')))), TR('', '', INPUT(_type='submit', _value=T('submit')))),
_action=URL(r=request,args=request.args)) _action=URL(r=request, args=request.args))
tb = None
if form.accepts(request.vars, formname=None): if form.accepts(request.vars, formname=None):
regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+') regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+')
match = regex.match(form.vars.query.strip()) match = regex.match(form.vars.query.strip())
if match: if match:
table = match.group('table') table = match.group('table')
try: try:
tb = None
nrows = db(query).count() nrows = db(query).count()
if form.vars.update_check and form.vars.update_fields: if form.vars.update_check and form.vars.update_fields:
db(query).update(**eval_in_global_env('dict(%s)' db(query).update(**eval_in_global_env('dict(%s)'
% form.vars.update_fields)) % form.vars.update_fields))
response.flash = T('%s %%{row} updated', nrows) response.flash = T('%s %%{row} updated', nrows)
elif form.vars.delete_check: elif form.vars.delete_check:
db(query).delete() db(query).delete()
response.flash = T('%s %%{row} deleted', nrows) response.flash = T('%s %%{row} deleted', nrows)
nrows = db(query).count() nrows = db(query).count()
if orderby: if orderby:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) rows = db(query, ignore_common_filters=True).select(limitby=(
start, stop), orderby=eval_in_global_env(orderby))
else: else:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) rows = db(query, ignore_common_filters=True).select(
limitby=(start, stop))
except Exception, e: except Exception, e:
import traceback import traceback
tb = traceback.format_exc() tb = traceback.format_exc()
(rows, nrows) = ([], 0) (rows, nrows) = ([], 0)
response.flash = DIV(T('Invalid Query'),PRE(str(e))) response.flash = DIV(T('Invalid Query'), PRE(str(e)))
# begin handle upload csv # begin handle upload csv
csv_table = table or request.vars.table csv_table = table or request.vars.table
if csv_table: if csv_table:
formcsv = FORM(str(T('or import from csv file'))+" ", formcsv = FORM(str(T('or import from csv file')) + " ",
INPUT(_type='file',_name='csvfile'), INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden',_value=csv_table,_name='table'), INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit',_value=T('import'))) INPUT(_type='submit', _value=T('import')))
else: else:
formcsv = None formcsv = None
if formcsv and formcsv.process().accepted: if formcsv and formcsv.process().accepted:
@@ -240,7 +245,7 @@ def select():
request.vars.csvfile.file) request.vars.csvfile.file)
response.flash = T('data uploaded') response.flash = T('data uploaded')
except Exception, e: except Exception, e:
response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) response.flash = DIV(T('unable to parse csv file'), PRE(str(e)))
# end handle upload csv # end handle upload csv
return dict( return dict(
@@ -251,9 +256,9 @@ def select():
nrows=nrows, nrows=nrows,
rows=rows, rows=rows,
query=request.vars.query, query=request.vars.query,
formcsv = formcsv, formcsv=formcsv,
tb = tb, tb=tb,
) )
# ########################################################## # ##########################################################
@@ -263,14 +268,16 @@ def select():
def update(): def update():
(db, table) = get_table(request) (db, table) = get_table(request)
keyed = hasattr(db[table],'_primarykey') keyed = hasattr(db[table], '_primarykey')
record = None record = None
if keyed: if keyed:
key = [f for f in request.vars if f in db[table]._primarykey] key = [f for f in request.vars if f in db[table]._primarykey]
if key: if key:
record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() record = db(db[table][key[0]] == request.vars[key[
0]], ignore_common_filters=True).select().first()
else: else:
record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() record = db(db[table].id == request.args(
2), ignore_common_filters=True).select().first()
if not record: if not record:
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
@@ -280,20 +287,21 @@ def update():
if keyed: if keyed:
for k in db[table]._primarykey: for k in db[table]._primarykey:
db[table][k].writable=False db[table][k].writable = False
form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), form = SQLFORM(
ignore_rw=ignore_rw and not keyed, db[table], record, deletable=True, delete_label=T('Check to delete'),
linkto=URL('select', ignore_rw=ignore_rw and not keyed,
linkto=URL('select',
args=request.args[:1]), upload=URL(r=request, args=request.args[:1]), upload=URL(r=request,
f='download', args=request.args[:1])) f='download', args=request.args[:1]))
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
session.flash = T('done!') session.flash = T('done!')
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
redirect(URL('select', args=request.args[:1], redirect(URL('select', args=request.args[:1],
vars=dict(query=qry))) vars=dict(query=qry)))
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -304,11 +312,15 @@ def update():
def state(): def state():
return dict() return dict()
def ccache(): def ccache():
form = FORM( form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), P(TAG.BUTTON(
P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
) )
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
@@ -332,11 +344,16 @@ def ccache():
redirect(URL(r=request)) redirect(URL(r=request))
try: try:
from guppy import hpy; hp=hpy() from guppy import hpy
hp = hpy()
except ImportError: except ImportError:
hp = False hp = False
import shelve, os, copy, time, math import shelve
import os
import copy
import time
import math
from gluon import portalocker from gluon import portalocker
ram = { ram = {
@@ -379,11 +396,13 @@ def ccache():
if value[0] < ram['oldest']: if value[0] < ram['oldest']:
ram['oldest'] = value[0] ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
folder = os.path.join(request.folder,'cache')
locker = open(os.path.join(request.folder, if not os.path.exists(folder):
'cache/cache.lock'), 'a') os.mkdir(folder)
locker = open(os.path.join(folder, 'cache.lock'), 'a')
portalocker.lock(locker, portalocker.LOCK_EX) portalocker.lock(locker, portalocker.LOCK_EX)
disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) disk_storage = shelve.open(
os.path.join(folder, 'cache.shelve'))
try: try:
for key, value in disk_storage.items(): for key, value in disk_storage.items():
if isinstance(value, dict): if isinstance(value, dict):
@@ -414,7 +433,8 @@ def ccache():
total['misses'] = ram['misses'] + disk['misses'] total['misses'] = ram['misses'] + disk['misses']
total['keys'] = ram['keys'] + disk['keys'] total['keys'] = ram['keys'] + disk['keys']
try: try:
total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])
except (KeyError, ZeroDivisionError): except (KeyError, ZeroDivisionError):
total['ratio'] = 0 total['ratio'] = 0
@@ -440,6 +460,3 @@ def ccache():
return dict(form=form, total=total, return dict(form=form, total=total,
ram=ram, disk=disk, object_stats=hp != False) ram=ram, disk=disk, object_stats=hp != False)
+39 -26
View File
@@ -5,35 +5,39 @@ import gluon.contrib.shell
import gluon.dal import gluon.dal
import gluon.html import gluon.html
import gluon.validators import gluon.validators
import code, thread import code
import thread
from gluon.debug import communicate, web_debugger, qdb_debugger from gluon.debug import communicate, web_debugger, qdb_debugger
import pydoc import pydoc
if DEMO_MODE or MULTI_USER_MODE: if DEMO_MODE or MULTI_USER_MODE:
session.flash = T('disabled in demo mode') session.flash = T('disabled in demo mode')
redirect(URL('default','site')) redirect(URL('default', 'site'))
FE = 10 ** 9
FE=10**9
def index(): def index():
app = request.args(0) or 'admin' app = request.args(0) or 'admin'
reset() reset()
# read buffer # read buffer
data = communicate() data = communicate()
return dict(app=app,data=data) return dict(app=app, data=data)
def callback(): def callback():
app = request.args[0] app = request.args[0]
command = request.vars.statement command = request.vars.statement
session['debug_commands:'+app].append(command) session['debug_commands:' + app].append(command)
output = communicate(command) output = communicate(command)
k = len(session['debug_commands:'+app]) - 1 k = len(session['debug_commands:' + app]) - 1
return '[%i] %s%s\n' % (k + 1, command, output) return '[%i] %s%s\n' % (k + 1, command, output)
def reset(): def reset():
app = request.args(0) or 'admin' app = request.args(0) or 'admin'
session['debug_commands:'+app] = [] session['debug_commands:' + app] = []
return 'done' return 'done'
@@ -50,9 +54,9 @@ def interact():
filename = web_debugger.filename filename = web_debugger.filename
lineno = web_debugger.lineno lineno = web_debugger.lineno
if filename: if filename:
lines = dict([(i+1, l) for (i, l) in enumerate( lines = dict([(i + 1, l) for (i, l) in enumerate(
[l.strip("\n").strip("\r") for l [l.strip("\n").strip("\r") for l
in open(filename).readlines()])]) in open(filename).readlines()])])
filename = os.path.basename(filename) filename = os.path.basename(filename)
else: else:
lines = {} lines = {}
@@ -64,8 +68,8 @@ def interact():
f_globals = {} f_globals = {}
for name, value in env['globals'].items(): for name, value in env['globals'].items():
if name not in gluon.html.__all__ and \ if name not in gluon.html.__all__ and \
name not in gluon.validators.__all__ and \ name not in gluon.validators.__all__ and \
name not in gluon.dal.__all__: name not in gluon.dal.__all__:
f_globals[name] = pydoc.text.repr(value) f_globals[name] = pydoc.text.repr(value)
else: else:
f_locals = {} f_locals = {}
@@ -81,37 +85,43 @@ def interact():
f_globals=f_globals, f_locals=f_locals, f_globals=f_globals, f_locals=f_locals,
exception=web_debugger.exception_info) exception=web_debugger.exception_info)
def step(): def step():
web_debugger.do_step() web_debugger.do_step()
redirect(URL("interact")) redirect(URL("interact"))
def next(): def next():
web_debugger.do_next() web_debugger.do_next()
redirect(URL("interact")) redirect(URL("interact"))
def cont(): def cont():
web_debugger.do_continue() web_debugger.do_continue()
redirect(URL("interact")) redirect(URL("interact"))
def ret(): def ret():
web_debugger.do_return() web_debugger.do_return()
redirect(URL("interact")) redirect(URL("interact"))
def stop(): def stop():
web_debugger.do_quit() web_debugger.do_quit()
redirect(URL("interact")) redirect(URL("interact"))
def execute(): def execute():
app = request.args[0] app = request.args[0]
command = request.vars.statement command = request.vars.statement
session['debug_commands:'+app].append(command) session['debug_commands:' + app].append(command)
try: try:
output = web_debugger.do_exec(command) output = web_debugger.do_exec(command)
if output is None: if output is None:
output = "" output = ""
except Exception, e: except Exception, e:
output = T("Exception %s") % str(e) output = T("Exception %s") % str(e)
k = len(session['debug_commands:'+app]) - 1 k = len(session['debug_commands:' + app]) - 1
return '[%i] %s%s\n' % (k + 1, command, output) return '[%i] %s%s\n' % (k + 1, command, output)
@@ -122,8 +132,8 @@ def breakpoints():
files = listdir(apath('', r=request), '.*\.py$') files = listdir(apath('', r=request), '.*\.py$')
files = [filename for filename in files files = [filename for filename in files
if filename and 'languages' not in filename if filename and 'languages' not in filename
and not filename.startswith("admin") and not filename.startswith("admin")
and not filename.startswith("examples")] and not filename.startswith("examples")]
form = SQLFORM.factory( form = SQLFORM.factory(
Field('filename', requires=IS_IN_SET(files), label=T("Filename")), Field('filename', requires=IS_IN_SET(files), label=T("Filename")),
@@ -133,17 +143,17 @@ def breakpoints():
comment=T("deleted after first hit")), comment=T("deleted after first hit")),
Field('condition', 'string', label=T("Condition"), Field('condition', 'string', label=T("Condition"),
comment=T("honored only if the expression evaluates to true")), comment=T("honored only if the expression evaluates to true")),
) )
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
filename = os.path.join(request.env['applications_parent'], filename = os.path.join(request.env['applications_parent'],
'applications', form.vars.filename) 'applications', form.vars.filename)
err = qdb_debugger.do_set_breakpoint(filename, err = qdb_debugger.do_set_breakpoint(filename,
form.vars.lineno, form.vars.lineno,
form.vars.temporary, form.vars.temporary,
form.vars.condition) form.vars.condition)
response.flash = T("Set Breakpoint on %s at line %s: %s") % ( response.flash = T("Set Breakpoint on %s at line %s: %s") % (
filename, form.vars.lineno, err or T('successful')) filename, form.vars.lineno, err or T('successful'))
for item in request.vars: for item in request.vars:
if item[:7] == 'delete_': if item[:7] == 'delete_':
@@ -153,7 +163,7 @@ def breakpoints():
'path': bp[1], 'lineno': bp[2], 'path': bp[1], 'lineno': bp[2],
'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5], 'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5],
'condition': bp[6]} 'condition': bp[6]}
for bp in qdb_debugger.do_list_breakpoint()] for bp in qdb_debugger.do_list_breakpoint()]
return dict(breakpoints=breakpoints, form=form) return dict(breakpoints=breakpoints, form=form)
@@ -166,6 +176,8 @@ def toggle_breakpoint():
try: try:
filename = os.path.join(request.env['applications_parent'], filename = os.path.join(request.env['applications_parent'],
'applications', request.vars.filename) 'applications', request.vars.filename)
# normalize path name: replace slashes, references, etc...
filename = os.path.normpath(os.path.normcase(filename))
if not request.vars.data: if not request.vars.data:
# ace send us the line number! # ace send us the line number!
lineno = int(request.vars.sel_start) + 1 lineno = int(request.vars.sel_start) + 1
@@ -182,20 +194,21 @@ def toggle_breakpoint():
if lineno is not None: if lineno is not None:
for bp in qdb_debugger.do_list_breakpoint(): for bp in qdb_debugger.do_list_breakpoint():
no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp
# normalize path name: replace slashes, references, etc...
bp_filename = os.path.normpath(os.path.normcase(bp_filename))
if filename == bp_filename and lineno == bp_lineno: if filename == bp_filename and lineno == bp_lineno:
err = qdb_debugger.do_clear_breakpoint(filename, lineno) err = qdb_debugger.do_clear_breakpoint(filename, lineno)
response.flash = T("Removed Breakpoint on %s at line %s", ( response.flash = T("Removed Breakpoint on %s at line %s", (
filename, lineno)) filename, lineno))
ok = False ok = False
break break
else: else:
err = qdb_debugger.do_set_breakpoint(filename, lineno) err = qdb_debugger.do_set_breakpoint(filename, lineno)
response.flash = T("Set Breakpoint on %s at line %s: %s") % ( response.flash = T("Set Breakpoint on %s at line %s: %s") % (
filename, lineno, err or T('successful')) filename, lineno, err or T('successful'))
ok = True ok = True
else: else:
response.flash = T("Unable to determine the line number!") response.flash = T("Unable to determine the line number!")
except Exception, e: except Exception, e:
session.flash = str(e) session.flash = str(e)
return response.json({'ok': ok, 'lineno': lineno}) return response.json({'ok': ok, 'lineno': lineno})
File diff suppressed because it is too large Load Diff
+43 -36
View File
@@ -9,85 +9,92 @@ try:
import shutil import shutil
from gluon.fileutils import read_file, write_file from gluon.fileutils import read_file, write_file
except: except:
session.flash='sorry, only on Unix systems' session.flash = 'sorry, only on Unix systems'
redirect(URL(request.application,'default','site')) redirect(URL(request.application, 'default', 'site'))
if MULTI_USER_MODE and not is_manager(): if MULTI_USER_MODE and not is_manager():
session.flash = 'Not Authorized' session.flash = 'Not Authorized'
redirect(URL('default','site')) redirect(URL('default', 'site'))
forever = 10 ** 8
forever=10**8
def kill(): def kill():
p = cache.ram('gae_upload',lambda:None,forever) p = cache.ram('gae_upload', lambda: None, forever)
if not p or p.poll()!=None: if not p or p.poll() is not None:
return 'oops' return 'oops'
os.kill(p.pid, signal.SIGKILL) os.kill(p.pid, signal.SIGKILL)
cache.ram('gae_upload',lambda:None,-1) cache.ram('gae_upload', lambda: None, -1)
class EXISTS(object): class EXISTS(object):
def __init__(self, error_message='file not found'): def __init__(self, error_message='file not found'):
self.error_message = error_message self.error_message = error_message
def __call__(self, value): def __call__(self, value):
if os.path.exists(value): if os.path.exists(value):
return (value,None) return (value, None)
return (value,self.error_message) return (value, self.error_message)
def deploy(): def deploy():
regex = re.compile('^\w+$') regex = re.compile('^\w+$')
apps = sorted(file for file in os.listdir(apath(r=request)) if regex.match(file)) apps = sorted(
file for file in os.listdir(apath(r=request)) if regex.match(file))
form = SQLFORM.factory( form = SQLFORM.factory(
Field('appcfg',default=GAE_APPCFG,label=T('Path to appcfg.py'), Field('appcfg', default=GAE_APPCFG, label=T('Path to appcfg.py'),
requires=EXISTS(error_message=T('file not found'))), requires=EXISTS(error_message=T('file not found'))),
Field('google_application_id',requires=IS_MATCH('[\w\-]+'),label=T('Google Application Id')), Field('google_application_id', requires=IS_MATCH(
Field('applications','list:string', '[\w\-]+'), label=T('Google Application Id')),
requires=IS_IN_SET(apps,multiple=True), Field('applications', 'list:string',
requires=IS_IN_SET(apps, multiple=True),
label=T('web2py apps to deploy')), label=T('web2py apps to deploy')),
Field('email',requires=IS_EMAIL(),label=T('GAE Email')), Field('email', requires=IS_EMAIL(), label=T('GAE Email')),
Field('password','password',requires=IS_NOT_EMPTY(),label=T('GAE Password'))) Field('password', 'password', requires=IS_NOT_EMPTY(), label=T('GAE Password')))
cmd = output = errors= "" cmd = output = errors = ""
if form.accepts(request,session): if form.accepts(request, session):
try: try:
kill() kill()
except: except:
pass pass
ignore_apps = [item for item in apps \ ignore_apps = [item for item in apps
if not item in form.vars.applications] if not item in form.vars.applications]
regex = re.compile('\(applications/\(.*') regex = re.compile('\(applications/\(.*')
yaml = apath('../app.yaml', r=request) yaml = apath('../app.yaml', r=request)
if not os.path.exists(yaml): if not os.path.exists(yaml):
example = apath('../app.example.yaml', r=request) example = apath('../app.example.yaml', r=request)
shutil.copyfile(example,yaml) shutil.copyfile(example, yaml)
data = read_file(yaml) data = read_file(yaml)
data = re.sub('application:.*','application: %s' % form.vars.google_application_id,data) data = re.sub('application:.*', 'application: %s' %
data = regex.sub('(applications/(%s)/.*)|' % '|'.join(ignore_apps),data) form.vars.google_application_id, data)
data = regex.sub(
'(applications/(%s)/.*)|' % '|'.join(ignore_apps), data)
write_file(yaml, data) write_file(yaml, data)
path = request.env.applications_parent path = request.env.applications_parent
cmd = '%s --email=%s --passin update %s' % \ cmd = '%s --email=%s --passin update %s' % \
(form.vars.appcfg, form.vars.email, path) (form.vars.appcfg, form.vars.email, path)
p = cache.ram('gae_upload', p = cache.ram('gae_upload',
lambda s=subprocess,c=cmd:s.Popen(c, shell=True, lambda s=subprocess, c=cmd: s.Popen(c, shell=True,
stdin=s.PIPE, stdin=s.PIPE,
stdout=s.PIPE, stdout=s.PIPE,
stderr=s.PIPE, close_fds=True),-1) stderr=s.PIPE, close_fds=True), -1)
p.stdin.write(form.vars.password+'\n') p.stdin.write(form.vars.password + '\n')
fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
fcntl.fcntl(p.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) fcntl.fcntl(p.stderr.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
return dict(form=form,command=cmd) return dict(form=form, command=cmd)
def callback(): def callback():
p = cache.ram('gae_upload',lambda:None,forever) p = cache.ram('gae_upload', lambda: None, forever)
if not p or p.poll()!=None: if not p or p.poll() is not None:
return '<done/>' return '<done/>'
try: try:
output = p.stdout.read() output = p.stdout.read()
except: except:
output='' output = ''
try: try:
errors = p.stderr.read() errors = p.stderr.read()
except: except:
errors='' errors = ''
return (output+errors).replace('\n','<br/>') return (output + errors).replace('\n', '<br/>')
+18 -17
View File
@@ -2,10 +2,10 @@ from gluon.fileutils import read_file, write_file
if DEMO_MODE or MULTI_USER_MODE: if DEMO_MODE or MULTI_USER_MODE:
session.flash = T('disabled in demo mode') session.flash = T('disabled in demo mode')
redirect(URL('default','site')) redirect(URL('default', 'site'))
if not have_mercurial: if not have_mercurial:
session.flash=T("Sorry, could not find mercurial installed") session.flash = T("Sorry, could not find mercurial installed")
redirect(URL('default','design',args=request.args(0))) redirect(URL('default', 'design', args=request.args(0)))
_hgignore_content = """\ _hgignore_content = """\
syntax: glob syntax: glob
@@ -22,6 +22,7 @@ sessions/*
errors/* errors/*
""" """
def hg_repo(path): def hg_repo(path):
import os import os
uio = ui.ui() uio = ui.ui()
@@ -37,13 +38,14 @@ def hg_repo(path):
write_file(hgignore, _hgignore_content) write_file(hgignore, _hgignore_content)
return repo return repo
def commit(): def commit():
app = request.args(0) app = request.args(0)
path = apath(app, r=request) path = apath(app, r=request)
repo = hg_repo(path) repo = hg_repo(path)
form = FORM('Comment:',INPUT(_name='comment',requires=IS_NOT_EMPTY()), form = FORM('Comment:', INPUT(_name='comment', requires=IS_NOT_EMPTY()),
INPUT(_type='submit',_value=T('Commit'))) INPUT(_type='submit', _value=T('Commit')))
if form.accepts(request.vars,session): if form.accepts(request.vars, session):
oldid = repo[repo.lookup('.')] oldid = repo[repo.lookup('.')]
addremove(repo) addremove(repo)
repo.commit(text=form.vars.comment) repo.commit(text=form.vars.comment)
@@ -51,34 +53,33 @@ def commit():
response.flash = 'no changes' response.flash = 'no changes'
try: try:
files = TABLE(*[TR(file) for file in repo[repo.lookup('.')].files()]) files = TABLE(*[TR(file) for file in repo[repo.lookup('.')].files()])
changes = TABLE(TR(TH('revision'),TH('description'))) changes = TABLE(TR(TH('revision'), TH('description')))
for change in repo.changelog: for change in repo.changelog:
ctx=repo.changectx(change) ctx = repo.changectx(change)
revision, description = ctx.rev(), ctx.description() revision, description = ctx.rev(), ctx.description()
changes.append(TR(A(revision,_href=URL('revision', changes.append(TR(A(revision, _href=URL('revision',
args=(app,revision))), args=(app, revision))),
description)) description))
except: except:
files = [] files = []
changes = [] changes = []
return dict(form=form,files=files,changes=changes,repo=repo) return dict(form=form, files=files, changes=changes, repo=repo)
def revision(): def revision():
app = request.args(0) app = request.args(0)
path = apath(app, r=request) path = apath(app, r=request)
repo = hg_repo(path) repo = hg_repo(path)
revision = request.args(1) revision = request.args(1)
ctx=repo.changectx(revision) ctx = repo.changectx(revision)
form=FORM(INPUT(_type='submit',_value=T('Revert'))) form = FORM(INPUT(_type='submit', _value=T('Revert')))
if form.accepts(request.vars): if form.accepts(request.vars):
hg.update(repo, revision) hg.update(repo, revision)
session.flash = "reverted to revision %s" % ctx.rev() session.flash = "reverted to revision %s" % ctx.rev()
redirect(URL('default','design',args=app)) redirect(URL('default', 'design', args=app))
return dict( return dict(
files=ctx.files(), files=ctx.files(),
rev=str(ctx.rev()), rev=str(ctx.rev()),
desc=ctx.description(), desc=ctx.description(),
form=form form=form
) )
+26 -17
View File
@@ -1,29 +1,36 @@
import os import os
from distutils import dir_util try:
from distutils import dir_util
except ImportError:
session.flash = T('requires distutils, but not installed')
redirect(URL('default', 'site'))
try: try:
from git import * from git import *
except ImportError: except ImportError:
session.flash = T('requires python-git, but not installed') session.flash = T('requires python-git, but not installed')
redirect(URL('default','site')) redirect(URL('default', 'site'))
def deploy(): def deploy():
apps = sorted(file for file in os.listdir(apath(r=request))) apps = sorted(file for file in os.listdir(apath(r=request)))
form = SQLFORM.factory( form = SQLFORM.factory(
Field('osrepo',default='/tmp',label=T('Path to local openshift repo root.'), Field(
requires=EXISTS(error_message=T('directory not found'))), 'osrepo', default='/tmp', label=T('Path to local openshift repo root.'),
Field('osname',default='web2py',label=T('WSGI reference name')), requires=EXISTS(error_message=T('directory not found'))),
Field('applications','list:string', Field('osname', default='web2py', label=T('WSGI reference name')),
requires=IS_IN_SET(apps,multiple=True), Field('applications', 'list:string',
requires=IS_IN_SET(apps, multiple=True),
label=T('web2py apps to deploy'))) label=T('web2py apps to deploy')))
cmd = output = errors= "" cmd = output = errors = ""
if form.accepts(request,session): if form.accepts(request, session):
try: try:
kill() kill()
except: except:
pass pass
ignore_apps = [item for item in apps if not item in form.vars.applications] ignore_apps = [
item for item in apps if not item in form.vars.applications]
regex = re.compile('\(applications/\(.*') regex = re.compile('\(applications/\(.*')
w2p_origin = os.getcwd() w2p_origin = os.getcwd()
osrepo = form.vars.osrepo osrepo = form.vars.osrepo
@@ -34,23 +41,25 @@ def deploy():
assert repo.bare == False assert repo.bare == False
for i in form.vars.applications: for i in form.vars.applications:
appsrc = os.path.join(apath(r=request),i) appsrc = os.path.join(apath(r=request), i)
appdest = os.path.join(osrepo,'wsgi',osname,'applications',i) appdest = os.path.join(osrepo, 'wsgi', osname, 'applications', i)
dir_util.copy_tree(appsrc,appdest) dir_util.copy_tree(appsrc, appdest)
#shutil.copytree(appsrc,appdest) #shutil.copytree(appsrc,appdest)
index.add(['wsgi/'+osname+'/applications/'+i]) index.add(['wsgi/' + osname + '/applications/' + i])
new_commit = index.commit("Deploy from Web2py IDE") new_commit = index.commit("Deploy from Web2py IDE")
origin = repo.remotes.origin origin = repo.remotes.origin
origin.push origin.push
origin.push() origin.push()
#Git code ends here #Git code ends here
return dict(form=form,command=cmd) return dict(form=form, command=cmd)
class EXISTS(object): class EXISTS(object):
def __init__(self, error_message='file not found'): def __init__(self, error_message='file not found'):
self.error_message = error_message self.error_message = error_message
def __call__(self, value): def __call__(self, value):
if os.path.exists(value): if os.path.exists(value):
return (value,None) return (value, None)
return (value,self.error_message) return (value, self.error_message)
@@ -1,10 +1,10 @@
response.files=response.files[:3] response.files = response.files[:3]
response.menu=[] response.menu = []
def index(): def index():
return locals() return locals()
def about(): def about():
return locals() return locals()
+17 -14
View File
@@ -1,25 +1,29 @@
import sys import sys
import cStringIO import cStringIO
import gluon.contrib.shell import gluon.contrib.shell
import code, thread import code
import thread
from gluon.shell import env from gluon.shell import env
if DEMO_MODE or MULTI_USER_MODE: if DEMO_MODE or MULTI_USER_MODE:
session.flash = T('disabled in demo mode') session.flash = T('disabled in demo mode')
redirect(URL('default','site')) redirect(URL('default', 'site'))
FE = 10 ** 9
FE=10**9
def index(): def index():
app = request.args(0) or 'admin' app = request.args(0) or 'admin'
reset() reset()
return dict(app=app) return dict(app=app)
def callback(): def callback():
app = request.args[0] app = request.args[0]
command = request.vars.statement command = request.vars.statement
escape = command[:1]!='!' escape = command[:1] != '!'
history = session['history:'+app] = session.get('history:'+app,gluon.contrib.shell.History()) history = session['history:' + app] = session.get(
'history:' + app, gluon.contrib.shell.History())
if not escape: if not escape:
command = command[1:] command = command[1:]
if command == '%reset': if command == '%reset':
@@ -27,21 +31,20 @@ def callback():
return '*** reset ***' return '*** reset ***'
elif command[0] == '%': elif command[0] == '%':
try: try:
command=session['commands:'+app][int(command[1:])] command = session['commands:' + app][int(command[1:])]
except ValueError: except ValueError:
return '' return ''
session['commands:'+app].append(command) session['commands:' + app].append(command)
environ=env(app,True) environ = env(app, True, extra_request=dict(is_https=request.is_https))
output = gluon.contrib.shell.run(history,command,environ) output = gluon.contrib.shell.run(history, command, environ)
k = len(session['commands:'+app]) - 1 k = len(session['commands:' + app]) - 1
#output = PRE(output) #output = PRE(output)
#return TABLE(TR('In[%i]:'%k,PRE(command)),TR('Out[%i]:'%k,output)) #return TABLE(TR('In[%i]:'%k,PRE(command)),TR('Out[%i]:'%k,output))
return 'In [%i] : %s%s\n' % (k + 1, command, output) return 'In [%i] : %s%s\n' % (k + 1, command, output)
def reset(): def reset():
app = request.args(0) or 'admin' app = request.args(0) or 'admin'
session['commands:'+app] = [] session['commands:' + app] = []
session['history:'+app] = gluon.contrib.shell.History() session['history:' + app] = gluon.contrib.shell.History()
return 'done' return 'done'
+5 -5
View File
@@ -2,10 +2,12 @@ import os
from gluon.settings import global_settings, read_file from gluon.settings import global_settings, read_file
# #
def index(): def index():
app = request.args(0) app = request.args(0)
return dict(app=app) return dict(app=app)
def profiler(): def profiler():
""" """
to use the profiler start web2py with -F profiler.log to use the profiler start web2py with -F profiler.log
@@ -19,13 +21,11 @@ def profiler():
else: else:
size = 0 size = 0
if os.path.exists(filename): if os.path.exists(filename):
data = read_file('profiler.log','rb') data = read_file('profiler.log', 'rb')
if size<len(data): if size < len(data):
data = data[size:] data = data[size:]
else: else:
size=0 size = 0
size += len(data) size += len(data)
response.cookies[KEY] = size response.cookies[KEY] = size
return data return data
@@ -33,7 +33,7 @@ def list_apps():
@service.jsonrpc @service.jsonrpc
def list_files(app, pattern='.*\.py$'): def list_files(app, pattern='.*\.py$'):
files = listdir(apath('%s/' % app, r=request), pattern) files = listdir(apath('%s/' % app, r=request), pattern)
return [x.replace('\\','/') for x in files] return [x.replace('\\', '/') for x in files]
@service.jsonrpc @service.jsonrpc
@@ -43,7 +43,7 @@ def read_file(filename, b64=False):
try: try:
data = f.read() data = f.read()
if not b64: if not b64:
data = data.replace('\r','') data = data.replace('\r', '')
else: else:
data = base64.b64encode(data) data = base64.b64encode(data)
finally: finally:
@@ -82,6 +82,7 @@ def install(app_name, filename, data, overwrite=True):
return installed return installed
@service.jsonrpc @service.jsonrpc
def attach_debugger(host='localhost', port=6000, authkey='secret password'): def attach_debugger(host='localhost', port=6000, authkey='secret password'):
import gluon.contrib.qdb as qdb import gluon.contrib.qdb as qdb
@@ -124,7 +125,7 @@ def detach_debugger():
gluon.debug.qdb_debugger = None gluon.debug.qdb_debugger = None
return True return True
def call(): def call():
session.forget() session.forget()
return service() return service()
+320 -273
View File
@@ -1,64 +1,73 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os, uuid, re, pickle, urllib, glob import os
import uuid
import re
import pickle
import urllib
import glob
from gluon.admin import app_create, plugin_install from gluon.admin import app_create, plugin_install
from gluon.fileutils import abspath, read_file, write_file from gluon.fileutils import abspath, read_file, write_file
def reset(session):
session.app={
'name':'',
'params':[('title','My New App'),
('subtitle','powered by web2py'),
('author','you'),
('author_email','you@example.com'),
('keywords',''),
('description',''),
('layout_theme','Default'),
('database_uri','sqlite://storage.sqlite'),
('security_key',str(uuid.uuid4())),
('email_server','localhost'),
('email_sender','you@example.com'),
('email_login',''),
('login_method','local'),
('login_config',''),
('plugins',[])],
'tables':['auth_user'],
'table_auth_user':['username','first_name',
'last_name','email','password'],
'pages':['index','error'],
'page_index':'# Welcome to my new app',
'page_error':'# Error: the document does not exist',
}
if not session.app: reset(session) def reset(session):
session.app = {
'name': '',
'params': [('title', 'My New App'),
('subtitle', 'powered by web2py'),
('author', 'you'),
('author_email', 'you@example.com'),
('keywords', ''),
('description', ''),
('layout_theme', 'Default'),
('database_uri', 'sqlite://storage.sqlite'),
('security_key', str(uuid.uuid4())),
('email_server', 'localhost'),
('email_sender', 'you@example.com'),
('email_login', ''),
('login_method', 'local'),
('login_config', ''),
('plugins', [])],
'tables': ['auth_user'],
'table_auth_user': ['username', 'first_name',
'last_name', 'email', 'password'],
'pages': ['index', 'error'],
'page_index': '# Welcome to my new app',
'page_error': '# Error: the document does not exist',
}
if not session.app:
reset(session)
def listify(x): def listify(x):
if not isinstance(x,(list,tuple)): if not isinstance(x, (list, tuple)):
return x and [x] or [] return x and [x] or []
return x return x
def clean(name): def clean(name):
return re.sub('\W+','_',name.strip().lower()) return re.sub('\W+', '_', name.strip().lower())
def index(): def index():
response.view='wizard/step.html' response.view = 'wizard/step.html'
reset(session) reset(session)
apps=os.listdir(os.path.join(request.folder,'..')) apps = os.listdir(os.path.join(request.folder, '..'))
form=SQLFORM.factory(Field('name',requires=[IS_NOT_EMPTY(), form = SQLFORM.factory(Field('name', requires=[IS_NOT_EMPTY(),
IS_ALPHANUMERIC()])) IS_ALPHANUMERIC()]))
if form.accepts(request.vars): if form.accepts(request.vars):
app = form.vars.name app = form.vars.name
session.app['name'] = app session.app['name'] = app
if MULTI_USER_MODE and db(db.app.name==app)\ if MULTI_USER_MODE and db(db.app.name == app)(db.app.owner != auth.user.id).count():
(db.app.owner!=auth.user.id).count():
session.flash = 'App belongs already to other user' session.flash = 'App belongs already to other user'
elif app in apps: elif app in apps:
meta = os.path.normpath(\ meta = os.path.normpath(
os.path.join(os.path.normpath(request.folder), os.path.join(os.path.normpath(request.folder),
'..',app,'wizard.metadata')) '..', app, 'wizard.metadata'))
if os.path.exists(meta): if os.path.exists(meta):
try: try:
metafile = open(meta,'rb') metafile = open(meta, 'rb')
try: try:
session.app = pickle.load(metafile) session.app = pickle.load(metafile)
finally: finally:
@@ -67,14 +76,14 @@ def index():
except: except:
session.flash = T("The app exists, was NOT created by wizard, continue to overwrite!") session.flash = T("The app exists, was NOT created by wizard, continue to overwrite!")
redirect(URL('step1')) redirect(URL('step1'))
return dict(step='Start',form=form) return dict(step='Start', form=form)
def step1(): def step1():
from gluon.contrib.simplejson import loads from gluon.contrib.simplejson import loads
import urllib import urllib
if not session.themes: if not session.themes:
url=LAYOUTS_APP+'/default/layouts.json' url = LAYOUTS_APP + '/default/layouts.json'
try: try:
data = urllib.urlopen(url).read() data = urllib.urlopen(url).read()
session.themes = ['Default'] + loads(data)['layouts'] session.themes = ['Default'] + loads(data)['layouts']
@@ -82,145 +91,158 @@ def step1():
session.themes = ['Default'] session.themes = ['Default']
themes = session.themes themes = session.themes
if not session.plugins: if not session.plugins:
url = PLUGINS_APP+'/default/plugins.json' url = PLUGINS_APP + '/default/plugins.json'
try: try:
data = urllib.urlopen(url).read() data = urllib.urlopen(url).read()
session.plugins = loads(data)['plugins'] session.plugins = loads(data)['plugins']
except: except:
session.plugins = [] session.plugins = []
plugins = [x.split('.')[2] for x in session.plugins] plugins = [x.split('.')[2] for x in session.plugins]
response.view='wizard/step.html' response.view = 'wizard/step.html'
params = dict(session.app['params']) params = dict(session.app['params'])
form=SQLFORM.factory( form = SQLFORM.factory(
Field('title',default=params.get('title',None), Field('title', default=params.get('title', None),
requires=IS_NOT_EMPTY()), requires=IS_NOT_EMPTY()),
Field('subtitle',default=params.get('subtitle',None)), Field('subtitle', default=params.get('subtitle', None)),
Field('author',default=params.get('author',None)), Field('author', default=params.get('author', None)),
Field('author_email',default=params.get('author_email',None)), Field(
Field('keywords',default=params.get('keywords',None)), 'author_email', default=params.get('author_email', None)),
Field('description','text', Field('keywords', default=params.get('keywords', None)),
default=params.get('description',None)), Field('description', 'text',
Field('layout_theme',requires=IS_IN_SET(themes), default=params.get('description', None)),
default=params.get('layout_theme',themes[0])), Field('layout_theme', requires=IS_IN_SET(themes),
Field('database_uri',default=params.get('database_uri',None)), default=params.get('layout_theme', themes[0])),
Field('security_key',default=params.get('security_key',None)), Field(
Field('email_server',default=params.get('email_server',None)), 'database_uri', default=params.get('database_uri', None)),
Field('email_sender',default=params.get('email_sender',None)), Field(
Field('email_login',default=params.get('email_login',None)), 'security_key', default=params.get('security_key', None)),
Field('login_method',requires=IS_IN_SET(('local','janrain')), Field(
default=params.get('login_method','local')), 'email_server', default=params.get('email_server', None)),
Field('login_config',default=params.get('login_config',None)), Field(
Field('plugins','list:string',requires=IS_IN_SET(plugins,multiple=True))) 'email_sender', default=params.get('email_sender', None)),
Field('email_login', default=params.get('email_login', None)),
Field('login_method', requires=IS_IN_SET(('local', 'janrain')),
default=params.get('login_method', 'local')),
Field(
'login_config', default=params.get('login_config', None)),
Field('plugins', 'list:string', requires=IS_IN_SET(plugins, multiple=True)))
if form.accepts(request.vars): if form.accepts(request.vars):
session.app['params']=[(key,form.vars.get(key,None)) session.app['params'] = [(key, form.vars.get(key, None))
for key,value in session.app['params']] for key, value in session.app['params']]
redirect(URL('step2')) redirect(URL('step2'))
return dict(step='1: Setting Parameters',form=form) return dict(step='1: Setting Parameters', form=form)
def step2(): def step2():
response.view='wizard/step.html' response.view = 'wizard/step.html'
form=SQLFORM.factory(Field('table_names','list:string', form = SQLFORM.factory(Field('table_names', 'list:string',
default=session.app['tables'])) default=session.app['tables']))
if form.accepts(request.vars): if form.accepts(request.vars):
table_names = [clean(t) for t in listify(form.vars.table_names) \ table_names = [clean(t) for t in listify(form.vars.table_names)
if t.strip()] if t.strip()]
if [t for t in table_names if t.startswith('auth_') and \ if [t for t in table_names if t.startswith('auth_') and
not t=='auth_user']: not t == 'auth_user']:
form.error.table_names = \ form.error.table_names = \
T('invalid table names (auth_* tables already defined)') T('invalid table names (auth_* tables already defined)')
else: else:
session.app['tables']=table_names session.app['tables'] = table_names
for table in session.app['tables']: for table in session.app['tables']:
if not 'table_'+table in session.app: if not 'table_' + table in session.app:
session.app['table_'+table]=['name'] session.app['table_' + table] = ['name']
if not table=='auth_user': if not table == 'auth_user':
name = table+'_manage' name = table + '_manage'
if not name in session.app['pages']: if not name in session.app['pages']:
session.app['pages'].append(name) session.app['pages'].append(name)
session.app['page_'+name] = \ session.app['page_' + name] = \
'## Manage %s\n\n{{=form}}' % (table) '## Manage %s\n\n{{=form}}' % (table)
if session.app['tables']: if session.app['tables']:
redirect(URL('step3',args=0)) redirect(URL('step3', args=0))
else: else:
redirect(URL('step4')) redirect(URL('step4'))
return dict(step='2: Tables',form=form) return dict(step='2: Tables', form=form)
def step3(): def step3():
response.view='wizard/step.html' response.view = 'wizard/step.html'
n=int(request.args(0) or 0) n = int(request.args(0) or 0)
m=len(session.app['tables']) m = len(session.app['tables'])
if n>=m: redirect(URL('step2')) if n >= m:
table=session.app['tables'][n] redirect(URL('step2'))
form=SQLFORM.factory(Field('field_names','list:string', table = session.app['tables'][n]
default=session.app.get('table_'+table,[]))) form = SQLFORM.factory(Field('field_names', 'list:string',
default=session.app.get('table_' + table, [])))
if form.accepts(request.vars) and form.vars.field_names: if form.accepts(request.vars) and form.vars.field_names:
fields=listify(form.vars.field_names) fields = listify(form.vars.field_names)
if table=='auth_user': if table == 'auth_user':
for field in ['first_name','last_name','username','email','password']: for field in ['first_name', 'last_name', 'username', 'email', 'password']:
if not field in fields: if not field in fields:
fields.append(field) fields.append(field)
session.app['table_'+table]=[t.strip().lower() session.app['table_' + table] = [t.strip().lower()
for t in listify(form.vars.field_names) for t in listify(form.vars.field_names)
if t.strip()] if t.strip()]
try: try:
tables=sort_tables(session.app['tables']) tables = sort_tables(session.app['tables'])
except RuntimeError: except RuntimeError:
response.flash=T('invalid circular reference') response.flash = T('invalid circular reference')
else: else:
if n<m-1: if n < m - 1:
redirect(URL('step3',args=n+1)) redirect(URL('step3', args=n + 1))
else: else:
redirect(URL('step4')) redirect(URL('step4'))
return dict(step='3: Fields for table "%s" (%s of %s)' \ return dict(step='3: Fields for table "%s" (%s of %s)'
% (table,n+1,m),table=table,form=form) % (table, n + 1, m), table=table, form=form)
def step4(): def step4():
response.view='wizard/step.html' response.view = 'wizard/step.html'
form=SQLFORM.factory(Field('pages','list:string', form = SQLFORM.factory(Field('pages', 'list:string',
default=session.app['pages'])) default=session.app['pages']))
if form.accepts(request.vars): if form.accepts(request.vars):
session.app['pages']=[clean(t) session.app['pages'] = [clean(t)
for t in listify(form.vars.pages) for t in listify(form.vars.pages)
if t.strip()] if t.strip()]
if session.app['pages']: if session.app['pages']:
redirect(URL('step5',args=0)) redirect(URL('step5', args=0))
else: else:
redirect(URL('step6')) redirect(URL('step6'))
return dict(step='4: Pages',form=form) return dict(step='4: Pages', form=form)
def step5(): def step5():
response.view='wizard/step.html' response.view = 'wizard/step.html'
n=int(request.args(0) or 0) n = int(request.args(0) or 0)
m=len(session.app['pages']) m = len(session.app['pages'])
if n>=m: redirect(URL('step4')) if n >= m:
page=session.app['pages'][n] redirect(URL('step4'))
markmin_url='http://web2py.com/examples/static/markmin.html' page = session.app['pages'][n]
form=SQLFORM.factory(Field('content','text', markmin_url = 'http://web2py.com/examples/static/markmin.html'
default=session.app.get('page_'+page,[]), form = SQLFORM.factory(Field('content', 'text',
comment=A('use markmin', default=session.app.get('page_' + page, []),
_href=markmin_url,_target='_blank')), comment=A('use markmin',
formstyle='table2cols') _href=markmin_url, _target='_blank')),
formstyle='table2cols')
if form.accepts(request.vars): if form.accepts(request.vars):
session.app['page_'+page]=form.vars.content session.app['page_' + page] = form.vars.content
if n<m-1: if n < m - 1:
redirect(URL('step5',args=n+1)) redirect(URL('step5', args=n + 1))
else: else:
redirect(URL('step6')) redirect(URL('step6'))
return dict(step='5: View for page "%s" (%s of %s)' % (page,n+1,m),form=form) return dict(step='5: View for page "%s" (%s of %s)' % (page, n + 1, m), form=form)
def step6(): def step6():
response.view='wizard/step.html' response.view = 'wizard/step.html'
params = dict(session.app['params']) params = dict(session.app['params'])
app = session.app['name'] app = session.app['name']
form=SQLFORM.factory( form = SQLFORM.factory(
Field('generate_model','boolean',default=True), Field('generate_model', 'boolean', default=True),
Field('generate_controller','boolean',default=True), Field('generate_controller', 'boolean', default=True),
Field('generate_views','boolean',default=True), Field('generate_views', 'boolean', default=True),
Field('generate_menu','boolean',default=True), Field('generate_menu', 'boolean', default=True),
Field('apply_layout','boolean',default=True), Field('apply_layout', 'boolean', default=True),
Field('erase_database','boolean',default=True), Field('erase_database', 'boolean', default=True),
Field('populate_database','boolean',default=True)) Field('populate_database', 'boolean', default=True))
if form.accepts(request.vars): if form.accepts(request.vars):
if DEMO_MODE: if DEMO_MODE:
session.flash = T('Application cannot be generated in demo mode') session.flash = T('Application cannot be generated in demo mode')
@@ -228,159 +250,173 @@ def step6():
create(form.vars) create(form.vars)
session.flash = 'Application %s created' % app session.flash = 'Application %s created' % app
redirect(URL('generated')) redirect(URL('generated'))
return dict(step='6: Generate app "%s"' % app,form=form) return dict(step='6: Generate app "%s"' % app, form=form)
def generated(): def generated():
return dict(app=session.app['name']) return dict(app=session.app['name'])
def sort_tables(tables): def sort_tables(tables):
import re import re
regex = re.compile('(%s)' % '|'.join(tables)) regex = re.compile('(%s)' % '|'.join(tables))
is_auth_user = 'auth_user' in tables is_auth_user = 'auth_user' in tables
d={} d = {}
for table in tables: for table in tables:
d[table]=[] d[table] = []
for field in session.app['table_%s' % table]: for field in session.app['table_%s' % table]:
d[table]+=regex.findall(field) d[table] += regex.findall(field)
tables=[] tables = []
if is_auth_user: if is_auth_user:
tables.append('auth_user') tables.append('auth_user')
def append(table,trail=[]):
def append(table, trail=[]):
if table in trail: if table in trail:
raise RuntimeError raise RuntimeError
for t in d[table]: for t in d[table]:
# if not t==table: (problem, no dropdown for self references) # if not t==table: (problem, no dropdown for self references)
append(t,trail=trail+[table]) append(t, trail=trail + [table])
if not table in tables: if not table in tables:
tables.append(table) tables.append(table)
for table in d: append(table) for table in d:
append(table)
return tables return tables
def make_table(table,fields):
rawtable=table def make_table(table, fields):
if table!='auth_user': table='t_'+table rawtable = table
s='' if table != 'auth_user':
s+='\n'+'#'*40+'\n' table = 't_' + table
s+="db.define_table('%s',\n" % table s = ''
first_field='id' s += '\n' + '#' * 40 + '\n'
s += "db.define_table('%s',\n" % table
first_field = 'id'
for field in fields: for field in fields:
items=[x.lower() for x in field.split()] items = [x.lower() for x in field.split()]
has = {} has = {}
keys = [] keys = []
for key in ['notnull','unique','integer','double','boolean','float', for key in ['notnull', 'unique', 'integer', 'double', 'boolean', 'float',
'boolean', 'date','time','datetime','text','wiki', 'boolean', 'date', 'time', 'datetime', 'text', 'wiki',
'html','file','upload','image','true', 'html', 'file', 'upload', 'image', 'true',
'hidden','readonly','writeonly','multiple', 'hidden', 'readonly', 'writeonly', 'multiple',
'notempty','required']: 'notempty', 'required']:
if key in items[1:]: if key in items[1:]:
keys.append(key) keys.append(key)
has[key] = True has[key] = True
tables = session.app['tables'] tables = session.app['tables']
refs = [t for t in tables if t in items] refs = [t for t in tables if t in items]
items = items[:1] + [x for x in items[1:] \ items = items[:1] + [x for x in items[1:]
if not x in keys and not x in tables] if not x in keys and not x in tables]
barename = name = '_'.join(items) barename = name = '_'.join(items)
if table[:2]=='t_': name='f_'+name if table[:2] == 't_': name = 'f_' + name
if first_field=='id': first_field=name if first_field == 'id':
first_field = name
### determine field type ### determine field type
ftype='string' ftype = 'string'
deftypes={'integer':'integer','double':'double','boolean':'boolean', deftypes = {'integer': 'integer', 'double': 'double', 'boolean': 'boolean',
'float':'double','bool':'boolean', 'float': 'double', 'bool': 'boolean',
'date':'date','time':'time','datetime':'datetime', 'date': 'date', 'time': 'time', 'datetime': 'datetime',
'text':'text','file':'upload','image':'upload', 'text': 'text', 'file': 'upload', 'image': 'upload',
'upload':'upload','wiki':'text', 'html':'text'} 'upload': 'upload', 'wiki': 'text', 'html': 'text'}
for key,t in deftypes.items(): for key, t in deftypes.items():
if key in has: if key in has:
ftype = t ftype = t
if refs: if refs:
key = refs[0] key = refs[0]
if not key=='auth_user': key='t_'+key if not key == 'auth_user':
key = 't_' + key
if 'multiple' in has: if 'multiple' in has:
ftype='list:reference %s' % key ftype = 'list:reference %s' % key
else: else:
ftype='reference %s' % key ftype = 'reference %s' % key
if ftype=='string' and 'multiple' in has: if ftype == 'string' and 'multiple' in has:
ftype='list:string' ftype = 'list:string'
elif ftype=='integer' and 'multiple' in has: elif ftype == 'integer' and 'multiple' in has:
ftype='list:integer' ftype = 'list:integer'
elif name=='password': elif name == 'password':
ftype='password' ftype = 'password'
s+=" Field('%s', type='%s'" % (name, ftype) s += " Field('%s', type='%s'" % (name, ftype)
### determine field attributes ### determine field attributes
if 'notnull' in has or 'notempty' in has or 'required' in has: if 'notnull' in has or 'notempty' in has or 'required' in has:
s+=', notnull=True' s += ', notnull=True'
if 'unique' in has: if 'unique' in has:
s+=', unique=True' s += ', unique=True'
if ftype=='boolean' and 'true' in has: if ftype == 'boolean' and 'true' in has:
s+=",\n default=True" s += ",\n default=True"
### determine field representation ### determine field representation
elif 'wiki' in has: elif 'wiki' in has:
s+=",\n represent=lambda x, row: MARKMIN(x)" s += ",\n represent=lambda x, row: MARKMIN(x)"
s+=",\n comment='WIKI (markmin)'" s += ",\n comment='WIKI (markmin)'"
elif 'html' in has: elif 'html' in has:
s+=",\n represent=lambda x, row: XML(x,sanitize=True)" s += ",\n represent=lambda x, row: XML(x,sanitize=True)"
s+=",\n comment='HTML (sanitized)'" s += ",\n comment='HTML (sanitized)'"
### determine field access ### determine field access
if name=='password' or 'writeonly' in has: if name == 'password' or 'writeonly' in has:
s+=",\n readable=False" s += ",\n readable=False"
elif 'hidden' in has: elif 'hidden' in has:
s+=",\n writable=False, readable=False" s += ",\n writable=False, readable=False"
elif 'readonly' in has: elif 'readonly' in has:
s+=",\n writable=False" s += ",\n writable=False"
### make up a label ### make up a label
s+=",\n label=T('%s')),\n" % \ s += ",\n label=T('%s')),\n" % \
' '.join(x.capitalize() for x in barename.split('_')) ' '.join(x.capitalize() for x in barename.split('_'))
if table=='auth_user': if table == 'auth_user':
s+=" Field('created_on','datetime',default=request.now,\n" s += " Field('created_on','datetime',default=request.now,\n"
s+=" label=T('Created On'),writable=False,readable=False),\n" s += " label=T('Created On'),writable=False,readable=False),\n"
s+=" Field('modified_on','datetime',default=request.now,\n" s += " Field('modified_on','datetime',default=request.now,\n"
s+=" label=T('Modified On'),writable=False,readable=False,\n" s += " label=T('Modified On'),writable=False,readable=False,\n"
s+=" update=request.now),\n" s += " update=request.now),\n"
s+=" Field('registration_key',default='',\n" s += " Field('registration_key',default='',\n"
s+=" writable=False,readable=False),\n" s += " writable=False,readable=False),\n"
s+=" Field('reset_password_key',default='',\n" s += " Field('reset_password_key',default='',\n"
s+=" writable=False,readable=False),\n" s += " writable=False,readable=False),\n"
s+=" Field('registration_id',default='',\n" s += " Field('registration_id',default='',\n"
s+=" writable=False,readable=False),\n" s += " writable=False,readable=False),\n"
elif 'auth_user' in session.app['tables']: elif 'auth_user' in session.app['tables']:
s+=" auth.signature,\n" s += " auth.signature,\n"
s+=" format='%("+first_field+")s',\n" s += " format='%(" + first_field + ")s',\n"
s+=" migrate=settings.migrate)\n\n" s += " migrate=settings.migrate)\n\n"
if table=='auth_user': if table == 'auth_user':
s+=""" s += """
db.auth_user.first_name.requires = IS_NOT_EMPTY(error_message=auth.messages.is_empty) db.auth_user.first_name.requires = IS_NOT_EMPTY(
db.auth_user.last_name.requires = IS_NOT_EMPTY(error_message=auth.messages.is_empty) error_message=auth.messages.is_empty)
db.auth_user.password.requires = CRYPT(key=auth.settings.hmac_key, min_length=4) db.auth_user.last_name.requires = IS_NOT_EMPTY(
error_message=auth.messages.is_empty)
db.auth_user.password.requires = CRYPT(
key=auth.settings.hmac_key, min_length=4)
db.auth_user.username.requires = IS_NOT_IN_DB(db, db.auth_user.username) db.auth_user.username.requires = IS_NOT_IN_DB(db, db.auth_user.username)
db.auth_user.email.requires = (IS_EMAIL(error_message=auth.messages.invalid_email), db.auth_user.email.requires = (
IS_EMAIL(error_message=auth.messages.invalid_email),
IS_NOT_IN_DB(db, db.auth_user.email)) IS_NOT_IN_DB(db, db.auth_user.email))
""" """
else: else:
s+="db.define_table('%s_archive',db.%s,Field('current_record','reference %s',readable=False,writable=False))\n" % (table,table,table) s += "db.define_table('%s_archive',db.%s,Field('current_record','reference %s',readable=False,writable=False))\n" % (table, table, table)
return s return s
def fix_db(filename): def fix_db(filename):
params = dict(session.app['params']) params = dict(session.app['params'])
content = read_file(filename,'rb') content = read_file(filename, 'rb')
if 'auth_user' in session.app['tables']: if 'auth_user' in session.app['tables']:
auth_user = make_table('auth_user',session.app['table_auth_user']) auth_user = make_table('auth_user', session.app['table_auth_user'])
content = content.replace('sqlite://storage.sqlite', content = content.replace('sqlite://storage.sqlite',
params['database_uri']) params['database_uri'])
content = content.replace('auth.define_tables()',\ content = content.replace('auth.define_tables()',
auth_user+'auth.define_tables(migrate = settings.migrate)') auth_user + 'auth.define_tables(migrate = settings.migrate)')
content += """ content += """
mail.settings.server = settings.email_server mail.settings.server = settings.email_server
mail.settings.sender = settings.email_sender mail.settings.sender = settings.email_sender
mail.settings.login = settings.email_login mail.settings.login = settings.email_login
""" """
if params['login_method']=='janrain': if params['login_method'] == 'janrain':
content+=""" content += """
from gluon.contrib.login_methods.rpx_account import RPXAccount from gluon.contrib.login_methods.rpx_account import RPXAccount
auth.settings.actions_disabled=['register','change_password','request_reset_password'] auth.settings.actions_disabled=['register','change_password',
'request_reset_password']
auth.settings.login_form = RPXAccount(request, auth.settings.login_form = RPXAccount(request,
api_key = settings.login_config.split(':')[-1], api_key = settings.login_config.split(':')[-1],
domain = settings.login_config.split(':')[0], domain = settings.login_config.split(':')[0],
@@ -388,14 +424,15 @@ auth.settings.login_form = RPXAccount(request,
""" """
write_file(filename, content, 'wb') write_file(filename, content, 'wb')
def make_menu(pages): def make_menu(pages):
s='' s = ''
s+='response.title = settings.title\n' s += 'response.title = settings.title\n'
s+='response.subtitle = settings.subtitle\n' s += 'response.subtitle = settings.subtitle\n'
s+="response.meta.author = '%(author)s <%(author_email)s>' % settings\n" s += "response.meta.author = '%(author)s <%(author_email)s>' % settings\n"
s+='response.meta.keywords = settings.keywords\n' s += 'response.meta.keywords = settings.keywords\n'
s+='response.meta.description = settings.description\n' s += 'response.meta.description = settings.description\n'
s+='response.menu = [\n' s += 'response.menu = [\n'
for page in pages: for page in pages:
if not page.startswith('error'): if not page.startswith('error'):
if page.endswith('_manage'): if page.endswith('_manage'):
@@ -403,65 +440,70 @@ def make_menu(pages):
else: else:
page_name = page page_name = page
page_name = ' '.join(x.capitalize() for x in page_name.split('_')) page_name = ' '.join(x.capitalize() for x in page_name.split('_'))
s+="(T('%s'),URL('default','%s')==URL(),URL('default','%s'),[]),\n" \ s += "(T('%s'),URL('default','%s')==URL(),URL('default','%s'),[]),\n" \
% (page_name,page,page) % (page_name, page, page)
s+=']' s += ']'
return s return s
def make_page(page,contents):
if 'auth_user' in session.app['tables'] and not page in ('index','error'): def make_page(page, contents):
s="@auth.requires_login()\ndef %s():\n" % page if 'auth_user' in session.app['tables'] and not page in ('index', 'error'):
s = "@auth.requires_login()\ndef %s():\n" % page
else: else:
s="def %s():\n" % page s = "def %s():\n" % page
items = page.rsplit('_',1) items = page.rsplit('_', 1)
if items[0] in session.app['tables'] and len(items)==2 and items[1]=='manage': if items[0] in session.app['tables'] and len(items) == 2 and items[1] == 'manage':
s+=" form = SQLFORM.smartgrid(db.t_%s,onupdate=auth.archive)\n" % items[0] s += " form = SQLFORM.smartgrid(db.t_%s,onupdate=auth.archive)\n" % items[0]
s+=" return locals()\n\n" s += " return locals()\n\n"
else: else:
s+=" return dict()\n\n" s += " return dict()\n\n"
return s return s
def make_view(page,contents):
s="{{extend 'layout.html'}}\n\n" def make_view(page, contents):
s+=str(MARKMIN(contents)) s = "{{extend 'layout.html'}}\n\n"
s += str(MARKMIN(contents))
return s return s
def populate(tables): def populate(tables):
s = 'from gluon.contrib.populate import populate\n' s = 'from gluon.contrib.populate import populate\n'
s+= 'if db(db.auth_user).isempty():\n' s += 'if db(db.auth_user).isempty():\n'
for table in sort_tables(tables): for table in sort_tables(tables):
t=table=='auth_user' and 'auth_user' or 't_'+table t = table == 'auth_user' and 'auth_user' or 't_' + table
s+=" populate(db.%s,10)\n" % t s += " populate(db.%s,10)\n" % t
return s return s
def create(options): def create(options):
if DEMO_MODE: if DEMO_MODE:
session.flash = T('disabled in demo mode') session.flash = T('disabled in demo mode')
redirect(URL('step6')) redirect(URL('step6'))
params = dict(session.app['params']) params = dict(session.app['params'])
app = session.app['name'] app = session.app['name']
if app_create(app,request,force=True,key=params['security_key']): if app_create(app, request, force=True, key=params['security_key']):
if MULTI_USER_MODE: if MULTI_USER_MODE:
db.app.insert(name=app,owner=auth.user.id) db.app.insert(name=app, owner=auth.user.id)
else: else:
session.flash = 'Failure to create application' session.flash = 'Failure to create application'
redirect(URL('step6')) redirect(URL('step6'))
### save metadata in newapp/wizard.metadata ### save metadata in newapp/wizard.metadata
try: try:
meta = os.path.join(request.folder,'..',app,'wizard.metadata') meta = os.path.join(request.folder, '..', app, 'wizard.metadata')
file=open(meta,'wb') file = open(meta, 'wb')
pickle.dump(session.app,file) pickle.dump(session.app, file)
file.close() file.close()
except IOError: except IOError:
session.flash = 'Failure to write wizard metadata' session.flash = 'Failure to write wizard metadata'
redirect(URL('step6')) redirect(URL('step6'))
### apply theme ### apply theme
if options.apply_layout and params['layout_theme']!='Default': if options.apply_layout and params['layout_theme'] != 'Default':
try: try:
fn = 'web2py.plugin.layout_%s.w2p' % params['layout_theme'] fn = 'web2py.plugin.layout_%s.w2p' % params['layout_theme']
theme = urllib.urlopen(LAYOUTS_APP+'/static/plugin_layouts/plugins/'+fn) theme = urllib.urlopen(
LAYOUTS_APP + '/static/plugin_layouts/plugins/' + fn)
plugin_install(app, theme, request, fn) plugin_install(app, theme, request, fn)
except: except:
session.flash = T("unable to download layout") session.flash = T("unable to download layout")
@@ -469,55 +511,58 @@ def create(options):
### apply plugins ### apply plugins
for plugin in params['plugins']: for plugin in params['plugins']:
try: try:
plugin_name = 'web2py.plugin.'+plugin+'.w2p' plugin_name = 'web2py.plugin.' + plugin + '.w2p'
stream = urllib.urlopen(PLUGINS_APP+'/static/'+plugin_name) stream = urllib.urlopen(PLUGINS_APP + '/static/' + plugin_name)
plugin_install(app, stream, request, plugin_name) plugin_install(app, stream, request, plugin_name)
except Exception, e: except Exception, e:
session.flash = T("unable to download plugin: %s" % plugin) session.flash = T("unable to download plugin: %s" % plugin)
### write configuration file into newapp/models/0.py ### write configuration file into newapp/models/0.py
model = os.path.join(request.folder,'..',app,'models','0.py') model = os.path.join(request.folder, '..', app, 'models', '0.py')
file = open(model, 'wb') file = open(model, 'wb')
try: try:
file.write("from gluon.storage import Storage\n") file.write("from gluon.storage import Storage\n")
file.write("settings = Storage()\n\n") file.write("settings = Storage()\n\n")
file.write("settings.migrate = True\n") file.write("settings.migrate = True\n")
for key,value in session.app['params']: for key, value in session.app['params']:
file.write("settings.%s = %s\n" % (key,repr(value))) file.write("settings.%s = %s\n" % (key, repr(value)))
finally: finally:
file.close() file.close()
### write configuration file into newapp/models/menu.py ### write configuration file into newapp/models/menu.py
if options.generate_menu: if options.generate_menu:
model = os.path.join(request.folder,'..',app,'models','menu.py') model = os.path.join(request.folder, '..', app, 'models', 'menu.py')
file = open(model,'wb') file = open(model, 'wb')
try: try:
file.write(make_menu(session.app['pages'])) file.write(make_menu(session.app['pages']))
finally: finally:
file.close() file.close()
### customize the auth_user table ### customize the auth_user table
model = os.path.join(request.folder,'..',app,'models','db.py') model = os.path.join(request.folder, '..', app, 'models', 'db.py')
fix_db(model) fix_db(model)
### create newapp/models/db_wizard.py ### create newapp/models/db_wizard.py
if options.generate_model: if options.generate_model:
model = os.path.join(request.folder,'..',app,'models','db_wizard.py') model = os.path.join(
file = open(model,'wb') request.folder, '..', app, 'models', 'db_wizard.py')
file = open(model, 'wb')
try: try:
file.write('### we prepend t_ to tablenames and f_ to fieldnames for disambiguity\n\n') file.write('### we prepend t_ to tablenames and f_ to fieldnames for disambiguity\n\n')
tables = sort_tables(session.app['tables']) tables = sort_tables(session.app['tables'])
for table in tables: for table in tables:
if table=='auth_user': continue if table == 'auth_user':
file.write(make_table(table,session.app['table_'+table])) continue
file.write(make_table(table, session.app['table_' + table]))
finally: finally:
file.close() file.close()
model = os.path.join(request.folder,'..',app, model = os.path.join(request.folder, '..', app,
'models','db_wizard_populate.py') 'models', 'db_wizard_populate.py')
if os.path.exists(model): os.unlink(model) if os.path.exists(model):
os.unlink(model)
if options.populate_database and session.app['tables']: if options.populate_database and session.app['tables']:
file = open(model,'wb') file = open(model, 'wb')
try: try:
file.write(populate(session.app['tables'])) file.write(populate(session.app['tables']))
finally: finally:
@@ -525,8 +570,9 @@ def create(options):
### create newapp/controllers/default.py ### create newapp/controllers/default.py
if options.generate_controller: if options.generate_controller:
controller = os.path.join(request.folder,'..',app,'controllers','default.py') controller = os.path.join(
file = open(controller,'wb') request.folder, '..', app, 'controllers', 'default.py')
file = open(controller, 'wb')
try: try:
file.write("""# -*- coding: utf-8 -*- file.write("""# -*- coding: utf-8 -*-
### required - do no delete ### required - do no delete
@@ -536,23 +582,24 @@ def call(): return service()
### end requires ### end requires
""") """)
for page in session.app['pages']: for page in session.app['pages']:
file.write(make_page(page,session.app.get('page_'+page,''))) file.write(
make_page(page, session.app.get('page_' + page, '')))
finally: finally:
file.close() file.close()
### create newapp/views/default/*.html ### create newapp/views/default/*.html
if options.generate_views: if options.generate_views:
for page in session.app['pages']: for page in session.app['pages']:
view = os.path.join(request.folder,'..',app,'views','default',page+'.html') view = os.path.join(
file = open(view,'wb') request.folder, '..', app, 'views', 'default', page + '.html')
file = open(view, 'wb')
try: try:
file.write(make_view(page,session.app.get('page_'+page,''))) file.write(
make_view(page, session.app.get('page_' + page, '')))
finally: finally:
file.close() file.close()
if options.erase_database: if options.erase_database:
path = os.path.join(request.folder,'..',app,'databases','*') path = os.path.join(request.folder, '..', app, 'databases', '*')
for file in glob.glob(path): for file in glob.glob(path):
os.unlink(file) os.unlink(file)
+3 -5
View File
@@ -1,10 +1,10 @@
EXPIRATION_MINUTES=60 EXPIRATION_MINUTES=60
DIGITS=('0','1','2','3','4','5','6','7','8','9') DIGITS=('0','1','2','3','4','5','6','7','8','9')
import os, time, stat, cPickle, logging import os, time, stat, cPickle, logging
path=os.path.join(request.folder,'sessions') path = os.path.join(request.folder,'sessions')
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.mkdir(path)
now=time.time() now = time.time()
for filename in os.listdir(path): for filename in os.listdir(path):
fullpath=os.path.join(path,filename) fullpath=os.path.join(path,filename)
if os.path.isfile(fullpath) and filename.startswith(DIGITS): if os.path.isfile(fullpath) and filename.startswith(DIGITS):
@@ -18,6 +18,4 @@ for filename in os.listdir(path):
if (now - filetime) > expiration: if (now - filetime) > expiration:
os.unlink(fullpath) os.unlink(fullpath)
except: except:
logging.exception('failure to check %s'%fullpath) logging.exception('failure to check %s' % fullpath)
+6 -1
View File
@@ -34,10 +34,12 @@
'App does not exist or your are not authorized': 'Додаток не існує, або ви не авторизовані', 'App does not exist or your are not authorized': 'Додаток не існує, або ви не авторизовані',
'appadmin': 'Aдм.панель', 'appadmin': 'Aдм.панель',
'appadmin is disabled because insecure channel': "адмін.панель відключено через використання ненадійного каналу зв'язку", 'appadmin is disabled because insecure channel': "адмін.панель відключено через використання ненадійного каналу зв'язку",
'Application': 'Додаток (Application)',
'application "%s" uninstalled': 'додаток "%s" вилучено', 'application "%s" uninstalled': 'додаток "%s" вилучено',
'application %(appname)s installed with md5sum: %(digest)s': 'додаток %(appname)s встановлено з md5sum: %(digest)s', 'application %(appname)s installed with md5sum: %(digest)s': 'додаток %(appname)s встановлено з md5sum: %(digest)s',
'Application cannot be generated in demo mode': 'В демо-режимі генерувати додатки не можна', 'Application cannot be generated in demo mode': 'В демо-режимі генерувати додатки не можна',
'application compiled': 'додаток скомпільовано', 'application compiled': 'додаток скомпільовано',
'Application exists already': 'Додаток вже існує',
'application is compiled and cannot be designed': 'додаток скомпільований. налаштування змінювати не можна', 'application is compiled and cannot be designed': 'додаток скомпільований. налаштування змінювати не можна',
'Application name:': 'Назва додатку:', 'Application name:': 'Назва додатку:',
'are not used': 'не використовуються', 'are not used': 'не використовуються',
@@ -160,6 +162,7 @@
'Error snapshot': 'Розгорнутий знімок стану (Error snapshot)', 'Error snapshot': 'Розгорнутий знімок стану (Error snapshot)',
'Error ticket': 'Позначка (ticket) про помилку', 'Error ticket': 'Позначка (ticket) про помилку',
'Errors': 'Помилки', 'Errors': 'Помилки',
'Errors in form, please check it out.': 'Помилка у формі, будь-ласка перевірте її.',
'Exception %(extype)s: %(exvalue)s': 'Виключення %(extype)s: %(exvalue)s', 'Exception %(extype)s: %(exvalue)s': 'Виключення %(extype)s: %(exvalue)s',
'Exception %s': 'Виключення %s', 'Exception %s': 'Виключення %s',
'Exception instance attributes': 'Атрибути примірника класу Exception (виключення)', 'Exception instance attributes': 'Атрибути примірника класу Exception (виключення)',
@@ -388,6 +391,7 @@
'There are no models': 'Моделей, наразі, нема', 'There are no models': 'Моделей, наразі, нема',
'There are no modules': 'Модулів поки що нема', 'There are no modules': 'Модулів поки що нема',
'There are no plugins': 'Жодної втулки, наразі, не встановлено', 'There are no plugins': 'Жодної втулки, наразі, не встановлено',
'There are no private files': 'Приватних файлів поки що нема',
'There are no static files': 'Статичних файлів, наразі, нема', 'There are no static files': 'Статичних файлів, наразі, нема',
'There are no translators': 'Перекладів нема', 'There are no translators': 'Перекладів нема',
'There are no translators, only default language is supported': 'Перекладів нема, підтримується тільки мова оригіналу', 'There are no translators, only default language is supported': 'Перекладів нема, підтримується тільки мова оригіналу',
@@ -418,6 +422,7 @@
'Traceback': 'Стек викликів (Traceback)', 'Traceback': 'Стек викликів (Traceback)',
'Translation strings for the application': 'Пари рядків <оригінал>:<переклад> для вибраної мови', 'Translation strings for the application': 'Пари рядків <оригінал>:<переклад> для вибраної мови',
'try something like': 'спробуйте щось схоже на', 'try something like': 'спробуйте щось схоже на',
'Try the mobile interface': 'Спробуйте мобільний інтерфейс',
'try view': 'дивитись результат', 'try view': 'дивитись результат',
'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'наберіть тут будь-які команди ладнача PDB і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', 'Type PDB debugger command in here and hit Return (Enter) to execute it.': 'наберіть тут будь-які команди ладнача PDB і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.',
'Type python statement in here and hit Return (Enter) to execute it.': 'Наберіть тут будь-які вирази Python і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.', 'Type python statement in here and hit Return (Enter) to execute it.': 'Наберіть тут будь-які вирази Python і натисніть клавішу [Return] ([Enter]), щоб запустити їх на виконання.',
@@ -463,7 +468,7 @@
'Views': 'Відображення (Views)', 'Views': 'Відображення (Views)',
'views': 'відображення', 'views': 'відображення',
'WARNING:': 'ПОПЕРЕДЖЕННЯ:', 'WARNING:': 'ПОПЕРЕДЖЕННЯ:',
'Web Framework': 'Web Framework', 'Web Framework': 'Веб-каркас (Web Framework)',
'web2py apps to deploy': 'Готові до розгортання додатки web2py', 'web2py apps to deploy': 'Готові до розгортання додатки web2py',
'web2py Debugger': 'Ладнач web2py', 'web2py Debugger': 'Ладнач web2py',
'web2py downgrade': 'повернення на попередню версію web2py', 'web2py downgrade': 'повернення на попередню версію web2py',
+5 -7
View File
@@ -1,7 +1,7 @@
EXPIRATION = 60 * 60 # logout after 60 minutes of inactivity EXPIRATION = 60 * 60 # logout after 60 minutes of inactivity
CHECK_VERSION = True CHECK_VERSION = True
WEB2PY_URL = 'http://web2py.com' WEB2PY_URL = 'http://web2py.com'
WEB2PY_VERSION_URL = WEB2PY_URL+'/examples/default/version' WEB2PY_VERSION_URL = WEB2PY_URL + '/examples/default/version'
########################################################################### ###########################################################################
# Preferences for EditArea # Preferences for EditArea
@@ -21,7 +21,7 @@ TEXT_EDITOR_THEME = (
"tomorrow_night_eighties", "twilight", "vibrant_ink")[0] "tomorrow_night_eighties", "twilight", "vibrant_ink")[0]
## Editor Keyboard bindings (only for ace and codemirror) ## Editor Keyboard bindings (only for ace and codemirror)
TEXT_EDITOR_KEYBINDING = '' # 'emacs' or 'vi' TEXT_EDITOR_KEYBINDING = '' # 'emacs' or 'vi'
### edit_area only ### edit_area only
# The default font size, measured in 'points'. The value must be an integer > 0 # The default font size, measured in 'points'. The value must be an integer > 0
@@ -59,9 +59,9 @@ GAE_APPCFG = os.path.abspath(os.path.join('/usr/local/bin/appcfg.py'))
# To use web2py as a teaching tool, set MULTI_USER_MODE to True # To use web2py as a teaching tool, set MULTI_USER_MODE to True
MULTI_USER_MODE = False MULTI_USER_MODE = False
EMAIL_SERVER = 'localhost' EMAIL_SERVER = 'localhost'
EMAIL_SENDER = 'professor@example.com' EMAIL_SENDER = 'professor@example.com'
EMAIL_LOGIN = None EMAIL_LOGIN = None
# configurable twitterbox, set to None/False to suppress # configurable twitterbox, set to None/False to suppress
TWITTER_HASH = "web2py" TWITTER_HASH = "web2py"
@@ -78,5 +78,3 @@ PLUGINS_APP = 'http://web2py.com/plugins'
# set the language # set the language
if 'adminLanguage' in request.cookies and not (request.cookies['adminLanguage'] is None): if 'adminLanguage' in request.cookies and not (request.cookies['adminLanguage'] is None):
T.force(request.cookies['adminLanguage'].value) T.force(request.cookies['adminLanguage'].value)
-2
View File
@@ -28,5 +28,3 @@ from gluon.languages import findT, update_all_languages
from gluon.myregex import * from gluon.myregex import *
from gluon.restricted import * from gluon.restricted import *
from gluon.compileapp import compile_application, remove_compiled_application from gluon.compileapp import compile_application, remove_compiled_application
+27 -21
View File
@@ -1,4 +1,6 @@
import base64, os, time import base64
import os
import time
from gluon import portalocker from gluon import portalocker
from gluon.admin import apath from gluon.admin import apath
from gluon.fileutils import read_file from gluon.fileutils import read_file
@@ -24,7 +26,8 @@ elif not request.is_local and not DEMO_MODE:
try: try:
_config = {} _config = {}
port = int(request.env.server_port or 0) port = int(request.env.server_port or 0)
restricted(read_file(apath('../parameters_%i.py' % port, request)), _config) restricted(
read_file(apath('../parameters_%i.py' % port, request)), _config)
if not 'password' in _config or not _config['password']: if not 'password' in _config or not _config['password']:
raise HTTP(200, T('admin disabled because no admin password')) raise HTTP(200, T('admin disabled because no admin password'))
@@ -38,7 +41,8 @@ except IOError:
raise HTTP(200, raise HTTP(200,
T('admin disabled because not supported on google app engine')) T('admin disabled because not supported on google app engine'))
else: else:
raise HTTP(200, T('admin disabled because unable to access password file')) raise HTTP(
200, T('admin disabled because unable to access password file'))
def verify_password(password): def verify_password(password):
@@ -50,7 +54,7 @@ def verify_password(password):
elif _config['password'].startswith('pam_user:'): elif _config['password'].startswith('pam_user:'):
session.pam_user = _config['password'][9:].strip() session.pam_user = _config['password'][9:].strip()
import gluon.contrib.pam import gluon.contrib.pam
return gluon.contrib.pam.authenticate(session.pam_user,password) return gluon.contrib.pam.authenticate(session.pam_user, password)
else: else:
return _config['password'] == CRYPT()(password)[0] return _config['password'] == CRYPT()(password)[0]
@@ -63,6 +67,7 @@ deny_file = os.path.join(request.folder, 'private', 'hosts.deny')
allowed_number_of_attempts = 5 allowed_number_of_attempts = 5
expiration_failed_logins = 3600 expiration_failed_logins = 3600
def read_hosts_deny(): def read_hosts_deny():
import datetime import datetime
hosts = {} hosts = {}
@@ -75,7 +80,7 @@ def read_hosts_deny():
continue continue
fields = line.strip().split() fields = line.strip().split()
if len(fields) > 2: if len(fields) > 2:
hosts[fields[0].strip()] = ( # ip hosts[fields[0].strip()] = ( # ip
int(fields[1].strip()), # n attemps int(fields[1].strip()), # n attemps
int(fields[2].strip()) # last attempts int(fields[2].strip()) # last attempts
) )
@@ -83,28 +88,30 @@ def read_hosts_deny():
f.close() f.close()
return hosts return hosts
def write_hosts_deny(denied_hosts): def write_hosts_deny(denied_hosts):
f = open(deny_file, 'w') f = open(deny_file, 'w')
portalocker.lock(f, portalocker.LOCK_EX) portalocker.lock(f, portalocker.LOCK_EX)
for key, val in denied_hosts.items(): for key, val in denied_hosts.items():
if time.time()-val[1] < expiration_failed_logins: if time.time() - val[1] < expiration_failed_logins:
line = '%s %s %s\n' % (key, val[0], val[1]) line = '%s %s %s\n' % (key, val[0], val[1])
f.write(line) f.write(line)
portalocker.unlock(f) portalocker.unlock(f)
f.close() f.close()
def login_record(success=True): def login_record(success=True):
denied_hosts = read_hosts_deny() denied_hosts = read_hosts_deny()
val = (0,0) val = (0, 0)
if success and request.client in denied_hosts: if success and request.client in denied_hosts:
del denied_hosts[request.client] del denied_hosts[request.client]
elif not success and not request.is_local: elif not success and not request.is_local:
val = denied_hosts.get(request.client,(0,0)) val = denied_hosts.get(request.client, (0, 0))
if time.time()-val[1]<expiration_failed_logins \ if time.time() - val[1] < expiration_failed_logins \
and val[0] >= allowed_number_of_attempts: and val[0] >= allowed_number_of_attempts:
return val[0] # locked out return val[0] # locked out
time.sleep(2**val[0]) time.sleep(2 ** val[0])
val = (val[0]+1,int(time.time())) val = (val[0] + 1, int(time.time()))
denied_hosts[request.client] = val denied_hosts[request.client] = val
write_hosts_deny(denied_hosts) write_hosts_deny(denied_hosts)
return val[0] return val[0]
@@ -124,9 +131,9 @@ if session.authorized:
session.last_time = t0 session.last_time = t0
if request.vars.is_mobile in ('true','false','auto'): if request.vars.is_mobile in ('true', 'false', 'auto'):
session.is_mobile = request.vars.is_mobile or 'auto' session.is_mobile = request.vars.is_mobile or 'auto'
if request.controller=='default' and request.function=='index': if request.controller == 'default' and request.function == 'index':
if not request.vars.is_mobile: if not request.vars.is_mobile:
session.is_mobile = 'auto' session.is_mobile = 'auto'
if not session.is_mobile: if not session.is_mobile:
@@ -141,14 +148,14 @@ else:
if request.controller == "webservices": if request.controller == "webservices":
basic = request.env.http_authorization basic = request.env.http_authorization
if not basic or not basic[:6].lower() == 'basic ': if not basic or not basic[:6].lower() == 'basic ':
raise HTTP(401,"Wrong credentials") raise HTTP(401, "Wrong credentials")
(username, password) = base64.b64decode(basic[6:]).split(':') (username, password) = base64.b64decode(basic[6:]).split(':')
if not verify_password(password) or MULTI_USER_MODE: if not verify_password(password) or MULTI_USER_MODE:
time.sleep(10) time.sleep(10)
raise HTTP(403,"Not authorized") raise HTTP(403, "Not authorized")
elif not session.authorized and not \ elif not session.authorized and not \
(request.controller+'/'+request.function in (request.controller + '/' + request.function in
('default/index','default/user','plugin_jqmobile/index','plugin_jqmobile/about')): ('default/index', 'default/user', 'plugin_jqmobile/index', 'plugin_jqmobile/about')):
if request.env.query_string: if request.env.query_string:
query_string = '?' + request.env.query_string query_string = '?' + request.env.query_string
@@ -165,7 +172,6 @@ elif session.authorized and \
request.function == 'index': request.function == 'index':
redirect(URL(request.application, 'default', 'site')) redirect(URL(request.application, 'default', 'site'))
if request.controller=='appadmin' and DEMO_MODE: if request.controller == 'appadmin' and DEMO_MODE:
session.flash = 'Appadmin disabled in demo mode' session.flash = 'Appadmin disabled in demo mode'
redirect(URL('default','sites')) redirect(URL('default', 'sites'))
+16 -12
View File
@@ -2,37 +2,41 @@
import os import os
def A_button(*a,**b):
def A_button(*a, **b):
b['_data-role'] = 'button' b['_data-role'] = 'button'
b['_data-inline'] = 'true' b['_data-inline'] = 'true'
return A(*a,**b) return A(*a, **b)
def button(href, label): def button(href, label):
if is_mobile: if is_mobile:
ret = A_button(SPAN(label), _href=href) ret = A_button(SPAN(label), _href=href)
else: else:
ret = A(SPAN(label),_class='button',_href=href) ret = A(SPAN(label), _class='button', _href=href)
return ret return ret
def button_enable(href, app): def button_enable(href, app):
if os.path.exists(os.path.join(apath(app,r=request),'DISABLED')): if os.path.exists(os.path.join(apath(app, r=request), 'DISABLED')):
label = SPAN(T('Enable'),_style='color:red') label = SPAN(T('Enable'), _style='color:red')
else: else:
label = SPAN(T('Disable'),_style='color:green') label = SPAN(T('Disable'), _style='color:green')
id = 'enable_'+app id = 'enable_' + app
return A(label,_class='button',_id=id,callback=href,target=id) return A(label, _class='button', _id=id, callback=href, target=id)
def sp_button(href, label): def sp_button(href, label):
if request.user_agent().is_mobile: if request.user_agent().is_mobile:
ret = A_button(SPAN(label), _href=href) ret = A_button(SPAN(label), _href=href)
else: else:
ret = A(SPAN(label),_class='button special',_href=href) ret = A(SPAN(label), _class='button special', _href=href)
return ret return ret
def helpicon(): def helpicon():
return IMG(_src=URL('static', 'images/help.png'), _alt='help') return IMG(_src=URL('static', 'images/help.png'), _alt='help')
def searchbox(elementid): def searchbox(elementid):
return TAG[''](LABEL(IMG(_id="search_start",_src=URL('static', 'images/search.png'), _alt=T('filter')), _class='icon', _for=elementid), ' ', INPUT(_id=elementid, _type='text', _size=12)) return TAG[''](LABEL(IMG(_id="search_start", _src=URL('static', 'images/search.png'), _alt=T('filter')), _class='icon', _for=elementid), ' ', INPUT(_id=elementid, _type='text', _size=12))
+14 -12
View File
@@ -4,37 +4,39 @@
if MULTI_USER_MODE: if MULTI_USER_MODE:
db = DAL('sqlite://storage.sqlite') # if not, use SQLite or other DB db = DAL('sqlite://storage.sqlite') # if not, use SQLite or other DB
from gluon.tools import * from gluon.tools import *
auth = Auth(globals(),db) # authentication/authorization auth = Auth(
crud = Crud(globals(),db) # for CRUD helpers using auth globals(), db) # authentication/authorization
service = Service(globals()) # for json, xml, jsonrpc, xmlrpc, amfrpc crud = Crud(
globals(), db) # for CRUD helpers using auth
service = Service(
globals()) # for json, xml, jsonrpc, xmlrpc, amfrpc
plugins = PluginManager() plugins = PluginManager()
mail = auth.settings.mailer mail = auth.settings.mailer
mail.settings.server = EMAIL_SERVER mail.settings.server = EMAIL_SERVER
mail.settings.sender = EMAIL_SENDER mail.settings.sender = EMAIL_SENDER
mail.settings.login = EMAIL_LOGIN mail.settings.login = EMAIL_LOGIN
auth.settings.extra_fields['auth_user'] = \ auth.settings.extra_fields['auth_user'] = \
[Field('is_manager','boolean',default=False,writable=False)] [Field('is_manager', 'boolean', default=False, writable=False)]
auth.define_tables() # creates all needed tables auth.define_tables() # creates all needed tables
auth.settings.registration_requires_verification = False auth.settings.registration_requires_verification = False
auth.settings.registration_requires_approval = True auth.settings.registration_requires_approval = True
auth.settings.reset_password_requires_verification = True auth.settings.reset_password_requires_verification = True
db.define_table('app',Field('name'),Field('owner',db.auth_user)) db.define_table('app', Field('name'), Field('owner', db.auth_user))
if not session.authorized and MULTI_USER_MODE: if not session.authorized and MULTI_USER_MODE:
if auth.user and not request.function=='user': if auth.user and not request.function == 'user':
session.authorized = True session.authorized = True
elif not request.function=='user': elif not request.function == 'user':
redirect(URL('default','user/login')) redirect(URL('default', 'user/login'))
def is_manager(): def is_manager():
if not MULTI_USER_MODE: if not MULTI_USER_MODE:
return True return True
elif auth.user and (auth.user.id==1 or auth.user.is_manager): elif auth.user and (auth.user.id == 1 or auth.user.is_manager):
return True return True
else: else:
return False return False
+9 -10
View File
@@ -7,31 +7,30 @@ _c = request.controller
_f = request.function _f = request.function
response.title = '%s %s' % (_f, '/'.join(request.args)) response.title = '%s %s' % (_f, '/'.join(request.args))
response.subtitle = 'admin' response.subtitle = 'admin'
response.menu = [(T('Site'), _f == 'site', URL(_a,'default','site'))] response.menu = [(T('Site'), _f == 'site', URL(_a, 'default', 'site'))]
if request.vars.app or request.args: if request.vars.app or request.args:
_t = request.vars.app or request.args[0] _t = request.vars.app or request.args[0]
response.menu.append((T('Edit'), _c == 'default' and _f == 'design', response.menu.append((T('Edit'), _c == 'default' and _f == 'design',
URL(_a,'default','design',args=_t))) URL(_a, 'default', 'design', args=_t)))
response.menu.append((T('About'), _c == 'default' and _f == 'about', response.menu.append((T('About'), _c == 'default' and _f == 'about',
URL(_a,'default','about',args=_t,))) URL(_a, 'default', 'about', args=_t,)))
response.menu.append((T('Errors'), _c == 'default' and _f == 'errors', response.menu.append((T('Errors'), _c == 'default' and _f == 'errors',
URL(_a,'default','errors',args=_t))) URL(_a, 'default', 'errors', args=_t)))
response.menu.append((T('Versioning'), response.menu.append((T('Versioning'),
_c == 'mercurial' and _f == 'commit', _c == 'mercurial' and _f == 'commit',
URL(_a,'mercurial','commit',args=_t))) URL(_a, 'mercurial', 'commit', args=_t)))
if not session.authorized: if not session.authorized:
response.menu = [(T('Login'), True, URL('site'))] response.menu = [(T('Login'), True, URL('site'))]
else: else:
response.menu.append((T('Logout'), False, response.menu.append((T('Logout'), False,
URL(_a,'default',f='logout'))) URL(_a, 'default', f='logout')))
response.menu.append((T('Debug'), False, response.menu.append((T('Debug'), False,
URL(_a, 'debug','interact'))) URL(_a, 'debug', 'interact')))
if os.path.exists('applications/examples'): if os.path.exists('applications/examples'):
response.menu.append((T('Help'), False, URL('examples','default','index'))) response.menu.append(
(T('Help'), False, URL('examples', 'default', 'index')))
else: else:
response.menu.append((T('Help'), False, 'http://web2py.com/examples')) response.menu.append((T('Help'), False, 'http://web2py.com/examples'))
@@ -1,5 +1,4 @@
response.files.append(URL('static','plugin_multiselect/jquery.multi-select.js')) response.files.append(
response.files.append(URL('static','plugin_multiselect/multi-select.css')) URL('static', 'plugin_multiselect/jquery.multi-select.js'))
response.files.append(URL('static','plugin_multiselect/start.js')) response.files.append(URL('static', 'plugin_multiselect/multi-select.css'))
response.files.append(URL('static', 'plugin_multiselect/start.js'))
-1
View File
@@ -1,2 +1 @@
+6 -2
View File
@@ -61,7 +61,7 @@ input[type=text],input[type=password],select{width:300px; margin-right:5px}
border-top:1px #DEDEDE solid; border-top:1px #DEDEDE solid;
} }
.header { .header {
// background:<fill here for header image>; /* background:<fill here for header image>; */
} }
@@ -115,6 +115,10 @@ div.flash {
z-index:2000; z-index:2000;
} }
div.flash #closeflash{color:inherit; float:right; margin-left:15px;}
.ie-lte7 div.flash #closeflash
{color:expression(this.parentNode.currentStyle['color']);float:none;position:absolute;right:4px;}
div.flash:hover { opacity:0.25; } div.flash:hover { opacity:0.25; }
div.error_wrapper {display:block} div.error_wrapper {display:block}
@@ -182,7 +186,7 @@ div.error {
* will look better with the declarations below * will look better with the declarations below
* if needed to remove base.css consider keeping these following lines in some css file. * if needed to remove base.css consider keeping these following lines in some css file.
*/ */
// .web2py_table {border:1px solid #ccc} /* .web2py_table {border:1px solid #ccc} */
.web2py_paginator {} .web2py_paginator {}
.web2py_grid {width:100%} .web2py_grid {width:100%}
.web2py_grid table {width:100%} .web2py_grid table {width:100%}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10 -4
View File
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
jQuery(function() { jQuery(function() {
var flash = jQuery('.flash'); var flash = jQuery('.flash');
flash.hide(); flash.hide();
if(flash.html()) flash.append('<span style="float:right;">&times;</span>').slideDown(); if(flash.html()) flash.append('<span id="closeflash">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -102,15 +102,20 @@ function web2py_ajax_page(method, action, data, target) {
web2py_ajax_init('#'+target); web2py_ajax_init('#'+target);
if(command) if(command)
eval(decodeURIComponent(command)); eval(decodeURIComponent(command));
if(flash) if(flash) {
jQuery('.flash').html(decodeURIComponent(flash)).slideDown(); jQuery('.flash')
.html(decodeURIComponent(flash))
.append('<span id="closeflash">&times;</span>')
.slideDown();
}
} }
}); });
} }
function web2py_component(action, target, timeout, times){ function web2py_component(action, target, timeout, times){
jQuery(function(){ jQuery(function(){
var element = jQuery("#" + target).get(0); var jelement = jQuery("#" + target);
var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function (){ element.reload = function (){
// Continue if times is Infinity or // Continue if times is Infinity or
@@ -119,6 +124,7 @@ function web2py_component(action, target, timeout, times){
web2py_ajax_page('get', action, null, target);} }; // reload web2py_ajax_page('get', action, null, target);} }; // reload
// Method to check timing limit // Method to check timing limit
element.reload_check = function (){ element.reload_check = function (){
if (jelement.hasClass('w2p_component_stop')) {return false;}
if (this.reload_counter == Infinity){return true;} if (this.reload_counter == Infinity){return true;}
else { else {
if (!isNaN(this.reload_counter)){ if (!isNaN(this.reload_counter)){
@@ -0,0 +1,246 @@
{{extend 'layout.html'}}
{{block sectionclass}}debug{{end}}
<style type="text/css">
.prompt, #output {
width: 45em;
height: 1em;
border: 1px solid #CCCCCC;
font-size: 10pt;
margin: 0.5em;
padding: 0.5em;
padding-right: 0em;
overflow: auto;
wrap: hard;
}
#output {
height:150px;overflow:auto;
}
#toolbar {
margin-left: 0.5em;
padding-left: 0.5em;
}
#caret {
width: 2.5em;
margin-right: 0px;
padding-right: 0px;
border-right: 0px;
float: left;
}
#statement {
width: 43em;
margin-left: 1em;
padding-left: 0px;
border-left: 0px;
background-position: top right;
background-repeat: no-repeat;
}
.processing {
background-image: url("{{=URL('static','images/spinner.gif')}}");
}
#ajax-status {
font-weight: bold;
}
.message {
color: #8AD;
font-weight: bold;
font-style: italic;
}
.error {
color: #F44;
}
.username {
font-weight: bold;
}
</style>
<script src="{{=URL('static', 'js/autoscroll.js')}}"></script>
<div class="applist f60">
<div class="applist_inner">
<h2>{{=T("web2py online debugger")}}</h2>
{{if filename:}}
<h3>{{=T("Interaction at %s line %s") % (filename, lineno)}}</h3>
{{if exception:}}
<h3 class="exception">{{=T("Exception %s", exception['title'])}}</h3>
{{pass}}
<h5>{{=T("Code listing")}}</h5>
{{if lines:}}
<pre>{{=CODE('\n'.join([x[1] for x in sorted(lines.items(),key=lambda x: x[0])]),
language='python', link=None, counter=min(lines.keys()),
highlight_line=lineno, context_lines=10)}}</pre>
{{pass}}
<div class="help">
<ul>
<li>{{=T("Your application will be blocked until you click an action button (next, step, continue, etc.)")}}</li>
<li>{{=T("Your can inspect variables using the console bellow")}}</li>
</ul>
</div>
<h3>{{=T("Interactive console")}}</h3>
<textarea id="output" readonly="readonly">{{=data}}</textarea>
<form id="form" action="{{=URL(r=request,f='execute',args=app)}}" method="get">
<div id="shellwrapper">
<div id="caret">&gt;&gt;&gt;</div>
<div id="autoscroll" style="cursor:pointer;float:right;">autoscroll</div>
<div class="tooltip">
<textarea class="prompt" name="statement" id="statement"></textarea>
<span>{{=T("Type python statement in here and hit Return (Enter) to execute it.")}}</span>
</div>
</div>
</form>
{{elif request.env.get('wsgi_multiprocess') or not request.env.get('wsgi_multithread'):}}
<h3 class="not_paused">{{=T("Unsupported webserver working mode: %s", request.env.get('server_software', ''))}}</h3>
<div class="help">
<li><b>{{=T("WARNING:")}} </b>{{=T("This debugger may not work properly if you don't have a threaded webserver or you're using multiple daemon processes.")}}</li>
<li>{{=T("In development, use the default Rocket webserver that is currently supported by this debugger.")}}</li>
<li>{{=T("On production, you'll have to configure your webserver to use one process and multiple threads to use this debugger.")}}</li>
</ul>
</div>
{{#=BEAUTIFY(request.env)}}
{{else:}}
<h3 class="not_paused">{{=T("No Interaction yet")}}</h3>
<div class="help">
<ul>
<li>{{=T("You need to set up and reach a")}} {{=A(T("breakpoint"), _href=URL('breakpoints'))}} {{=T('to use the debugger!')}}</li>
<li>{{=T('To emulate a breakpoint programatically, write:')}}
{{=CODE("from gluon.debug import dbg\n"
"dbg.set_trace() # stop here!\n",
counter=None)}}</li>
<li>{{=T('Please')}} {{=A(T("refresh"), _href=URL('interact'))}} {{=T('this page to see if a breakpoint was hit and debug interaction is required.')}}</li>
</ul>
</div>
{{pass}}
</div>
</div>
<div class="sidebar fl60">
<div class="sidebar_inner controls">
<span class="pwdchange">
{{if filename:}}
{{=sp_button(URL('step'), T("step"))}}
{{=sp_button(URL('next'), T("next"))}}
{{=sp_button(URL('ret'), T("return"))}}
{{=sp_button(URL('cont'), T("continue"))}}
{{=sp_button(URL('stop'), T("stop"))}}
{{pass}}
{{=button(URL('breakpoints'), T("breakpoints"))}}
</span>
{{if exception:}}
<div class="box">
<h3>{{=T('Exception %(extype)s: %(exvalue)s', dict(extype=exception['extype'], exvalue=exception['exvalue']))}}</h3>
<div class="formfield">
{{=CODE((exception['request']), counter=None)}}
</div>
</div>
{{pass}}
<div class="box">
<h3>{{=T('Locals##debug')}}</h3>
<div class="formfield">
{{=BEAUTIFY(f_locals)}}
</div>
</div>
<div class="box">
<h3>{{=T('Globals##debug')}}</h3>
<div class="formfield">
{{=BEAUTIFY(f_globals)}}
</div>
</div>
</div>
</div>
<script type="text/javascript">
var bShellScrolling=0
jQuery(document).ready(function(){
jQuery('#statement').focus();
jQuery('#statement').keyup(function(event){
var t=jQuery(this),
s=t.val(),
o=jQuery('#output'),
RETURN = 38;
if(s=='\n') t.val('');
if(s.length>1 && s.substr(s.length-1,1)=='\n' && s.substr(s.length-2,1)!=':' &&
(s.indexOf(':\n ')<0 || s.substr(s.length-2,1)=='\n')) {
t.val('');
jQuery.post("{{=URL(r=request,f='execute',args=app)}}",
{statement:s},function(data){o.html(o.html()+data).attr('scrollTop',o.attr('scrollHeight'));});
} else { };
if(event.keyCode==RETURN){
var i=s.length
if(i==0){
var s=o.find('table:last pre:first').text();
bShellScrolling=o.find('table').length;
}else if(bShellScrolling){
var i=bShellScrolling
if(i<1){
return
}else{
i--
var s=o.find('table:nth-child('+(i)+') pre:first').text();
bShellScrolling=i
}
}else if(s.indexOf('\n')<0){
var oo=o.find('tr:first-child pre:contains("'+s+'")')
if(oo.length==0){
return
}else if(oo.length==1){
s=oo.text();
}else{
sVar=oo.text();
o.html(o.html()+'<dd>'+s+' ?</dd><dt>'+sVar+'</dt>').attr('scrollTop',o.attr('scrollHeight'))
return
}
}else{
//multistring expr
return;
}
// if(s.slice(s.length-1)=='\n'){
s=s.slice(0,s.length-1)
// }
t.val(s);
}
if(bShellScrolling && event.keyCode==40){
var i=bShellScrolling
i++
var s=o.find('table:nth-child('+i+') tr:first-child pre').text();
if(s){
s=s.slice(0,s.length-1)
t.val(s);
bShellScrolling=i
}else{
bShellScrolling=0
t.val('')
}
};
if(bShellScrolling && (event.keyCode==37 || event.keyCode==39)){
bShellScrolling=0;
};
if(event.keyCode==27){
bShellScrolling=0;
t.val('');
};
});
});
</script>
+2 -1
View File
@@ -134,7 +134,7 @@ jQuery(document).ready(function(){
<textarea style="width: auto; height:400px;direction:ltr;" rows="58" cols="100" -amy-enabled="true" id="body" name="data">{{=data}}</textarea> <textarea style="width: auto; height:400px;direction:ltr;" rows="58" cols="100" -amy-enabled="true" id="body" name="data">{{=data}}</textarea>
<script>window.eamy = eamy;</script> <script>window.eamy = eamy;</script>
{{elif TEXT_EDITOR == 'codemirror':}} {{elif TEXT_EDITOR == 'codemirror':}}
<textarea style="width: auto; height:400px;direction:ltr;" id="body" name="data">{{=data}}</textarea> <textarea style="width: auto; height:auto; direction:ltr;" id="body" name="data">{{=data}}</textarea>
<script> <script>
function isFullScreen(instance) { function isFullScreen(instance) {
return /\bCodeMirror-fullscreen\b/.test(instance.getWrapperElement().className); return /\bCodeMirror-fullscreen\b/.test(instance.getWrapperElement().className);
@@ -197,6 +197,7 @@ jQuery(document).ready(function(){
document.getElementById("body"),cm_opts); document.getElementById("body"),cm_opts);
var hlLine = editor.setLineClass(0, "activeline"); var hlLine = editor.setLineClass(0, "activeline");
window.mirror = editor; window.mirror = editor;
jQuery(function(){jQuery('.CodeMirror-scroll').css("height","auto").css("overflow-x","auto");});
</script> </script>
{{elif TEXT_EDITOR == 'ace':}} {{elif TEXT_EDITOR == 'ace':}}
<div id="editor" style="height: 500px; width: auto; position: relative">{{=data}}</div> <div id="editor" style="height: 500px; width: auto; position: relative">{{=data}}</div>
@@ -9,6 +9,13 @@
body { body {
background: url('{{=URL('static','plugin_jqmobile/images/iphone.jpg')}}') no-repeat white; background: url('{{=URL('static','plugin_jqmobile/images/iphone.jpg')}}') no-repeat white;
} }
#back {
z-index: 1000;
padding: 10px;
position: absolute;
top: 0;
right: 0;
}
iframe { iframe {
position: absolute; position: absolute;
margin-left: 320px; margin-left: 320px;
@@ -35,6 +42,7 @@
</head> </head>
<body> <body>
<iframe src="{{=URL('default','index',vars=dict(is_mobile='true'))}}"></iframe> <iframe src="{{=URL('default','index',vars=dict(is_mobile='true'))}}"></iframe>
<div id="back"><a href="{{=URL('default','index',vars=dict(is_mobile='false'))}}">Back</a></div>
<div id="about"> <div id="about">
<h1><a href="http://web2py.com">web2py</a> plugin</h1> <h1><a href="http://web2py.com">web2py</a> plugin</h1>
<h2>for <a href="http://jquerymobile.com/">jQuery Mobile</a></h2> <h2>for <a href="http://jquerymobile.com/">jQuery Mobile</a></h2>
+2 -2
View File
@@ -66,7 +66,7 @@
<div id="wrapper"> <div id="wrapper">
<textarea id="output" readonly="readonly">web2py Shell {{=request.env.web2py_version}}</textarea> <textarea id="output" readonly="readonly">web2py Shell {{=request.env.web2py_version}}</textarea>
<form id="form" action="{{=URL(r=request,f='callback',args=app)}}" method="get"> <form id="form" action="{{=URL('callback',args=app)}}" method="get">
<div id="shellwrapper"> <div id="shellwrapper">
<div id="caret">&gt;&gt;&gt;</div> <div id="caret">&gt;&gt;&gt;</div>
<div class="tooltip"> <div class="tooltip">
@@ -103,7 +103,7 @@ jQuery(document).ready(function(){
if(s.length>1 && s.substr(s.length-1,1)=='\n' && s.substr(s.length-2,1)!=':' && if(s.length>1 && s.substr(s.length-1,1)=='\n' && s.substr(s.length-2,1)!=':' &&
(s.indexOf(':\n ')<0 || s.substr(s.length-2,1)=='\n')) { (s.indexOf(':\n ')<0 || s.substr(s.length-2,1)=='\n')) {
t.val(''); t.val('');
jQuery.post("{{=URL(r=request,f='callback',args=app)}}", jQuery.post("{{=URL('callback',args=app)}}",
{statement:s},function(data){o.html(o.html()+data).attr('scrollTop',o.attr('scrollHeight'));}); {statement:s},function(data){o.html(o.html()+data).attr('scrollTop',o.attr('scrollHeight'));});
} else { }; } else { };
if(event.keyCode==RETURN){ if(event.keyCode==RETURN){
+1
View File
@@ -0,0 +1 @@
@@ -18,6 +18,3 @@ def flash():
def fade(): def fade():
return dict() return dict()
+71 -54
View File
@@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr
try: try:
hosts = (http_host, socket.gethostname(), hosts = (http_host, socket.gethostname(),
socket.gethostbyname(http_host), socket.gethostbyname(http_host),
'::1','127.0.0.1','::ffff:127.0.0.1') '::1', '127.0.0.1', '::ffff:127.0.0.1')
except: except:
hosts = (http_host, ) hosts = (http_host, )
@@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https:
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
raise HTTP(200, T('appadmin is disabled because insecure channel')) raise HTTP(200, T('appadmin is disabled because insecure channel'))
if (request.application=='admin' and not session.authorized) or \ if (request.application == 'admin' and not session.authorized) or \
(request.application!='admin' and not gluon.fileutils.check_credentials(request)): (request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
redirect(URL('admin', 'default', 'index', redirect(URL('admin', 'default', 'index',
vars=dict(send=URL(args=request.args,vars=request.vars)))) vars=dict(send=URL(args=request.args, vars=request.vars))))
ignore_rw = True ignore_rw = True
response.view = 'appadmin.html' response.view = 'appadmin.html'
@@ -95,24 +95,23 @@ def get_query(request):
return None return None
def query_by_table_type(tablename,db,request=request): def query_by_table_type(tablename, db, request=request):
keyed = hasattr(db[tablename],'_primarykey') keyed = hasattr(db[tablename], '_primarykey')
if keyed: if keyed:
firstkey = db[tablename][db[tablename]._primarykey[0]] firstkey = db[tablename][db[tablename]._primarykey[0]]
cond = '>0' cond = '>0'
if firstkey.type in ['string', 'text']: if firstkey.type in ['string', 'text']:
cond = '!=""' cond = '!=""'
qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) qry = '%s.%s.%s%s' % (
request.args[0], request.args[1], firstkey.name, cond)
else: else:
qry = '%s.%s.id>0' % tuple(request.args[:2]) qry = '%s.%s.id>0' % tuple(request.args[:2])
return qry return qry
# ########################################################## # ##########################################################
# ## list all databases and tables # ## list all databases and tables
# ########################################################### # ###########################################################
def index(): def index():
return dict(databases=databases) return dict(databases=databases)
@@ -127,7 +126,7 @@ def insert():
form = SQLFORM(db[table], ignore_rw=ignore_rw) form = SQLFORM(db[table], ignore_rw=ignore_rw)
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
response.flash = T('new record inserted') response.flash = T('new record inserted')
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -138,7 +137,8 @@ def insert():
def download(): def download():
import os import os
db = get_database(request) db = get_database(request)
return response.download(request,db) return response.download(request, db)
def csv(): def csv():
import gluon.contenttype import gluon.contenttype
@@ -149,26 +149,27 @@ def csv():
if not query: if not query:
return None return None
response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\
% tuple(request.vars.query.split('.')[:2]) % tuple(request.vars.query.split('.')[:2])
return str(db(query,ignore_common_filters=True).select()) return str(db(query, ignore_common_filters=True).select())
def import_csv(table, file): def import_csv(table, file):
table.import_from_csv_file(file) table.import_from_csv_file(file)
def select(): def select():
import re import re
db = get_database(request) db = get_database(request)
dbname = request.args[0] dbname = request.args[0]
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'):
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)')
if request.vars.query: if request.vars.query:
match = regex.match(request.vars.query) match = regex.match(request.vars.query)
if match: if match:
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else: else:
request.vars.query = session.last_query request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
@@ -192,46 +193,50 @@ def select():
session.last_query = request.vars.query session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _name='query', _value=request.vars.query or '',
requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox', INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px', value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields _name='update_fields', _value=request.vars.update_fields
or '')), TR(T('Delete:'), INPUT(_name='delete_check', or '')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''), _class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit')))), TR('', '', INPUT(_type='submit', _value=T('submit')))),
_action=URL(r=request,args=request.args)) _action=URL(r=request, args=request.args))
tb = None
if form.accepts(request.vars, formname=None): if form.accepts(request.vars, formname=None):
regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+') regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+')
match = regex.match(form.vars.query.strip()) match = regex.match(form.vars.query.strip())
if match: if match:
table = match.group('table') table = match.group('table')
try: try:
tb = None
nrows = db(query).count() nrows = db(query).count()
if form.vars.update_check and form.vars.update_fields: if form.vars.update_check and form.vars.update_fields:
db(query).update(**eval_in_global_env('dict(%s)' db(query).update(**eval_in_global_env('dict(%s)'
% form.vars.update_fields)) % form.vars.update_fields))
response.flash = T('%s %%{row} updated', nrows) response.flash = T('%s %%{row} updated', nrows)
elif form.vars.delete_check: elif form.vars.delete_check:
db(query).delete() db(query).delete()
response.flash = T('%s %%{row} deleted', nrows) response.flash = T('%s %%{row} deleted', nrows)
nrows = db(query).count() nrows = db(query).count()
if orderby: if orderby:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) rows = db(query, ignore_common_filters=True).select(limitby=(
start, stop), orderby=eval_in_global_env(orderby))
else: else:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) rows = db(query, ignore_common_filters=True).select(
limitby=(start, stop))
except Exception, e: except Exception, e:
import traceback import traceback
tb = traceback.format_exc() tb = traceback.format_exc()
(rows, nrows) = ([], 0) (rows, nrows) = ([], 0)
response.flash = DIV(T('Invalid Query'),PRE(str(e))) response.flash = DIV(T('Invalid Query'), PRE(str(e)))
# begin handle upload csv # begin handle upload csv
csv_table = table or request.vars.table csv_table = table or request.vars.table
if csv_table: if csv_table:
formcsv = FORM(str(T('or import from csv file'))+" ", formcsv = FORM(str(T('or import from csv file')) + " ",
INPUT(_type='file',_name='csvfile'), INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden',_value=csv_table,_name='table'), INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit',_value=T('import'))) INPUT(_type='submit', _value=T('import')))
else: else:
formcsv = None formcsv = None
if formcsv and formcsv.process().accepted: if formcsv and formcsv.process().accepted:
@@ -240,7 +245,7 @@ def select():
request.vars.csvfile.file) request.vars.csvfile.file)
response.flash = T('data uploaded') response.flash = T('data uploaded')
except Exception, e: except Exception, e:
response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) response.flash = DIV(T('unable to parse csv file'), PRE(str(e)))
# end handle upload csv # end handle upload csv
return dict( return dict(
@@ -251,9 +256,9 @@ def select():
nrows=nrows, nrows=nrows,
rows=rows, rows=rows,
query=request.vars.query, query=request.vars.query,
formcsv = formcsv, formcsv=formcsv,
tb = tb, tb=tb,
) )
# ########################################################## # ##########################################################
@@ -263,14 +268,16 @@ def select():
def update(): def update():
(db, table) = get_table(request) (db, table) = get_table(request)
keyed = hasattr(db[table],'_primarykey') keyed = hasattr(db[table], '_primarykey')
record = None record = None
if keyed: if keyed:
key = [f for f in request.vars if f in db[table]._primarykey] key = [f for f in request.vars if f in db[table]._primarykey]
if key: if key:
record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() record = db(db[table][key[0]] == request.vars[key[
0]], ignore_common_filters=True).select().first()
else: else:
record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() record = db(db[table].id == request.args(
2), ignore_common_filters=True).select().first()
if not record: if not record:
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
@@ -280,20 +287,21 @@ def update():
if keyed: if keyed:
for k in db[table]._primarykey: for k in db[table]._primarykey:
db[table][k].writable=False db[table][k].writable = False
form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), form = SQLFORM(
ignore_rw=ignore_rw and not keyed, db[table], record, deletable=True, delete_label=T('Check to delete'),
linkto=URL('select', ignore_rw=ignore_rw and not keyed,
linkto=URL('select',
args=request.args[:1]), upload=URL(r=request, args=request.args[:1]), upload=URL(r=request,
f='download', args=request.args[:1])) f='download', args=request.args[:1]))
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
session.flash = T('done!') session.flash = T('done!')
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
redirect(URL('select', args=request.args[:1], redirect(URL('select', args=request.args[:1],
vars=dict(query=qry))) vars=dict(query=qry)))
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -304,11 +312,15 @@ def update():
def state(): def state():
return dict() return dict()
def ccache(): def ccache():
form = FORM( form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), P(TAG.BUTTON(
P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
) )
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
@@ -332,11 +344,16 @@ def ccache():
redirect(URL(r=request)) redirect(URL(r=request))
try: try:
from guppy import hpy; hp=hpy() from guppy import hpy
hp = hpy()
except ImportError: except ImportError:
hp = False hp = False
import shelve, os, copy, time, math import shelve
import os
import copy
import time
import math
from gluon import portalocker from gluon import portalocker
ram = { ram = {
@@ -379,11 +396,13 @@ def ccache():
if value[0] < ram['oldest']: if value[0] < ram['oldest']:
ram['oldest'] = value[0] ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
folder = os.path.join(request.folder,'cache')
locker = open(os.path.join(request.folder, if not os.path.exists(folder):
'cache/cache.lock'), 'a') os.mkdir(folder)
locker = open(os.path.join(folder, 'cache.lock'), 'a')
portalocker.lock(locker, portalocker.LOCK_EX) portalocker.lock(locker, portalocker.LOCK_EX)
disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) disk_storage = shelve.open(
os.path.join(folder, 'cache.shelve'))
try: try:
for key, value in disk_storage.items(): for key, value in disk_storage.items():
if isinstance(value, dict): if isinstance(value, dict):
@@ -414,7 +433,8 @@ def ccache():
total['misses'] = ram['misses'] + disk['misses'] total['misses'] = ram['misses'] + disk['misses']
total['keys'] = ram['keys'] + disk['keys'] total['keys'] = ram['keys'] + disk['keys']
try: try:
total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])
except (KeyError, ZeroDivisionError): except (KeyError, ZeroDivisionError):
total['ratio'] = 0 total['ratio'] = 0
@@ -440,6 +460,3 @@ def ccache():
return dict(form=form, total=total, return dict(form=form, total=total,
ram=ram, disk=disk, object_stats=hp != False) ram=ram, disk=disk, object_stats=hp != False)
@@ -1,25 +1,24 @@
import time import time
def cache_in_ram(): def cache_in_ram():
"""cache the output of the lambda function in ram""" """cache the output of the lambda function in ram"""
t = cache.ram('time', lambda : time.ctime(), time_expire=5) t = cache.ram('time', lambda: time.ctime(), time_expire=5)
return dict(time=t, link=A('click to reload', _href=URL(r=request))) return dict(time=t, link=A('click to reload', _href=URL(r=request)))
def cache_on_disk(): def cache_on_disk():
"""cache the output of the lambda function on disk""" """cache the output of the lambda function on disk"""
t = cache.disk('time', lambda : time.ctime(), time_expire=5) t = cache.disk('time', lambda: time.ctime(), time_expire=5)
return dict(time=t, link=A('click to reload', _href=URL(r=request))) return dict(time=t, link=A('click to reload', _href=URL(r=request)))
def cache_in_ram_and_disk(): def cache_in_ram_and_disk():
"""cache the output of the lambda function on disk and in ram""" """cache the output of the lambda function on disk and in ram"""
t = cache.ram('time', lambda : cache.disk('time', lambda : \ t = cache.ram('time', lambda: cache.disk('time', lambda:
time.ctime(), time_expire=5), time_expire=5) time.ctime(), time_expire=5), time_expire=5)
return dict(time=t, link=A('click to reload', _href=URL(r=request))) return dict(time=t, link=A('click to reload', _href=URL(r=request)))
@@ -47,5 +46,3 @@ def cache_controller_and_view():
t = time.ctime() t = time.ctime()
d = dict(time=t, link=A('click to reload', _href=URL(r=request))) d = dict(time=t, link=A('click to reload', _href=URL(r=request)))
return response.render(d) return response.render(d)
+19 -4
View File
@@ -9,68 +9,83 @@ response.description = T('web2py Web Framework')
session.forget() session.forget()
cache_expire = not request.is_local and 300 or 0 cache_expire = not request.is_local and 300 or 0
@cache('index', time_expire=cache_expire) @cache('index', time_expire=cache_expire)
def index(): def index():
return response.render() return response.render()
@cache('what', time_expire=cache_expire) @cache('what', time_expire=cache_expire)
def what(): def what():
import urllib; import urllib
try: try:
images = XML(urllib.urlopen('http://web2py.com/poweredby/default/images').read()) images = XML(urllib.urlopen(
'http://web2py.com/poweredby/default/images').read())
except: except:
images = [] images = []
return response.render(images=images) return response.render(images=images)
@cache('download', time_expire=cache_expire) @cache('download', time_expire=cache_expire)
def download(): def download():
return response.render() return response.render()
@cache('who', time_expire=cache_expire) @cache('who', time_expire=cache_expire)
def who(): def who():
return response.render() return response.render()
@cache('support', time_expire=cache_expire) @cache('support', time_expire=cache_expire)
def support(): def support():
return response.render() return response.render()
@cache('documentation', time_expire=cache_expire) @cache('documentation', time_expire=cache_expire)
def documentation(): def documentation():
return response.render() return response.render()
@cache('usergroups', time_expire=cache_expire) @cache('usergroups', time_expire=cache_expire)
def usergroups(): def usergroups():
return response.render() return response.render()
def contact(): def contact():
redirect(URL('default','usergroups')) redirect(URL('default', 'usergroups'))
@cache('videos', time_expire=cache_expire) @cache('videos', time_expire=cache_expire)
def videos(): def videos():
return response.render() return response.render()
def security(): def security():
redirect('http://www.web2py.com/book/default/chapter/01#security') redirect('http://www.web2py.com/book/default/chapter/01#security')
def api(): def api():
redirect('http://web2py.com/book/default/chapter/04#API') redirect('http://web2py.com/book/default/chapter/04#API')
@cache('license', time_expire=cache_expire) @cache('license', time_expire=cache_expire)
def license(): def license():
import os import os
filename = os.path.join(request.env.gluon_parent, 'LICENSE') filename = os.path.join(request.env.gluon_parent, 'LICENSE')
return response.render(dict(license=MARKMIN(read_file(filename)))) return response.render(dict(license=MARKMIN(read_file(filename))))
def version(): def version():
return 'Version %s.%s.%s (%s) %s' % request.env.web2py_version return 'Version %s.%s.%s (%s) %s' % request.env.web2py_version
@cache('examples', time_expire=cache_expire) @cache('examples', time_expire=cache_expire)
def examples(): def examples():
return response.render() return response.render()
@cache('changelog', time_expire=cache_expire) @cache('changelog', time_expire=cache_expire)
def changelog(): def changelog():
import os import os
filename = os.path.join(request.env.gluon_parent, 'CHANGELOG') filename = os.path.join(request.env.gluon_parent, 'CHANGELOG')
return response.render(dict(changelog=MARKMIN(read_file(filename)))) return response.render(dict(changelog=MARKMIN(read_file(filename))))
@@ -1,6 +1,3 @@
def form(): def form():
""" a simple entry form with various types of objects """ """ a simple entry form with various types of objects """
@@ -15,7 +12,7 @@ def form():
TR('Profile', TEXTAREA(_name='profile', TR('Profile', TEXTAREA(_name='profile',
value='write something here')), value='write something here')),
TR('', INPUT(_type='submit', _value='SUBMIT')), TR('', INPUT(_type='submit', _value='SUBMIT')),
)) ))
if form.process().accepted: if form.process().accepted:
response.flash = 'form accepted' response.flash = 'form accepted'
elif form.errors: elif form.errors:
@@ -23,6 +20,3 @@ def form():
else: else:
response.flash = 'please fill the form' response.flash = 'please fill the form'
return dict(form=form, vars=form.vars) return dict(form=form, vars=form.vars)
+3 -7
View File
@@ -1,4 +1,3 @@
session.forget() session.forget()
response.menu = [['home', False, '/%s/default/index' response.menu = [['home', False, '/%s/default/index'
@@ -17,14 +16,14 @@ def vars():
c, c,
d, d,
value, value,
) = ( ) = (
'Global variables', 'Global variables',
globals(), globals(),
None, None,
None, None,
(), (),
None, None,
) )
(title, args) = ('globals()', '') (title, args) = ('globals()', '')
elif len(request.args) < 3: elif len(request.args) < 3:
args = '.'.join(request.args) args = '.'.join(request.args)
@@ -76,7 +75,4 @@ def vars():
d=d, d=d,
doc=doc, doc=doc,
attributes=attributes, attributes=attributes,
) )
@@ -1,6 +1,6 @@
def civilized(): def civilized():
response.menu = [['civilized', True, URL('civilized' response.menu = [['civilized', True, URL('civilized'
)], ['slick', False, URL('slick')], )], ['slick', False, URL('slick')],
['basic', False, URL('basic')]] ['basic', False, URL('basic')]]
response.flash = 'you clicked on civilized' response.flash = 'you clicked on civilized'
return dict(message='you clicked on civilized') return dict(message='you clicked on civilized')
@@ -8,7 +8,7 @@ def civilized():
def slick(): def slick():
response.menu = [['civilized', False, URL('civilized' response.menu = [['civilized', False, URL('civilized'
)], ['slick', True, URL('slick')], )], ['slick', True, URL('slick')],
['basic', False, URL('basic')]] ['basic', False, URL('basic')]]
response.flash = 'you clicked on slick' response.flash = 'you clicked on slick'
return dict(message='you clicked on slick') return dict(message='you clicked on slick')
@@ -16,10 +16,7 @@ def slick():
def basic(): def basic():
response.menu = [['civilized', False, URL('civilized' response.menu = [['civilized', False, URL('civilized'
)], ['slick', False, URL('slick')], )], ['slick', False, URL('slick')],
['basic', True, URL('basic')]] ['basic', True, URL('basic')]]
response.flash = 'you clicked on basic' response.flash = 'you clicked on basic'
return dict(message='you clicked on basic') return dict(message='you clicked on basic')
@@ -1,6 +1,3 @@
def counter(): def counter():
""" every time you reload, it increases the session.counter """ """ every time you reload, it increases the session.counter """
@@ -8,6 +5,3 @@ def counter():
session.counter = 0 session.counter = 0
session.counter += 1 session.counter += 1
return dict(counter=session.counter) return dict(counter=session.counter)
@@ -1,3 +1,4 @@
def hello1(): def hello1():
""" simple page without template """ """ simple page without template """
@@ -99,12 +100,11 @@ def rss_aggregator():
pubDate=datetime.datetime.now()) for entry in pubDate=datetime.datetime.now()) for entry in
d.entries]) d.entries])
response.headers['Content-Type'] = 'application/rss+xml' response.headers['Content-Type'] = 'application/rss+xml'
return rss2.dumps(rss) return rss.to_xml(encoding='utf-8')
def ajaxwiki(): def ajaxwiki():
default=""" default = """
# section # section
## subsection ## subsection
@@ -129,12 +129,12 @@ Quoted text
3 | 0 | 0 3 | 0 | 0
--------- ---------
""" """
form = FORM(TEXTAREA(_id='text',_name='text',value=default), form = FORM(TEXTAREA(_id='text', _name='text', value=default),
INPUT(_type='button', INPUT(_type='button',
_value='markmin', _value='markmin',
_onclick="ajax('ajaxwiki_onclick',['text'],'html')")) _onclick="ajax('ajaxwiki_onclick',['text'],'html')"))
return dict(form=form, html=DIV(_id='html')) return dict(form=form, html=DIV(_id='html'))
def ajaxwiki_onclick(): def ajaxwiki_onclick():
return MARKMIN(request.vars.text).xml() return MARKMIN(request.vars.text).xml()
@@ -1,10 +1,11 @@
from gluon.contrib.spreadsheet import Sheet from gluon.contrib.spreadsheet import Sheet
def callback(): def callback():
return cache.ram('sheet1',lambda:None,None).process(request) return cache.ram('sheet1', lambda: None, None).process(request)
def index(): def index():
sheet = cache.ram('sheet1',lambda:Sheet(10,10,URL('callback')),0) sheet = cache.ram('sheet1', lambda: Sheet(10, 10, URL('callback')), 0)
#sheet.cell('r0c3',value='=r0c0+r0c1+r0c2',readonly=True) #sheet.cell('r0c3',value='=r0c0+r0c1+r0c2',readonly=True)
return dict(sheet=sheet) return dict(sheet=sheet)
@@ -1,6 +1,3 @@
def variables(): def variables():
return dict(a=10, b=20) return dict(a=10, b=20)
@@ -31,6 +28,3 @@ def xml():
def beautify(): def beautify():
return dict(message=BEAUTIFY(request)) return dict(message=BEAUTIFY(request))
+21 -26
View File
@@ -1,49 +1,44 @@
def group_feed_reader(group, mode='div', counter='5'):
def group_feed_reader(group,mode='div',counter='5'):
"""parse group feeds""" """parse group feeds"""
url = "http://groups.google.com/group/%s/feed/rss_v2_0_topics.xml?num=%s" %\ url = "http://groups.google.com/group/%s/feed/rss_v2_0_topics.xml?num=%s" %\
(group,counter) (group, counter)
from gluon.contrib import feedparser from gluon.contrib import feedparser
g = feedparser.parse(url) g = feedparser.parse(url)
if mode == 'div': if mode == 'div':
html = XML(TAG.BLOCKQUOTE(UL(*[LI(A(entry['title']+' - ' +\ html = XML(TAG.BLOCKQUOTE(UL(*[LI(A(entry['title'] + ' - ' +
entry['author'][entry['author'].rfind('('):],\ entry['author'][
_href=entry['link'],_target='_blank'))\ entry['author'].rfind('('):],
for entry in g['entries'] ]),\ _href=entry['link'], _target='_blank'))
_class="boxInfo",\ for entry in g['entries']]),
_style="padding-bottom:5px;")) _class="boxInfo",
_style="padding-bottom:5px;"))
else: else:
html = XML(UL(*[LI(A(entry['title']+' - ' +\ html = XML(UL(*[LI(A(entry['title'] + ' - ' +
entry['author'][entry['author'].rfind('('):],\ entry['author'][entry['author'].rfind('('):],
_href=entry['link'],_target='_blank'))\ _href=entry['link'], _target='_blank'))
for entry in g['entries'] ])) for entry in g['entries']]))
return html return html
def code_feed_reader(project,mode='div'): def code_feed_reader(project, mode='div'):
"""parse code feeds""" """parse code feeds"""
url = "http://code.google.com/feeds/p/%s/hgchanges/basic" % project url = "http://code.google.com/feeds/p/%s/hgchanges/basic" % project
from gluon.contrib import feedparser from gluon.contrib import feedparser
g = feedparser.parse(url) g = feedparser.parse(url)
if mode == 'div': if mode == 'div':
html = XML(DIV(UL(*[LI(A(entry['title'],_href=entry['link'],\ html = XML(DIV(UL(*[LI(A(entry['title'], _href=entry['link'],
_target='_blank'))\ _target='_blank'))
for entry in g['entries'][0:5]]),\ for entry in g['entries'][0:5]]),
_class="boxInfo",\ _class="boxInfo",
_style="padding-bottom:5px;")) _style="padding-bottom:5px;"))
else: else:
html = XML(UL(*[LI(A(entry['title'],_href=entry['link'],\ html = XML(UL(*[LI(A(entry['title'], _href=entry['link'],
_target='_blank'))\ _target='_blank'))
for entry in g['entries'][0:5]])) for entry in g['entries'][0:5]]))
return html return html
+16 -15
View File
@@ -2,18 +2,19 @@ import gluon.template
markmin_dict = dict( markmin_dict = dict(
code_python=lambda code: str(CODE(code)), code_python=lambda code: str(CODE(code)),
template=lambda \ template=lambda
code:gluon.template.render(code,context=globals()), code: gluon.template.render(code, context=globals()),
sup=lambda \ sup=lambda
code:'<sup style="font-size:0.5em;">%s</sup>'%code, code: '<sup style="font-size:0.5em;">%s</sup>' % code,
br=lambda n:'<br>'*int(n), br=lambda n: '<br>' * int(n),
groupdates=lambda group:group_feed_reader(group), groupdates=lambda group: group_feed_reader(group),
) )
def get_content(b=None,\
c=request.controller,\ def get_content(b=None,
f=request.function,\ c=request.controller,
l='en',\ f=request.function,
l='en',
format='markmin'): format='markmin'):
"""Gets and renders the file in """Gets and renders the file in
<app>/private/content/<lang>/<controller>/<function>/<block>.<format> <app>/private/content/<lang>/<controller>/<function>/<block>.<format>
@@ -21,20 +22,20 @@ def get_content(b=None,\
def openfile(): def openfile():
import os import os
path = os.path.join(request.folder,'private','content',l,c,f,b+'.'+format) path = os.path.join(
request.folder, 'private', 'content', l, c, f, b + '.' + format)
return open(path) return open(path)
try: try:
openedfile = openfile() openedfile = openfile()
except Exception, IOError: except Exception, IOError:
l='en' l = 'en'
openedfile = openfile() openedfile = openfile()
if format == 'markmin': if format == 'markmin':
html = MARKMIN(str(T(openedfile.read())),markmin_dict) html = MARKMIN(str(T(openedfile.read())), markmin_dict)
else: else:
html = str(T(openedfile.read())) html = str(T(openedfile.read()))
openedfile.close() openedfile.close()
return html return html
+20 -20
View File
@@ -1,28 +1,29 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
response.menu = [ response.menu = [
(T('Home'),False,URL('default','index')), (T('Home'), False, URL('default', 'index')),
(T('About'),False,URL('default','what')), (T('About'), False, URL('default', 'what')),
(T('Download'),False,URL('default','download')), (T('Download'), False, URL('default', 'download')),
(T('Docs & Resources'),False,URL('default','documentation')), (T('Docs & Resources'), False, URL('default', 'documentation')),
(T('Support'),False,URL('default','support')), (T('Support'), False, URL('default', 'support')),
(T('Contributors'),False,URL('default','who'))] (T('Contributors'), False, URL('default', 'who'))]
######################################################################### #########################################################################
## Changes the menu active item ## Changes the menu active item
######################################################################### #########################################################################
def toggle_menuclass(cssclass='pressed',menuid='headermenu'):
def toggle_menuclass(cssclass='pressed', menuid='headermenu'):
"""This function changes the menu class to put pressed appearance""" """This function changes the menu class to put pressed appearance"""
positions = dict( positions = dict(
index='', index='',
what='-108px -115px', what='-108px -115px',
download='-211px -115px', download='-211px -115px',
who='-315px -115px', who='-315px -115px',
support='-418px -115px', support='-418px -115px',
documentation='-520px -115px' documentation='-520px -115px'
) )
if request.function in positions.keys(): if request.function in positions.keys():
jscript = """ jscript = """
@@ -34,12 +35,11 @@ def toggle_menuclass(cssclass='pressed',menuid='headermenu'):
}); });
</script> </script>
""" % dict(cssclass=cssclass, """ % dict(cssclass=cssclass,
menuid=menuid, menuid=menuid,
function=request.function, function=request.function,
cssposition=positions[request.function] cssposition=positions[request.function]
) )
return XML(jscript) return XML(jscript)
else: else:
return '' return ''
@@ -7,6 +7,7 @@
- [[User Voice http://web2py.uservoice.com/ popup]] - [[User Voice http://web2py.uservoice.com/ popup]]
#### Learning and Demos #### Learning and Demos
- [[Intro video http://www.youtube.com/watch?v=BXzqmHx6edY]] and [[code examples https://github.com/mjhea0/web2py]]
- [[Killer Web Development Tutorial http://killer-web-development.com/]] - [[Killer Web Development Tutorial http://killer-web-development.com/]]
- [[Admin Demo http://www.web2py.com/demo_admin popup]] (web-based IDE) - [[Admin Demo http://www.web2py.com/demo_admin popup]] (web-based IDE)
- [[Welcome App Demo http://www.web2py.com/welcome]] (scaffolding application) - [[Welcome App Demo http://www.web2py.com/welcome]] (scaffolding application)
+6 -2
View File
@@ -61,7 +61,7 @@ input[type=text],input[type=password],select{width:300px; margin-right:5px}
border-top:1px #DEDEDE solid; border-top:1px #DEDEDE solid;
} }
.header { .header {
// background:<fill here for header image>; /* background:<fill here for header image>; */
} }
@@ -115,6 +115,10 @@ div.flash {
z-index:2000; z-index:2000;
} }
div.flash #closeflash{color:inherit; float:right; margin-left:15px;}
.ie-lte7 div.flash #closeflash
{color:expression(this.parentNode.currentStyle['color']);float:none;position:absolute;right:4px;}
div.flash:hover { opacity:0.25; } div.flash:hover { opacity:0.25; }
div.error_wrapper {display:block} div.error_wrapper {display:block}
@@ -182,7 +186,7 @@ div.error {
* will look better with the declarations below * will look better with the declarations below
* if needed to remove base.css consider keeping these following lines in some css file. * if needed to remove base.css consider keeping these following lines in some css file.
*/ */
// .web2py_table {border:1px solid #ccc} /* .web2py_table {border:1px solid #ccc} */
.web2py_paginator {} .web2py_paginator {}
.web2py_grid {width:100%} .web2py_grid {width:100%}
.web2py_grid table {width:100%} .web2py_grid table {width:100%}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10 -4
View File
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
jQuery(function() { jQuery(function() {
var flash = jQuery('.flash'); var flash = jQuery('.flash');
flash.hide(); flash.hide();
if(flash.html()) flash.append('<span style="float:right;">&times;</span>').slideDown(); if(flash.html()) flash.append('<span id="closeflash">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -102,15 +102,20 @@ function web2py_ajax_page(method, action, data, target) {
web2py_ajax_init('#'+target); web2py_ajax_init('#'+target);
if(command) if(command)
eval(decodeURIComponent(command)); eval(decodeURIComponent(command));
if(flash) if(flash) {
jQuery('.flash').html(decodeURIComponent(flash)).slideDown(); jQuery('.flash')
.html(decodeURIComponent(flash))
.append('<span id="closeflash">&times;</span>')
.slideDown();
}
} }
}); });
} }
function web2py_component(action, target, timeout, times){ function web2py_component(action, target, timeout, times){
jQuery(function(){ jQuery(function(){
var element = jQuery("#" + target).get(0); var jelement = jQuery("#" + target);
var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function (){ element.reload = function (){
// Continue if times is Infinity or // Continue if times is Infinity or
@@ -119,6 +124,7 @@ function web2py_component(action, target, timeout, times){
web2py_ajax_page('get', action, null, target);} }; // reload web2py_ajax_page('get', action, null, target);} }; // reload
// Method to check timing limit // Method to check timing limit
element.reload_check = function (){ element.reload_check = function (){
if (jelement.hasClass('w2p_component_stop')) {return false;}
if (this.reload_counter == Infinity){return true;} if (this.reload_counter == Infinity){return true;}
else { else {
if (!isNaN(this.reload_counter)){ if (!isNaN(this.reload_counter)){
@@ -29,6 +29,7 @@
</li><li>Alvaro Justen (dynamical translations) </li><li>Alvaro Justen (dynamical translations)
</li><li>Anders Roos (file locking) </li><li>Anders Roos (file locking)
</li><li>Andrew Willimott (documentation, TeraData support) </li><li>Andrew Willimott (documentation, TeraData support)
</li><li>Andriy Kornatskyy (benchmarks and profiling)
</li><li>Angelo Compagnucci (mobile devices) </li><li>Angelo Compagnucci (mobile devices)
</li><li>Anthony Bastardi (book, poweredby site, multiple contributions) </li><li>Anthony Bastardi (book, poweredby site, multiple contributions)
</li><li>Arun K. Rajeevan (plugin_wiki) </li><li>Arun K. Rajeevan (plugin_wiki)
@@ -0,0 +1,9 @@
{{extend 'layout.html'}}
<h2>form.vars</h2>
<pre>{{=vars}}</pre>
<h2>form</h2>
{{=form}}
+1 -15
View File
@@ -1,15 +1 @@
{{ {{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}}
###
# response._vars contains the dictionary returned by thecontroller action
###
try:
from gluon.serializers import xml
response.write(xml(response._vars), escape=False)
response.headers['Content-Type'] = 'text/xml'
except (TypeError, ValueError):
raise HTTP(405, 'XML serialization error')
except ImportError:
raise HTTP(405, 'XML not available')
except:
raise HTTP(405, 'XML error')
}}
@@ -0,0 +1,3 @@
{{extend 'layout.html'}}
{{=toolbar}}
+1
View File
@@ -0,0 +1 @@
+71 -54
View File
@@ -23,7 +23,7 @@ remote_addr = request.env.remote_addr
try: try:
hosts = (http_host, socket.gethostname(), hosts = (http_host, socket.gethostname(),
socket.gethostbyname(http_host), socket.gethostbyname(http_host),
'::1','127.0.0.1','::ffff:127.0.0.1') '::1', '127.0.0.1', '::ffff:127.0.0.1')
except: except:
hosts = (http_host, ) hosts = (http_host, )
@@ -32,10 +32,10 @@ if request.env.http_x_forwarded_for or request.is_https:
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"): elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
raise HTTP(200, T('appadmin is disabled because insecure channel')) raise HTTP(200, T('appadmin is disabled because insecure channel'))
if (request.application=='admin' and not session.authorized) or \ if (request.application == 'admin' and not session.authorized) or \
(request.application!='admin' and not gluon.fileutils.check_credentials(request)): (request.application != 'admin' and not gluon.fileutils.check_credentials(request)):
redirect(URL('admin', 'default', 'index', redirect(URL('admin', 'default', 'index',
vars=dict(send=URL(args=request.args,vars=request.vars)))) vars=dict(send=URL(args=request.args, vars=request.vars))))
ignore_rw = True ignore_rw = True
response.view = 'appadmin.html' response.view = 'appadmin.html'
@@ -95,24 +95,23 @@ def get_query(request):
return None return None
def query_by_table_type(tablename,db,request=request): def query_by_table_type(tablename, db, request=request):
keyed = hasattr(db[tablename],'_primarykey') keyed = hasattr(db[tablename], '_primarykey')
if keyed: if keyed:
firstkey = db[tablename][db[tablename]._primarykey[0]] firstkey = db[tablename][db[tablename]._primarykey[0]]
cond = '>0' cond = '>0'
if firstkey.type in ['string', 'text']: if firstkey.type in ['string', 'text']:
cond = '!=""' cond = '!=""'
qry = '%s.%s.%s%s' % (request.args[0], request.args[1], firstkey.name, cond) qry = '%s.%s.%s%s' % (
request.args[0], request.args[1], firstkey.name, cond)
else: else:
qry = '%s.%s.id>0' % tuple(request.args[:2]) qry = '%s.%s.id>0' % tuple(request.args[:2])
return qry return qry
# ########################################################## # ##########################################################
# ## list all databases and tables # ## list all databases and tables
# ########################################################### # ###########################################################
def index(): def index():
return dict(databases=databases) return dict(databases=databases)
@@ -127,7 +126,7 @@ def insert():
form = SQLFORM(db[table], ignore_rw=ignore_rw) form = SQLFORM(db[table], ignore_rw=ignore_rw)
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
response.flash = T('new record inserted') response.flash = T('new record inserted')
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -138,7 +137,8 @@ def insert():
def download(): def download():
import os import os
db = get_database(request) db = get_database(request)
return response.download(request,db) return response.download(request, db)
def csv(): def csv():
import gluon.contenttype import gluon.contenttype
@@ -149,26 +149,27 @@ def csv():
if not query: if not query:
return None return None
response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\ response.headers['Content-disposition'] = 'attachment; filename=%s_%s.csv'\
% tuple(request.vars.query.split('.')[:2]) % tuple(request.vars.query.split('.')[:2])
return str(db(query,ignore_common_filters=True).select()) return str(db(query, ignore_common_filters=True).select())
def import_csv(table, file): def import_csv(table, file):
table.import_from_csv_file(file) table.import_from_csv_file(file)
def select(): def select():
import re import re
db = get_database(request) db = get_database(request)
dbname = request.args[0] dbname = request.args[0]
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>\d+)')
if len(request.args)>1 and hasattr(db[request.args[1]],'_primarykey'): if len(request.args) > 1 and hasattr(db[request.args[1]], '_primarykey'):
regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)') regex = re.compile('(?P<table>\w+)\.(?P<field>\w+)=(?P<value>.+)')
if request.vars.query: if request.vars.query:
match = regex.match(request.vars.query) match = regex.match(request.vars.query)
if match: if match:
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else: else:
request.vars.query = session.last_query request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
@@ -192,46 +193,50 @@ def select():
session.last_query = request.vars.query session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _name='query', _value=request.vars.query or '',
requires=IS_NOT_EMPTY(error_message=T("Cannot be empty")))), TR(T('Update:'), requires=IS_NOT_EMPTY(
error_message=T("Cannot be empty")))), TR(T('Update:'),
INPUT(_name='update_check', _type='checkbox', INPUT(_name='update_check', _type='checkbox',
value=False), INPUT(_style='width:400px', value=False), INPUT(_style='width:400px',
_name='update_fields', _value=request.vars.update_fields _name='update_fields', _value=request.vars.update_fields
or '')), TR(T('Delete:'), INPUT(_name='delete_check', or '')), TR(T('Delete:'), INPUT(_name='delete_check',
_class='delete', _type='checkbox', value=False), ''), _class='delete', _type='checkbox', value=False), ''),
TR('', '', INPUT(_type='submit', _value=T('submit')))), TR('', '', INPUT(_type='submit', _value=T('submit')))),
_action=URL(r=request,args=request.args)) _action=URL(r=request, args=request.args))
tb = None
if form.accepts(request.vars, formname=None): if form.accepts(request.vars, formname=None):
regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+') regex = re.compile(request.args[0] + '\.(?P<table>\w+)\..+')
match = regex.match(form.vars.query.strip()) match = regex.match(form.vars.query.strip())
if match: if match:
table = match.group('table') table = match.group('table')
try: try:
tb = None
nrows = db(query).count() nrows = db(query).count()
if form.vars.update_check and form.vars.update_fields: if form.vars.update_check and form.vars.update_fields:
db(query).update(**eval_in_global_env('dict(%s)' db(query).update(**eval_in_global_env('dict(%s)'
% form.vars.update_fields)) % form.vars.update_fields))
response.flash = T('%s %%{row} updated', nrows) response.flash = T('%s %%{row} updated', nrows)
elif form.vars.delete_check: elif form.vars.delete_check:
db(query).delete() db(query).delete()
response.flash = T('%s %%{row} deleted', nrows) response.flash = T('%s %%{row} deleted', nrows)
nrows = db(query).count() nrows = db(query).count()
if orderby: if orderby:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop), orderby=eval_in_global_env(orderby)) rows = db(query, ignore_common_filters=True).select(limitby=(
start, stop), orderby=eval_in_global_env(orderby))
else: else:
rows = db(query,ignore_common_filters=True).select(limitby=(start, stop)) rows = db(query, ignore_common_filters=True).select(
limitby=(start, stop))
except Exception, e: except Exception, e:
import traceback import traceback
tb = traceback.format_exc() tb = traceback.format_exc()
(rows, nrows) = ([], 0) (rows, nrows) = ([], 0)
response.flash = DIV(T('Invalid Query'),PRE(str(e))) response.flash = DIV(T('Invalid Query'), PRE(str(e)))
# begin handle upload csv # begin handle upload csv
csv_table = table or request.vars.table csv_table = table or request.vars.table
if csv_table: if csv_table:
formcsv = FORM(str(T('or import from csv file'))+" ", formcsv = FORM(str(T('or import from csv file')) + " ",
INPUT(_type='file',_name='csvfile'), INPUT(_type='file', _name='csvfile'),
INPUT(_type='hidden',_value=csv_table,_name='table'), INPUT(_type='hidden', _value=csv_table, _name='table'),
INPUT(_type='submit',_value=T('import'))) INPUT(_type='submit', _value=T('import')))
else: else:
formcsv = None formcsv = None
if formcsv and formcsv.process().accepted: if formcsv and formcsv.process().accepted:
@@ -240,7 +245,7 @@ def select():
request.vars.csvfile.file) request.vars.csvfile.file)
response.flash = T('data uploaded') response.flash = T('data uploaded')
except Exception, e: except Exception, e:
response.flash = DIV(T('unable to parse csv file'),PRE(str(e))) response.flash = DIV(T('unable to parse csv file'), PRE(str(e)))
# end handle upload csv # end handle upload csv
return dict( return dict(
@@ -251,9 +256,9 @@ def select():
nrows=nrows, nrows=nrows,
rows=rows, rows=rows,
query=request.vars.query, query=request.vars.query,
formcsv = formcsv, formcsv=formcsv,
tb = tb, tb=tb,
) )
# ########################################################## # ##########################################################
@@ -263,14 +268,16 @@ def select():
def update(): def update():
(db, table) = get_table(request) (db, table) = get_table(request)
keyed = hasattr(db[table],'_primarykey') keyed = hasattr(db[table], '_primarykey')
record = None record = None
if keyed: if keyed:
key = [f for f in request.vars if f in db[table]._primarykey] key = [f for f in request.vars if f in db[table]._primarykey]
if key: if key:
record = db(db[table][key[0]] == request.vars[key[0]], ignore_common_filters=True).select().first() record = db(db[table][key[0]] == request.vars[key[
0]], ignore_common_filters=True).select().first()
else: else:
record = db(db[table].id == request.args(2),ignore_common_filters=True).select().first() record = db(db[table].id == request.args(
2), ignore_common_filters=True).select().first()
if not record: if not record:
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
@@ -280,20 +287,21 @@ def update():
if keyed: if keyed:
for k in db[table]._primarykey: for k in db[table]._primarykey:
db[table][k].writable=False db[table][k].writable = False
form = SQLFORM(db[table], record, deletable=True, delete_label=T('Check to delete'), form = SQLFORM(
ignore_rw=ignore_rw and not keyed, db[table], record, deletable=True, delete_label=T('Check to delete'),
linkto=URL('select', ignore_rw=ignore_rw and not keyed,
linkto=URL('select',
args=request.args[:1]), upload=URL(r=request, args=request.args[:1]), upload=URL(r=request,
f='download', args=request.args[:1])) f='download', args=request.args[:1]))
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
session.flash = T('done!') session.flash = T('done!')
qry = query_by_table_type(table, db) qry = query_by_table_type(table, db)
redirect(URL('select', args=request.args[:1], redirect(URL('select', args=request.args[:1],
vars=dict(query=qry))) vars=dict(query=qry)))
return dict(form=form,table=db[table]) return dict(form=form, table=db[table])
# ########################################################## # ##########################################################
@@ -304,11 +312,15 @@ def update():
def state(): def state():
return dict() return dict()
def ccache(): def ccache():
form = FORM( form = FORM(
P(TAG.BUTTON(T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")), P(TAG.BUTTON(
P(TAG.BUTTON(T("Clear RAM"), _type="submit", _name="ram", _value="ram")), T("Clear CACHE?"), _type="submit", _name="yes", _value="yes")),
P(TAG.BUTTON(T("Clear DISK"), _type="submit", _name="disk", _value="disk")), P(TAG.BUTTON(
T("Clear RAM"), _type="submit", _name="ram", _value="ram")),
P(TAG.BUTTON(
T("Clear DISK"), _type="submit", _name="disk", _value="disk")),
) )
if form.accepts(request.vars, session): if form.accepts(request.vars, session):
@@ -332,11 +344,16 @@ def ccache():
redirect(URL(r=request)) redirect(URL(r=request))
try: try:
from guppy import hpy; hp=hpy() from guppy import hpy
hp = hpy()
except ImportError: except ImportError:
hp = False hp = False
import shelve, os, copy, time, math import shelve
import os
import copy
import time
import math
from gluon import portalocker from gluon import portalocker
ram = { ram = {
@@ -379,11 +396,13 @@ def ccache():
if value[0] < ram['oldest']: if value[0] < ram['oldest']:
ram['oldest'] = value[0] ram['oldest'] = value[0]
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
folder = os.path.join(request.folder,'cache')
locker = open(os.path.join(request.folder, if not os.path.exists(folder):
'cache/cache.lock'), 'a') os.mkdir(folder)
locker = open(os.path.join(folder, 'cache.lock'), 'a')
portalocker.lock(locker, portalocker.LOCK_EX) portalocker.lock(locker, portalocker.LOCK_EX)
disk_storage = shelve.open(os.path.join(request.folder, 'cache/cache.shelve')) disk_storage = shelve.open(
os.path.join(folder, 'cache.shelve'))
try: try:
for key, value in disk_storage.items(): for key, value in disk_storage.items():
if isinstance(value, dict): if isinstance(value, dict):
@@ -414,7 +433,8 @@ def ccache():
total['misses'] = ram['misses'] + disk['misses'] total['misses'] = ram['misses'] + disk['misses']
total['keys'] = ram['keys'] + disk['keys'] total['keys'] = ram['keys'] + disk['keys']
try: try:
total['ratio'] = total['hits'] * 100 / (total['hits'] + total['misses']) total['ratio'] = total['hits'] * 100 / (total['hits'] +
total['misses'])
except (KeyError, ZeroDivisionError): except (KeyError, ZeroDivisionError):
total['ratio'] = 0 total['ratio'] = 0
@@ -440,6 +460,3 @@ def ccache():
return dict(form=form, total=total, return dict(form=form, total=total,
ram=ram, disk=disk, object_stats=hp != False) ram=ram, disk=disk, object_stats=hp != False)
+3 -1
View File
@@ -9,6 +9,7 @@
## - call exposes all registered services (none by default) ## - call exposes all registered services (none by default)
######################################################################### #########################################################################
def index(): def index():
""" """
example action using the internationalization operator T and flash example action using the internationalization operator T and flash
@@ -20,6 +21,7 @@ def index():
response.flash = T("Welcome to web2py!") response.flash = T("Welcome to web2py!")
return dict(message=T('Hello World')) return dict(message=T('Hello World'))
def user(): def user():
""" """
exposes: exposes:
@@ -42,7 +44,7 @@ def download():
allows downloading of uploaded files allows downloading of uploaded files
http://..../[app]/default/download/[filename] http://..../[app]/default/download/[filename]
""" """
return response.download(request,db) return response.download(request, db)
def call(): def call():
+1
View File
@@ -89,6 +89,7 @@
'export as csv file': 'експортувати як файл csv', 'export as csv file': 'експортувати як файл csv',
'FAQ': 'ЧаПи (FAQ)', 'FAQ': 'ЧаПи (FAQ)',
'First name': "Ім'я", 'First name': "Ім'я",
'Forgot username?': "Забули ім'я користувача?",
'Forms and Validators': 'Форми та коректність даних', 'Forms and Validators': 'Форми та коректність даних',
'Free Applications': 'Вільні додатки', 'Free Applications': 'Вільні додатки',
'Group %(group_id)s created': 'Групу %(group_id)s створено', 'Group %(group_id)s created': 'Групу %(group_id)s створено',
+4 -4
View File
@@ -11,12 +11,12 @@
if not request.env.web2py_runtime_gae: if not request.env.web2py_runtime_gae:
## if NOT running on Google App Engine use SQLite or other DB ## if NOT running on Google App Engine use SQLite or other DB
db = DAL('sqlite://storage.sqlite') db = DAL('sqlite://storage.sqlite',pool_size=1,check_reserved=['all'])
else: else:
## connect to Google BigTable (optional 'google:datastore://namespace') ## connect to Google BigTable (optional 'google:datastore://namespace')
db = DAL('google:datastore') db = DAL('google:datastore')
## store sessions and tickets there ## store sessions and tickets there
session.connect(request, response, db = db) session.connect(request, response, db=db)
## or store session in Memcache, Redis, etc. ## or store session in Memcache, Redis, etc.
## from gluon.contrib.memdb import MEMDB ## from gluon.contrib.memdb import MEMDB
## from google.appengine.api.memcache import Client ## from google.appengine.api.memcache import Client
@@ -47,7 +47,7 @@ crud, service, plugins = Crud(db), Service(), PluginManager()
auth.define_tables(username=False, signature=False) auth.define_tables(username=False, signature=False)
## configure email ## configure email
mail=auth.settings.mailer mail = auth.settings.mailer
mail.settings.server = 'logging' or 'smtp.gmail.com:587' mail.settings.server = 'logging' or 'smtp.gmail.com:587'
mail.settings.sender = 'you@gmail.com' mail.settings.sender = 'you@gmail.com'
mail.settings.login = 'username:password' mail.settings.login = 'username:password'
@@ -60,7 +60,7 @@ auth.settings.reset_password_requires_verification = True
## if you need to use OpenID, Facebook, MySpace, Twitter, Linkedin, etc. ## if you need to use OpenID, Facebook, MySpace, Twitter, Linkedin, etc.
## register with janrain.com, write your domain:api_key in private/janrain.key ## register with janrain.com, write your domain:api_key in private/janrain.key
from gluon.contrib.login_methods.rpx_account import use_janrain from gluon.contrib.login_methods.rpx_account import use_janrain
use_janrain(auth,filename='private/janrain.key') use_janrain(auth, filename='private/janrain.key')
######################################################################### #########################################################################
## Define your tables below (or better in another model file) for example ## Define your tables below (or better in another model file) for example
+102 -64
View File
@@ -5,7 +5,10 @@
## Customize your APP title, subtitle and menus here ## Customize your APP title, subtitle and menus here
######################################################################### #########################################################################
response.title = ' '.join(word.capitalize() for word in request.application.split('_')) response.logo = A(B('web',SPAN(2),'py'),XML('&trade;&nbsp;'),
_class="brand",_href="http://www.web2py.com/")
response.title = ' '.join(
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
@@ -22,8 +25,10 @@ response.google_analytics_id = None
######################################################################### #########################################################################
response.menu = [ response.menu = [
(T('Home'), False, URL('default','index'), []) (T('Home'), False, URL('default', 'index'), [])
] ]
DEVELOPMENT_MENU = True
######################################################################### #########################################################################
## provide shortcuts for development. remove in production ## provide shortcuts for development. remove in production
@@ -34,68 +39,101 @@ def _():
app = request.application app = request.application
ctr = request.controller ctr = request.controller
# useful links to internal and external resources # useful links to internal and external resources
response.menu+=[ response.menu += [
(SPAN('web2py',_class='highlighted'),False, 'http://web2py.com', [ (SPAN('web2py', _class='highlighted'), False, 'http://web2py.com', [
(T('My Sites'),False,URL('admin','default','site')), (T('My Sites'), False, URL('admin', 'default', 'site')),
(T('This App'),False,URL('admin','default','design/%s' % app), [ (T('This App'), False, URL('admin', 'default', 'design/%s' % app), [
(T('Controller'),False, (T('Controller'), False,
URL('admin','default','edit/%s/controllers/%s.py' % (app,ctr))), URL(
(T('View'),False, 'admin', 'default', 'edit/%s/controllers/%s.py' % (app, ctr))),
URL('admin','default','edit/%s/views/%s' % (app,response.view))), (T('View'), False,
(T('Layout'),False, URL(
URL('admin','default','edit/%s/views/layout.html' % app)), 'admin', 'default', 'edit/%s/views/%s' % (app, response.view))),
(T('Stylesheet'),False, (T('Layout'), False,
URL('admin','default','edit/%s/static/css/web2py.css' % app)), URL(
(T('DB Model'),False, 'admin', 'default', 'edit/%s/views/layout.html' % app)),
URL('admin','default','edit/%s/models/db.py' % app)), (T('Stylesheet'), False,
(T('Menu Model'),False, URL(
URL('admin','default','edit/%s/models/menu.py' % app)), 'admin', 'default', 'edit/%s/static/css/web2py.css' % app)),
(T('Database'),False, URL(app,'appadmin','index')), (T('DB Model'), False,
(T('Errors'),False, URL('admin','default','errors/' + app)), URL(
(T('About'),False, URL('admin','default','about/' + app)), 'admin', 'default', 'edit/%s/models/db.py' % app)),
(T('Menu Model'), False,
URL(
'admin', 'default', 'edit/%s/models/menu.py' % app)),
(T('Database'), False, URL(app, 'appadmin', 'index')),
(T('Errors'), False, URL(
'admin', 'default', 'errors/' + app)),
(T('About'), False, URL(
'admin', 'default', 'about/' + app)),
]),
('web2py.com', False, 'http://www.web2py.com', [
(T('Download'), False,
'http://www.web2py.com/examples/default/download'),
(T('Support'), False,
'http://www.web2py.com/examples/default/support'),
(T('Demo'), False, 'http://web2py.com/demo_admin'),
(T('Quick Examples'), False,
'http://web2py.com/examples/default/examples'),
(T('FAQ'), False, 'http://web2py.com/AlterEgo'),
(T('Videos'), False,
'http://www.web2py.com/examples/default/videos/'),
(T('Free Applications'),
False, 'http://web2py.com/appliances'),
(T('Plugins'), False, 'http://web2py.com/plugins'),
(T('Layouts'), False, 'http://web2py.com/layouts'),
(T('Recipes'), False, 'http://web2pyslices.com/'),
(T('Semantic'), False, 'http://web2py.com/semantic'),
]),
(T('Documentation'), False, 'http://www.web2py.com/book', [
(T('Preface'), False,
'http://www.web2py.com/book/default/chapter/00'),
(T('Introduction'), False,
'http://www.web2py.com/book/default/chapter/01'),
(T('Python'), False,
'http://www.web2py.com/book/default/chapter/02'),
(T('Overview'), False,
'http://www.web2py.com/book/default/chapter/03'),
(T('The Core'), False,
'http://www.web2py.com/book/default/chapter/04'),
(T('The Views'), False,
'http://www.web2py.com/book/default/chapter/05'),
(T('Database'), False,
'http://www.web2py.com/book/default/chapter/06'),
(T('Forms and Validators'), False,
'http://www.web2py.com/book/default/chapter/07'),
(T('Email and SMS'), False,
'http://www.web2py.com/book/default/chapter/08'),
(T('Access Control'), False,
'http://www.web2py.com/book/default/chapter/09'),
(T('Services'), False,
'http://www.web2py.com/book/default/chapter/10'),
(T('Ajax Recipes'), False,
'http://www.web2py.com/book/default/chapter/11'),
(T('Components and Plugins'), False,
'http://www.web2py.com/book/default/chapter/12'),
(T('Deployment Recipes'), False,
'http://www.web2py.com/book/default/chapter/13'),
(T('Other Recipes'), False,
'http://www.web2py.com/book/default/chapter/14'),
(T('Buy this book'), False,
'http://stores.lulu.com/web2py'),
]),
(T('Community'), False, None, [
(T('Groups'), False,
'http://www.web2py.com/examples/default/usergroups'),
(T('Twitter'), False, 'http://twitter.com/web2py'),
(T('Live Chat'), False,
'http://webchat.freenode.net/?channels=web2py'),
]), ]),
('web2py.com',False,'http://www.web2py.com', [ (T('Plugins'), False, None, [
(T('Download'),False,'http://www.web2py.com/examples/default/download'), ('plugin_wiki', False,
(T('Support'),False,'http://www.web2py.com/examples/default/support'), 'http://web2py.com/examples/default/download'),
(T('Demo'),False,'http://web2py.com/demo_admin'), (T('Other Plugins'), False,
(T('Quick Examples'),False,'http://web2py.com/examples/default/examples'), 'http://web2py.com/plugins'),
(T('FAQ'),False,'http://web2py.com/AlterEgo'), (T('Layout Plugins'),
(T('Videos'),False,'http://www.web2py.com/examples/default/videos/'), False, 'http://web2py.com/layouts'),
(T('Free Applications'),False,'http://web2py.com/appliances'),
(T('Plugins'),False,'http://web2py.com/plugins'),
(T('Layouts'),False,'http://web2py.com/layouts'),
(T('Recipes'),False,'http://web2pyslices.com/'),
(T('Semantic'),False,'http://web2py.com/semantic'),
]),
(T('Documentation'),False,'http://www.web2py.com/book', [
(T('Preface'),False,'http://www.web2py.com/book/default/chapter/00'),
(T('Introduction'),False,'http://www.web2py.com/book/default/chapter/01'),
(T('Python'),False,'http://www.web2py.com/book/default/chapter/02'),
(T('Overview'),False,'http://www.web2py.com/book/default/chapter/03'),
(T('The Core'),False,'http://www.web2py.com/book/default/chapter/04'),
(T('The Views'),False,'http://www.web2py.com/book/default/chapter/05'),
(T('Database'),False,'http://www.web2py.com/book/default/chapter/06'),
(T('Forms and Validators'),False,'http://www.web2py.com/book/default/chapter/07'),
(T('Email and SMS'),False,'http://www.web2py.com/book/default/chapter/08'),
(T('Access Control'),False,'http://www.web2py.com/book/default/chapter/09'),
(T('Services'),False,'http://www.web2py.com/book/default/chapter/10'),
(T('Ajax Recipes'),False,'http://www.web2py.com/book/default/chapter/11'),
(T('Components and Plugins'),False,'http://www.web2py.com/book/default/chapter/12'),
(T('Deployment Recipes'),False,'http://www.web2py.com/book/default/chapter/13'),
(T('Other Recipes'),False,'http://www.web2py.com/book/default/chapter/14'),
(T('Buy this book'),False,'http://stores.lulu.com/web2py'),
]),
(T('Community'),False, None, [
(T('Groups'),False,'http://www.web2py.com/examples/default/usergroups'),
(T('Twitter'),False,'http://twitter.com/web2py'),
(T('Live Chat'),False,'http://webchat.freenode.net/?channels=web2py'),
]),
(T('Plugins'),False,None, [
('plugin_wiki',False,'http://web2py.com/examples/default/download'),
(T('Other Plugins'),False,'http://web2py.com/plugins'),
(T('Layout Plugins'),False,'http://web2py.com/layouts'),
]) ])
] ]
)] )]
_() if DEVELOPMENT_MENU: _()
-1
View File
@@ -37,4 +37,3 @@ routers = {
#NOTE! To change language in your application using these rules add this line #NOTE! To change language in your application using these rules add this line
#in one of your models files: #in one of your models files:
# if request.uri_language: T.force(request.uri_language) # if request.uri_language: T.force(request.uri_language)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -115,6 +115,10 @@ div.flash {
z-index:2000; z-index:2000;
} }
div.flash #closeflash{color:inherit; float:right; margin-left:15px;}
.ie-lte7 div.flash #closeflash
{color:expression(this.parentNode.currentStyle['color']);float:none;position:absolute;right:4px;}
div.flash:hover { opacity:0.25; } div.flash:hover { opacity:0.25; }
div.error_wrapper {display:block} div.error_wrapper {display:block}
@@ -67,10 +67,13 @@ div.controls .error{
border:none; border:none;
padding:0; padding:0;
margin:0; margin:0;
//display:inline; /* uncommenting this, the animation effect is lost */ /*display:inline;*/ /* uncommenting this, the animation effect is lost */
} }
div.controls .inline-help{color:#3A87AD;} div.controls .help-inline{color:#3A87AD;}
div.controls .error_wrapper+.inline-help{margin-left:-99999px;} div.controls .error_wrapper +.help-inline {margin-left:-99999px;}
div.controls select +.error_wrapper {margin-left:5px;}
.ie-lte7 div.error{color:#fff;}
/* beautify brand */ /* beautify brand */
.navbar-inverse .brand{color:#c6cecc;} .navbar-inverse .brand{color:#c6cecc;}
.navbar-inverse .brand b{display:inline-block;margin-top:-1px;} .navbar-inverse .brand b{display:inline-block;margin-top:-1px;}
@@ -175,9 +178,6 @@ background-repeat:repeat;
OTHER RULES OTHER RULES
==============================================================*/ ==============================================================*/
.navbar-inner{
position:relative; /*unnecessary ??*/
}
/* Massimo Di Pierro fixed alignment in forms with list:string */ /* Massimo Di Pierro fixed alignment in forms with list:string */
form table tr{margin-bottom:9px;} form table tr{margin-bottom:9px;}
td.w2p_fw ul{margin-left:0px;} td.w2p_fw ul{margin-left:0px;}
@@ -212,7 +212,7 @@ td.w2p_fw ul{margin-left:0px;}
@media only screen and (max-width:979px){ @media only screen and (max-width:979px){
body{padding-top:0px;} body{padding-top:0px;}
#navbar{bottom:-10px;left:4px;} #navbar{top:5px;}
div.flash{right:5px;} div.flash{right:5px;}
.dropdown-menu ul{visibility:visible;} .dropdown-menu ul{visibility:visible;}
} }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -16,7 +16,7 @@ jQuery(function(){
return match && decodeURIComponent(match[1].replace(/\+/g, ' '))||default_value; return match && decodeURIComponent(match[1].replace(/\+/g, ' '))||default_value;
} }
var path = params('static','social'); var path = params('static','social');
var url = window.location.href; var url = encodeURIComponent(window.location.href);
var host = window.location.hostname; var host = window.location.hostname;
var title = escape(jQuery('title').text()); var title = escape(jQuery('title').text());
var twit = 'http://twitter.com/home?status='+title+'%20'+url; var twit = 'http://twitter.com/home?status='+title+'%20'+url;
@@ -27,7 +27,7 @@ jQuery(function(){
jQuery('body').append(tbar); jQuery('body').append(tbar);
var st = jQuery('#socialdrawer'); var st = jQuery('#socialdrawer');
st.css({'opacity':'.7','z-index':'3000','background':'#FFF','border':'solid 1px #666','border-width':' 1px 0 0 1px','height':'20px','width':'40px','position':'fixed','bottom':'0','right':'0','padding':'2px 5px','overflow':'hidden','-webkit-border-top-left-radius':' 12px','-moz-border-radius-topleft':' 12px','border-top-left-radius':' 12px','-moz-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','-webkit-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)'}); st.css({'opacity':'.7','z-index':'3000','background':'#FFF','border':'solid 1px #666','border-width':' 1px 0 0 1px','height':'20px','width':'40px','position':'fixed','bottom':'0','right':'0','padding':'2px 5px','overflow':'hidden','-webkit-border-top-left-radius':' 12px','-moz-border-radius-topleft':' 12px','border-top-left-radius':' 12px','-moz-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','-webkit-box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)','box-shadow':' -3px -3px 3px rgba(0,0,0,0.5)'});
jQuery('#socialdrawer a').css({'float':'left','width':'32px','margin':'3px 2px 2px 2px','padding':'0'}); jQuery('#socialdrawer a').css({'float':'left','width':'32px','margin':'3px 2px 2px 2px','padding':'0','cursor':'pointer'});
jQuery('#socialdrawer span').css({'float':'left','margin':'2px 3px','text-shadow':' 1px 1px 1px #FFF','color':'#444','font-size':'12px','line-height':'1em'}); jQuery('#socialdrawer span').css({'float':'left','margin':'2px 3px','text-shadow':' 1px 1px 1px #FFF','color':'#444','font-size':'12px','line-height':'1em'});
jQuery('#socialdrawer img').hide(); jQuery('#socialdrawer img').hide();
// hover // hover
+10 -4
View File
@@ -55,7 +55,7 @@ function web2py_event_handlers() {
jQuery(function() { jQuery(function() {
var flash = jQuery('.flash'); var flash = jQuery('.flash');
flash.hide(); flash.hide();
if(flash.html()) flash.append('<span style="float:right;">&times;</span>').slideDown(); if(flash.html()) flash.append('<span id="closeflash">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -102,15 +102,20 @@ function web2py_ajax_page(method, action, data, target) {
web2py_ajax_init('#'+target); web2py_ajax_init('#'+target);
if(command) if(command)
eval(decodeURIComponent(command)); eval(decodeURIComponent(command));
if(flash) if(flash) {
jQuery('.flash').html(decodeURIComponent(flash)).slideDown(); jQuery('.flash')
.html(decodeURIComponent(flash))
.append('<span id="closeflash">&times;</span>')
.slideDown();
}
} }
}); });
} }
function web2py_component(action, target, timeout, times){ function web2py_component(action, target, timeout, times){
jQuery(function(){ jQuery(function(){
var element = jQuery("#" + target).get(0); var jelement = jQuery("#" + target);
var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function (){ element.reload = function (){
// Continue if times is Infinity or // Continue if times is Infinity or
@@ -119,6 +124,7 @@ function web2py_component(action, target, timeout, times){
web2py_ajax_page('get', action, null, target);} }; // reload web2py_ajax_page('get', action, null, target);} }; // reload
// Method to check timing limit // Method to check timing limit
element.reload_check = function (){ element.reload_check = function (){
if (jelement.hasClass('w2p_component_stop')) {return false;}
if (this.reload_counter == Infinity){return true;} if (this.reload_counter == Infinity){return true;}
else { else {
if (!isNaN(this.reload_counter)){ if (!isNaN(this.reload_counter)){
+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" encoding="ISO-8859-1"?>{{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}} {{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}}
+10 -3
View File
@@ -29,7 +29,7 @@
device-width: Occupy full width of the screen in its current orientation device-width: Occupy full width of the screen in its current orientation
initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height initial-scale = 1.0 retains dimensions instead of zooming out if page height > device height
user-scalable = yes allows the user to zoom in --> user-scalable = yes allows the user to zoom in -->
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="{{=URL('static','images/favicon.ico')}}" type="image/x-icon"> <link rel="shortcut icon" href="{{=URL('static','images/favicon.ico')}}" type="image/x-icon">
<link rel="apple-touch-icon" href="{{=URL('static','images/favicon.png')}}"> <link rel="apple-touch-icon" href="{{=URL('static','images/favicon.png')}}">
@@ -76,7 +76,7 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="brand" href="http://www.web2py.com/"><b>web<span>2</span>py</b>&trade;&nbsp;</a> {{=response.logo or ''}}
<ul id="navbar" class="nav pull-right">{{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}</ul> <ul id="navbar" class="nav pull-right">{{='auth' in globals() and auth.navbar(mode="dropdown") or ''}}</ul>
<div class="nav-collapse"> <div class="nav-collapse">
{{is_mobile=request.user_agent().is_mobile}} {{is_mobile=request.user_agent().is_mobile}}
@@ -152,7 +152,14 @@
<script src="{{=URL('static','js/dd_belatedpng.js')}}"></script> <script src="{{=URL('static','js/dd_belatedpng.js')}}"></script>
<script> DD_belatedPNG.fix('img, .png_bg'); //fix any <img> or .png_bg background-images </script> <script> DD_belatedPNG.fix('img, .png_bg'); //fix any <img> or .png_bg background-images </script>
<![endif]--> <![endif]-->
{{if response.google_analytics_id:}}<script type="text/javascript"> var _gaq = _gaq || []; _gaq.push(['_setAccount', '{{=response.google_analytics_id}}']); _gaq.push(['_trackPageview']); (function() { var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); })(); </script> {{pass}}
{{if response.google_analytics_id:}}
<script src="{{=URL('static','js/analytics.js')}}"></script>
<script type="text/javascript">
analytics.initialize({
'Google Analytics':{trackingId:'{{=response.google_analytics_id}}'}
});</script>
{{pass}}
<script src="{{=URL('static','js/share.js',vars=dict(static=URL('static','images')))}}"></script> <script src="{{=URL('static','js/share.js',vars=dict(static=URL('static','images')))}}"></script>
</body> </body>
</html> </html>
+1 -6
View File
@@ -56,13 +56,8 @@ import wsgiref.handlers
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
os.chdir(path) os.chdir(path)
sys.path = [path]+[p for p in sys.path if not p==path] sys.path = [path] + [p for p in sys.path if not p == path]
import gluon.main import gluon.main
wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase) wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase)
+1 -6
View File
@@ -34,7 +34,7 @@ import os
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
os.chdir(path) os.chdir(path)
sys.path = [path]+[p for p in sys.path if not p==path] sys.path = [path] + [p for p in sys.path if not p == path]
import gluon.main import gluon.main
import gluon.contrib.gateways.fcgi as fcgi import gluon.contrib.gateways.fcgi as fcgi
@@ -51,8 +51,3 @@ if SOFTCRON:
global_settings.web2py_crontype = 'soft' global_settings.web2py_crontype = 'soft'
fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run() fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run()
+9 -7
View File
@@ -33,7 +33,7 @@ import wsgiref.handlers
import datetime import datetime
path = os.path.dirname(os.path.abspath(__file__)) path = os.path.dirname(os.path.abspath(__file__))
sys.path = [path]+[p for p in sys.path if not p==path] sys.path = [path] + [p for p in sys.path if not p == path]
sys.modules['cPickle'] = sys.modules['pickle'] sys.modules['cPickle'] = sys.modules['pickle']
@@ -78,12 +78,18 @@ def wsgiapp(env, res):
"""Return the wsgiapp""" """Return the wsgiapp"""
env['PATH_INFO'] = env['PATH_INFO'].decode('latin1').encode('utf8') env['PATH_INFO'] = env['PATH_INFO'].decode('latin1').encode('utf8')
#when using the blobstore image uploader GAE dev SDK passes these as unicode
# they should be regular strings as they are parts of URLs
env['wsgi.url_scheme'] = str(env['wsgi.url_scheme'])
env['QUERY_STRING'] = str(env['QUERY_STRING'])
env['SERVER_NAME'] = str(env['SERVER_NAME'])
#this deals with a problem where GAE development server seems to forget #this deals with a problem where GAE development server seems to forget
# the path between requests # the path between requests
if global_settings.web2py_runtime == 'gae:development': if global_settings.web2py_runtime == 'gae:development':
gluon.admin.create_missing_folders() gluon.admin.create_missing_folders()
web2py_path = global_settings.applications_parent # backward compatibility web2py_path = global_settings.applications_parent # backward compatibility
return gluon.main.wsgibase(env, res) return gluon.main.wsgibase(env, res)
@@ -91,14 +97,10 @@ def wsgiapp(env, res):
if LOG_STATS or DEBUG: if LOG_STATS or DEBUG:
wsgiapp = log_stats(wsgiapp) wsgiapp = log_stats(wsgiapp)
def main(): def main():
"""Run the wsgi app""" """Run the wsgi app"""
run_wsgi_app(wsgiapp) run_wsgi_app(wsgiapp)
if __name__ == '__main__': if __name__ == '__main__':
main() main()
+1 -12
View File
@@ -10,7 +10,7 @@ Web2Py framework modules
======================== ========================
""" """
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML','redirect','current','embed64'] __all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
from globals import current from globals import current
from html import * from html import *
@@ -42,14 +42,3 @@ if 0:
mail = Mail() mail = Mail()
service = Service() service = Service()
plugins = PluginManager() plugins = PluginManager()
+32 -25
View File
@@ -23,6 +23,7 @@ from http import HTTP
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
import site import site
def apath(path='', r=None): def apath(path='', r=None):
""" """
Builds a path inside an application folder Builds a path inside an application folder
@@ -95,6 +96,7 @@ def app_pack_compiled(app, request, raise_ex=False):
raise raise
return None return None
def app_cleanup(app, request): def app_cleanup(app, request):
""" """
Removes session, cache and error files Removes session, cache and error files
@@ -113,7 +115,7 @@ def app_cleanup(app, request):
if os.path.exists(path): if os.path.exists(path):
for f in os.listdir(path): for f in os.listdir(path):
try: try:
if f[:1]!='.': os.unlink(os.path.join(path,f)) if f[:1] != '.': os.unlink(os.path.join(path, f))
except IOError: except IOError:
r = False r = False
@@ -122,7 +124,7 @@ def app_cleanup(app, request):
if os.path.exists(path): if os.path.exists(path):
for f in os.listdir(path): for f in os.listdir(path):
try: try:
if f[:1]!='.': recursive_unlink(os.path.join(path,f)) if f[:1] != '.': recursive_unlink(os.path.join(path, f))
except IOError: except IOError:
r = False r = False
@@ -131,7 +133,7 @@ def app_cleanup(app, request):
if os.path.exists(path): if os.path.exists(path):
for f in os.listdir(path): for f in os.listdir(path):
try: try:
if f[:1]!='.': os.unlink(os.path.join(path,f)) if f[:1] != '.': os.unlink(os.path.join(path, f))
except IOError: except IOError:
r = False r = False
return r return r
@@ -158,7 +160,8 @@ def app_compile(app, request):
remove_compiled_application(folder) remove_compiled_application(folder)
return tb return tb
def app_create(app, request,force=False,key=None,info=False):
def app_create(app, request, force=False, key=None, info=False):
""" """
Create a copy of welcome.w2p (scaffolding) app Create a copy of welcome.w2p (scaffolding) app
@@ -186,17 +189,18 @@ def app_create(app, request,force=False,key=None,info=False):
return False return False
try: try:
w2p_unpack('welcome.w2p', path) w2p_unpack('welcome.w2p', path)
for subfolder in ['models','views','controllers', 'databases', for subfolder in [
'modules','cron','errors','sessions', 'models', 'views', 'controllers', 'databases',
'languages','static','private','uploads']: 'modules', 'cron', 'errors', 'sessions', 'cache',
subpath = os.path.join(path,subfolder) 'languages', 'static', 'private', 'uploads']:
subpath = os.path.join(path, subfolder)
if not os.path.exists(subpath): if not os.path.exists(subpath):
os.mkdir(subpath) os.mkdir(subpath)
db = os.path.join(path, 'models', 'db.py') db = os.path.join(path, 'models', 'db.py')
if os.path.exists(db): if os.path.exists(db):
data = read_file(db) data = read_file(db)
data = data.replace('<your secret key>', data = data.replace('<your secret key>',
'sha512:'+(key or web2py_uuid())) 'sha512:' + (key or web2py_uuid()))
write_file(db, data) write_file(db, data)
if info: if info:
return True, None return True, None
@@ -283,6 +287,7 @@ def app_uninstall(app, request):
except Exception: except Exception:
return False return False
def plugin_pack(app, plugin_name, request): def plugin_pack(app, plugin_name, request):
""" """
Builds a w2p package for the application Builds a w2p package for the application
@@ -302,12 +307,14 @@ def plugin_pack(app, plugin_name, request):
filename of the w2p file or None on error filename of the w2p file or None on error
""" """
try: try:
filename = apath('../deposit/web2py.plugin.%s.w2p' % plugin_name, request) filename = apath(
'../deposit/web2py.plugin.%s.w2p' % plugin_name, request)
w2p_pack_plugin(filename, apath(app, request), plugin_name) w2p_pack_plugin(filename, apath(app, request), plugin_name)
return filename return filename
except Exception: except Exception:
return False return False
def plugin_install(app, fobj, request, filename): def plugin_install(app, fobj, request, filename):
""" """
Installs an application: Installs an application:
@@ -345,6 +352,7 @@ def plugin_install(app, fobj, request, filename):
os.unlink(upname) os.unlink(upname)
return False return False
def check_new_version(myversion, version_URL): def check_new_version(myversion, version_URL):
""" """
Compares current web2py's version with the latest stable web2py version. Compares current web2py's version with the latest stable web2py version.
@@ -375,6 +383,7 @@ def check_new_version(myversion, version_URL):
else: else:
return False, version return False, version
def unzip(filename, dir, subfolder=''): def unzip(filename, dir, subfolder=''):
""" """
Unzips filename into dir (.zip only, no .gz etc) Unzips filename into dir (.zip only, no .gz etc)
@@ -382,7 +391,7 @@ def unzip(filename, dir, subfolder=''):
""" """
filename = abspath(filename) filename = abspath(filename)
if not zipfile.is_zipfile(filename): if not zipfile.is_zipfile(filename):
raise RuntimeError, 'Not a valid zipfile' raise RuntimeError('Not a valid zipfile')
zf = zipfile.ZipFile(filename) zf = zipfile.ZipFile(filename)
if not subfolder.endswith('/'): if not subfolder.endswith('/'):
subfolder = subfolder + '/' subfolder = subfolder + '/'
@@ -392,7 +401,7 @@ def unzip(filename, dir, subfolder=''):
continue continue
#print name[n:] #print name[n:]
if name.endswith('/'): if name.endswith('/'):
folder = os.path.join(dir,name[n:]) folder = os.path.join(dir, name[n:])
if not os.path.exists(folder): if not os.path.exists(folder):
os.mkdir(folder) os.mkdir(folder)
else: else:
@@ -421,7 +430,7 @@ def upgrade(request, url='http://web2py.com'):
if not gluon_parent.endswith('/'): if not gluon_parent.endswith('/'):
gluon_parent = gluon_parent + '/' gluon_parent = gluon_parent + '/'
(check, version) = check_new_version(web2py_version, (check, version) = check_new_version(web2py_version,
url+'/examples/default/version') url + '/examples/default/version')
if not check: if not check:
return (False, 'Already latest version') return (False, 'Already latest version')
if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')): if os.path.exists(os.path.join(gluon_parent, 'web2py.exe')):
@@ -442,42 +451,40 @@ def upgrade(request, url='http://web2py.com'):
file = None file = None
try: try:
write_file(filename, urllib.urlopen(full_url).read(), 'wb') write_file(filename, urllib.urlopen(full_url).read(), 'wb')
except Exception,e: except Exception, e:
return False, e return False, e
try: try:
unzip(filename, destination, subfolder) unzip(filename, destination, subfolder)
return True, None return True, None
except Exception,e: except Exception, e:
return False, e return False, e
def add_path_first(path): def add_path_first(path):
sys.path = [path]+[p for p in sys.path if (not p==path and not p==(path+'/'))] sys.path = [path] + [p for p in sys.path if (
not p == path and not p == (path + '/'))]
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
site.addsitedir(path) site.addsitedir(path)
def create_missing_folders(): def create_missing_folders():
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
for path in ('applications', 'deposit', 'site-packages', 'logs'): for path in ('applications', 'deposit', 'site-packages', 'logs'):
path = abspath(path, gluon=True) path = abspath(path, gluon=True)
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.mkdir(path)
paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '') paths = (global_settings.gluon_parent, abspath(
'site-packages', gluon=True), abspath('gluon', gluon=True), '')
[add_path_first(path) for path in paths] [add_path_first(path) for path in paths]
def create_missing_app_folders(request): def create_missing_app_folders(request):
if not global_settings.web2py_runtime_gae: if not global_settings.web2py_runtime_gae:
if request.folder not in global_settings.app_folders: if request.folder not in global_settings.app_folders:
for subfolder in ('models', 'views', 'controllers', 'databases', for subfolder in ('models', 'views', 'controllers', 'databases',
'modules', 'cron', 'errors', 'sessions', 'modules', 'cron', 'errors', 'sessions',
'languages', 'static', 'private', 'uploads'): 'languages', 'static', 'private', 'uploads'):
path = os.path.join(request.folder, subfolder) path = os.path.join(request.folder, subfolder)
if not os.path.exists(path): if not os.path.exists(path):
os.mkdir(path) os.mkdir(path)
global_settings.app_folders.add(request.folder) global_settings.app_folders.add(request.folder)
+47 -36
View File
@@ -40,6 +40,7 @@ __all__ = ['Cache', 'lazy_cache']
DEFAULT_TIME_EXPIRE = 300 DEFAULT_TIME_EXPIRE = 300
class CacheAbstract(object): class CacheAbstract(object):
""" """
Abstract class for cache implementations. Abstract class for cache implementations.
@@ -73,7 +74,7 @@ class CacheAbstract(object):
raise NotImplementedError raise NotImplementedError
def __call__(self, key, f, def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE): time_expire=DEFAULT_TIME_EXPIRE):
""" """
Tries retrieve the value corresponding to `key` from the cache of the Tries retrieve the value corresponding to `key` from the cache of the
object exists and if it did not expire, else it called the function `f` object exists and if it did not expire, else it called the function `f`
@@ -130,6 +131,7 @@ class CacheAbstract(object):
if r.match(str(key)): if r.match(str(key)):
del storage[key] del storage[key]
class CacheInRam(CacheAbstract): class CacheInRam(CacheAbstract):
""" """
Ram based caching Ram based caching
@@ -145,10 +147,13 @@ class CacheInRam(CacheAbstract):
def __init__(self, request=None): def __init__(self, request=None):
self.initialized = False self.initialized = False
self.request = request self.request = request
self.storage = {}
def initialize(self): def initialize(self):
if self.initialized: return if self.initialized:
else: self.initialized = True return
else:
self.initialized = True
self.locker.acquire() self.locker.acquire()
request = self.request request = self.request
if request: if request:
@@ -172,13 +177,14 @@ class CacheInRam(CacheAbstract):
self._clear(storage, regex) self._clear(storage, regex)
if not CacheAbstract.cache_stats_name in storage.keys(): if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {'hit_total': 0,'misses': 0} storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'misses': 0}
self.locker.release() self.locker.release()
def __call__(self, key, f, def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE, time_expire=DEFAULT_TIME_EXPIRE,
destroyer = None): destroyer=None):
""" """
Attention! cache.ram does not copy the cached object. It just stores a reference to it. Attention! cache.ram does not copy the cached object. It just stores a reference to it.
Turns out the deepcopying the object has some problems: Turns out the deepcopying the object has some problems:
@@ -269,14 +275,15 @@ class CacheOnDisk(CacheAbstract):
try: try:
storage = shelve.open(self.shelve_name) storage = shelve.open(self.shelve_name)
except: except:
logger.error('corrupted cache file %s, will try rebuild it' \ logger.error('corrupted cache file %s, will try rebuild it'
% (self.shelve_name)) % (self.shelve_name))
storage = None storage = None
if not storage and os.path.exists(self.shelve_name): if not storage and os.path.exists(self.shelve_name):
os.unlink(self.shelve_name) os.unlink(self.shelve_name)
storage = shelve.open(self.shelve_name) storage = shelve.open(self.shelve_name)
if not CacheAbstract.cache_stats_name in storage.keys(): if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {'hit_total':0, 'misses': 0} storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'misses': 0}
storage.sync() storage.sync()
except Exception, e: except Exception, e:
if storage: if storage:
@@ -286,7 +293,8 @@ class CacheOnDisk(CacheAbstract):
portalocker.unlock(locker) portalocker.unlock(locker)
locker.close() locker.close()
locked = False locked = False
raise RuntimeError, 'unable to create/re-create cache file %s' % self.shelve_name raise RuntimeError(
'unable to create/re-create cache file %s' % self.shelve_name)
self.locker = locker self.locker = locker
self.locked = locked self.locked = locked
self.storage = storage self.storage = storage
@@ -296,10 +304,13 @@ class CacheOnDisk(CacheAbstract):
self.initialized = False self.initialized = False
self.request = request self.request = request
self.folder = folder self.folder = folder
self.storage = {}
def initialize(self): def initialize(self):
if self.initialized: return if self.initialized:
else: self.initialized = True return
else:
self.initialized = True
folder = self.folder folder = self.folder
request = self.request request = self.request
@@ -312,8 +323,8 @@ class CacheOnDisk(CacheAbstract):
### we need this because of a possible bug in shelve that may ### we need this because of a possible bug in shelve that may
### or may not lock ### or may not lock
self.locker_name = os.path.join(folder,'cache.lock') self.locker_name = os.path.join(folder, 'cache.lock')
self.shelve_name = os.path.join(folder,'cache.shelve') self.shelve_name = os.path.join(folder, 'cache.shelve')
def clear(self, regex=None): def clear(self, regex=None):
self.initialize() self.initialize()
@@ -328,7 +339,7 @@ class CacheOnDisk(CacheAbstract):
self._close_shelve_and_unlock() self._close_shelve_and_unlock()
def __call__(self, key, f, def __call__(self, key, f,
time_expire = DEFAULT_TIME_EXPIRE): time_expire=DEFAULT_TIME_EXPIRE):
self.initialize() self.initialize()
dt = time_expire dt = time_expire
storage = self._open_shelve_and_lock() storage = self._open_shelve_and_lock()
@@ -346,7 +357,7 @@ class CacheOnDisk(CacheAbstract):
else: else:
value = f() value = f()
storage[key] = (now, value) storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name]['misses']+=1 storage[CacheAbstract.cache_stats_name]['misses'] += 1
storage.sync() storage.sync()
finally: finally:
self._close_shelve_and_unlock() self._close_shelve_and_unlock()
@@ -365,8 +376,9 @@ class CacheOnDisk(CacheAbstract):
self._close_shelve_and_unlock() self._close_shelve_and_unlock()
return value return value
class CacheAction(object): class CacheAction(object):
def __init__(self,func,key,time_expire,cache,cache_model): def __init__(self, func, key, time_expire, cache, cache_model):
self.__name__ = func.__name__ self.__name__ = func.__name__
self.__doc__ = func.__doc__ self.__doc__ = func.__doc__
self.func = func self.func = func
@@ -374,17 +386,18 @@ class CacheAction(object):
self.time_expire = time_expire self.time_expire = time_expire
self.cache = cache self.cache = cache
self.cache_model = cache_model self.cache_model = cache_model
def __call__(self,*a,**b):
def __call__(self, *a, **b):
if not self.key: if not self.key:
key2 = self.__name__+':'+repr(a)+':'+repr(b) key2 = self.__name__ + ':' + repr(a) + ':' + repr(b)
else: else:
key2 = self.key.replace('%(name)s',self.__name__)\ key2 = self.key.replace('%(name)s', self.__name__)\
.replace('%(args)s',str(a)).replace('%(vars)s',str(b)) .replace('%(args)s', str(a)).replace('%(vars)s', str(b))
cache_model = self.cache_model cache_model = self.cache_model
if not cache_model or isinstance(cache_model,str): if not cache_model or isinstance(cache_model, str):
cache_model = getattr(self.cache,cache_model or 'ram') cache_model = getattr(self.cache, cache_model or 'ram')
return cache_model(key2, return cache_model(key2,
lambda a=a,b=b:self.func(*a,**b), lambda a=a, b=b: self.func(*a, **b),
self.time_expire) self.time_expire)
@@ -424,9 +437,9 @@ class Cache(object):
logger.warning('no cache.disk (AttributeError)') logger.warning('no cache.disk (AttributeError)')
def __call__(self, def __call__(self,
key = None, key=None,
time_expire = DEFAULT_TIME_EXPIRE, time_expire=DEFAULT_TIME_EXPIRE,
cache_model = None): cache_model=None):
""" """
Decorator function that can be used to cache any function/method. Decorator function that can be used to cache any function/method.
@@ -459,8 +472,8 @@ class Cache(object):
`request.env.path_info` as key. `request.env.path_info` as key.
""" """
def tmp(func,cache=self,cache_model=cache_model): def tmp(func, cache=self, cache_model=cache_model):
return CacheAction(func,key,time_expire,self,cache_model) return CacheAction(func, key, time_expire, self, cache_model)
return tmp return tmp
@staticmethod @staticmethod
@@ -473,7 +486,7 @@ class Cache(object):
cache_model(prefix + key, f, time_expire) cache_model(prefix + key, f, time_expire)
def lazy_cache(key=None,time_expire=None,cache_model='ram'): def lazy_cache(key=None, time_expire=None, cache_model='ram'):
""" """
can be used to cache any function including in modules, can be used to cache any function including in modules,
as long as the cached function is only called within a web2py request as long as the cached function is only called within a web2py request
@@ -481,14 +494,12 @@ def lazy_cache(key=None,time_expire=None,cache_model='ram'):
the time_expire defaults to None (no cache expiration) the time_expire defaults to None (no cache expiration)
if cache_model is "ram" then the model is current.cache.ram, etc. if cache_model is "ram" then the model is current.cache.ram, etc.
""" """
def decorator(f,key=key,time_expire=time_expire,cache_model=cache_model): def decorator(f, key=key, time_expire=time_expire, cache_model=cache_model):
key = key or repr(f) key = key or repr(f)
def g(*c,**d):
def g(*c, **d):
from gluon import current from gluon import current
return current.cache(key,time_expire,cache_model)(f)(*c,**d) return current.cache(key, time_expire, cache_model)(f)(*c, **d)
g.__name__ = f.__name__ g.__name__ = f.__name__
return g return g
return decorator return decorator
-7
View File
@@ -51,10 +51,3 @@ def getcfs(key, filename, filter=None):
cfs[key] = (t, data) cfs[key] = (t, data)
cfs_lock.release() cfs_lock.release()
return data return data
+86 -75
View File
@@ -48,7 +48,7 @@ except:
logger.warning('unable to import py_compile') logger.warning('unable to import py_compile')
is_pypy = settings.global_settings.is_pypy is_pypy = settings.global_settings.is_pypy
is_gae = settings.global_settings.web2py_runtime_gae is_gae = settings.global_settings.web2py_runtime_gae
is_jython = settings.global_settings.is_jython is_jython = settings.global_settings.is_jython
pjoin = os.path.join pjoin = os.path.join
@@ -95,6 +95,7 @@ _TEST()
CACHED_REGEXES = {} CACHED_REGEXES = {}
CACHED_REGEXES_MAX_SIZE = 1000 CACHED_REGEXES_MAX_SIZE = 1000
def re_compile(regex): def re_compile(regex):
try: try:
return CACHED_REGEXES[regex] return CACHED_REGEXES[regex]
@@ -104,6 +105,7 @@ def re_compile(regex):
compiled_regex = CACHED_REGEXES[regex] = re.compile(regex) compiled_regex = CACHED_REGEXES[regex] = re.compile(regex)
return compiled_regex return compiled_regex
class mybuiltin(object): class mybuiltin(object):
""" """
NOTE could simple use a dict and populate it, NOTE could simple use a dict and populate it,
@@ -114,14 +116,16 @@ class mybuiltin(object):
try: try:
return getattr(__builtin__, key) return getattr(__builtin__, key)
except AttributeError: except AttributeError:
raise KeyError, key raise KeyError(key)
def __setitem__(self, key, value): def __setitem__(self, key, value):
setattr(self, key, value) setattr(self, key, value)
def LOAD(c=None, f='index', args=None, vars=None, def LOAD(c=None, f='index', args=None, vars=None,
extension=None, target=None,ajax=False,ajax_trap=False, extension=None, target=None, ajax=False, ajax_trap=False,
url=None,user_signature=False, timeout=None, times=1, url=None, user_signature=False, timeout=None, times=1,
content='loading...',**attr): content='loading...', **attr):
""" LOAD a component into the action's document """ LOAD a component into the action's document
Timing options: Timing options:
@@ -134,13 +138,14 @@ def LOAD(c=None, f='index', args=None, vars=None,
is added on page loading without delay. is added on page loading without delay.
""" """
from html import TAG, DIV, URL, SCRIPT, XML from html import TAG, DIV, URL, SCRIPT, XML
if args is None: args = [] if args is None:
args = []
vars = Storage(vars or {}) vars = Storage(vars or {})
target = target or 'c'+str(random.random())[2:] target = target or 'c' + str(random.random())[2:]
attr['_id']=target attr['_id'] = target
request = current.request request = current.request
if '.' in f: if '.' in f:
f, extension = f.rsplit('.',1) f, extension = f.rsplit('.', 1)
if url or ajax: if url or ajax:
url = url or URL(request.application, c, f, r=request, url = url or URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension, args=args, vars=vars, extension=extension,
@@ -160,19 +165,20 @@ def LOAD(c=None, f='index', args=None, vars=None,
if not isinstance(timeout, (int, long)): if not isinstance(timeout, (int, long)):
raise ValueError("Timeout argument must be an integer or None") raise ValueError("Timeout argument must be an integer or None")
elif timeout <= 0: elif timeout <= 0:
raise ValueError("Timeout argument must be greater than zero or None") raise ValueError(
"Timeout argument must be greater than zero or None")
statement = "web2py_component('%s','%s', %s, %s);" \ statement = "web2py_component('%s','%s', %s, %s);" \
% (url, target, timeout, times) % (url, target, timeout, times)
else: else:
statement = "web2py_component('%s','%s');" % (url, target) statement = "web2py_component('%s','%s');" % (url, target)
script = SCRIPT(statement, _type="text/javascript") script = SCRIPT(statement, _type="text/javascript")
if not content is None: if not content is None:
return TAG[''](script, DIV(content,**attr)) return TAG[''](script, DIV(content, **attr))
else: else:
return TAG[''](script) return TAG[''](script)
else: else:
if not isinstance(args,(list,tuple)): if not isinstance(args, (list, tuple)):
args = [args] args = [args]
c = c or request.controller c = c or request.controller
other_request = Storage(request) other_request = Storage(request)
@@ -186,17 +192,17 @@ def LOAD(c=None, f='index', args=None, vars=None,
other_request.post_vars = Storage() other_request.post_vars = Storage()
other_response = Response() other_response = Response()
other_request.env.path_info = '/' + \ other_request.env.path_info = '/' + \
'/'.join([request.application,c,f] + \ '/'.join([request.application, c, f] +
map(str, other_request.args)) map(str, other_request.args))
other_request.env.query_string = \ other_request.env.query_string = \
vars and URL(vars=vars).split('?')[1] or '' vars and URL(vars=vars).split('?')[1] or ''
other_request.env.http_web2py_component_location = \ other_request.env.http_web2py_component_location = \
request.env.path_info request.env.path_info
other_request.cid = target other_request.cid = target
other_request.env.http_web2py_component_element = target other_request.env.http_web2py_component_element = target
other_response.view = '%s/%s.%s' % (c,f, other_request.extension) other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
other_environment = copy.copy(current.globalenv) ### NASTY other_environment = copy.copy(current.globalenv) # NASTY
other_response._view_environment = other_environment other_response._view_environment = other_environment
other_response.generic_patterns = \ other_response.generic_patterns = \
@@ -218,40 +224,41 @@ def LOAD(c=None, f='index', args=None, vars=None,
js = None js = None
if ajax_trap: if ajax_trap:
link = URL(request.application, c, f, r=request, link = URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension, args=args, vars=vars, extension=extension,
user_signature=user_signature) user_signature=user_signature)
js = "web2py_trap_form('%s','%s');" % (link, target) js = "web2py_trap_form('%s','%s');" % (link, target)
script = js and SCRIPT(js,_type="text/javascript") or '' script = js and SCRIPT(js, _type="text/javascript") or ''
return TAG[''](DIV(XML(page),**attr),script) return TAG[''](DIV(XML(page), **attr), script)
class LoadFactory(object): class LoadFactory(object):
""" """
Attention: this helper is new and experimental Attention: this helper is new and experimental
""" """
def __init__(self,environment): def __init__(self, environment):
self.environment = environment self.environment = environment
def __call__(self, c=None, f='index', args=None, vars=None, def __call__(self, c=None, f='index', args=None, vars=None,
extension=None, target=None,ajax=False,ajax_trap=False, extension=None, target=None, ajax=False, ajax_trap=False,
url=None,user_signature=False, content='loading...',**attr): url=None, user_signature=False, content='loading...', **attr):
if args is None: args = [] if args is None:
args = []
vars = Storage(vars or {}) vars = Storage(vars or {})
import globals import globals
target = target or 'c'+str(random.random())[2:] target = target or 'c' + str(random.random())[2:]
attr['_id']=target attr['_id'] = target
request = self.environment['request'] request = self.environment['request']
if '.' in f: if '.' in f:
f, extension = f.rsplit('.',1) f, extension = f.rsplit('.', 1)
if url or ajax: if url or ajax:
url = url or html.URL(request.application, c, f, r=request, url = url or html.URL(request.application, c, f, r=request,
args=args, vars=vars, extension=extension, args=args, vars=vars, extension=extension,
user_signature=user_signature) user_signature=user_signature)
script = html.SCRIPT('web2py_component("%s","%s")' % (url, target), script = html.SCRIPT('web2py_component("%s","%s")' % (url, target),
_type="text/javascript") _type="text/javascript")
return html.TAG[''](script, html.DIV(content,**attr)) return html.TAG[''](script, html.DIV(content, **attr))
else: else:
if not isinstance(args,(list,tuple)): if not isinstance(args, (list, tuple)):
args = [args] args = [args]
c = c or request.controller c = c or request.controller
@@ -266,15 +273,15 @@ class LoadFactory(object):
other_request.post_vars = Storage() other_request.post_vars = Storage()
other_response = globals.Response() other_response = globals.Response()
other_request.env.path_info = '/' + \ other_request.env.path_info = '/' + \
'/'.join([request.application,c,f] + \ '/'.join([request.application, c, f] +
map(str, other_request.args)) map(str, other_request.args))
other_request.env.query_string = \ other_request.env.query_string = \
vars and html.URL(vars=vars).split('?')[1] or '' vars and html.URL(vars=vars).split('?')[1] or ''
other_request.env.http_web2py_component_location = \ other_request.env.http_web2py_component_location = \
request.env.path_info request.env.path_info
other_request.cid = target other_request.cid = target
other_request.env.http_web2py_component_element = target other_request.env.http_web2py_component_element = target
other_response.view = '%s/%s.%s' % (c,f, other_request.extension) other_response.view = '%s/%s.%s' % (c, f, other_request.extension)
other_environment = copy.copy(self.environment) other_environment = copy.copy(self.environment)
other_response._view_environment = other_environment other_response._view_environment = other_environment
other_response.generic_patterns = \ other_response.generic_patterns = \
@@ -299,8 +306,8 @@ class LoadFactory(object):
args=args, vars=vars, extension=extension, args=args, vars=vars, extension=extension,
user_signature=user_signature) user_signature=user_signature)
js = "web2py_trap_form('%s','%s');" % (link, target) js = "web2py_trap_form('%s','%s');" % (link, target)
script = js and html.SCRIPT(js,_type="text/javascript") or '' script = js and html.SCRIPT(js, _type="text/javascript") or ''
return html.TAG[''](html.DIV(html.XML(page),**attr),script) return html.TAG[''](html.DIV(html.XML(page), **attr), script)
def local_import_aux(name, reload_force=False, app='welcome'): def local_import_aux(name, reload_force=False, app='welcome'):
@@ -321,7 +328,7 @@ def local_import_aux(name, reload_force=False, app='welcome'):
This prevents conflict between applications and un-necessary execs. This prevents conflict between applications and un-necessary execs.
It can be used to import any module, including regular Python modules. It can be used to import any module, including regular Python modules.
""" """
items = name.replace('/','.') items = name.replace('/', '.')
name = "applications.%s.modules.%s" % (app, items) name = "applications.%s.modules.%s" % (app, items)
module = __import__(name) module = __import__(name)
for item in name.split(".")[1:]: for item in name.split(".")[1:]:
@@ -355,12 +362,14 @@ OLD IMPLEMENTATION:
file.close() file.close()
imp.release_lock() imp.release_lock()
if not module: if not module:
raise ImportError, "cannot find module %s in %s" % (filename, modulepath) raise ImportError, "cannot find module %s in %s" % (
filename, modulepath)
return module return module
""" """
_base_environment_ = dict((k,getattr(html,k)) for k in html.__all__) _base_environment_ = dict((k, getattr(html, k)) for k in html.__all__)
_base_environment_.update((k,getattr(validators,k)) for k in validators.__all__) _base_environment_.update(
(k, getattr(validators, k)) for k in validators.__all__)
_base_environment_['__builtins__'] = __builtins__ _base_environment_['__builtins__'] = __builtins__
_base_environment_['HTTP'] = HTTP _base_environment_['HTTP'] = HTTP
_base_environment_['redirect'] = redirect _base_environment_['redirect'] = redirect
@@ -372,6 +381,7 @@ _base_environment_['SQLFORM'] = SQLFORM
_base_environment_['SQLTABLE'] = SQLTABLE _base_environment_['SQLTABLE'] = SQLTABLE
_base_environment_['LOAD'] = LOAD _base_environment_['LOAD'] = LOAD
def build_environment(request, response, session, store_current=True): def build_environment(request, response, session, store_current=True):
""" """
Build the environment dictionary into which web2py files are executed. Build the environment dictionary into which web2py files are executed.
@@ -384,9 +394,10 @@ def build_environment(request, response, session, store_current=True):
# Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and # Enable standard conditional models (i.e., /*.py, /[controller]/*.py, and
# /[controller]/[function]/*.py) # /[controller]/[function]/*.py)
response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller, response.models_to_run = [r'^\w+\.py$', r'^%s/\w+\.py$' % request.controller,
r'^%s/%s/\w+\.py$' % (request.controller, request.function)] r'^%s/%s/\w+\.py$' % (request.controller, request.function)]
t = environment['T'] = translator(request) t = environment['T'] = translator(os.path.join(request.folder,'languages'),
request.env.http_accept_language)
c = environment['cache'] = Cache(request) c = environment['cache'] = Cache(request)
if store_current: if store_current:
@@ -398,23 +409,24 @@ def build_environment(request, response, session, store_current=True):
current.cache = c current.cache = c
global __builtins__ global __builtins__
if is_jython: # jython hack if is_jython: # jython hack
__builtins__ = mybuiltin() __builtins__ = mybuiltin()
elif is_pypy: # apply the same hack to pypy too elif is_pypy: # apply the same hack to pypy too
__builtins__ = mybuiltin() __builtins__ = mybuiltin()
else: else:
__builtins__['__import__'] = __builtin__.__import__ ### WHY? __builtins__['__import__'] = __builtin__.__import__ # WHY?
environment['request'] = request environment['request'] = request
environment['response'] = response environment['response'] = response
environment['session'] = session environment['session'] = session
environment['local_import'] = \ environment['local_import'] = \
lambda name, reload=False, app=request.application:\ lambda name, reload=False, app=request.application:\
local_import_aux(name,reload,app) local_import_aux(name, reload, app)
BaseAdapter.set_folder(pjoin(request.folder, 'databases')) BaseAdapter.set_folder(pjoin(request.folder, 'databases'))
response._view_environment = copy.copy(environment) response._view_environment = copy.copy(environment)
custom_import_install() custom_import_install()
return environment return environment
def save_pyc(filename): def save_pyc(filename):
""" """
Bytecode compiles the file `filename` Bytecode compiles the file `filename`
@@ -431,7 +443,7 @@ def read_pyc(filename):
""" """
data = read_file(filename, 'rb') data = read_file(filename, 'rb')
if not is_gae and data[:4] != imp.get_magic(): if not is_gae and data[:4] != imp.get_magic():
raise SystemError, 'compiled code is incompatible' raise SystemError('compiled code is incompatible')
return marshal.loads(data[8:]) return marshal.loads(data[8:])
@@ -441,8 +453,11 @@ def compile_views(folder):
""" """
path = pjoin(folder, 'views') path = pjoin(folder, 'views')
for file in listdir(path, '^[\w/\-]+(\.\w+)+$'): for file in listdir(path, '^[\w/\-]+(\.\w+)*$'):
data = parse_template(file, path) try:
data = parse_template(file, path)
except Exception, e:
raise Exception("%s in %s" % (e, file))
filename = ('views/%s.py' % file).replace('/', '_').replace('\\', '_') filename = ('views/%s.py' % file).replace('/', '_').replace('\\', '_')
filename = pjoin(folder, 'compiled', filename) filename = pjoin(folder, 'compiled', filename)
write_file(filename, data) write_file(filename, data)
@@ -458,7 +473,7 @@ def compile_models(folder):
path = pjoin(folder, 'models') path = pjoin(folder, 'models')
for file in listdir(path, '.+\.py$'): for file in listdir(path, '.+\.py$'):
data = read_file(pjoin(path, file)) data = read_file(pjoin(path, file))
filename = pjoin(folder, 'compiled','models',file) filename = pjoin(folder, 'compiled', 'models', file)
mktree(filename) mktree(filename)
write_file(filename, data) write_file(filename, data)
save_pyc(filename) save_pyc(filename)
@@ -473,14 +488,14 @@ def compile_controllers(folder):
path = pjoin(folder, 'controllers') path = pjoin(folder, 'controllers')
for file in listdir(path, '.+\.py$'): for file in listdir(path, '.+\.py$'):
### why is this here? save_pyc(pjoin(path, file)) ### why is this here? save_pyc(pjoin(path, file))
data = read_file(pjoin(path,file)) data = read_file(pjoin(path, file))
exposed = regex_expose.findall(data) exposed = regex_expose.findall(data)
for function in exposed: for function in exposed:
command = data + "\nresponse._vars=response._caller(%s)\n" % \ command = data + "\nresponse._vars=response._caller(%s)\n" % \
function function
filename = pjoin(folder, 'compiled', ('controllers/' filename = pjoin(folder, 'compiled', ('controllers/'
+ file[:-3]).replace('/', '_') + file[:-3]).replace('/', '_')
+ '_' + function + '.py') + '_' + function + '.py')
write_file(filename, command) write_file(filename, command)
save_pyc(filename) save_pyc(filename)
os.unlink(filename) os.unlink(filename)
@@ -500,19 +515,19 @@ def run_models_in(environment):
for model in listdir(cpath, '^models_\w+\.pyc$', 0): for model in listdir(cpath, '^models_\w+\.pyc$', 0):
restricted(read_pyc(model), environment, layer=model) restricted(read_pyc(model), environment, layer=model)
path = pjoin(cpath, 'models') path = pjoin(cpath, 'models')
models = listdir(path, '^\w+\.pyc$',0,sort=False) models = listdir(path, '^\w+\.pyc$', 0)
compiled=True compiled = True
else: else:
path = pjoin(folder, 'models') path = pjoin(folder, 'models')
models = listdir(path, '^\w+\.py$',0,sort=False) models = listdir(path, '^\w+\.py$', 0)
compiled=False compiled = False
n = len(path) + 1 n = len(path) + 1
for model in models: for model in models:
regex = environment['response'].models_to_run regex = environment['response'].models_to_run
if isinstance(regex, list): if isinstance(regex, list):
regex = re_compile('|'.join(regex)) regex = re_compile('|'.join(regex))
file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py') file = model[n:].replace(os.path.sep, '/').replace('.pyc', '.py')
if not regex.search(file) and c!= 'appadmin': if not regex.search(file) and c != 'appadmin':
continue continue
elif compiled: elif compiled:
code = read_pyc(model) code = read_pyc(model)
@@ -538,7 +553,7 @@ def run_controller_in(controller, function, environment):
badf = 'invalid function (%s/%s)' % (controller, function) badf = 'invalid function (%s/%s)' % (controller, function)
if os.path.exists(path): if os.path.exists(path):
filename = pjoin(path, 'controllers_%s_%s.pyc' filename = pjoin(path, 'controllers_%s_%s.pyc'
% (controller, function)) % (controller, function))
if not os.path.exists(filename): if not os.path.exists(filename):
raise HTTP(404, raise HTTP(404,
rewrite.THREAD_LOCAL.routes.error_message % badf, rewrite.THREAD_LOCAL.routes.error_message % badf,
@@ -548,7 +563,8 @@ def run_controller_in(controller, function, environment):
# TESTING: adjust the path to include site packages # TESTING: adjust the path to include site packages
from settings import global_settings from settings import global_settings
from admin import abspath, add_path_first from admin import abspath, add_path_first
paths = (global_settings.gluon_parent, abspath('site-packages', gluon=True), abspath('gluon', gluon=True), '') paths = (global_settings.gluon_parent, abspath(
'site-packages', gluon=True), abspath('gluon', gluon=True), '')
[add_path_first(path) for path in paths] [add_path_first(path) for path in paths]
# TESTING END # TESTING END
@@ -578,18 +594,19 @@ def run_controller_in(controller, function, environment):
code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function) code = "%s\nresponse._vars=response._caller(%s)\n" % (code, function)
if is_gae: if is_gae:
layer = filename + ':' + function layer = filename + ':' + function
code = getcfs(layer, filename, lambda: compile2(code,layer)) code = getcfs(layer, filename, lambda: compile2(code, layer))
restricted(code, environment, filename) restricted(code, environment, filename)
response = environment['response'] response = environment['response']
vars=response._vars vars = response._vars
if response.postprocessing: if response.postprocessing:
vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars) vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
if isinstance(vars,unicode): if isinstance(vars, unicode):
vars = vars.encode('utf8') vars = vars.encode('utf8')
elif hasattr(vars,'xml') and callable(vars.xml): elif hasattr(vars, 'xml') and callable(vars.xml):
vars = vars.xml() vars = vars.xml()
return vars return vars
def run_view_in(environment): def run_view_in(environment):
""" """
Executes the view for the requested action. Executes the view for the requested action.
@@ -606,7 +623,7 @@ def run_view_in(environment):
if response.generic_patterns: if response.generic_patterns:
patterns = response.generic_patterns patterns = response.generic_patterns
regex = re_compile('|'.join(map(fnmatch.translate, patterns))) regex = re_compile('|'.join(map(fnmatch.translate, patterns)))
short_action = '%(controller)s/%(function)s.%(extension)s' % request short_action = '%(controller)s/%(function)s.%(extension)s' % request
allow_generic = regex.search(short_action) allow_generic = regex.search(short_action)
else: else:
allow_generic = False allow_generic = False
@@ -626,7 +643,7 @@ def run_view_in(environment):
files.append('views_generic.pyc') files.append('views_generic.pyc')
# end backward compatibility code # end backward compatibility code
for f in files: for f in files:
filename = pjoin(path,f) filename = pjoin(path, f)
if os.path.exists(filename): if os.path.exists(filename):
code = read_pyc(filename) code = read_pyc(filename)
restricted(code, environment, layer=filename) restricted(code, environment, layer=filename)
@@ -648,13 +665,14 @@ def run_view_in(environment):
ccode = getcfs(layer, filename, ccode = getcfs(layer, filename,
lambda: compile2(parse_template(view, lambda: compile2(parse_template(view,
pjoin(folder, 'views'), pjoin(folder, 'views'),
context=environment),layer)) context=environment), layer))
else: else:
ccode = parse_template(view, ccode = parse_template(view,
pjoin(folder, 'views'), pjoin(folder, 'views'),
context=environment) context=environment)
restricted(ccode, environment, layer) restricted(ccode, environment, layer)
def remove_compiled_application(folder): def remove_compiled_application(folder):
""" """
Deletes the folder `compiled` containing the compiled application. Deletes the folder `compiled` containing the compiled application.
@@ -662,7 +680,7 @@ def remove_compiled_application(folder):
try: try:
shutil.rmtree(pjoin(folder, 'compiled')) shutil.rmtree(pjoin(folder, 'compiled'))
path = pjoin(folder, 'controllers') path = pjoin(folder, 'controllers')
for file in listdir(path,'.*\.pyc$',drop=False): for file in listdir(path, '.*\.pyc$', drop=False):
os.unlink(file) os.unlink(file)
except OSError: except OSError:
pass pass
@@ -700,10 +718,3 @@ def test():
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()
+6 -12
View File
@@ -644,6 +644,7 @@ CONTENT_TYPE = {
'.wmls': 'text/vnd.wap.wmlscript', '.wmls': 'text/vnd.wap.wmlscript',
'.wmv': 'video/x-ms-wmv', '.wmv': 'video/x-ms-wmv',
'.wmx': 'audio/x-ms-asx', '.wmx': 'audio/x-ms-asx',
'.woff': 'application/font-woff',
'.wp': 'application/vnd.wordperfect', '.wp': 'application/vnd.wordperfect',
'.wp4': 'application/vnd.wordperfect', '.wp4': 'application/vnd.wordperfect',
'.wp5': 'application/vnd.wordperfect', '.wp5': 'application/vnd.wordperfect',
@@ -700,7 +701,7 @@ CONTENT_TYPE = {
'.zabw': 'application/x-abiword', '.zabw': 'application/x-abiword',
'.zip': 'application/zip', '.zip': 'application/zip',
'.zoo': 'application/x-zoo', '.zoo': 'application/x-zoo',
} }
def contenttype(filename, default='text/plain'): def contenttype(filename, default='text/plain'):
@@ -709,18 +710,11 @@ def contenttype(filename, default='text/plain'):
""" """
i = filename.rfind('.') i = filename.rfind('.')
if i>=0: if i >= 0:
default = CONTENT_TYPE.get(filename[i:].lower(),default) default = CONTENT_TYPE.get(filename[i:].lower(), default)
j = filename.rfind('.', 0, i) j = filename.rfind('.', 0, i)
if j>=0: if j >= 0:
default = CONTENT_TYPE.get(filename[j:].lower(),default) default = CONTENT_TYPE.get(filename[j:].lower(), default)
if default.startswith('text/'): if default.startswith('text/'):
default += '; charset=utf-8' default += '; charset=utf-8'
return default return default
+38 -34
View File
@@ -21,6 +21,7 @@ import urllib
_known_tuple_types = {} _known_tuple_types = {}
class NamedTupleBase(tuple): class NamedTupleBase(tuple):
"""Base class for named tuples with the __new__ operator set, named tuples """Base class for named tuples with the __new__ operator set, named tuples
yielded by the namedtuple() function will subclass this and add yielded by the namedtuple() function will subclass this and add
@@ -29,7 +30,7 @@ class NamedTupleBase(tuple):
"""Create a new instance of this fielded tuple""" """Create a new instance of this fielded tuple"""
# May need to unpack named field values here # May need to unpack named field values here
if kws: if kws:
values = list(args) + [None]*(len(cls._fields) - len(args)) values = list(args) + [None] * (len(cls._fields) - len(args))
fields = dict((val, idx) for idx, val in enumerate(cls._fields)) fields = dict((val, idx) for idx, val in enumerate(cls._fields))
for kw, val in kws.iteritems(): for kw, val in kws.iteritems():
assert kw in kws, "%r not in field list" % kw assert kw in kws, "%r not in field list" % kw
@@ -37,6 +38,7 @@ class NamedTupleBase(tuple):
args = tuple(values) args = tuple(values)
return tuple.__new__(cls, args) return tuple.__new__(cls, args)
def namedtuple(typename, fieldnames): def namedtuple(typename, fieldnames):
""" """
>>> import namedtuples >>> import namedtuples
@@ -75,24 +77,26 @@ def namedtuple(typename, fieldnames):
# Done # Done
return new_tuple_type return new_tuple_type
class AIM: class AIM:
class AIMError(Exception): class AIMError(Exception):
def __init__(self, value): def __init__(self, value):
self.parameter = value self.parameter = value
def __str__(self): def __str__(self):
return str(self.parameter) return str(self.parameter)
def __init__(self, login, transkey, testmode=False): def __init__(self, login, transkey, testmode=False):
if str(login).strip() == '' or login == None: if str(login).strip() == '' or login is None:
raise AIM.AIMError('No login name provided') raise AIM.AIMError('No login name provided')
if str(transkey).strip() == '' or transkey == None: if str(transkey).strip() == '' or transkey is None:
raise AIM.AIMError('No transaction key provided') raise AIM.AIMError('No transaction key provided')
if testmode != True and testmode != False: if testmode != True and testmode != False:
raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode)) raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode))
self.testmode = testmode self.testmode = testmode
self.proxy = None; self.proxy = None
self.delimiter = '|' self.delimiter = '|'
self.results = [] self.results = []
self.error = True self.error = True
@@ -117,8 +121,9 @@ class AIM:
else: else:
url = 'https://secure.authorize.net/gateway/transact.dll' url = 'https://secure.authorize.net/gateway/transact.dll'
if self.proxy == None: if self.proxy is None:
self.results += str(urllib.urlopen(url, encoded_args).read()).split(self.delimiter) self.results += str(urllib.urlopen(
url, encoded_args).read()).split(self.delimiter)
else: else:
opener = urllib.FancyURLopener(self.proxy) opener = urllib.FancyURLopener(self.proxy)
opened = opener.open(url, encoded_args) opened = opener.open(url, encoded_args)
@@ -147,36 +152,37 @@ class AIM:
raise AIM.AIMError(self.response.ResponseText) raise AIM.AIMError(self.response.ResponseText)
def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None): def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None):
if str(creditcard).strip() == '' or creditcard == None: if str(creditcard).strip() == '' or creditcard is None:
raise AIM.AIMError('No credit card number passed to setTransaction(): {0}'.format(creditcard)) raise AIM.AIMError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
if str(expiration).strip() == '' or expiration == None: if str(expiration).strip() == '' or expiration is None:
raise AIM.AIMError('No expiration number to setTransaction(): {0}'.format(expiration)) raise AIM.AIMError('No expiration number to setTransaction(): {0}'.format(expiration))
if str(total).strip() == '' or total == None: if str(total).strip() == '' or total is None:
raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total)) raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total))
self.setParameter('x_card_num', creditcard) self.setParameter('x_card_num', creditcard)
self.setParameter('x_exp_date', expiration) self.setParameter('x_exp_date', expiration)
self.setParameter('x_amount', total) self.setParameter('x_amount', total)
if cvv != None: if cvv is not None:
self.setParameter('x_card_code', cvv) self.setParameter('x_card_code', cvv)
if tax != None: if tax is not None:
self.setParameter('x_tax', tax) self.setParameter('x_tax', tax)
if invoice != None: if invoice is not None:
self.setParameter('x_invoice_num', invoice) self.setParameter('x_invoice_num', invoice)
def setTransactionType(self, transtype=None): def setTransactionType(self, transtype=None):
types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE', 'CREDIT', 'CAPTURE_ONLY', 'VOID'] types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE',
'CREDIT', 'CAPTURE_ONLY', 'VOID']
if transtype.upper() not in types: if transtype.upper() not in types:
raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype)) raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype))
self.setParameter('x_type', transtype.upper()) self.setParameter('x_type', transtype.upper())
def setProxy(self, proxy=None): def setProxy(self, proxy=None):
if str(proxy).strip() == '' or proxy == None: if str(proxy).strip() == '' or proxy is None:
raise AIM.AIMError('No proxy passed to setProxy()') raise AIM.AIMError('No proxy passed to setProxy()')
self.proxy = {'http': str(proxy).strip()} self.proxy = {'http': str(proxy).strip()}
def setParameter(self, key=None, value=None): def setParameter(self, key=None, value=None):
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '': if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
self.parameters[key] = str(value).strip() self.parameters[key] = str(value).strip()
else: else:
raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value)) raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
@@ -194,10 +200,11 @@ class AIM:
responses = ['', 'Approved', 'Declined', 'Error'] responses = ['', 'Approved', 'Declined', 'Error']
return responses[int(self.results[0])] return responses[int(self.results[0])]
def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True): def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
payment = AIM(login,transkey,testmode) login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
expiration = expiration.replace('/','') payment = AIM(login, transkey, testmode)
expiration = expiration.replace('/', '')
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice) payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
try: try:
payment.process() payment.process()
@@ -205,6 +212,7 @@ def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
except AIM.AIMError: except AIM.AIMError:
return False return False
def test(): def test():
import socket import socket
import sys import sys
@@ -215,12 +223,14 @@ def test():
total = '1.00' total = '1.00'
cvv = '123' cvv = '123'
tax = '0.00' tax = '0.00'
invoice = str(time())[4:10] # get a random invoice number invoice = str(time())[4:10] # get a random invoice number
try: try:
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True) payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice) payment.setTransaction(
payment.setParameter('x_duplicate_window', 180) # three minutes duplicate windows creditcard, expiration, total, cvv, tax, invoice)
payment.setParameter(
'x_duplicate_window', 180) # three minutes duplicate windows
payment.setParameter('x_cust_id', '1324') # customer ID payment.setParameter('x_cust_id', '1324') # customer ID
payment.setParameter('x_first_name', 'John') payment.setParameter('x_first_name', 'John')
payment.setParameter('x_last_name', 'Conde') payment.setParameter('x_last_name', 'Conde')
@@ -232,7 +242,8 @@ def test():
payment.setParameter('x_country', 'US') payment.setParameter('x_country', 'US')
payment.setParameter('x_phone', '800-555-1234') payment.setParameter('x_phone', '800-555-1234')
payment.setParameter('x_description', 'Test Transaction') payment.setParameter('x_description', 'Test Transaction')
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname())) payment.setParameter(
'x_customer_ip', socket.gethostbyname(socket.gethostname()))
payment.setParameter('x_email', 'john@example.com') payment.setParameter('x_email', 'john@example.com')
payment.setParameter('x_email_customer', False) payment.setParameter('x_email_customer', False)
payment.process() payment.process()
@@ -251,16 +262,9 @@ def test():
except AIM.AIMError, e: except AIM.AIMError, e:
print "Exception thrown:", e print "Exception thrown:", e
print 'An error occured' print 'An error occured'
print 'approved',payment.isApproved() print 'approved', payment.isApproved()
print 'declined',payment.isDeclined() print 'declined', payment.isDeclined()
print 'error',payment.isError() print 'error', payment.isError()
if __name__=='__main__': if __name__ == '__main__':
test() test()
+31 -33
View File
@@ -15,25 +15,27 @@ __all__ = ['DowCommerce']
from operator import itemgetter from operator import itemgetter
import urllib import urllib
class DowCommerce: class DowCommerce:
class DowCommerceError(Exception): class DowCommerceError(Exception):
def __init__(self, value): def __init__(self, value):
self.parameter = value self.parameter = value
def __str__(self): def __str__(self):
return str(self.parameter) return str(self.parameter)
def __init__(self, username=None, password=None, demomode=False): def __init__(self, username=None, password=None, demomode=False):
if not demomode: if not demomode:
if str(username).strip() == '' or username == None: if str(username).strip() == '' or username is None:
raise DowCommerce.DowCommerceError('No username provided') raise DowCommerce.DowCommerceError('No username provided')
if str(password).strip() == '' or password == None: if str(password).strip() == '' or password is None:
raise DowCommerce.DowCommerceError('No password provided') raise DowCommerce.DowCommerceError('No password provided')
else: else:
username = 'demo' username = 'demo'
password = 'password' password = 'password'
self.proxy = None; self.proxy = None
self.delimiter = '&' self.delimiter = '&'
self.results = {} self.results = {}
self.error = True self.error = True
@@ -45,11 +47,11 @@ class DowCommerce:
self.setParameter('username', username) self.setParameter('username', username)
self.setParameter('password', password) self.setParameter('password', password)
def process(self): def process(self):
encoded_args = urllib.urlencode(self.parameters) encoded_args = urllib.urlencode(self.parameters)
if self.proxy == None: if self.proxy is None:
results = str(urllib.urlopen(self.url, encoded_args).read()).split(self.delimiter) results = str(urllib.urlopen(
self.url, encoded_args).read()).split(self.delimiter)
else: else:
opener = urllib.FancyURLopener(self.proxy) opener = urllib.FancyURLopener(self.proxy)
opened = opener.open(self.url, encoded_args) opened = opener.open(self.url, encoded_args)
@@ -59,7 +61,7 @@ class DowCommerce:
opened.close() opened.close()
for result in results: for result in results:
(key,val) = result.split('=') (key, val) = result.split('=')
self.results[key] = val self.results[key] = val
if self.results['response'] == '1': if self.results['response'] == '1':
@@ -80,17 +82,18 @@ class DowCommerce:
self.declined = False self.declined = False
raise DowCommerce.DowCommerceError(self.results) raise DowCommerce.DowCommerceError(self.results)
def setTransaction(self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None, def setTransaction(
ipaddress=None, tax=None, shipping=None, self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None, ipaddress=None, tax=None, shipping=None,
country=None, phone=None, fax=None, emailaddress=None, website=None, firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None, country=None, phone=None, fax=None, emailaddress=None, website=None,
shipping_city=None, shipping_state=None, shipping_zipcode = None, shipping_country=None, shipping_emailaddress=None): shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
if str(creditcard).strip() == '' or creditcard == None: shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None):
if str(creditcard).strip() == '' or creditcard is None:
raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard)) raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
if str(expiration).strip() == '' or expiration == None: if str(expiration).strip() == '' or expiration is None:
raise DowCommerce.DowCommerceError('No expiration number passed to setTransaction(): {0}'.format(expiration)) raise DowCommerce.DowCommerceError('No expiration number passed to setTransaction(): {0}'.format(expiration))
if str(total).strip() == '' or total == None: if str(total).strip() == '' or total is None:
raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total)) raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total))
self.setParameter('ccnumber', creditcard) self.setParameter('ccnumber', creditcard)
@@ -165,12 +168,12 @@ class DowCommerce:
self.setParameter('type', transtype.lower()) self.setParameter('type', transtype.lower())
def setProxy(self, proxy=None): def setProxy(self, proxy=None):
if str(proxy).strip() == '' or proxy == None: if str(proxy).strip() == '' or proxy is None:
raise DowCommerce.DowCommerceError('No proxy passed to setProxy()') raise DowCommerce.DowCommerceError('No proxy passed to setProxy()')
self.proxy = {'http': str(proxy).strip()} self.proxy = {'http': str(proxy).strip()}
def setParameter(self, key=None, value=None): def setParameter(self, key=None, value=None):
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '': if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
self.parameters[key] = str(value).strip() self.parameters[key] = str(value).strip()
else: else:
raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value)) raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
@@ -194,6 +197,7 @@ class DowCommerce:
def getResponseText(self): def getResponseText(self):
return self.results['responsetext'] return self.results['responsetext']
def test(): def test():
import socket import socket
import sys import sys
@@ -212,13 +216,14 @@ def test():
total = '1.00' total = '1.00'
cvv = '999' cvv = '999'
tax = '0.00' tax = '0.00'
orderid = str(time())[4:10] # get a random invoice number orderid = str(time())[4:10] # get a random invoice number
try: try:
payment = DowCommerce(demomode=True) payment = DowCommerce(demomode=True)
payment.setTransaction(creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction', payment.setTransaction(
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA', creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1') firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
payment.process() payment.process()
if payment.isApproved(): if payment.isApproved():
@@ -231,16 +236,9 @@ def test():
except DowCommerce.DowCommerceError, e: except DowCommerce.DowCommerceError, e:
print "Exception thrown:", e print "Exception thrown:", e
print 'An error occured' print 'An error occured'
print 'approved',payment.isApproved() print 'approved', payment.isApproved()
print 'declined',payment.isDeclined() print 'declined', payment.isDeclined()
print 'error',payment.isError() print 'error', payment.isError()
if __name__=='__main__': if __name__ == '__main__':
test() test()
-8
View File
@@ -1,8 +0,0 @@
+5 -7
View File
@@ -64,7 +64,7 @@ def new(key, mode=MODE_CBC, IV=None):
return ECBMode(AES(key)) return ECBMode(AES(key))
elif mode == MODE_CBC: elif mode == MODE_CBC:
if IV is None: if IV is None:
raise ValueError, "CBC mode needs an IV value!" raise ValueError("CBC mode needs an IV value!")
return CBCMode(AES(key), IV) return CBCMode(AES(key), IV)
else: else:
@@ -91,7 +91,7 @@ class AES(object):
elif self.key_size == 32: elif self.key_size == 32:
self.rounds = 14 self.rounds = 14
else: else:
raise ValueError, "Key length must be 16, 24 or 32 bytes" raise ValueError("Key length must be 16, 24 or 32 bytes")
self.expand_key() self.expand_key()
@@ -313,7 +313,7 @@ class ECBMode(object):
"""Perform ECB mode with the given function""" """Perform ECB mode with the given function"""
if len(data) % self.block_size != 0: if len(data) % self.block_size != 0:
raise ValueError, "Plaintext length must be multiple of 16" raise ValueError("Plaintext length must be multiple of 16")
block_size = self.block_size block_size = self.block_size
data = array('B', data) data = array('B', data)
@@ -357,7 +357,7 @@ class CBCMode(object):
block_size = self.block_size block_size = self.block_size
if len(data) % block_size != 0: if len(data) % block_size != 0:
raise ValueError, "Plaintext length must be multiple of 16" raise ValueError("Plaintext length must be multiple of 16")
data = array('B', data) data = array('B', data)
IV = self.IV IV = self.IV
@@ -381,7 +381,7 @@ class CBCMode(object):
block_size = self.block_size block_size = self.block_size
if len(data) % block_size != 0: if len(data) % block_size != 0:
raise ValueError, "Ciphertext length must be multiple of 16" raise ValueError("Ciphertext length must be multiple of 16")
data = array('B', data) data = array('B', data)
IV = self.IV IV = self.IV
@@ -500,5 +500,3 @@ aes_Rcon = array('B',
'c697356ad4b37dfaefc5913972e4d3bd' 'c697356ad4b37dfaefc5913972e4d3bd'
'61c29f254a943366cc831d3a74e8cb'.decode('hex') '61c29f254a943366cc831d3a74e8cb'.decode('hex')
) )
+29 -19
View File
@@ -42,7 +42,9 @@ revision3.com
viddler.com viddler.com
""" """
import re, cgi, sys import re
import cgi
import sys
from simplejson import loads from simplejson import loads
import urllib import urllib
import uuid import uuid
@@ -75,23 +77,28 @@ EMBED_MAPS = [
'http://revision3.com/api/oembed/'), 'http://revision3.com/api/oembed/'),
(re.compile('http://\S+.viddler.com/\S+'), (re.compile('http://\S+.viddler.com/\S+'),
'http://lab.viddler.com/services/oembed/'), 'http://lab.viddler.com/services/oembed/'),
] ]
def image(url): def image(url):
return '<img src="%s" style="max-width:100%%"/>' % url return '<img src="%s" style="max-width:100%%"/>' % url
def audio(url): def audio(url):
return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url
def video(url): def video(url):
return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url
def googledoc_viewer(url): def googledoc_viewer(url):
return '<iframe src="http://docs.google.com/viewer?url=%s&embedded=true" style="max-width:100%%"></iframe>' % urllib.quote(url) return '<iframe src="http://docs.google.com/viewer?url=%s&embedded=true" style="max-width:100%%"></iframe>' % urllib.quote(url)
def web2py_component(url): def web2py_component(url):
code = str(uuid.uuid4()) code = str(uuid.uuid4())
return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (code,url,code) return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (code, url, code)
EXTENSION_MAPS = { EXTENSION_MAPS = {
'png': image, 'png': image,
@@ -126,33 +133,36 @@ EXTENSION_MAPS = {
'xps': googledoc_viewer, 'xps': googledoc_viewer,
} }
class VimeoURLOpener(urllib.FancyURLopener): class VimeoURLOpener(urllib.FancyURLopener):
"Vimeo blocks the urllib user agent for some reason" "Vimeo blocks the urllib user agent for some reason"
version = "Mozilla/4.0" version = "Mozilla/4.0"
urllib._urlopener = VimeoURLOpener() urllib._urlopener = VimeoURLOpener()
def oembed(url): def oembed(url):
for k,v in EMBED_MAPS: for k, v in EMBED_MAPS:
if k.match(url): if k.match(url):
oembed = v+'?format=json&url='+cgi.escape(url) oembed = v + '?format=json&url=' + cgi.escape(url)
try: try:
data = urllib.urlopen(oembed).read() data = urllib.urlopen(oembed).read()
print data return loads(data) # json!
return loads(data) # json!
except: except:
pass pass
return {} return {}
def extension(url): def extension(url):
return url.split('?')[0].split('.')[-1].lower() return url.split('?')[0].split('.')[-1].lower()
def expand_one(url,cdict):
def expand_one(url, cdict):
# try ombed but first check in cache # try ombed but first check in cache
if cdict and url in cdict: if cdict and url in cdict:
r = cdict[url] r = cdict[url]
else: else:
r = oembed(url) r = oembed(url)
if isinstance(cdict,dict): if isinstance(cdict, dict):
cdict[url] = r cdict[url] = r
# if oembed service # if oembed service
if 'html' in r: if 'html' in r:
@@ -170,21 +180,23 @@ def expand_one(url,cdict):
# else regular link # else regular link
return '<a href="%(u)s">%(u)s</a>' % dict(u=url) return '<a href="%(u)s">%(u)s</a>' % dict(u=url)
def expand_html(html,cdict=None):
def expand_html(html, cdict=None):
if not have_soup: if not have_soup:
raise RuntimeError, "Missing BeautifulSoup" raise RuntimeError("Missing BeautifulSoup")
soup = BeautifulSoup(html) soup = BeautifulSoup(html)
comments = soup.findAll(text=lambda text:isinstance(text, Comment)) comments = soup.findAll(text=lambda text: isinstance(text, Comment))
[comment.extract() for comment in comments] [comment.extract() for comment in comments]
for txt in soup.findAll(text=True): for txt in soup.findAll(text=True):
if not txt.parent.name in ('a','script','pre','code','embed','object','audio','video'): if not txt.parent.name in ('a', 'script', 'pre', 'code', 'embed', 'object', 'audio', 'video'):
ntxt = regex_link.sub( ntxt = regex_link.sub(
lambda match: expand_one(match.group(0),cdict), txt) lambda match: expand_one(match.group(0), cdict), txt)
txt.replaceWith(BeautifulSoup(ntxt)) txt.replaceWith(BeautifulSoup(ntxt))
return str(soup) return str(soup)
def test(): def test():
example=""" example = """
<h3>Fringilla nisi parturient nullam</h3> <h3>Fringilla nisi parturient nullam</h3>
<p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p> <p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p>
<p>http://www.web2py.com/examples/static/images/logo_bw.png</p> <p>http://www.web2py.com/examples/static/images/logo_bw.png</p>
@@ -198,10 +210,8 @@ laoreet tortor.</p>
""" """
return expand_html(example) return expand_html(example)
if __name__=="__main__": if __name__ == "__main__":
if len(sys.argv)>1: if len(sys.argv) > 1:
print expand_html(open(sys.argv[1]).read()) print expand_html(open(sys.argv[1]).read())
else: else:
print test() print test()
File diff suppressed because it is too large Load Diff
+6 -4
View File
@@ -26,8 +26,9 @@ def hex2dec(color = "#000000"):
class HTML2FPDF(HTMLParser): class HTML2FPDF(HTMLParser):
"Render basic HTML to FPDF" "Render basic HTML to FPDF"
def __init__(self, pdf): def __init__(self, pdf, image_map = None):
HTMLParser.__init__(self) HTMLParser.__init__(self)
self.image_map = image_map or (lambda src: src)
self.style = {} self.style = {}
self.pre = False self.pre = False
self.href = '' self.href = ''
@@ -265,7 +266,8 @@ class HTML2FPDF(HTMLParser):
h = px2mm(attrs.get('height',0)) h = px2mm(attrs.get('height',0))
if self.align and self.align[0].upper() == 'C': if self.align and self.align[0].upper() == 'C':
x = (self.pdf.w-x)/2.0 - w/2.0 x = (self.pdf.w-x)/2.0 - w/2.0
self.pdf.image(attrs['src'], x, y, w, h, link=self.href) self.pdf.image(self.image_map(attrs['src']),
x, y, w, h, link=self.href)
self.pdf.set_x(x+w) self.pdf.set_x(x+w)
self.pdf.set_y(y+h) self.pdf.set_y(y+h)
if tag=='b' or tag=='i' or tag=='u': if tag=='b' or tag=='i' or tag=='u':
@@ -389,9 +391,9 @@ class HTML2FPDF(HTMLParser):
self.pdf.ln(3) self.pdf.ln(3)
class HTMLMixin(object): class HTMLMixin(object):
def write_html(self, text): def write_html(self, text, image_map=None):
"Parse HTML and convert it to PDF" "Parse HTML and convert it to PDF"
h2p = HTML2FPDF(self) h2p = HTML2FPDF(self, image_map)
h2p.feed(text) h2p.feed(text)
+12 -11
View File
@@ -12,6 +12,7 @@ cache.ram=cache.disk=MemcacheClient(request)
import time import time
from google.appengine.api.memcache import Client from google.appengine.api.memcache import Client
class MemcacheClient(object): class MemcacheClient(object):
client = Client() client = Client()
@@ -24,12 +25,12 @@ class MemcacheClient(object):
key, key,
f, f,
time_expire=300, time_expire=300,
): ):
key = '%s/%s' % (self.request.application, key) key = '%s/%s' % (self.request.application, key)
dt = time_expire dt = time_expire
value = None value = None
obj = self.client.get(key) obj = self.client.get(key)
if obj and (dt == None or obj[0] > time.time() - dt): if obj and (dt is None or obj[0] > time.time() - dt):
value = obj[1] value = obj[1]
elif f is None: elif f is None:
if obj: if obj:
@@ -47,21 +48,21 @@ class MemcacheClient(object):
self.client.set(key, (time.time(), value)) self.client.set(key, (time.time(), value))
return value return value
def clear(self, key = None): def clear(self, key=None):
if key: if key:
key = '%s/%s' % (self.request.application, key) key = '%s/%s' % (self.request.application, key)
self.client.delete(key) self.client.delete(key)
else: else:
self.client.flush_all() self.client.flush_all()
def delete(self,*a,**b): def delete(self, *a, **b):
return self.client.delete(*a,**b) return self.client.delete(*a, **b)
def get(self,*a,**b): def get(self, *a, **b):
return self.client.delete(*a,**b) return self.client.delete(*a, **b)
def set(self,*a,**b): def set(self, *a, **b):
return self.client.delete(*a,**b) return self.client.delete(*a, **b)
def flush_all(self,*a,**b): def flush_all(self, *a, **b):
return self.client.delete(*a,**b) return self.client.delete(*a, **b)
+8 -12
View File
@@ -51,7 +51,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
:param exponent: rate of exponential back-off. :param exponent: rate of exponential back-off.
""" """
import time, logging import time
import logging
from google.appengine.api import apiproxy_stub_map from google.appengine.api import apiproxy_stub_map
from google.appengine.runtime import apiproxy_errors from google.appengine.runtime import apiproxy_errors
from google.appengine.datastore import datastore_pb from google.appengine.datastore import datastore_pb
@@ -60,8 +61,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
interval = float(interval) interval = float(interval)
exponent = float(exponent) exponent = float(exponent)
wrapped = apiproxy_stub_map.MakeSyncCall wrapped = apiproxy_stub_map.MakeSyncCall
errors = {datastore_pb.Error.TIMEOUT:'Timeout', errors = {datastore_pb.Error.TIMEOUT: 'Timeout',
datastore_pb.Error.CONCURRENT_TRANSACTION:'TransactionFailedError'} datastore_pb.Error.CONCURRENT_TRANSACTION: 'TransactionFailedError'}
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
count = 0.0 count = 0.0
@@ -70,10 +71,12 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
return wrapped(*args, **kwargs) return wrapped(*args, **kwargs)
except apiproxy_errors.ApplicationError, err: except apiproxy_errors.ApplicationError, err:
errno = err.application_error errno = err.application_error
if errno not in errors: raise if errno not in errors:
raise
sleep = (exponent ** count) * interval sleep = (exponent ** count) * interval
count += 1.0 count += 1.0
if count > attempts: raise if count > attempts:
raise
msg = "Datastore %s: retry #%d in %s seconds.\n%s" msg = "Datastore %s: retry #%d in %s seconds.\n%s"
vals = '' vals = ''
if count == 1.0: if count == 1.0:
@@ -84,10 +87,3 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
setattr(wrapper, '_autoretry_datastore_timeouts', False) setattr(wrapper, '_autoretry_datastore_timeouts', False)
if getattr(wrapped, '_autoretry_datastore_timeouts', True): if getattr(wrapped, '_autoretry_datastore_timeouts', True):
apiproxy_stub_map.MakeSyncCall = wrapper apiproxy_stub_map.MakeSyncCall = wrapper
+25 -23
View File
@@ -12,6 +12,7 @@ from gluon.sanitizer import sanitize
from gluon.contrib.markmin.markmin2latex import markmin2latex from gluon.contrib.markmin.markmin2latex import markmin2latex
from gluon.contrib.markmin.markmin2pdf import markmin2pdf from gluon.contrib.markmin.markmin2pdf import markmin2pdf
def wrapper(f): def wrapper(f):
def g(data): def g(data):
try: try:
@@ -25,46 +26,47 @@ def wrapper(f):
raise HTTP(405, '%s error' % e) raise HTTP(405, '%s error' % e)
return g return g
def latex_from_html(html): def latex_from_html(html):
markmin=TAG(html).element('body').flatten(markmin_serializer) markmin = TAG(html).element('body').flatten(markmin_serializer)
return XML(markmin2latex(markmin)) return XML(markmin2latex(markmin))
def pdflatex_from_html(html): def pdflatex_from_html(html):
if os.system('which pdflatex > /dev/null')==0: if os.system('which pdflatex > /dev/null') == 0:
markmin=TAG(html).element('body').flatten(markmin_serializer) markmin = TAG(html).element('body').flatten(markmin_serializer)
out,warnings,errors=markmin2pdf(markmin) out, warnings, errors = markmin2pdf(markmin)
if errors: if errors:
current.response.headers['Content-Type']='text/html' current.response.headers['Content-Type'] = 'text/html'
raise HTTP(405,HTML(BODY(H1('errors'), raise HTTP(405, HTML(BODY(H1('errors'),
UL(*errors), UL(*errors),
H1('warnings'), H1('warnings'),
UL(*warnings))).xml()) UL(*warnings))).xml())
else: else:
return XML(out) return XML(out)
def pyfpdf_from_html(html): def pyfpdf_from_html(html):
request = current.request request = current.request
def image_map(path): def image_map(path):
if path.startswith('/%s/static/' % request.application): if path.startswith('/%s/static/' % request.application):
return os.path.join(request.folder,path.split('/',2)[2]) return os.path.join(request.folder, path.split('/', 2)[2])
return 'http%s://%s%s' % (request.is_https and 's' or '',request.env.http_host, path) return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path)
class MyFPDF(FPDF, HTMLMixin): pass
pdf=MyFPDF() class MyFPDF(FPDF, HTMLMixin):
pass
pdf = MyFPDF()
pdf.add_page() pdf.add_page()
html = sanitize(html, escape=False) #### should have better list of allowed tags html = sanitize(
pdf.write_html(html,image_map=image_map) html, escape=False) # should have better list of allowed tags
pdf.write_html(html, image_map=image_map)
return XML(pdf.output(dest='S')) return XML(pdf.output(dest='S'))
def pdf_from_html(html): def pdf_from_html(html):
# try use latex and pdflatex # try use latex and pdflatex
if os.system('which pdflatex > /dev/null')==0: if os.system('which pdflatex > /dev/null') == 0:
return pdflatex_from_html(html) return pdflatex_from_html(html)
else: else:
return pyfpdf_from_html(html) return pyfpdf_from_html(html)

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