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)
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)
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)
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)
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
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)
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)
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)
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)
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))
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()
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)
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
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()
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)
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))
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()
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)
def _process_args(self): RHMenuBase._process_args(self) self.entry = MenuEntry.get_or_404(request.view_args['menu_entry_id'])
def has_data(self): return MenuEntry.has_rows()
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)
def _process_args(self): RHMenuBase._process_args(self) self.entry = MenuEntry.get_one(request.view_args['menu_entry_id'])
def _checkParams(self, params): RHMenuBase._checkParams(self, params) self.entry = MenuEntry.get_one(request.view_args['menu_entry_id'])
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