Compare commits

...
272 Commits
Author SHA1 Message Date
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
mdipierro c2b6c645ef R-2.1.1 2012-10-15 06:40:55 -05:00
mdipierro 36810aaccc fixed error for missing driver 2012-10-15 06:36:44 -05:00
mdipierro 8e14d1c92e updated changelog 2012-10-15 06:15:33 -05:00
mdipierro 9b7e944dea improved custom_inport with app-level track changes 2012-10-15 05:55:45 -05:00
mdipierro b6429ca5fd better errors in custom_import, thanks Michele 2012-10-15 05:46:57 -05:00
mdipierro 8607b09e64 -Y starts cron 2012-10-15 05:33:21 -05:00
mdipierro 64151a2c3e reverted custom_install tests 2012-10-14 20:07:54 -05:00
mdipierro d5749db0cd cron disabled by default unless -H 2012-10-14 18:29:17 -05:00
mdipierro df7d6847e6 fixed issue 1088 2012-10-14 16:40:49 -05:00
mdipierro c6b5ad8179 auth = Auth(db).define_tables() in one line 2012-10-14 15:55:45 -05:00
mdipierro 71ec500fde changed default driver for sqlite, thanks Niphlod 2012-10-14 15:32:33 -05:00
mdipierro 6cf05edf63 DAL(...,do_connect=False,migrate_enabled=False) 2012-10-14 15:08:25 -05:00
mdipierro 39c5341dbc auth_wiki extra fields, thanks Alan 2012-10-14 14:44:37 -05:00
mdipierro e2234a8771 added storage of length, notnull, unique in .table files 2012-10-14 14:41:28 -05:00
mdipierro cac9741cb7 wiki preview patch, thanks Niphlod 2012-10-14 14:28:17 -05:00
mdipierro 648cb296e6 bettere errors in custom_import, thanks Michele 2012-10-14 14:20:12 -05:00
mdipierro 5a0ebb7d8c faster better web2py_uuid, thanks Michele 2012-10-14 14:16:43 -05:00
mdipierro b7b534ccf2 new tests in test_web, tests static/_1.2.3/... 2012-10-13 14:36:20 -05:00
mdipierro fab23d210b removed un-necessary setup_exe_2.6.py 2012-10-13 10:16:06 -05:00
mdipierro 6a18539f7e no more custom_import_install in gaehandler.py 2012-10-13 09:37:41 -05:00
mdipierro 4d87a90c10 redis session patch, thanks Niphlod 2012-10-13 09:26:53 -05:00
mdipierro 2c9767e500 fixed issue 1081, conflict betweeen web2py css error and bootstrap error classes, thanks Paolo and lapcchan 2012-10-13 09:25:32 -05:00
mdipierro 2b1d9acba5 fixed issue 1084 2012-10-12 22:16:23 -05:00
mdipierro 8a2d18b63b ip address validator fixed 2012-10-12 18:17:06 -05:00
Massimo b59f9f896c fixed problem with renaming of the rewrite.thread to rewrite.THREAD_LOCAL 2012-10-12 12:19:08 -05:00
Massimo 496e6663fd some minor simplifications in rewrite.py 2012-10-12 11:48:25 -05:00
Massimo a4fbf9b6b4 improved speed of regex_url_in 2012-10-12 11:33:02 -05:00
mdipierro b8afce746b much better custom_import, smaller, faster, and finally works with shell 2012-10-11 22:03:20 -05:00
mdipierro 4c239eaf79 fixed cron 2012-10-11 16:48:07 -05:00
mdipierro 1d03233c83 just alignment of custom_import 2012-10-11 09:23:36 -05:00
mdipierro 5358ad271e faster init on gae and better gae_memcache 2012-10-11 08:27:58 -05:00
Massimo 2fa7d72b01 added comments in cache, thanks Michele 2012-10-10 15:16:21 -05:00
mdipierro f034d83c81 fixed problem with DAL on googlesql 2012-10-10 12:20:25 -05:00
mdipierro b3afa2ab57 fixed impval=0, thanks vp 2012-10-10 12:10:51 -05:00
mdipierro 36fc758690 increased admin (is_local) security, thanks Jonathan 2012-10-10 12:00:59 -05:00
mdipierro f764eb2653 rocket passes all pathoc tests without hang 2012-10-10 11:57:30 -05:00
mdipierro 63f56ddecc faster local_hosts lookup 2012-10-10 11:43:06 -05:00
mdipierro 0b2bab3f3d fixed issue 1077, thanks c.guimaraes 2012-10-10 11:25:25 -05:00
mdipierro 66de02929b fixed gae_memcache increment bug, issue 1079, thanks in4tunio 2012-10-10 11:21:01 -05:00
mdipierro 3c4a142f9a many improvements in cache, lazy cache.ram and cache.disk 2012-10-10 11:03:52 -05:00
mdipierro bf6f090428 many improvements in cache 2012-10-10 11:01:09 -05:00
mdipierro 59ab818f5b better toolbar, thanks Niphlod 2012-10-10 09:19:01 -05:00
mdipierro 77847daa77 auth.wiki preview, thanks Niphlod 2012-10-10 09:18:09 -05:00
Massimo 690d2df774 redis_sessions.py, thanks Niphlod 2012-10-09 14:36:34 -05:00
Massimo 643cc9c141 added jquery.mobile 1.2.0 2012-10-09 09:04:27 -05:00
mdipierro 562cb58342 some work on mongodb but references do not work 2012-10-08 22:09:25 -05:00
mdipierro 1c8ef29413 fixed time_expire typo in mamcache, thanks Bruno 2012-10-08 12:09:00 -05:00
mdipierro 8e67c551de fixed problem with close all instances when no instances 2012-10-08 11:03:21 -05:00
mdipierro 03e3324a12 removed rc1 2012-10-08 09:15:33 -05:00
mdipierro a3ba3f662a 2.1.0rc1 2012-10-07 22:45:38 -05:00
mdipierro 2b21678d48 clearing zombie list. Should not be necessary but what the heck 2012-10-07 13:31:02 -05:00
mdipierro 09f29257d2 fixed issue 1074, thanks mjmare 2012-10-07 10:48:37 -05:00
mdipierro 62be48decf fixed issue 1072 2012-10-07 10:13:20 -05:00
mdipierro 952a29d5da fixed routes example, issue 1073 2012-10-07 10:12:04 -05:00
mdipierro db9151b993 fixed routes example, issue 1073 2012-10-07 10:11:10 -05:00
mdipierro e1bb2b4556 db.define_table(...,redefine=True) 2012-10-07 01:10:08 -05:00
mdipierro 668658b7c9 zombie dal is here, should fix scheduler problems while retaining Row/Rows serialization 2012-10-07 01:07:49 -05:00
mdipierro 231a3e1278 Try the mobile interface link in site 2012-10-05 10:38:58 -05:00
mdipierro 41d234bf48 fixed mobile admin, and improved it 2012-10-05 10:24:14 -05:00
mdipierro f16c3a394f lazy tables re-appeared in appadmin 2012-10-05 09:01:51 -05:00
mdipierro 570538042e fixed IMAP reconnect 2012-10-05 07:25:21 -05:00
mdipierro 6a97bfe517 Auth use request.requires_https, thanks Yarin 2012-10-05 07:18:35 -05:00
mdipierro 7b6e5c5270 fixed issue 990 2012-10-04 21:52:04 -05:00
mdipierro 20deb56e33 minor indentation change 2012-10-04 21:45:25 -05:00
mdipierro a23a2c52c2 fixed url_in(request, environ)[1]['PATH_INFO'] 2012-10-04 20:39:00 -05:00
mdipierro 3f6fa941cb fixed issue 1067 2012-10-04 20:36:30 -05:00
mdipierro 28f914463b removed depredation working, thanks Niphlod 2012-10-04 20:17:12 -05:00
mdipierro f0c84b5230 scheduler non-writable field, thanks Niphlod 2012-10-04 20:10:55 -05:00
mdipierro dc51abe54c Auth(secure=True), thanks Yarin and Niphlod 2012-10-04 15:12:52 -05:00
mdipierro f501da74b7 fixed setup, thanks Mike 2012-10-04 15:06:58 -05:00
mdipierro c1d14a35ee fixed preblem with db singledon serialization in tickets 2012-10-04 11:53:04 -05:00
mdipierro 4f4a0318aa fixed wiki for missing body 2012-10-04 10:44:35 -05:00
mdipierro 54a1005af3 fixed problem with __new__ deprecated 2012-10-04 08:44:26 -05:00
mdipierro 7b4fac7a7f allow re-definition of tables 2012-10-04 08:35:39 -05:00
Massimo c80a6db604 better request.requires_https, thanks Yarin and Niphlod 2012-10-03 12:32:12 -05:00
Massimo 96247eda51 fixed some problem with grid 2012-10-03 11:58:50 -05:00
Massimo 433a02537c fixed issue 1064 2012-10-03 11:54:03 -05:00
Massimo bacff3453b fised issue 1063 2012-10-02 14:55:56 -05:00
Massimo b20e74c899 wiki uses contains instead of startswith, thanks David 2012-10-02 14:50:16 -05:00
mdipierro efa5ceb6de fixed issue 738 2012-10-01 21:16:42 -05:00
mdipierro f79660dd2a attempt to fix issue 981 2012-10-01 21:11:45 -05:00
mdipierro 606daabdd3 possibly fixed 1020 2012-10-01 20:52:02 -05:00
mdipierro fc1e4ad620 fixed DAL.__new__ and SQLFORM.factory 2012-10-01 19:15:37 -05:00
mdipierro 54dcf2ead9 more customizaiton of booleans 2012-10-01 17:07:06 -05:00
mdipierro 69cea23cd4 better generic.xml, thanks Derek 2012-10-01 15:54:35 -05:00
mdipierro 083b598c93 fixed issue 1031, thanks David 2012-10-01 14:49:42 -05:00
mdipierro 94f11501ed another rocket fix 2012-10-01 14:25:38 -05:00
mdipierro 9ac4a11db1 fixed textarea for long translation strings in admin 2012-10-01 12:30:02 -05:00
mdipierro a56cf59136 speedup in build-environment, thanks Michele 2012-10-01 12:01:35 -05:00
mdipierro 12cc81147c fixed rocket (removed handling of static files, faster parse headers, pathoc fault-tolerant) 2012-10-01 11:57:45 -05:00
mdipierro f70737ddc2 simplification in after_update 2012-09-30 16:50:57 -05:00
mdipierro 1a22497b64 self.connector 2012-09-30 14:45:47 -05:00
mdipierro 6da330a001 adapter have do_connect 2012-09-30 14:19:52 -05:00
mdipierro f6652e7530 made contrib.pbkdf2 optionional for utils 2012-09-30 13:47:49 -05:00
mdipierro 5148c6ec3a cleaner code in dal 2012-09-30 13:40:41 -05:00
mdipierro 954c79abcb fixed Storage copy 2012-09-30 13:20:05 -05:00
mdipierro 7ec3fe3bfe experimental DAL rewrite (work in progress) 2012-09-30 08:37:27 -05:00
mdipierro 9ce1cde6ca fixed issues with grid form and b.parent 2012-09-30 08:32:03 -05:00
mdipierro 672af73e96 fixed admin and new dal 2012-09-30 07:30:08 -05:00
mdipierro 67022578e3 fixed some bugs with singleton and remobed password from session.auth.user 2012-09-30 07:26:23 -05:00
mdipierro 65fe4c16fc fixed problem with order of lazyness in DAL serialization in session 2012-09-30 00:23:15 -05:00
mdipierro b6f9cd1863 login works fine with lazy DAL 2012-09-30 00:10:25 -05:00
mdipierro 4908a5e7be reverted DAL singleton until better understood 2012-09-29 23:47:30 -05:00
mdipierro 853c0b1a3a Better LazySet 2012-09-29 23:38:07 -05:00
mdipierro d0d8a4827e pickle.dumps(db(query).select() now always works and loads restore the full object (almost) 2012-09-29 23:20:37 -05:00
mdipierro 4745ce42f9 DAL is now a pickeable singleton 2012-09-29 22:33:18 -05:00
mdipierro bba13becd0 in dal renamed thread to thread_local 2012-09-29 20:59:50 -05:00
mdipierro 7712e005c4 MemcacheClient(time_expire=3600) 2012-09-29 20:20:03 -05:00
mdipierro 07c667f469 better rocket parsing, support for multiline headers 2012-09-29 18:39:53 -05:00
mdipierro 57ad1ec1cf made new web2py_uuid working on python 2.5 2012-09-29 18:35:09 -05:00
mdipierro 60a043479c yet faster web2py_uuid by caching urandom results 2012-09-29 17:56:43 -05:00
mdipierro 1a43e1671f more web2py_uuid speedup, thanks Michele 2012-09-29 17:16:35 -05:00
mdipierro cd955d3603 fixed issue 1046, thanks Houdini 2012-09-29 10:17:06 -05:00
mdipierro 492a0c51d1 fixed copy.copy(storage), thanks Corne 2012-09-29 09:55:57 -05:00
mdipierro dd4a4c281f prettidate works with future dates, thanks Sanjoy 2012-09-29 09:53:46 -05:00
mdipierro 41ec6da72c fixed issue 1044, thanks Paolo 2012-09-28 20:20:05 -05:00
mdipierro e18f38d4da static asset management, thanks Niphlod 2012-09-28 17:06:11 -05:00
Massimo bd27df8fa5 fixed issue 1042, thanks Dominic 2012-09-28 12:47:36 -05:00
Massimo 2f44c2de9b closed issue 995, thanks Peter 2012-09-28 12:38:03 -05:00
mdipierro ccf154a807 fixed issue 1040 2012-09-28 09:25:27 -05:00
mdipierro 837453dd6e faster web2py_uuid() thanks Michele 2012-09-28 09:21:03 -05:00
mdipierro 64bc60cdf0 fixed search in wiki again for firebird 2012-09-28 08:51:18 -05:00
mdipierro af258c334e fixed menu hoover issue 1036, thanks Paolo 2012-09-27 22:28:59 -05:00
mdipierro 627fff624a fixed many bugs in dal, thanks Mart Senecal 2012-09-27 22:15:56 -05:00
mdipierro bc8127f6de fixed problem with pypy 2012-09-27 22:09:23 -05:00
mdipierro 19bf43815d added better comments to address issue 1033 2012-09-27 21:45:19 -05:00
mdipierro 7709074d7c fixed field_parent references broken in 2.0.x, thanks Marin 2012-09-27 15:12:36 -05:00
mdipierro 4b73f249dd removed some un-necessary has_attr 2012-09-27 14:43:57 -05:00
mdipierro ae9e2c2be0 fixed add button in grid 2012-09-27 14:21:55 -05:00
mdipierro fd8edb5aa2 fixed issue 1032, thanks Carlos 2012-09-27 14:09:05 -05:00
mdipierro 82ab59c46a http 422, better tagcloud, possibly fixed firbird issue with tagcloud 2012-09-27 14:05:43 -05:00
mdipierro 74a63e98b5 possibly fixed issue 1031, thanks villas 2012-09-26 12:45:56 -05:00
Massimo 9e23b3dac5 db.get(,default=None), thanks Carlos 2012-09-25 16:18:52 -05:00
Massimo 077ce011e2 distinct instead of groupby 2012-09-25 14:56:49 -05:00
Massimo 1430bc824f test 2012-09-25 14:50:32 -05:00
mdipierro 722b16e620 CAS should disabled retrive username 2012-09-24 22:44:19 -05:00
mdipierro 30727ef9e4 synced web2py.js, thanks Jeremy 2012-09-24 20:18:30 -05:00
mdipierro 0f322f8a69 db().select(...query.case('true','false)) code cleanup 2012-09-24 18:09:22 -05:00
mdipierro ec5b4dde6f db().select(...query.case('true','false)) 2012-09-24 18:06:07 -05:00
mdipierro 630dcb799e gluon/contrib/comet_messaging.py -> gluon/contrib/websocket_messaging.py 2012-09-24 16:21:11 -05:00
mdipierro c6d1e47226 nore more typo fixed, thanks niphlod 2012-09-24 15:54:58 -05:00
mdipierro aa8315a7c9 data, no attr, thanks Niphlod 2012-09-24 15:13:34 -05:00
mdipierro 80040bf8e5 apos entity fix, thanks Michele 2012-09-24 12:48:39 -05:00
mdipierro 8f382f322e better setup-web2py-nginx-uwsgi-ubuntu.sh, thanks Bruno 2012-09-24 12:44:42 -05:00
mdipierro 9ccc7dc59a crud methods have FORM **attributes, thanks Daniel 2012-09-24 12:43:22 -05:00
mdipierro 70f6f86827 fixed https://github.com/web2py/web2py/pull/33, fetching Oracle Clobs, thanks dhx 2012-09-24 12:39:28 -05:00
mdipierro 3055c87567 fixed issue 1028, thanks Sherdim 2012-09-24 11:59:57 -05:00
mdipierro 473955611a addClass/hasClass entropy_check, thanks Niphlod 2012-09-24 11:50:59 -05:00
mdipierro 21a7ded8e0 fixed minify code to fix paths in css files, thanks Jeremy 2012-09-24 10:11:35 -05:00
mdipierro 23347995e2 css:inline patch,thanks Jeremy 2012-09-24 09:25:26 -05:00
mdipierro 4027a3cda3 SQLFORM().accepts(onvalidation=dict(onchange=lambda....)), thanks Alan 2012-09-23 19:24:05 -05:00
mdipierro a9b6c4f1a3 linked_tables in smartgrid can be a dict() 2012-09-23 19:16:27 -05:00
mdipierro c1330662aa better entropy colors and optional callback, thanks Niphlod 2012-09-23 17:00:34 -05:00
mdipierro 3e38f8017f better entropy colors and optional callback, thanks Niphlod 2012-09-23 16:58:11 -05:00
mdipierro 41bc1cb1e9 linked_tables in smartgrid can be a dict() 2012-09-23 16:31:44 -05:00
mdipierro 9fb94008ea linked_tables in smartgrid can be a dict() 2012-09-23 16:30:31 -05:00
mdipierro 5f647a46b8 moved layout.js in web2py_bootstrap.js and include_files(extensions=[...]), thanks Jeremy 2012-09-23 14:37:50 -05:00
mdipierro 69a2e76c3c entropy check code in web2py.js, thanks Niphlod 2012-09-23 14:07:50 -05:00
mdipierro 4d6598c645 fixed typo in validators 2012-09-22 11:19:14 -05:00
mdipierro 56acb685e9 removed some duplication of code in dal.py, thanks Mart 2012-09-22 10:11:17 -05:00
mdipierro f0be4416b3 restored comments in boostrap_css.css 2012-09-22 09:54:53 -05:00
mdipierro 93ce59f65b moved entropy js back to user.html 2012-09-22 09:52:08 -05:00
mdipierro 28b94eceb2 formstyle='inline' for forms with single field 2012-09-21 22:49:45 -05:00
mdipierro e4c63769f2 reverted 4077 because distinct on and groupby are not equivalent. Moreover distinct on is supported only by postgresql because potentially non-deterministic and should be discouraged. Thanks Alexander 2012-09-21 22:29:55 -05:00
mdipierro e0fc61932d broken long lines in minify 2012-09-21 22:23:45 -05:00
mdipierro 3de3046260 fixed share Google+, thanks nicozanf 2012-09-21 22:14:08 -05:00
mdipierro b6a96301cd Merge branch 'master' of https://github.com/nicozanf/web2py 2012-09-21 22:13:09 -05:00
mdipierro 480dea23ad moved entropy JS in PasswordWidget but the source should be moved in separate file 2012-09-21 22:12:17 -05:00
mdipierro 457ef24c04 added missing patch, thanks Niphlod 2012-09-21 22:01:51 -05:00
Nico Zanferrari dbf6ce6d6c Modified share to Buzz to Google+ 2012-09-22 00:10:23 +02:00
nicozanf 7686a6fc93 Merge pull request #3 from mdipierro/master
up
2012-09-21 14:32:55 -07:00
mdipierro afde6efafa cleanup web2py_bootstrap.css 2012-09-21 09:37:37 -05:00
mdipierro af53f17f5a scheduler optimization for single process, thanks Niphlod 2012-09-21 08:58:43 -05:00
mdipierro 8f9cf4034d user.html has entrpy color-coding, thanks Niphlod 2012-09-21 08:57:11 -05:00
mdipierro 31a6c5df9b hidden fields should be visible 2012-09-21 08:29:16 -05:00
nicozanf 08d1b26738 Merge pull request #1 from mdipierro/master
test
2012-09-20 13:29:35 -07:00
mdipierro abd739123c added search to codemirror 2012-09-20 13:25:54 -05:00
mdipierro da8570b560 better zip_static_files.py, thanks Niphlod 2012-09-20 11:19:11 -05:00
mdipierro 8d238890a2 fixed issue 1021, thanks Edouard and Jonathan 2012-09-20 11:16:51 -05:00
nicozanf ca29f262e5 Unnecessary calls to logging()
the module logging() is not imported and called unnecessary.
2012-09-20 17:52:55 +03:00
mdipierro cffefbeb2e possibly fixed issue 1020 2012-09-20 09:27:05 -05:00
mdipierro 52528f9311 changed brand color 2012-09-20 09:24:38 -05:00
mdipierro 8b75256281 better layout yet, thanks Paolo 2012-09-20 09:19:21 -05:00
mdipierro 2385ed22b1 new web2py_bootstrap_nojs.css, thanks Paolo 2012-09-20 09:16:04 -05:00
mdipierro 7a0cf446ba entropy computation in IS_STRONG, thanks Jonathan 2012-09-20 09:14:51 -05:00
mdipierro 586d85cf08 fixed a problem with reset_password redirect loop 2012-09-19 23:04:10 -05:00
mdipierro f371e72e07 mail.send(...,sender='Mr X <%(sender)s>') 2012-09-19 00:03:06 -05:00
mdipierro 8d9bc1cb53 mail.send(...,sender='Mr X <%(sender)s>') 2012-09-19 00:00:18 -05:00
mdipierro 29edf94498 enhanced imapadapter, thanks Alan 2012-09-18 13:39:16 -05:00
mdipierro d7a108ff06 fixed recent typo, thanks Christian 2012-09-18 13:37:41 -05:00
mdipierro 5ed41285c0 possibly fixed issue 1016 2012-09-18 13:35:47 -05:00
mdipierro b16d79e654 some more CSS fixes, thanks Paolo 2012-09-17 21:37:13 -05:00
mdipierro 90e5d06d3d bootstrap 2.1.1, thanks Paolo 2012-09-17 21:32:49 -05:00
mdipierro a1156cae6c improved minify, thanks Niphlod 2012-09-17 21:25:05 -05:00
mdipierro 037a200357 IS_IN_DB cacheable=True 2012-09-17 16:22:30 -05:00
mdipierro e282454bfe fixed a problem with order of table definitions in auth.wiki, thanks David 2012-09-17 14:23:13 -05:00
mdipierro e499c1d5a4 fixed problem with missing wiki_media.filename 2012-09-17 11:47:07 -05:00
mdipierro 8540b1b113 fixed issue 1014, thanks Sherdim 2012-09-17 11:43:26 -05:00
mdipierro 6b5e3f2abf better layout, thanks Paolo 2012-09-17 11:36:00 -05:00
mdipierro f8be550e6a fixed comment 2012-09-17 10:26:17 -05:00
mdipierro 6a7b04199f fixed issue 1005, thanks howesc 2012-09-17 09:36:13 -05:00
mdipierro 2847e0f4df fixed imap dal __contains__ issue 1013, thanks Alan 2012-09-17 09:12:13 -05:00
mdipierro eec7f7c687 cid argument in SQLTABLE, thanks Massimiliano 2012-09-17 08:58:01 -05:00
mdipierro a4253deda3 fixed ducktyping problem, thanks Jonathan 2012-09-17 07:13:26 -05:00
mdipierro 372d639601 fixed exception on missin stored_password 2012-09-17 07:10:51 -05:00
mdipierro 06e5c6869d scripts/zip_static_files.py, thanks Niphlod 2012-09-16 15:55:10 -05:00
mdipierro 933189c216 niphlod is right, no need to to restrict gzip streaming to rocket 2012-09-16 15:54:20 -05:00
mdipierro c942881f36 convert distinct to groupby when not supported 2012-09-16 15:51:51 -05:00
mdipierro 99e491ca0f better streamer 2012-09-16 15:38:31 -05:00
mdipierro ea539d50e2 adding missing import in scheduler 2012-09-16 14:10:39 -05:00
mdipierro 497bbf002c possibly fixed issue 1011 2012-09-16 13:55:29 -05:00
mdipierro 2629fac624 fixed problem with error on liststring widget 2012-09-16 13:06:43 -05:00
mdipierro 30af5901eb fixed alignment in forms with list:string 2012-09-16 12:23:19 -05:00
mdipierro 42b60d3291 fixed datetime T in scheduler 2012-09-16 11:46:03 -05:00
mdipierro 9b53fd2302 fixed issue 1010, error reporting on list:string 2012-09-16 11:43:03 -05:00
mdipierro 972f64b6f9 better impersonate, thanks Alan 2012-09-16 10:43:39 -05:00
mdipierro 3eb4627ccf typo in new appadmin.html 2012-09-16 08:53:01 -05:00
mdipierro 2ba88b8951 auth.wiki(render='markmin'), thanks Guruyaya 2012-09-15 15:42:44 -05:00
mdipierro 117b04169c wiki file should be filename, thanks David 2012-09-15 13:41:24 -05:00
mdipierro c56d5bd066 gae_memcache.clear(), thanks Matt 2012-09-14 21:17:01 -05:00
mdipierro 00bfde9ce9 cache.with_prefix(cache.ram,'prefix'), thanks Bruno 2012-09-14 16:08:25 -05:00
mdipierro 5f503d0427 db.t.f.epoch(), thanks Niphlod 2012-09-14 15:51:58 -05:00
mdipierro 47bcab6b26 fixed google+ share 2012-09-14 09:42:16 -05:00
mdipierro ecca0439f1 db(table).select() uses table._id<>None instead of table._id>0 2012-09-14 09:27:09 -05:00
mdipierro e896be4037 fixed typo in setup script, thanks Devon 2012-09-14 09:23:31 -05:00
mdipierro b0e4284130 uninstalled apps backup under deposit 2012-09-14 08:47:42 -05:00
mdipierro 3e69889cca removed troublesome chipin widget 2012-09-13 21:48:40 -05:00
mdipierro dfb2e62c6c tables and field names should be allowed to start in a number, thanks Adi 2012-09-13 21:04:45 -05:00
249 changed files with 13449 additions and 11440 deletions
+44 -1
View File
@@ -1,4 +1,47 @@
## 2.00.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
- overall faster web2py
- when apps are deleted, a w2p copy left in deposit folder
- change in cron (it is now disabled by default). removed -N option and introduced -Y.
- faster web2py_uuid() and request initialization logic, thanks Michele
- static asset management, thanks Niphlod
- improved mobile admin
- request.requires_https and Auth(secure=True), thanks Yarin and Niphlod
- better custom_import (works per app and is faster), thanks Michele
- redis_sesssion.py, thanks Niphlod
- allow entropy computation in IS_STRONG and web2py.js, thanks Jonathan and Niphlod
- fixed many aith.wiki problems
- support for auth.wiki(render='html')
- better welcome layout, thanks Paolo
- db.define_table(...,redefine=True)
- DAL, Row, and Rows object can now be pickled/unpickled, thanks to zombie DAL.
- admin uses codemirror
- allow syntax auth = Auth(db).define_tables()
- better auth.wiki with preview, thanks Alan
- better auth.impersonate, thanks Alan
- upgraded jQuery 1.8
- upgraded Bootstrap 2.1
- fixed problem with dropbox_account.py
- many fixes to cache.ram, cache.disk, memcache and gae_memcache
- cache.with_prefix(cache.ram,'prefix')
- db.table.field.epoch() counts seconds from epoch
- DAL support for SQL CASE, example: db().select(...query.case('true','false))
- DAL(...,do_connect=False) allows faking connections
- DAL(...,auto_import=True) now retieves some fiel attributes
- mail can specify a sender: mail.send(...,sender='Mr X <%(sender)s>')
- renamed gluon/contrib/comet_messaging.py -> gluon/contrib/websocket_messaging.py
## 2.0.1-11
### DAL Improvements ### DAL Improvements
+8 -5
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.0.9 ('`date +%Y-%m-%d\ %H:%M:%S`') stable' > VERSION echo 'Version 2.2.1 ('`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
@@ -107,10 +107,6 @@ win:
cp applications/__init__.py ../web2py_win/web2py/applications cp applications/__init__.py ../web2py_win/web2py/applications
cd ../web2py_win; zip -r web2py_win.zip web2py cd ../web2py_win; zip -r web2py_win.zip web2py
mv ../web2py_win/web2py_win.zip . mv ../web2py_win/web2py_win.zip .
pip:
# create Web2py distribution for upload to Pypi
# after upload clean Web2py sources with rm -R ./dist
python setup.py sdist
run: run:
python2.5 web2py.py -a hello python2.5 web2py.py -a hello
commit: commit:
@@ -129,3 +125,10 @@ tag:
hg tag -l '$(S)' hg tag -l '$(S)'
make commit S='$(S)' make commit S='$(S)'
make push make push
pip:
# create Web2py distribution for upload to Pypi
# after upload clean Web2py sources with rm -R ./dist
# http://guide.python-distribute.org/creation.html
python setup.py sdist
sudo python setup.py register
sudo python setup.py sdist upload
+1 -1
View File
@@ -1 +1 @@
Version 2.0.9 (2012-09-13 17:48:15) stable Version 2.2.1 (2012-10-21 10:54:10) 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()
-1
View File
@@ -80,7 +80,6 @@ skip_files: |
builtins: builtins:
- remote_api: on - remote_api: on
# - datastore_admin: on
- appstats: on - appstats: on
- admin_redirect: on - admin_redirect: on
- deferred: on - deferred: on
-5
View File
@@ -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 @@
+70 -51
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,25 +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)
@@ -128,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])
# ########################################################## # ##########################################################
@@ -139,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
@@ -150,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)
@@ -193,14 +193,17 @@ 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())
@@ -210,26 +213,30 @@ def select():
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
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:
@@ -238,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(
@@ -249,8 +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,
)
# ########################################################## # ##########################################################
@@ -260,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)
@@ -277,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])
# ########################################################## # ##########################################################
@@ -301,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):
@@ -329,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 = {
@@ -378,9 +398,10 @@ def ccache():
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
locker = open(os.path.join(request.folder, locker = open(os.path.join(request.folder,
'cache/cache.lock'), 'a') 'cache/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(request.folder, 'cache/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):
@@ -411,7 +432,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
@@ -437,6 +459,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)
+35 -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)
@@ -185,17 +195,16 @@ def toggle_breakpoint():
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_ALPHANUMERIC(),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)
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)
@@ -19,5 +19,3 @@ for filename in os.listdir(path):
os.unlink(fullpath) os.unlink(fullpath)
except: except:
logging.exception('failure to check %s'%fullpath) logging.exception('failure to check %s'%fullpath)
+183 -125
View File
@@ -3,143 +3,26 @@
'!langcode!': 'it', '!langcode!': 'it',
'!langname!': 'Italiano', '!langname!': 'Italiano',
'"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"update" è un\'espressione opzionale come "campo1=\'nuovo valore\'". Non si può fare "update" o "delete" dei risultati di un JOIN ',
'%Y-%m-%d': '%d/%m/%Y',
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
'%s %%{row} deleted': '%s righe ("record") cancellate', '%s %%{row} deleted': '%s righe ("record") cancellate',
'%s %%{row} updated': '%s righe ("record") modificate', '%s %%{row} updated': '%s righe ("record") modificate',
'%Y-%m-%d': '%d/%m/%Y',
'%Y-%m-%d %H:%M:%S': '%d/%m/%Y %H:%M:%S',
'(requires internet access)': '(requires internet access)', '(requires internet access)': '(requires internet access)',
'(something like "it-it")': '(qualcosa simile a "it-it")', '(something like "it-it")': '(qualcosa simile a "it-it")',
'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)',
'@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files',
'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s', 'A new version of web2py is available: %s': 'È disponibile una nuova versione di web2py: %s',
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)",
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")',
'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ",
'About': 'informazioni', 'About': 'informazioni',
'About application': "Informazioni sull'applicazione", 'About application': "Informazioni sull'applicazione",
'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura',
'Admin language': 'Admin language',
'Administrator Password:': 'Password Amministratore:',
'Application name:': 'Application name:',
'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?',
'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?',
'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?',
'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?',
'Available databases and tables': 'Database e tabelle disponibili',
'Cannot be empty': 'Non può essere vuoto',
'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.",
'Change admin password': 'change admin password',
'Check for upgrades': 'check for upgrades',
'Check to delete': 'Seleziona per cancellare',
'Checking for upgrades...': 'Controllo aggiornamenti in corso...',
'Clean': 'pulisci',
'Compile': 'compila',
'Controller': 'Controller',
'Controllers': 'Controllers',
'Copyright': 'Copyright',
'Create': 'crea',
'Create new simple application': 'Crea nuova applicazione',
'Current request': 'Richiesta (request) corrente',
'Current response': 'Risposta (response) corrente',
'Current session': 'Sessione (session) corrente',
'DB Model': 'Modello di DB',
'Database': 'Database',
'Date and Time': 'Data and Ora',
'Delete': 'Cancella',
'Delete:': 'Cancella:',
'Deploy': 'deploy',
'Deploy on Google App Engine': 'Installa su Google App Engine',
'EDIT': 'MODIFICA',
'Edit': 'modifica',
'Edit This App': 'Modifica questa applicazione',
'Edit application': 'Modifica applicazione',
'Edit current record': 'Modifica record corrente',
'Editing Language file': 'Modifica file linguaggio',
'Editing file "%s"': 'Modifica del file "%s"',
'Enterprise Web Framework': 'Enterprise Web Framework',
'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"',
'Errors': 'errori',
'Exception instance attributes': 'Exception instance attributes',
'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].',
'Hello World': 'Salve Mondo',
'Help': 'aiuto',
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
'Import/Export': 'Importa/Esporta',
'Index': 'Indice',
'Install': 'installa',
'Installed applications': 'Applicazioni installate',
'Internal State': 'Stato interno',
'Invalid Query': 'Richiesta (query) non valida',
'Invalid action': 'Azione non valida',
'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati',
'Languages': 'Linguaggi',
'Last saved on:': 'Ultimo salvataggio:',
'Layout': 'Layout',
'License for': 'Licenza relativa a',
'Login': 'Accesso',
'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa",
'Logout': 'uscita',
'Main Menu': 'Menu principale',
'Menu Model': 'Menu Modelli',
'Models': 'Modelli',
'Modules': 'Moduli',
'NO': 'NO',
'New Record': 'Nuovo elemento (record)',
'New application wizard': 'New application wizard',
'New simple application': 'New simple application',
'No databases in this application': 'Nessun database presente in questa applicazione',
'Original/Translation': 'Originale/Traduzione',
'Overwrite installed app': 'sovrascrivi applicazione installata',
'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui',
'Pack all': 'crea pacchetto',
'Pack compiled': 'crea pacchetto del codice compilato',
'Peeking at file': 'Uno sguardo al file',
'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione',
'Plugins': 'I Plugins',
'Powered by': 'Powered by',
'Query:': 'Richiesta (query):',
'Remove compiled': 'rimozione codice compilato',
'Resolve Conflict file': 'File di risoluzione conflitto',
'Rows in table': 'Righe nella tabella',
'Rows selected': 'Righe selezionate',
'Saved file hash:': 'Hash del file salvato:',
'Site': 'sito',
'Start wizard': 'start wizard',
'Static files': 'Files statici',
'Stylesheet': 'Foglio di stile (stylesheet)',
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
'TM': 'TM',
'Testing application': 'Test applicazione in corsg',
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.',
'There are no controllers': 'Non ci sono controller',
'There are no models': 'Non ci sono modelli',
'There are no modules': 'Non ci sono moduli',
'There are no static files': 'Non ci sono file statici',
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
'There are no views': 'Non ci sono viste ("view")',
'This is the %(filename)s template': 'Questo è il template %(filename)s',
'Ticket': 'Ticket',
'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]',
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
'Unable to download app because:': 'Impossibile scaricare applicazione perché',
'Unable to download because': 'Impossibile scaricare perché',
'Unable to download because:': 'Unable to download because:',
'Uninstall': 'disinstalla',
'Update:': 'Aggiorna:',
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
'Upload a package:': 'Upload a package:',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
'Use an url:': 'Use an url:',
'Version': 'Versione',
'View': 'Vista',
'Views': 'viste',
'Welcome %s': 'Benvenuto %s',
'Welcome to web2py': 'Benvenuto su web2py',
'YES': 'SI',
'additional code for your application': 'righe di codice aggiuntive per la tua applicazione', 'additional code for your application': 'righe di codice aggiuntive per la tua applicazione',
'Additional code for your application': 'Additional code for your application',
'admin disabled because no admin password': 'amministrazione disabilitata per mancanza di password amministrativa', 'admin disabled because no admin password': 'amministrazione disabilitata per mancanza di password amministrativa',
'admin disabled because not supported on google app engine': 'amministrazione non supportata da Google Apps Engine', 'admin disabled because not supported on google app engine': 'amministrazione non supportata da Google Apps Engine',
'admin disabled because unable to access password file': 'amministrazione disabilitata per impossibilità di leggere il file delle password', 'admin disabled because unable to access password file': 'amministrazione disabilitata per impossibilità di leggere il file delle password',
'Admin is disabled because insecure channel': 'amministrazione disabilitata: comunicazione non sicura',
'Admin language': 'Admin language',
'administrative interface': 'administrative interface', 'administrative interface': 'administrative interface',
'Administrator Password:': 'Password Amministratore:',
'and rename it (required):': 'e rinominala (obbligatorio):', 'and rename it (required):': 'e rinominala (obbligatorio):',
'and rename it:': 'e rinominala:', 'and rename it:': 'e rinominala:',
'appadmin': 'appadmin ', 'appadmin': 'appadmin ',
@@ -147,44 +30,104 @@
'application "%s" uninstalled': 'applicazione "%s" disinstallata', 'application "%s" uninstalled': 'applicazione "%s" disinstallata',
'application compiled': 'applicazione compilata', 'application compiled': 'applicazione compilata',
'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare", 'application is compiled and cannot be designed': "l'applicazione è compilata e non si può modificare",
'Application name:': 'Application name:',
'are not used': 'are not used',
'are not used yet': 'are not used yet',
'Are you sure you want to delete file "%s"?': 'Confermi di voler cancellare il file "%s"?',
'Are you sure you want to delete plugin "%s"?': 'Confermi di voler cancellare il plugin "%s"?',
'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?',
'Are you sure you want to uninstall application "%s"?': 'Confermi di voler disinstallare l\'applicazione "%s"?',
'Are you sure you want to upgrade web2py now?': 'Confermi di voler aggiornare web2py ora?',
'arguments': 'arguments', 'arguments': 'arguments',
'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': "ATTENZIONE: L'accesso richiede una connessione sicura (HTTPS) o l'esecuzione di web2py in locale (connessione su localhost)",
'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTZIONE: NON ESEGUIRE PIÙ TEST IN PARALLELO (I TEST NON SONO "THREAD SAFE")',
'ATTENTION: you cannot edit the running application!': "ATTENZIONE: non puoi modificare l'applicazione correntemente in uso ",
'Available databases and tables': 'Database e tabelle disponibili',
'back': 'indietro', 'back': 'indietro',
'cache': 'cache', 'cache': 'cache',
'cache, errors and sessions cleaned': 'pulitura cache, errori and sessioni ', 'cache, errors and sessions cleaned': 'pulitura cache, errori and sessioni ',
'can be a git repo': 'can be a git repo',
'Cannot be empty': 'Non può essere vuoto',
'Cannot compile: there are errors in your app:': "Compilazione fallita: ci sono errori nell'applicazione.",
'cannot create file': 'impossibile creare il file', 'cannot create file': 'impossibile creare il file',
'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"', 'cannot upload file "%(filename)s"': 'impossibile caricare il file "%(filename)s"',
'Change admin password': 'change admin password',
'change password': 'cambia password', 'change password': 'cambia password',
'check all': 'controlla tutto', 'check all': 'controlla tutto',
'Check for upgrades': 'check for upgrades',
'Check to delete': 'Seleziona per cancellare',
'Checking for upgrades...': 'Controllo aggiornamenti in corso...',
'Clean': 'pulisci',
'click here for online examples': 'clicca per vedere gli esempi', 'click here for online examples': 'clicca per vedere gli esempi',
'click here for the administrative interface': "clicca per l'interfaccia amministrativa", 'click here for the administrative interface': "clicca per l'interfaccia amministrativa",
'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti', 'click to check for upgrades': 'clicca per controllare presenza di aggiornamenti',
'code': 'code', 'code': 'code',
'collapse/expand all': 'collapse/expand all',
'Compile': 'compila',
'compiled application removed': "rimosso il codice compilato dell'applicazione", 'compiled application removed': "rimosso il codice compilato dell'applicazione",
'Controller': 'Controller',
'Controllers': 'Controllers',
'controllers': 'controllers', 'controllers': 'controllers',
'Copyright': 'Copyright',
'Create': 'crea',
'create file with filename:': 'crea un file col nome:', 'create file with filename:': 'crea un file col nome:',
'create new application:': 'create new application:', 'create new application:': 'create new application:',
'Create new simple application': 'Crea nuova applicazione',
'created by': 'creato da', 'created by': 'creato da',
'crontab': 'crontab', 'crontab': 'crontab',
'Current request': 'Richiesta (request) corrente',
'Current response': 'Risposta (response) corrente',
'Current session': 'Sessione (session) corrente',
'currently running': 'currently running', 'currently running': 'currently running',
'currently saved or': 'attualmente salvato o', 'currently saved or': 'attualmente salvato o',
'customize me!': 'Personalizzami!', 'customize me!': 'Personalizzami!',
'data uploaded': 'dati caricati', 'data uploaded': 'dati caricati',
'Database': 'Database',
'database': 'database', 'database': 'database',
'database %s select': 'database %s select', 'database %s select': 'database %s select',
'database administration': 'amministrazione database', 'database administration': 'amministrazione database',
'Date and Time': 'Data and Ora',
'db': 'db', 'db': 'db',
'DB Model': 'Modello di DB',
'Debug': 'Debug',
'defines tables': 'defininisce le tabelle', 'defines tables': 'defininisce le tabelle',
'Delete': 'Cancella',
'delete': 'Cancella', 'delete': 'Cancella',
'delete all checked': 'cancella tutti i selezionati', 'delete all checked': 'cancella tutti i selezionati',
'delete plugin': 'cancella plugin', 'delete plugin': 'cancella plugin',
'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)',
'Delete:': 'Cancella:',
'Deploy': 'deploy',
'Deploy on Google App Engine': 'Installa su Google App Engine',
'Deploy to OpenShift': 'Deploy to OpenShift',
'design': 'progetta', 'design': 'progetta',
'Detailed traceback description': 'Detailed traceback description',
'direction: ltr': 'direction: ltr', 'direction: ltr': 'direction: ltr',
'Disable': 'Disable',
'docs': 'docs',
'done!': 'fatto!', 'done!': 'fatto!',
'download layouts': 'download layouts',
'download plugins': 'download plugins',
'EDIT': 'MODIFICA',
'Edit': 'modifica',
'Edit application': 'Modifica applicazione',
'edit controller': 'modifica controller', 'edit controller': 'modifica controller',
'Edit current record': 'Modifica record corrente',
'edit profile': 'modifica profilo', 'edit profile': 'modifica profilo',
'Edit This App': 'Modifica questa applicazione',
'edit views:': 'modifica viste (view):', 'edit views:': 'modifica viste (view):',
'Editing file "%s"': 'Modifica del file "%s"',
'Editing Language file': 'Modifica file linguaggio',
'Enterprise Web Framework': 'Enterprise Web Framework',
'Error logs for "%(app)s"': 'Log degli errori per "%(app)s"',
'Error snapshot': 'Error snapshot',
'Error ticket': 'Error ticket',
'Errors': 'errori',
'Exception instance attributes': 'Exception instance attributes',
'Expand Abbreviation': 'Expand Abbreviation',
'export as csv file': 'esporta come file CSV', 'export as csv file': 'esporta come file CSV',
'exposes': 'espone', 'exposes': 'espone',
'exposes:': 'exposes:',
'extends': 'estende', 'extends': 'estende',
'failed to reload module because:': 'ricaricamento modulo fallito perché:', 'failed to reload module because:': 'ricaricamento modulo fallito perché:',
'file "%(filename)s" created': 'creato il file "%(filename)s"', 'file "%(filename)s" created': 'creato il file "%(filename)s"',
@@ -195,73 +138,188 @@
'file does not exist': 'file inesistente', 'file does not exist': 'file inesistente',
'file saved on %(time)s': "file salvato nell'istante %(time)s", 'file saved on %(time)s': "file salvato nell'istante %(time)s",
'file saved on %s': 'file salvato: %s', 'file saved on %s': 'file salvato: %s',
'filter': 'filter',
'Frames': 'Frames',
'Functions with no doctests will result in [passed] tests.': 'I test delle funzioni senza "doctests" risulteranno sempre [passed].',
'Get from URL:': 'Get from URL:',
'Git Pull': 'Git Pull',
'Git Push': 'Git Push',
'Hello World': 'Salve Mondo',
'Help': 'aiuto',
'htmledit': 'modifica come html', 'htmledit': 'modifica come html',
'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.',
'Import/Export': 'Importa/Esporta',
'includes': 'include', 'includes': 'include',
'Index': 'Indice',
'insert new': 'inserisci nuovo', 'insert new': 'inserisci nuovo',
'insert new %s': 'inserisci nuovo %s', 'insert new %s': 'inserisci nuovo %s',
'inspect attributes': 'inspect attributes',
'Install': 'installa',
'Installed applications': 'Applicazioni installate',
'internal error': 'errore interno', 'internal error': 'errore interno',
'Internal State': 'Stato interno',
'Invalid action': 'Azione non valida',
'invalid password': 'password non valida', 'invalid password': 'password non valida',
'Invalid Query': 'Richiesta (query) non valida',
'invalid request': 'richiesta non valida', 'invalid request': 'richiesta non valida',
'invalid ticket': 'ticket non valido', 'invalid ticket': 'ticket non valido',
'Key bindings': 'Key bindings',
'Key bindings for ZenCoding Plugin': 'Key bindings for ZenCoding Plugin',
'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato', 'language file "%(filename)s" created/updated': 'file linguaggio "%(filename)s" creato/aggiornato',
'Language files (static strings) updated': 'Linguaggi (documenti con stringhe statiche) aggiornati',
'languages': 'linguaggi', 'languages': 'linguaggi',
'Languages': 'Linguaggi',
'Last saved on:': 'Ultimo salvataggio:',
'Layout': 'Layout',
'License for': 'Licenza relativa a',
'loading...': 'caricamento...', 'loading...': 'caricamento...',
'locals': 'locals',
'login': 'accesso', 'login': 'accesso',
'Login': 'Accesso',
'Login to the Administrative Interface': "Accesso all'interfaccia amministrativa",
'Logout': 'uscita',
'Main Menu': 'Menu principale',
'Menu Model': 'Menu Modelli',
'merge': 'unisci', 'merge': 'unisci',
'models': 'modelli', 'models': 'modelli',
'Models': 'Modelli',
'Modules': 'Moduli',
'modules': 'moduli', 'modules': 'moduli',
'new application "%s" created': 'creata la nuova applicazione "%s"', 'new application "%s" created': 'creata la nuova applicazione "%s"',
'New application wizard': 'New application wizard',
'new plugin installed': 'installato nuovo plugin', 'new plugin installed': 'installato nuovo plugin',
'New Record': 'Nuovo elemento (record)',
'new record inserted': 'nuovo record inserito', 'new record inserted': 'nuovo record inserito',
'New simple application': 'New simple application',
'next 100 rows': 'prossime 100 righe', 'next 100 rows': 'prossime 100 righe',
'NO': 'NO',
'No databases in this application': 'Nessun database presente in questa applicazione',
'no match': 'nessuna corrispondenza', 'no match': 'nessuna corrispondenza',
'or import from csv file': 'oppure importa da file CSV', 'or import from csv file': 'oppure importa da file CSV',
'or provide app url:': "oppure fornisci url dell'applicazione:", 'or provide app url:': "oppure fornisci url dell'applicazione:",
'Original/Translation': 'Originale/Traduzione',
'Overwrite installed app': 'sovrascrivi applicazione installata',
'Pack all': 'crea pacchetto',
'Pack compiled': 'crea pacchetto del codice compilato',
'pack plugin': 'crea pacchetto del plugin', 'pack plugin': 'crea pacchetto del plugin',
'PAM authenticated user, cannot change password here': 'utente autenticato tramite PAM, impossibile modificare password qui',
'password changed': 'password modificata', 'password changed': 'password modificata',
'Peeking at file': 'Uno sguardo al file',
'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" cancellato',
'Plugin "%s" in application': 'Plugin "%s" nell\'applicazione',
'plugins': 'plugins',
'Plugins': 'I Plugins',
'Plural-Forms:': 'Plural-Forms:',
'Powered by': 'Powered by',
'previous 100 rows': '100 righe precedenti', 'previous 100 rows': '100 righe precedenti',
'private files': 'private files',
'Private files': 'Private files',
'Query:': 'Richiesta (query):',
'record': 'record', 'record': 'record',
'record does not exist': 'il record non esiste', 'record does not exist': 'il record non esiste',
'record id': 'ID del record', 'record id': 'ID del record',
'register': 'registrazione', 'register': 'registrazione',
'Remove compiled': 'rimozione codice compilato',
'request': 'request',
'Resolve Conflict file': 'File di risoluzione conflitto',
'response': 'response',
'restore': 'ripristino', 'restore': 'ripristino',
'revert': 'versione precedente', 'revert': 'versione precedente',
'Rows in table': 'Righe nella tabella',
'Rows selected': 'Righe selezionate',
'rules are not defined': 'rules are not defined',
"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')",
'Running on %s': 'Running on %s',
'Save': 'Save',
'Save via Ajax': 'Save via Ajax',
'Saved file hash:': 'Hash del file salvato:',
'selected': 'selezionato', 'selected': 'selezionato',
'session': 'session',
'session expired': 'sessions scaduta', 'session expired': 'sessions scaduta',
'shell': 'shell', 'shell': 'shell',
'Site': 'sito',
'some files could not be removed': 'non è stato possibile rimuovere alcuni files', 'some files could not be removed': 'non è stato possibile rimuovere alcuni files',
'Start wizard': 'start wizard',
'state': 'stato', 'state': 'stato',
'static': 'statico', 'static': 'statico',
'Static files': 'Files statici',
'Stylesheet': 'Foglio di stile (stylesheet)',
'submit': 'invia', 'submit': 'invia',
'Submit': 'Submit',
'Sure you want to delete this object?': 'Vuoi veramente cancellare questo oggetto?',
'table': 'tabella', 'table': 'tabella',
'test': 'test', 'test': 'test',
'Testing application': 'Test applicazione in corsg',
'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La richiesta (query) è una condizione come ad esempio "db.tabella1.campo1==\'valore\'". Una condizione come "db.tabella1.campo1==db.tabella2.campo2" produce un "JOIN" SQL.',
'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller',
'the application logic, each URL path is mapped in one exposed function in the controller': 'logica dell\'applicazione, ogni percorso "URL" corrisponde ad una funzione esposta da un controller', 'the application logic, each URL path is mapped in one exposed function in the controller': 'logica dell\'applicazione, ogni percorso "URL" corrisponde ad una funzione esposta da un controller',
'The data representation, define database tables and sets': 'The data representation, define database tables and sets',
'the data representation, define database tables and sets': 'rappresentazione dei dati, definizione di tabelle di database e di "set" ', 'the data representation, define database tables and sets': 'rappresentazione dei dati, definizione di tabelle di database e di "set" ',
'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates',
'the presentations layer, views are also known as templates': 'Presentazione dell\'applicazione, viste (views, chiamate anche "templates")', 'the presentations layer, views are also known as templates': 'Presentazione dell\'applicazione, viste (views, chiamate anche "templates")',
'There are no controllers': 'Non ci sono controller',
'There are no models': 'Non ci sono modelli',
'There are no modules': 'Non ci sono moduli',
'There are no plugins': 'There are no plugins',
'There are no static files': 'Non ci sono file statici',
'There are no translators, only default language is supported': 'Non ci sono traduzioni, viene solo supportato il linguaggio di base',
'There are no views': 'Non ci sono viste ("view")',
'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app',
'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui', 'these files are served without processing, your images go here': 'questi files vengono serviti così come sono, le immagini vanno qui',
'These files are served without processing, your images go here': 'These files are served without processing, your images go here',
'This is the %(filename)s template': 'Questo è il template %(filename)s',
'Ticket': 'Ticket',
'Ticket ID': 'Ticket ID',
'TM': 'TM',
'to previous version.': 'torna a versione precedente', 'to previous version.': 'torna a versione precedente',
'To create a plugin, name a file/folder plugin_[name]': 'Per creare un plugin, chiamare un file o cartella plugin_[nome]',
'toggle breakpoint': 'toggle breakpoint',
'Toggle Fullscreen': 'Toggle Fullscreen',
'Traceback': 'Traceback',
'translation strings for the application': "stringhe di traduzioni per l'applicazione", 'translation strings for the application': "stringhe di traduzioni per l'applicazione",
'Translation strings for the application': 'Translation strings for the application',
'try': 'prova', 'try': 'prova',
'try something like': 'prova qualcosa come', 'try something like': 'prova qualcosa come',
'try view': 'try view',
'Unable to check for upgrades': 'Impossibile controllare presenza di aggiornamenti',
'unable to create application "%s"': 'impossibile creare applicazione "%s"', 'unable to create application "%s"': 'impossibile creare applicazione "%s"',
'unable to delete file "%(filename)s"': 'impossibile rimuovere file "%(plugin)s"', 'unable to delete file "%(filename)s"': 'impossibile rimuovere file "%(plugin)s"',
'unable to delete file plugin "%(plugin)s"': 'impossibile rimuovere file di plugin "%(plugin)s"', 'unable to delete file plugin "%(plugin)s"': 'impossibile rimuovere file di plugin "%(plugin)s"',
'Unable to download app because:': 'Impossibile scaricare applicazione perché',
'Unable to download because': 'Impossibile scaricare perché',
'Unable to download because:': 'Unable to download because:',
'unable to parse csv file': 'non riesco a decodificare questo file CSV', 'unable to parse csv file': 'non riesco a decodificare questo file CSV',
'unable to uninstall "%s"': 'impossibile disinstallare "%s"', 'unable to uninstall "%s"': 'impossibile disinstallare "%s"',
'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"', 'unable to upgrade because "%s"': 'impossibile aggiornare perché "%s"',
'uncheck all': 'smarca tutti', 'uncheck all': 'smarca tutti',
'Uninstall': 'disinstalla',
'update': 'aggiorna', 'update': 'aggiorna',
'update all languages': 'aggiorna tutti i linguaggi', 'update all languages': 'aggiorna tutti i linguaggi',
'Update:': 'Aggiorna:',
'upgrade web2py now': 'upgrade web2py now', 'upgrade web2py now': 'upgrade web2py now',
'upload': 'upload',
'Upload & install packed application': 'Carica ed installa pacchetto con applicazione',
'Upload a package:': 'Upload a package:',
'Upload and install packed application': 'Upload and install packed application',
'upload application:': 'carica applicazione:', 'upload application:': 'carica applicazione:',
'upload file:': 'carica file:', 'upload file:': 'carica file:',
'upload plugin file:': 'carica file di plugin:', 'upload plugin file:': 'carica file di plugin:',
'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Per costruire richieste (query) più complesse si usano (...)&(...) come "e" (AND), (...)|(...) come "o" (OR), e ~(...) come negazione (NOT).',
'Use an url:': 'Use an url:',
'variables': 'variables', 'variables': 'variables',
'Version': 'Versione',
'Version %s.%s.%s (%s) %s': 'Version %s.%s.%s (%s) %s',
'versioning': 'sistema di versioni', 'versioning': 'sistema di versioni',
'Versioning': 'Versioning',
'view': 'vista', 'view': 'vista',
'View': 'Vista',
'Views': 'viste',
'views': 'viste', 'views': 'viste',
'web2py Recent Tweets': 'Tweets recenti per web2py', 'Web Framework': 'Web Framework',
'web2py is up to date': 'web2py è aggiornato', 'web2py is up to date': 'web2py è aggiornato',
'web2py Recent Tweets': 'Tweets recenti per web2py',
'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo', 'web2py upgraded; please restart it': 'web2py aggiornato; prego riavviarlo',
'Welcome %s': 'Benvenuto %s',
'Welcome to web2py': 'Benvenuto su web2py',
'YES': 'SI',
} }
+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
+40 -20
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]
@@ -123,17 +130,32 @@ if session.authorized:
else: else:
session.last_time = t0 session.last_time = t0
if request.vars.is_mobile in ('true', 'false', 'auto'):
session.is_mobile = request.vars.is_mobile or 'auto'
if request.controller == 'default' and request.function == 'index':
if not request.vars.is_mobile:
session.is_mobile = 'auto'
if not session.is_mobile:
session.is_mobile = 'auto'
if session.is_mobile == 'true':
is_mobile = True
elif session.is_mobile == 'false':
is_mobile = False
else:
is_mobile = request.user_agent().is_mobile
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 == 'default' and \ (request.controller + '/' + request.function in
request.function in ('index','user')): ('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
@@ -150,8 +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'))
+17 -13
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 request.user_agent().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 @@
File diff suppressed because one or more lines are too long
+9 -7
View File
@@ -10,7 +10,7 @@ h3 {font-size:2.00em}
h4 {font-size:1.50em} h4 {font-size:1.50em}
h5 {font-size:1.25em} h5 {font-size:1.25em}
h6 {font-size:1.12em} h6 {font-size:1.12em}
th,label {font-weight:bold; white-space:nowrap} th,label {font-weight:bold; white-space:nowrap;}
td,th {text-align:left; padding:2px 5px 2px 5px} td,th {text-align:left; padding:2px 5px 2px 5px}
th {vertical-align:middle; border-right:1px solid white} th {vertical-align:middle; border-right:1px solid white}
td {vertical-align:top} td {vertical-align:top}
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1} fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
/* fix ie problem with menu */ /* fix ie problem with menu */
.ie-lte7 .topbar .container {z-index:2}
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
@@ -187,10 +186,7 @@ div.error {
.web2py_paginator {} .web2py_paginator {}
.web2py_grid {width:100%} .web2py_grid {width:100%}
.web2py_grid table {width:100%} .web2py_grid table {width:100%}
.web2py_grid tbody td { .web2py_grid tbody td {padding:2px 5px 2px 5px; vertical-align: middle;}
padding:2px 5px 2px 5px;
vertical-align:middle;
}
.web2py_grid thead th,.web2py_grid tfoot td { .web2py_grid thead th,.web2py_grid tfoot td {
background-color:#EAEAEA; background-color:#EAEAEA;
@@ -299,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
.web2py_console input, .web2py_console select, .web2py_console input, .web2py_console select,
.web2py_console a { margin: 2px; } .web2py_console a { margin: 2px; }
.ie9 #w2p_query_panel {padding-bottom:2px}
#wiki_page_body { #wiki_page_body {
width: 600px; width: 600px;
height: auto; height: auto;
min-height: 400px; min-height: 400px;
} }
/* fix some IE problems */
.ie-lte7 .topbar .container {z-index:2}
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
.ie9 #w2p_query_panel {padding-bottom:2px}
File diff suppressed because one or more lines are too long
+49 -14
View File
@@ -39,7 +39,7 @@ function web2py_ajax_init(target) {
function web2py_event_handlers() { function web2py_event_handlers() {
var doc = jQuery(document) var doc = jQuery(document)
doc.on('click', '.flash', function(e){jQuery(this).fadeOut('slow'); e.preventDefault();}); doc.on('click', '.flash', function(e){var t=jQuery(this); if(t.css('top')=='0px') t.slideUp('slow'); else t.fadeOut(); e.preventDefault();});
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();}); doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
@@ -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.slideDown(); if(flash.html()) flash.append('<span style="float:right;">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
form.submit(function(e){ form.submit(function(e){
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('post',action,form.serialize(),target); web2py_ajax_page('post',action,form.serialize(),target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_trap_link(target) { function web2py_trap_link(target) {
jQuery('#'+target+' a.w2p_trap').each(function(i){ jQuery('#'+target+' a.w2p_trap').each(function(i){
var link=jQuery(this); var link=jQuery(this);
link.click(function(e) { link.click(function(e) {
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('get',link.attr('href'),[],target); web2py_ajax_page('get',link.attr('href'),[],target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_ajax_page(method, action, data, target) { function web2py_ajax_page(method, action, data, target) {
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
web2py_trap_link(target); web2py_trap_link(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)).slideDown();
} }
}); });
} }
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
} }
} else { } else {
// run once (no timeout specified) // run once (no timeout specified)
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py_ajax_page('get', action, null, target); web2py_ajax_page('get', action, null, target);
} }); } } }); }
function web2py_comet(url,onmessage,onopen,onclose) { function web2py_websocket(url,onmessage,onopen,onclose) {
if ("WebSocket" in window) { if ("WebSocket" in window) {
var ws = new WebSocket(url); var ws = new WebSocket(url);
ws.onopen = onopen?onopen:(function(){}); ws.onopen = onopen?onopen:(function(){});
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
} else return false; // not supported } else return false; // not supported
} }
function web2py_calc_entropy(mystring) {
//calculate a simple entropy for a given string
var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
'0123456789abcdefghijklmnopqrstuvwxyz');
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
for (var i=0;i<mystringlist.length;i++) { // classify this character
var c = mystringlist[i], inset=5;
for(var j = 0; j<csets.length; j++)
if (csets[j].indexOf(c) != -1) {inset = j; break;}
//calculate effect of character on alphabet size
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
else if (!(c in other)) {score += 1;other[c] = 1;}
if (inset != lastset) {score += 1;lastset = inset;}
}
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
return Math.round(entropy*100)/100
}
function web2py_validate_entropy(myfield, req_entropy) {
var validator = function () {
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
if(v<=0.5) {r=1.0; g=2.0*v;}
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
var color = '#'+rs(r)+rs(g)+rs(b);
myfield.css('background-color',color);
entropy_callback = myfield.data('entropy_callback');
if(entropy_callback) entropy_callback(v);
}
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
}
File diff suppressed because one or more lines are too long
@@ -1,173 +0,0 @@
/*! jQuery Mobile v1.0 jquerymobile.com | jquery.org/license */
(function(a,e){if(a.cleanData){var b=a.cleanData;a.cleanData=function(f){for(var c=0,h;(h=f[c])!=null;c++)a(h).triggerHandler("remove");b(f)}}else{var d=a.fn.remove;a.fn.remove=function(b,c){return this.each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add([this]).each(function(){a(this).triggerHandler("remove")});return d.call(a(this),b,c)})}}a.widget=function(b,c,h){var d=b.split(".")[0],e,b=b.split(".")[1];e=d+"-"+b;if(!h)h=c,c=a.Widget;a.expr[":"][e]=function(c){return!!a.data(c,
b)};a[d]=a[d]||{};a[d][b]=function(a,b){arguments.length&&this._createWidget(a,b)};c=new c;c.options=a.extend(true,{},c.options);a[d][b].prototype=a.extend(true,c,{namespace:d,widgetName:b,widgetEventPrefix:a[d][b].prototype.widgetEventPrefix||b,widgetBaseClass:e},h);a.widget.bridge(b,a[d][b])};a.widget.bridge=function(b,c){a.fn[b]=function(d){var g=typeof d==="string",i=Array.prototype.slice.call(arguments,1),k=this,d=!g&&i.length?a.extend.apply(null,[true,d].concat(i)):d;if(g&&d.charAt(0)==="_")return k;
g?this.each(function(){var c=a.data(this,b);if(!c)throw"cannot call methods on "+b+" prior to initialization; attempted to call method '"+d+"'";if(!a.isFunction(c[d]))throw"no such method '"+d+"' for "+b+" widget instance";var g=c[d].apply(c,i);if(g!==c&&g!==e)return k=g,false}):this.each(function(){var e=a.data(this,b);e?e.option(d||{})._init():a.data(this,b,new c(d,this))});return k}};a.Widget=function(a,b){arguments.length&&this._createWidget(a,b)};a.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",
options:{disabled:false},_createWidget:function(b,c){a.data(c,this.widgetName,this);this.element=a(c);this.options=a.extend(true,{},this.options,this._getCreateOptions(),b);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){var b={};a.metadata&&(b=a.metadata.get(element)[this.widgetName]);return b},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);
this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(b,c){var d=b;if(arguments.length===0)return a.extend({},this.options);if(typeof b==="string"){if(c===e)return this.options[b];d={};d[b]=c}this._setOptions(d);return this},_setOptions:function(b){var c=this;a.each(b,function(a,b){c._setOption(a,b)});return this},_setOption:function(a,b){this.options[a]=b;a==="disabled"&&
this.widget()[b?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",b);return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(b,c,d){var e=this.options[b],c=a.Event(c);c.type=(b===this.widgetEventPrefix?b:this.widgetEventPrefix+b).toLowerCase();d=d||{};if(c.originalEvent)for(var b=a.event.props.length,i;b;)i=a.event.props[--b],c[i]=c.originalEvent[i];this.element.trigger(c,
d);return!(a.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
(function(a,e){a.widget("mobile.widget",{_createWidget:function(){a.Widget.prototype._createWidget.apply(this,arguments);this._trigger("init")},_getCreateOptions:function(){var b=this.element,d={};a.each(this.options,function(a){var c=b.jqmData(a.replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()}));c!==e&&(d[a]=c)});return d},enhanceWithin:function(b){var d=a(b).closest(":jqmData(role='page')").data("page"),d=d&&d.keepNativeSelector()||"";a(this.options.initSelector,b).not(d)[this.widgetName]()}})})(jQuery);
(function(a){a(window);var e=a("html");a.mobile.media=function(){var b={},d=a("<div id='jquery-mediatest'>"),f=a("<body>").append(d);return function(a){if(!(a in b)){var h=document.createElement("style"),g="@media "+a+" { #jquery-mediatest { position:absolute; } }";h.type="text/css";h.styleSheet?h.styleSheet.cssText=g:h.appendChild(document.createTextNode(g));e.prepend(f).prepend(h);b[a]=d.css("position")==="absolute";f.add(h).remove()}return b[a]}}()})(jQuery);
(function(a,e){function b(a){var b=a.charAt(0).toUpperCase()+a.substr(1),a=(a+" "+c.join(b+" ")+b).split(" "),d;for(d in a)if(f[a[d]]!==e)return true}var d=a("<body>").prependTo("html"),f=d[0].style,c=["Webkit","Moz","O"],h="palmGetResource"in window,g=window.operamini&&{}.toString.call(window.operamini)==="[object OperaMini]",i=window.blackberry;a.mobile.browser={};a.mobile.browser.ie=function(){for(var a=3,b=document.createElement("div"),c=b.all||[];b.innerHTML="<\!--[if gt IE "+ ++a+"]><br><![endif]--\>",
c[0];);return a>4?a:!a}();a.extend(a.support,{orientation:"orientation"in window&&"onorientationchange"in window,touch:"ontouchend"in document,cssTransitions:"WebKitTransitionEvent"in window,pushState:"pushState"in history&&"replaceState"in history,mediaquery:a.mobile.media("only all"),cssPseudoElement:!!b("content"),touchOverflow:!!b("overflowScrolling"),boxShadow:!!b("boxShadow")&&!i,scrollTop:("pageXOffset"in window||"scrollTop"in document.documentElement||"scrollTop"in d[0])&&!h&&!g,dynamicBaseTag:function(){var b=
location.protocol+"//"+location.host+location.pathname+"ui-dir/",c=a("head base"),f=null,e="",h;c.length?e=c.attr("href"):c=f=a("<base>",{href:b}).appendTo("head");h=a("<a href='testurl' />").prependTo(d)[0].href;c[0].href=e||location.pathname;f&&f.remove();return h.indexOf(b)===0}()});d.remove();h=function(){var a=window.navigator.userAgent;return a.indexOf("Nokia")>-1&&(a.indexOf("Symbian/3")>-1||a.indexOf("Series60/5")>-1)&&a.indexOf("AppleWebKit")>-1&&a.match(/(BrowserNG|NokiaBrowser)\/7\.[0-3]/)}();
a.mobile.ajaxBlacklist=window.blackberry&&!window.WebKitPoint||g||h;h&&a(function(){a("head link[rel='stylesheet']").attr("rel","alternate stylesheet").attr("rel","stylesheet")});a.support.boxShadow||a("html").addClass("ui-mobile-nosupport-boxshadow")})(jQuery);
(function(a,e,b,d){function f(a){for(;a&&typeof a.originalEvent!=="undefined";)a=a.originalEvent;return a}function c(b){for(var c={},f,d;b;){f=a.data(b,n);for(d in f)if(f[d])c[d]=c.hasVirtualBinding=true;b=b.parentNode}return c}function h(){v&&(clearTimeout(v),v=0);v=setTimeout(function(){E=v=0;u.length=0;D=false;y=true},a.vmouse.resetTimerDuration)}function g(b,c,r){var e,h;if(!(h=r&&r[b])){if(r=!r)a:{for(r=c.target;r;){if((h=a.data(r,n))&&(!b||h[b]))break a;r=r.parentNode}r=null}h=r}if(h){e=c;var r=
e.type,j,g;e=a.Event(e);e.type=b;h=e.originalEvent;j=a.event.props;if(h)for(g=j.length;g;)b=j[--g],e[b]=h[b];if(r.search(/mouse(down|up)|click/)>-1&&!e.which)e.which=1;if(r.search(/^touch/)!==-1&&(b=f(h),r=b.touches,b=b.changedTouches,r=r&&r.length?r[0]:b&&b.length?b[0]:d))for(h=0,len=z.length;h<len;h++)b=z[h],e[b]=r[b];a(c.target).trigger(e)}return e}function i(b){var c=a.data(b.target,A);if(!D&&(!E||E!==c))if(c=g("v"+b.type,b))c.isDefaultPrevented()&&b.preventDefault(),c.isPropagationStopped()&&
b.stopPropagation(),c.isImmediatePropagationStopped()&&b.stopImmediatePropagation()}function k(b){var d=f(b).touches,e;if(d&&d.length===1&&(e=b.target,d=c(e),d.hasVirtualBinding))E=r++,a.data(e,A,E),v&&(clearTimeout(v),v=0),w=y=false,e=f(b).touches[0],x=e.pageX,t=e.pageY,g("vmouseover",b,d),g("vmousedown",b,d)}function l(a){y||(w||g("vmousecancel",a,c(a.target)),w=true,h())}function o(b){if(!y){var d=f(b).touches[0],r=w,e=a.vmouse.moveDistanceThreshold;w=w||Math.abs(d.pageX-x)>e||Math.abs(d.pageY-
t)>e;flags=c(b.target);w&&!r&&g("vmousecancel",b,flags);g("vmousemove",b,flags);h()}}function m(a){if(!y){y=true;var b=c(a.target),d;g("vmouseup",a,b);if(!w&&(d=g("vclick",a,b))&&d.isDefaultPrevented())d=f(a).changedTouches[0],u.push({touchID:E,x:d.clientX,y:d.clientY}),D=true;g("vmouseout",a,b);w=false;h()}}function p(b){var b=a.data(b,n),c;if(b)for(c in b)if(b[c])return true;return false}function j(){}function q(b){var c=b.substr(1);return{setup:function(){p(this)||a.data(this,n,{});a.data(this,
n)[b]=true;s[b]=(s[b]||0)+1;s[b]===1&&B.bind(c,i);a(this).bind(c,j);if(C)s.touchstart=(s.touchstart||0)+1,s.touchstart===1&&B.bind("touchstart",k).bind("touchend",m).bind("touchmove",o).bind("scroll",l)},teardown:function(){--s[b];s[b]||B.unbind(c,i);C&&(--s.touchstart,s.touchstart||B.unbind("touchstart",k).unbind("touchmove",o).unbind("touchend",m).unbind("scroll",l));var d=a(this),f=a.data(this,n);f&&(f[b]=false);d.unbind(c,j);p(this)||d.removeData(n)}}}var n="virtualMouseBindings",A="virtualTouchID",
e="vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel".split(" "),z="clientX clientY pageX pageY screenX screenY".split(" "),s={},v=0,x=0,t=0,w=false,u=[],D=false,y=false,C="addEventListener"in b,B=a(b),r=1,E=0;a.vmouse={moveDistanceThreshold:10,clickDistanceThreshold:10,resetTimerDuration:1500};for(var F=0;F<e.length;F++)a.event.special[e[F]]=q(e[F]);C&&b.addEventListener("click",function(b){var c=u.length,d=b.target,f,r,e,h,j;if(c){f=b.clientX;r=b.clientY;threshold=a.vmouse.clickDistanceThreshold;
for(e=d;e;){for(h=0;h<c;h++)if(j=u[h],e===d&&Math.abs(j.x-f)<threshold&&Math.abs(j.y-r)<threshold||a.data(e,A)===j.touchID){b.preventDefault();b.stopPropagation();return}e=e.parentNode}}},true)})(jQuery,window,document);
(function(a,e,b){function d(b,c,d){var f=d.type;d.type=c;a.event.handle.call(b,d);d.type=f}a.each("touchstart touchmove touchend orientationchange throttledresize tap taphold swipe swipeleft swiperight scrollstart scrollstop".split(" "),function(b,c){a.fn[c]=function(a){return a?this.bind(c,a):this.trigger(c)};a.attrFn[c]=true});var f=a.support.touch,c=f?"touchstart":"mousedown",h=f?"touchend":"mouseup",g=f?"touchmove":"mousemove";a.event.special.scrollstart={enabled:true,setup:function(){function b(a,
e){f=e;d(c,f?"scrollstart":"scrollstop",a)}var c=this,f,e;a(c).bind("touchmove scroll",function(c){a.event.special.scrollstart.enabled&&(f||b(c,true),clearTimeout(e),e=setTimeout(function(){b(c,false)},50))})}};a.event.special.tap={setup:function(){var b=this,c=a(b);c.bind("vmousedown",function(f){function e(){clearTimeout(q)}function h(){e();c.unbind("vclick",g).unbind("vmouseup",e).unbind("vmousecancel",h)}function g(a){h();j==a.target&&d(b,"tap",a)}if(f.which&&f.which!==1)return false;var j=f.target,
q;c.bind("vmousecancel",h).bind("vmouseup",e).bind("vclick",g);q=setTimeout(function(){d(b,"taphold",a.Event("taphold"))},750)})}};a.event.special.swipe={scrollSupressionThreshold:10,durationThreshold:1E3,horizontalDistanceThreshold:30,verticalDistanceThreshold:75,setup:function(){var d=a(this);d.bind(c,function(c){function f(b){if(m){var c=b.originalEvent.touches?b.originalEvent.touches[0]:b;p={time:(new Date).getTime(),coords:[c.pageX,c.pageY]};Math.abs(m.coords[0]-p.coords[0])>a.event.special.swipe.scrollSupressionThreshold&&
b.preventDefault()}}var e=c.originalEvent.touches?c.originalEvent.touches[0]:c,m={time:(new Date).getTime(),coords:[e.pageX,e.pageY],origin:a(c.target)},p;d.bind(g,f).one(h,function(){d.unbind(g,f);m&&p&&p.time-m.time<a.event.special.swipe.durationThreshold&&Math.abs(m.coords[0]-p.coords[0])>a.event.special.swipe.horizontalDistanceThreshold&&Math.abs(m.coords[1]-p.coords[1])<a.event.special.swipe.verticalDistanceThreshold&&m.origin.trigger("swipe").trigger(m.coords[0]>p.coords[0]?"swipeleft":"swiperight");
m=p=b})})}};(function(a,b){function c(){var a=f();a!==e&&(e=a,d.trigger("orientationchange"))}var d=a(b),f,e;a.event.special.orientationchange={setup:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;e=f();d.bind("throttledresize",c)},teardown:function(){if(a.support.orientation&&a.mobile.orientationChangeEnabled)return false;d.unbind("throttledresize",c)},add:function(a){var b=a.handler;a.handler=function(a){a.orientation=f();return b.apply(this,arguments)}}};a.event.special.orientationchange.orientation=
f=function(){var c=true,c=document.documentElement;return(c=a.support.orientation?b.orientation%180==0:c&&c.clientWidth/c.clientHeight<1.1)?"portrait":"landscape"}})(jQuery,e);(function(){a.event.special.throttledresize={setup:function(){a(this).bind("resize",b)},teardown:function(){a(this).unbind("resize",b)}};var b=function(){f=(new Date).getTime();e=f-c;e>=250?(c=f,a(this).trigger("throttledresize")):(d&&clearTimeout(d),d=setTimeout(b,250-e))},c=0,d,f,e})();a.each({scrollstop:"scrollstart",taphold:"tap",
swipeleft:"swipe",swiperight:"swipe"},function(b,c){a.event.special[b]={setup:function(){a(this).bind(c,a.noop)}}})})(jQuery,this);
(function(a,e,b){function d(a){a=a||location.href;return"#"+a.replace(/^[^#]*#?(.*)$/,"$1")}var f="hashchange",c=document,h,g=a.event.special,i=c.documentMode,k="on"+f in e&&(i===b||i>7);a.fn[f]=function(a){return a?this.bind(f,a):this.trigger(f)};a.fn[f].delay=50;g[f]=a.extend(g[f],{setup:function(){if(k)return false;a(h.start)},teardown:function(){if(k)return false;a(h.stop)}});h=function(){function h(){var b=d(),c=n(p);if(b!==p)q(p=b,c),a(e).trigger(f);else if(c!==p)location.href=location.href.replace(/#.*/,
"")+c;i=setTimeout(h,a.fn[f].delay)}var g={},i,p=d(),j=function(a){return a},q=j,n=j;g.start=function(){i||h()};g.stop=function(){i&&clearTimeout(i);i=b};a.browser.msie&&!k&&function(){var b,e;g.start=function(){if(!b)e=(e=a.fn[f].src)&&e+d(),b=a('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){e||q(d());h()}).attr("src",e||"javascript:0").insertAfter("body")[0].contentWindow,c.onpropertychange=function(){try{if(event.propertyName==="title")b.document.title=c.title}catch(a){}}};
g.stop=j;n=function(){return d(b.location.href)};q=function(d,e){var h=b.document,g=a.fn[f].domain;if(d!==e)h.title=c.title,h.open(),g&&h.write('<script>document.domain="'+g+'"<\/script>'),h.close(),b.location.hash=d}}();return g}()})(jQuery,this);
(function(a){a.widget("mobile.page",a.mobile.widget,{options:{theme:"c",domCache:false,keepNativeDefault:":jqmData(role='none'), :jqmData(role='nojs')"},_create:function(){this._trigger("beforecreate");this.element.attr("tabindex","0").addClass("ui-page ui-body-"+this.options.theme)},keepNativeSelector:function(){var e=this.options;return e.keepNative&&a.trim(e.keepNative)&&e.keepNative!==e.keepNativeDefault?[e.keepNative,e.keepNativeDefault].join(", "):e.keepNativeDefault}})})(jQuery);
(function(a,e){var b={};a.extend(a.mobile,{ns:"",subPageUrlKey:"ui-page",activePageClass:"ui-page-active",activeBtnClass:"ui-btn-active",ajaxEnabled:true,hashListeningEnabled:true,linkBindingEnabled:true,defaultPageTransition:"slide",minScrollBack:250,defaultDialogTransition:"pop",loadingMessage:"loading",pageLoadErrorMessage:"Error Loading Page",autoInitializePage:true,pushStateEnabled:true,orientationChangeEnabled:true,gradeA:function(){return a.support.mediaquery||a.mobile.browser.ie&&a.mobile.browser.ie>=
7},keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91},silentScroll:function(b){if(a.type(b)!=="number")b=a.mobile.defaultHomeScroll;a.event.special.scrollstart.enabled=false;
setTimeout(function(){e.scrollTo(0,b);a(document).trigger("silentscroll",{x:0,y:b})},20);setTimeout(function(){a.event.special.scrollstart.enabled=true},150)},nsNormalizeDict:b,nsNormalize:function(c){return!c?void 0:b[c]||(b[c]=a.camelCase(a.mobile.ns+c))},getInheritedTheme:function(a,b){for(var d=a[0],f="",e=/ui-(bar|body)-([a-z])\b/,l,o;d;){l=d.className||"";if((o=e.exec(l))&&(f=o[2]))break;d=d.parentNode}return f||b||"a"}});a.fn.jqmData=function(b,d){var f;typeof b!="undefined"&&(f=this.data(b?
a.mobile.nsNormalize(b):b,d));return f};a.jqmData=function(b,d,f){var e;typeof d!="undefined"&&(e=a.data(b,d?a.mobile.nsNormalize(d):d,f));return e};a.fn.jqmRemoveData=function(b){return this.removeData(a.mobile.nsNormalize(b))};a.jqmRemoveData=function(b,d){return a.removeData(b,a.mobile.nsNormalize(d))};a.fn.removeWithDependents=function(){a.removeWithDependents(this)};a.removeWithDependents=function(b){b=a(b);(b.jqmData("dependents")||a()).remove();b.remove()};a.fn.addDependents=function(b){a.addDependents(a(this),
b)};a.addDependents=function(b,d){var f=a(b).jqmData("dependents")||a();a(b).jqmData("dependents",a.merge(f,d))};a.fn.getEncodedText=function(){return a("<div/>").text(a(this).text()).html()};var d=a.find,f=/:jqmData\(([^)]*)\)/g;a.find=function(b,e,g,i){b=b.replace(f,"[data-"+(a.mobile.ns||"")+"$1]");return d.call(this,b,e,g,i)};a.extend(a.find,d);a.find.matches=function(b,d){return a.find(b,null,null,d)};a.find.matchesSelector=function(b,d){return a.find(d,null,null,[b]).length>0}})(jQuery,this);
(function(a,e){function b(a){var b=a.find(".ui-title:eq(0)");b.length?b.focus():a.focus()}function d(b){q&&(!q.closest(".ui-page-active").length||b)&&q.removeClass(a.mobile.activeBtnClass);q=null}function f(){z=false;A.length>0&&a.mobile.changePage.apply(null,A.pop())}function c(c,d,f,e){var g=a.mobile.urlHistory.getActive(),j=a.support.touchOverflow&&a.mobile.touchOverflowEnabled,i=g.lastScroll||(j?0:a.mobile.defaultHomeScroll),g=h();window.scrollTo(0,a.mobile.defaultHomeScroll);d&&d.data("page")._trigger("beforehide",
null,{nextPage:c});j||c.height(g+i);c.data("page")._trigger("beforeshow",null,{prevPage:d||a("")});a.mobile.hidePageLoadingMsg();j&&i&&(c.addClass("ui-mobile-pre-transition"),b(c),c.is(".ui-native-fixed")?c.find(".ui-content").scrollTop(i):c.scrollTop(i));f=(a.mobile.transitionHandlers[f||"none"]||a.mobile.defaultTransitionHandler)(f,e,c,d);f.done(function(){j||(c.height(""),b(c));j||a.mobile.silentScroll(i);d&&(j||d.height(""),d.data("page")._trigger("hide",null,{nextPage:c}));c.data("page")._trigger("show",
null,{prevPage:d||a("")})});return f}function h(){var b=a.event.special.orientationchange.orientation()==="portrait",c=b?screen.availHeight:screen.availWidth,b=Math.max(b?480:320,a(window).height());return Math.min(c,b)}function g(){(!a.support.touchOverflow||!a.mobile.touchOverflowEnabled)&&a("."+a.mobile.activePageClass).css("min-height",h())}function i(b,c){c&&b.attr("data-"+a.mobile.ns+"role",c);b.page()}function k(a){for(;a;){if(typeof a.nodeName==="string"&&a.nodeName.toLowerCase()=="a")break;
a=a.parentNode}return a}function l(b){var b=a(b).closest(".ui-page").jqmData("url"),c=t.hrefNoHash;if(!b||!j.isPath(b))b=c;return j.makeUrlAbsolute(b,c)}var o=a(window),m=a("html"),p=a("head"),j={urlParseRE:/^(((([^:\/#\?]+:)?(?:(\/\/)((?:(([^:@\/#\?]+)(?:\:([^:@\/#\?]+))?)@)?(([^:\/#\?\]\[]+|\[[^\/\]@#?]+\])(?:\:([0-9]+))?))?)?)?((\/?(?:[^\/\?#]+\/+)*)([^\?#]*)))?(\?[^#]+)?)(#.*)?/,parseUrl:function(b){if(a.type(b)==="object")return b;b=j.urlParseRE.exec(b||"")||[];return{href:b[0]||"",hrefNoHash:b[1]||
"",hrefNoSearch:b[2]||"",domain:b[3]||"",protocol:b[4]||"",doubleSlash:b[5]||"",authority:b[6]||"",username:b[8]||"",password:b[9]||"",host:b[10]||"",hostname:b[11]||"",port:b[12]||"",pathname:b[13]||"",directory:b[14]||"",filename:b[15]||"",search:b[16]||"",hash:b[17]||""}},makePathAbsolute:function(a,b){if(a&&a.charAt(0)==="/")return a;for(var a=a||"",c=(b=b?b.replace(/^\/|(\/[^\/]*|[^\/]+)$/g,""):"")?b.split("/"):[],d=a.split("/"),f=0;f<d.length;f++){var e=d[f];switch(e){case ".":break;case "..":c.length&&
c.pop();break;default:c.push(e)}}return"/"+c.join("/")},isSameDomain:function(a,b){return j.parseUrl(a).domain===j.parseUrl(b).domain},isRelativeUrl:function(a){return j.parseUrl(a).protocol===""},isAbsoluteUrl:function(a){return j.parseUrl(a).protocol!==""},makeUrlAbsolute:function(a,b){if(!j.isRelativeUrl(a))return a;var c=j.parseUrl(a),d=j.parseUrl(b),f=c.protocol||d.protocol,e=c.protocol?c.doubleSlash:c.doubleSlash||d.doubleSlash,h=c.authority||d.authority,g=c.pathname!=="",i=j.makePathAbsolute(c.pathname||
d.filename,d.pathname);return f+e+h+i+(c.search||!g&&d.search||"")+c.hash},addSearchParams:function(b,c){var d=j.parseUrl(b),f=typeof c==="object"?a.param(c):c,e=d.search||"?";return d.hrefNoSearch+e+(e.charAt(e.length-1)!=="?"?"&":"")+f+(d.hash||"")},convertUrlToDataUrl:function(a){var b=j.parseUrl(a);if(j.isEmbeddedPage(b))return b.hash.split(s)[0].replace(/^#/,"");else if(j.isSameDomain(b,t))return b.hrefNoHash.replace(t.domain,"");return a},get:function(a){if(a===e)a=location.hash;return j.stripHash(a).replace(/[^\/]*\.[^\/*]+$/,
"")},getFilePath:function(b){var c="&"+a.mobile.subPageUrlKey;return b&&b.split(c)[0].split(s)[0]},set:function(a){location.hash=a},isPath:function(a){return/\//.test(a)},clean:function(a){return a.replace(t.domain,"")},stripHash:function(a){return a.replace(/^#/,"")},cleanHash:function(a){return j.stripHash(a.replace(/\?.*$/,"").replace(s,""))},isExternal:function(a){a=j.parseUrl(a);return a.protocol&&a.domain!==x.domain?true:false},hasProtocol:function(a){return/^(:?\w+:)/.test(a)},isFirstPageUrl:function(b){var b=
j.parseUrl(j.makeUrlAbsolute(b,t)),c=a.mobile.firstPage,c=c&&c[0]?c[0].id:e;return(b.hrefNoHash===x.hrefNoHash||w&&b.hrefNoHash===t.hrefNoHash)&&(!b.hash||b.hash==="#"||c&&b.hash.replace(/^#/,"")===c)},isEmbeddedPage:function(a){a=j.parseUrl(a);return a.protocol!==""?a.hash&&(a.hrefNoHash===x.hrefNoHash||w&&a.hrefNoHash===t.hrefNoHash):/^#/.test(a.href)}},q=null,n={stack:[],activeIndex:0,getActive:function(){return n.stack[n.activeIndex]},getPrev:function(){return n.stack[n.activeIndex-1]},getNext:function(){return n.stack[n.activeIndex+
1]},addNew:function(a,b,c,d,f){n.getNext()&&n.clearForward();n.stack.push({url:a,transition:b,title:c,pageUrl:d,role:f});n.activeIndex=n.stack.length-1},clearForward:function(){n.stack=n.stack.slice(0,n.activeIndex+1)},directHashChange:function(b){var c,d,f;this.getActive();a.each(n.stack,function(a,e){b.currentUrl===e.url&&(c=a<n.activeIndex,d=!c,f=a)});this.activeIndex=f!==e?f:this.activeIndex;c?(b.either||b.isBack)(true):d&&(b.either||b.isForward)(false)},ignoreNextHashChange:false},A=[],z=false,
s="&ui-state=dialog",v=p.children("base"),x=j.parseUrl(location.href),t=v.length?j.parseUrl(j.makeUrlAbsolute(v.attr("href"),x.href)):x,w=x.hrefNoHash!==t.hrefNoHash,u=a.support.dynamicBaseTag?{element:v.length?v:a("<base>",{href:t.hrefNoHash}).prependTo(p),set:function(a){u.element.attr("href",j.makeUrlAbsolute(a,t))},reset:function(){u.element.attr("href",t.hrefNoHash)}}:e,D=true,y,C,B;y=function(){var b=o;a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&(b=a(".ui-page-active"),b=b.is(".ui-native-fixed")?
b.find(".ui-content"):b);return b};C=function(b){if(D){var c=a.mobile.urlHistory.getActive();if(c)b=b&&b.scrollTop(),c.lastScroll=b<a.mobile.minScrollBack?a.mobile.defaultHomeScroll:b}};B=function(){setTimeout(C,100,a(this))};o.bind(a.support.pushState?"popstate":"hashchange",function(){D=false});o.one(a.support.pushState?"popstate":"hashchange",function(){D=true});o.one("pagecontainercreate",function(){a.mobile.pageContainer.bind("pagechange",function(){var a=y();D=true;a.unbind("scrollstop",B);
a.bind("scrollstop",B)})});y().bind("scrollstop",B);a.mobile.getScreenHeight=h;a.fn.animationComplete=function(b){return a.support.cssTransitions?a(this).one("webkitAnimationEnd",b):(setTimeout(b,0),a(this))};a.mobile.path=j;a.mobile.base=u;a.mobile.urlHistory=n;a.mobile.dialogHashKey=s;a.mobile.noneTransitionHandler=function(b,c,d,f){f&&f.removeClass(a.mobile.activePageClass);d.addClass(a.mobile.activePageClass);return a.Deferred().resolve(b,c,d,f).promise()};a.mobile.defaultTransitionHandler=a.mobile.noneTransitionHandler;
a.mobile.transitionHandlers={none:a.mobile.defaultTransitionHandler};a.mobile.allowCrossDomainPages=false;a.mobile.getDocumentUrl=function(b){return b?a.extend({},x):x.href};a.mobile.getDocumentBase=function(b){return b?a.extend({},t):t.href};a.mobile._bindPageRemove=function(){var b=a(this);!b.data("page").options.domCache&&b.is(":jqmData(external-page='true')")&&b.bind("pagehide.remove",function(){var b=a(this),c=new a.Event("pageremove");b.trigger(c);c.isDefaultPrevented()||b.removeWithDependents()})};
a.mobile.loadPage=function(b,c){var d=a.Deferred(),f=a.extend({},a.mobile.loadPage.defaults,c),h=null,g=null,m=j.makeUrlAbsolute(b,a.mobile.activePage&&l(a.mobile.activePage)||t.hrefNoHash);if(f.data&&f.type==="get")m=j.addSearchParams(m,f.data),f.data=e;if(f.data&&f.type==="post")f.reloadPage=true;var s=j.getFilePath(m),p=j.convertUrlToDataUrl(m);f.pageContainer=f.pageContainer||a.mobile.pageContainer;h=f.pageContainer.children(":jqmData(url='"+p+"')");h.length===0&&p&&!j.isPath(p)&&(h=f.pageContainer.children("#"+
p).attr("data-"+a.mobile.ns+"url",p));if(h.length===0)if(a.mobile.firstPage&&j.isFirstPageUrl(s))a.mobile.firstPage.parent().length&&(h=a(a.mobile.firstPage));else if(j.isEmbeddedPage(s))return d.reject(m,c),d.promise();u&&u.reset();if(h.length){if(!f.reloadPage)return i(h,f.role),d.resolve(m,c,h),d.promise();g=h}var n=f.pageContainer,k=new a.Event("pagebeforeload"),q={url:b,absUrl:m,dataUrl:p,deferred:d,options:f};n.trigger(k,q);if(k.isDefaultPrevented())return d.promise();if(f.showLoadMsg)var v=
setTimeout(function(){a.mobile.showPageLoadingMsg()},f.loadMsgDelay);!a.mobile.allowCrossDomainPages&&!j.isSameDomain(x,m)?d.reject(m,c):a.ajax({url:s,type:f.type,data:f.data,dataType:"html",success:function(e,n,k){var o=a("<div></div>"),l=e.match(/<title[^>]*>([^<]*)/)&&RegExp.$1,t=RegExp("\\bdata-"+a.mobile.ns+"url=[\"']?([^\"'>]*)[\"']?");RegExp("(<[^>]+\\bdata-"+a.mobile.ns+"role=[\"']?page[\"']?[^>]*>)").test(e)&&RegExp.$1&&t.test(RegExp.$1)&&RegExp.$1&&(b=s=j.getFilePath(RegExp.$1));u&&u.set(s);
o.get(0).innerHTML=e;h=o.find(":jqmData(role='page'), :jqmData(role='dialog')").first();h.length||(h=a("<div data-"+a.mobile.ns+"role='page'>"+e.split(/<\/?body[^>]*>/gmi)[1]+"</div>"));l&&!h.jqmData("title")&&(~l.indexOf("&")&&(l=a("<div>"+l+"</div>").text()),h.jqmData("title",l));if(!a.support.dynamicBaseTag){var x=j.get(s);h.find("[src], link[href], a[rel='external'], :jqmData(ajax='false'), a[target]").each(function(){var b=a(this).is("[href]")?"href":a(this).is("[src]")?"src":"action",c=a(this).attr(b),
c=c.replace(location.protocol+"//"+location.host+location.pathname,"");/^(\w+:|#|\/)/.test(c)||a(this).attr(b,x+c)})}h.attr("data-"+a.mobile.ns+"url",j.convertUrlToDataUrl(s)).attr("data-"+a.mobile.ns+"external-page",true).appendTo(f.pageContainer);h.one("pagecreate",a.mobile._bindPageRemove);i(h,f.role);m.indexOf("&"+a.mobile.subPageUrlKey)>-1&&(h=f.pageContainer.children(":jqmData(url='"+p+"')"));f.showLoadMsg&&(clearTimeout(v),a.mobile.hidePageLoadingMsg());q.xhr=k;q.textStatus=n;q.page=h;f.pageContainer.trigger("pageload",
q);d.resolve(m,c,h,g)},error:function(b,e,h){u&&u.set(j.get());q.xhr=b;q.textStatus=e;q.errorThrown=h;b=new a.Event("pageloadfailed");f.pageContainer.trigger(b,q);b.isDefaultPrevented()||(f.showLoadMsg&&(clearTimeout(v),a.mobile.hidePageLoadingMsg(),a("<div class='ui-loader ui-overlay-shadow ui-body-e ui-corner-all'><h1>"+a.mobile.pageLoadErrorMessage+"</h1></div>").css({display:"block",opacity:0.96,top:o.scrollTop()+100}).appendTo(f.pageContainer).delay(800).fadeOut(400,function(){a(this).remove()})),
d.reject(m,c))}});return d.promise()};a.mobile.loadPage.defaults={type:"get",data:e,reloadPage:false,role:e,showLoadMsg:false,pageContainer:e,loadMsgDelay:50};a.mobile.changePage=function(b,h){if(z)A.unshift(arguments);else{var g=a.extend({},a.mobile.changePage.defaults,h);g.pageContainer=g.pageContainer||a.mobile.pageContainer;g.fromPage=g.fromPage||a.mobile.activePage;var p=g.pageContainer,k=new a.Event("pagebeforechange"),q={toPage:b,options:g};p.trigger(k,q);if(!k.isDefaultPrevented())if(b=q.toPage,
z=true,typeof b=="string")a.mobile.loadPage(b,g).done(function(b,c,d,f){z=false;c.duplicateCachedPage=f;a.mobile.changePage(d,c)}).fail(function(){z=false;d(true);f();g.pageContainer.trigger("pagechangefailed",q)});else{if(b[0]===a.mobile.firstPage[0]&&!g.dataUrl)g.dataUrl=x.hrefNoHash;var k=g.fromPage,l=g.dataUrl&&j.convertUrlToDataUrl(g.dataUrl)||b.jqmData("url"),v=l;j.getFilePath(l);var o=n.getActive(),t=n.activeIndex===0,w=0,u=document.title,y=g.role==="dialog"||b.jqmData("role")==="dialog";if(k&&
k[0]===b[0]&&!g.allowSamePageTransition)z=false,p.trigger("pagechange",q);else{i(b,g.role);g.fromHashChange&&n.directHashChange({currentUrl:l,isBack:function(){w=-1},isForward:function(){w=1}});try{document.activeElement&&document.activeElement.nodeName.toLowerCase()!="body"?a(document.activeElement).blur():a("input:focus, textarea:focus, select:focus").blur()}catch(B){}y&&o&&(l=(o.url||"")+s);if(g.changeHash!==false&&l)n.ignoreNextHashChange=true,j.set(l);var C=!o?u:b.jqmData("title")||b.children(":jqmData(role='header')").find(".ui-title").getEncodedText();
C&&u==document.title&&(u=C);b.jqmData("title")||b.jqmData("title",u);g.transition=g.transition||(w&&!t?o.transition:e)||(y?a.mobile.defaultDialogTransition:a.mobile.defaultPageTransition);w||n.addNew(l,g.transition,u,v,g.role);document.title=n.getActive().title;a.mobile.activePage=b;g.reverse=g.reverse||w<0;c(b,k,g.transition,g.reverse).done(function(){d();g.duplicateCachedPage&&g.duplicateCachedPage.remove();m.removeClass("ui-mobile-rendering");f();p.trigger("pagechange",q)})}}}};a.mobile.changePage.defaults=
{transition:e,reverse:false,changeHash:true,fromHashChange:false,role:e,duplicateCachedPage:e,pageContainer:e,showLoadMsg:true,dataUrl:e,fromPage:e,allowSamePageTransition:false};a.mobile._registerInternalEvents=function(){a("form").live("submit",function(b){var c=a(this);if(a.mobile.ajaxEnabled&&!c.is(":jqmData(ajax='false')")){var d=c.attr("method"),f=c.attr("target"),e=c.attr("action");if(!e&&(e=l(c),e===t.hrefNoHash))e=x.hrefNoSearch;e=j.makeUrlAbsolute(e,l(c));!j.isExternal(e)&&!f&&(a.mobile.changePage(e,
{type:d&&d.length&&d.toLowerCase()||"get",data:c.serialize(),transition:c.jqmData("transition"),direction:c.jqmData("direction"),reloadPage:true}),b.preventDefault())}});a(document).bind("vclick",function(b){if(!(b.which>1)&&a.mobile.linkBindingEnabled&&(b=k(b.target))&&j.parseUrl(b.getAttribute("href")||"#").hash!=="#")d(true),q=a(b).closest(".ui-btn").not(".ui-disabled"),q.addClass(a.mobile.activeBtnClass),a("."+a.mobile.activePageClass+" .ui-btn").not(b).blur()});a(document).bind("click",function(b){if(a.mobile.linkBindingEnabled){var c=
k(b.target);if(c&&!(b.which>1)){var f=a(c),h=function(){window.setTimeout(function(){d(true)},200)};if(f.is(":jqmData(rel='back')"))return window.history.back(),false;var g=l(f),c=j.makeUrlAbsolute(f.attr("href")||"#",g);if(!a.mobile.ajaxEnabled&&!j.isEmbeddedPage(c))h();else{if(c.search("#")!=-1)if(c=c.replace(/[^#]*#/,""))c=j.isPath(c)?j.makeUrlAbsolute(c,g):j.makeUrlAbsolute("#"+c,x.hrefNoHash);else{b.preventDefault();return}var g=f.is("[rel='external']")||f.is(":jqmData(ajax='false')")||f.is("[target]"),
i=a.mobile.allowCrossDomainPages&&x.protocol==="file:"&&c.search(/^https?:/)!=-1;g||j.isExternal(c)&&!i?h():(h=f.jqmData("transition"),g=(g=f.jqmData("direction"))&&g==="reverse"||f.jqmData("back"),f=f.attr("data-"+a.mobile.ns+"rel")||e,a.mobile.changePage(c,{transition:h,reverse:g,role:f}),b.preventDefault())}}}});a(".ui-page").live("pageshow.prefetch",function(){var b=[];a(this).find("a:jqmData(prefetch)").each(function(){var c=a(this),f=c.attr("href");f&&a.inArray(f,b)===-1&&(b.push(f),a.mobile.loadPage(f,
{role:c.attr("data-"+a.mobile.ns+"rel")}))})});a.mobile._handleHashChange=function(b){var c=j.stripHash(b),f={transition:a.mobile.urlHistory.stack.length===0?"none":e,changeHash:false,fromHashChange:true};if(!a.mobile.hashListeningEnabled||n.ignoreNextHashChange)n.ignoreNextHashChange=false;else{if(n.stack.length>1&&c.indexOf(s)>-1)if(a.mobile.activePage.is(".ui-dialog"))n.directHashChange({currentUrl:c,either:function(b){var d=a.mobile.urlHistory.getActive();c=d.pageUrl;a.extend(f,{role:d.role,transition:d.transition,
reverse:b})}});else{n.directHashChange({currentUrl:c,isBack:function(){window.history.back()},isForward:function(){window.history.forward()}});return}c?(c=typeof c==="string"&&!j.isPath(c)?j.makeUrlAbsolute("#"+c,t):c,a.mobile.changePage(c,f)):a.mobile.changePage(a.mobile.firstPage,f)}};o.bind("hashchange",function(){a.mobile._handleHashChange(location.hash)});a(document).bind("pageshow",g);a(window).bind("throttledresize",g)}})(jQuery);
(function(a,e){var b={},d=a(e),f=a.mobile.path.parseUrl(location.href);a.extend(b,{initialFilePath:f.pathname+f.search,initialHref:f.hrefNoHash,hashchangeFired:false,state:function(){return{hash:location.hash||"#"+b.initialFilePath,title:document.title,initialHref:b.initialHref}},resetUIKeys:function(b){var f="&"+a.mobile.subPageUrlKey,d=b.indexOf(a.mobile.dialogHashKey);d>-1?b=b.slice(0,d)+"#"+b.slice(d):b.indexOf(f)>-1&&(b=b.split(f).join("#"+f));return b},nextHashChangePrevented:function(c){a.mobile.urlHistory.ignoreNextHashChange=
c;b.onHashChangeDisabled=c},onHashChange:function(){if(!b.onHashChangeDisabled){var c,f;c=location.hash;var d=a.mobile.path.isPath(c),e=d?location.href:a.mobile.getDocumentUrl();c=d?c.replace("#",""):c;f=b.state();c=a.mobile.path.makeUrlAbsolute(c,e);d&&(c=b.resetUIKeys(c));history.replaceState(f,document.title,c)}},onPopState:function(c){var f=c.originalEvent.state;f&&(b.nextHashChangePrevented(true),setTimeout(function(){b.nextHashChangePrevented(false);a.mobile._handleHashChange(f.hash)},100))},
init:function(){d.bind("hashchange",b.onHashChange);d.bind("popstate",b.onPopState);location.hash===""&&history.replaceState(b.state(),document.title,location.href)}});a(function(){a.mobile.pushStateEnabled&&a.support.pushState&&b.init()})})(jQuery,this);
(function(a){function e(b,d,f,c){var e=new a.Deferred,g=d?" reverse":"",i="ui-mobile-viewport-transitioning viewport-"+b;f.animationComplete(function(){f.add(c).removeClass("out in reverse "+b);c&&c[0]!==f[0]&&c.removeClass(a.mobile.activePageClass);f.parent().removeClass(i);e.resolve(b,d,f,c)});f.parent().addClass(i);c&&c.addClass(b+" out"+g);f.addClass(a.mobile.activePageClass+" "+b+" in"+g);return e.promise()}a.mobile.css3TransitionHandler=e;if(a.mobile.defaultTransitionHandler===a.mobile.noneTransitionHandler)a.mobile.defaultTransitionHandler=
e})(jQuery,this);
(function(a){a.mobile.page.prototype.options.degradeInputs={color:false,date:false,datetime:false,"datetime-local":false,email:false,month:false,number:false,range:"number",search:"text",tel:false,time:false,url:false,week:false};a(document).bind("pagecreate create",function(e){var b=a(e.target).closest(':jqmData(role="page")').data("page"),d;if(b)d=b.options,a(e.target).find("input").not(b.keepNativeSelector()).each(function(){var b=a(this),c=this.getAttribute("type"),e=d.degradeInputs[c]||"text";
if(d.degradeInputs[c]){var g=a("<div>").html(b.clone()).html(),i=g.indexOf(" type=")>-1;b.replaceWith(g.replace(i?/\s+type=["']?\w+['"]?/:/\/?>/,' type="'+e+'" data-'+a.mobile.ns+'type="'+c+'"'+(i?"":">")))}})})})(jQuery);
(function(a,e){a.widget("mobile.dialog",a.mobile.widget,{options:{closeBtnText:"Close",overlayTheme:"a",initSelector:":jqmData(role='dialog')"},_create:function(){var b=this,d=this.element,f=a("<a href='#' data-"+a.mobile.ns+"icon='delete' data-"+a.mobile.ns+"iconpos='notext'>"+this.options.closeBtnText+"</a>");d.addClass("ui-overlay-"+this.options.overlayTheme);d.attr("role","dialog").addClass("ui-dialog").find(":jqmData(role='header')").addClass("ui-corner-top ui-overlay-shadow").prepend(f).end().find(":jqmData(role='content'),:jqmData(role='footer')").addClass("ui-overlay-shadow").last().addClass("ui-corner-bottom");
f.bind("vclick",function(){b.close()});d.bind("vclick submit",function(b){var b=a(b.target).closest(b.type==="vclick"?"a":"form"),f;b.length&&!b.jqmData("transition")&&(f=a.mobile.urlHistory.getActive()||{},b.attr("data-"+a.mobile.ns+"transition",f.transition||a.mobile.defaultDialogTransition).attr("data-"+a.mobile.ns+"direction","reverse"))}).bind("pagehide",function(){a(this).find("."+a.mobile.activeBtnClass).removeClass(a.mobile.activeBtnClass)})},close:function(){e.history.back()}});a(a.mobile.dialog.prototype.options.initSelector).live("pagecreate",
function(){a(this).dialog()})})(jQuery,this);
(function(a){a.mobile.page.prototype.options.backBtnText="Back";a.mobile.page.prototype.options.addBackBtn=false;a.mobile.page.prototype.options.backBtnTheme=null;a.mobile.page.prototype.options.headerTheme="a";a.mobile.page.prototype.options.footerTheme="a";a.mobile.page.prototype.options.contentTheme=null;a(":jqmData(role='page'), :jqmData(role='dialog')").live("pagecreate",function(){var e=a(this),b=e.data("page").options,d=e.jqmData("role"),f=b.theme;a(":jqmData(role='header'), :jqmData(role='footer'), :jqmData(role='content')",
this).each(function(){var c=a(this),e=c.jqmData("role"),g=c.jqmData("theme"),i=g||b.contentTheme||d==="dialog"&&f,k;c.addClass("ui-"+e);if(e==="header"||e==="footer"){var l=g||(e==="header"?b.headerTheme:b.footerTheme)||f;c.addClass("ui-bar-"+l).attr("role",e==="header"?"banner":"contentinfo");g=c.children("a");i=g.hasClass("ui-btn-left");k=g.hasClass("ui-btn-right");i=i||g.eq(0).not(".ui-btn-right").addClass("ui-btn-left").length;k||g.eq(1).addClass("ui-btn-right");b.addBackBtn&&e==="header"&&a(".ui-page").length>
1&&c.jqmData("url")!==a.mobile.path.stripHash(location.hash)&&!i&&a("<a href='#' class='ui-btn-left' data-"+a.mobile.ns+"rel='back' data-"+a.mobile.ns+"icon='arrow-l'>"+b.backBtnText+"</a>").attr("data-"+a.mobile.ns+"theme",b.backBtnTheme||l).prependTo(c);c.children("h1, h2, h3, h4, h5, h6").addClass("ui-title").attr({tabindex:"0",role:"heading","aria-level":"1"})}else e==="content"&&(i&&c.addClass("ui-body-"+i),c.attr("role","main"))})})})(jQuery);
(function(a){a.widget("mobile.collapsible",a.mobile.widget,{options:{expandCueText:" click to expand contents",collapseCueText:" click to collapse contents",collapsed:true,heading:"h1,h2,h3,h4,h5,h6,legend",theme:null,contentTheme:null,iconTheme:"d",initSelector:":jqmData(role='collapsible')"},_create:function(){var e=this.element,b=this.options,d=e.addClass("ui-collapsible"),f=e.children(b.heading).first(),c=d.wrapInner("<div class='ui-collapsible-content'></div>").find(".ui-collapsible-content"),
h=e.closest(":jqmData(role='collapsible-set')").addClass("ui-collapsible-set"),e=h.children(":jqmData(role='collapsible')");f.is("legend")&&(f=a("<div role='heading'>"+f.html()+"</div>").insertBefore(f),f.next().remove());if(h.length){if(!b.theme)b.theme=h.jqmData("theme");if(!b.contentTheme)b.contentTheme=h.jqmData("content-theme")}c.addClass(b.contentTheme?"ui-body-"+b.contentTheme:"");f.insertBefore(c).addClass("ui-collapsible-heading").append("<span class='ui-collapsible-heading-status'></span>").wrapInner("<a href='#' class='ui-collapsible-heading-toggle'></a>").find("a").first().buttonMarkup({shadow:false,
corners:false,iconPos:"left",icon:"plus",theme:b.theme});h.length?(h.jqmData("collapsiblebound")||h.jqmData("collapsiblebound",true).bind("expand",function(b){a(b.target).closest(".ui-collapsible").siblings(".ui-collapsible").trigger("collapse")}),e.first().find("a").first().addClass("ui-corner-top").find(".ui-btn-inner").addClass("ui-corner-top"),e.last().jqmData("collapsible-last",true).find("a").first().addClass("ui-corner-bottom").find(".ui-btn-inner").addClass("ui-corner-bottom"),d.jqmData("collapsible-last")&&
f.find("a").first().add(f.find(".ui-btn-inner")).addClass("ui-corner-bottom")):f.find("a").first().add(f.find(".ui-btn-inner")).addClass("ui-corner-top ui-corner-bottom");d.bind("expand collapse",function(e){if(!e.isDefaultPrevented()){e.preventDefault();var i=a(this),e=e.type==="collapse",k=b.contentTheme;f.toggleClass("ui-collapsible-heading-collapsed",e).find(".ui-collapsible-heading-status").text(e?b.expandCueText:b.collapseCueText).end().find(".ui-icon").toggleClass("ui-icon-minus",!e).toggleClass("ui-icon-plus",
e);i.toggleClass("ui-collapsible-collapsed",e);c.toggleClass("ui-collapsible-content-collapsed",e).attr("aria-hidden",e);if(k&&(!h.length||d.jqmData("collapsible-last")))f.find("a").first().add(f.find(".ui-btn-inner")).toggleClass("ui-corner-bottom",e),c.toggleClass("ui-corner-bottom",!e);c.trigger("updatelayout")}}).trigger(b.collapsed?"collapse":"expand");f.bind("click",function(a){var b=f.is(".ui-collapsible-heading-collapsed")?"expand":"collapse";d.trigger(b);a.preventDefault()})}});a(document).bind("pagecreate create",
function(e){a(a.mobile.collapsible.prototype.options.initSelector,e.target).collapsible()})})(jQuery);(function(a){a.fn.fieldcontain=function(){return this.addClass("ui-field-contain ui-body ui-br")};a(document).bind("pagecreate create",function(e){a(":jqmData(role='fieldcontain')",e.target).fieldcontain()})})(jQuery);
(function(a){a.fn.grid=function(e){return this.each(function(){var b=a(this),d=a.extend({grid:null},e),f=b.children(),c={solo:1,a:2,b:3,c:4,d:5},d=d.grid;if(!d)if(f.length<=5)for(var h in c)c[h]===f.length&&(d=h);else d="a";c=c[d];b.addClass("ui-grid-"+d);f.filter(":nth-child("+c+"n+1)").addClass("ui-block-a");c>1&&f.filter(":nth-child("+c+"n+2)").addClass("ui-block-b");c>2&&f.filter(":nth-child(3n+3)").addClass("ui-block-c");c>3&&f.filter(":nth-child(4n+4)").addClass("ui-block-d");c>4&&f.filter(":nth-child(5n+5)").addClass("ui-block-e")})}})(jQuery);
(function(a,e){a.widget("mobile.navbar",a.mobile.widget,{options:{iconpos:"top",grid:null,initSelector:":jqmData(role='navbar')"},_create:function(){var b=this.element,d=b.find("a"),f=d.filter(":jqmData(icon)").length?this.options.iconpos:e;b.addClass("ui-navbar").attr("role","navigation").find("ul").grid({grid:this.options.grid});f||b.addClass("ui-navbar-noicons");d.buttonMarkup({corners:false,shadow:false,iconpos:f});b.delegate("a","vclick",function(){d.not(".ui-state-persist").removeClass(a.mobile.activeBtnClass);
a(this).addClass(a.mobile.activeBtnClass)})}});a(document).bind("pagecreate create",function(b){a(a.mobile.navbar.prototype.options.initSelector,b.target).navbar()})})(jQuery);
(function(a){var e={};a.widget("mobile.listview",a.mobile.widget,{options:{theme:null,countTheme:"c",headerTheme:"b",dividerTheme:"b",splitIcon:"arrow-r",splitTheme:"b",inset:false,initSelector:":jqmData(role='listview')"},_create:function(){var a=this;a.element.addClass(function(d,f){return f+" ui-listview "+(a.options.inset?" ui-listview-inset ui-corner-all ui-shadow ":"")});a.refresh(true)},_removeCorners:function(a,d){a=a.add(a.find(".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb"));d==="top"?a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl"):
d==="bottom"?a.removeClass("ui-corner-bottom ui-corner-br ui-corner-bl"):a.removeClass("ui-corner-top ui-corner-tr ui-corner-tl ui-corner-bottom ui-corner-br ui-corner-bl")},_refreshCorners:function(a){var d,f;this.options.inset&&(d=this.element.children("li"),f=a?d.not(".ui-screen-hidden"):d.filter(":visible"),this._removeCorners(d),d=f.first().addClass("ui-corner-top"),d.add(d.find(".ui-btn-inner").not(".ui-li-link-alt span:first-child")).addClass("ui-corner-top").end().find(".ui-li-link-alt, .ui-li-link-alt span:first-child").addClass("ui-corner-tr").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-tl"),
f=f.last().addClass("ui-corner-bottom"),f.add(f.find(".ui-btn-inner")).find(".ui-li-link-alt").addClass("ui-corner-br").end().find(".ui-li-thumb").not(".ui-li-icon").addClass("ui-corner-bl"));a||this.element.trigger("updatelayout")},_findFirstElementByTagName:function(a,d,f,c){var e={};for(e[f]=e[c]=true;a;){if(e[a.nodeName])return a;a=a[d]}return null},_getChildrenByTagName:function(b,d,f){var c=[],e={};e[d]=e[f]=true;for(b=b.firstChild;b;)e[b.nodeName]&&c.push(b),b=b.nextSibling;return a(c)},_addThumbClasses:function(b){var d,
f,c=b.length;for(d=0;d<c;d++)f=a(this._findFirstElementByTagName(b[d].firstChild,"nextSibling","img","IMG")),f.length&&(f.addClass("ui-li-thumb"),a(this._findFirstElementByTagName(f[0].parentNode,"parentNode","li","LI")).addClass(f.is(".ui-li-icon")?"ui-li-has-icon":"ui-li-has-thumb"))},refresh:function(b){this.parentPage=this.element.closest(".ui-page");this._createSubPages();var d=this.options,f=this.element,c=f.jqmData("dividertheme")||d.dividerTheme,e=f.jqmData("splittheme"),g=f.jqmData("spliticon"),
i=this._getChildrenByTagName(f[0],"li","LI"),k=a.support.cssPseudoElement||!a.nodeName(f[0],"ol")?0:1,l={},o,m,p,j,q;k&&f.find(".ui-li-dec").remove();if(!d.theme)d.theme=a.mobile.getInheritedTheme(this.element,"c");for(var n=0,A=i.length;n<A;n++){o=i.eq(n);m="ui-li";if(b||!o.hasClass("ui-li"))p=o.jqmData("theme")||d.theme,j=this._getChildrenByTagName(o[0],"a","A"),j.length?(q=o.jqmData("icon"),o.buttonMarkup({wrapperEls:"div",shadow:false,corners:false,iconpos:"right",icon:j.length>1||q===false?false:
q||"arrow-r",theme:p}),q!=false&&j.length==1&&o.addClass("ui-li-has-arrow"),j.first().addClass("ui-link-inherit"),j.length>1&&(m+=" ui-li-has-alt",j=j.last(),q=e||j.jqmData("theme")||d.splitTheme,j.appendTo(o).attr("title",j.getEncodedText()).addClass("ui-li-link-alt").empty().buttonMarkup({shadow:false,corners:false,theme:p,icon:false,iconpos:false}).find(".ui-btn-inner").append(a(document.createElement("span")).buttonMarkup({shadow:true,corners:true,theme:q,iconpos:"notext",icon:g||j.jqmData("icon")||
d.splitIcon})))):o.jqmData("role")==="list-divider"?(m+=" ui-li-divider ui-btn ui-bar-"+c,o.attr("role","heading"),k&&(k=1)):m+=" ui-li-static ui-body-"+p;k&&m.indexOf("ui-li-divider")<0&&(p=o.is(".ui-li-static:first")?o:o.find(".ui-link-inherit"),p.addClass("ui-li-jsnumbering").prepend("<span class='ui-li-dec'>"+k++ +". </span>"));l[m]||(l[m]=[]);l[m].push(o[0])}for(m in l)a(l[m]).addClass(m).children(".ui-btn-inner").addClass(m);f.find("h1, h2, h3, h4, h5, h6").addClass("ui-li-heading").end().find("p, dl").addClass("ui-li-desc").end().find(".ui-li-aside").each(function(){var b=
a(this);b.prependTo(b.parent())}).end().find(".ui-li-count").each(function(){a(this).closest("li").addClass("ui-li-has-count")}).addClass("ui-btn-up-"+(f.jqmData("counttheme")||this.options.countTheme)+" ui-btn-corner-all");this._addThumbClasses(i);this._addThumbClasses(f.find(".ui-link-inherit"));this._refreshCorners(b)},_idStringEscape:function(a){return a.replace(/[^a-zA-Z0-9]/g,"-")},_createSubPages:function(){var b=this.element,d=b.closest(".ui-page"),f=d.jqmData("url"),c=f||d[0][a.expando],
h=b.attr("id"),g=this.options,i="data-"+a.mobile.ns,k=this,l=d.find(":jqmData(role='footer')").jqmData("id"),o;typeof e[c]==="undefined"&&(e[c]=-1);h=h||++e[c];a(b.find("li>ul, li>ol").toArray().reverse()).each(function(c){var d=a(this),e=d.attr("id")||h+"-"+c,c=d.parent(),k=a(d.prevAll().toArray().reverse()),k=k.length?k:a("<span>"+a.trim(c.contents()[0].nodeValue)+"</span>"),n=k.first().getEncodedText(),e=(f||"")+"&"+a.mobile.subPageUrlKey+"="+e,A=d.jqmData("theme")||g.theme,z=d.jqmData("counttheme")||
b.jqmData("counttheme")||g.countTheme;o=true;d.detach().wrap("<div "+i+"role='page' "+i+"url='"+e+"' "+i+"theme='"+A+"' "+i+"count-theme='"+z+"'><div "+i+"role='content'></div></div>").parent().before("<div "+i+"role='header' "+i+"theme='"+g.headerTheme+"'><div class='ui-title'>"+n+"</div></div>").after(l?a("<div "+i+"role='footer' "+i+"id='"+l+"'>"):"").parent().appendTo(a.mobile.pageContainer).page();d=c.find("a:first");d.length||(d=a("<a/>").html(k||n).prependTo(c.empty()));d.attr("href","#"+e)}).listview();
o&&d.is(":jqmData(external-page='true')")&&d.data("page").options.domCache===false&&d.unbind("pagehide.remove").bind("pagehide.remove",function(b,c){var e=c.nextPage;c.nextPage&&(e=e.jqmData("url"),e.indexOf(f+"&"+a.mobile.subPageUrlKey)!==0&&(k.childPages().remove(),d.remove()))})},childPages:function(){var b=this.parentPage.jqmData("url");return a(":jqmData(url^='"+b+"&"+a.mobile.subPageUrlKey+"')")}});a(document).bind("pagecreate create",function(b){a(a.mobile.listview.prototype.options.initSelector,
b.target).listview()})})(jQuery);
(function(a){a.mobile.listview.prototype.options.filter=false;a.mobile.listview.prototype.options.filterPlaceholder="Filter items...";a.mobile.listview.prototype.options.filterTheme="c";a.mobile.listview.prototype.options.filterCallback=function(a,b){return a.toLowerCase().indexOf(b)===-1};a(":jqmData(role='listview')").live("listviewcreate",function(){var e=a(this),b=e.data("listview");if(b.options.filter){var d=a("<form>",{"class":"ui-listview-filter ui-bar-"+b.options.filterTheme,role:"search"});
a("<input>",{placeholder:b.options.filterPlaceholder}).attr("data-"+a.mobile.ns+"type","search").jqmData("lastval","").bind("keyup change",function(){var d=a(this),c=this.value.toLowerCase(),h=null,h=d.jqmData("lastval")+"",g=false,i="";d.jqmData("lastval",c);i=c.substr(0,h.length-1).replace(h,"");h=c.length<h.length||i.length!=c.length-h.length?e.children():e.children(":not(.ui-screen-hidden)");if(c){for(var k=h.length-1;k>=0;k--)d=a(h[k]),i=d.jqmData("filtertext")||d.text(),d.is("li:jqmData(role=list-divider)")?
(d.toggleClass("ui-filter-hidequeue",!g),g=false):b.options.filterCallback(i,c)?d.toggleClass("ui-filter-hidequeue",true):g=true;h.filter(":not(.ui-filter-hidequeue)").toggleClass("ui-screen-hidden",false);h.filter(".ui-filter-hidequeue").toggleClass("ui-screen-hidden",true).toggleClass("ui-filter-hidequeue",false)}else h.toggleClass("ui-screen-hidden",false);b._refreshCorners()}).appendTo(d).textinput();a(this).jqmData("inset")&&d.addClass("ui-listview-filter-inset");d.bind("submit",function(){return false}).insertBefore(e)}})})(jQuery);
(function(a){a(document).bind("pagecreate create",function(e){a(":jqmData(role='nojs')",e.target).addClass("ui-nojs")})})(jQuery);
(function(a,e){a.widget("mobile.checkboxradio",a.mobile.widget,{options:{theme:null,initSelector:"input[type='checkbox'],input[type='radio']"},_create:function(){var b=this,d=this.element,f=d.closest("form,fieldset,:jqmData(role='page')").find("label[for='"+d[0].id+"']"),c=d.attr("type"),h=c+"-on",g=c+"-off",i=d.parents(":jqmData(type='horizontal')").length?e:g;if(!(c!=="checkbox"&&c!=="radio")){a.extend(this,{label:f,inputtype:c,checkedClass:"ui-"+h+(i?"":" "+a.mobile.activeBtnClass),uncheckedClass:"ui-"+
g,checkedicon:"ui-icon-"+h,uncheckedicon:"ui-icon-"+g});if(!this.options.theme)this.options.theme=this.element.jqmData("theme");f.buttonMarkup({theme:this.options.theme,icon:i,shadow:false});d.add(f).wrapAll("<div class='ui-"+c+"'></div>");f.bind({vmouseover:function(b){a(this).parent().is(".ui-disabled")&&b.stopPropagation()},vclick:function(a){if(d.is(":disabled"))a.preventDefault();else return b._cacheVals(),d.prop("checked",c==="radio"&&true||!d.prop("checked")),d.triggerHandler("click"),b._getInputSet().not(d).prop("checked",
false),b._updateAll(),false}});d.bind({vmousedown:function(){b._cacheVals()},vclick:function(){var c=a(this);c.is(":checked")?(c.prop("checked",true),b._getInputSet().not(c).prop("checked",false)):c.prop("checked",false);b._updateAll()},focus:function(){f.addClass("ui-focus")},blur:function(){f.removeClass("ui-focus")}});this.refresh()}},_cacheVals:function(){this._getInputSet().each(function(){var b=a(this);b.jqmData("cacheVal",b.is(":checked"))})},_getInputSet:function(){return this.inputtype==
"checkbox"?this.element:this.element.closest("form,fieldset,:jqmData(role='page')").find("input[name='"+this.element.attr("name")+"'][type='"+this.inputtype+"']")},_updateAll:function(){var b=this;this._getInputSet().each(function(){var d=a(this);(d.is(":checked")||b.inputtype==="checkbox")&&d.trigger("change")}).checkboxradio("refresh")},refresh:function(){var b=this.element,d=this.label,f=d.find(".ui-icon");a(b[0]).prop("checked")?(d.addClass(this.checkedClass).removeClass(this.uncheckedClass),
f.addClass(this.checkedicon).removeClass(this.uncheckedicon)):(d.removeClass(this.checkedClass).addClass(this.uncheckedClass),f.removeClass(this.checkedicon).addClass(this.uncheckedicon));b.is(":disabled")?this.disable():this.enable()},disable:function(){this.element.prop("disabled",true).parent().addClass("ui-disabled")},enable:function(){this.element.prop("disabled",false).parent().removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(b){a.mobile.checkboxradio.prototype.enhanceWithin(b.target)})})(jQuery);
(function(a,e){a.widget("mobile.button",a.mobile.widget,{options:{theme:null,icon:null,iconpos:null,inline:null,corners:true,shadow:true,iconshadow:true,initSelector:"button, [type='button'], [type='submit'], [type='reset'], [type='image']"},_create:function(){var b=this.element,d=this.options,f,c;this.button=a("<div></div>").text(b.text()||b.val()).insertBefore(b).buttonMarkup({theme:d.theme,icon:d.icon,iconpos:d.iconpos,inline:d.inline,corners:d.corners,shadow:d.shadow,iconshadow:d.iconshadow}).append(b.addClass("ui-btn-hidden"));
d=b.attr("type");f=b.attr("name");d!=="button"&&d!=="reset"&&f&&b.bind("vclick",function(){c===e&&(c=a("<input>",{type:"hidden",name:b.attr("name"),value:b.attr("value")}).insertBefore(b),a(document).one("submit",function(){c.remove();c=e}))});this.refresh()},enable:function(){this.element.attr("disabled",false);this.button.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.button.addClass("ui-disabled").attr("aria-disabled",
true);return this._setOption("disabled",true)},refresh:function(){var a=this.element;a.prop("disabled")?this.disable():this.enable();this.button.data("textWrapper").text(a.text()||a.val())}});a(document).bind("pagecreate create",function(b){a.mobile.button.prototype.enhanceWithin(b.target)})})(jQuery);
(function(a,e){a.widget("mobile.slider",a.mobile.widget,{options:{theme:null,trackTheme:null,disabled:false,initSelector:"input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"},_create:function(){var b=this,d=this.element,f=a.mobile.getInheritedTheme(d,"c"),c=this.options.theme||f,h=this.options.trackTheme||f,g=d[0].nodeName.toLowerCase(),f=g=="select"?"ui-slider-switch":"",i=d.attr("id"),k=i+"-label",i=a("[for='"+i+"']").attr("id",k),l=function(){return g=="input"?parseFloat(d.val()):
d[0].selectedIndex},o=g=="input"?parseFloat(d.attr("min")):0,m=g=="input"?parseFloat(d.attr("max")):d.find("option").length-1,p=window.parseFloat(d.attr("step")||1),j=a("<div class='ui-slider "+f+" ui-btn-down-"+h+" ui-btn-corner-all' role='application'></div>"),q=a("<a href='#' class='ui-slider-handle'></a>").appendTo(j).buttonMarkup({corners:true,theme:c,shadow:true}).attr({role:"slider","aria-valuemin":o,"aria-valuemax":m,"aria-valuenow":l(),"aria-valuetext":l(),title:l(),"aria-labelledby":k});
a.extend(this,{slider:j,handle:q,dragging:false,beforeStart:null,userModified:false,mouseMoved:false});g=="select"&&(j.wrapInner("<div class='ui-slider-inneroffset'></div>"),q.addClass("ui-slider-handle-snapping"),d.find("option"),d.find("option").each(function(b){var c=!b?"b":"a",d=!b?"right":"left",b=!b?" ui-btn-down-"+h:" "+a.mobile.activeBtnClass;a("<div class='ui-slider-labelbg ui-slider-labelbg-"+c+b+" ui-btn-corner-"+d+"'></div>").prependTo(j);a("<span class='ui-slider-label ui-slider-label-"+
c+b+" ui-btn-corner-"+d+"' role='img'>"+a(this).getEncodedText()+"</span>").prependTo(q)}));i.addClass("ui-slider");d.addClass(g==="input"?"ui-slider-input":"ui-slider-switch").change(function(){b.mouseMoved||b.refresh(l(),true)}).keyup(function(){b.refresh(l(),true,true)}).blur(function(){b.refresh(l(),true)});a(document).bind("vmousemove",function(a){if(b.dragging)return b.mouseMoved=true,g==="select"&&q.removeClass("ui-slider-handle-snapping"),b.refresh(a),b.userModified=b.beforeStart!==d[0].selectedIndex,
false});j.bind("vmousedown",function(a){b.dragging=true;b.userModified=false;b.mouseMoved=false;if(g==="select")b.beforeStart=d[0].selectedIndex;b.refresh(a);return false});j.add(document).bind("vmouseup",function(){if(b.dragging)return b.dragging=false,g==="select"&&(q.addClass("ui-slider-handle-snapping"),b.mouseMoved?b.userModified?b.refresh(b.beforeStart==0?1:0):b.refresh(b.beforeStart):b.refresh(b.beforeStart==0?1:0)),b.mouseMoved=false});j.insertAfter(d);this.handle.bind("vmousedown",function(){a(this).focus()}).bind("vclick",
false);this.handle.bind("keydown",function(c){var d=l();if(!b.options.disabled){switch(c.keyCode){case a.mobile.keyCode.HOME:case a.mobile.keyCode.END:case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:if(c.preventDefault(),!b._keySliding)b._keySliding=true,a(this).addClass("ui-state-active")}switch(c.keyCode){case a.mobile.keyCode.HOME:b.refresh(o);break;case a.mobile.keyCode.END:b.refresh(m);
break;case a.mobile.keyCode.PAGE_UP:case a.mobile.keyCode.UP:case a.mobile.keyCode.RIGHT:b.refresh(d+p);break;case a.mobile.keyCode.PAGE_DOWN:case a.mobile.keyCode.DOWN:case a.mobile.keyCode.LEFT:b.refresh(d-p)}}}).keyup(function(){if(b._keySliding)b._keySliding=false,a(this).removeClass("ui-state-active")});this.refresh(e,e,true)},refresh:function(a,d,f){(this.options.disabled||this.element.attr("disabled"))&&this.disable();var c=this.element,e,g=c[0].nodeName.toLowerCase(),i=g==="input"?parseFloat(c.attr("min")):
0,k=g==="input"?parseFloat(c.attr("max")):c.find("option").length-1;if(typeof a==="object"){if(!this.dragging||a.pageX<this.slider.offset().left-8||a.pageX>this.slider.offset().left+this.slider.width()+8)return;e=Math.round((a.pageX-this.slider.offset().left)/this.slider.width()*100)}else a==null&&(a=g==="input"?parseFloat(c.val()):c[0].selectedIndex),e=(parseFloat(a)-i)/(k-i)*100;if(!isNaN(e)&&(e<0&&(e=0),e>100&&(e=100),a=Math.round(e/100*(k-i))+i,a<i&&(a=i),a>k&&(a=k),this.handle.css("left",e+"%"),
this.handle.attr({"aria-valuenow":g==="input"?a:c.find("option").eq(a).attr("value"),"aria-valuetext":g==="input"?a:c.find("option").eq(a).getEncodedText(),title:a}),g==="select"&&(a===0?this.slider.addClass("ui-slider-switch-a").removeClass("ui-slider-switch-b"):this.slider.addClass("ui-slider-switch-b").removeClass("ui-slider-switch-a")),!f))f=false,g==="input"?(f=c.val()!==a,c.val(a)):(f=c[0].selectedIndex!==a,c[0].selectedIndex=a),!d&&f&&c.trigger("change")},enable:function(){this.element.attr("disabled",
false);this.slider.removeClass("ui-disabled").attr("aria-disabled",false);return this._setOption("disabled",false)},disable:function(){this.element.attr("disabled",true);this.slider.addClass("ui-disabled").attr("aria-disabled",true);return this._setOption("disabled",true)}});a(document).bind("pagecreate create",function(b){a.mobile.slider.prototype.enhanceWithin(b.target)})})(jQuery);
(function(a){a.widget("mobile.textinput",a.mobile.widget,{options:{theme:null,initSelector:"input[type='text'], input[type='search'], :jqmData(type='search'), input[type='number'], :jqmData(type='number'), input[type='password'], input[type='email'], input[type='url'], input[type='tel'], textarea, input[type='time'], input[type='date'], input[type='month'], input[type='week'], input[type='datetime'], input[type='datetime-local'], input[type='color'], input:not([type])"},_create:function(){var e=this.element,
b=this.options.theme||a.mobile.getInheritedTheme(this.element,"c"),d=" ui-body-"+b,f,c;a("label[for='"+e.attr("id")+"']").addClass("ui-input-text");f=e.addClass("ui-input-text ui-body-"+b);typeof e[0].autocorrect!=="undefined"&&!a.support.touchOverflow&&(e[0].setAttribute("autocorrect","off"),e[0].setAttribute("autocomplete","off"));e.is("[type='search'],:jqmData(type='search')")?(f=e.wrap("<div class='ui-input-search ui-shadow-inset ui-btn-corner-all ui-btn-shadow ui-icon-searchfield"+d+"'></div>").parent(),
c=a("<a href='#' class='ui-input-clear' title='clear text'>clear text</a>").tap(function(a){e.val("").focus();e.trigger("change");c.addClass("ui-input-clear-hidden");a.preventDefault()}).appendTo(f).buttonMarkup({icon:"delete",iconpos:"notext",corners:true,shadow:true}),b=function(){setTimeout(function(){c.toggleClass("ui-input-clear-hidden",!e.val())},0)},b(),e.bind("paste cut keyup focus change blur",b)):e.addClass("ui-corner-all ui-shadow-inset"+d);e.focus(function(){f.addClass("ui-focus")}).blur(function(){f.removeClass("ui-focus")});
if(e.is("textarea")){var h=function(){var a=e[0].scrollHeight;e[0].clientHeight<a&&e.height(a+15)},g;e.keyup(function(){clearTimeout(g);g=setTimeout(h,100)});a.trim(e.val())&&(a(window).load(h),a(document).one("pagechange",h))}},disable:function(){(this.element.attr("disabled",true).is("[type='search'],:jqmData(type='search')")?this.element.parent():this.element).addClass("ui-disabled")},enable:function(){(this.element.attr("disabled",false).is("[type='search'],:jqmData(type='search')")?this.element.parent():
this.element).removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(e){a.mobile.textinput.prototype.enhanceWithin(e.target)})})(jQuery);
(function(a){var e=function(b){var d=b.selectID,f=b.label,c=b.select.closest(".ui-page"),e=a("<div>",{"class":"ui-selectmenu-screen ui-screen-hidden"}).appendTo(c),g=b._selectOptions(),i=b.isMultiple=b.select[0].multiple,k=d+"-button",l=d+"-menu",o=a("<div data-"+a.mobile.ns+"role='dialog' data-"+a.mobile.ns+"theme='"+b.options.theme+"' data-"+a.mobile.ns+"overlay-theme='"+b.options.overlayTheme+"'><div data-"+a.mobile.ns+"role='header'><div class='ui-title'>"+f.getEncodedText()+"</div></div><div data-"+
a.mobile.ns+"role='content'></div></div>").appendTo(a.mobile.pageContainer).page(),m=a("<div>",{"class":"ui-selectmenu ui-selectmenu-hidden ui-overlay-shadow ui-corner-all ui-body-"+b.options.overlayTheme+" "+a.mobile.defaultDialogTransition}).insertAfter(e),p=a("<ul>",{"class":"ui-selectmenu-list",id:l,role:"listbox","aria-labelledby":k}).attr("data-"+a.mobile.ns+"theme",b.options.theme).appendTo(m),j=a("<div>",{"class":"ui-header ui-bar-"+b.options.theme}).prependTo(m),q=a("<h1>",{"class":"ui-title"}).appendTo(j),
n=a("<a>",{text:b.options.closeText,href:"#","class":"ui-btn-left"}).attr("data-"+a.mobile.ns+"iconpos","notext").attr("data-"+a.mobile.ns+"icon","delete").appendTo(j).buttonMarkup(),A=o.find(".ui-content"),z=o.find(".ui-header a");a.extend(b,{select:b.select,selectID:d,buttonId:k,menuId:l,thisPage:c,menuPage:o,label:f,screen:e,selectOptions:g,isMultiple:i,theme:b.options.theme,listbox:m,list:p,header:j,headerTitle:q,headerClose:n,menuPageContent:A,menuPageClose:z,placeholder:"",build:function(){var b=
this;b.refresh();b.select.attr("tabindex","-1").focus(function(){a(this).blur();b.button.focus()});b.button.bind("vclick keydown",function(c){if(c.type=="vclick"||c.keyCode&&(c.keyCode===a.mobile.keyCode.ENTER||c.keyCode===a.mobile.keyCode.SPACE))b.open(),c.preventDefault()});b.list.attr("role","listbox").delegate(".ui-li>a","focusin",function(){a(this).attr("tabindex","0")}).delegate(".ui-li>a","focusout",function(){a(this).attr("tabindex","-1")}).delegate("li:not(.ui-disabled, .ui-li-divider)",
"click",function(c){var d=b.select[0].selectedIndex,f=b.list.find("li:not(.ui-li-divider)").index(this),e=b._selectOptions().eq(f)[0];e.selected=b.isMultiple?!e.selected:true;b.isMultiple&&a(this).find(".ui-icon").toggleClass("ui-icon-checkbox-on",e.selected).toggleClass("ui-icon-checkbox-off",!e.selected);(b.isMultiple||d!==f)&&b.select.trigger("change");b.isMultiple||b.close();c.preventDefault()}).keydown(function(b){var c=a(b.target),d=c.closest("li");switch(b.keyCode){case 38:return b=d.prev(),
b.length&&(c.blur().attr("tabindex","-1"),b.find("a").first().focus()),false;case 40:return b=d.next(),b.length&&(c.blur().attr("tabindex","-1"),b.find("a").first().focus()),false;case 13:case 32:return c.trigger("click"),false}});b.menuPage.bind("pagehide",function(){b.list.appendTo(b.listbox);b._focusButton();a.mobile._bindPageRemove.call(b.thisPage)});b.screen.bind("vclick",function(){b.close()});b.headerClose.click(function(){if(b.menuType=="overlay")return b.close(),false});b.thisPage.addDependents(this.menuPage)},
_isRebuildRequired:function(){var a=this.list.find("li");return this._selectOptions().text()!==a.text()},refresh:function(b){var c=this;this._selectOptions();this.selected();var d=this.selectedIndices();(b||this._isRebuildRequired())&&c._buildList();c.setButtonText();c.setButtonCount();c.list.find("li:not(.ui-li-divider)").removeClass(a.mobile.activeBtnClass).attr("aria-selected",false).each(function(b){a.inArray(b,d)>-1&&(b=a(this),b.attr("aria-selected",true),c.isMultiple?b.find(".ui-icon").removeClass("ui-icon-checkbox-off").addClass("ui-icon-checkbox-on"):
b.addClass(a.mobile.activeBtnClass))})},close:function(){if(!this.options.disabled&&this.isOpen)this.menuType=="page"?window.history.back():(this.screen.addClass("ui-screen-hidden"),this.listbox.addClass("ui-selectmenu-hidden").removeAttr("style").removeClass("in"),this.list.appendTo(this.listbox),this._focusButton()),this.isOpen=false},open:function(){if(!this.options.disabled){var b=this,c=b.list.parent().outerHeight(),d=b.list.parent().outerWidth(),f=a(".ui-page-active"),e=a.support.touchOverflow&&
a.mobile.touchOverflowEnabled,f=f.is(".ui-native-fixed")?f.find(".ui-content"):f;scrollTop=e?f.scrollTop():a(window).scrollTop();btnOffset=b.button.offset().top;screenHeight=window.innerHeight;screenWidth=window.innerWidth;b.button.addClass(a.mobile.activeBtnClass);setTimeout(function(){b.button.removeClass(a.mobile.activeBtnClass)},300);if(c>screenHeight-80||!a.support.scrollTop){b.thisPage.unbind("pagehide.remove");if(scrollTop==0&&btnOffset>screenHeight)b.thisPage.one("pagehide",function(){a(this).jqmData("lastScroll",
btnOffset)});b.menuPage.one("pageshow",function(){a(window).one("silentscroll",function(){b.list.find(a.mobile.activeBtnClass).focus()});b.isOpen=true});b.menuType="page";b.menuPageContent.append(b.list);b.menuPage.find("div .ui-title").text(b.label.text());a.mobile.changePage(b.menuPage,{transition:a.mobile.defaultDialogTransition})}else{b.menuType="overlay";b.screen.height(a(document).height()).removeClass("ui-screen-hidden");var f=btnOffset-scrollTop,h=scrollTop+screenHeight-btnOffset,g=c/2,e=
parseFloat(b.list.parent().css("max-width")),c=f>c/2&&h>c/2?btnOffset+b.button.outerHeight()/2-g:f>h?scrollTop+screenHeight-c-30:scrollTop+30;d<e?e=(screenWidth-d)/2:(e=b.button.offset().left+b.button.outerWidth()/2-d/2,e<30?e=30:e+d>screenWidth&&(e=screenWidth-d-30));b.listbox.append(b.list).removeClass("ui-selectmenu-hidden").css({top:c,left:e}).addClass("in");b.list.find(a.mobile.activeBtnClass).focus();b.isOpen=true}}},_buildList:function(){var b=this,c=this.options,d=this.placeholder,f=[],e=
[],h=b.isMultiple?"checkbox-off":"false";b.list.empty().filter(".ui-listview").listview("destroy");b.select.find("option").each(function(g){var j=a(this),i=j.parent(),m=j.getEncodedText(),p="<a href='#'>"+m+"</a>",k=[],n=[];i.is("optgroup")&&(i=i.attr("label"),a.inArray(i,f)===-1&&(e.push("<li data-"+a.mobile.ns+"role='list-divider'>"+i+"</li>"),f.push(i)));if(!this.getAttribute("value")||m.length==0||j.jqmData("placeholder"))c.hidePlaceholderMenuItems&&k.push("ui-selectmenu-placeholder"),d=b.placeholder=
m;this.disabled&&(k.push("ui-disabled"),n.push("aria-disabled='true'"));e.push("<li data-"+a.mobile.ns+"option-index='"+g+"' data-"+a.mobile.ns+"icon='"+h+"' class='"+k.join(" ")+"' "+n.join(" ")+">"+p+"</li>")});b.list.html(e.join(" "));b.list.find("li").attr({role:"option",tabindex:"-1"}).first().attr("tabindex","0");this.isMultiple||this.headerClose.hide();!this.isMultiple&&!d.length?this.header.hide():this.headerTitle.text(this.placeholder);b.list.listview()},_button:function(){return a("<a>",
{href:"#",role:"button",id:this.buttonId,"aria-haspopup":"true","aria-owns":this.menuId})}})};a("select").live("selectmenubeforecreate",function(){var b=a(this).data("selectmenu");b.options.nativeMenu||e(b)})})(jQuery);
(function(a){a.widget("mobile.selectmenu",a.mobile.widget,{options:{theme:null,disabled:false,icon:"arrow-d",iconpos:"right",inline:null,corners:true,shadow:true,iconshadow:true,menuPageTheme:"b",overlayTheme:"a",hidePlaceholderMenuItems:true,closeText:"Close",nativeMenu:true,initSelector:"select:not(:jqmData(role='slider'))"},_button:function(){return a("<div/>")},_setDisabled:function(a){this.element.attr("disabled",a);this.button.attr("aria-disabled",a);return this._setOption("disabled",a)},_focusButton:function(){var a=
this;setTimeout(function(){a.button.focus()},40)},_selectOptions:function(){return this.select.find("option")},_preExtension:function(){this.select=this.element.wrap("<div class='ui-select'>");this.selectID=this.select.attr("id");this.label=a("label[for='"+this.selectID+"']").addClass("ui-select");this.isMultiple=this.select[0].multiple;if(!this.options.theme)this.options.theme=a.mobile.getInheritedTheme(this.select,"c")},_create:function(){this._preExtension();this._trigger("beforeCreate");this.button=
this._button();var e=this,b=this.options,d=this.button.text(a(this.select[0].options.item(this.select[0].selectedIndex==-1?0:this.select[0].selectedIndex)).text()).insertBefore(this.select).buttonMarkup({theme:b.theme,icon:b.icon,iconpos:b.iconpos,inline:b.inline,corners:b.corners,shadow:b.shadow,iconshadow:b.iconshadow});b.nativeMenu&&window.opera&&window.opera.version&&this.select.addClass("ui-select-nativeonly");if(this.isMultiple)this.buttonCount=a("<span>").addClass("ui-li-count ui-btn-up-c ui-btn-corner-all").hide().appendTo(d.addClass("ui-li-has-count"));
(b.disabled||this.element.attr("disabled"))&&this.disable();this.select.change(function(){e.refresh()});this.build()},build:function(){var e=this;this.select.appendTo(e.button).bind("vmousedown",function(){e.button.addClass(a.mobile.activeBtnClass)}).bind("focus vmouseover",function(){e.button.trigger("vmouseover")}).bind("vmousemove",function(){e.button.removeClass(a.mobile.activeBtnClass)}).bind("change blur vmouseout",function(){e.button.trigger("vmouseout").removeClass(a.mobile.activeBtnClass)}).bind("change blur",
function(){e.button.removeClass("ui-btn-down-"+e.options.theme)})},selected:function(){return this._selectOptions().filter(":selected")},selectedIndices:function(){var a=this;return this.selected().map(function(){return a._selectOptions().index(this)}).get()},setButtonText:function(){var e=this,b=this.selected();this.button.find(".ui-btn-text").text(function(){return!e.isMultiple?b.text():b.length?b.map(function(){return a(this).text()}).get().join(", "):e.placeholder})},setButtonCount:function(){var a=
this.selected();this.isMultiple&&this.buttonCount[a.length>1?"show":"hide"]().text(a.length)},refresh:function(){this.setButtonText();this.setButtonCount()},open:a.noop,close:a.noop,disable:function(){this._setDisabled(true);this.button.addClass("ui-disabled")},enable:function(){this._setDisabled(false);this.button.removeClass("ui-disabled")}});a(document).bind("pagecreate create",function(e){a.mobile.selectmenu.prototype.enhanceWithin(e.target)})})(jQuery);
(function(a,e){function b(b){for(var c;b;){if((c=typeof b.className==="string"&&b.className.split(" "))&&a.inArray("ui-btn",c)>-1&&a.inArray("ui-disabled",c)<0)break;b=b.parentNode}return b}a.fn.buttonMarkup=function(b){for(var b=b||{},c=0;c<this.length;c++){var h=this.eq(c),g=h[0],i=a.extend({},a.fn.buttonMarkup.defaults,{icon:b.icon!==e?b.icon:h.jqmData("icon"),iconpos:b.iconpos!==e?b.iconpos:h.jqmData("iconpos"),theme:b.theme!==e?b.theme:h.jqmData("theme"),inline:b.inline!==e?b.inline:h.jqmData("inline"),
shadow:b.shadow!==e?b.shadow:h.jqmData("shadow"),corners:b.corners!==e?b.corners:h.jqmData("corners"),iconshadow:b.iconshadow!==e?b.iconshadow:h.jqmData("iconshadow")},b),k="ui-btn-inner",l,o,m=document.createElement(i.wrapperEls),p=document.createElement(i.wrapperEls),j=i.icon?document.createElement("span"):null;d&&d();if(!i.theme)i.theme=a.mobile.getInheritedTheme(h,"c");l="ui-btn ui-btn-up-"+i.theme;i.inline&&(l+=" ui-btn-inline");if(i.icon)i.icon="ui-icon-"+i.icon,i.iconpos=i.iconpos||"left",
o="ui-icon "+i.icon,i.iconshadow&&(o+=" ui-icon-shadow");i.iconpos&&(l+=" ui-btn-icon-"+i.iconpos,i.iconpos=="notext"&&!h.attr("title")&&h.attr("title",h.getEncodedText()));i.corners&&(l+=" ui-btn-corner-all",k+=" ui-btn-corner-all");i.shadow&&(l+=" ui-shadow");g.setAttribute("data-"+a.mobile.ns+"theme",i.theme);h.addClass(l);m.className=k;m.setAttribute("aria-hidden","true");p.className="ui-btn-text";m.appendChild(p);if(j)j.className=o,m.appendChild(j);for(;g.firstChild;)p.appendChild(g.firstChild);
g.appendChild(m);a.data(g,"textWrapper",a(p))}return this};a.fn.buttonMarkup.defaults={corners:true,shadow:true,iconshadow:true,inline:false,wrapperEls:"span"};var d=function(){a(document).bind({vmousedown:function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-up-"+c).addClass("ui-btn-down-"+c))},"vmousecancel vmouseup":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-down-"+c).addClass("ui-btn-up-"+
c))},"vmouseover focus":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-up-"+c).addClass("ui-btn-hover-"+c))},"vmouseout blur":function(d){var d=b(d.target),c;d&&(d=a(d),c=d.attr("data-"+a.mobile.ns+"theme"),d.removeClass("ui-btn-hover-"+c+" ui-btn-down-"+c).addClass("ui-btn-up-"+c))}});d=null};a(document).bind("pagecreate create",function(b){a(":jqmData(role='button'), .ui-bar > a, .ui-header > a, .ui-footer > a, .ui-bar > :jqmData(role='controlgroup') > a",
b.target).not(".ui-btn, :jqmData(role='none'), :jqmData(role='nojs')").buttonMarkup()})})(jQuery);
(function(a){a.fn.controlgroup=function(e){return this.each(function(){function b(a){a.removeClass("ui-btn-corner-all ui-shadow").eq(0).addClass(h[0]).end().last().addClass(h[1]).addClass("ui-controlgroup-last")}var d=a(this),f=a.extend({direction:d.jqmData("type")||"vertical",shadow:false,excludeInvisible:true},e),c=d.children("legend"),h=f.direction=="horizontal"?["ui-corner-left","ui-corner-right"]:["ui-corner-top","ui-corner-bottom"];d.find("input").first().attr("type");c.length&&(d.wrapInner("<div class='ui-controlgroup-controls'></div>"),
a("<div role='heading' class='ui-controlgroup-label'>"+c.html()+"</div>").insertBefore(d.children(0)),c.remove());d.addClass("ui-corner-all ui-controlgroup ui-controlgroup-"+f.direction);b(d.find(".ui-btn"+(f.excludeInvisible?":visible":"")));b(d.find(".ui-btn-inner"));f.shadow&&d.addClass("ui-shadow")})};a(document).bind("pagecreate create",function(e){a(":jqmData(role='controlgroup')",e.target).controlgroup({excludeInvisible:false})})})(jQuery);
(function(a){a(document).bind("pagecreate create",function(e){a(e.target).find("a").not(".ui-btn, .ui-link-inherit, :jqmData(role='none'), :jqmData(role='nojs')").addClass("ui-link")})})(jQuery);
(function(a,e){a.fn.fixHeaderFooter=function(){return!a.support.scrollTop||a.support.touchOverflow&&a.mobile.touchOverflowEnabled?this:this.each(function(){var b=a(this);b.jqmData("fullscreen")&&b.addClass("ui-page-fullscreen");b.find(".ui-header:jqmData(position='fixed')").addClass("ui-header-fixed ui-fixed-inline fade");b.find(".ui-footer:jqmData(position='fixed')").addClass("ui-footer-fixed ui-fixed-inline fade")})};a.mobile.fixedToolbars=function(){function b(){!i&&g==="overlay"&&(h||a.mobile.fixedToolbars.hide(true),
a.mobile.fixedToolbars.startShowTimer())}function d(a){var b=0,c,d;if(a){d=document.body;c=a.offsetParent;for(b=a.offsetTop;a&&a!=d;){b+=a.scrollTop||0;if(a==c)b+=c.offsetTop,c=a.offsetParent;a=a.parentNode}}return b}function f(b){var c=a(window).scrollTop(),e=d(b[0]),f=b.css("top")=="auto"?0:parseFloat(b.css("top")),h=window.innerHeight,g=b.outerHeight(),i=b.parents(".ui-page:not(.ui-page-fullscreen)").length;return b.is(".ui-header-fixed")?(f=c-e+f,f<e&&(f=0),b.css("top",i?f:c)):b.css("top",i?c+
h-g-(e-f):c+h-g)}if(a.support.scrollTop&&(!a.support.touchOverflow||!a.mobile.touchOverflowEnabled)){var c,h,g="inline",i=false,k=null,l=false,o=true;a(function(){var c=a(document),d=a(window);c.bind("vmousedown",function(){o&&(k=g)}).bind("vclick",function(b){o&&!a(b.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length&&!l&&(a.mobile.fixedToolbars.toggle(k),k=null)}).bind("silentscroll",b);(c.scrollTop()===0?d:c).bind("scrollstart",function(){l=true;k===
null&&(k=g);var b=k=="overlay";if(i=b||!!h)a.mobile.fixedToolbars.clearShowTimer(),b&&a.mobile.fixedToolbars.hide(true)}).bind("scrollstop",function(b){a(b.target).closest("a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed").length||(l=false,i&&(a.mobile.fixedToolbars.startShowTimer(),i=false),k=null)});d.bind("resize updatelayout",b)});a(".ui-page").live("pagebeforeshow",function(b,d){var e=a(b.target).find(":jqmData(role='footer')"),h=e.data("id"),g=d.prevPage,g=g&&g.find(":jqmData(role='footer')"),
g=g.length&&g.jqmData("id")===h;h&&g&&(c=e,f(c.removeClass("fade in out").appendTo(a.mobile.pageContainer)))}).live("pageshow",function(){var b=a(this);c&&c.length&&setTimeout(function(){f(c.appendTo(b).addClass("fade"));c=null},500);a.mobile.fixedToolbars.show(true,this)});a(".ui-collapsible-contain").live("collapse expand",b);return{show:function(b,c){a.mobile.fixedToolbars.clearShowTimer();g="overlay";return(c?a(c):a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var c=
a(this),e=a(window).scrollTop(),h=d(c[0]),g=window.innerHeight,i=c.outerHeight(),e=c.is(".ui-header-fixed")&&e<=h+i||c.is(".ui-footer-fixed")&&h<=e+g;c.addClass("ui-fixed-overlay").removeClass("ui-fixed-inline");!e&&!b&&c.animationComplete(function(){c.removeClass("in")}).addClass("in");f(c)})},hide:function(b){g="inline";return(a.mobile.activePage?a.mobile.activePage:a(".ui-page-active")).children(".ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last").each(function(){var c=a(this),
d=c.css("top"),d=d=="auto"?0:parseFloat(d);c.addClass("ui-fixed-inline").removeClass("ui-fixed-overlay");if(d<0||c.is(".ui-header-fixed")&&d!==0)b?c.css("top",0):c.css("top")!=="auto"&&parseFloat(c.css("top"))!==0&&c.animationComplete(function(){c.removeClass("out reverse").css("top",0)}).addClass("out reverse")})},startShowTimer:function(){a.mobile.fixedToolbars.clearShowTimer();var b=[].slice.call(arguments);h=setTimeout(function(){h=e;a.mobile.fixedToolbars.show.apply(null,b)},100)},clearShowTimer:function(){h&&
clearTimeout(h);h=e},toggle:function(b){b&&(g=b);return g==="overlay"?a.mobile.fixedToolbars.hide():a.mobile.fixedToolbars.show()},setTouchToggleEnabled:function(a){o=a}}}}();a(document).bind("pagecreate create",function(b){a(":jqmData(position='fixed')",b.target).length&&a(b.target).each(function(){if(!a.support.scrollTop||a.support.touchOverflow&&a.mobile.touchOverflowEnabled)return this;var b=a(this);b.jqmData("fullscreen")&&b.addClass("ui-page-fullscreen");b.find(".ui-header:jqmData(position='fixed')").addClass("ui-header-fixed ui-fixed-inline fade");
b.find(".ui-footer:jqmData(position='fixed')").addClass("ui-footer-fixed ui-fixed-inline fade")})})})(jQuery);
(function(a){a.mobile.touchOverflowEnabled=false;a.mobile.touchOverflowZoomEnabled=false;a(document).bind("pagecreate",function(e){a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&(e=a(e.target),e.is(":jqmData(role='page')")&&e.each(function(){var b=a(this),d=b.find(":jqmData(role='header'), :jqmData(role='footer')").filter(":jqmData(position='fixed')"),e=b.jqmData("fullscreen"),c=d.length?b.find(".ui-content"):b;b.addClass("ui-mobile-touch-overflow");c.bind("scrollstop",function(){c.scrollTop()>
0&&window.scrollTo(0,a.mobile.defaultHomeScroll)});d.length&&(b.addClass("ui-native-fixed"),e&&(b.addClass("ui-native-fullscreen"),d.addClass("fade in"),a(document).bind("vclick",function(){d.removeClass("ui-native-bars-hidden").toggleClass("in out").animationComplete(function(){a(this).not(".in").addClass("ui-native-bars-hidden")})})))}))})})(jQuery);
(function(a,e){function b(){var b=a("meta[name='viewport']");b.length?b.attr("content",b.attr("content")+", user-scalable=no"):a("head").prepend("<meta>",{name:"viewport",content:"user-scalable=no"})}var d=a("html");a("head");var f=a(e);a(e.document).trigger("mobileinit");if(a.mobile.gradeA()){if(a.mobile.ajaxBlacklist)a.mobile.ajaxEnabled=false;d.addClass("ui-mobile ui-mobile-rendering");var c=a("<div class='ui-loader ui-body-a ui-corner-all'><span class='ui-icon ui-icon-loading spin'></span><h1></h1></div>");
a.extend(a.mobile,{showPageLoadingMsg:function(){if(a.mobile.loadingMessage){var b=a("."+a.mobile.activeBtnClass).first();c.find("h1").text(a.mobile.loadingMessage).end().appendTo(a.mobile.pageContainer).css({top:a.support.scrollTop&&f.scrollTop()+f.height()/2||b.length&&b.offset().top||100})}d.addClass("ui-loading")},hidePageLoadingMsg:function(){d.removeClass("ui-loading")},initializePage:function(){var b=a(":jqmData(role='page')");b.length||(b=a("body").wrapInner("<div data-"+a.mobile.ns+"role='page'></div>").children(0));
b.add(":jqmData(role='dialog')").each(function(){var b=a(this);b.jqmData("url")||b.attr("data-"+a.mobile.ns+"url",b.attr("id")||location.pathname+location.search)});a.mobile.firstPage=b.first();a.mobile.pageContainer=b.first().parent().addClass("ui-mobile-viewport");f.trigger("pagecontainercreate");a.mobile.showPageLoadingMsg();!a.mobile.hashListeningEnabled||!a.mobile.path.stripHash(location.hash)?a.mobile.changePage(a.mobile.firstPage,{transition:"none",reverse:true,changeHash:false,fromHashChange:true}):
f.trigger("hashchange",[true])}});a.support.touchOverflow&&a.mobile.touchOverflowEnabled&&!a.mobile.touchOverflowZoomEnabled&&b();a.mobile._registerInternalEvents();a(function(){e.scrollTo(0,1);a.mobile.defaultHomeScroll=!a.support.scrollTop||a(e).scrollTop()===1?0:1;a.mobile.autoInitializePage&&a.mobile.initializePage();f.load(a.mobile.silentScroll)})}})(jQuery,this);
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
@@ -27,7 +27,7 @@
{{pass}} {{pass}}
{{else:}} {{else:}}
{{qry=''}} {{qry=''}}
{{pass}} {{pass}}
{{pass}} {{pass}}
<tr> <tr>
<th style="font-size: 1.75em;"> <th style="font-size: 1.75em;">
@@ -37,13 +37,19 @@
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}} {{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
</td> </td>
</tr> </tr>
{{pass}}
</table>
{{pass}} {{pass}}
{{pass}}
</table>
{{elif request.function=='select':}} {{elif request.function=='select':}}
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }} <h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
</h2> </h2>
{{if tb:}}
<h3>{{=T('Traceback')}}</h3>
<pre>
{{=tb}}
</pre>
{{pass}}
{{if table:}} {{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/> {{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/> <h3>{{=T("Rows in Table")}}</h3><br/>
@@ -149,7 +155,7 @@
<h4>{{=T("RAM")}}</h4> <h4>{{=T("RAM")}}</h4>
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p> <p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
{{if ram['entries'] > 0:}} {{if ram['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", <p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}} dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
</p> </p>
<p> <p>
@@ -43,8 +43,7 @@ def file_create_form(location):
form=FORM(T("create file with filename:")," ", form=FORM(T("create file with filename:")," ",
INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY), INPUT(_type="text",_name="filename",requires=IS_NOT_EMPTY),
INPUT(_type="hidden",_name="location",_value=location), INPUT(_type="hidden",_name="location",_value=location),
INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)), INPUT(_type="hidden",_name="sender",_value=URL('design',args=app)))
INPUT(_type="submit",_value=T("Create")),_action=URL('create_file'))
return form return form
def upload_plugin_form(app): def upload_plugin_form(app):
form=FORM(T("upload plugin file:")," ", form=FORM(T("upload plugin file:")," ",
@@ -207,7 +206,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
{{pass}} {{pass}}
</ul> </ul>
<div data-role="fieldcontain">{{=file_create_form('%s/static/' % app)}} <div data-role="fieldcontain">{{=file_create_form('%s/static/' % app)}}
{{=file_upload_form('%s/static/' % app)}}</div> {{#=file_upload_form('%s/static/' % app)}}</div>
</div> </div>
</div> </div>
@@ -225,7 +224,7 @@ for c in controllers: controller_functions+=[c[:-3]+'/%s.html'%x for x in functi
</span> </span>
{{pass}} {{pass}}
<div data-role="fieldcontain">{{=file_create_form('%s/modules/' % app)}} <div data-role="fieldcontain">{{=file_create_form('%s/modules/' % app)}}
{{=file_upload_form('%s/modules/' % app)}}</div> {{#=file_upload_form('%s/modules/' % app)}}</div>
</div> </div>
</div> </div>
@@ -9,8 +9,9 @@
<div><input type="hidden" name="send" value="{{=send}}"/></div> <div><input type="hidden" name="send" value="{{=send}}"/></div>
<table> <table>
<tr><td>{{=T('Administrator Password:')}}</td><td><input type="password" name="password" id="password"/></td></tr> <tr><td>{{=T('Administrator Password:')}}</td><td><input type="password" name="password" id="password"/></td></tr>
<tr><td></td><td><input type="submit" name="login" value="{{=T('Login')}}"/></td></tr> <tr><td></td><td><button name="login">{{=T('Login')}}</button></td></tr>
</table> </table>
<input type="hidden" name="is_mobile" value="true"/>
</form> </form>
</div> </div>
{{else:}} {{else:}}
@@ -9,7 +9,7 @@
Remove this if you use the .htaccess --> Remove this if you use the .htaccess -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>web2py mobile admin</title> <title>Web2py Mobile Admin</title>
<!-- http://dev.w3.org/html5/markup/meta.name.html --> <!-- http://dev.w3.org/html5/markup/meta.name.html -->
<meta name="application-name" content="{{=request.application}}" /> <meta name="application-name" content="{{=request.application}}" />
@@ -24,16 +24,17 @@
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
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
--> -->
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references --> <!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon"> <link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}"> <link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
<!-- For the less-enabled mobile browsers like Opera Mini --> <!-- For the less-enabled mobile browsers like Opera Mini -->
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.css')}}"> <link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.css')}}">
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects --> <!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script> <script src="{{=URL('static','js/modernizr.custom.js')}}"></script!>
{{include 'web2py_ajax.html'}} {{include 'web2py_ajax.html'}}
@@ -60,7 +61,7 @@ $(document).bind("mobileinit", function(){
}); });
</script> </script>
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.js')}}"></script> <script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.js')}}"></script>
<style> <style>
.error { color: red; } .error { color: red; }
@@ -75,7 +76,7 @@ $(document).bind("mobileinit", function(){
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
<div data-role="page"> <div data-role="page">
<div data-role="header" data-fullscreen="true" data-position="fixed"> <div data-role="header">
<h1>web2py mobile admin/{{block sectionclass}}{{end}}</h1> <h1>web2py mobile admin/{{block sectionclass}}{{end}}</h1>
{{block header}} {{block header}}
{{if 'auth' in globals():}} {{if 'auth' in globals():}}
@@ -85,7 +86,7 @@ $(document).bind("mobileinit", function(){
<a rel="external" href="{{=URL('default','user',args='logout')}}" data-icon="logout" class="ui-btn-left">Logout</a> <a rel="external" href="{{=URL('default','user',args='logout')}}" data-icon="logout" class="ui-btn-left">Logout</a>
{{pass}} {{pass}}
{{pass}} {{pass}}
<a rel="external" href="{{=URL('default','index')}}" data-icon="home" class="ui-btn-right">{{=T("Home")}}</a> <a rel="external" href="{{=URL('default','index',vars=dict(is_mobile='true'))}}" data-icon="home" class="ui-btn-right">{{=T("Home")}}</a>
{{end}} {{end}}
</div> </div>
<div data-role="content"> <div data-role="content">
@@ -100,7 +101,7 @@ $(document).bind("mobileinit", function(){
{{pass}} {{pass}}
{{include}} {{include}}
</div> </div>
<div data-role="footer" data-fullscreen="true" data-position="fixed" style="padding: 5px; text-align: center"> <div data-role="footer">
{{block footer}} {{block footer}}
powered by web2py - @{{=request.now.year}} powered by web2py - @{{=request.now.year}}
{{end}} {{end}}
@@ -4,16 +4,16 @@
<h2>{{=T("Installed applications")}}</h2> <h2>{{=T("Installed applications")}}</h2>
<ul data-role="listview"> <ul data-role="listview" data-inset="true">
{{for a in apps:}} {{for a in apps:}}
<li> <li>
{{if a==request.application:}} {{if a==request.application:}}
<h3>{{=a}} ({{=T('currently running')}})</h3> <h3>{{=a}} ({{=T('currently running')}})</h3>
{{else:}} {{else:}}
<h3>{{=a}}</h3> <h3>{{=T("Application")}} {{=a}}</h3>
{{if MULTI_USER_MODE and db.app(name=a):}}<p>created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}</p>{{pass}} {{if MULTI_USER_MODE and db.app(name=a):}}<p>created by {{="%(first_name)s %(last_name)s" % db.auth_user[db.app(name=a).owner]}}</p>{{pass}}
{{pass}} {{pass}}
<ul> <ul data-role="listview" data-inset="true">
{{if a!=request.application:}} {{if a!=request.application:}}
{{=LI(A(T('Goto'),_rel="external",_href=URL(a,'default','index')))}} {{=LI(A(T('Goto'),_rel="external",_href=URL(a,'default','index')))}}
{{if not os.path.exists('applications/%s/compiled' % a):}} {{if not os.path.exists('applications/%s/compiled' % a):}}
+16 -1
View File
@@ -19,6 +19,10 @@
<script src="{{=cm}}/mode/css/css.js"></script> <script src="{{=cm}}/mode/css/css.js"></script>
<script src="{{=cm}}/mode/javascript/javascript.js"></script> <script src="{{=cm}}/mode/javascript/javascript.js"></script>
<script src="{{=cm}}/mode/htmlmixed/htmlmixed.js"></script> <script src="{{=cm}}/mode/htmlmixed/htmlmixed.js"></script>
<script src="{{=cm}}/lib/util/search.js"></script>
<script src="{{=cm}}/lib/util/searchcursor.js"></script>
<script src="{{=cm}}/lib/util/dialog.js"></script>
<link rel="stylesheet" href="{{=cm}}/lib/util/dialog.css">
<script src="{{=cm}}/emmet.min.js"></script> <script src="{{=cm}}/emmet.min.js"></script>
<script language="Javascript" type="text/javascript" src="{{=URL('static','js/ajax_editor.js')}}"></script> <script language="Javascript" type="text/javascript" src="{{=URL('static','js/ajax_editor.js')}}"></script>
{{elif TEXT_EDITOR == 'ace':}} {{elif TEXT_EDITOR == 'ace':}}
@@ -130,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);
@@ -193,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>
@@ -246,6 +251,11 @@ window.onload = function() {
<ul> <ul>
{{=shortcut('Ctrl+S', T('Save via Ajax'))}} {{=shortcut('Ctrl+S', T('Save via Ajax'))}}
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}} {{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
{{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}}
{{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}}
{{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}}
{{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}}
{{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}}
{{=shortcut('Tab', T('Expand Abbreviation'))}} {{=shortcut('Tab', T('Expand Abbreviation'))}}
</ul> </ul>
{{elif TEXT_EDITOR == 'codemirror':}} {{elif TEXT_EDITOR == 'codemirror':}}
@@ -253,6 +263,11 @@ window.onload = function() {
<ul> <ul>
{{=shortcut('Ctrl+S', T('Save via Ajax'))}} {{=shortcut('Ctrl+S', T('Save via Ajax'))}}
{{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}} {{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}}
{{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}}
{{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}}
{{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}}
{{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}}
{{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}}
</ul> </ul>
{{else:}} {{else:}}
<h3>{{=T("Key bindings")}}</h3> <h3>{{=T("Key bindings")}}</h3>
@@ -76,6 +76,9 @@
<p> <p>
{{=T("Running on %s", request.env.server_software)}} {{=T("Running on %s", request.env.server_software)}}
</p> </p>
{{if session.is_mobile=='auto':}}<p>
{{=A(T('Try the mobile interface'),_href=URL('plugin_jqmobile','about'))}}</p>
{{pass}}
</div> </div>
{{pass}} {{pass}}
<!-- MULTI_USER_INTERFACE --> <!-- MULTI_USER_INTERFACE -->
@@ -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;
@@ -34,7 +41,8 @@
</style> </style>
</head> </head>
<body> <body>
<iframe src="{{=URL('index')}}"></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>
@@ -24,17 +24,17 @@
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
maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width maximum-scale = 1.0 retains dimensions instead of zooming in if page width < device width
--> -->
<!--meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"--> <meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references --> <!-- Place favicon.ico and apple-touch-icon.png in the root of your domain and delete these references -->
<link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon"> <link rel="shortcut icon" href="{{=URL('static','favicon.ico')}}" type="image/x-icon">
<link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}"> <link rel="apple-touch-icon" href="{{=URL('static','favicon.png')}}">
<!-- For the less-enabled mobile browsers like Opera Mini --> <!-- For the less-enabled mobile browsers like Opera Mini -->
<link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.css')}}"> <link rel="stylesheet" href="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.css')}}">
<!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects --> <!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
<script src="{{=URL('static','js/modernizr.custom.js')}}"></script> <script src="{{=URL('static','js/modernizr.custom.js')}}"></script!>
{{include 'web2py_ajax.html'}} {{include 'web2py_ajax.html'}}
@@ -61,7 +61,7 @@ $(document).bind("mobileinit", function(){
}); });
</script> </script>
<script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.0.min.js')}}"></script> <script src="{{=URL('static','plugin_jqmobile/jquery.mobile-1.2.0.min.js')}}"></script>
<style> <style>
.error { color: red; } .error { color: red; }
@@ -76,7 +76,7 @@ $(document).bind("mobileinit", function(){
<!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <body> <!--<![endif]-->
<div data-role="page"> <div data-role="page">
<div data-role="header" data-fullscreen="true" data-position="fixed"> <div data-role="header">
<h1>{{=response.title}}</h1> <h1>{{=response.title}}</h1>
{{block header}} {{block header}}
{{if 'auth' in globals():}} {{if 'auth' in globals():}}
@@ -101,7 +101,7 @@ $(document).bind("mobileinit", function(){
{{pass}} {{pass}}
{{include}} {{include}}
</div> </div>
<div data-role="footer" data-fullscreen="true" data-position="fixed" style="padding: 5px; text-align: center"> <div data-role="footer">
{{block footer}} {{block footer}}
powered by web2py - @{{=request.now.year}} powered by web2py - @{{=request.now.year}}
{{end}} {{end}}
+1
View File
@@ -0,0 +1 @@
@@ -18,6 +18,3 @@ def flash():
def fade(): def fade():
return dict() return dict()
+70 -51
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,25 +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)
@@ -128,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])
# ########################################################## # ##########################################################
@@ -139,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
@@ -150,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)
@@ -193,14 +193,17 @@ 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())
@@ -210,26 +213,30 @@ def select():
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
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:
@@ -238,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(
@@ -249,8 +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,
)
# ########################################################## # ##########################################################
@@ -260,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)
@@ -277,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])
# ########################################################## # ##########################################################
@@ -301,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):
@@ -329,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 = {
@@ -378,9 +398,10 @@ def ccache():
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
locker = open(os.path.join(request.folder, locker = open(os.path.join(request.folder,
'cache/cache.lock'), 'a') 'cache/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(request.folder, 'cache/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):
@@ -411,7 +432,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
@@ -437,6 +459,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)
@@ -38,7 +38,6 @@ def hello6():
def status(): def status():
""" page that shows internal status""" """ page that shows internal status"""
response.view = 'generic.html'
return dict(toolbar=response.toolbar()) return dict(toolbar=response.toolbar())
@@ -103,9 +102,8 @@ def rss_aggregator():
return rss2.dumps(rss) return rss2.dumps(rss)
def ajaxwiki(): def ajaxwiki():
default=""" default = """
# section # section
## subsection ## subsection
@@ -130,12 +128,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 ''
+9 -7
View File
@@ -10,7 +10,7 @@ h3 {font-size:2.00em}
h4 {font-size:1.50em} h4 {font-size:1.50em}
h5 {font-size:1.25em} h5 {font-size:1.25em}
h6 {font-size:1.12em} h6 {font-size:1.12em}
th,label {font-weight:bold; white-space:nowrap} th,label {font-weight:bold; white-space:nowrap;}
td,th {text-align:left; padding:2px 5px 2px 5px} td,th {text-align:left; padding:2px 5px 2px 5px}
th {vertical-align:middle; border-right:1px solid white} th {vertical-align:middle; border-right:1px solid white}
td {vertical-align:top} td {vertical-align:top}
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1} fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
/* fix ie problem with menu */ /* fix ie problem with menu */
.ie-lte7 .topbar .container {z-index:2}
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
@@ -187,10 +186,7 @@ div.error {
.web2py_paginator {} .web2py_paginator {}
.web2py_grid {width:100%} .web2py_grid {width:100%}
.web2py_grid table {width:100%} .web2py_grid table {width:100%}
.web2py_grid tbody td { .web2py_grid tbody td {padding:2px 5px 2px 5px; vertical-align: middle;}
padding:2px 5px 2px 5px;
vertical-align:middle;
}
.web2py_grid thead th,.web2py_grid tfoot td { .web2py_grid thead th,.web2py_grid tfoot td {
background-color:#EAEAEA; background-color:#EAEAEA;
@@ -299,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
.web2py_console input, .web2py_console select, .web2py_console input, .web2py_console select,
.web2py_console a { margin: 2px; } .web2py_console a { margin: 2px; }
.ie9 #w2p_query_panel {padding-bottom:2px}
#wiki_page_body { #wiki_page_body {
width: 600px; width: 600px;
height: auto; height: auto;
min-height: 400px; min-height: 400px;
} }
/* fix some IE problems */
.ie-lte7 .topbar .container {z-index:2}
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
.ie9 #w2p_query_panel {padding-bottom:2px}
+49 -14
View File
@@ -39,7 +39,7 @@ function web2py_ajax_init(target) {
function web2py_event_handlers() { function web2py_event_handlers() {
var doc = jQuery(document) var doc = jQuery(document)
doc.on('click', '.flash', function(e){jQuery(this).fadeOut('slow'); e.preventDefault();}); doc.on('click', '.flash', function(e){var t=jQuery(this); if(t.css('top')=='0px') t.slideUp('slow'); else t.fadeOut(); e.preventDefault();});
doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();}); doc.on('keyup', 'input.integer', function(){this.value=this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g,'').reverse();});
doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();});
var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?";
@@ -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.slideDown(); if(flash.html()) flash.append('<span style="float:right;">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
form.submit(function(e){ form.submit(function(e){
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('post',action,form.serialize(),target); web2py_ajax_page('post',action,form.serialize(),target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_trap_link(target) { function web2py_trap_link(target) {
jQuery('#'+target+' a.w2p_trap').each(function(i){ jQuery('#'+target+' a.w2p_trap').each(function(i){
var link=jQuery(this); var link=jQuery(this);
link.click(function(e) { link.click(function(e) {
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('get',link.attr('href'),[],target); web2py_ajax_page('get',link.attr('href'),[],target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_ajax_page(method, action, data, target) { function web2py_ajax_page(method, action, data, target) {
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
web2py_trap_link(target); web2py_trap_link(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)).slideDown();
} }
}); });
} }
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
} }
} else { } else {
// run once (no timeout specified) // run once (no timeout specified)
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py_ajax_page('get', action, null, target); web2py_ajax_page('get', action, null, target);
} }); } } }); }
function web2py_comet(url,onmessage,onopen,onclose) { function web2py_websocket(url,onmessage,onopen,onclose) {
if ("WebSocket" in window) { if ("WebSocket" in window) {
var ws = new WebSocket(url); var ws = new WebSocket(url);
ws.onopen = onopen?onopen:(function(){}); ws.onopen = onopen?onopen:(function(){});
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
} else return false; // not supported } else return false; // not supported
} }
function web2py_calc_entropy(mystring) {
//calculate a simple entropy for a given string
var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
'0123456789abcdefghijklmnopqrstuvwxyz');
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
for (var i=0;i<mystringlist.length;i++) { // classify this character
var c = mystringlist[i], inset=5;
for(var j = 0; j<csets.length; j++)
if (csets[j].indexOf(c) != -1) {inset = j; break;}
//calculate effect of character on alphabet size
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
else if (!(c in other)) {score += 1;other[c] = 1;}
if (inset != lastset) {score += 1;lastset = inset;}
}
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
return Math.round(entropy*100)/100
}
function web2py_validate_entropy(myfield, req_entropy) {
var validator = function () {
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
if(v<=0.5) {r=1.0; g=2.0*v;}
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
var color = '#'+rs(r)+rs(g)+rs(b);
myfield.css('background-color',color);
entropy_callback = myfield.data('entropy_callback');
if(entropy_callback) entropy_callback(v);
}
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
}
+10 -4
View File
@@ -27,7 +27,7 @@
{{pass}} {{pass}}
{{else:}} {{else:}}
{{qry=''}} {{qry=''}}
{{pass}} {{pass}}
{{pass}} {{pass}}
<tr> <tr>
<th style="font-size: 1.75em;"> <th style="font-size: 1.75em;">
@@ -37,13 +37,19 @@
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}} {{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
</td> </td>
</tr> </tr>
{{pass}}
</table>
{{pass}} {{pass}}
{{pass}}
</table>
{{elif request.function=='select':}} {{elif request.function=='select':}}
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }} <h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
</h2> </h2>
{{if tb:}}
<h3>{{=T('Traceback')}}</h3>
<pre>
{{=tb}}
</pre>
{{pass}}
{{if table:}} {{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/> {{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/> <h3>{{=T("Rows in Table")}}</h3><br/>
@@ -149,7 +155,7 @@
<h4>{{=T("RAM")}}</h4> <h4>{{=T("RAM")}}</h4>
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p> <p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
{{if ram['entries'] > 0:}} {{if ram['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", <p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}} dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
</p> </p>
<p> <p>
@@ -80,8 +80,7 @@ Current version: {{="%s.%s.%s (%s) %s" % request.env.web2py_version}}</p>
<br/> <br/>
<a class="button" href="{{=URL('download')}}" style="width:90%">DOWNLOAD NOW</a><br/> <a class="button" href="{{=URL('download')}}" style="width:90%">DOWNLOAD NOW</a><br/>
<a class="button" href="http://web2py.com/demo_admin" style="width:90%">ONLINE DEMO</a><br/> <a class="button" href="http://web2py.com/demo_admin" style="width:90%">ONLINE DEMO</a><br/>
<a class="button" href="http://web2py.com/poweredby" style="width:90%">SITES POWERED BY WEB2PY</a> <a class="button" href="http://web2py.com/poweredby" style="width:90%">SITES POWERED BY WEB2PY</a><br/>
<br/> <a class="button" href="http://www.chipin.com/contribute/id/dcd384d58839aa4d" style="width:90%">SUPPORT/DONATE</a></br>
<object width="234" height="60"><param name="movie" value="http://widget.chipin.com/widget/id/dcd384d58839aa4d"></param><param name="allowScriptAccess" value="always"></param><param name="wmode" value="transparent"></param><param name="color_scheme" value="gray"></param><embed src="http://widget.chipin.com/widget/id/dcd384d58839aa4d" flashVars="color_scheme=gray" type="application/x-shockwave-flash" allowScriptAccess="always" wmode="transparent" width="234" height="60"></embed></object>
</div> </div>
{{end}} {{end}}
@@ -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)
@@ -70,6 +71,7 @@
</li><li>Ian Reinhart Geiser (html helpers) </li><li>Ian Reinhart Geiser (html helpers)
</li><li>Ionel Anton (Romanian translation) </li><li>Ionel Anton (Romanian translation)
</li><li>Jan Beilicke (markmin) </li><li>Jan Beilicke (markmin)
</li><li>Jeremy Dillworth
</li><li>Jonathan Benn (is_url validator and tests) </li><li>Jonathan Benn (is_url validator and tests)
</li><li>Jonathan Lundell (multiple contributions) </li><li>Jonathan Lundell (multiple contributions)
</li><li>Josh Goldfoot (xaml/html sanitizer) </li><li>Josh Goldfoot (xaml/html sanitizer)
@@ -156,6 +158,7 @@
<li><a href="http://www.danga.com/memcached/">memcache</a> developed by Evan Martin</li> <li><a href="http://www.danga.com/memcached/">memcache</a> developed by Evan Martin</li>
<li><a href="http://jquery.com/">jQuery</a> developed by John Resig</li> <li><a href="http://jquery.com/">jQuery</a> developed by John Resig</li>
<li>A syntax highlighter inspired by the code of <a href="http://www.petersblog.org/node/763">Peter Wilkinson</a></li> <li>A syntax highlighter inspired by the code of <a href="http://www.petersblog.org/node/763">Peter Wilkinson</a></li>
<li><a href="https://github.com/jtauber/pyuca">pyUCA</a> developed by <a href="http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/">James Tauber</a></li>
</ul> </ul>
+1
View File
@@ -0,0 +1 @@
+70 -51
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,25 +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)
@@ -128,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])
# ########################################################## # ##########################################################
@@ -139,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
@@ -150,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)
@@ -193,14 +193,17 @@ 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())
@@ -210,26 +213,30 @@ def select():
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
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:
@@ -238,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(
@@ -249,8 +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,
)
# ########################################################## # ##########################################################
@@ -260,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)
@@ -277,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])
# ########################################################## # ##########################################################
@@ -301,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):
@@ -329,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 = {
@@ -378,9 +398,10 @@ def ccache():
ram['keys'].append((key, GetInHMS(time.time() - value[0]))) ram['keys'].append((key, GetInHMS(time.time() - value[0])))
locker = open(os.path.join(request.folder, locker = open(os.path.join(request.folder,
'cache/cache.lock'), 'a') 'cache/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(request.folder, 'cache/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):
@@ -411,7 +432,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
@@ -437,6 +459,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():
@@ -5,6 +5,7 @@
'book': ['books'], 'book': ['books'],
'is': ['are'], 'is': ['are'],
'man': ['men'], 'man': ['men'],
'miss': ['misses'],
'person': ['people'], 'person': ['people'],
'quark': ['quarks'], 'quark': ['quarks'],
'shop': ['shops'], 'shop': ['shops'],
+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 створено',
+3 -3
View File
@@ -16,7 +16,7 @@ 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
+98 -63
View File
@@ -5,7 +5,8 @@
## 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.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,80 +23,114 @@ response.google_analytics_id = None
######################################################################### #########################################################################
response.menu = [ response.menu = [
(T('Home'), False, URL('default','index'), []) (T('Home'), False, URL('default', 'index'), [])
] ]
######################################################################### #########################################################################
## provide shortcuts for development. remove in production ## provide shortcuts for development. remove in production
######################################################################### #########################################################################
def _(): def _():
# shortcuts # shortcuts
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',_style='color:yellow'),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'),
]) ])
] ]
)] )]
_() _()
-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
+9 -4
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>; */
} }
@@ -69,7 +69,6 @@ fieldset {padding:16px; border-top:1px #DEDEDE solid}
fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1} fieldset legend {text-transform:uppercase; font-weight:bold; padding:4px 16px 4px 16px; background:#f1f1f1}
/* fix ie problem with menu */ /* fix ie problem with menu */
.ie-lte7 .topbar .container {z-index:2}
td.w2p_fw {padding-bottom:1px} td.w2p_fw {padding-bottom:1px}
td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top} td.w2p_fl,td.w2p_fw,td.w2p_fc {vertical-align:top}
@@ -183,7 +182,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%}
@@ -296,10 +295,16 @@ li.w2p_grid_breadcrumb_elem {
.web2py_console input, .web2py_console select, .web2py_console input, .web2py_console select,
.web2py_console a { margin: 2px; } .web2py_console a { margin: 2px; }
.ie9 #w2p_query_panel {padding-bottom:2px}
#wiki_page_body { #wiki_page_body {
width: 600px; width: 600px;
height: auto; height: auto;
min-height: 400px; min-height: 400px;
} }
/* fix some IE problems */
.ie-lte7 .topbar .container {z-index:2}
.ie-lte8 div.flash{ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#222222', endColorstr='#000000', GradientType=0 ); }
.ie-lte8 div.flash:hover {filter:alpha(opacity=25);}
.ie9 #w2p_query_panel {padding-bottom:2px}
@@ -1,20 +1,19 @@
#navbar .auth_navbar, #navbar .auth_navbar a {color:inherit;} /*=============================================================
CUSTOM RULES
==============================================================*/
div.flash.flash-center { body{height:auto;} /* to avoid vertical scroll bar */
left: 25%; div.flash.flash-center{left:25%;right:25%;}
right: 25%; div.flash.flash-top,div.flash.flash-top:hover{
} position:relative;
display:block;
div.flash.flash-top, div.flash.flash-top:hover {
position: relative;
display: block;
margin:0; margin:0;
padding:1em; padding:1em;
top:0; top:0;
left:0; left:0;
width:100%; width:100%;
text-align:center; text-align:center;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);
color:#865100; color:#865100;
background:#feea9a; background:#feea9a;
border:1px solid; border:1px solid;
@@ -24,154 +23,209 @@ div.flash.flash-top, div.flash.flash-top:hover {
border-radius:0; border-radius:0;
opacity:1; opacity:1;
} }
#header{margin-top:60px;}
/* bootstrap dropdown */
.dropdown-menu ul {
left: 100%;
position: absolute;
top: 0;
visibility: hidden;
margin-top: -1px;
}
.dropdown-menu li:hover ul {
visibility: visible;
}
#header {
margin-top: 60px;
}
.mastheader h1 { .mastheader h1 {
margin-bottom: 9px; margin-bottom:9px;
font-size: 81px; font-size:81px;
font-weight: bold; font-weight:bold;
letter-spacing: -1px; letter-spacing:-1px;
line-height: 1; line-height:1;
font-size:54px;
} }
.mastheader h1 {
font-size: 54px;
}
.mastheader small { .mastheader small {
font-size: 20px; font-size:20px;
font-weight: 300; font-weight:300;
} }
/* auth navbar - primitive style */
.navbar .dropdown-menu ul:before { .auth_navbar,.auth_navbar a{color:inherit;}
border-bottom: 7px solid transparent; .ie-lte7 .auth_navbar,.auth_navbar a{color:expression(this.parentNode.currentStyle['color']); /* ie7 doesn't support inherit */}
border-left: none; .auth_navbar a{white-space:nowrap;} /* to avoid the nav split on more lines */
border-right: 7px solid rgba(0, 0, 0, 0.2); .auth_navbar a:hover{color:white;text-decoration:none;}
border-top: 7px solid transparent; ul#navbar>.auth_navbar{
left: -7px;
top: 5px;
}
.navbar .dropdown-menu ul:after {
border-top: 6px solid transparent;
border-left: none;
border-right: 6px solid #fff;
border-bottom: 6px solid transparent;
left: 10px;
top: 6px;
left: -6px;
}
.dropdown-menu span{
display:inline-block; display:inline-block;
padding:5px;
} }
/* form errors message box customization */
.chevron-right { div.error_wrapper{margin-bottom:9px;}
border-left: 4px solid #000; div.error_wrapper .error{
border-right: 4px solid transparent;
border-bottom: 4px solid transparent;
border-top: 4px solid transparent;
content: "";
display: inline-block;
height: 0;
opacity: 0.7;
vertical-align: top;
width: 0;
margin-right:-13px;
margin-top: 7px;
float:right;
}
.open > .dropdown-menu ul { /* fix menu issue when BS2.0.4 is applied */
display: block;
}
.ie-lte7 #navbar .auth_navbar, #navbar .auth_navbar a {color:expression(this.parentNode.currentStyle['color']); /* ie7 doesn't support inherit */}
#navbar .auth_navbar a:hover {color:white;text-decoration:none;} /* this overwrite bootswatch */
ul#navbar{padding:0;} /*reset ul padding */
ul#navbar>.auth_navbar{display:inline-block;padding:5px;} /* set padding of span.auth_navbar inside ul*/
body {
height:auto; /*to avoid vertical scroll bar*/
}
h1,h2,h3,h4,h5,h6 {font-family: inherit;}
li {margin-bottom: 0;} /*bootswatch*/
.auth_navbar, .navbar .btn-group { padding:0; }
[class^="icon-"],[class*=" icon-"]{background-image:url("../images/glyphicons-halflings.png")} /* right folder for bootstrap black images/icons */
.icon-white{background-image:url("../images/glyphicons-halflings-white.png");} /* right folder for bootstrap white images/icons */
.navbar-inner{
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
position:relative;
}
@media only screen and (max-width: 979px) {
body {
padding-top: 0px;
}
#navbar{bottom:-10px;left:4px;}
div.flash {
right: 5px;
}
.dropdown-menu ul {
visibility: visible;
}
}
div.error_wrapper {
margin-bottom: 9px;
}
div.error {
border-radius: 4px; border-radius: 4px;
-o-border-radius: 4px; -o-border-radius: 4px;
-moz-border-radius: 4px; -moz-border-radius: 4px;
-webkit-border-radius: 4px; -webkit-border-radius: 4px;
} }
/* below rules are only for formstyle = bootstrap
trying to make errors look like bootstrap ones */
div.controls .error_wrapper{
display:inline-block;
margin-bottom:0;
vertical-align:middle;
}
div.controls .error{
min-width:5px;
background:inherit;
color:#B94A48;
border:none;
padding:0;
margin:0;
//display:inline; /* uncommenting this, the animation effect is lost */
}
div.controls .inline-help{color:#3A87AD;}
div.controls .error_wrapper+.inline-help{margin-left:-99999px;}
/* beautify brand */
.navbar-inverse .brand{color:#c6cecc;}
.navbar-inverse .brand b{display:inline-block;margin-top:-1px;}
.navbar-inverse .brand b>span{font-size:22px;color:white}
.navbar-inverse .brand:hover b>span{color:white}
/* beautify web2py link in navbar */
span.highlighted{color:#d8d800;}
.open span.highlighted{color:#ffff00;}
input[type="button"], input[type="submit"] { /*=============================================================
// to be fixed OVERRIDING WEB2PY.CSS RULES
==============================================================*/
/* reset to default */
a{white-space:normal;}
li{margin-bottom:0;}
textarea,button{display:block;}
/*reset ul padding */
ul#navbar{padding:0;}
/* label aligned to related input */
td.w2p_fl,td.w2p_fc {padding:0;}
#web2py_user_form td{vertical-align:middle;}
/*=============================================================
OVERRIDING BOOTSTRAP.CSS RULES
==============================================================*/
/* because web2py handles this via js */
.hidden{visibility:visible;}
/* right folder for bootstrap black images/icons */
[class^="icon-"],[class*=" icon-"]{
background-image:url("../images/glyphicons-halflings.png")
}
/* right folder for bootstrap white images/icons */
.icon-white,
.nav-tabs > .active > a > [class^="icon-"],
.nav-tabs > .active > a > [class*=" icon-"],
.nav-pills > .active > a > [class^="icon-"],
.nav-pills > .active > a > [class*=" icon-"],
.nav-list > .active > a > [class^="icon-"],
.nav-list > .active > a > [class*=" icon-"],
.navbar-inverse .nav > .active > a > [class^="icon-"],
.navbar-inverse .nav > .active > a > [class*=" icon-"],
.dropdown-menu > li > a:hover > [class^="icon-"],
.dropdown-menu > li > a:hover > [class*=" icon-"],
.dropdown-menu > .active > a > [class^="icon-"],
.dropdown-menu > .active > a > [class*=" icon-"] {
background-image:url("../images/glyphicons-halflings-white.png");
}
/* bootstrap has a label as input's wrapper while web2py has a div */
div>input[type="radio"],div>input[type="checkbox"]{margin:0;}
/* bootstrap has button instead of input */
input[type="button"], input[type="submit"]{margin-right:8px;}
/*=============================================================
RULES FOR SOLVING CONFLICTS BETWEEN WEB2PY.CSS AND BOOTSTRAP.CSS
==============================================================*/
/*when formstyle=table3cols*/
tr#auth_user_remember__row>td.w2p_fw>div{padding-bottom:8px;}
td.w2p_fw div>label{vertical-align:middle;}
td.w2p_fc {padding-bottom:5px;}
/*when formstyle=divs*/
div#auth_user_remember__row{margin-top:4px;}
div#auth_user_remember__row>.w2p_fl{display:none;}
div#auth_user_remember__row>.w2p_fw{min-height:39px;}
div.w2p_fw,div.w2p_fc{
display:inline-block;
vertical-align:middle;
margin-bottom:0;
}
div.w2p_fc{
padding-left:5px;
margin-top:-8px;
}
/*when formstyle=ul*/
form>ul{
list-style:none;
margin:0;
}
li#auth_user_remember__row{margin-top:4px;}
li#auth_user_remember__row>.w2p_fl{display:none;}
li#auth_user_remember__row>.w2p_fw{min-height:39px;}
/*when formstyle=bootstrap*/
#auth_user_remember__row label.checkbox{display:block;}
span.inline-help{display:inline-block;}
input[type="text"].input-xlarge,input[type="password"].input-xlarge{width:270px;}
/*when recaptcha is used*/
#recaptcha{min-height:30px;display:inline-block;margin-bottom:0;line-height:30px;vertical-align:middle;}
td>#recaptcha{margin-bottom:6px;}
div>#recaptcha{margin-bottom:9px;}
div.control-group.error{
width:auto;
background:transparent;
border:0;
color:inherit;
padding:0;
background-repeat:repeat;
} }
@media only screen and (max-width: 479px) { /*=============================================================
body { OTHER RULES
padding-left: 10px; ==============================================================*/
padding-right: 10px;
}
.navbar-fixed-top, .navbar-fixed-bottom { .navbar-inner{
margin-left: -10px; position:relative; /*unnecessary ??*/
margin-right: -10px; }
} /* Massimo Di Pierro fixed alignment in forms with list:string */
form table tr{margin-bottom:9px;}
td.w2p_fw ul{margin-left:0px;}
input[type="text"], input[type="password"], select { /* web2py_console in grid and smartgrid */
width: 95%; .hidden{visibility:visible;}
.web2py_console input{
display: inline-block;
margin-bottom: 0;
vertical-align: middle;
}
.web2py_console input[type="submit"],
.web2py_console input[type="button"],
.web2py_console button{
padding-top:4px;
padding-bottom:4px;
margin:3px 0 0 2px;
}
.web2py_console a,
.web2py_console select,
.web2py_console input
{
margin:3px 0 0 2px;
}
.web2py_grid form table{width:auto;}
/* auth_user_remember checkbox extrapadding in IE fix */
.ie-lte9 input#auth_user_remember.checkbox {padding-left:0;}
/*=============================================================
MEDIA QUERIES
==============================================================*/
@media only screen and (max-width:979px){
body{padding-top:0px;}
#navbar{bottom:-10px;left:4px;}
div.flash{right:5px;}
.dropdown-menu ul{visibility:visible;}
}
@media only screen and (max-width:479px){
body{
padding-left:10px;
padding-right:10px;
}
.navbar-fixed-top,.navbar-fixed-bottom {
margin-left:-10px;
margin-right:-10px;
}
input[type="text"],input[type="password"],select{
width:95%;
} }
} }
@@ -1,3 +1,23 @@
/*=============================================================
BOOTSTRAP DROPDOWN MENU
==============================================================*/
.dropdown-menu ul{
left:100%;
position:absolute;
top:0;
visibility:hidden;
margin-top:-1px;
}
.dropdown-menu li:hover ul{visibility:visible;}
.navbar .dropdown-menu ul:before{
border-bottom:7px solid transparent;
border-left:none;
border-right:7px solid rgba(0, 0, 0, 0.2);
border-top:7px solid transparent;
left:-7px;
top:5px;
}
.nav > li.dropdown > a:after { .nav > li.dropdown > a:after {
border-left: 4px solid transparent; border-left: 4px solid transparent;
border-right: 4px solid transparent; border-right: 4px solid transparent;
@@ -15,8 +35,7 @@
border-bottom-color: #FFFFFF; border-bottom-color: #FFFFFF;
border-top-color: #FFFFFF; border-top-color: #FFFFFF;
} }
.dropdown-menu span{display:inline-block;}
ul.dropdown-menu li.dropdown > a:after { ul.dropdown-menu li.dropdown > a:after {
border-left: 4px solid #000; border-left: 4px solid #000;
border-right: 4px solid transparent; border-right: 4px solid transparent;
@@ -36,3 +55,68 @@ ul.dropdown-menu li.dropdown > a:after {
ul.nav li.dropdown:hover ul.dropdown-menu { ul.nav li.dropdown:hover ul.dropdown-menu {
display: block; display: block;
} }
.open >.dropdown-menu ul{display:block;} /* fix menu issue when BS2.0.4 is applied */
/*=============================================================
BOOTSTRAP SUBMIT BUTTON
==============================================================*/
input[type='submit']:not(.btn) {
display: inline-block;
padding: 4px 14px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
color: #333;
text-align: center;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
vertical-align: middle;
cursor: pointer;
background-color: whiteSmoke;
background-image: -webkit-gradient(linear,0 0,0 100%,from(white),to(#E6E6E6));
background-image: -webkit-linear-gradient(top,white,#E6E6E6);
background-image: -o-linear-gradient(top,white,#E6E6E6);
background-image: linear-gradient(to bottom,white,#E6E6E6);
background-image: -moz-linear-gradient(top,white,#E6E6E6);
background-repeat: repeat-x;
border: 1px solid #BBB;
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
border-bottom-color: #A2A2A2;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);
filter: progid:dximagetransform.microsoft.gradient(enabled=false);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);
}
input[type='submit']:not(.btn):hover {
color: #333;
text-decoration: none;
background-color: #E6E6E6;
background-position: 0 -15px;
-webkit-transition: background-position .1s linear;
-moz-transition: background-position .1s linear;
-o-transition: background-position .1s linear;
transition: background-position .1s linear;
}
input[type='submit']:not(.btn).active, input[type='submit']:not(.btn):active {
background-color: #E6E6E6;
background-color: #D9D9D9 9;
background-image: none;
outline: 0;
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15),0 1px 2px rgba(0, 0, 0, 0.05);
}
/*=============================================================
OTHER
==============================================================*/
.ie-lte8 .navbar-fixed-top {position:static;}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -21,8 +21,8 @@ jQuery(function(){
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;
var facebook = 'http://www.facebook.com/sharer.php?u='+url; var facebook = 'http://www.facebook.com/sharer.php?u='+url;
var buzz = 'http://www.google.com/reader/link?url='+url+'&amp;title='+title+'&amp;srcURL='+host; var gplus = 'https://plus.google.com/share?url='+url;
var tbar = '<div id="socialdrawer"><span>Share<br/></span><div id="sicons"><a href="'+twit+'" id="twit" title="Share on twitter"><img src="'+path+'/twitter.png" alt="Share on Twitter" width="32" height="32" /></a><a href="'+facebook+'" id="facebook" title="Share on Facebook"><img src="'+path+'/facebook.png" alt="Share on facebook" width="32" height="32" /></a><a href="'+buzz+'" id="buzz" title="Share on Buzz"><img src="'+path+'/google-buzz.png" alt="Share on Buzz" width="32" height="32" /></a></div></div>'; var tbar = '<div id="socialdrawer"><span>Share<br/></span><div id="sicons"><a href="'+twit+'" id="twit" title="Share on twitter"><img src="'+path+'/twitter.png" alt="Share on Twitter" width="32" height="32" /></a><a href="'+facebook+'" id="facebook" title="Share on Facebook"><img src="'+path+'/facebook.png" alt="Share on facebook" width="32" height="32" /></a><a href="'+gplus+'" id="gplus" title="Share on Google Plus"><img src="'+path+'/gplus-32.png" alt="Share on Google Plus" width="32" height="32" /></a></div></div>';
// Add the share tool bar. // Add the share tool bar.
jQuery('body').append(tbar); jQuery('body').append(tbar);
var st = jQuery('#socialdrawer'); var st = jQuery('#socialdrawer');
+48 -13
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 style="float:right;">&times;</span>').slideDown();
web2py_ajax_init(document); web2py_ajax_init(document);
web2py_event_handlers(); web2py_event_handlers();
}); });
@@ -67,20 +67,20 @@ function web2py_trap_form(action,target) {
form.submit(function(e){ form.submit(function(e){
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('post',action,form.serialize(),target); web2py_ajax_page('post',action,form.serialize(),target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_trap_link(target) { function web2py_trap_link(target) {
jQuery('#'+target+' a.w2p_trap').each(function(i){ jQuery('#'+target+' a.w2p_trap').each(function(i){
var link=jQuery(this); var link=jQuery(this);
link.click(function(e) { link.click(function(e) {
jQuery('.flash').hide().html(''); jQuery('.flash').hide().html('');
web2py_ajax_page('get',link.attr('href'),[],target); web2py_ajax_page('get',link.attr('href'),[],target);
e.preventDefault(); e.preventDefault();
}); });
}); });
} }
function web2py_ajax_page(method, action, data, target) { function web2py_ajax_page(method, action, data, target) {
@@ -101,9 +101,9 @@ function web2py_ajax_page(method, action, data, target) {
web2py_trap_link(target); web2py_trap_link(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)).slideDown();
} }
}); });
} }
@@ -151,11 +151,11 @@ function web2py_component(action, target, timeout, times){
} }
} else { } else {
// run once (no timeout specified) // run once (no timeout specified)
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py_ajax_page('get', action, null, target); web2py_ajax_page('get', action, null, target);
} }); } } }); }
function web2py_comet(url,onmessage,onopen,onclose) { function web2py_websocket(url,onmessage,onopen,onclose) {
if ("WebSocket" in window) { if ("WebSocket" in window) {
var ws = new WebSocket(url); var ws = new WebSocket(url);
ws.onopen = onopen?onopen:(function(){}); ws.onopen = onopen?onopen:(function(){});
@@ -165,3 +165,38 @@ function web2py_comet(url,onmessage,onopen,onclose) {
} else return false; // not supported } else return false; // not supported
} }
function web2py_calc_entropy(mystring) {
//calculate a simple entropy for a given string
var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
'0123456789abcdefghijklmnopqrstuvwxyz');
var score = 0, other = {}, seen = {}, lastset = null, mystringlist = mystring.split('');
for (var i=0;i<mystringlist.length;i++) { // classify this character
var c = mystringlist[i], inset=5;
for(var j = 0; j<csets.length; j++)
if (csets[j].indexOf(c) != -1) {inset = j; break;}
//calculate effect of character on alphabet size
if(!(inset in seen)) {seen[inset] = 1;score += csets[inset].length;}
else if (!(c in other)) {score += 1;other[c] = 1;}
if (inset != lastset) {score += 1;lastset = inset;}
}
var entropy = mystring.length*Math.log(score)/0.6931471805599453;
return Math.round(entropy*100)/100
}
function web2py_validate_entropy(myfield, req_entropy) {
var validator = function () {
var v = (web2py_calc_entropy(myfield.val())||0)/req_entropy;
var r=0,g=0,b=0,rs=function(x){return Math.round(x*15).toString(16)};
if(v<=0.5) {r=1.0; g=2.0*v;}
else {r=(1.0-2.0*(Math.max(v,0)-0.5)); g=1.0;}
var color = '#'+rs(r)+rs(g)+rs(b);
myfield.css('background-color',color);
entropy_callback = myfield.data('entropy_callback');
if(entropy_callback) entropy_callback(v);
}
if(!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator).addClass('entropy_check');
}
@@ -0,0 +1,28 @@
// this code improves bootstrap menus and adds dropdown support
jQuery(function(){
jQuery('.nav>li>a').each(function(){
if(jQuery(this).parent().find('ul').length)
jQuery(this).attr({'class':'dropdown-toggle','data-toggle':'dropdown'}).append('<b class="caret"></b>');
});
jQuery('.nav li li').each(function(){
if(jQuery(this).find('ul').length)
jQuery(this).addClass('dropdown-submenu');
});
function hoverMenu(){
var wid = document.documentElement.clientWidth; //faster than $(window).width() and cross browser
if (wid>=980){
jQuery('ul.nav a.dropdown-toggle').parent().hover(function(){
mi = jQuery(this).addClass('open');
mi.children('.dropdown-menu').stop(true, true).delay(200).fadeIn(400);
}, function(){
mi = jQuery(this);
mi.children('.dropdown-menu').stop(true, true).delay(200).fadeOut(function(){mi.removeClass('open')});
});
};
}
hoverMenu(); // first page load
jQuery(window).resize(hoverMenu); // on resize event
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
// make all buttons bootstrap buttons
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn');
});
+10 -4
View File
@@ -27,7 +27,7 @@
{{pass}} {{pass}}
{{else:}} {{else:}}
{{qry=''}} {{qry=''}}
{{pass}} {{pass}}
{{pass}} {{pass}}
<tr> <tr>
<th style="font-size: 1.75em;"> <th style="font-size: 1.75em;">
@@ -37,13 +37,19 @@
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}} {{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
</td> </td>
</tr> </tr>
{{pass}}
</table>
{{pass}} {{pass}}
{{pass}}
</table>
{{elif request.function=='select':}} {{elif request.function=='select':}}
<h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }} <h2>{{=XML(str(T("Database %s select"))%A(request.args[0],_href=URL('index'))) }}
</h2> </h2>
{{if tb:}}
<h3>{{=T('Traceback')}}</h3>
<pre>
{{=tb}}
</pre>
{{pass}}
{{if table:}} {{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/> {{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/> <h3>{{=T("Rows in Table")}}</h3><br/>
@@ -149,7 +155,7 @@
<h4>{{=T("RAM")}}</h4> <h4>{{=T("RAM")}}</h4>
<p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p> <p>{{=T.M("Number of entries: **%s**", ram['entries'])}}</p>
{{if ram['entries'] > 0:}} {{if ram['entries'] > 0:}}
<p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %{hit(hits)} and **%(misses)s** %%{miss(misses)})", <p>{{=T.M("Hit Ratio: **%(ratio)s%%** (**%(hits)s** %%{hit(hits)} and **%(misses)s** %%{miss(misses)})",
dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}} dict( ratio=ram['ratio'], hits=ram['hits'], misses=ram['misses']))}}
</p> </p>
<p> <p>
+9 -5
View File
@@ -4,16 +4,20 @@
{{ {{
if request.args(0)=='login': if request.args(0)=='login':
if not 'register' in auth.settings.actions_disabled: if not 'register' in auth.settings.actions_disabled:
form.add_button(T('Register'),URL(args='register')) form.add_button(T('Register'),URL(args='register'),_class='btn')
pass pass
if not 'request_reset_password' in auth.settings.actions_disabled: if not 'request_reset_password' in auth.settings.actions_disabled:
form.add_button(T('Lost Password'),URL(args='request_reset_password')) form.add_button(T('Lost Password'),URL(args='request_reset_password'),_class='btn')
pass pass
pass pass
=form =form
}} }}
</div> </div>
<script language="javascript"><!-- <script language="javascript"><!--
jQuery("#web2py_user_form input:visible:enabled:first").focus(); jQuery("#web2py_user_form input:visible:enabled:first").focus();
//--></script> {{if request.args(0)=='register':}}
web2py_validate_entropy(jQuery('#auth_user_password'),100);
{{elif request.args(0)=='change_password':}}
web2py_validate_entropy(jQuery('#no_table_new_password'),100);
{{pass}}
//--></script>
+1 -1
View File
@@ -1 +1 @@
{{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}} <?xml version="1.0" encoding="ISO-8859-1"?>{{from gluon.serializers import xml}}{{=XML(xml(response._vars,quote=False))}}
+6 -28
View File
@@ -40,9 +40,9 @@
<!-- include stylesheets --> <!-- include stylesheets -->
{{ {{
response.files.append(URL('static','css/web2py.css'))
response.files.append(URL('static','css/bootstrap.min.css')) response.files.append(URL('static','css/bootstrap.min.css'))
response.files.append(URL('static','css/bootstrap-responsive.min.css')) response.files.append(URL('static','css/bootstrap-responsive.min.css'))
response.files.append(URL('static','css/web2py.css'))
response.files.append(URL('static','css/web2py_bootstrap.css')) response.files.append(URL('static','css/web2py_bootstrap.css'))
}} }}
@@ -66,17 +66,17 @@
<body> <body>
<!-- Navbar ================================================== --> <!-- Navbar ================================================== -->
<div class="navbar navbar-fixed-top"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="flash">{{=response.flash or ''}}</div> <div class="flash">{{=response.flash or ''}}</div>
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<!-- the next tag is necessary for bootstrap menus, do not remove --> <!-- the next tag is necessary for bootstrap menus, do not remove -->
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</a> </button>
<a class="brand" href="#">web2py&trade;&nbsp;</a> <a class="brand" href="http://www.web2py.com/"><b>web<span>2</span>py</b>&trade;&nbsp;</a>
<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}}
@@ -146,30 +146,8 @@
<!-- The javascript ============================================= <!-- The javascript =============================================
(Placed at the end of the document so the pages load faster) --> (Placed at the end of the document so the pages load faster) -->
<script>
// this code improves bootstrap menus and adds dropdown support
jQuery(function(){
jQuery('.nav>li>a').each(function(){
if(jQuery(this).parent().find('ul').length)
jQuery(this).attr({'class':'dropdown-toggle','data-toggle':'dropdown'}).append('<b class="caret"></b>');
});
jQuery('.nav li li').each(function(){
if(jQuery(this).find('ul').length)
jQuery(this).children('a').contents().before('<i class="chevron-right"></i>');
});
if(jQuery(document).width()>=980) {
jQuery('ul.nav li.dropdown').hover(function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn();
}, function() {
jQuery(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut();
});
}
jQuery('ul.nav li.dropdown a').click(function(){window.location=jQuery(this).attr('href');});
// make all buttons bootstrap buttons
jQuery('button, form input[type="submit"], form input[type="button"]').addClass('btn').css({'margin-right':'2px','margin-bottom':'2px'});
});
</script>
<script src="{{=URL('static','js/bootstrap.min.js')}}"></script> <script src="{{=URL('static','js/bootstrap.min.js')}}"></script>
<script src="{{=URL('static','js/web2py_bootstrap.js')}}"></script>
<!--[if lt IE 7 ]> <!--[if lt IE 7 ]>
<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>
+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 -9
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,14 +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()
from gluon.custom_import import custom_import_install web2py_path = global_settings.applications_parent # backward compatibility
web2py_path = global_settings.applications_parent # backward compatibility
custom_import_install(web2py_path)
return gluon.main.wsgibase(env, res) return gluon.main.wsgibase(env, res)
@@ -93,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 -26
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
@@ -60,7 +61,7 @@ def app_pack(app, request, raise_ex=False):
""" """
try: try:
app_cleanup(app, request) app_cleanup(app, request)
filename = apath('../deposit/%s.w2p' % app, request) filename = apath('../deposit/web2py.app.%s.w2p' % app, request)
w2p_pack(filename, apath(app, request)) w2p_pack(filename, apath(app, request))
return filename return filename
except Exception, e: except Exception, e:
@@ -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,17 @@ 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 ['models', 'views', 'controllers', 'databases',
'modules','cron','errors','sessions', 'modules', 'cron', 'errors', 'sessions',
'languages','static','private','uploads']: 'languages', 'static', 'private', 'uploads']:
subpath = os.path.join(path,subfolder) 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 +286,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 +306,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 +351,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 +382,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 +390,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 +400,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 +429,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 +450,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)
+138 -135
View File
@@ -11,7 +11,7 @@ Basic caching classes and methods
- Cache - The generic caching object interfacing with the others - Cache - The generic caching object interfacing with the others
- CacheInRam - providing caching in ram - CacheInRam - providing caching in ram
- CacheInDisk - provides caches on disk - CacheOnDisk - provides caches on disk
Memcache is also available via a different module (see gluon.contrib.memcache) Memcache is also available via a different module (see gluon.contrib.memcache)
@@ -40,12 +40,26 @@ __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.
Main function is now to provide referenced api documentation. Main function is now to provide referenced api documentation.
Use CacheInRam or CacheOnDisk instead which are derived from this class. Use CacheInRam or CacheOnDisk instead which are derived from this class.
Attentions, Michele says:
There are signatures inside gdbm files that are used directly
by the python gdbm adapter that often are lagging behind in the
detection code in python part.
On every occasion that a gdbm store is probed by the python adapter,
the probe fails, because gdbm file version is newer.
Using gdbm directly from C would work, because there is backward
compatibility, but not from python!
The .shelve file is discarded and a new one created (with new
signature) and it works until it is probed again...
The possible consequences are memory leaks and broken sessions.
""" """
cache_stats_name = 'web2py_cache_statistics' cache_stats_name = 'web2py_cache_statistics'
@@ -60,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`
@@ -117,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
@@ -130,22 +145,29 @@ class CacheInRam(CacheAbstract):
meta_storage = {} meta_storage = {}
def __init__(self, request=None): def __init__(self, request=None):
self.locker.acquire() self.initialized = False
self.request = request self.request = request
def initialize(self):
if self.initialized:
return
else:
self.initialized = True
self.locker.acquire()
request = self.request
if request: if request:
app = request.application app = request.application
else: else:
app = '' app = ''
if not app in self.meta_storage: if not app in self.meta_storage:
self.storage = self.meta_storage[app] = {CacheAbstract.cache_stats_name: { self.storage = self.meta_storage[app] = {
'hit_total': 0, CacheAbstract.cache_stats_name: {'hit_total': 0, 'misses': 0}}
'misses': 0,
}}
else: else:
self.storage = self.meta_storage[app] self.storage = self.meta_storage[app]
self.locker.release() self.locker.release()
def clear(self, regex=None): def clear(self, regex=None):
self.initialize()
self.locker.acquire() self.locker.acquire()
storage = self.storage storage = self.storage
if regex is None: if regex is None:
@@ -155,15 +177,13 @@ class CacheInRam(CacheAbstract):
if not CacheAbstract.cache_stats_name in storage.keys(): if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = { storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'hit_total': 0, 'misses': 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:
@@ -172,6 +192,7 @@ class CacheInRam(CacheAbstract):
3) would work unless we deepcopy no storage and retrival which would make things slow. 3) would work unless we deepcopy no storage and retrival which would make things slow.
Anyway. You can deepcopy explicitly in the function generating the value to be cached. Anyway. You can deepcopy explicitly in the function generating the value to be cached.
""" """
self.initialize()
dt = time_expire dt = time_expire
now = time.time() now = time.time()
@@ -200,6 +221,7 @@ class CacheInRam(CacheAbstract):
return value return value
def increment(self, key, value=1): def increment(self, key, value=1):
self.initialize()
self.locker.acquire() self.locker.acquire()
try: try:
if key in self.storage: if key in self.storage:
@@ -226,58 +248,69 @@ class CacheOnDisk(CacheAbstract):
Values stored in disk cache must be pickable. Values stored in disk cache must be pickable.
""" """
speedup_checks = set() def _close_shelve_and_unlock(self):
try:
if self.storage:
self.storage.close()
finally:
if self.locker and self.locked:
portalocker.unlock(self.locker)
self.locker.close()
self.locked = False
def _open_shelf_with_lock(self): def _open_shelve_and_lock(self):
"""Open and return a shelf object, obtaining an exclusive lock """Open and return a shelf object, obtaining an exclusive lock
on self.locker first. Replaces the close method of the on self.locker first. Replaces the close method of the
returned shelf instance with one that releases the lock upon returned shelf instance with one that releases the lock upon
closing.""" closing."""
def _close(self):
try:
shelve.Shelf.close(self)
finally:
portalocker.unlock(self.locker)
self.locker.close()
storage, locker, locker_locked = None, None, False storage = None
locker = None
locked = False
try: try:
locker = open(self.locker_name, 'a') locker = locker = open(self.locker_name, 'a')
portalocker.lock(locker, portalocker.LOCK_EX) portalocker.lock(locker, portalocker.LOCK_EX)
locker_locked = True locked = True
storage = shelve.open(self.shelve_name) try:
storage.close = _close.__get__(storage, shelve.Shelf) storage = shelve.open(self.shelve_name)
storage.locker = locker except:
except Exception: logger.error('corrupted cache file %s, will try rebuild it'
logger.error('corrupted cache file %s, will try to delete and recreate it!' % (self.shelve_name)) % (self.shelve_name))
storage = None
if not storage and os.path.exists(self.shelve_name):
os.unlink(self.shelve_name)
storage = shelve.open(self.shelve_name)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0, 'misses': 0}
storage.sync()
except Exception, e:
if storage: if storage:
storage.close() storage.close()
storage = None storage = None
if locked:
try: portalocker.unlock(locker)
os.unlink(self.shelve_name) locker.close()
storage = shelve.open(self.shelve_name) locked = False
storage.close = _close.__get__(storage, shelve.Shelf) raise RuntimeError(
storage.locker = locker 'unable to create/re-create cache file %s' % self.shelve_name)
if not CacheAbstract.cache_stats_name in storage.keys(): self.locker = locker
storage[CacheAbstract.cache_stats_name] = { self.locked = locked
'hit_total': 0, self.storage = storage
'misses': 0,
}
storage.sync()
except (IOError, OSError):
logger.warn('unable to delete and recreate cache file %s' % self.shelve_name)
if storage:
storage.close()
storage = None
if locker_locked:
portalocker.unlock(locker)
if locker:
locker.close()
return storage return storage
def __init__(self, request, folder=None): def __init__(self, request=None, folder=None):
self.initialized = False
self.request = request self.request = request
self.folder = folder
def initialize(self):
if self.initialized:
return
else:
self.initialized = True
folder = self.folder
request = self.request
# Lets test if the cache folder exists, if not # Lets test if the cache folder exists, if not
# we are going to create it # we are going to create it
@@ -288,100 +321,62 @@ 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')
speedup_key = (folder,CacheAbstract.cache_stats_name)
if not speedup_key in self.speedup_checks or \
not os.path.exists(self.shelve_name):
try:
storage = self._open_shelf_with_lock()
try:
if not CacheAbstract.cache_stats_name in storage:
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0,
'misses': 0,
}
storage.sync()
finally:
storage.close()
self.speedup_checks.add(speedup_key)
except ImportError:
pass # no module _bsddb, ignoring exception now so it makes a ticket only if used
def clear(self, regex=None): def clear(self, regex=None):
storage = self._open_shelf_with_lock() self.initialize()
storage = self._open_shelve_and_lock()
try: try:
if regex is None: if regex is None:
storage.clear() storage.clear()
else: else:
self._clear(storage, regex) self._clear(storage, regex)
if not CacheAbstract.cache_stats_name in storage.keys():
storage[CacheAbstract.cache_stats_name] = {
'hit_total': 0,
'misses': 0,
}
storage.sync() storage.sync()
finally: finally:
storage.close() 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()
dt = time_expire dt = time_expire
storage = self._open_shelve_and_lock()
storage = self._open_shelf_with_lock()
try: try:
item = storage.get(key, None) item = storage.get(key, None)
storage[CacheAbstract.cache_stats_name]['hit_total'] += 1
if item and f is None: if item and f is None:
del storage[key] del storage[key]
storage.sync()
storage[CacheAbstract.cache_stats_name] = { now = time.time()
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'] + 1, if f is None:
'misses': storage[CacheAbstract.cache_stats_name]['misses'] value = None
} elif item and (dt is None or item[0] > now - dt):
value = item[1]
storage.sync() else:
value = f()
storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name]['misses'] += 1
storage.sync()
finally: finally:
if storage: self._close_shelve_and_unlock()
storage.close()
now = time.time()
if f is None:
return None
if item and (dt is None or item[0] > now - dt):
return item[1]
value = f()
storage = self._open_shelf_with_lock()
try:
storage[key] = (now, value)
storage[CacheAbstract.cache_stats_name] = {
'hit_total': storage[CacheAbstract.cache_stats_name]['hit_total'],
'misses': storage[CacheAbstract.cache_stats_name]['misses'] + 1
}
storage.sync()
finally:
if storage:
storage.close()
return value return value
def increment(self, key, value=1): def increment(self, key, value=1):
storage = self._open_shelf_with_lock() self.initialize()
storage = self._open_shelve_and_lock()
try: try:
if key in storage: if key in storage:
value = storage[key][1] + value value = storage[key][1] + value
storage[key] = (time.time(), value) storage[key] = (time.time(), value)
storage.sync() storage.sync()
finally: finally:
if storage: self._close_shelve_and_unlock()
storage.close()
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
@@ -389,17 +384,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)
@@ -423,10 +419,9 @@ class Cache(object):
the global request object the global request object
""" """
# GAE will have a special caching # GAE will have a special caching
if have_settings and settings.global_settings.web2py_runtime_gae: if have_settings and settings.global_settings.web2py_runtime_gae:
from contrib.gae_memcache import MemcacheClient from contrib.gae_memcache import MemcacheClient
self.ram=self.disk=MemcacheClient(request) self.ram = self.disk = MemcacheClient(request)
else: else:
# Otherwise use ram (and try also disk) # Otherwise use ram (and try also disk)
self.ram = CacheInRam(request) self.ram = CacheInRam(request)
@@ -440,9 +435,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.
@@ -475,11 +470,21 @@ 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
def lazy_cache(key=None,time_expire=None,cache_model='ram'): @staticmethod
def with_prefix(cache_model, prefix):
"""
allow replacing cache.ram with cache.with_prefix(cache.ram,'prefix')
it will add prefix to all the cache keys used.
"""
return lambda key, f, time_expire=DEFAULT_TIME_EXPIRE, prefix=prefix:\
cache_model(prefix + key, f, time_expire)
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
@@ -487,14 +492,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
+105 -93
View File
@@ -40,6 +40,7 @@ import imp
import logging import logging
logger = logging.getLogger("web2py") logger = logging.getLogger("web2py")
import rewrite import rewrite
from custom_import import custom_import_install
try: try:
import py_compile import py_compile
@@ -47,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
@@ -94,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]
@@ -103,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,
@@ -113,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:
@@ -133,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,
@@ -159,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)
@@ -185,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 = \
@@ -217,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
@@ -265,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 = \
@@ -298,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'):
@@ -320,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:]:
@@ -354,26 +362,43 @@ 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_.update(
(k, getattr(validators, k)) for k in validators.__all__)
_base_environment_['__builtins__'] = __builtins__
_base_environment_['HTTP'] = HTTP
_base_environment_['redirect'] = redirect
_base_environment_['DAL'] = DAL
_base_environment_['Field'] = Field
_base_environment_['SQLDB'] = SQLDB # for backward compatibility
_base_environment_['SQLField'] = SQLField # for backward compatibility
_base_environment_['SQLFORM'] = SQLFORM
_base_environment_['SQLTABLE'] = SQLTABLE
_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.
""" """
h,v = html,validators #h,v = html,validators
environment = dict((k,getattr(h,k)) for k in h.__all__) environment = dict(_base_environment_)
environment.update((k,getattr(v, k)) for k in v.__all__)
if not request.env: if not request.env:
request.env = Storage() request.env = Storage()
# 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(request)
c = environment['cache'] = Cache(request) c = environment['cache'] = Cache(request)
if store_current: if store_current:
current.globalenv = environment current.globalenv = environment
current.request = request current.request = request
@@ -383,30 +408,21 @@ 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['__builtins__'] = __builtins__
environment['HTTP'] = HTTP
environment['redirect'] = redirect
environment['request'] = request environment['request'] = request
environment['response'] = response environment['response'] = response
environment['session'] = session environment['session'] = session
environment['DAL'] = DAL
environment['Field'] = Field
environment['SQLDB'] = SQLDB # for backward compatibility
environment['SQLField'] = SQLField # for backward compatibility
environment['SQLFORM'] = SQLFORM
environment['SQLTABLE'] = SQLTABLE
environment['LOAD'] = LOAD
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()
return environment return environment
@@ -426,7 +442,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:])
@@ -437,7 +453,10 @@ 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)
@@ -453,7 +472,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)
@@ -468,14 +487,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)
@@ -495,19 +514,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, sort=False)
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, sort=False)
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)
@@ -527,24 +546,24 @@ def run_controller_in(controller, function, environment):
""" """
# if compiled should run compiled! # if compiled should run compiled!
folder = environment['request'].folder folder = environment['request'].folder
path = pjoin(folder, 'compiled') path = pjoin(folder, 'compiled')
badc = 'invalid controller (%s/%s)' % (controller, function) badc = 'invalid controller (%s/%s)' % (controller, function)
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.routes.error_message % badf, rewrite.THREAD_LOCAL.routes.error_message % badf,
web2py_error=badf) web2py_error=badf)
restricted(read_pyc(filename), environment, layer=filename) restricted(read_pyc(filename), environment, layer=filename)
elif function == '_TEST': elif function == '_TEST':
# 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
@@ -552,7 +571,7 @@ def run_controller_in(controller, function, environment):
% controller) % controller)
if not os.path.exists(filename): if not os.path.exists(filename):
raise HTTP(404, raise HTTP(404,
rewrite.thread.routes.error_message % badc, rewrite.THREAD_LOCAL.routes.error_message % badc,
web2py_error=badc) web2py_error=badc)
environment['__symbols__'] = environment.keys() environment['__symbols__'] = environment.keys()
code = read_file(filename) code = read_file(filename)
@@ -563,30 +582,30 @@ def run_controller_in(controller, function, environment):
% controller) % controller)
if not os.path.exists(filename): if not os.path.exists(filename):
raise HTTP(404, raise HTTP(404,
rewrite.thread.routes.error_message % badc, rewrite.THREAD_LOCAL.routes.error_message % badc,
web2py_error=badc) web2py_error=badc)
code = read_file(filename) code = read_file(filename)
exposed = regex_expose.findall(code) exposed = regex_expose.findall(code)
if not function in exposed: if not function in exposed:
raise HTTP(404, raise HTTP(404,
rewrite.thread.routes.error_message % badf, rewrite.THREAD_LOCAL.routes.error_message % badf,
web2py_error=badf) web2py_error=badf)
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:
for p in response.postprocessing: vars = reduce(lambda vars, p: p(vars), response.postprocessing, vars)
vars = p(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.
@@ -594,7 +613,6 @@ def run_view_in(environment):
or `view/generic.extension` or `view/generic.extension`
It tries the pre-compiled views_controller_function.pyc before compiling it. It tries the pre-compiled views_controller_function.pyc before compiling it.
""" """
request = environment['request'] request = environment['request']
response = environment['response'] response = environment['response']
view = response.view view = response.view
@@ -604,7 +622,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
@@ -624,13 +642,13 @@ 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)
return return
raise HTTP(404, raise HTTP(404,
rewrite.thread.routes.error_message % badv, rewrite.THREAD_LOCAL.routes.error_message % badv,
web2py_error=badv) web2py_error=badv)
else: else:
filename = pjoin(folder, 'views', view) filename = pjoin(folder, 'views', view)
@@ -639,20 +657,21 @@ def run_view_in(environment):
filename = pjoin(folder, 'views', view) filename = pjoin(folder, 'views', view)
if not os.path.exists(filename): if not os.path.exists(filename):
raise HTTP(404, raise HTTP(404,
rewrite.thread.routes.error_message % badv, rewrite.THREAD_LOCAL.routes.error_message % badv,
web2py_error=badv) web2py_error=badv)
layer = filename layer = filename
if is_gae: if is_gae:
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.
@@ -660,7 +679,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
@@ -698,10 +717,3 @@ def test():
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()

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