mirror of
https://github.com/lucaspalomodevelop/indico-plugins.git
synced 2026-03-12 23:27:22 +00:00
Triggering a change for the event is useless, but in case of scheduling or unscheduling contributions (in conferences) we need to record this change in order to update their time.
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.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))
|