def verify_diff(diff, screen):
    """ Verifies that all the channels in the diff have been correctly added to/removed from the bundle"""
    for (channel_id, subscribed) in diff.items():
        if subscribed:
            assert Channel.get(channel_id) in screen.subscribed_channels
        else:
            assert Channel.get(channel_id) not in screen.subscribed_channels
Beispiel #2
0
 def post(self, channel_id):
     channel = Channel.get(int(channel_id))
     form = self.form
     try:
         if form.action == 'add-channel-to-bundles':
             bundles_diff = json.loads(form.pop('diff', '{}'))
             for bundle_id, part_of in bundles_diff.items():
                 bundle = ChannelBundle.get(int(bundle_id))
                 if part_of:
                     try:
                         bundle.add_channel(channel)
                     except ValueError:
                         raise ImmediateFeedback(form.action,
                                                 'bundle_cycle',
                                                 bundle.name)
                 else:
                     bundle.remove_channel(channel)
             add_feedback(form.action, 'ok')
     except ImmediateFeedback:
         pass
     form.was_bundle = type(
         channel
     ) == ChannelBundle  # Hack to display the bundles tab instead
     form.data_edit = json.dumps([b.id for b in channel.bundles])
     form.channel_id = channel_id
     form.name = channel.name
     store_form(form)
     resp.seeother('/channels')
Beispiel #3
0
    def get(self, id, user_id):
        channel_id = int(id)
        chan = Channel.get(channel_id)
        user_id = int(user_id)
        user = User.get(user_id)

        st = "You just receive a request of subscription for channel " + chan.name + ". Could you please subscribe " + str(
            user.fullname) + " (" + user.email + ") to this channel."
        for admin in chan.get_admins():
            web.sendmail(web.config.smtp_sendername,
                         admin.email,
                         'Request for subscription to a channel',
                         st,
                         headers={'Content-Type': 'text/html;charset=utf-8'})
        resp.seeother('/channels')
Beispiel #4
0
 def get(self, channel_id):
     channel = Channel.get(channel_id)
     current_user = User.get(self.session['user']['id'])
     screens_of_current_user = Screen.get_visible_screens_of(current_user)
     subscriptions = current_user.get_subscriptions_of_owned_screens()
     last_by = {
         sub.screen.id: {
             'user': sub.created_by.readable_name,
             'channel_name': sub.channel.name,
             'plugin_channel': hasattr(sub.channel, 'plugin')
         }
         for sub in subscriptions if sub.channel.id == channel.id
     }
     screen_names = {s.id: s.name for s in screens_of_current_user}
     return self.renderer.channel_subscriptions(
         channel=channel,
         possible_screens=screens_of_current_user,
         user=current_user,
         subscriptions=subscriptions,
         last_by=last_by,
         screen_names=screen_names)
Beispiel #5
0
 def GET(self, channel_id, secret):
     """ Render the capsules of this channel. """
     try:
         channel = Channel.get(channel_id)
         if channel.secret != secret:
             raise web.forbidden()
     except SQLObjectNotFound:
         raise web.notfound()
     channel_capsules = []
     already_added_channels = set()
     for plugin_channel in channel.flatten(keep_disabled_channels=True):
         if plugin_channel.id not in already_added_channels:
             if plugin_channel.plugin.activated == 'yes':
                 for capsule in self.plugin_manager.get_plugin_content(
                         plugin_channel):
                     if len(capsule.get_slides()) > 0:
                         channel_capsules.append(capsule)
             already_added_channels.add(plugin_channel.id)
     return self.ictv_renderer.preview_capsules(channel_capsules,
                                                context='channel',
                                                auto_slide=True)
