def _merge_users(target, source, **kwargs): from indico.modules.attachments.models.attachments import Attachment, AttachmentFile from indico.modules.attachments.models.principals import AttachmentPrincipal, AttachmentFolderPrincipal Attachment.find(user_id=source.id).update({Attachment.user_id: target.id}) AttachmentFile.find(user_id=source.id).update({AttachmentFile.user_id: target.id}) AttachmentPrincipal.merge_users(target, source, 'attachment') AttachmentFolderPrincipal.merge_users(target, source, 'folder')
def update_merged_ids(self): self.print_step('updating merged users in attachment acls') for p in AttachmentPrincipal.find(User.merged_into_id != None, _join=AttachmentPrincipal.user): # noqa user = p.user while p.user.merged_into_user: p.user = p.user.merged_into_user self.print_success(cformat('%{cyan}{}%{reset} -> %{cyan}{}%{reset}').format(user, p.user), always=True) self.print_step('updating merged users in folder acls') for p in AttachmentFolderPrincipal.find(User.merged_into_id != None, _join=AttachmentFolderPrincipal.user): # noqa while p.user.merged_into_user: p.user = p.user.merged_into_user self.print_success(cformat('%{cyan}{}%{reset} -> %{cyan}{}%{reset}').format(user, p.user), always=True) db.session.commit()
class Attachment(ProtectionMixin, VersionedResourceMixin, db.Model): __tablename__ = 'attachments' __table_args__ = ( # links: url but no file db.CheckConstraint( f'type != {AttachmentType.link.value} OR (link_url IS NOT NULL AND file_id IS NULL)', 'valid_link'), # we can't require the file_id to be NOT NULL for files because of the circular relationship... # but we can ensure that we never have both a file_id AND a link_url...for db.CheckConstraint('link_url IS NULL OR file_id IS NULL', 'link_or_file'), { 'schema': 'attachments' }) stored_file_table = 'attachments.files' stored_file_class = AttachmentFile stored_file_fkey = 'attachment_id' #: The ID of the attachment id = db.Column(db.Integer, primary_key=True) #: The ID of the folder the attachment belongs to folder_id = db.Column(db.Integer, db.ForeignKey('attachments.folders.id'), nullable=False, index=True) #: The ID of the user who created the attachment user_id = db.Column(db.Integer, db.ForeignKey('users.users.id'), index=True, nullable=False) #: If the attachment has been deleted is_deleted = db.Column(db.Boolean, nullable=False, default=False) #: The name of the attachment title = db.Column(db.String, nullable=False) #: The description of the attachment description = db.Column(db.Text, nullable=False, default='') #: The date/time when the attachment was created/modified modified_dt = db.Column(UTCDateTime, nullable=False, default=now_utc, onupdate=now_utc) #: The type of the attachment (file or link) type = db.Column(PyIntEnum(AttachmentType), nullable=False) #: The target URL for a link attachment link_url = db.Column(db.String, nullable=True) #: The user who created the attachment user = db.relationship('User', lazy=True, backref=db.backref('attachments', lazy='dynamic')) #: The folder containing the attachment folder = db.relationship('AttachmentFolder', lazy=True, backref=db.backref('all_attachments', lazy=True)) acl_entries = db.relationship('AttachmentPrincipal', backref='attachment', cascade='all, delete-orphan', collection_class=set) #: The ACL of the folder (used for ProtectionMode.protected) acl = association_proxy('acl_entries', 'principal', creator=lambda v: AttachmentPrincipal(principal=v)) # relationship backrefs: # - legacy_mapping (LegacyAttachmentMapping.attachment) @property def protection_parent(self): return self.folder @property def locator(self): return dict(self.folder.locator, attachment_id=self.id) def get_download_url(self, absolute=False): """Return the download url for the attachment. During static site generation this returns a local URL for the file or the target URL for the link. :param absolute: If the returned URL should be absolute. """ if g.get('static_site'): return _offline_download_url(self) else: filename = self.file.filename if self.type == AttachmentType.file else 'go' return url_for('attachments.download', self, filename=filename, _external=absolute) @property def download_url(self): """The download url for the attachment.""" return self.get_download_url() @property def absolute_download_url(self): """The absolute download url for the attachment.""" return self.get_download_url(absolute=True) def can_access(self, user, *args, **kwargs): """Check if the user is allowed to access the attachment. This is the case if the user has access to see the attachment or if the user can manage attachments for the linked object. """ return (super().can_access(user, *args, **kwargs) or can_manage_attachments(self.folder.object, user)) def __repr__(self): return '<Attachment({}, {}, {}{}, {}, {})>'.format( self.id, self.title, self.file if self.type == AttachmentType.file else self.link_url, ', is_deleted=True' if self.is_deleted else '', self.protection_repr, self.folder_id)