mirror of
https://github.com/lucaspalomodevelop/indico-plugins.git
synced 2026-03-13 07:29:39 +00:00
237 lines
9.8 KiB
Python
237 lines
9.8 KiB
Python
# This file is part of the Indico plugins.
|
|
# Copyright (C) 2002 - 2021 CERN
|
|
#
|
|
# The Indico plugins are free software; you can redistribute
|
|
# them and/or modify them under the terms of the MIT License;
|
|
# see the LICENSE file for more details.
|
|
|
|
from collections import defaultdict
|
|
|
|
from flask import g
|
|
from sqlalchemy import inspect
|
|
|
|
from indico.core import signals
|
|
from indico.core.db.sqlalchemy.links import LinkType
|
|
from indico.core.db.sqlalchemy.protection import ProtectionMode
|
|
from indico.modules.attachments.models.attachments import Attachment
|
|
from indico.modules.attachments.models.folders import AttachmentFolder
|
|
from indico.modules.categories.models.categories import Category
|
|
from indico.modules.events import Event
|
|
from indico.modules.events.contributions.models.contributions import Contribution
|
|
from indico.modules.events.contributions.models.subcontributions import SubContribution
|
|
from indico.modules.events.notes.models.notes import EventNote
|
|
from indico.modules.events.sessions import Session
|
|
from indico.modules.events.sessions.models.blocks import SessionBlock
|
|
from indico.modules.events.timetable.models.entries import TimetableEntryType
|
|
|
|
from indico_livesync.models.queue import ChangeType, LiveSyncQueueEntry
|
|
from indico_livesync.util import get_excluded_categories, obj_ref
|
|
|
|
|
|
def connect_signals(plugin):
|
|
# request
|
|
plugin.connect(signals.core.after_process, _apply_changes)
|
|
# moved
|
|
plugin.connect(signals.category.moved, _moved)
|
|
plugin.connect(signals.event.moved, _moved)
|
|
# created
|
|
plugin.connect(signals.event.created, _created)
|
|
plugin.connect(signals.event.restored, _restored)
|
|
plugin.connect(signals.event.contribution_created, _created)
|
|
plugin.connect(signals.event.subcontribution_created, _created)
|
|
# deleted
|
|
plugin.connect(signals.event.deleted, _deleted)
|
|
plugin.connect(signals.event.contribution_deleted, _deleted)
|
|
plugin.connect(signals.event.subcontribution_deleted, _deleted)
|
|
# updated
|
|
plugin.connect(signals.event.updated, _updated)
|
|
plugin.connect(signals.event.contribution_updated, _updated)
|
|
plugin.connect(signals.event.subcontribution_updated, _updated)
|
|
# event times
|
|
plugin.connect(signals.event.times_changed, _event_times_changed, sender=Event)
|
|
plugin.connect(signals.event.times_changed, _event_times_changed, sender=Contribution)
|
|
# location
|
|
plugin.connect(signals.event.location_changed, _location_changed, sender=Event)
|
|
plugin.connect(signals.event.location_changed, _location_changed, sender=Contribution)
|
|
plugin.connect(signals.event.location_changed, _location_changed, sender=Session)
|
|
plugin.connect(signals.event.location_changed, _session_block_location_changed, sender=SessionBlock)
|
|
# timetable
|
|
plugin.connect(signals.event.timetable_entry_created, _timetable_changed)
|
|
plugin.connect(signals.event.timetable_entry_updated, _timetable_changed)
|
|
plugin.connect(signals.event.timetable_entry_deleted, _timetable_changed)
|
|
# protection
|
|
plugin.connect(signals.acl.protection_changed, _category_protection_changed, sender=Category)
|
|
plugin.connect(signals.acl.protection_changed, _protection_changed, sender=Event)
|
|
plugin.connect(signals.acl.protection_changed, _protection_changed, sender=Session)
|
|
plugin.connect(signals.acl.protection_changed, _protection_changed, sender=Contribution)
|
|
# ACLs
|
|
plugin.connect(signals.acl.entry_changed, _acl_entry_changed, sender=Category)
|
|
plugin.connect(signals.acl.entry_changed, _acl_entry_changed, sender=Event)
|
|
plugin.connect(signals.acl.entry_changed, _acl_entry_changed, sender=Session)
|
|
plugin.connect(signals.acl.entry_changed, _acl_entry_changed, sender=Contribution)
|
|
# notes
|
|
plugin.connect(signals.event.notes.note_added, _created)
|
|
plugin.connect(signals.event.notes.note_restored, _restored)
|
|
plugin.connect(signals.event.notes.note_deleted, _deleted)
|
|
plugin.connect(signals.event.notes.note_modified, _updated)
|
|
# attachments
|
|
plugin.connect(signals.attachments.folder_deleted, _attachment_folder_deleted)
|
|
plugin.connect(signals.attachments.attachment_created, _created)
|
|
plugin.connect(signals.attachments.attachment_deleted, _attachment_deleted)
|
|
plugin.connect(signals.attachments.attachment_updated, _updated)
|
|
plugin.connect(signals.acl.protection_changed, _attachment_folder_protection_changed, sender=AttachmentFolder)
|
|
plugin.connect(signals.acl.protection_changed, _protection_changed, sender=Attachment)
|
|
plugin.connect(signals.acl.entry_changed, _attachment_folder_acl_entry_changed, sender=AttachmentFolder)
|
|
plugin.connect(signals.acl.entry_changed, _acl_entry_changed, sender=Attachment)
|
|
|
|
|
|
def _is_category_excluded(category):
|
|
excluded_categories = get_excluded_categories()
|
|
return any(c.id in excluded_categories for c in category.chain_query)
|
|
|
|
|
|
def _moved(obj, old_parent, **kwargs):
|
|
_register_change(obj, ChangeType.moved)
|
|
|
|
new_category = obj if isinstance(obj, Category) else obj.category
|
|
old_excluded = _is_category_excluded(old_parent)
|
|
new_excluded = _is_category_excluded(new_category)
|
|
if old_excluded != new_excluded:
|
|
_register_change(obj, ChangeType.unpublished if new_excluded else ChangeType.published)
|
|
|
|
if obj.is_inheriting:
|
|
# If protection is inherited, check whether it changed
|
|
category_protection = old_parent.effective_protection_mode
|
|
new_category_protection = obj.protection_parent.effective_protection_mode
|
|
# Protection of new parent is different
|
|
if category_protection != new_category_protection:
|
|
_register_change(obj, ChangeType.protection_changed)
|
|
|
|
|
|
def _created(obj, **kwargs):
|
|
if not isinstance(obj, (Event, EventNote, Attachment, Contribution, SubContribution)):
|
|
raise TypeError(f'Unexpected object: {type(obj).__name__}')
|
|
_register_change(obj, ChangeType.created)
|
|
|
|
|
|
def _restored(obj, **kwargs):
|
|
_register_change(obj, ChangeType.undeleted)
|
|
|
|
|
|
def _deleted(obj, **kwargs):
|
|
_register_deletion(obj)
|
|
|
|
|
|
def _updated(obj, **kwargs):
|
|
_register_change(obj, ChangeType.data_changed)
|
|
|
|
|
|
def _event_times_changed(sender, obj, **kwargs):
|
|
_register_change(obj, ChangeType.data_changed)
|
|
|
|
|
|
def _session_block_location_changed(sender, obj, **kwargs):
|
|
for contrib in obj.contributions:
|
|
_register_change(contrib, ChangeType.location_changed)
|
|
|
|
|
|
def _location_changed(sender, obj, **kwargs):
|
|
_register_change(obj, ChangeType.location_changed)
|
|
|
|
|
|
def _timetable_changed(entry, **kwargs):
|
|
if entry.type == TimetableEntryType.CONTRIBUTION:
|
|
_register_change(entry.object, ChangeType.data_changed)
|
|
|
|
|
|
def _category_protection_changed(sender, obj, mode, old_mode, **kwargs):
|
|
parent_mode = obj.protection_parent.effective_protection_mode if obj.protection_parent else None
|
|
if ((old_mode == ProtectionMode.inheriting and parent_mode == mode) or
|
|
(old_mode == parent_mode and mode == ProtectionMode.inheriting)):
|
|
return
|
|
_protection_changed(sender, obj, mode=mode, old_mode=old_mode, **kwargs)
|
|
|
|
|
|
def _protection_changed(sender, obj, **kwargs):
|
|
if not inspect(obj).persistent:
|
|
return
|
|
_register_change(obj, ChangeType.protection_changed)
|
|
|
|
|
|
def _acl_entry_changed(sender, obj, entry, old_data, **kwargs):
|
|
if not inspect(obj).persistent:
|
|
return
|
|
register = False
|
|
# entry deleted
|
|
if entry is None and old_data is not None:
|
|
register = True
|
|
# entry added
|
|
elif entry is not None and old_data is None:
|
|
register = True
|
|
# entry updated
|
|
elif entry is not None and old_data is not None:
|
|
old_access = bool(old_data['read_access'] or old_data['full_access'] or old_data['permissions'])
|
|
new_access = bool(entry.full_access or entry.read_access or entry.permissions)
|
|
register = old_access != new_access
|
|
if register:
|
|
_register_change(obj, ChangeType.protection_changed)
|
|
|
|
|
|
def _attachment_folder_deleted(folder, **kwargs):
|
|
if folder.link_type not in (LinkType.event, LinkType.contribution, LinkType.subcontribution):
|
|
return
|
|
for attachment in folder.attachments:
|
|
_register_deletion(attachment)
|
|
|
|
|
|
def _attachment_deleted(attachment, **kwargs):
|
|
if attachment.folder.link_type not in (LinkType.event, LinkType.contribution, LinkType.subcontribution):
|
|
return
|
|
_register_deletion(attachment)
|
|
|
|
|
|
def _attachment_folder_protection_changed(sender, obj, **kwargs):
|
|
if not inspect(obj).persistent:
|
|
return
|
|
if obj.link_type not in (LinkType.event, LinkType.contribution, LinkType.subcontribution):
|
|
return
|
|
for attachment in obj.attachments:
|
|
_register_change(attachment, ChangeType.protection_changed)
|
|
|
|
|
|
def _attachment_folder_acl_entry_changed(sender, obj, entry, old_data, **kwargs):
|
|
if not inspect(obj).persistent:
|
|
return
|
|
if obj.link_type not in (LinkType.event, LinkType.contribution, LinkType.subcontribution):
|
|
return
|
|
for attachment in obj.attachments:
|
|
_acl_entry_changed(type(attachment), attachment, entry, old_data)
|
|
|
|
|
|
def _apply_changes(sender, **kwargs):
|
|
if not hasattr(g, 'livesync_changes'):
|
|
return
|
|
excluded_categories = get_excluded_categories()
|
|
for ref, changes in g.livesync_changes.items():
|
|
LiveSyncQueueEntry.create(changes, ref, excluded_categories=excluded_categories)
|
|
|
|
|
|
def _register_deletion(obj):
|
|
_init_livesync_g()
|
|
g.livesync_changes[obj_ref(obj)].add(ChangeType.deleted)
|
|
|
|
|
|
def _register_change(obj, action):
|
|
if not isinstance(obj, Category):
|
|
event = obj.folder.event if isinstance(obj, Attachment) else obj.event
|
|
if event is None or event.is_deleted:
|
|
# When deleting an event we get data change signals afterwards. We can simple ignore them.
|
|
# Also, ACL changes during user merges might involve deleted objects which we also don't care about
|
|
return
|
|
_init_livesync_g()
|
|
g.livesync_changes[obj_ref(obj)].add(action)
|
|
|
|
|
|
def _init_livesync_g():
|
|
g.setdefault('livesync_changes', defaultdict(set))
|