Beispiel #6
0
 def post(self, screen_id):
     def wrong_channel(channel, subscribe, user):
         """ returns True if the the user wants to subscribe to the channel while its plugin is not activated or if
             the user tries to subscribe to the channel without being in its authorized subscribers"""
         return subscribe and (type(channel) is PluginChannel and channel.plugin.activated != "yes" or (subscribe and not channel.can_subscribe(user)))
     form = self.form
     try:
         screen = Screen.get(screen_id)
         u = User.get(self.session['user']['id'])
         # Forbid if not admin or does not own this screen
         if not (UserPermissions.administrator in u.highest_permission_level or screen in u.screens):
             logger.warning('user %s tried change subscriptions of screen %d without having the rights to do this',
                            u.log_name, screen.id)
             resp.forbidden()
         diff = json.loads(form.diff)
         if diff == {}:
             logger.info('user %s submitted empty diff for subscriptions of screen %s', u.log_name, screen.name)
             raise ImmediateFeedback("subscription", 'nothing_changed')
         # Do the subscription/unsubscription for every channel in the diff
         subscribed = []
         unsubscribed = []
         try:
             changes = [(Channel.get(channel_id), subscribe) for channel_id, subscribe in diff.items()]
         except SQLObjectNotFound:
             logger.warning('user %s tried to subscribe/unsubscribe screen %d to a channel which does not exist',
                            u.log_name, screen.id)
             resp.forbidden()
         # if somebody tries to subscribe to a channel with a disabled plugin
         wrong_channels = [(channel, subscribe) for channel, subscribe in changes if wrong_channel(channel, subscribe, u)]
         if wrong_channels:
             channel, subscribe = wrong_channels[0]
             if channel.plugin.activated != "yes":
                 logger.warning('user %s tried to %s screen %d to channel %d with disabled plugin',
                                u.log_name, 'subscribe' if subscribe else "unsubscribe", screen.id, channel.id)
                 raise ImmediateFeedback("subscription", "disabled_plugin")
             else:
                 logger.warning('user %s tried to subscribe screen %d to channel %d without having the right to do this',
                                u.log_name, screen.id, channel.id)
                 resp.forbidden()
         for channel, subscribe in changes:
             if subscribe:
                 screen.subscribe_to(u, channel)
                 subscribed.append(str(channel.id))
             else:
                 screen.unsubscribe_from(u, channel)
                 unsubscribed.append(str(channel.id))
         if subscribed and unsubscribed:
             message = "user %s has subscribed screen %d to channel(s) %s and unsubscribed from channel(s) %s" % \
                       (u.log_name, screen.id, ', '.join(subscribed), ', '.join(unsubscribed))
         else:
             message = "user %s has %s screen %d to channel(s) %s" % \
                       (u.log_name, "subscribed" if subscribed else "unsubscribed", screen.id,
                        ', '.join(subscribed if subscribed else unsubscribed))
         logger.info(message)
         add_feedback("subscription", 'ok')
     except SQLObjectNotFound:
         resp.notfound()
     except ImmediateFeedback:
         pass
     store_form(form)
     resp.seeother("/screens/%s/subscriptions" % screen.id)
Beispiel #7
0
 def get(self, channel_id):
     channel = Channel.get(int(channel_id))
     current_user = User.get(self.session['user']['id'])
     if channel in Channel.get_visible_channels_of(current_user):
         return self.render_page(channel)
     raise self.forbidden()
Beispiel #8
0
    def post(self, channel_id):
        form = self.form
        u = User.get(self.session['user']['id'])
        subscribed = []
        unsubscribed = []
        if form.diff == 'diff':
            raise ImmediateFeedback(form.action, 'nothing_changed')
        try:
            diff = json.loads(form.diff)
        except (json.JSONDecodeError, ValueError):
            raise ImmediateFeedback(form.action, 'inconsistent_diff')
        try:
            channelid = int(channel_id)
        except ValueError:
            raise ImmediateFeedback(form.action, 'invalid_channel/screen_id')
        for k, v in diff.items():
            try:
                sub = bool(v)
            except ValueError:
                raise ImmediateFeedback(form.action,
                                        'invalid_channel/screen_id')
            try:
                screenid = int(k)
            except ValueError:
                raise ImmediateFeedback(form.action,
                                        'invalid_channel/screen_id')
            try:
                channel = Channel.get(channelid)
                if not channel.can_subscribe(u):
                    raise self.forbidden(message="You're not allow to do that")
                screen = Screen.get(screenid)
                if not u in screen.owners:
                    raise self.forbidden(message="You're not allow to do that")
                #sub true -> New subscription
                #sub false -> Remove subscription
                if sub:
                    screen.subscribe_to(u, channel)
                    subscribed.append(str(channel.id))
                else:
                    screen.unsubscribe_from(u, channel)
                    unsubscribed.append(str(channel.id))
                if subscribed and unsubscribed:
                    message = "user %s has subscribed screen %d to channel(s) %s and unsubscribed from channel(s) %s" % \
                              (u.log_name, screen.id, ', '.join(subscribed), ', '.join(unsubscribed))
                else:
                    message = "user %s has %s screen %d to channel(s) %s" % \
                              (u.log_name, "subscribed" if subscribed else "unsubscribed", screen.id,
                               ', '.join(subscribed if subscribed else unsubscribed))
                logger.info(message)
                add_feedback("subscription", 'ok')

            except SQLObjectNotFound:
                raise ImmediateFeedback(form.action,
                                        'invalid_channel/screen_id')
            except DuplicateEntryError:
                # if the user has checked + unchecked the same checkbox, it will appear on the diff,
                # we just need to do nothing.
                pass
            except SQLObjectIntegrityError:
                # when there id more than one subscription matching the pair channel/screen
                pass
        resp.seeother("/channels/config/%s/subscriptions" % channel_id)
