1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__
import unicode_literals
19 from flask
import flash
, request
, session
20 from werkzeug
.utils
import secure_filename
22 from indico
.core
.db
import db
23 from indico
.core
import signals
24 from indico
.modules
.attachments
import logger
25 from indico
.modules
.attachments
.controllers
.util
import SpecificAttachmentMixin
, SpecificFolderMixin
26 from indico
.modules
.attachments
.forms
import (AddAttachmentFilesForm
, AttachmentLinkForm
, AttachmentFolderForm
,
27 EditAttachmentFileForm
)
28 from indico
.modules
.attachments
.models
.folders
import AttachmentFolder
29 from indico
.modules
.attachments
.models
.attachments
import Attachment
, AttachmentFile
, AttachmentType
30 from indico
.modules
.attachments
.util
import get_attached_items
31 from indico
.util
.i18n
import _
, ngettext
32 from indico
.web
.flask
.templating
import get_template_module
33 from indico
.web
.flask
.util
import url_for
34 from indico
.web
.forms
.base
import FormDefaults
35 from indico
.web
.util
import jsonify_template
, jsonify_data
38 def _render_attachment_list(linked_object
):
39 tpl
= get_template_module('attachments/_attachments.html')
40 return tpl
.render_attachments(attachments
=get_attached_items(linked_object
), linked_object
=linked_object
)
43 class ManageAttachmentsMixin
:
44 """Shows the attachment management page"""
48 return self
.wp
.render_template('attachments.html', self
.object, linked_object
=self
.object,
49 linked_object_type
=self
.object_type
, attachments
=get_attached_items(self
.object))
52 class AddAttachmentFilesMixin
:
53 """Upload file attachments"""
56 form
= AddAttachmentFilesForm(linked_object
=self
.object)
57 if form
.validate_on_submit():
58 files
= request
.files
.getlist('file')
59 folder
= form
.folder
.data
or AttachmentFolder
.get_or_create_default(linked_object
=self
.object)
61 filename
= secure_filename(f
.filename
) or 'attachment'
62 attachment
= Attachment(folder
=folder
, user
=session
.user
, title
=f
.filename
, type=AttachmentType
.file,
63 protection_mode
=form
.protection_mode
.data
)
64 attachment
.file = AttachmentFile(user
=session
.user
, filename
=filename
, content_type
=f
.mimetype
)
65 attachment
.file.save(f
.file)
66 db
.session
.add(attachment
)
67 logger
.info('File attachment {} added by {}'.format(attachment
, session
.user
))
68 signals
.attachments
.attachment_created
.send(attachment
, user
=session
.user
)
69 flash(ngettext("The attachment has been uploaded", "%(num)d attachments have been uploaded", len(files
)),
71 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
72 return jsonify_template('attachments/upload.html', form
=form
, action
=url_for('.upload', self
.object))
75 class AddAttachmentLinkMixin
:
76 """Add link attachment"""
79 form
= AttachmentLinkForm(linked_object
=self
.object)
80 if form
.validate_on_submit():
81 folder
= form
.folder
.data
or AttachmentFolder
.get_or_create_default(linked_object
=self
.object)
82 link
= Attachment(user
=session
.user
, type=AttachmentType
.link
)
83 form
.populate_obj(link
, skip
={'acl'})
86 link
.acl
= form
.acl
.data
88 logger
.info('Link attachment {} added by {}'.format(link
, session
.user
))
89 signals
.attachments
.attachment_created
.send(link
, user
=session
.user
)
90 flash(_("The link has been added"), 'success')
91 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
92 return jsonify_template('attachments/add_link.html', form
=form
)
95 class EditAttachmentMixin(SpecificAttachmentMixin
):
96 """Edit an attachment"""
99 defaults
= FormDefaults(self
.attachment
, protected
=self
.attachment
.is_protected
)
100 form_cls
= EditAttachmentFileForm
if self
.attachment
.type == AttachmentType
.file else AttachmentLinkForm
101 form
= form_cls(linked_object
=self
.object, obj
=defaults
)
102 if form
.validate_on_submit():
103 folder
= form
.folder
.data
or AttachmentFolder
.get_or_create_default(linked_object
=self
.object)
104 logger
.info('Edited attachment {} by {}'.format(self
.attachment
, session
.user
))
105 form
.populate_obj(self
.attachment
, skip
={'acl'})
106 self
.attachment
.folder
= folder
107 if self
.attachment
.is_protected
:
108 self
.attachment
.acl
= form
.acl
.data
109 # files need special handling; links are already updated in `populate_obj`
110 if self
.attachment
.type == AttachmentType
.file:
111 file = request
.files
['file'] if request
.files
else None
113 self
.attachment
.file = AttachmentFile(user
=session
.user
, filename
=secure_filename(file.filename
),
114 content_type
=file.mimetype
)
115 self
.attachment
.file.save(file.file)
117 signals
.attachments
.attachment_updated
.send(self
.attachment
, user
=session
.user
)
118 flash(_("The attachment has been updated"), 'success')
119 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
121 template
= ('attachments/upload.html' if self
.attachment
.type == AttachmentType
.file else
122 'attachments/add_link.html')
123 return jsonify_template(template
, form
=form
, existing_attachment
=self
.attachment
,
124 action
=url_for('.modify_attachment', self
.attachment
))
127 class CreateFolderMixin
:
128 """Create a new empty folder"""
131 form
= AttachmentFolderForm(obj
=FormDefaults(is_always_visible
=True))
132 if form
.validate_on_submit():
133 folder
= AttachmentFolder(linked_object
=self
.object)
134 form
.populate_obj(folder
, skip
={'acl'})
135 if folder
.is_protected
:
136 folder
.acl
= form
.acl
.data
137 db
.session
.add(folder
)
138 logger
.info('Folder {} created by {}'.format(folder
, session
.user
))
139 signals
.attachments
.folder_created
.send(folder
, user
=session
.user
)
140 flash(_("Folder \"{name}\" created").format(name
=folder
.title
), 'success')
141 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
142 return jsonify_template('attachments/create_folder.html', form
=form
)
145 class EditFolderMixin(SpecificFolderMixin
):
149 defaults
= FormDefaults(self
.folder
, protected
=self
.folder
.is_protected
)
150 form
= AttachmentFolderForm(obj
=defaults
)
151 if form
.validate_on_submit():
152 form
.populate_obj(self
.folder
, skip
={'acl'})
153 if self
.folder
.is_protected
:
154 self
.folder
.acl
= form
.acl
.data
155 logger
.info('Folder {} updated by {}'.format(self
.folder
, session
.user
))
156 signals
.attachments
.folder_updated
.send(self
.folder
, user
=session
.user
)
157 flash(_("Folder \"{name}\" updated").format(name
=self
.folder
.title
), 'success')
158 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
159 return jsonify_template('attachments/create_folder.html', form
=form
)
162 class DeleteFolderMixin(SpecificFolderMixin
):
163 """Delete a folder"""
166 self
.folder
.is_deleted
= True
167 logger
.info('Folder {} deleted by {}'.format(self
.folder
, session
.user
))
168 signals
.attachments
.folder_deleted
.send(self
.folder
, user
=session
.user
)
169 flash(_("Folder \"{name}\" deleted").format(name
=self
.folder
.title
), 'success')
170 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))
173 class DeleteAttachmentMixin(SpecificAttachmentMixin
):
174 """Delete an attachment"""
177 self
.attachment
= Attachment
.get_one(request
.view_args
['attachment_id'])
178 self
.attachment
.is_deleted
= True
179 logger
.info('Deleted attachment {} by {}'.format(self
.attachment
, session
.user
))
180 signals
.attachments
.attachment_deleted
.send(self
.attachment
, user
=session
.user
)
181 flash(_("Attachment \"{name}\" deleted").format(name
=self
.attachment
.title
), 'success')
182 return jsonify_data(attachment_list
=_render_attachment_list(self
.object))