Ticket #3068: attachment-api.diff
| File attachment-api.diff, 9.5 kB (added by athomas, 2 years ago) |
|---|
-
trac/attachment.py
33 33 from trac.wiki import IWikiSyntaxProvider 34 34 35 35 36 class IAttachmentPointProvider(Interface): 37 def get_attachment_points(): 38 """Provide details on file attachment points provided by this 39 component. Returns an iterable of tuples in the form `(type, 40 description, title, (view_perm, write_perm, delete_perm))`. 41 `description` and `title` will be used as the formatting string for the 42 attachment point identifier.""" 43 44 36 45 class Attachment(object): 37 46 38 47 def __init__(self, env, parent_type, parent_id, filename=None, db=None): … … 86 95 return req.href(self.parent_type, self.parent_id) 87 96 88 97 def _get_title(self): 89 return '%s%s: %s' % (self.parent_type == 'ticket' and '#' or '', 90 self.parent_id, self.filename) 98 title = AttachmentModule(self.env)._get_attachment_point(self.parent_type)[2] 99 title = title % self.parent_id 100 return '%s: %s' % (title, self.filename) 91 101 title = property(_get_title) 92 102 93 103 def delete(self, db=None): … … 215 225 implements(IEnvironmentSetupParticipant, IRequestHandler, 216 226 INavigationContributor, IWikiSyntaxProvider) 217 227 228 attachment_points = ExtensionPoint(IAttachmentPointProvider) 229 218 230 CHUNK_SIZE = 4096 219 231 220 232 max_size = IntOption('attachment', 'max_size', 262144, 221 """Maximum allowed file size for ticket and wikiattachments.""")233 """Maximum allowed file size for attachments.""") 222 234 223 235 render_unsafe_content = BoolOption('attachment', 'render_unsafe_content', 224 236 'false', … … 256 268 # IRequestHandler methods 257 269 258 270 def match_request(self, req): 259 match = re.match(r'^/attachment/( ticket|wiki)(?:[/:](.*))?$',271 match = re.match(r'^/attachment/([^/]+)(?:[/:](.*))?$', 260 272 req.path_info) 261 273 if match: 262 274 req.args['type'] = match.group(1) … … 268 280 path = req.args.get('path') 269 281 if not parent_type or not path: 270 282 raise HTTPBadRequest('Bad request') 271 if not parent_type in ['ticket', 'wiki']: 272 raise HTTPBadRequest('Unknown attachment type') 283 for point in self.attachment_points: 284 for descriptor in point.get_attachment_points(): 285 if descriptor[0] == parent_type: 286 break 287 else: 288 continue 289 break 290 else: 291 raise HTTPBadRequest('Unknown attachment type "%s"' % parent_type) 273 292 274 293 action = req.args.get('action', 'view') 275 294 if action == 'new': … … 306 325 def _parent_to_hdf(self, req, parent_type, parent_id): 307 326 # Populate attachment.parent: 308 327 parent_link = req.href(parent_type, parent_id) 309 if parent_type == 'ticket': 310 parent_text = 'Ticket #' + parent_id 311 else: # 'wiki' 312 parent_text = parent_id 328 type, description, title, perms = self._get_attachment_point(parent_type) 329 parent_text = description % parent_id 313 330 req.hdf['attachment.parent'] = { 314 331 'type': parent_type, 'id': parent_id, 315 332 'name': parent_text, 'href': parent_link … … 326 343 327 344 # Internal methods 328 345 346 def _get_attachment_point(self, point_type): 347 for point in self.attachment_points: 348 for descriptor in point.get_attachment_points(): 349 if descriptor[0] == point_type: 350 return descriptor 351 raise TracError('No attachment point found for %s' % 352 attachment.parent_type) 353 354 def _get_permission(self, operation, attachment): 355 """Find the actual permission required to perform `operation`, which can 356 be one of `view`, `write`, `delete`.""" 357 operation = {'view': 0, 'modify': 1, 'delete': 2}[operation] 358 return self._get_attachment_point(attachment.parent_type)[-1][operation] 359 329 360 def _do_save(self, req, attachment): 330 perm_map = {'ticket': 'TICKET_APPEND', 'wiki': 'WIKI_MODIFY'} 331 req.perm.assert_permission(perm_map[attachment.parent_type]) 361 req.perm.assert_permission(self._get_permission('modify', attachment)) 332 362 333 363 if req.args.has_key('cancel'): 334 364 req.redirect(attachment.parent_href(req)) … … 367 397 attachment.parent_id, filename) 368 398 if not (old_attachment.author and req.authname \ 369 399 and old_attachment.author == req.authname): 370 perm_map = {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE'}371 req.perm.assert_permission(perm_map[old_attachment.parent_type])400 req.perm.assert_permission(self._get_permission('delete', 401 old_attachment)) 372 402 old_attachment.delete() 373 403 except TracError: 374 404 pass # don't worry if there's nothing to replace … … 379 409 req.redirect(attachment.href(req)) 380 410 381 411 def _do_delete(self, req, attachment): 382 perm_map = {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE'} 383 req.perm.assert_permission(perm_map[attachment.parent_type]) 412 req.perm.assert_permission(self._get_permission('delete', attachment)) 384 413 385 414 if req.args.has_key('cancel'): 386 415 req.redirect(attachment.href(req)) … … 391 420 req.redirect(attachment.parent_href(req)) 392 421 393 422 def _render_confirm(self, req, attachment): 394 perm_map = {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE'} 395 req.perm.assert_permission(perm_map[attachment.parent_type]) 423 req.perm.assert_permission(self._get_permission('delete', attachment)) 396 424 397 425 req.hdf['title'] = '%s (delete)' % attachment.title 398 426 req.hdf['attachment'] = {'filename': attachment.filename, 399 427 'mode': 'delete'} 400 428 401 429 def _render_form(self, req, attachment): 402 perm_map = {'ticket': 'TICKET_APPEND', 'wiki': 'WIKI_MODIFY'} 403 req.perm.assert_permission(perm_map[attachment.parent_type]) 430 req.perm.assert_permission(self._get_permission('modify', attachment)) 404 431 405 432 req.hdf['attachment'] = {'mode': 'new', 406 433 'author': util.get_reporter_id(req)} 407 434 408 435 def _render_view(self, req, attachment): 409 perm_map = {'ticket': 'TICKET_VIEW', 'wiki': 'WIKI_VIEW'} 410 req.perm.assert_permission(perm_map[attachment.parent_type]) 436 req.perm.assert_permission(self._get_permission('view', attachment)) 411 437 412 438 req.check_modified(attachment.time) 413 439 … … 416 442 req.hdf['attachment'] = attachment_to_hdf(self.env, req, None, 417 443 attachment) 418 444 419 perm_map = {'ticket': 'TICKET_ADMIN', 'wiki': 'WIKI_DELETE'} 420 if req.perm.has_permission(perm_map[attachment.parent_type]): 445 if req.perm.has_permission(self._get_permission('delete', attachment)): 421 446 req.hdf['attachment.can_delete'] = 1 422 447 423 448 fd = attachment.open() -
trac/ticket/web_ui.py
18 18 import re 19 19 import time 20 20 21 from trac.attachment import attachments_to_hdf, Attachment 21 from trac.attachment import attachments_to_hdf, Attachment, \ 22 IAttachmentPointProvider 22 23 from trac.config import BoolOption, Option 23 24 from trac.core import * 24 25 from trac.env import IEnvironmentSetupParticipant … … 179 180 180 181 class TicketModule(TicketModuleBase): 181 182 182 implements(INavigationContributor, IRequestHandler, ITimelineEventProvider) 183 implements(INavigationContributor, IRequestHandler, ITimelineEventProvider, 184 IAttachmentPointProvider) 183 185 184 186 default_version = Option('ticket', 'default_version', '', 185 187 """Default version for newly created tickets.""") … … 200 202 """Enable the display of all ticket changes in the timeline 201 203 (''since 0.9'').""") 202 204 205 # IAttachmentPointProvider methods 206 207 def get_attachment_points(self): 208 yield ('ticket', 'Ticket #%s', '#%s', 209 ('TICKET_VIEW', 'TICKET_APPEND', 'TICKET_ADMIN')) 210 203 211 # INavigationContributor methods 204 212 205 213 def get_active_navigation_item(self, req): -
trac/wiki/web_ui.py
19 19 import re 20 20 import StringIO 21 21 22 from trac.attachment import attachments_to_hdf, Attachment 22 from trac.attachment import attachments_to_hdf, Attachment, \ 23 IAttachmentPointProvider 23 24 from trac.core import * 24 25 from trac.perm import IPermissionRequestor 25 26 from trac.Search import ISearchSource, search_to_sql, shorten_result … … 38 39 class WikiModule(Component): 39 40 40 41 implements(INavigationContributor, IPermissionRequestor, IRequestHandler, 41 ITimelineEventProvider, ISearchSource )42 ITimelineEventProvider, ISearchSource, IAttachmentPointProvider) 42 43 43 44 page_manipulators = ExtensionPoint(IWikiPageManipulator) 44 45 46 # IAttachmentPointProvider methods 47 48 def get_attachment_points(self): 49 yield ('wiki', '%s', '%s', ('WIKI_VIEW', 'WIKI_MODIFY', 'WIKI_DELETE')) 50 45 51 # INavigationContributor methods 46 52 47 53 def get_active_navigation_item(self, req):