Beispiel #9
0
    def post(self, bundle_id):
        def wrong_channel(channel, bundle, add):
            """ returns True if the channel to add is the bundle itself
                or if the channel is a PluginChannel with disabled plugin """
            return bundle.id == channel.id or add and type(channel) is PluginChannel and channel.plugin.activated != "yes"
        form = self.form
        try:
            bundle = ChannelBundle.get(bundle_id)
            u = User.get(self.session['user']['id'])
            diff = json.loads(form.diff)
            if diff == {}:
                logger.info('user %s submitted empty diff for bundle management %s', u.log_name, bundle.name)
                raise ImmediateFeedback("manage_channels", 'nothing_changed')
            # Do the subscription/unsubscription for every channel in the diff
            contained = []
            not_contained = []
            try:
                changes = [(Channel.get(channel_id), add) for channel_id, add in diff.items()]
            except SQLObjectNotFound:
                logger.warning('user %s tried to add a channel which does not exist to bundle %d',
                               u.log_name, bundle.id)
                resp.forbidden()
            # if somebody tries to add a channel with a disabled plugin or to add a bundle to itself
            wrong_channels = [(channel, add) for channel, add in changes if wrong_channel(channel, bundle, add)]
            if wrong_channels:
                channel, add = wrong_channels[0]
                if channel.id == bundle.id:
                    logger.warning('user %s tried to %s bundle %d to itself',
                                   u.log_name, 'add' if add else "remove", bundle.id)
                    raise ImmediateFeedback("manage_channels", "added_to_itself")
                else:
                    logger.warning('user %s tried to %s channel %d with disabled plugin to bundle %d',
                                   u.log_name, 'add' if add else "remove", channel.id, bundle.id)
                    raise ImmediateFeedback("manage_channels", "disabled_plugin")
            for channel, add in changes:
                if add:
                    try:
                        bundle.add_channel(channel)
                    except ValueError:
                        logger.warning("user %s has made changes in channels management of bundle %d that created a "
                                       "cycle of bundles by adding channel %d (%s)", u.log_name, bundle.id, channel.id,
                                       channel.name)
                        form.channel_name = channel.name
                        raise ImmediateFeedback("manage_channels", "bundle_cycle")

                    contained.append(str(channel.id))
                else:
                    bundle.remove_channel(channel)
                    not_contained.append(str(channel.id))
            if contained and not_contained:
                message = "user %s has added to channel(s) %s and removed channel(s) %s to bundle %d" % \
                          (u.log_name, ', '.join(contained), ', '.join(not_contained), bundle.id)
            else:
                message = "user %s has %s channel(s) %s to bundle %d" % \
                          (u.log_name, "added" if contained else "removed",
                           ', '.join(contained if contained else not_contained), bundle.id)
            logger.info(message)
            add_feedback("manage_channels", 'ok')
        except SQLObjectNotFound:
            resp.notfound()
        except ImmediateFeedback:
            pass
        store_form(form)
        resp.seeother("/channels/config/%s/manage_bundle" % bundle.id)
