1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | |
---|
5 | import os |
---|
6 | from urllib.parse import quote as urlquote |
---|
7 | |
---|
8 | from twisted.python.filepath import FilePath |
---|
9 | from twisted.web.template import tags as T, Element, renderElement, XMLFile, renderer |
---|
10 | |
---|
11 | from allmydata.util import base32 |
---|
12 | from allmydata.interfaces import IDirectoryNode, IFileNode, MDMF_VERSION |
---|
13 | from allmydata.web.common import MultiFormatResource |
---|
14 | from allmydata.mutable.common import UnrecoverableFileError # TODO: move |
---|
15 | |
---|
16 | |
---|
17 | class MoreInfo(MultiFormatResource): |
---|
18 | """ |
---|
19 | A ``Resource`` for describing more information about a node. |
---|
20 | |
---|
21 | :param node Node: The node to describe. |
---|
22 | """ |
---|
23 | |
---|
24 | def __init__(self, node): |
---|
25 | super(MoreInfo, self).__init__() |
---|
26 | self.node = node |
---|
27 | |
---|
28 | def render_HTML(self, req): |
---|
29 | """ |
---|
30 | Render an HTML template describing this node. |
---|
31 | """ |
---|
32 | return renderElement(req, MoreInfoElement(self.node)) |
---|
33 | |
---|
34 | render_INFO = render_HTML |
---|
35 | |
---|
36 | |
---|
37 | class MoreInfoElement(Element): |
---|
38 | """ |
---|
39 | An ``Element`` HTML template which can be flattened to describe this node. |
---|
40 | |
---|
41 | :param Node node: The node to describe. |
---|
42 | """ |
---|
43 | |
---|
44 | loader = XMLFile(FilePath(__file__).sibling("info.xhtml")) |
---|
45 | |
---|
46 | def __init__(self, node): |
---|
47 | super(MoreInfoElement, self).__init__() |
---|
48 | self.original = node |
---|
49 | |
---|
50 | def abbrev(self, storage_index_or_none): |
---|
51 | if storage_index_or_none: |
---|
52 | return str(base32.b2a(storage_index_or_none)[:6], "ascii") |
---|
53 | return "LIT file" |
---|
54 | |
---|
55 | def get_type(self): |
---|
56 | node = self.original |
---|
57 | if IDirectoryNode.providedBy(node): |
---|
58 | if not node.is_mutable(): |
---|
59 | return "immutable directory" |
---|
60 | return "directory" |
---|
61 | if IFileNode.providedBy(node): |
---|
62 | si = node.get_storage_index() |
---|
63 | if si: |
---|
64 | if node.is_mutable(): |
---|
65 | ret = "mutable file" |
---|
66 | if node.get_version() == MDMF_VERSION: |
---|
67 | ret += " (mdmf)" |
---|
68 | else: |
---|
69 | ret += " (sdmf)" |
---|
70 | return ret |
---|
71 | return "immutable file" |
---|
72 | return "immutable LIT file" |
---|
73 | return "unknown" |
---|
74 | |
---|
75 | @renderer |
---|
76 | def title(self, req, tag): |
---|
77 | node = self.original |
---|
78 | si = node.get_storage_index() |
---|
79 | t = "More Info for %s" % self.get_type() |
---|
80 | if si: |
---|
81 | t += " (SI=%s)" % self.abbrev(si) |
---|
82 | return tag(t) |
---|
83 | |
---|
84 | @renderer |
---|
85 | def header(self, req, tag): |
---|
86 | return self.title(req, tag) |
---|
87 | |
---|
88 | @renderer |
---|
89 | def type(self, req, tag): |
---|
90 | return tag(self.get_type()) |
---|
91 | |
---|
92 | @renderer |
---|
93 | def si(self, req, tag): |
---|
94 | si = self.original.get_storage_index() |
---|
95 | if not si: |
---|
96 | return "None" |
---|
97 | return tag(base32.b2a(si)) |
---|
98 | |
---|
99 | @renderer |
---|
100 | def size(self, req, tag): |
---|
101 | node = self.original |
---|
102 | d = node.get_current_size() |
---|
103 | def _no_size(size): |
---|
104 | if size is None: |
---|
105 | return "?" |
---|
106 | return size |
---|
107 | d.addCallback(_no_size) |
---|
108 | def _handle_unrecoverable(f): |
---|
109 | f.trap(UnrecoverableFileError) |
---|
110 | return "?" |
---|
111 | d.addErrback(_handle_unrecoverable) |
---|
112 | d.addCallback(lambda size: tag(str(size))) |
---|
113 | return d |
---|
114 | |
---|
115 | @renderer |
---|
116 | def directory_writecap(self, req, tag): |
---|
117 | node = self.original |
---|
118 | if not IDirectoryNode.providedBy(node): |
---|
119 | return "" |
---|
120 | if node.is_readonly(): |
---|
121 | return "" |
---|
122 | return tag(node.get_uri()) |
---|
123 | |
---|
124 | @renderer |
---|
125 | def directory_readcap(self, req, tag): |
---|
126 | node = self.original |
---|
127 | if not IDirectoryNode.providedBy(node): |
---|
128 | return "" |
---|
129 | return tag(node.get_readonly_uri()) |
---|
130 | |
---|
131 | @renderer |
---|
132 | def directory_verifycap(self, req, tag): |
---|
133 | node = self.original |
---|
134 | if not IDirectoryNode.providedBy(node): |
---|
135 | return "" |
---|
136 | verifier = node.get_verify_cap() |
---|
137 | if verifier: |
---|
138 | return tag(node.get_verify_cap().to_string()) |
---|
139 | return "" |
---|
140 | |
---|
141 | @renderer |
---|
142 | def file_writecap(self, req, tag): |
---|
143 | node = self.original |
---|
144 | if IDirectoryNode.providedBy(node): |
---|
145 | node = node._node |
---|
146 | write_uri = node.get_write_uri() |
---|
147 | if not write_uri: |
---|
148 | return "" |
---|
149 | return tag(write_uri) |
---|
150 | |
---|
151 | @renderer |
---|
152 | def file_readcap(self, req, tag): |
---|
153 | node = self.original |
---|
154 | if IDirectoryNode.providedBy(node): |
---|
155 | node = node._node |
---|
156 | read_uri = node.get_readonly_uri() |
---|
157 | if not read_uri: |
---|
158 | return "" |
---|
159 | return tag(read_uri) |
---|
160 | |
---|
161 | @renderer |
---|
162 | def file_verifycap(self, req, tag): |
---|
163 | node = self.original |
---|
164 | if IDirectoryNode.providedBy(node): |
---|
165 | node = node._node |
---|
166 | verifier = node.get_verify_cap() |
---|
167 | if verifier: |
---|
168 | return tag(node.get_verify_cap().to_string()) |
---|
169 | return "" |
---|
170 | |
---|
171 | def get_root(self, req): |
---|
172 | # the addSlash=True gives us one extra (empty) segment |
---|
173 | depth = len(req.prepath) + len(req.postpath) - 1 |
---|
174 | link = "/".join([".."] * depth) |
---|
175 | return link |
---|
176 | |
---|
177 | @renderer |
---|
178 | def raw_link(self, req, tag): |
---|
179 | node = self.original |
---|
180 | if IDirectoryNode.providedBy(node): |
---|
181 | node = node._node |
---|
182 | elif IFileNode.providedBy(node): |
---|
183 | pass |
---|
184 | else: |
---|
185 | return "" |
---|
186 | root = self.get_root(req) |
---|
187 | quoted_uri = urlquote(node.get_uri()) |
---|
188 | text_plain_url = "%s/file/%s/@@named=/raw.txt" % (root, quoted_uri) |
---|
189 | return T.li("Raw data as ", T.a("text/plain", href=text_plain_url)) |
---|
190 | |
---|
191 | @renderer |
---|
192 | def is_checkable(self, req, tag): |
---|
193 | node = self.original |
---|
194 | si = node.get_storage_index() |
---|
195 | if si: |
---|
196 | return tag |
---|
197 | # don't show checker button for LIT files |
---|
198 | return "" |
---|
199 | |
---|
200 | @renderer |
---|
201 | def check_form(self, req, tag): |
---|
202 | node = self.original |
---|
203 | quoted_uri = urlquote(node.get_uri()) |
---|
204 | target = self.get_root(req) + "/uri/" + quoted_uri |
---|
205 | if IDirectoryNode.providedBy(node): |
---|
206 | target += "/" |
---|
207 | check = T.form(action=target, method="post", |
---|
208 | enctype="multipart/form-data")( |
---|
209 | T.fieldset( |
---|
210 | T.input(type="hidden", name="t", value="check"), |
---|
211 | T.input(type="hidden", name="return_to", value="."), |
---|
212 | T.legend("Check on this object", class_="freeform-form-label"), |
---|
213 | T.div( |
---|
214 | "Verify every bit? (EXPENSIVE):", |
---|
215 | T.input(type="checkbox", name="verify"), |
---|
216 | ), |
---|
217 | T.div("Repair any problems?: ", |
---|
218 | T.input(type="checkbox", name="repair")), |
---|
219 | T.div("Add/renew lease on all shares?: ", |
---|
220 | T.input(type="checkbox", name="add-lease")), |
---|
221 | T.div("Emit results in JSON format?: ", |
---|
222 | T.input(type="checkbox", name="output", value="JSON")), |
---|
223 | |
---|
224 | T.input(type="submit", value="Check"), |
---|
225 | |
---|
226 | )) |
---|
227 | return tag(check) |
---|
228 | |
---|
229 | @renderer |
---|
230 | def is_mutable_file(self, req, tag): |
---|
231 | node = self.original |
---|
232 | if IDirectoryNode.providedBy(node): |
---|
233 | return "" |
---|
234 | if (IFileNode.providedBy(node) |
---|
235 | and node.is_mutable() and not node.is_readonly()): |
---|
236 | return tag |
---|
237 | return "" |
---|
238 | |
---|
239 | @renderer |
---|
240 | def overwrite_form(self, req, tag): |
---|
241 | node = self.original |
---|
242 | root = self.get_root(req) |
---|
243 | action = "%s/uri/%s" % (root, urlquote(node.get_uri())) |
---|
244 | done_url = "%s/uri/%s?t=info" % (root, urlquote(node.get_uri())) |
---|
245 | overwrite = T.form(action=action, method="post", |
---|
246 | enctype="multipart/form-data")( |
---|
247 | T.fieldset( |
---|
248 | T.input(type="hidden", name="t", value="upload"), |
---|
249 | T.input(type='hidden', name='when_done', value=done_url), |
---|
250 | T.legend("Overwrite", class_="freeform-form-label"), |
---|
251 | "Upload new contents: ", |
---|
252 | T.input(type="file", name="file"), |
---|
253 | " ", |
---|
254 | T.input(type="submit", value="Replace Contents") |
---|
255 | )) |
---|
256 | return tag(overwrite) |
---|
257 | |
---|
258 | @renderer |
---|
259 | def is_directory(self, req, tag): |
---|
260 | node = self.original |
---|
261 | if IDirectoryNode.providedBy(node): |
---|
262 | return tag |
---|
263 | return "" |
---|
264 | |
---|
265 | @renderer |
---|
266 | def deep_check_form(self, req, tag): |
---|
267 | ophandle = base32.b2a(os.urandom(16)) |
---|
268 | deep_check = T.form(action=req.path, method="post", |
---|
269 | enctype="multipart/form-data")( |
---|
270 | T.fieldset( |
---|
271 | T.input(type="hidden", name="t", value="start-deep-check"), |
---|
272 | T.input(type="hidden", name="return_to", value="."), |
---|
273 | T.legend("Run a deep-check operation (EXPENSIVE)", class_="freeform-form-label"), |
---|
274 | T.div( |
---|
275 | "Verify every bit? (EVEN MORE EXPENSIVE):", |
---|
276 | T.input(type="checkbox", name="verify"), |
---|
277 | ), |
---|
278 | T.div("Repair any problems?: ", |
---|
279 | T.input(type="checkbox", name="repair")), |
---|
280 | T.div("Add/renew lease on all shares?: ", |
---|
281 | T.input(type="checkbox", name="add-lease")), |
---|
282 | T.div("Emit results in JSON format?: ", |
---|
283 | T.input(type="checkbox", name="output", value="JSON")), |
---|
284 | |
---|
285 | T.input(type="hidden", name="ophandle", value=ophandle), |
---|
286 | T.input(type="submit", value="Deep-Check"), |
---|
287 | |
---|
288 | )) |
---|
289 | return tag(deep_check) |
---|
290 | |
---|
291 | @renderer |
---|
292 | def deep_size_form(self, req, tag): |
---|
293 | ophandle = base32.b2a(os.urandom(16)) |
---|
294 | deep_size = T.form(action=req.path, method="post", |
---|
295 | enctype="multipart/form-data")( |
---|
296 | T.fieldset( |
---|
297 | T.input(type="hidden", name="t", value="start-deep-size"), |
---|
298 | T.legend("Run a deep-size operation (EXPENSIVE)", class_="freeform-form-label"), |
---|
299 | T.input(type="hidden", name="ophandle", value=ophandle), |
---|
300 | T.input(type="submit", value="Deep-Size"), |
---|
301 | )) |
---|
302 | return tag(deep_size) |
---|
303 | |
---|
304 | @renderer |
---|
305 | def deep_stats_form(self, req, tag): |
---|
306 | ophandle = base32.b2a(os.urandom(16)) |
---|
307 | deep_stats = T.form(action=req.path, method="post", |
---|
308 | enctype="multipart/form-data")( |
---|
309 | T.fieldset( |
---|
310 | T.input(type="hidden", name="t", value="start-deep-stats"), |
---|
311 | T.legend("Run a deep-stats operation (EXPENSIVE)", class_="freeform-form-label"), |
---|
312 | T.input(type="hidden", name="ophandle", value=ophandle), |
---|
313 | T.input(type="submit", value="Deep-Stats"), |
---|
314 | )) |
---|
315 | return tag(deep_stats) |
---|
316 | |
---|
317 | @renderer |
---|
318 | def manifest_form(self, req, tag): |
---|
319 | ophandle = base32.b2a(os.urandom(16)) |
---|
320 | manifest = T.form(action=req.path, method="post", |
---|
321 | enctype="multipart/form-data")( |
---|
322 | T.fieldset( |
---|
323 | T.input(type="hidden", name="t", value="start-manifest"), |
---|
324 | T.legend("Run a manifest operation (EXPENSIVE)", class_="freeform-form-label"), |
---|
325 | T.div("Output Format: ", |
---|
326 | T.select(name="output") |
---|
327 | ( T.option("HTML", value="html", selected="true"), |
---|
328 | T.option("text", value="text"), |
---|
329 | T.option("JSON", value="json"), |
---|
330 | ), |
---|
331 | ), |
---|
332 | T.input(type="hidden", name="ophandle", value=ophandle), |
---|
333 | T.input(type="submit", value="Manifest"), |
---|
334 | )) |
---|
335 | return tag(manifest) |
---|
336 | |
---|
337 | |
---|
338 | # TODO: edge metadata |
---|