Esempio n. 1
0
    def _process(self):
        defaults = FormDefaults(get_default_values(MenuEntry))
        entry_type = request.args['type']

        if entry_type == MenuEntryType.separator.name:
            entry = MenuEntry(event=self.event, type=MenuEntryType.separator)
            db.session.add(entry)
            db.session.flush()
            return jsonify_data(flash=False, entry=_render_menu_entry(entry))

        elif entry_type == MenuEntryType.user_link.name:
            form_cls = MenuLinkForm
        elif entry_type == MenuEntryType.page.name:
            form_cls = MenuPageForm
        else:
            raise BadRequest

        form = form_cls(obj=defaults)
        if form.validate_on_submit():
            entry = MenuEntry(event=self.event, type=MenuEntryType[entry_type])
            form.populate_obj(entry, skip={'html'})

            if entry.is_page:
                page = EventPage(html=form.html.data)
                self.event.custom_pages.append(page)
                entry.page = page

            db.session.add(entry)
            db.session.flush()
            return jsonify_data(entry=_render_menu_entry(entry))
        return jsonify_form(form)
Esempio n. 2
0
    def _process(self):
        defaults = FormDefaults(get_default_values(MenuEntry))
        entry_type = request.args['type']

        if entry_type == MenuEntryType.separator.name:
            entry = MenuEntry(event_id=self._conf.id, type=MenuEntryType.separator)
            db.session.add(entry)
            db.session.flush()
            return jsonify_data(flash=False, entry=_render_menu_entry(entry))

        elif entry_type == MenuEntryType.user_link.name:
            form_cls = MenuLinkForm
        elif entry_type == MenuEntryType.page.name:
            form_cls = MenuPageForm
        else:
            raise BadRequest

        form = form_cls(obj=defaults)
        if form.validate_on_submit():
            entry = MenuEntry(
                event_id=self._conf.id,
                type=MenuEntryType[entry_type]
            )
            form.populate_obj(entry, skip={'html'})

            if entry.is_page:
                page = EventPage(html=form.html.data)
                self._conf.as_event.custom_pages.append(page)
                entry.page = page

            db.session.add(entry)
            db.session.flush()
            return jsonify_data(entry=_render_menu_entry(entry))
        return jsonify_template('events/layout/menu_entry_form.html', form=form)
Esempio n. 3
0
def test_access_everyone(dummy_contribution, dummy_user, dummy_event):
    menu_entry = MenuEntry(event=dummy_event,
                           type=MenuEntryType.page,
                           access=MenuEntryAccess.everyone)
    person = EventPerson.create_from_user(dummy_user, dummy_event)
    assert menu_entry.can_access(dummy_user)
    contrib_person_link = ContributionPersonLink(person=person)
    dummy_contribution.person_links.append(contrib_person_link)
    assert menu_entry.can_access(dummy_user)
    contrib_person_link.is_speaker = True
    assert menu_entry.can_access(dummy_user)
Esempio n. 4
0
def test_access_participants_not_registered(dummy_contribution, dummy_user,
                                            dummy_event):
    menu_entry = MenuEntry(event=dummy_event,
                           type=MenuEntryType.page,
                           access=MenuEntryAccess.registered_participants)
    person = EventPerson.create_from_user(dummy_user, dummy_event)
    assert not menu_entry.can_access(dummy_user)
    contrib_person_link = ContributionPersonLink(person=person)
    dummy_contribution.person_links.append(contrib_person_link)
    assert not menu_entry.can_access(dummy_user)
    contrib_person_link.is_speaker = True
    assert not menu_entry.can_access(dummy_user)