Beispiel #10
0
    def post(self):
        """ Handles screen creation, editing, deletion, channel subscriptions. """
        form = self.form
        u = User.get(self.session['user']['id'])
        try:
            if form.action == 'delete':
                if not u.super_admin:
                    resp.forbidden()
                try:
                    screenid = int(form.screenid)
                except ValueError:
                    raise ImmediateFeedback(form.action, 'invalid_id')
                try:
                    Screen.delete(screenid)
                    raise ImmediateFeedback(form.action, 'ok')
                except SQLObjectNotFound as e:
                    raise ImmediateFeedback(form.action, 'no_id_matching')
            elif form.action == 'subscribe':
                if form.diff == 'diff':
                    raise ImmediateFeedback(form.action, 'nothing_changed')
                try:
                    diff = json.loads(form.diff)
                except (json.JSONDecodeError, ValueError):
                    raise ImmediateFeedback(form.action, 'inconsistent_diff')
                try:
                    screenid = int(form.screenid)
                except ValueError:
                    raise ImmediateFeedback(form.action,
                                            'invalid_channel/screen_id')
                for k, v in diff.items():
                    try:
                        sub = bool(v)
                    except ValueError:
                        raise ImmediateFeedback(form.action,
                                                'invalid_channel/screen_id')
                    try:
                        channelid = int(k)
                    except ValueError:
                        raise ImmediateFeedback(form.action,
                                                'invalid_channel/screen_id')
                    try:
                        channel = Channel.get(channelid)
                        screen = Screen.get(screenid)
                        if UserPermissions.administrator not in u.highest_permission_level and not (
                                channel.can_subscribe(u)
                                and u in screen.owners):
                            resp.forbidden()
                        if sub:
                            if hasattr(channel, "plugin"
                                       ) and channel.plugin.activated != 'yes':
                                resp.forbidden()
                            screen.subscribe_to(user=u, channel=channel)
                        else:
                            screen.unsubscribe_from(user=u, channel=channel)
                    except SQLObjectNotFound:
                        raise ImmediateFeedback(form.action,
                                                'invalid_channel/screen_id')
                    except DuplicateEntryError:
                        # if the user has checked + unchecked the same checkbox, it will appear on the diff,
                        # we just need to do nothing.
                        pass
                    except SQLObjectIntegrityError:
                        # when there id more than one subscription matching the pair channel/screen
                        # TODO: log something
                        pass
            elif form.action == 'chown':
                if form.diff == 'diff':
                    raise ImmediateFeedback(form.action, 'nothing_changed')
                try:
                    diff = json.loads(form.diff)
                except (json.JSONDecodeError, ValueError):
                    raise ImmediateFeedback(form.action, 'inconsistent_diff')
                try:
                    screenid = int(form.screenid)
                except ValueError:
                    raise ImmediateFeedback(form.action, 'screenid')
                for k, v in diff.items():
                    try:
                        own = bool(v)
                    except ValueError:
                        raise ImmediateFeedback(form.action,
                                                'invalid_screen/user_id')
                    try:
                        userid = int(k)
                    except ValueError:
                        raise ImmediateFeedback(form.action,
                                                'invalid_screen/user_id')
                    try:
                        screen = Screen.get(screenid)
                        if UserPermissions.administrator not in u.highest_permission_level and u not in screen.owners:
                            resp.forbidden()
                        user = User.get(userid)
                        if own:
                            screen.safe_add_user(user)
                        else:
                            screen.removeUser(user)
                    except SQLObjectNotFound:
                        raise ImmediateFeedback(form.action,
                                                'invalid_screen/user_id')
                    except DuplicateEntryError:
                        # if the user has checked + unchecked the same checkbox, it will appear on the diff,
                        # we just need to do nothing.
                        pass
                    except SQLObjectIntegrityError:
                        # when there is more than one subscription mathing the pair channel/screen
                        # TODO: log something
                        pass
            elif form.action == 'configure':
                screen = Screen.get(int(form.id))
                screen.shuffle = form.get('shuffle') == 'on'
                screen.show_postit = form.get('postit') == 'on'
                screen.show_slide_number = form.get(
                    'show_slide_number') == 'on'
            else:
                building = Building.get(form.building.strip())
                name = form.name.strip()
                form.building_name = None
                if not building:
                    raise ImmediateFeedback(form.action, 'empty_building')
                form.building_name = building.name
                if not name:
                    raise ImmediateFeedback(form.action, 'empty_name')
                if len(name) > Screen.sqlmeta.columns['name'].length:
                    raise ImmediateFeedback(form.action, 'too_long_name')

                try:
                    macs = {
                        self.check_mac_address(mac)
                        for mac in form.mac.strip().lower().split(';') if mac
                    }
                except ValueError:
                    raise ImmediateFeedback(form.action, 'invalid_mac_address')

                if form.action == 'create':
                    if UserPermissions.administrator not in u.highest_permission_level:
                        resp.forbidden()
                    try:
                        screen = Screen(name=form.name.strip(),
                                        building=form.building,
                                        location=form.location.strip(),
                                        comment=form.comment.strip(),
                                        orientation=form.orientation)
                    except DuplicateEntryError:
                        raise ImmediateFeedback(form.action,
                                                'name_already_exists')

                elif form.action == 'edit':
                    if UserPermissions.administrator not in u.highest_permission_level:
                        resp.forbidden()
                    try:
                        screenid = int(form.screenid)
                    except ValueError:
                        raise ImmediateFeedback(form.action, 'invalid_id')

                    screen = Screen.get(screenid)
                    if screen is None:
                        raise ImmediateFeedback(form.action, 'no_id_matching')

                    try:
                        screen.name = form.name.strip()
                        screen.building = form.building
                        screen.orientation = form.orientation
                    except DuplicateEntryError:
                        raise ImmediateFeedback(form.action,
                                                'name_already_exists')
                    screen.location = form.location.strip()
                    screen.comment = form.comment.strip()
                else:
                    raise NotImplementedError

                try:
                    screen_macs = [
                        screen_mac.mac for screen_mac in screen.macs
                    ]
                    for mac in screen_macs:
                        if mac not in macs:
                            ScreenMac.selectBy(mac=mac).getOne().destroySelf()
                    for mac in macs:
                        if mac not in screen_macs:
                            ScreenMac(screen=screen, mac=mac)
                except DuplicateEntryError:
                    raise ImmediateFeedback(form.action, 'mac_already_exists')

            add_feedback(form.action, 'ok')
        except ImmediateFeedback:
            pass
        store_form(form)

        return self.render_page()
