Hi
I tried to create a plugin and then delete the one file that was in
the plugin. Then I recieves an error ticket shown below.
I created a file with name plugin_init/default/index.html and then
clicked on the init plugin in the bottom of the init screen, looked at
the file and then deleted it.
Then it crashes and I think it has something do with that the plugin
disappears when the last file has been deleted but the admin still
tries to show all files that contains to the non existing plugin.
Br.
Mr. Electronic
Ticket 83.89.69.186.2010-11-06.11-11-12.9954ddbe-b9e6-4424-93d4-
ed8ea601a010
Version
web2py Version 1.88.2 (2010-10-29 23:04:43)
Error traceback
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
Traceback (most recent call last):
File "/home/6dstudio/web2py/gluon/restricted.py", line 188, in
restricted
exec ccode in environment
File "/home/6dstudio/web2py/applications/admin/controllers/
default.py", line 1134, in <module>
File "/home/6dstudio/web2py/gluon/globals.py", line 96, in <lambda>
self._caller = lambda f: f()
File "/home/6dstudio/web2py/applications/admin/controllers/
default.py", line 807, in plugin
models=filter_plugins(models),
File "/home/6dstudio/web2py/applications/admin/controllers/
default.py", line 803, in filter_plugins
regex=re.compile('^plugin_'+plugin+'(/.*|\..*)?$')
TypeError: cannot concatenate 'str' and 'NoneType' objects
Error snapshot
Detailed traceback description
* Exception: <type 'exceptions.TypeError'>(cannot concatenate
'str' and 'NoneType' objects)
Exception instance attributes
o __setattr__: <method-wrapper '__setattr__' of
exceptions.TypeError object>
o __reduce_ex__: <built-in method __reduce_ex__ of
exceptions.TypeError object>
o __getslice__: <method-wrapper '__getslice__' of
exceptions.TypeError object>
o __getitem__: <method-wrapper '__getitem__' of
exceptions.TypeError object>
o __setstate__: <built-in method __setstate__ of
exceptions.TypeError object>
o __getattribute__: <method-wrapper '__getattribute__' of
exceptions.TypeError object>
o __str__: <method-wrapper '__str__' of exceptions.TypeError
object>
o args: ("cannot concatenate 'str' and 'NoneType' objects",)
o __reduce__: <built-in method __reduce__ of
exceptions.TypeError object>
o __format__: <built-in method __format__ of
exceptions.TypeError object>
o __class__: <type 'exceptions.TypeError'>
o __dict__: {}
o __delattr__: <method-wrapper '__delattr__' of
exceptions.TypeError object>
o __subclasshook__: <built-in method __subclasshook__ of
type object>
o __repr__: <method-wrapper '__repr__' of
exceptions.TypeError object>
o __init__: <method-wrapper '__init__' of
exceptions.TypeError object>
o __hash__: <method-wrapper '__hash__' of
exceptions.TypeError object>
o __sizeof__: <built-in method __sizeof__ of
exceptions.TypeError object>
o __doc__: 'Inappropriate argument type.'
o __unicode__: <built-in method __unicode__ of
exceptions.TypeError object>
o __new__: <built-in method __new__ of type object>
* Python 2.6.5: /usr/bin/python
File /home/6dstudio/web2py/gluon/restricted.py in restricted at line
188
[ code | arguments | variables ]
Function argument list: (code='# coding: utf8 \n\nfrom gluon.admin
import *\nfrom ...r(e)))\n\n\nresponse._vars=response._caller(plugin)
\n', environment={'A': <class 'gluon.html.A'>, 'ALLOW_TOGGLE': 'true',
'B': <class 'gluon.html.B'>, 'BEAUTIFY': <class
'gluon.html.BEAUTIFY'>, 'BODY': <class 'gluon.html.BODY'>, 'BR':
<class 'gluon.html.BR'>, 'CENTER': <class 'gluon.html.CENTER'>,
'CHECK_VERSION': True, 'CLEANUP': <class 'gluon.validators.CLEANUP'>,
'CODE': <class 'gluon.html.CODE'>, ...}, layer='/home/6dstudio/web2py/
applications/admin/controllers/default.py')
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
if type(code) == types.CodeType:
ccode = code
else:
ccode = compile2(code,layer)
exec ccode in environment
except HTTP:
raise
except Exception:
# XXX Show exception in Wing IDE if running in debugger
* environment: {'A': <class 'gluon.html.A'>, 'ALLOW_TOGGLE':
'true', 'B': <class 'gluon.html.B'>, 'BEAUTIFY': <class
'gluon.html.BEAUTIFY'>, 'BODY': <class 'gluon.html.BODY'>, 'BR':
<class 'gluon.html.BR'>, 'CENTER': <class 'gluon.html.CENTER'>,
'CHECK_VERSION': True, 'CLEANUP': <class 'gluon.validators.CLEANUP'>,
'CODE': <class 'gluon.html.CODE'>, ...}
* ccode: <code object <module> at 0x7f8b441c9e40, file
"/...plications/admin/controllers/default.py", line 3>
File /home/6dstudio/web2py/applications/admin/controllers/default.py
in <module> at line 1134
[ code | arguments | variables ]
Function argument list: ()
1123.
1124.
1125.
1126.
1127.
1128.
1129.
1130.
1131.
1132.
session.forget()
session._unlock(response)
import gluon.tools
import gluon.contrib.simplejson as sj
try:
page = gluon.tools.fetch('http://twitter.com/web2py?
format=json')
return sj.loads(page)['#timeline']
except Exception, e:
return DIV(T('Unable to download because'),PRE(str(e)))
File /home/6dstudio/web2py/gluon/globals.py in <lambda> at line 96
[ code | arguments | variables ]
Function argument list: (f=<function plugin>)
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
self.flash = '' # used by the default view layout
self.meta = Storage() # used by web2py_ajax.html
self.menu = [] # used by the default view layout
self.files = [] # used by web2py_ajax.html
self._vars = None
self._caller = lambda f: f()
self._view_environment = None
self._custom_commit = None
self._custom_rollback = None
* self: undefined
* f: <function plugin>
File /home/6dstudio/web2py/applications/admin/controllers/default.py
in plugin at line 807
[ code | arguments | variables ]
Function argument list: ()
802.
803.
804.
805.
806.
807.
808.
809.
810.
811.
def filter_plugins(items):
regex=re.compile('^plugin_'+plugin+'(/.*|\..*)?$')
return [item for item in items if regex.match(item)]
return dict(app=app,
models=filter_plugins(models),
defines=defines,
controllers=filter_plugins(controllers),
functions=functions,
views=filter_plugins(views),
* models: []
* filter_plugins: <function filter_plugins>
File /home/6dstudio/web2py/applications/admin/controllers/default.py
in filter_plugins at line 803
[ code | arguments | variables ]
Function argument list: (items=[])
798.
799.
800.
801.
802.
803.
804.
805.
806.
807.
if not os.path.exists(crontab):
safe_open(crontab,'w').write('#crontab')
def filter_plugins(items):
regex=re.compile('^plugin_'+plugin+'(/.*|\..*)?$')
return [item for item in items if regex.match(item)]
return dict(app=app,
models=filter_plugins(models),
* regex: undefined
* global re: <module 're' from '/usr/lib/python2.6/re.pyc'>
* re.compile: <function compile>
* plugin: None
locals
locals
items :
[]
plugin :
None
request
request
ajax :
False
application :
'admin'
args :
['init']
body :
<cStringIO.StringO object>
cid :
None
client :
'83.89.69.186'
controller :
'default'
cookies :
<SimpleCookie: session_id_admin='83-89-69-186-8f...-89-69-186-3db8d937-
c1ef-4c43-931f-54e267fb56a9'>
env :
<Storage {'mod_wsgi_listener_host': '', 'mod_wsg...t',
'mod_wsgi_process_group': '6dstudio-web2py'}>
extension :
'html'
folder :
'/home/6dstudio/web2py/applications/admin/'
function :
'plugin'
get_vars :
<Storage {}>
now :
datetime.datetime(2010, 11, 6, 11, 11, 12, 5040)
post_vars :
<Storage {}>
raw_args :
'init'
url :
<gluon.html.XML object>
vars :
<Storage {}>
wsgi :
<Storage {'start_response': <function <lambda> a...te', 'PATH_INFO': '/
admin/default/plugin/init'}}>
session
session
authorized :
True
check_version :
False
flash :
None
last_time :
1289038272.010576
pam_user :
None
response
response
body :
<cStringIO.StringO object>
cookies :
<SimpleCookie: session_id_admin='83-89-69-186-8f06e290-c2a4-42ed-
a212-52b611cf8ff4'>
files :
[]
flash :
'file "init/views/plugin_init/default/index.html" deleted'
headers :
<Storage {'Expires': 'Sat, 06 Nov 2010 10:11:12 ...he, must-
revalidate, post-check=0, pre-check=0'}>
menu :
[(<lazyT 'site'>, False, <gluon.html.XML object>), (<lazyT 'edit'>,
False, <gluon.html.XML object>), (<lazyT 'about'>, False,
<gluon.html.XML object>), (<lazyT 'errors'>, False, <gluon.html.XML
object>), (<lazyT 'logout'>, False, <gluon.html.XML object>), (<lazyT
'help'>, False, <gluon.html.XML object>)]
meta :
<Storage {}>
postprocessing :
[]
session_file :
<open file '/home/6dstudio/web2py/applications/a...f06e290-c2a4-42ed-
a212-52b611cf8ff4', mode 'rb+'>
session_filename :
'/home/6dstudio/web2py/applications/admin/sessions/
83-89-69-186-8f06e290-c2a4-42ed-a212-52b611cf8ff4'
session_id :
'83-89-69-186-8f06e290-c2a4-42ed-a212-52b611cf8ff4'
session_id_name :
'session_id_admin'
status :
200
subtitle :
'admin'
title :
'plugin init'
view :
'default/plugin.html'
In file: /home/6dstudio/web2py/applications/admin/controllers/
default.py
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
411.
412.
413.
414.
415.
416.
417.
418.
419.
420.
421.
422.
423.
424.
425.
426.
427.
428.
429.
430.
431.
432.
433.
434.
435.
436.
437.
438.
439.
440.
441.
442.
443.
444.
445.
446.
447.
448.
449.
450.
451.
452.
453.
454.
455.
456.
457.
458.
459.
460.
461.
462.
463.
464.
465.
466.
467.
468.
469.
470.
471.
472.
473.
474.
475.
476.
477.
478.
479.
480.
481.
482.
483.
484.
485.
486.
487.
488.
489.
490.
491.
492.
493.
494.
495.
496.
497.
498.
499.
500.
501.
502.
503.
504.
505.
506.
507.
508.
509.
510.
511.
512.
513.
514.
515.
516.
517.
518.
519.
520.
521.
522.
523.
524.
525.
526.
527.
528.
529.
530.
531.
532.
533.
534.
535.
536.
537.
538.
539.
540.
541.
542.
543.
544.
545.
546.
547.
548.
549.
550.
551.
552.
553.
554.
555.
556.
557.
558.
559.
560.
561.
562.
563.
564.
565.
566.
567.
568.
569.
570.
571.
572.
573.
574.
575.
576.
577.
578.
579.
580.
581.
582.
583.
584.
585.
586.
587.
588.
589.
590.
591.
592.
593.
594.
595.
596.
597.
598.
599.
600.
601.
602.
603.
604.
605.
606.
607.
608.
609.
610.
611.
612.
613.
614.
615.
616.
617.
618.
619.
620.
621.
622.
623.
624.
625.
626.
627.
628.
629.
630.
631.
632.
633.
634.
635.
636.
637.
638.
639.
640.
641.
642.
643.
644.
645.
646.
647.
648.
649.
650.
651.
652.
653.
654.
655.
656.
657.
658.
659.
660.
661.
662.
663.
664.
665.
666.
667.
668.
669.
670.
671.
672.
673.
674.
675.
676.
677.
678.
679.
680.
681.
682.
683.
684.
685.
686.
687.
688.
689.
690.
691.
692.
693.
694.
695.
696.
697.
698.
699.
700.
701.
702.
703.
704.
705.
706.
707.
708.
709.
710.
711.
712.
713.
714.
715.
716.
717.
718.
719.
720.
721.
722.
723.
724.
725.
726.
727.
728.
729.
730.
731.
732.
733.
734.
735.
736.
737.
738.
739.
740.
741.
742.
743.
744.
745.
746.
747.
748.
749.
750.
751.
752.
753.
754.
755.
756.
757.
758.
759.
760.
761.
762.
763.
764.
765.
766.
767.
768.
769.
770.
771.
772.
773.
774.
775.
776.
777.
778.
779.
780.
781.
782.
783.
784.
785.
786.
787.
788.
789.
790.
791.
792.
793.
794.
795.
796.
797.
798.
799.
800.
801.
802.
803.
804.
805.
806.
807.
808.
809.
810.
811.
812.
813.
814.
815.
816.
817.
818.
819.
820.
821.
822.
823.
824.
825.
826.
827.
828.
829.
830.
831.
832.
833.
834.
835.
836.
837.
838.
839.
840.
841.
842.
843.
844.
845.
846.
847.
848.
849.
850.
851.
852.
853.
854.
855.
856.
857.
858.
859.
860.
861.
862.
863.
864.
865.
866.
867.
868.
869.
870.
871.
872.
873.
874.
875.
876.
877.
878.
879.
880.
881.
882.
883.
884.
885.
886.
887.
888.
889.
890.
891.
892.
893.
894.
895.
896.
897.
898.
899.
900.
901.
902.
903.
904.
905.
906.
907.
908.
909.
910.
911.
912.
913.
914.
915.
916.
917.
918.
919.
920.
921.
922.
923.
924.
925.
926.
927.
928.
929.
930.
931.
932.
933.
934.
935.
936.
937.
938.
939.
940.
941.
942.
943.
944.
945.
946.
947.
948.
949.
950.
951.
952.
953.
954.
955.
956.
957.
958.
959.
960.
961.
962.
963.
964.
965.
966.
967.
968.
969.
970.
971.
972.
973.
974.
975.
976.
977.
978.
979.
980.
981.
982.
983.
984.
985.
986.
987.
988.
989.
990.
991.
992.
993.
994.
995.
996.
997.
998.
999.
1000.
1001.
1002.
1003.
1004.
1005.
1006.
1007.
1008.
1009.
1010.
1011.
1012.
1013.
1014.
1015.
1016.
1017.
1018.
1019.
1020.
1021.
1022.
1023.
1024.
1025.
1026.
1027.
1028.
1029.
1030.
1031.
1032.
1033.
1034.
1035.
1036.
1037.
1038.
1039.
1040.
1041.
1042.
1043.
1044.
1045.
1046.
1047.
1048.
1049.
1050.
1051.
1052.
1053.
1054.
1055.
1056.
1057.
1058.
1059.
1060.
1061.
1062.
1063.
1064.
1065.
1066.
1067.
1068.
1069.
1070.
1071.
1072.
1073.
1074.
1075.
1076.
1077.
1078.
1079.
1080.
1081.
1082.
1083.
1084.
1085.
1086.
1087.
1088.
1089.
1090.
1091.
1092.
1093.
1094.
1095.
1096.
1097.
1098.
1099.
1100.
1101.
1102.
1103.
1104.
1105.
1106.
1107.
1108.
1109.
1110.
1111.
1112.
1113.
1114.
1115.
1116.
1117.
1118.
1119.
1120.
1121.
1122.
1123.
1124.
1125.
1126.
1127.
1128.
1129.
1130.
1131.
1132.
1133.
1134.
1135.
# coding: utf8
from gluon.admin import *
from glob import glob
import shutil
if DEMO_MODE and request.function in
['change_password','pack','pack_plugin','upgrade_web2py','uninstall','cleanup','compile_app','remove_compiled_app','delete','delete_plugin','create_file','upload_file','errors','update_languages']:
session.flash = T('disabled in demo mode')
redirect(URL('site'))
if FILTER_APPS and request.args(0) and not request.args(0) in
FILTER_APPS:
session.flash = T('disabled in demo mode')
redirect(URL('site'))
def safe_open(a,b):
if DEMO_MODE and 'w' in b:
class tmp:
def write(self,data): pass
return tmp()
return open(a,b)
def index():
""" Index handler """
send = request.vars.send
if not send:
send = URL('site')
if session.authorized:
redirect(send)
elif request.vars.password:
if verify_password(request.vars.password):
session.authorized = True
if CHECK_VERSION:
session.check_version = True
else:
session.check_version = False
session.last_time = t0
if isinstance(send, list): # ## why does this happen?
send = str(send[0])
redirect(send)
else:
response.flash = T('invalid password')
if DEMO_MODE:
response.flash = T('Demo mode: any random passoword will get
you in')
# f == file
apps = [f for f in os.listdir(apath(r=request)) if f.find('.') <
0]
return dict(apps=apps, send=send)
def check_version():
""" Checks if web2py is up to date """
session.forget()
session._unlock(response)
new_version, version_number =
check_new_version(request.env.web2py_version,
WEB2PY_VERSION_URL)
if new_version == -1:
return A(T('Unable to check for upgrades'), _href=WEB2PY_URL)
elif new_version == True:
return A(T('A new version of web2py is available: %s'
% version_number),
_href=WEB2PY_URL)
else:
return A(T('web2py is up to date'), _href=WEB2PY_URL)
def logout():
""" Logout handler """
session.authorized = None
redirect(URL('index'))
def change_password():
if session.pam_user:
session.flash = T('PAM authenticated user, cannot change
password here')
redirect(URL('site'))
form=SQLFORM.factory(Field('current_admin_password','password'),
Field('new_admin_password','password',requires=IS_STRONG()),
Field('new_admin_password_again','password'))
if form.accepts(request.vars):
if not verify_password(request.vars.current_admin_password):
form.errors.current_admin_password = T('invalid password')
elif form.vars.new_admin_password !=
form.vars.new_admin_password_again:
form.errors.new_admin_password_again = T('no match')
else:
path =
os.path.join(request.env.applications_path,'parameters_%s.py' %
request.env.server_port)
safe_open(path,'w').write('password="%s"' % CRYPT()
(request.vars.new_admin_password)[0])
session.flash = T('password changed')
redirect(URL('site'))
return dict(form=form)
def site():
""" Site handler """
myversion = request.env.web2py_version
# Shortcut to make the elif statements more legible
file_or_appurl = 'file' in request.vars or 'appurl' in
request.vars
if DEMO_MODE:
pass
elif request.vars.filename and not 'file' in request.vars:
# create a new application
appname = cleanpath(request.vars.filename).replace('.', '_')
if app_create(appname, request):
session.flash = T('new application "%s" created', appname)
redirect(URL('design',args=appname))
else:
session.flash = \
T('unable to create application "%s"',
request.vars.filename)
redirect(URL(r=request))
elif file_or_appurl and not request.vars.filename:
# can't do anything without an app name
msg = 'you must specify a name for the uploaded application'
response.flash = T(msg)
elif file_or_appurl and request.vars.filename:
# fetch an application via URL or file upload
if request.vars.appurl is not '':
try:
f = urllib.urlopen(request.vars.appurl)
except Exception, e:
session.flash = DIV(T('Unable to download app
because:'),PRE(str(e)))
redirect(URL(r=request))
fname = request.vars.appurl
elif request.vars.file is not '':
f = request.vars.file.file
fname = request.vars.file.filename
appname = cleanpath(request.vars.filename).replace('.', '_')
installed = app_install(appname, f, request, fname,
overwrite=request.vars.overwrite_check)
if installed:
msg = 'application %(appname)s installed with md5sum: %
(digest)s'
session.flash = T(msg, dict(appname=appname,
digest=md5_hash(installed)))
elif request.vars.overwrite_check:
msg = 'unable to install application "%(appname)s"'
session.flash = T(msg,
dict(appname=request.vars.filename))
else:
msg = 'unable to install application "%(appname)s"'
session.flash = T(msg,
dict(appname=request.vars.filename))
redirect(URL(r=request))
regex = re.compile('^\w+$')
apps = sorted([(f.upper(), f) for f in
os.listdir(apath(r=request)) \
if regex.match(f)])
apps = [item[1] for item in apps]
if FILTER_APPS:
apps = [f for f in apps if f in FILTER_APPS]
return dict(app=None, apps=apps, myversion=myversion)
def pack():
if len(request.args) == 1:
fname = 'web2py.app.%s.w2p' % request.args[0]
filename = app_pack(request.args[0], request)
else:
fname = 'web2py.app.%s.compiled.w2p' % request.args[0]
filename = app_pack_compiled(request.args[0], request)
if filename:
response.headers['Content-Type'] = 'application/w2p'
disposition = 'attachment; filename=%s' % fname
response.headers['Content-Disposition'] = disposition
return safe_open(filename, 'rb').read()
else:
session.flash = T('internal error')
redirect(URL('site'))
def pack_plugin():
if len(request.args) == 2:
fname = 'web2py.plugin.%s.w2p' % request.args[1]
filename = plugin_pack(request.args[0], request.args[1],
request)
if filename:
response.headers['Content-Type'] = 'application/w2p'
disposition = 'attachment; filename=%s' % fname
response.headers['Content-Disposition'] = disposition
return safe_open(filename, 'rb').read()
else:
session.flash = T('internal error')
redirect(URL('plugin',args=request.args))
def upgrade_web2py():
if 'upgrade' in request.vars:
(success, error) = upgrade(request)
if success:
session.flash = T('web2py upgraded; please restart it')
else:
session.flash = T('unable to upgrade because "%s"', error)
redirect(URL('site'))
elif 'noupgrade' in request.vars:
redirect(URL('site'))
return dict()
def uninstall():
app = request.args[0]
if 'delete' in request.vars:
deleted = app_uninstall(app, request)
if deleted:
session.flash = T('application "%s" uninstalled', app)
else:
session.flash = T('unable to uninstall "%s"', app)
redirect(URL('site'))
elif 'nodelete' in request.vars:
redirect(URL('site'))
return dict(app=app)
def cleanup():
clean = app_cleanup(request.args[0], request)
if not clean:
session.flash = T("some files could not be removed")
else:
session.flash = T('cache, errors and sessions cleaned')
redirect(URL('site'))
def compile_app():
c = app_compile(request.args[0], request)
if not c:
session.flash = T('application compiled')
else:
session.flash = DIV(T('Cannot compile: there are errors in
your app:',
CODE(c)))
redirect(URL('site'))
def remove_compiled_app():
""" Remove the compiled application """
remove_compiled_application(apath(request.args[0], r=request))
session.flash = T('compiled application removed')
redirect(URL('site'))
def delete():
""" Object delete handler """
filename = '/'.join(request.args)
sender = request.vars.sender
if isinstance(sender, list): # ## fix a problem with Vista
sender = sender[0]
if 'nodelete' in request.vars:
redirect(URL(sender))
elif 'delete' in request.vars:
try:
os.unlink(apath(filename, r=request))
session.flash = T('file "%(filename)s" deleted',
dict(filename=filename))
except Exception:
session.flash = T('unable to delete file "%(filename)s"',
dict(filename=filename))
redirect(URL(sender))
return dict(filename=filename, sender=sender)
def peek():
""" Visualize object code """
filename = '/'.join(request.args)
try:
data = safe_open(apath(filename, r=request),
'r').read().replace('\r','')
except IOError:
session.flash = T('file does not exist')
redirect(URL('site'))
extension = filename[filename.rfind('.') + 1:].lower()
return dict(app=request.args[0],
filename=filename,
data=data,
extension=extension)
def test():
""" Execute controller tests """
app = request.args[0]
if len(request.args) > 1:
file = request.args[1]
else:
file = '.*\.py'
controllers = listdir(apath('%s/controllers/' % app, r=request),
file + '$')
return dict(app=app, controllers=controllers)
def keepalive():
return ''
def edit():
""" File edit handler """
# Load json only if it is ajax edited...
filename = '/'.join(request.args)
# Try to discover the file type
if filename[-3:] == '.py':
filetype = 'python'
elif filename[-5:] == '.html':
filetype = 'html'
elif filename[-4:] == '.css':
filetype = 'css'
elif filename[-3:] == '.js':
filetype = 'js'
else:
filetype = 'text'
# ## check if file is not there
path = apath(filename, r=request)
if request.vars.revert and os.path.exists(path + '.bak'):
try:
data = safe_open(path + '.bak', 'r').read()
data1 = safe_open(path, 'r').read()
except IOError:
session.flash = T('Invalid action')
if 'from_ajax' in request.vars:
return response.json({'error': str(T('Invalid
action'))})
else:
redirect(URL('site'))
safe_open(path, 'w').write(data)
file_hash = md5_hash(data)
saved_on = time.ctime(os.stat(path)[stat.ST_MTIME])
safe_open(path + '.bak', 'w').write(data1)
response.flash = T('file "%s" of %s restored', (filename,
saved_on))
else:
try:
data = safe_open(path, 'r').read()
except IOError:
session.flash = T('Invalid action')
if 'from_ajax' in request.vars:
return response.json({'error': str(T('Invalid
action'))})
else:
redirect(URL('site'))
file_hash = md5_hash(data)
saved_on = time.ctime(os.stat(path)[stat.ST_MTIME])
if request.vars.file_hash and request.vars.file_hash !=
file_hash:
session.flash = T('file changed on disk')
data = request.vars.data.replace('\r\n', '\n').strip() +
'\n'
safe_open(path + '.1', 'w').write(data)
if 'from_ajax' in request.vars:
return response.json({'error': str(T('file changed on
disk')),
'redirect': URL('resolve',
args=request.args)})
else:
redirect(URL('resolve', args=request.args))
elif request.vars.data:
safe_open(path + '.bak', 'w').write(data)
data = request.vars.data.replace('\r\n', '\n').strip() +
'\n'
safe_open(path, 'w').write(data)
file_hash = md5_hash(data)
saved_on = time.ctime(os.stat(path)[stat.ST_MTIME])
response.flash = T('file saved on %s', saved_on)
data_or_revert = (request.vars.data or request.vars.revert)
# Check compile errors
highlight = None
if filetype == 'python' and request.vars.data:
import _ast
try:
code = request.vars.data.rstrip().replace('\r\n','\n')
+'\n'
compile(code, path, "exec", _ast.PyCF_ONLY_AST)
except Exception, e:
start = sum([len(line)+1 for l, line
in
enumerate(request.vars.data.split("\n"))
if l < e.lineno-1])
if e.text and e.offset:
offset = e.offset - (len(e.text) -
len(e.text.splitlines()[-1]))
else:
offset = 0
highlight = {'start': start, 'end': start + offset + 1}
try:
ex_name = e.__class__.__name__
except:
ex_name = 'unknown exception!'
response.flash = DIV(T('failed to compile file because:'),
BR(),
B(ex_name), T(' at line %s') %
e.lineno,
offset and T(' at char %s') % offset
or '',
PRE(str(e)))
if data_or_revert and request.args[1] == 'modules':
# Lets try to reload the modules
try:
mopath = '.'.join(request.args[2:])[:-3]
exec 'import applications.%s.modules.%s' %
(request.args[0], mopath)
reload(sys.modules['applications.%s.modules.%s'
% (request.args[0], mopath)])
except Exception, e:
response.flash = DIV(T('failed to reload module
because:'),PRE(str(e)))
edit_controller = None
editviewlinks = None
view_link = None
if filetype == 'html' and request.args >= 3:
cfilename = os.path.join(request.args[0], 'controllers',
request.args[2] + '.py')
if os.path.exists(apath(cfilename, r=request)):
edit_controller = URL('edit', args=[cfilename])
view = request.args[3].replace('.html','')
view_link =
A(T('view'),_href=URL(request.args[0],request.args[2],view))
elif filetype == 'python' and request.args[1] == 'controllers':
## it's a controller file.
## Create links to all of the associated view files.
app = request.args[0]
viewname = os.path.splitext(request.args[2])[0]
viewpath = os.path.join(app,'views',viewname)
aviewpath = apath(viewpath, r=request)
viewlist = []
if os.path.exists(aviewpath):
if os.path.isdir(aviewpath):
viewlist = glob(os.path.join(aviewpath,'*.html'))
elif os.path.exists(aviewpath+'.html'):
viewlist.append(aviewpath+'.html')
if len(viewlist):
editviewlinks = []
for v in viewlist:
vf = os.path.split(v)[-1]
vargs = "/".join([viewpath.replace(os.sep,"/"),vf])
editviewlinks.append(A(T(vf.split(".")[0]),\
_href=URL('edit',args=[vargs])))
if len(request.args) > 2 and request.args[1] == 'controllers':
controller = (request.args[2])[:-3]
functions = regex_expose.findall(data)
else:
(controller, functions) = (None, None)
if 'from_ajax' in request.vars:
return response.json({'file_hash': file_hash, 'saved_on':
saved_on, 'functions':functions, 'controller': controller,
'application': request.args[0], 'highlight': highlight })
else:
editarea_preferences = {}
editarea_preferences['FONT_SIZE'] = '10'
editarea_preferences['FULL_SCREEN'] = 'false'
editarea_preferences['ALLOW_TOGGLE'] = 'true'
editarea_preferences['REPLACE_TAB_BY_SPACES'] = '4'
editarea_preferences['DISPLAY'] = 'onload'
for key in editarea_preferences:
if globals().has_key(key):
editarea_preferences[key]=globals()[key]
return dict(app=request.args[0],
filename=filename,
filetype=filetype,
data=data,
edit_controller=edit_controller,
file_hash=file_hash,
saved_on=saved_on,
controller=controller,
functions=functions,
view_link=view_link,
editarea_preferences=editarea_preferences,
editviewlinks=editviewlinks)
def resolve():
""" """
filename = '/'.join(request.args)
# ## check if file is not there
path = apath(filename, r=request)
a = safe_open(path, 'r').readlines()
try:
b = safe_open(path + '.1', 'r').readlines()
except IOError:
session.flash = 'Other file, no longer there'
redirect(URL('edit', args=request.args))
d = difflib.ndiff(a, b)
def leading(line):
""" """
# TODO: we really need to comment this
z = ''
for (k, c) in enumerate(line):
if c == ' ':
z += ' '
elif c == ' \t':
z += ' '
elif k == 0 and c == '?':
pass
else:
break
return XML(z)
def getclass(item):
""" Determine item class """
if item[0] == ' ':
return 'normal'
if item[0] == '+':
return 'plus'
if item[0] == '-':
return 'minus'
if request.vars:
c = ''.join([item[2:] for (i, item) in enumerate(d) if item[0]
\
== ' ' or 'line%i' % i in request.vars])
safe_open(path, 'w').write(c)
session.flash = 'files merged'
redirect(URL('edit', args=request.args))
else:
# Making the short circuit compatible with <= python2.4
gen_data = lambda index,item: not item[:1] in ['+','-'] and ""
\
or INPUT(_type='checkbox',
_name='line%i' % index,
value=item[0] == '+')
diff = TABLE(*[TR(TD(gen_data(i,item)),
TD(item[0]),
TD(leading(item[2:]),
TT(item[2:].rstrip())),
_class=getclass(item))
for (i, item) in enumerate(d) if item[0] !=
'?'])
return dict(diff=diff, filename=filename)
def edit_language():
""" Edit language file """
filename = '/'.join(request.args)
from gluon.languages import read_dict, write_dict
strings = read_dict(apath(filename, r=request))
keys = sorted(strings.keys(),lambda x,y: cmp(x.lower(),
y.lower()))
rows = []
rows.append(H2(T('Original/Translation')))
for key in keys:
name = md5_hash(key)
if key==strings[key]:
_class='untranslated'
else:
_class='translated'
if len(key) <= 40:
elem = INPUT(_type='text', _name=name,value=strings[key],
_size=70,_class=_class)
else:
elem = TEXTAREA(_name=name, value=strings[key], _cols=70,
_rows=5, _class=_class)
# Making the short circuit compatible with <= python2.4
k = (strings[key] != key) and key or B(key)
rows.append(P(k, BR(), elem, TAG.BUTTON(T('delete'),
_onclick='return delkey("%s")' % name),
_id=name))
rows.append(INPUT(_type='submit', _value=T('update')))
form = FORM(*rows)
if form.accepts(request.vars, keepvalues=True):
strs = dict()
for key in keys:
name = md5_hash(key)
if form.vars[name]==chr(127): continue
strs[key] = form.vars[name]
write_dict(apath(filename, r=request), strs)
session.flash = T('file saved on %(time)s',
dict(time=time.ctime()))
redirect(URL(r=request,args=request.args))
return dict(app=request.args[0], filename=filename, form=form)
def about():
""" Read about info """
app = request.args[0]
# ## check if file is not there
about = safe_open(apath('%s/ABOUT' % app, r=request), 'r').read()
license = safe_open(apath('%s/LICENSE' % app, r=request),
'r').read()
return dict(app=app, about=WIKI(about), license=WIKI(license))
def design():
""" Application design handler """
app = request.args[0]
if not response.flash and app == request.application:
msg = T('ATTENTION: you cannot edit the running application!')
response.flash = msg
if request.vars.pluginfile!=None:
filename=os.path.basename(request.vars.pluginfile.filename)
if plugin_install(app, request.vars.pluginfile.file,
request, filename):
session.flash = T('new plugin installed')
redirect(URL('design',args=app))
else:
session.flash = \
T('unable to create application "%s"',
request.vars.filename)
redirect(URL(r=request))
# If we have only pyc files it means that
# we cannot design
if os.path.exists(apath('%s/compiled' % app, r=request)):
session.flash = \
T('application is compiled and cannot be designed')
redirect(URL('site'))
# Get all models
models = listdir(apath('%s/models/' % app, r=request), '.*\.py$')
models=[x.replace('\\','/') for x in models]
defines = {}
for m in models:
data = safe_open(apath('%s/models/%s' % (app, m), r=request),
'r').read()
defines[m] = regex_tables.findall(data)
defines[m].sort()
# Get all controllers
controllers = sorted(listdir(apath('%s/controllers/' % app,
r=request), '.*\.py$'))
controllers = [x.replace('\\','/') for x in controllers]
functions = {}
for c in controllers:
data = safe_open(apath('%s/controllers/%s' % (app, c),
r=request), 'r').read()
items = regex_expose.findall(data)
functions[c] = items
# Get all views
views = sorted(listdir(apath('%s/views/' % app, r=request), '[\w/
\-]+\.\w+$'))
views = [x.replace('\\','/') for x in views]
extend = {}
include = {}
for c in views:
data = safe_open(apath('%s/views/%s' % (app, c), r=request),
'r').read()
items = regex_extend.findall(data)
if items:
extend[c] = items[0][1]
items = regex_include.findall(data)
include[c] = [i[1] for i in items]
# Get all modules
modules = listdir(apath('%s/modules/' % app, r=request), '.*\.py
$')
modules = modules=[x.replace('\\','/') for x in modules]
modules.sort()
# Get all static files
statics = listdir(apath('%s/static/' % app, r=request), '[^
\.#].*')
statics = [x.replace('\\','/') for x in statics]
statics.sort()
# Get all languages
languages = listdir(apath('%s/languages/' % app, r=request),
'[\w-]*\.py')
#Get crontab
cronfolder = apath('%s/cron' % app, r=request)
if not os.path.exists(cronfolder): os.mkdir(cronfolder)
crontab = apath('%s/cron/crontab' % app, r=request)
if not os.path.exists(crontab):
safe_open(crontab,'w').write('#crontab')
plugins=[]
def filter_plugins(items,plugins):
plugins+=[item[7:].split('/')[0].split('.')[0] for item in
items if item.startswith('plugin_')]
plugins[:]=list(set(plugins))
plugins.sort()
return [item for item in items if not
item.startswith('plugin_')]
return dict(app=app,
models=filter_plugins(models,plugins),
defines=defines,
controllers=filter_plugins(controllers,plugins),
functions=functions,
views=filter_plugins(views,plugins),
modules=filter_plugins(modules,plugins),
extend=extend,
include=include,
statics=filter_plugins(statics,plugins),
languages=languages,
crontab=crontab,
plugins=plugins)
def delete_plugin():
""" Object delete handler """
app=request.args(0)
plugin = request.args(1)
plugin_name='plugin_'+plugin
if 'nodelete' in request.vars:
redirect(URL('design',args=app))
elif 'delete' in request.vars:
try:
for folder in
['models','views','controllers','static','modules']:
path=os.path.join(apath(app,r=request),folder)
for item in os.listdir(path):
if item.startswith(plugin_name):
filename=os.path.join(path,item)
if os.path.isdir(filename):
shutil.rmtree(filename)
else:
os.unlink(filename)
session.flash = T('plugin "%(plugin)s" deleted',
dict(plugin=plugin))
except Exception:
session.flash = T('unable to delete file plugin "%
(plugin)s"',
dict(plugin=plugin))
redirect(URL('design',args=request.args(0)))
return dict(plugin=plugin)
def plugin():
""" Application design handler """
app = request.args(0)
plugin = request.args(1)
if not response.flash and app == request.application:
msg = T('ATTENTION: you cannot edit the running application!')
response.flash = msg
# If we have only pyc files it means that
# we cannot design
if os.path.exists(apath('%s/compiled' % app, r=request)):
session.flash = \
T('application is compiled and cannot be designed')
redirect(URL('site'))
# Get all models
models = listdir(apath('%s/models/' % app, r=request), '.*\.py$')
models=[x.replace('\\','/') for x in models]
defines = {}
for m in models:
data = safe_open(apath('%s/models/%s' % (app, m), r=request),
'r').read()
defines[m] = regex_tables.findall(data)
defines[m].sort()
# Get all controllers
controllers = sorted(listdir(apath('%s/controllers/' % app,
r=request), '.*\.py$'))
controllers = [x.replace('\\','/') for x in controllers]
functions = {}
for c in controllers:
data = safe_open(apath('%s/controllers/%s' % (app, c),
r=request), 'r').read()
items = regex_expose.findall(data)
functions[c] = items
# Get all views
views = sorted(listdir(apath('%s/views/' % app, r=request), '[\w/
\-]+\.\w+$'))
views = [x.replace('\\','/') for x in views]
extend = {}
include = {}
for c in views:
data = safe_open(apath('%s/views/%s' % (app, c), r=request),
'r').read()
items = regex_extend.findall(data)
if items:
extend[c] = items[0][1]
items = regex_include.findall(data)
include[c] = [i[1] for i in items]
# Get all modules
modules = listdir(apath('%s/modules/' % app, r=request), '.*\.py
$')
modules = modules=[x.replace('\\','/') for x in modules]
modules.sort()
# Get all static files
statics = listdir(apath('%s/static/' % app, r=request), '[^
\.#].*')
statics = [x.replace('\\','/') for x in statics]
statics.sort()
# Get all languages
languages = listdir(apath('%s/languages/' % app, r=request),
'[\w-]*\.py')
#Get crontab
crontab = apath('%s/cron/crontab' % app, r=request)
if not os.path.exists(crontab):
safe_open(crontab,'w').write('#crontab')
def filter_plugins(items):
regex=re.compile('^plugin_'+plugin+'(/.*|\..*)?$')
return [item for item in items if regex.match(item)]
return dict(app=app,
models=filter_plugins(models),
defines=defines,
controllers=filter_plugins(controllers),
functions=functions,
views=filter_plugins(views),
modules=filter_plugins(modules),
extend=extend,
include=include,
statics=filter_plugins(statics),
languages=languages,
crontab=crontab)
def create_file():
""" Create files handler """
try:
path = apath(request.vars.location, r=request)
filename = re.sub('[^\w./-]+', '_', request.vars.filename)
if path[-11:] == '/languages/':
# Handle language files
if len(filename) == 0:
raise SyntaxError
if not filename[-3:] == '.py':
filename += '.py'
app = path.split('/')[-3]
path=os.path.join(apath(app,
r=request),'languages',filename)
if not os.path.exists(path):
safe_open(path,'w').write('')
findT(apath(app, r=request), filename[:-3])
session.flash = T('language file "%(filename)s" created/
updated',
dict(filename=filename))
redirect(request.vars.sender)
elif path[-8:] == '/models/':
# Handle python models
if not filename[-3:] == '.py':
filename += '.py'
if len(filename) == 3:
raise SyntaxError
text = '# coding: utf8\n'
elif path[-13:] == '/controllers/':
# Handle python controllers
if not filename[-3:] == '.py':
filename += '.py'
if len(filename) == 3:
raise SyntaxError
text = '# coding: utf8\n# %s\ndef index(): return
dict(message="hello from %s")'
text = text % (T('try something like'), filename)
elif path[-7:] == '/views/':
if request.vars.plugin and not filename.startswith('plugin_
%s/' % request.vars.plugin):
filename = 'plugin_%s/%s' % (request.vars.plugin,
filename)
# Handle template (html) views
if filename.find('.')<0:
filename += '.html'
if len(filename) == 5:
raise SyntaxError
msg = T('This is the %(filename)s template',
dict(filename=filename))
text = dedent("""
{{extend 'layout.html'}}
<h1>%s</h1>
{{=BEAUTIFY(response._vars)}}""" % msg)
elif path[-9:] == '/modules/':
if request.vars.plugin and not filename.startswith('plugin_
%s/' % request.vars.plugin):
filename = 'plugin_%s/%s' % (request.vars.plugin,
filename)
# Handle python module files
if not filename[-3:] == '.py':
filename += '.py'
if len(filename) == 3:
raise SyntaxError
text = dedent("""
#!/usr/bin/env python
# coding: utf8
from gluon.html import *
from gluon.http import *
from gluon.validators import *
from gluon.sqlhtml import *
# request, response, session, cache, T, db(s)
# must be passed and cannot be imported!""")
elif path[-8:] == '/static/':
if request.vars.plugin and not filename.startswith('plugin_
%s/' % request.vars.plugin):
filename = 'plugin_%s/%s' % (request.vars.plugin,
filename)
text = ''
else:
redirect(request.vars.sender)
full_filename = os.path.join(path, filename)
dirpath = os.path.dirname(full_filename)
if not os.path.exists(dirpath):
os.makedirs(dirpath)
if os.path.exists(full_filename):
raise SyntaxError
safe_open(full_filename, 'w').write(text)
session.flash = T('file "%(filename)s" created',
dict(filename=full_filename[len(path):]))
redirect(URL('edit',
args=[os.path.join(request.vars.location,
filename)]))
except Exception, e:
if not isinstance(e,HTTP):
session.flash = T('cannot create file')
redirect(request.vars.sender)
def upload_file():
""" File uploading handler """
try:
path = apath(request.vars.location, r=request)
if request.vars.filename:
filename = re.sub('[^\w\./]+', '_', request.vars.filename)
else:
filename = os.path.split(request.vars.file.filename)[-1]
if path[-8:] == '/models/' and not filename[-3:] == '.py':
filename += '.py'
if path[-9:] == '/modules/' and not filename[-3:] == '.py':
filename += '.py'
if path[-13:] == '/controllers/' and not filename[-3:] ==
'.py':
filename += '.py'
if path[-7:] == '/views/' and not filename[-5:] == '.html':
filename += '.html'
if path[-11:] == '/languages/' and not filename[-3:] == '.py':
filename += '.py'
filename = os.path.join(path, filename)
dirpath = os.path.dirname(filename)
if not os.path.exists(dirpath):
os.makedirs(dirpath)
safe_open(filename, 'wb').write(request.vars.file.file.read())
session.flash = T('file "%(filename)s" uploaded',
dict(filename=filename[len(path):]))
except Exception:
session.flash = T('cannot upload file "%(filename)s"',
dict(filename[len(path):]))
redirect(request.vars.sender)
def errors():
""" Error handler """
import operator
import os
import pickle
import hashlib
app = request.args[0]
method = request.args(1) or 'new'
if method == 'new':
errors_path = apath('%s/errors' % app, r=request)
delete_hashes = []
for item in request.vars:
if item[:7] == 'delete_':
delete_hashes.append(item[7:])
hash2error = dict()
for fn in listdir(errors_path, '^\w.*'):
try:
error = pickle.load(open(os.path.join(errors_path,
fn), 'r'))
except IOError:
continue
hash = hashlib.md5(error['traceback']).hexdigest()
if hash in delete_hashes:
os.unlink(os.path.join(errors_path, fn))
else:
try:
hash2error[hash]['count'] += 1
except KeyError:
error_lines = error['traceback'].split("\n")
last_line = error_lines[-2]
error_causer = os.path.split(error['layer'])[1]
hash2error[hash] = dict(count=1, pickel=error,
causer=error_causer,
last_line=last_line,hash=hash, ticket=fn)
decorated = [(x['count'], x) for x in hash2error.values()]
decorated.sort(key=operator.itemgetter(0), reverse=True)
return dict(errors = [x[1] for x in decorated], app=app,
method=method)
else:
for item in request.vars:
if item[:7] == 'delete_':
os.unlink(apath('%s/errors/%s' % (app, item[7:]),
r=request))
func = lambda p: os.stat(apath('%s/errors/%s' % (app, p),
r=request)).st_mtime
tickets = sorted(listdir(apath('%s/errors/' % app, r=request),
'^\w.*'),
key=func,
reverse=True)
return dict(app=app, tickets=tickets, method=method)
def make_link(path):
""" Create a link from a path """
tryFile = path.replace('\\', '/')
if os.path.isabs(tryFile) and os.path.isfile(tryFile):
(folder, filename) = os.path.split(tryFile)
(base, ext) = os.path.splitext(filename)
app = request.args[0]
editable = {'controllers': '.py', 'models': '.py', 'views':
'.html'}
for key in editable.keys():
check_extension = folder.endswith("%s/%s" % (app,key))
if ext.lower() == editable[key] and check_extension:
return A('"' + tryFile + '"',
_href=URL(r=request,
f='edit/%s/%s/%s' % (app, key,
filename))).xml()
return ''
def make_links(traceback):
""" Make links using the given traceback """
lwords = traceback.split('"')
# Making the short circuit compatible with <= python2.4
result = (len(lwords) != 0) and lwords[0] or ''
i = 1
while i < len(lwords):
link = make_link(lwords[i])
if link == '':
result += '"' + lwords[i]
else:
result += link
if i + 1 < len(lwords):
result += lwords[i + 1]
i = i + 1
i = i + 1
return result
class TRACEBACK(object):
""" Generate the traceback """
def __init__(self, text):
""" TRACEBACK constructor """
self.s = make_links(CODE(text).xml())
def xml(self):
""" Returns the xml """
return self.s
def ticket():
""" Ticket handler """
if len(request.args) != 2:
session.flash = T('invalid ticket')
redirect(URL('site'))
myversion = request.env.web2py_version
app = request.args[0]
ticket = request.args[1]
e = RestrictedError()
e.load(request, app, ticket)
return dict(app=app,
ticket=ticket,
output=e.output,
traceback=(e.traceback and TRACEBACK(e.traceback)),
snapshot=e.snapshot,
code=e.code,
layer=e.layer,
myversion=myversion)
def error():
""" Generate a ticket (for testing) """
raise RuntimeError('admin ticket generator at your service')
def update_languages():
""" Update available languages """
app = request.args[0]
update_all_languages(apath(app, r=request))
session.flash = T('Language files (static strings) updated')
redirect(URL('design',args=app))
def twitter():
session.forget()
session._unlock(response)
import gluon.tools
import gluon.contrib.simplejson as sj
try:
page = gluon.tools.fetch('http://twitter.com/web2py?
format=json')
return sj.loads(page)['#timeline']
except Exception, e:
return DIV(T('Unable to download because'),PRE(str(e)))
response._vars=response._caller(plugin)