Esempio n. 5
0
def menu_entries_for_event(event):
    from indico.core.plugins import plugin_engine

    custom_menu_enabled = layout_settings.get(event, "use_custom_menu")
    entries = MenuEntry.get_for_event(event) if custom_menu_enabled else []
    signal_entries = get_menu_entries_from_signal()

    cache_key = unicode(event.id)
    plugin_hash = binascii.crc32(",".join(sorted(plugin_engine.get_active_plugins()))) & 0xFFFFFFFF
    cache_version = "{}:{}".format(MaKaC.__version__, plugin_hash)
    processed = entries and _cache.get(cache_key) == cache_version

    if not processed:
        # menu entries from signal
        pos_gen = count(start=(entries[-1].position + 1) if entries else 0)
        entry_names = {entry.name for entry in entries}

        # Keeping only new entries from the signal
        new_entry_names = signal_entries.viewkeys() - entry_names

        # Mapping children data to their parent
        children = defaultdict(list)
        for name, data in signal_entries.iteritems():
            if name in new_entry_names and data.parent is not None:
                children[data.parent].append(data)

        # Building the entries
        new_entries = [
            _build_menu_entry(event, custom_menu_enabled, data, next(pos_gen), children=children.get(data.name))
            for (name, data) in sorted(signal_entries.iteritems(), key=lambda (name, data): _menu_entry_key(data))
            if name in new_entry_names and data.parent is None
        ]

        if custom_menu_enabled:
            with db.tmp_session() as sess:
                sess.add_all(new_entries)
                try:
                    sess.commit()
                except IntegrityError as e:
                    # If there are two parallel requests trying to insert a new menu
                    # item one of them will fail with an error due to the unique index.
                    # If the IntegrityError involves that index, we assume it's just the
                    # race condition and ignore it.
                    sess.rollback()
                    if "ix_uq_menu_entries_event_id_name" not in unicode(e.message):
                        raise
                else:
                    _cache.set(cache_key, cache_version)
            entries = MenuEntry.get_for_event(event)
        else:
            entries = new_entries

    return entries
Esempio n. 6
0
 def _copy_menu_entry(self, menu_entry, new_event, container, include_children=True):
     base_columns = get_simple_column_attrs(MenuEntry)
     new_menu_entry = MenuEntry(**{col: getattr(menu_entry, col) for col in base_columns})
     if menu_entry.is_page:
         with db.session.no_autoflush:  # menu_entry.page is lazy-loaded
             page = EventPage(event_new=new_event, html=menu_entry.page.html)
         new_menu_entry.page = page
         if menu_entry.page.is_default:
             new_event.default_page = new_menu_entry.page
     container.append(new_menu_entry)
     if include_children:
         for child in menu_entry.children:
             self._copy_menu_entry(child, new_event, new_menu_entry.children, include_children=False)
Esempio n. 7
0
 def _copy_menu_entry(self, menu_entry, new_event, container, include_children=True):
     base_columns = {column.key for column in inspect(MenuEntry).column_attrs} - {'id', 'event_id', 'parent_id',
                                                                                  'page_id'}
     new_menu_entry = MenuEntry(**{col: getattr(menu_entry, col) for col in base_columns})
     if menu_entry.is_page:
         with db.session.no_autoflush:  # menu_entry.page is lazy-loaded
             page = EventPage(event_id=new_event.id, html=menu_entry.page.html)
         new_menu_entry.page = page
         if menu_entry.page.is_default:
             new_event.as_event.default_page = new_menu_entry.page
     container.append(new_menu_entry)
     if include_children:
         for child in menu_entry.children:
             self._copy_menu_entry(child, new_event, new_menu_entry.children, include_children=False)
Esempio n. 8
0
 def _copy_menu_entry(self, menu_entry, new_event, parent=None, include_children=True):
     base_columns = get_simple_column_attrs(MenuEntry)
     new_menu_entry = MenuEntry(**{col: getattr(menu_entry, col) for col in base_columns})
     if menu_entry.is_page:
         with db.session.no_autoflush:  # menu_entry.page is lazy-loaded
             page = EventPage(event_new=new_event, html=menu_entry.page.html)
         new_menu_entry.page = page
         if menu_entry.page.is_default:
             new_event.default_page = new_menu_entry.page
     new_event.menu_entries.append(new_menu_entry)
     if parent is not None:
         parent.append(new_menu_entry)
     if include_children:
         for child in menu_entry.children:
             self._copy_menu_entry(child, new_event, new_menu_entry.children, include_children=False)