Beispiel #11
0
def get_content(channel_id, config=None):
    channel = Channel.get(channel_id)
    logger = get_logger('rss', channel)
    if not config:

        def get_param(x):
            return channel.get_config_param(x)
    else:

        def get_param(x):
            return config[x]

    url = get_param('url')
    parser_rules = get_param(
        'parser_rules'
    )  # A list of rules in the form slide_element;entry_item;regexp
    additional_rules = get_param(
        'additional_rules')  # A list of rules in the form entry_item;string
    filter = get_param('filter')
    exception_rules = get_param(
        'exception_rules')  # A list of rules in the form entry_item;string
    no_slides = get_param('no_slides')
    time_limit = get_param('time_limit')
    duration = get_param('duration') * 1000
    template = get_param('template')
    theme = get_param('theme')
    min_age = datetime.now() - timedelta(days=time_limit)

    entries = feedparser_parse(
        url)['entries'][:no_slides] if not config or not config.get(
            'feed') else config.get('feed')
    capsules = []
    last_entries = []

    with open(cache_path, 'r+') as f:
        fcntl.flock(f, fcntl.LOCK_EX)
        cache = json.load(f, cls=DateTimeDecoder)

        for entry in entries:
            if 'published_parsed' in entry:
                entry_age = datetime.fromtimestamp(
                    mktime(entry['published_parsed']))
                if entry_age >= min_age:
                    last_entries.append(entry)
            else:
                entry_hash = hash_dict(entry)
                if entry_hash in cache:
                    if cache[entry_hash] >= min_age:
                        last_entries.append(entry)
                else:
                    cache[entry_hash] = datetime.now()
        f.seek(0)
        json.dump(cache, f, cls=DateTimeEncoder)
        f.truncate()
        fcntl.flock(f, fcntl.LOCK_UN)

    for entry in last_entries:
        slide_content = {}
        link_page = None
        for slide_element, entry_item, regexp in [
                rule.split(';') for rule in parser_rules
        ]:
            field, input_type = slide_element.split(':')
            if field not in slide_content:
                slide_content[field] = {}

            if entry_item == 'link_page':
                if not link_page:
                    with urllib.request.urlopen(entry.link) as response:
                        link_page = response.read().decode(errors='ignore')
                        entry_item += "@" + response.geturl(
                        )  # in case of redirect(s), geturl() returns the final url of the page
                item = link_page
            else:
                item = deep_get(entry, *entry_item.split('.'))

            value = get_value(item, regexp)
            if input_type == 'src' and not _is_url(value):
                ref_url = entry.link
                if entry_item.startswith('link_page@'):
                    ref_url = entry_item.split('@')[1]
                value = urljoin(ref_url, value)

            slide_content[field].update({input_type: value})

        for slide_element, string in [
                rule.split(';') for rule in additional_rules
        ]:
            field, input_type = slide_element.split(':')
            if field not in slide_content:
                slide_content[field] = {}
            if string.lower() == 'qrcode':
                input_type = 'qrcode'
                string = entry.link
            slide_content[field].update({input_type: string})

        if len(exception_rules) == 1 and not exception_rules[0].strip():
            capsules.append(
                RssCapsule(theme=theme,
                           slides=[
                               RssSlide(content=slide_content,
                                        template=template,
                                        duration=duration)
                           ]))
        else:
            for entry_item, regexp in [
                    rule.split(';') for rule in exception_rules
            ]:
                if entry_item == 'link_page':
                    if not link_page:
                        with urllib.request.urlopen(entry.link) as response:
                            link_page = response.read().decode(errors='ignore')
                            entry_item += "@" + response.geturl(
                            )  # in case of redirect(s), geturl() returns the final url of the page
                    item = link_page
                else:
                    item = deep_get(entry, *entry_item.split('.'))

                value = get_value(item, regexp)

                if filter and value is None:
                    capsules.append(
                        RssCapsule(theme=theme,
                                   slides=[
                                       RssSlide(content=slide_content,
                                                template=template,
                                                duration=duration)
                                   ]))

                if not filter and value is not None:
                    capsules.append(
                        RssCapsule(theme=theme,
                                   slides=[
                                       RssSlide(content=slide_content,
                                                template=template,
                                                duration=duration)
                                   ]))

    return capsules
