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 += '&nbsp;'
            elif c == ' \t':
                z += '&nbsp;'
            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)

Reply via email to