Esempio n. 9
0
    def _process(self):
        position = request.form.get('position')
        try:
            position = int(position)
        except (TypeError, ValueError):
            position = None

        parent_id = request.form.get('parent_id')
        try:
            parent_id = int(parent_id)
        except (TypeError, ValueError):
            parent_id = None

        if parent_id != self.entry.parent_id:
            if self.entry.type not in {MenuEntryType.user_link, MenuEntryType.page}:
                raise BadRequest('Menu entry "{0}" cannot be moved to another menu: Invalid type "{0.type.name}".'
                                 .format(self.entry))
            if self.entry.is_root and self.entry.children:
                raise BadRequest('Menu entry "{0}" cannot be moved to another menu: Entry has nested entries.'
                                 .format(self.entry))

            if parent_id is not None:
                parent_entry = MenuEntry.find_first(MenuEntry.type.in_({MenuEntryType.user_link, MenuEntryType.page}),
                                                    id=parent_id, parent_id=None, event_id=self.entry.event_id)
                if not parent_entry:
                    raise BadRequest('New parent entry not found for Menu entry "{0}".'.format(self.entry))

            self.entry.insert(parent_id, position)

        else:
            self.entry.move(position)

        return jsonify_data(flash=False)
Esempio n. 10
0
    def _process(self):
        if self.entry.type not in (MenuEntryType.user_link, MenuEntryType.page,
                                   MenuEntryType.separator):
            raise BadRequest('Menu entry of type {} cannot be deleted'.format(
                self.entry.type.name))

        position_gen = count(self.entry.position)
        if self.entry.children:
            for child in self.entry.children:
                child.parent_id = self.entry.parent_id
                child.position = next(position_gen)

        with db.session.no_autoflush:
            entries = MenuEntry.find_all(
                MenuEntry.event_id == self.entry.event_id,
                MenuEntry.parent_id == self.entry.parent_id,
                MenuEntry.position >= self.entry.position,
                MenuEntry.id != self.entry.id)
        for entry in entries:
            entry.position = next(position_gen)

        db.session.delete(self.entry)
        db.session.flush()

        return jsonify_data(flash=False,
                            menu=_render_menu_entries(self._conf,
                                                      connect_menu=True))
Esempio n. 11
0
    def run(self, new_event, cloners, shared_data):
        for col in ('logo_metadata', 'logo', 'stylesheet_metadata', 'stylesheet'):
            setattr(new_event, col, getattr(self.old_event, col))

        layout_settings.set_multi(new_event, layout_settings.get_all(self.old_event, no_defaults=True))
        if layout_settings.get(self.old_event, 'use_custom_menu'):
            for menu_entry in MenuEntry.get_for_event(self.old_event):
                self._copy_menu_entry(menu_entry, new_event)
        db.session.flush()
Esempio n. 12
0
 def _process(self):
     enabled = request.form['enabled'] == '1'
     if enabled:
         # nothing else to do here. menu items are added to the DB when retrieving the menu
         flash(_('Menu customization has been enabled.'), 'success')
     else:
         for entry in MenuEntry.find(event_id=int(self._conf.id)):
             db.session.delete(entry)
         flash(_('Menu customization has been disabled.'), 'success')
     layout_settings.set(self._conf, 'use_custom_menu', enabled)
     logger.info('Menu customization for {} {} by {}'.format(self._conf, 'enabled' if enabled else 'disabled',
                                                             session.user))
     return jsonify(enabled=enabled)
Esempio n. 13
0
def _build_menu(event):
    """Fetch the customizable menu data from the database."""
    entries = MenuEntry.get_for_event(event)
    if not entries:
        # empty menu, just build the whole structure without checking
        # for existing menu entries
        if _rebuild_menu(event):
            _set_menu_checked(event)
        return MenuEntry.get_for_event(event)
    elif _menu_needs_recheck(event):
        # menu items found, but maybe something new has been added
        if _check_menu(event):
            _set_menu_checked(event)
        # For some reason SQLAlchemy uses old data for the children
        # relationships even when querying the entries again below.
        # Expire them explicitly to avoid having to reload the page
        # after missing menu items have been created.
        for entry in entries:
            db.session.expire(entry, ('children',))
        return MenuEntry.get_for_event(event)
    else:
        # menu is assumed up to date
        return entries
