def post(self, channel): config = json.loads(self.form.config) post_process_config(config, channel) try: content = get_content(channel.id, config) except ValueError: resp.badrequest() self.plugin_manager.dereference_assets(content) self.plugin_manager.cache_assets(content, channel.id) try: preview = self.ictv_renderer.render_screen(content, controls=True) except (KeyError, ValueError): return "Preview could not be generated.<br><code>%s</code>" % traceback.format_exc( ) except (TypeError): return "A problem occured." return preview
def PATCH_AUTH(self, capsule_id, channel): try: post_data = flask.request.get_json(force=True) except JSONDecodeError: resp.badrequest() try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() except SQLObjectNotFound: resp.notfound() if 'name' in post_data and len(post_data['name']) < 3: resp.badrequest() if 'validity' in post_data: validity_from, validity_to = post_data['validity'] if not (type(validity_from) == type(validity_to) == int) or validity_to < validity_from: resp.badrequest() try: validity_from, validity_to = datetime.fromtimestamp( validity_from), datetime.fromtimestamp(validity_to) except (TypeError, ValueError): resp.badrequest() post_data['validity_from'] = validity_from post_data['validity_to'] = validity_to del post_data['validity'] update_dict = { k: v for k, v in filter( lambda x: x[0] in ['name', 'theme', 'validity_from', 'validity_to'], post_data.items()) } try: c.set(**update_dict) except DuplicateEntryError: resp.badrequest() resp.nocontent()
def POST_AUTH(self, capsule_id, channel): try: post_data = flask.request.get_json(force=True) except JSONDecodeError: resp.badrequest() try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() except SQLObjectNotFound: resp.notfound() if {'duration', 'template', 'content'} != set(post_data.keys()): resp.badrequest() try: s = EditorSlide(s_order=c.slides.count(), capsule=c, **post_data) except SQLObjectIntegrityError: resp.badrequest() EditorSlide.rectify_s_order(c.id) response = resp.created() response.header[ 'Location'] = '/channels/{}/api/capsules/{}/slides/{}'.format( channel.id, capsule_id, s.id) return response
def PATCH_AUTH(self, capsule_id, slide_id, channel): try: post_data = flask.request.get_json(force=True) except JSONDecodeError: resp.badrequest() try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() s = EditorSlide.selectBy(id=int(slide_id), capsule=c).getOne() except SQLObjectNotFound: resp.notfound() update_dict = { k: v for k, v in filter( lambda x: x[0] in ['duration', 'template', 'content'], post_data.items()) } s.set(**update_dict) resp.nocontent()
def post(self, capsuleid, channel): try: caps_db = EditorCapsule.get(int(capsuleid)) slides = caps_db.slides form = self.form logger_extra = {'channel_name': channel.name, 'channel_id': channel.id} logger = get_logger('editor', channel) if caps_db.channel != channel: return "there is no capsule with the id " + str(capsuleid) + " in the channel " + str(channel.id) if form.action == 'delete': try: slide = EditorSlide.get(int(form.id)) except ValueError: return "this is not a valid slide id" except SQLObjectNotFound: return "there is no slide with the id " + form.id + " in the channel " + str(channel.id) slide.destroySelf() slides = EditorSlide.rectify_s_order(capsuleid) elif form.action == 'duplicate': try: slide = EditorSlide.get(int(form.id)) except ValueError: return "this is not a valid slide id" except SQLObjectNotFound: return "there is no slide with the id " + form.id + " in the channel " + str(channel.id) duplicate = slide.duplicate(s_order=-1) caps_db.insert_slide_at(duplicate, slide.s_order + 1) slides = caps_db.slides elif form.action == 'order': try: new_order = json.loads(form.order) except AttributeError: return "you seem to try to change the order of slides that doesn't exist..." except JSONDecodeError: return "invalid changes" for k, v in new_order.items(): try: slide = EditorSlide.get(int(k)) if slide.capsule.id != int(capsuleid): return "you try to change the order of a slide in a different capsule..." slide.s_order = int(v) except ValueError: return "invalid changes" except SQLObjectNotFound: return "You try to change the order of slides that doesn't exist..." slides = EditorSlide.rectify_s_order(capsuleid) elif form.action == 'theme': caps_db = EditorCapsule.get(int(capsuleid)) if caps_db.channel != channel: return "there is no capsule with the id " + str(capsuleid) + " in the channel " + str(channel.id) if form.theme not in Themes: raise ImmediateFeedback(form.action, 'not_existing_theme') caps_db.theme = form.theme elif form.action == 'edit': try: name = form.name.strip() capsule = caps_db if not name: raise ValueError('name') date_from = datetime.datetime.strptime(form['date-from'], "%Y-%m-%dT%H:%M:%S%z") date_to = datetime.datetime.strptime(form['date-to'], "%Y-%m-%dT%H:%M:%S%z") if date_to <= date_from: raise ValueError('dates') capsule.name = name capsule.validity_from = date_from capsule.validity_to = date_to except SQLObjectNotFound: return seeother(channel.id, '/') except DuplicateEntryError: raise ImmediateFeedback(form.action, 'name_already_exists') except ValueError as e: raise ImmediateFeedback(form.action, 'invalid_name' if e.args[0] == 'name' else 'dates_inverted') elif form.action.startswith('import'): storage_manager = StorageManager(channel.id) capsule = caps_db background_color = 'white' if 'white-background' in form and form['white-background'] == 'on' else 'black' offset = EditorSlide.selectBy(capsule=capsule).max('s_order') offset = offset + 1 if offset is not None else 0 if form.action == 'import-slides' and 'pdf' in form: slide_files = [] try: with Color(background_color) as bg: with Image(blob=form.pdf.read(), resolution=150) as pdf: for i, page in enumerate(pdf.sequence): img_page = Image(image=page) img_page.background_color = bg img_page.alpha_channel = False img_page.format = 'jpeg' img_page.transform(resize='1920x1080>') asset = storage_manager.store_file(img_page.make_blob('jpeg'), filename='import-capsule-%d-slide-%d.jpeg' % (capsule.id, offset + i), user=self.session['user']['id']) slide_files.append(asset) slide_duration = channel.get_config_param('duration') * 1000 for i, slide_file in enumerate(slide_files): s = EditorSlide(duration=slide_duration, content={'background-1': {'file': slide_file.id, 'size': 'contain', 'color': background_color}}, s_order=offset + i, template='template-image-bg', capsule=capsule) AssetSlideMapping(assetID=slide_file.id, slideID=s.id) except (ValueError, TypeError): logger.warning('An Exception has been encountered when importing PDF file:', extra=logger_extra, exc_info=True) elif form.action == 'import-video' and 'video' in form: try: video_slide = EditorSlide.from_video(form.video, storage_manager, self.transcoding_queue, capsule, self.session['user']['id'], background_color) if type(video_slide) is str: # Video is being transcoded raise ImmediateFeedback(form.action, 'video_transcoding', video_slide) except TypeError as e: raise ImmediateFeedback(form.action, 'invalid_video_format', e.type) else: resp.badrequest() elif form.action == 'duration': try: slide_id = int(form.id) slide = EditorSlide.get(slide_id) if "duration" in form: duration = float(form.duration)*1000 if duration < 0: raise ImmediateFeedback(form.action, "negative_slide_duration") else: duration = -1 slide.duration = duration except SQLObjectNotFound: return seeother(channel.id, '/') except ValueError: raise ImmediateFeedback(form.action, 'invalid_slide_duration') add_feedback(form.action, 'ok') except ValueError: return "this is not a valid capsule id" except SQLObjectNotFound: return "there is no capsule with the id " + str(capsuleid) + " in the channel " + str(channel.id) except ImmediateFeedback: store_form(form) return self.render_page(channel=channel, capsule=caps_db, slides=caps_db.slides)
def post(self, channel): form = self.form logger_extra = {'channel_name': channel.name, 'channel_id': channel.id} logger = get_logger('editor', channel) capsules = None try: if form['action'] == 'delete': try: capsule_id = int(form['id']) except ValueError: raise ImmediateFeedback(form.action, 'invalid_id') try: capsule = EditorCapsule.get(capsule_id) except SQLObjectNotFound: raise ImmediateFeedback(form.action, 'no_id_matching') if channel != capsule.channel: raise ImmediateFeedback(form.action, 'wrong_channel') if capsule is None: raise ImmediateFeedback(form.action, 'no_id_matching') capsule.destroySelf() capsules = EditorCapsule.rectify_c_order(channel.id) elif form['action'] == 'duplicate': try: capsule_id = int(form['id']) except ValueError: raise ImmediateFeedback(form.action, 'invalid_id') try: capsule = EditorCapsule.get(capsule_id) except SQLObjectNotFound: raise ImmediateFeedback(form.action, 'no_id_matching') if channel != capsule.channel: raise ImmediateFeedback(form.action, 'wrong_channel') if capsule is None: raise ImmediateFeedback(form.action, 'no_id_matching') capsule.duplicate(owner_id=self.session['user']['id']) capsules = EditorCapsule.rectify_c_order(channel.id) elif form.action == 'order': try: new_order = json.loads(form.order) except AttributeError: return "you seem to try to change the order of slides that doesn't exist..." except JSONDecodeError: return "invalid changes" capsules_to_reorder = {} for k in new_order.keys(): try: capsule = EditorCapsule.get(int(k)) capsules_to_reorder[k] = capsule if capsule.channel.id != channel.id: return "you try to change the order of a capsule in a different channel..." except SQLObjectNotFound: return "You try to change the order of slides that doesn't exist..." sorted_list = sorted(capsules_to_reorder.values(), key=lambda caps: caps.c_order) new_to_old_order = {} i = 0 for elem in sorted_list: new_to_old_order[i] = elem.c_order i += 1 try: for k, v in new_order.items(): capsules_to_reorder[k].c_order = new_to_old_order[int( v)] except ValueError: return "invalid changes" capsules = EditorCapsule.rectify_c_order(channel.id) elif form['action'] == 'create' or form['action'].startswith( 'import'): name = form['name'].strip() if not name: raise ImmediateFeedback(form.action, 'invalid_name') try: if 'date-from' in form and 'date-to' in form: try: date_from = datetime.datetime.strptime( form['date-from'], "%Y-%m-%dT%H:%M:%S%z") date_to = datetime.datetime.strptime( form['date-to'], "%Y-%m-%dT%H:%M:%S%z") except ValueError: raise ImmediateFeedback(form.action, 'wrong_date_values') else: if 'capsule_validity' in channel.plugin_config: validity = int( channel.plugin_config['capsule_validity']) else: validity = int( channel.plugin. channels_params['capsule_validity']['default']) date_from = datetime.datetime.now() time_delta = datetime.timedelta(hours=validity) date_to = date_from + time_delta if date_to <= date_from: raise ImmediateFeedback(form.action, 'dates_inverted') capsule = EditorCapsule( name=form['name'], channel=channel, ownerID=self.session['user']['id'], creation_date=datetime.datetime.now(), c_order=EditorCapsule.select().count(), validity_from=date_from, validity_to=date_to) if form.action.startswith('import'): storage_manager = StorageManager(channel.id) background_color = 'white' if 'white-background' in form and form[ 'white-background'] == 'on' else 'black' if form.action == 'import-capsule' and 'pdf' in form: slide_files = [] try: with Color(background_color) as bg: with Image(blob=form.pdf.read(), resolution=150) as pdf: for i, page in enumerate(pdf.sequence): img_page = Image(image=page) img_page.background_color = bg img_page.alpha_channel = False img_page.format = 'jpeg' img_page.transform( resize='1920x1080>') slide_files.append( storage_manager.store_file( img_page.make_blob('jpeg'), filename= 'import-capsule-%d-slide-%d.jpeg' % (capsule.id, i), user=self.session['user'] ['id'])) slide_duration = channel.get_config_param( 'duration') * 1000 for i, slide_file in enumerate(slide_files): s = EditorSlide( duration=slide_duration, content={ 'background-1': { 'file': slide_file.id, 'size': 'contain', 'color': background_color } }, s_order=i, template='template-image-bg', capsule=capsule) AssetSlideMapping(assetID=slide_file.id, slideID=s.id) except (ValueError, TypeError): logger.warning( 'An Exception has been encountered when importing PDF file:', extra=logger_extra, exc_info=True) elif form.action == 'import-video' and 'video' in form: try: video_slide = EditorSlide.from_video( form.video, storage_manager, self.transcoding_queue, capsule, self.session['user']['id'], background_color) if type(video_slide ) is str: # Video is being transcoded raise ImmediateFeedback( form.action, 'video_transcoding', video_slide) except TypeError as e: capsule.destroySelf() raise ImmediateFeedback( form.action, 'invalid_video_format', e.type if hasattr(e, 'type') else None) else: resp.badrequest() except DuplicateEntryError: raise ImmediateFeedback(form.action, 'name_already_exists') elif form['action'] == 'edit': name = form['name'].strip() if not name: raise ImmediateFeedback(form.action, 'invalid_name') try: capsule_id = int(form['id']) except ValueError: raise ImmediateFeedback(form.action, 'invalid_id') try: capsule = EditorCapsule.get(capsule_id) except SQLObjectNotFound: raise ImmediateFeedback(form.action, 'no_id_matching') if channel != capsule.channel: raise ImmediateFeedback(form.action, 'wrong_channel') try: capsule.name = name except DuplicateEntryError: raise ImmediateFeedback(form.action, 'name_already_exists') try: date_from = datetime.datetime.strptime( form['date-from'], "%Y-%m-%dT%H:%M:%S%z") date_to = datetime.datetime.strptime( form['date-to'], "%Y-%m-%dT%H:%M:%S%z") except ValueError: raise ImmediateFeedback(form.action, 'wrong_date_values') if date_to <= date_from: raise ImmediateFeedback(form.action, 'dates_inverted') capsule.validity_from = date_from capsule.validity_to = date_to add_feedback(form.action, 'ok') except ImmediateFeedback: pass store_form(form) return self.render_page(channel, capsules)
def POST_AUTH(self, channel): try: post_data = flask.request.get_json(force=True) except JSONDecodeError: resp.badrequest() if {'name', 'theme', 'validity'} != set(post_data.keys()): resp.badrequest() if 'name' in post_data and len(post_data['name']) < 3: resp.badrequest() validity_from, validity_to = post_data['validity'] if not (type(validity_from) == type(validity_to) == int) or validity_to < validity_from: resp.badrequest() try: validity_from, validity_to = datetime.fromtimestamp( validity_from), datetime.fromtimestamp(validity_to) except (TypeError, ValueError): resp.badrequest() try: c = EditorCapsule( name=post_data['name'], theme=post_data['theme'], validity_from=validity_from, validity_to=validity_to, channel=channel, c_order=EditorCapsule.selectBy(channel=channel).count()) except DuplicateEntryError: resp.badrequest() EditorCapsule.rectify_c_order(channel.id) response = resp.created() response.headers['Location'] = '/channels/{}/api/capsules/{}'.format( channel.id, c.id) return response
def post(self): """ Handles channel creation, editing, deletion, configuration and user permissions. """ form = self.form 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 len(name) > Channel.sqlmeta.columns['name'].length: raise ImmediateFeedback(form.action, 'too_long_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 self.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, drop_silently_non_complying_slides=p. drop_silently_non_complying_slides_default) 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: resp.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 self.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: resp.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 self.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: resp.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 self.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 = self.input(**{k: ['']})[k] elif pattern.match(inner_type): inner_type = inner_type[5:-1] if inner_type == 'string': delimiter = form[k + '-delimiter'] values = self.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.drop_silently_non_complying_slides = 'drop_silently_non_complying_slides' in form and form[ 'drop_silently_non_complying_slides'] == '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) resp.seeother('/channels/config/%d' % channel.id) except Exception as e: add_feedback(form.action, 'general_error', str(e)) resp.seeother('/channels/config/%d' % channel.id) else: resp.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)