1
2 from zope.interface import implements
3 from twisted.python import components
4 from twisted.spread import pb
5 from twisted.web import html, server
6 from twisted.web.resource import Resource
7 from twisted.web.error import NoResource
8
9 from buildbot import interfaces
10 from buildbot.status import builder
11 from buildbot.status.web.base import IHTMLLog, HtmlResource
12
13
14 textlog_stylesheet = """
15 <style type="text/css">
16 div.data {
17 font-family: "Courier New", courier, monotype;
18 }
19 span.stdout {
20 font-family: "Courier New", courier, monotype;
21 }
22 span.stderr {
23 font-family: "Courier New", courier, monotype;
24 color: red;
25 }
26 span.header {
27 font-family: "Courier New", courier, monotype;
28 color: blue;
29 }
30 </style>
31 """
32
34 implements(interfaces.IStatusLogConsumer)
35
37 self.original = original
38 self.textlog = textlog
40 self.producer = producer
41 self.original.registerProducer(producer, streaming)
45 formatted = self.textlog.content([chunk])
46 try:
47 if isinstance(formatted, unicode):
48 formatted = formatted.encode('utf-8')
49 self.original.write(formatted)
50 except pb.DeadReferenceError:
51 self.producing.stopProducing()
54
55
56
57 -class TextLog(Resource):
58
59
60 implements(IHTMLLog)
61
62 asText = False
63 subscribed = False
64
65 - def __init__(self, original):
66 Resource.__init__(self)
67 self.original = original
68
69 - def getChild(self, path, req):
70 if path == "text":
71 self.asText = True
72 return self
73 return HtmlResource.getChild(self, path, req)
74
76 title = "Log File contents"
77 data = "<html>\n<head><title>" + title + "</title>\n"
78 data += textlog_stylesheet
79 data += "</head>\n"
80 data += "<body vlink=\"#800080\">\n"
81 texturl = request.childLink("text")
82 data += '<a href="%s">(view as text)</a><br />\n' % texturl
83 data += "<pre>\n"
84 return data
85
86 - def content(self, entries):
87 spanfmt = '<span class="%s">%s</span>'
88 data = ""
89 for type, entry in entries:
90 if type >= len(builder.ChunkTypes) or type < 0:
91
92 continue
93 if self.asText:
94 if type != builder.HEADER:
95 data += entry
96 else:
97 data += spanfmt % (builder.ChunkTypes[type],
98 html.escape(entry))
99 return data
100
102 data = "</pre>\n"
103 data += "</body></html>\n"
104 return data
105
106 - def render_HEAD(self, request):
107 if self.asText:
108 request.setHeader("content-type", "text/plain")
109 else:
110 request.setHeader("content-type", "text/html")
111
112
113 request.setHeader("content-length", self.original.length)
114 return ''
115
116 - def render_GET(self, req):
117 self.req = req
118
119 if self.asText:
120 req.setHeader("content-type", "text/plain")
121 else:
122 req.setHeader("content-type", "text/html")
123
124 if not self.asText:
125 req.write(self.htmlHeader(req))
126
127 self.original.subscribeConsumer(ChunkConsumer(req, self))
128 return server.NOT_DONE_YET
129
130 - def finished(self):
131 if not self.req:
132 return
133 try:
134 if not self.asText:
135 self.req.write(self.htmlFooter())
136 self.req.finish()
137 except pb.DeadReferenceError:
138 pass
139
140
141 self.req = None
142
143 components.registerAdapter(TextLog, interfaces.IStatusLog, IHTMLLog)
144
145
147 implements(IHTMLLog)
148
150 Resource.__init__(self)
151 self.original = original
152
154 request.setHeader("content-type", "text/html")
155 return self.original.html
156
157 components.registerAdapter(HTMLLog, builder.HTMLLogFile, IHTMLLog)
158
159
174