Esempio n. 14
0
def _build_menu(event):
    """Fetch the customizable menu data from the database."""
    entries = MenuEntry.get_for_event(event)
    if not entries:
        # empty menu, just build the whole structure without checking
        # for existing menu entries
        if _rebuild_menu(event):
            _set_menu_checked(event)
        return MenuEntry.get_for_event(event)
    elif _menu_needs_recheck(event):
        # menu items found, but maybe something new has been added
        if _check_menu(event):
            _set_menu_checked(event)
        # For some reason SQLAlchemy uses old data for the children
        # relationships even when querying the entries again below.
        # Expire them explicitly to avoid having to reload the page
        # after missing menu items have been created.
        for entry in entries:
            db.session.expire(entry, ('children',))
        return MenuEntry.get_for_event(event)
    else:
        # menu is assumed up to date
        return entries
Esempio n. 15
0
    def clone(self, new_event, options):
        if self.event.getType() != 'conference':
            # for meetings/lecture we want to keep the default timetable style in all cases
            theme = layout_settings.get(self.event, 'timetable_theme')
            if theme is not None:
                layout_settings.set(new_event, 'timetable_theme', theme)
            return

        if 'layout' not in options:
            return

        for col in ('logo_metadata', 'logo', 'stylesheet_metadata', 'stylesheet'):
            setattr(new_event.as_event, col, getattr(self.event.as_event, col))

        layout_settings.set_multi(new_event, layout_settings.get_all(self.event, no_defaults=True))
        if layout_settings.get(self.event, 'use_custom_menu'):
            for menu_entry in MenuEntry.get_for_event(self.event):
                self._copy_menu_entry(menu_entry, new_event, new_event.as_event.menu_entries)
        db.session.flush()
Esempio n. 16
0
def test_access_speakers_contrib(dummy_contribution, dummy_user, dummy_event):
    set_feature_enabled(dummy_event, 'registration', True)
    menu_entry = MenuEntry(event=dummy_event,
                           type=MenuEntryType.page,
                           access=MenuEntryAccess.speakers)
    person = EventPerson.create_from_user(dummy_user, dummy_event)
    assert not menu_entry.can_access(dummy_user)
    contrib_person_link = ContributionPersonLink(person=person)
    dummy_contribution.person_links.append(contrib_person_link)
    assert not menu_entry.can_access(dummy_user)
    contrib_person_link.is_speaker = True
    assert menu_entry.can_access(dummy_user)
    dummy_contribution.is_deleted = True
    assert not menu_entry.can_access(dummy_user)
Esempio n. 17
0
    def _process(self):
        if self.entry.type not in (MenuEntryType.user_link, MenuEntryType.page, MenuEntryType.separator):
            raise BadRequest('Menu entry of type {} cannot be deleted'.format(self.entry.type.name))

        position_gen = count(self.entry.position)
        if self.entry.children:
            for child in self.entry.children:
                child.parent_id = self.entry.parent_id
                child.position = next(position_gen)

        with db.session.no_autoflush:
            entries = MenuEntry.find_all(MenuEntry.event_id == self.entry.event_id,
                                         MenuEntry.parent_id == self.entry.parent_id,
                                         MenuEntry.position >= self.entry.position,
                                         MenuEntry.id != self.entry.id)
        for entry in entries:
            entry.position = next(position_gen)

        db.session.delete(self.entry)
        db.session.flush()

        return jsonify_data(flash=False, menu=_render_menu_entries(self._conf, connect_menu=True))
