comparison sat/plugins/plugin_comp_file_sharing_management.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 0bafdbda7f5f
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin to detect language (experimental) 4 # SAT plugin to detect language (experimental)
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
29 29
30 log = getLogger(__name__) 30 log = getLogger(__name__)
31 31
32 32
33 PLUGIN_INFO = { 33 PLUGIN_INFO = {
34 C.PI_NAME: u"File Sharing Management", 34 C.PI_NAME: "File Sharing Management",
35 C.PI_IMPORT_NAME: u"FILE_SHARING_MANAGEMENT", 35 C.PI_IMPORT_NAME: "FILE_SHARING_MANAGEMENT",
36 C.PI_MODES: [C.PLUG_MODE_COMPONENT], 36 C.PI_MODES: [C.PLUG_MODE_COMPONENT],
37 C.PI_TYPE: u"EXP", 37 C.PI_TYPE: "EXP",
38 C.PI_PROTOCOLS: [], 38 C.PI_PROTOCOLS: [],
39 C.PI_DEPENDENCIES: [u"XEP-0050", u"XEP-0264"], 39 C.PI_DEPENDENCIES: ["XEP-0050", "XEP-0264"],
40 C.PI_RECOMMENDATIONS: [], 40 C.PI_RECOMMENDATIONS: [],
41 C.PI_MAIN: u"FileSharingManagement", 41 C.PI_MAIN: "FileSharingManagement",
42 C.PI_HANDLER: u"no", 42 C.PI_HANDLER: "no",
43 C.PI_DESCRIPTION: _( 43 C.PI_DESCRIPTION: _(
44 u"Experimental handling of file management for file sharing. This plugins allows " 44 "Experimental handling of file management for file sharing. This plugins allows "
45 u"to change permissions of stored files/directories or remove them." 45 "to change permissions of stored files/directories or remove them."
46 ), 46 ),
47 } 47 }
48 48
49 NS_FILE_MANAGEMENT = u"https://salut-a-toi.org/protocol/file-management:0" 49 NS_FILE_MANAGEMENT = "https://salut-a-toi.org/protocol/file-management:0"
50 NS_FILE_MANAGEMENT_PERM = u"https://salut-a-toi.org/protocol/file-management:0#perm" 50 NS_FILE_MANAGEMENT_PERM = "https://salut-a-toi.org/protocol/file-management:0#perm"
51 NS_FILE_MANAGEMENT_DELETE = u"https://salut-a-toi.org/protocol/file-management:0#delete" 51 NS_FILE_MANAGEMENT_DELETE = "https://salut-a-toi.org/protocol/file-management:0#delete"
52 NS_FILE_MANAGEMENT_THUMB = u"https://salut-a-toi.org/protocol/file-management:0#thumb" 52 NS_FILE_MANAGEMENT_THUMB = "https://salut-a-toi.org/protocol/file-management:0#thumb"
53 53
54 54
55 class WorkflowError(Exception): 55 class WorkflowError(Exception):
56 """Raised when workflow can't be completed""" 56 """Raised when workflow can't be completed"""
57 57
66 class FileSharingManagement(object): 66 class FileSharingManagement(object):
67 # This is a temporary way (Q&D) to handle stored files, a better way (using pubsub 67 # This is a temporary way (Q&D) to handle stored files, a better way (using pubsub
68 # syntax?) should be elaborated and proposed as a standard. 68 # syntax?) should be elaborated and proposed as a standard.
69 69
70 def __init__(self, host): 70 def __init__(self, host):
71 log.info(_(u"File Sharing Management plugin initialization")) 71 log.info(_("File Sharing Management plugin initialization"))
72 self.host = host 72 self.host = host
73 self._c = host.plugins["XEP-0050"] 73 self._c = host.plugins["XEP-0050"]
74 self._t = host.plugins["XEP-0264"] 74 self._t = host.plugins["XEP-0264"]
75 self.files_path = host.getLocalPath(None, C.FILES_DIR, profile=False) 75 self.files_path = host.getLocalPath(None, C.FILES_DIR, profile=False)
76 76
77 def profileConnected(self, client): 77 def profileConnected(self, client):
78 self._c.addAdHocCommand( 78 self._c.addAdHocCommand(
79 client, self._onChangeFile, u"Change Permissions of File(s)", 79 client, self._onChangeFile, "Change Permissions of File(s)",
80 node=NS_FILE_MANAGEMENT_PERM, 80 node=NS_FILE_MANAGEMENT_PERM,
81 allowed_magics=C.ENTITY_ALL, 81 allowed_magics=C.ENTITY_ALL,
82 ) 82 )
83 self._c.addAdHocCommand( 83 self._c.addAdHocCommand(
84 client, self._onDeleteFile, u"Delete File(s)", 84 client, self._onDeleteFile, "Delete File(s)",
85 node=NS_FILE_MANAGEMENT_DELETE, 85 node=NS_FILE_MANAGEMENT_DELETE,
86 allowed_magics=C.ENTITY_ALL, 86 allowed_magics=C.ENTITY_ALL,
87 ) 87 )
88 self._c.addAdHocCommand( 88 self._c.addAdHocCommand(
89 client, self._onGenThumbnails, u"Generate Thumbnails", 89 client, self._onGenThumbnails, "Generate Thumbnails",
90 node=NS_FILE_MANAGEMENT_THUMB, 90 node=NS_FILE_MANAGEMENT_THUMB,
91 allowed_magics=C.ENTITY_ALL, 91 allowed_magics=C.ENTITY_ALL,
92 ) 92 )
93 93
94 def _err(self, reason): 94 def _err(self, reason):
107 """Create the form to select the file to use 107 """Create the form to select the file to use
108 108
109 @return (tuple): arguments to use in defer.returnValue 109 @return (tuple): arguments to use in defer.returnValue
110 """ 110 """
111 status = self._c.STATUS.EXECUTING 111 status = self._c.STATUS.EXECUTING
112 form = data_form.Form("form", title=u"File Management", 112 form = data_form.Form("form", title="File Management",
113 formNamespace=NS_FILE_MANAGEMENT) 113 formNamespace=NS_FILE_MANAGEMENT)
114 114
115 field = data_form.Field( 115 field = data_form.Field(
116 "text-single", "path", required=True 116 "text-single", "path", required=True
117 ) 117 )
134 @return (D(dict)): found file data 134 @return (D(dict)): found file data
135 @raise WorkflowError: something is wrong 135 @raise WorkflowError: something is wrong
136 """ 136 """
137 fields = command_form.fields 137 fields = command_form.fields
138 try: 138 try:
139 path = fields[u'path'].value.strip() 139 path = fields['path'].value.strip()
140 namespace = fields[u'namespace'].value or None 140 namespace = fields['namespace'].value or None
141 except KeyError: 141 except KeyError:
142 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 142 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
143 143
144 if not path: 144 if not path:
145 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 145 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
146 146
147 requestor = session_data[u'requestor'] 147 requestor = session_data['requestor']
148 requestor_bare = requestor.userhostJID() 148 requestor_bare = requestor.userhostJID()
149 path = path.rstrip(u'/') 149 path = path.rstrip('/')
150 parent_path, basename = os.path.split(path) 150 parent_path, basename = os.path.split(path)
151 151
152 # TODO: if parent_path and basename are empty, we ask for root directory 152 # TODO: if parent_path and basename are empty, we ask for root directory
153 # this must be managed 153 # this must be managed
154 154
156 found_files = yield self.host.memory.getFiles( 156 found_files = yield self.host.memory.getFiles(
157 client, requestor_bare, path=parent_path, name=basename, 157 client, requestor_bare, path=parent_path, name=basename,
158 namespace=namespace) 158 namespace=namespace)
159 found_file = found_files[0] 159 found_file = found_files[0]
160 except (exceptions.NotFound, IndexError): 160 except (exceptions.NotFound, IndexError):
161 raise WorkflowError(self._err(_(u"file not found"))) 161 raise WorkflowError(self._err(_("file not found")))
162 except exceptions.PermissionError: 162 except exceptions.PermissionError:
163 raise WorkflowError(self._err(_(u"forbidden"))) 163 raise WorkflowError(self._err(_("forbidden")))
164 164
165 if found_file['owner'] != requestor_bare: 165 if found_file['owner'] != requestor_bare:
166 # only owner can manage files 166 # only owner can manage files
167 log.warning(_(u"Only owner can manage files")) 167 log.warning(_("Only owner can manage files"))
168 raise WorkflowError(self._err(_(u"forbidden"))) 168 raise WorkflowError(self._err(_("forbidden")))
169 169
170 session_data[u'found_file'] = found_file 170 session_data['found_file'] = found_file
171 session_data[u'namespace'] = namespace 171 session_data['namespace'] = namespace
172 defer.returnValue(found_file) 172 defer.returnValue(found_file)
173 173
174 def _updateReadPermission(self, access, allowed_jids): 174 def _updateReadPermission(self, access, allowed_jids):
175 if not allowed_jids: 175 if not allowed_jids:
176 if C.ACCESS_PERM_READ in access: 176 if C.ACCESS_PERM_READ in access:
177 del access[C.ACCESS_PERM_READ] 177 del access[C.ACCESS_PERM_READ]
178 elif allowed_jids == u'PUBLIC': 178 elif allowed_jids == 'PUBLIC':
179 access[C.ACCESS_PERM_READ] = { 179 access[C.ACCESS_PERM_READ] = {
180 u"type": C.ACCESS_TYPE_PUBLIC 180 "type": C.ACCESS_TYPE_PUBLIC
181 } 181 }
182 else: 182 else:
183 access[C.ACCESS_PERM_READ] = { 183 access[C.ACCESS_PERM_READ] = {
184 u"type": C.ACCESS_TYPE_WHITELIST, 184 "type": C.ACCESS_TYPE_WHITELIST,
185 u"jids": [j.full() for j in allowed_jids] 185 "jids": [j.full() for j in allowed_jids]
186 } 186 }
187 187
188 @defer.inlineCallbacks 188 @defer.inlineCallbacks
189 def _updateDir(self, client, requestor, namespace, file_data, allowed_jids): 189 def _updateDir(self, client, requestor, namespace, file_data, allowed_jids):
190 """Recursively update permission of a directory and all subdirectories 190 """Recursively update permission of a directory and all subdirectories
191 191
192 @param file_data(dict): metadata of the file 192 @param file_data(dict): metadata of the file
193 @param allowed_jids(list[jid.JID]): list of entities allowed to read the file 193 @param allowed_jids(list[jid.JID]): list of entities allowed to read the file
194 """ 194 """
195 assert file_data[u'type'] == C.FILE_TYPE_DIRECTORY 195 assert file_data['type'] == C.FILE_TYPE_DIRECTORY
196 files_data = yield self.host.memory.getFiles( 196 files_data = yield self.host.memory.getFiles(
197 client, requestor, parent=file_data[u'id'], namespace=namespace) 197 client, requestor, parent=file_data['id'], namespace=namespace)
198 198
199 for file_data in files_data: 199 for file_data in files_data:
200 if not file_data[u'access'].get(C.ACCESS_PERM_READ, {}): 200 if not file_data['access'].get(C.ACCESS_PERM_READ, {}):
201 log.debug(u"setting {perm} read permission for {name}".format( 201 log.debug("setting {perm} read permission for {name}".format(
202 perm=allowed_jids, name=file_data[u'name'])) 202 perm=allowed_jids, name=file_data['name']))
203 yield self.host.memory.fileUpdate( 203 yield self.host.memory.fileUpdate(
204 file_data[u'id'], u'access', 204 file_data['id'], 'access',
205 partial(self._updateReadPermission, allowed_jids=allowed_jids)) 205 partial(self._updateReadPermission, allowed_jids=allowed_jids))
206 if file_data[u'type'] == C.FILE_TYPE_DIRECTORY: 206 if file_data['type'] == C.FILE_TYPE_DIRECTORY:
207 yield self._updateDir(client, requestor, namespace, file_data, u'PUBLIC') 207 yield self._updateDir(client, requestor, namespace, file_data, 'PUBLIC')
208 208
209 @defer.inlineCallbacks 209 @defer.inlineCallbacks
210 def _onChangeFile(self, client, command_elt, session_data, action, node): 210 def _onChangeFile(self, client, command_elt, session_data, action, node):
211 try: 211 try:
212 x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next() 212 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
213 command_form = data_form.Form.fromElement(x_elt) 213 command_form = data_form.Form.fromElement(x_elt)
214 except StopIteration: 214 except StopIteration:
215 command_form = None 215 command_form = None
216 216
217 found_file = session_data.get('found_file') 217 found_file = session_data.get('found_file')
218 requestor = session_data[u'requestor'] 218 requestor = session_data['requestor']
219 requestor_bare = requestor.userhostJID() 219 requestor_bare = requestor.userhostJID()
220 220
221 if command_form is None or len(command_form.fields) == 0: 221 if command_form is None or len(command_form.fields) == 0:
222 # root request 222 # root request
223 defer.returnValue(self._getRootArgs()) 223 defer.returnValue(self._getRootArgs())
228 found_file = yield self._getFileData(client, session_data, command_form) 228 found_file = yield self._getFileData(client, session_data, command_form)
229 except WorkflowError as e: 229 except WorkflowError as e:
230 defer.returnValue(e.err_args) 230 defer.returnValue(e.err_args)
231 231
232 # management request 232 # management request
233 if found_file[u'type'] == C.FILE_TYPE_DIRECTORY: 233 if found_file['type'] == C.FILE_TYPE_DIRECTORY:
234 instructions = D_(u"Please select permissions for this directory") 234 instructions = D_("Please select permissions for this directory")
235 else: 235 else:
236 instructions = D_(u"Please select permissions for this file") 236 instructions = D_("Please select permissions for this file")
237 237
238 form = data_form.Form("form", title=u"File Management", 238 form = data_form.Form("form", title="File Management",
239 instructions=[instructions], 239 instructions=[instructions],
240 formNamespace=NS_FILE_MANAGEMENT) 240 formNamespace=NS_FILE_MANAGEMENT)
241 field = data_form.Field( 241 field = data_form.Field(
242 "text-multi", "read_allowed", required=False, 242 "text-multi", "read_allowed", required=False,
243 desc=u'list of jids allowed to read this file (beside yourself), or ' 243 desc='list of jids allowed to read this file (beside yourself), or '
244 u'"PUBLIC" to let a public access' 244 '"PUBLIC" to let a public access'
245 ) 245 )
246 read_access = found_file[u"access"].get(C.ACCESS_PERM_READ, {}) 246 read_access = found_file["access"].get(C.ACCESS_PERM_READ, {})
247 access_type = read_access.get(u'type', C.ACCESS_TYPE_WHITELIST) 247 access_type = read_access.get('type', C.ACCESS_TYPE_WHITELIST)
248 if access_type == C.ACCESS_TYPE_PUBLIC: 248 if access_type == C.ACCESS_TYPE_PUBLIC:
249 field.values = [u'PUBLIC'] 249 field.values = ['PUBLIC']
250 else: 250 else:
251 field.values = read_access.get('jids', []) 251 field.values = read_access.get('jids', [])
252 form.addField(field) 252 form.addField(field)
253 if found_file[u'type'] == C.FILE_TYPE_DIRECTORY: 253 if found_file['type'] == C.FILE_TYPE_DIRECTORY:
254 field = data_form.Field( 254 field = data_form.Field(
255 "boolean", "recursive", value=False, required=False, 255 "boolean", "recursive", value=False, required=False,
256 desc=u"Files under it will be made public to follow this dir " 256 desc="Files under it will be made public to follow this dir "
257 u"permission (only if they don't have already a permission set)." 257 "permission (only if they don't have already a permission set)."
258 ) 258 )
259 form.addField(field) 259 form.addField(field)
260 260
261 status = self._c.STATUS.EXECUTING 261 status = self._c.STATUS.EXECUTING
262 payload = form.toElement() 262 payload = form.toElement()
267 try: 267 try:
268 read_allowed = command_form.fields['read_allowed'] 268 read_allowed = command_form.fields['read_allowed']
269 except KeyError: 269 except KeyError:
270 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 270 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
271 271
272 if read_allowed.value == u'PUBLIC': 272 if read_allowed.value == 'PUBLIC':
273 allowed_jids = u'PUBLIC' 273 allowed_jids = 'PUBLIC'
274 elif read_allowed.value.strip() == u'': 274 elif read_allowed.value.strip() == '':
275 allowed_jids = None 275 allowed_jids = None
276 else: 276 else:
277 try: 277 try:
278 allowed_jids = [jid.JID(v.strip()) for v in read_allowed.values 278 allowed_jids = [jid.JID(v.strip()) for v in read_allowed.values
279 if v.strip()] 279 if v.strip()]
280 except RuntimeError as e: 280 except RuntimeError as e:
281 log.warning(_(u"Can't use read_allowed values: {reason}").format( 281 log.warning(_("Can't use read_allowed values: {reason}").format(
282 reason=e)) 282 reason=e))
283 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 283 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
284 284
285 if found_file[u'type'] == C.FILE_TYPE_FILE: 285 if found_file['type'] == C.FILE_TYPE_FILE:
286 yield self.host.memory.fileUpdate( 286 yield self.host.memory.fileUpdate(
287 found_file[u'id'], u'access', 287 found_file['id'], 'access',
288 partial(self._updateReadPermission, allowed_jids=allowed_jids)) 288 partial(self._updateReadPermission, allowed_jids=allowed_jids))
289 else: 289 else:
290 try: 290 try:
291 recursive = command_form.fields['recursive'] 291 recursive = command_form.fields['recursive']
292 except KeyError: 292 except KeyError:
293 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 293 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
294 yield self.host.memory.fileUpdate( 294 yield self.host.memory.fileUpdate(
295 found_file[u'id'], u'access', 295 found_file['id'], 'access',
296 partial(self._updateReadPermission, allowed_jids=allowed_jids)) 296 partial(self._updateReadPermission, allowed_jids=allowed_jids))
297 if recursive: 297 if recursive:
298 # we set all file under the directory as public (if they haven't 298 # we set all file under the directory as public (if they haven't
299 # already a permission set), so allowed entities of root directory 299 # already a permission set), so allowed entities of root directory
300 # can read them. 300 # can read them.
301 namespace = session_data[u'namespace'] 301 namespace = session_data['namespace']
302 yield self._updateDir( 302 yield self._updateDir(
303 client, requestor_bare, namespace, found_file, u'PUBLIC') 303 client, requestor_bare, namespace, found_file, 'PUBLIC')
304 304
305 # job done, we can end the session 305 # job done, we can end the session
306 status = self._c.STATUS.COMPLETED 306 status = self._c.STATUS.COMPLETED
307 payload = None 307 payload = None
308 note = (self._c.NOTE.INFO, _(u"management session done")) 308 note = (self._c.NOTE.INFO, _("management session done"))
309 defer.returnValue((payload, status, None, note)) 309 defer.returnValue((payload, status, None, note))
310 310
311 @defer.inlineCallbacks 311 @defer.inlineCallbacks
312 def _onDeleteFile(self, client, command_elt, session_data, action, node): 312 def _onDeleteFile(self, client, command_elt, session_data, action, node):
313 try: 313 try:
314 x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next() 314 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
315 command_form = data_form.Form.fromElement(x_elt) 315 command_form = data_form.Form.fromElement(x_elt)
316 except StopIteration: 316 except StopIteration:
317 command_form = None 317 command_form = None
318 318
319 found_file = session_data.get('found_file') 319 found_file = session_data.get('found_file')
320 requestor = session_data[u'requestor'] 320 requestor = session_data['requestor']
321 requestor_bare = requestor.userhostJID() 321 requestor_bare = requestor.userhostJID()
322 322
323 if command_form is None or len(command_form.fields) == 0: 323 if command_form is None or len(command_form.fields) == 0:
324 # root request 324 # root request
325 defer.returnValue(self._getRootArgs()) 325 defer.returnValue(self._getRootArgs())
328 # file selected, we need confirmation before actually deleting 328 # file selected, we need confirmation before actually deleting
329 try: 329 try:
330 found_file = yield self._getFileData(client, session_data, command_form) 330 found_file = yield self._getFileData(client, session_data, command_form)
331 except WorkflowError as e: 331 except WorkflowError as e:
332 defer.returnValue(e.err_args) 332 defer.returnValue(e.err_args)
333 if found_file[u'type'] == C.FILE_TYPE_DIRECTORY: 333 if found_file['type'] == C.FILE_TYPE_DIRECTORY:
334 msg = D_(u"Are you sure to delete directory {name} and all files and " 334 msg = D_("Are you sure to delete directory {name} and all files and "
335 u"directories under it?").format(name=found_file[u'name']) 335 "directories under it?").format(name=found_file['name'])
336 else: 336 else:
337 msg = D_(u"Are you sure to delete file {name}?" 337 msg = D_("Are you sure to delete file {name}?"
338 .format(name=found_file[u'name'])) 338 .format(name=found_file['name']))
339 form = data_form.Form("form", title=u"File Management", 339 form = data_form.Form("form", title="File Management",
340 instructions = [msg], 340 instructions = [msg],
341 formNamespace=NS_FILE_MANAGEMENT) 341 formNamespace=NS_FILE_MANAGEMENT)
342 field = data_form.Field( 342 field = data_form.Field(
343 "boolean", "confirm", value=False, required=True, 343 "boolean", "confirm", value=False, required=True,
344 desc=u"check this box to confirm" 344 desc="check this box to confirm"
345 ) 345 )
346 form.addField(field) 346 form.addField(field)
347 status = self._c.STATUS.EXECUTING 347 status = self._c.STATUS.EXECUTING
348 payload = form.toElement() 348 payload = form.toElement()
349 defer.returnValue((payload, status, None, None)) 349 defer.returnValue((payload, status, None, None))
355 except KeyError: 355 except KeyError:
356 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD) 356 self._c.adHocError(self._c.ERROR.BAD_PAYLOAD)
357 if not confirmed: 357 if not confirmed:
358 note = None 358 note = None
359 else: 359 else:
360 recursive = found_file[u'type'] == C.FILE_TYPE_DIRECTORY 360 recursive = found_file['type'] == C.FILE_TYPE_DIRECTORY
361 yield self.host.memory.fileDelete( 361 yield self.host.memory.fileDelete(
362 client, requestor_bare, found_file[u'id'], recursive) 362 client, requestor_bare, found_file['id'], recursive)
363 note = (self._c.NOTE.INFO, _(u"file deleted")) 363 note = (self._c.NOTE.INFO, _("file deleted"))
364 status = self._c.STATUS.COMPLETED 364 status = self._c.STATUS.COMPLETED
365 payload = None 365 payload = None
366 defer.returnValue((payload, status, None, note)) 366 defer.returnValue((payload, status, None, note))
367 367
368 def _updateThumbs(self, extra, thumbnails): 368 def _updateThumbs(self, extra, thumbnails):
372 def _genThumbs(self, client, requestor, namespace, file_data): 372 def _genThumbs(self, client, requestor, namespace, file_data):
373 """Recursively generate thumbnails 373 """Recursively generate thumbnails
374 374
375 @param file_data(dict): metadata of the file 375 @param file_data(dict): metadata of the file
376 """ 376 """
377 if file_data[u'type'] == C.FILE_TYPE_DIRECTORY: 377 if file_data['type'] == C.FILE_TYPE_DIRECTORY:
378 sub_files_data = yield self.host.memory.getFiles( 378 sub_files_data = yield self.host.memory.getFiles(
379 client, requestor, parent=file_data[u'id'], namespace=namespace) 379 client, requestor, parent=file_data['id'], namespace=namespace)
380 for sub_file_data in sub_files_data: 380 for sub_file_data in sub_files_data:
381 yield self._genThumbs(client, requestor, namespace, sub_file_data) 381 yield self._genThumbs(client, requestor, namespace, sub_file_data)
382 382
383 elif file_data[u'type'] == C.FILE_TYPE_FILE: 383 elif file_data['type'] == C.FILE_TYPE_FILE:
384 mime_type = file_data[u'mime_type'] 384 mime_type = file_data['mime_type']
385 file_path = os.path.join(self.files_path, file_data[u'file_hash']) 385 file_path = os.path.join(self.files_path, file_data['file_hash'])
386 if mime_type is not None and mime_type.startswith(u"image"): 386 if mime_type is not None and mime_type.startswith("image"):
387 thumbnails = [] 387 thumbnails = []
388 388
389 for max_thumb_size in (self._t.SIZE_SMALL, self._t.SIZE_MEDIUM): 389 for max_thumb_size in (self._t.SIZE_SMALL, self._t.SIZE_MEDIUM):
390 try: 390 try:
391 thumb_size, thumb_id = yield self._t.generateThumbnail( 391 thumb_size, thumb_id = yield self._t.generateThumbnail(
393 max_thumb_size, 393 max_thumb_size,
394 #  we keep thumbnails for 6 months 394 #  we keep thumbnails for 6 months
395 60 * 60 * 24 * 31 * 6, 395 60 * 60 * 24 * 31 * 6,
396 ) 396 )
397 except Exception as e: 397 except Exception as e:
398 log.warning(_(u"Can't create thumbnail: {reason}") 398 log.warning(_("Can't create thumbnail: {reason}")
399 .format(reason=e)) 399 .format(reason=e))
400 break 400 break
401 thumbnails.append({u"id": thumb_id, u"size": thumb_size}) 401 thumbnails.append({"id": thumb_id, "size": thumb_size})
402 402
403 yield self.host.memory.fileUpdate( 403 yield self.host.memory.fileUpdate(
404 file_data[u'id'], u'extra', 404 file_data['id'], 'extra',
405 partial(self._updateThumbs, thumbnails=thumbnails)) 405 partial(self._updateThumbs, thumbnails=thumbnails))
406 406
407 log.info(u"thumbnails for [{file_name}] generated" 407 log.info("thumbnails for [{file_name}] generated"
408 .format(file_name=file_data[u'name'])) 408 .format(file_name=file_data['name']))
409 409
410 else: 410 else:
411 log.warning(u"unmanaged file type: {type_}".format(type_=file_data[u'type'])) 411 log.warning("unmanaged file type: {type_}".format(type_=file_data['type']))
412 412
413 @defer.inlineCallbacks 413 @defer.inlineCallbacks
414 def _onGenThumbnails(self, client, command_elt, session_data, action, node): 414 def _onGenThumbnails(self, client, command_elt, session_data, action, node):
415 try: 415 try:
416 x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next() 416 x_elt = next(command_elt.elements(data_form.NS_X_DATA, "x"))
417 command_form = data_form.Form.fromElement(x_elt) 417 command_form = data_form.Form.fromElement(x_elt)
418 except StopIteration: 418 except StopIteration:
419 command_form = None 419 command_form = None
420 420
421 found_file = session_data.get('found_file') 421 found_file = session_data.get('found_file')
422 requestor = session_data[u'requestor'] 422 requestor = session_data['requestor']
423 423
424 if command_form is None or len(command_form.fields) == 0: 424 if command_form is None or len(command_form.fields) == 0:
425 # root request 425 # root request
426 defer.returnValue(self._getRootArgs()) 426 defer.returnValue(self._getRootArgs())
427 427
430 try: 430 try:
431 found_file = yield self._getFileData(client, session_data, command_form) 431 found_file = yield self._getFileData(client, session_data, command_form)
432 except WorkflowError as e: 432 except WorkflowError as e:
433 defer.returnValue(e.err_args) 433 defer.returnValue(e.err_args)
434 434
435 log.info(u"Generating thumbnails as requested") 435 log.info("Generating thumbnails as requested")
436 yield self._genThumbs(client, requestor, found_file[u'namespace'], found_file) 436 yield self._genThumbs(client, requestor, found_file['namespace'], found_file)
437 437
438 # job done, we can end the session 438 # job done, we can end the session
439 status = self._c.STATUS.COMPLETED 439 status = self._c.STATUS.COMPLETED
440 payload = None 440 payload = None
441 note = (self._c.NOTE.INFO, _(u"thumbnails generated")) 441 note = (self._c.NOTE.INFO, _("thumbnails generated"))
442 defer.returnValue((payload, status, None, note)) 442 defer.returnValue((payload, status, None, note))