Beispiel #12
0
    def POST(self):
        """ Handles channel creation, editing, deletion, configuration and user permissions. """
        form = web.input()
        current_user = User.get(self.session['user']['id'])
        channel = None
        try:
            if form.action.startswith('create') or form.action.startswith(
                    'edit'):
                # Prepare creation or edition of channels/bundles
                name = form.name.strip()
                description = form.description if form.description and form.description.strip(
                ) else None
                enabled = form.get('enabled') == 'on'
                if form.subscription_right not in [
                        'public', 'restricted', 'private'
                ]:
                    raise ImmediateFeedback(form.action,
                                            'invalid_subscription_right')
                if len(name) < 3:
                    raise ImmediateFeedback(form.action, 'invalid_name')

            if form.action.startswith('create'):
                if UserPermissions.administrator not in current_user.highest_permission_level:
                    logger.warning(
                        'user %s tried to create a channel without being admin',
                        current_user.log_name)
                    raise web.forbidden()

                try:
                    if form.action == 'create-channel':
                        try:
                            plugin_id = int(form.plugin)
                        except ValueError:
                            raise ImmediateFeedback(form.action,
                                                    'invalid_plugin')
                        p = Plugin.get(plugin_id)
                        channel = PluginChannel(
                            name=name,
                            plugin=p,
                            subscription_right=form.subscription_right,
                            description=description,
                            enabled=enabled)
                        if p.webapp:
                            self.plugin_manager.add_mapping(self.app, channel)

                    elif form.action == 'create-bundle':
                        channel = ChannelBundle(
                            name=name,
                            description=description,
                            subscription_right=form.subscription_right,
                            enabled=enabled)
                    else:
                        raise web.badrequest()
                except SQLObjectNotFound:
                    raise ImmediateFeedback(form.action, 'invalid_plugin')
                except DuplicateEntryError:
                    raise ImmediateFeedback(form.action, 'name_already_exists')
                logger.info('channel ' + channel.name + ' created by ' +
                            current_user.log_name)
            elif form.action.startswith('edit'):
                if UserPermissions.administrator not in current_user.highest_permission_level:
                    raise web.forbidden()
                try:
                    form.id = int(form.id)
                    channel = (PluginChannel if form.action == 'edit-channel'
                               else Channel).get(form.id)
                except (SQLObjectNotFound, ValueError):
                    raise ImmediateFeedback(form.action, 'invalid_id')

                previous_state = {
                    'name': channel.name,
                    'description': channel.description,
                    'subscription_right': channel.subscription_right,
                    'enabled': channel.enabled
                }
                new_state = {
                    'name': name,
                    'description': description,
                    'subscription_right': form.subscription_right,
                    'enabled': enabled
                }
                state_diff = dict(
                    set(new_state.items()) - set(previous_state.items()))

                if form.action == 'edit-channel':
                    try:
                        plugin_id = int(form.plugin)
                        p = Plugin.get(plugin_id)
                        add_mapping = p.webapp and channel.plugin != p
                        previous_state['plugin'] = channel.plugin
                        new_state['plugin'] = p
                    except (SQLObjectNotFound, ValueError):
                        raise ImmediateFeedback(form.action, 'invalid_plugin')
                elif form.action == 'edit-bundle':
                    pass  # There is nothing more to edit for a bundle than a channel
                else:
                    raise web.badrequest()

                try:
                    channel.set(**new_state)
                except DuplicateEntryError:
                    channel.set(**previous_state)  # Rollback
                    raise ImmediateFeedback(form.action, 'name_already_exists')

                logger.info(
                    '[Channel %s (%d)] ' % (channel.name, channel.id) +
                    current_user.log_name + ' edited the channel.\n'
                    'Previous state: %s\n' % str({
                        k: v
                        for k, v in previous_state.items() if k in state_diff
                    }) + 'New state: %s' % str(state_diff))

                if form.action == 'edit-channel' and add_mapping:
                    self.plugin_manager.add_mapping(self.app, channel)
            elif form.action.startswith('delete'):
                if not current_user.super_admin:
                    logger.warning(
                        'the user %s tried to delete a channel without having the rights to do it',
                        current_user.log_name)
                    raise web.forbidden()
                try:
                    form.id = int(form.id)
                    channel = Channel.get(form.id)
                    if channel.subscriptions.count(
                    ) > 0 and 'confirm-delete' not in form:
                        raise ImmediateFeedback(
                            form.action, 'channel_has_subscriptions', {
                                'channel': {
                                    'name':
                                    channel.name,
                                    'id':
                                    channel.id,
                                    'description':
                                    channel.description,
                                    'subscription_right':
                                    channel.subscription_right
                                },
                                'plugin_id':
                                channel.plugin.id
                                if form.action == 'delete-channel' else None,
                                'subscriptions':
                                [(s.screen.id, s.screen.name,
                                  s.screen.building.name)
                                 for s in channel.subscriptions]
                            })
                    form.name = channel.name
                    channel_name = channel.name
                    channel.destroySelf()
                    logger.info('the channel %s has been deleted by user %s',
                                channel_name, current_user.log_name)
                except (SQLObjectNotFound, ValueError) as e:
                    print(e)
                    raise ImmediateFeedback(form.action, 'invalid_id')
            elif form.action == 'add-users-channel':
                try:
                    if 'users' not in form:
                        raise web.badrequest()
                    form.users = json.loads(form.users)
                    form.id = int(form.id)
                    channel = PluginChannel.get(form.id)
                    if not channel.has_admin(
                            current_user
                    ) and UserPermissions.administrator not in current_user.highest_permission_level:
                        raise web.forbidden()
                    form.name = channel.name
                    for user_id, diff in form.users.items():
                        user_id = int(user_id)
                        user = User.get(user_id)
                        if 'permission' in diff:
                            permission_level = diff['permission']
                            new_permission_level = UserPermissions(
                                permission_level)
                            old_permission_level = channel.get_channel_permissions_of(
                                user)
                            if new_permission_level == UserPermissions.no_permission \
                                    and (UserPermissions.administrator in current_user.highest_permission_level or old_permission_level == UserPermissions.channel_contributor):
                                channel.remove_permission_to_user(user)
                                logger.info(
                                    'permissions of user %s concerning channel %s have been removed by user %s',
                                    user.log_name, channel.name,
                                    current_user.log_name)
                            elif (new_permission_level == UserPermissions.channel_contributor and channel.has_admin(
                                    current_user) and old_permission_level == UserPermissions.no_permission) \
                                    or (new_permission_level in UserPermissions.channel_administrator and UserPermissions.administrator in current_user.highest_permission_level):
                                channel.give_permission_to_user(
                                    user, new_permission_level)
                                logger.info(
                                    'permissions of user %s concerning channel %s have been set to %s by user %s',
                                    user.log_name, channel.name,
                                    UserPermissions.get_permission_string(
                                        new_permission_level),
                                    current_user.log_name)
                        if 'authorized_subscriber' in diff:
                            authorized_subscriber = diff[
                                'authorized_subscriber']
                            if authorized_subscriber and (
                                    user
                                    not in channel.authorized_subscribers):
                                channel.addUser(user)
                                logger.info(
                                    'the user %s has been added to channel %s as authorized subscriber by user %s',
                                    user.log_name, channel.name,
                                    current_user.log_name)
                            elif not authorized_subscriber and (
                                    user in channel.authorized_subscribers):
                                channel.removeUser(user)
                                logger.info(
                                    'the user %s has been removed from channel %s as authorized subscriber by user %s',
                                    user.log_name, channel.name,
                                    current_user.log_name)
                except (SQLObjectNotFound, ValueError):
                    raise ImmediateFeedback(form.action, 'invalid_id')
                except (KeyError, json.JSONDecodeError):
                    raise ImmediateFeedback(form.action, 'invalid_users')
            elif form.action == 'configure':
                try:
                    form.id = int(form.id)
                    channel = PluginChannel.get(form.id)
                    pattern = re.compile(r'list\[.*\]')
                    if UserPermissions.administrator in current_user.highest_permission_level or UserPermissions.channel_administrator in channel.get_channel_permissions_of(
                            current_user) or channel.has_contrib(current_user):
                        for k, v in [(k, v) for k, v in
                                     channel.plugin.channels_params.items()
                                     if channel.get_access_rights_for(
                                         k, current_user)[1]]:
                            # Iterates on the parameters the current user can write to
                            if v['type'] == 'bool':
                                value = k in form and form[k] == 'on'
                            elif v['type'] == 'int':
                                value = int(form[k])
                                if not (v.get('min', float('-inf')) <= value <=
                                        v.get('max', float('inf'))):
                                    continue
                            elif pattern.match(v['type']):
                                inner_type = v['type'][5:-1]
                                if inner_type == 'string':
                                    value = web.input(**{k: ['']})[k]
                                elif pattern.match(inner_type):
                                    inner_type = inner_type[5:-1]
                                    if inner_type == 'string':
                                        delimiter = form[k + '-delimiter']
                                        values = web.input(**{k: ['']})[k]
                                        lists = []
                                        l = []
                                        for v in values:
                                            if v == delimiter:
                                                lists.append(l)
                                                l = []
                                            else:
                                                l.append(v)
                                        value = lists

                            elif k in form:
                                if v['type'] == 'template' and form[k] == '~':
                                    value = None
                                else:
                                    value = form[k]
                            else:
                                continue

                            if channel.get_config_param(k) != value:
                                channel.plugin_config[k] = value
                                logger.info(
                                    'the %s parameter of channel %s has been changed to %s by user %s',
                                    k, channel.name, value,
                                    current_user.log_name)

                        if current_user.super_admin:
                            channel.cache_activated = 'cache-activated' in form and form[
                                'cache-activated'] == 'on'
                            channel.cache_validity = int(
                                form['cache-validity']
                            ) if 'cache-validity' in form and form[
                                'cache-validity'] else channel.cache_validity
                            channel.keep_noncomplying_capsules = 'keep-capsules' in form and form[
                                'keep-capsules'] == 'on'

                        channel.plugin_config = channel.plugin_config  # Force SQLObject update
                        try:
                            self.plugin_manager.invalidate_cache(
                                channel.plugin.name, channel.id)
                            self.plugin_manager.get_plugin(
                                channel.plugin.name).get_content(channel.id)
                        except MisconfiguredParameters as e:
                            for faulty_param in e:
                                add_feedback(form.action, faulty_param[0],
                                             faulty_param)
                            raise web.seeother('/channels/%d' % channel.id)
                        except Exception as e:
                            add_feedback(form.action, 'general_error', str(e))
                            raise web.seeother('/channels/%d' % channel.id)
                    else:
                        raise web.forbidden()
                    form.name = channel.name
                except (SQLObjectNotFound, ValueError):
                    raise ImmediateFeedback(form.action, 'invalid_id')
            if channel:
                form.name = channel.name
            add_feedback(form.action, 'ok')

        except ImmediateFeedback:
            if channel is not None and channel.enabled:
                form.enabled = 'on'
        store_form(form)
        return self.render_page(current_user)