Esempio n. 18
0
    def clone(self, new_event, options):
        if self.event.getType() != 'conference':
            # for meetings/lecture we want to keep the default timetable style in all cases
            theme = layout_settings.get(self.event, 'timetable_theme')
            if theme is not None:
                layout_settings.set(new_event, 'timetable_theme', theme)
            return

        if 'layout' not in options:
            return

        for col in ('logo_metadata', 'logo', 'stylesheet_metadata',
                    'stylesheet'):
            setattr(new_event.as_event, col, getattr(self.event.as_event, col))

        layout_settings.set_multi(
            new_event, layout_settings.get_all(self.event, no_defaults=True))
        if layout_settings.get(self.event, 'use_custom_menu'):
            for menu_entry in MenuEntry.get_for_event(self.event):
                self._copy_menu_entry(menu_entry, new_event,
                                      new_event.as_event.menu_entries)
        db.session.flush()
Esempio n. 19
0
def test_access_speakers_subcontrib(dummy_contribution, dummy_user,
                                    dummy_event):
    set_feature_enabled(dummy_event, 'registration', True)
    menu_entry = MenuEntry(event=dummy_event,
                           type=MenuEntryType.page,
                           access=MenuEntryAccess.speakers)
    person = EventPerson.create_from_user(dummy_user, dummy_event)
    assert not menu_entry.can_access(dummy_user)
    subcontrib = SubContribution(contribution=dummy_contribution,
                                 title='sc',
                                 duration=timedelta(minutes=10))
    subcontrib_person_link = SubContributionPersonLink(person=person)
    subcontrib.person_links.append(subcontrib_person_link)
    assert menu_entry.can_access(dummy_user)
    dummy_contribution.is_deleted = True
    assert not menu_entry.can_access(dummy_user)
    dummy_contribution.is_deleted = False
    subcontrib.is_deleted = True
    assert not menu_entry.can_access(dummy_user)
Esempio n. 20
0
 def _process_args(self):
     RHMenuBase._process_args(self)
     self.entry = MenuEntry.get_or_404(request.view_args['menu_entry_id'])
Esempio n. 21
0
 def has_data(self):
     return MenuEntry.has_rows()
Esempio n. 22
0
 def _migrate_menu(self, event, container, parent=None, used=None):
     if used is None:
         used = set()
     for pos, item in enumerate(container._listLink, 1):
         data = {
             'parent': parent,
             'event_id': int(event.id),
             'is_enabled': item._active,
             'position': pos
         }
         item_type = item.__class__.__name__
         if item_type == 'SystemLink':
             if item._name in REMOVED_MENU_NAMES:
                 continue
             data['name'] = MENU_ENTRY_NAME_MAP[item._name]
             if not parent and data['name'] in NOT_TOP_LEVEL_NAMES:
                 self.print_warning(cformat(
                     '%{yellow}Skipping top-level menu entry {}').format(
                         data['name']),
                                    event_id=event.id)
                 continue
             elif data['name'] in used:
                 self.print_error(
                     cformat('%{red!}duplicate menu entry name {}; skipping'
                             ).format(data['name']),
                     event_id=event.id)
                 continue
             used.add(data['name'])
             data['title'] = _sanitize_title(item._caption)
             if not data['title']:
                 data['title'] = None
                 self.print_warning(cformat(
                     '%{yellow!}Menu entry {} has no title; using default').
                                    format(data['name']),
                                    event_id=event.id)
             elif data['title'].lower() in DEFAULT_MENU_TITLES[
                     data['name']]:
                 data['title'] = None
             if item._name == 'chatrooms':
                 data['plugin'] = 'chat'
                 data['type'] = MenuEntryType.plugin_link
             else:
                 data['type'] = MenuEntryType.internal_link
         elif item_type == 'Spacer':
             data['type'] = MenuEntryType.separator
         elif item_type == 'ExternLink':
             data['type'] = MenuEntryType.user_link
             data['title'] = _sanitize_title(item._caption)
             data['link_url'] = item._URL.strip()
             if not data['link_url']:
                 if getattr(item, '_listLink', None):
                     self.print_warning(cformat(
                         '%{yellow!}Link "{}" has no URL but children').
                                        format(data['title']),
                                        event_id=event.id)
                 else:
                     self.print_warning(cformat(
                         '%{yellow}Skipping link "{}" with no URL').format(
                             data['title']),
                                        event_id=event.id)
                     continue
             if not data['title']:
                 if getattr(item, '_listLink', None):
                     self.print_warning(cformat(
                         '%{yellow!}Link has no title but children'),
                                        event_id=event.id)
                 else:
                     self.print_warning(
                         cformat('%{yellow}Skipping link with no title'),
                         event_id=event.id)
                     continue
         elif item_type == 'PageLink':
             data['type'] = MenuEntryType.page
             data['title'] = _sanitize_title(item._caption)
             data['page'] = EventPage(event_id=event.id,
                                      html=item._page._content)
             data['page'].legacy_mapping = LegacyPageMapping(
                 event_id=event.id, legacy_page_id=item._page._id)
             if item._page._isHome:
                 Event.get(event.id).default_page = data['page']
         else:
             self.print_error(
                 'Unexpected menu item type: {}'.format(item_type),
                 event_id=event.id)
             continue
         entry = MenuEntry(**data)
         yield entry
         if getattr(item, '_listLink', None):
             # child entries
             if not parent:
                 for sub_entry in self._migrate_menu(
                         event, item, entry, used):
                     yield sub_entry
             else:
                 self.print_warning('Skipping children inside nested entry',
                                    event_id=event.id)
Esempio n. 23
0
 def _process_args(self):
     RHMenuBase._process_args(self)
     self.entry = MenuEntry.get_one(request.view_args['menu_entry_id'])
Esempio n. 24
0
 def has_data(self):
     return MenuEntry.has_rows()
Esempio n. 25
0
 def _checkParams(self, params):
     RHMenuBase._checkParams(self, params)
     self.entry = MenuEntry.get_one(request.view_args['menu_entry_id'])
Esempio n. 26
0
def menu_entries_for_event(event):
    from indico.core.plugins import plugin_engine

    custom_menu_enabled = layout_settings.get(event, 'use_custom_menu')
    entries = MenuEntry.get_for_event(event) if custom_menu_enabled else []
    signal_entries = get_menu_entries_from_signal()

    cache_key = unicode(event.id)
    plugin_hash = crc32(','.join(sorted(plugin_engine.get_active_plugins())))
    cache_version = '{}:{}'.format(MaKaC.__version__, plugin_hash)
    processed = entries and _cache.get(cache_key) == cache_version

    if not processed:
        # menu entries from signal
        pos_gen = count(start=(entries[-1].position + 1) if entries else 0)
        entry_names = {entry.name for entry in entries}

        # Keeping only new entries from the signal
        new_entry_names = signal_entries.viewkeys() - entry_names

        # Mapping children data to their parent
        children = defaultdict(list)
        for name, data in signal_entries.iteritems():
            if name in new_entry_names and data.parent is not None:
                children[data.parent].append(data)

        # Building the entries
        new_entries = [
            _build_menu_entry(event,
                              custom_menu_enabled,
                              data,
                              next(pos_gen),
                              children=children.get(data.name))
            for (
                name,
                data) in sorted(signal_entries.iteritems(),
                                key=lambda (name, data): _menu_entry_key(data))
            if name in new_entry_names and data.parent is None
        ]

        if custom_menu_enabled:
            with db.tmp_session() as sess:
                sess.add_all(new_entries)
                try:
                    sess.commit()
                except IntegrityError as e:
                    # If there are two parallel requests trying to insert a new menu
                    # item one of them will fail with an error due to the unique index.
                    # If the IntegrityError involves that index, we assume it's just the
                    # race condition and ignore it.
                    sess.rollback()
                    if 'ix_uq_menu_entries_event_id_name' not in unicode(
                            e.message):
                        raise
                else:
                    _cache.set(cache_key, cache_version)
            entries = MenuEntry.get_for_event(event)
        else:
            entries = new_entries

    return entries
Esempio n. 27
0
 def _checkParams(self, params):
     RHMenuBase._checkParams(self, params)
     self.entry = MenuEntry.get_one(request.view_args['menu_entry_id'])