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 DELETE_AUTH(self, capsule_id, slide_id, channel): try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() EditorSlide.selectBy(id=int(slide_id), capsule=c).getOne().destroySelf() except SQLObjectNotFound: raise web.notfound() raise web.nocontent()
def POST(self, slide_id, channel): try: s_db = EditorSlide.get(int(slide_id)) except ValueError: return "this is not a valid slide id" except SQLObjectNotFound: return "there is no slide with the id " + str( slide_id) + " in the channel " + str(channel.id) form = web.input() new_content, assets = update_slide(form=form, storage_manager=StorageManager( channel.id), user_id=self.session['user']['id']) template = form['template'] previous_content = copy.deepcopy(s_db.content) for field_type, input_data in previous_content.items(): if 'file' in input_data and new_content[field_type].pop( 'src', None): # New content is a link to the previous asset, keep the asset reference new_content[field_type]['file'] = input_data['file'] if 'file' in input_data and 'file' in new_content[ field_type] and input_data['file'] != new_content[ field_type]['file']: AssetSlideMapping.selectBy( assetID=input_data['file'], slideID=s_db.id).getOne().destroySelf() for asset in assets: mapping = AssetSlideMapping(assetID=asset.id, slideID=s_db.id) s_db.template = template s_db.content = new_content # Force SQLObject update raise seeother(channel.id, '/capsules/' + str(s_db.capsule.id))
def GET_AUTH(self, capsule_id, slide_id, channel): try: EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() return EditorSlide.selectBy( id=int(slide_id), capsule=int(capsule_id)).getOne().to_json_api() except SQLObjectNotFound: raise web.notfound()
def get(self, capsuleid, channel): try: 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) 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) slides = EditorSlide.rectify_s_order(capsuleid) return self.render_page(channel=channel, capsule=caps_db, slides=slides)
def runTest(self): """ Tests the GET endpoints. """ self.channel.plugin_config['enable_rest_api'] = True self.channel.plugin_config['api_key'] = 'aaaaaaaa' validity_from = datetime(2018, 10, 2, 12, 00) validity_to = datetime(2018, 10, 2, 14, 00) api_capsule = EditorCapsule(name='api_capsule', channel=self.channel, creation_date=datetime.now(), c_order=0, validity_from=validity_from, validity_to=validity_to, theme='ictv') r = self.testApp.get('/channels/{}/api/capsules'.format( self.channel.id), status=200, headers={'X-ICTV-editor-API-Key': 'aaaaaaaa'}) assert r.body.decode( ) == '[{"id": 1, "name": "api_capsule", "slides": [], "validity": [1538474400, 1538481600], "theme": "ictv"}]' EditorCapsule(name='api_capsule2', channel=self.channel, creation_date=datetime.now(), c_order=1, validity_from=validity_from + timedelta(minutes=1), validity_to=validity_to, theme='ictv') r = self.testApp.get('/channels/{}/api/capsules'.format( self.channel.id), status=200, headers={'X-ICTV-editor-API-Key': 'aaaaaaaa'}) assert r.body.decode( ) == '[{"id": 1, "name": "api_capsule", "slides": [], "validity": [1538474400, 1538481600], "theme": "ictv"}, {"id": 2, "name": "api_capsule2", "slides": [], "validity": [1538474460, 1538481600], "theme": "ictv"}]' EditorSlide(duration=42, s_order=0, template='api-template', capsule=api_capsule) r = self.testApp.get('/channels/{}/api/capsules'.format( self.channel.id), status=200, headers={'X-ICTV-editor-API-Key': 'aaaaaaaa'}) assert r.body.decode( ) == '[{"id": 1, "name": "api_capsule", "slides": [{"id": 1, "duration": 42, "content": {}, "template": "api-template"}], "validity": [1538474400, 1538481600], "theme": "ictv"}, {"id": 2, "name": "api_capsule2", "slides": [], "validity": [1538474460, 1538481600], "theme": "ictv"}]' r = self.testApp.get('/channels/{}/api/capsules/1'.format( self.channel.id), status=200, headers={'X-ICTV-editor-API-Key': 'aaaaaaaa'}) assert r.body.decode( ) == '{"id": 1, "name": "api_capsule", "slides": [{"id": 1, "duration": 42, "content": {}, "template": "api-template"}], "validity": [1538474400, 1538481600], "theme": "ictv"}' r = self.testApp.get('/channels/{}/api/capsules/1/slides/1'.format( self.channel.id), status=200, headers={'X-ICTV-editor-API-Key': 'aaaaaaaa'}) assert r.body.decode( ) == '{"id": 1, "duration": 42, "content": {}, "template": "api-template"}'
def POST(self, capsuleid, channel): form = web.input() content, assets = update_slide(form=form, storage_manager=StorageManager( channel.id), user_id=self.session['user']['id']) template = form['template'] try: capsule = EditorCapsule.get(capsuleid) if capsule.channel != channel: return 'there is no capsule with id ' + capsuleid + ' in channel ' + channel.name except SQLObjectNotFound: return 'there is no capsule with id ' + capsuleid s = EditorSlide( duration=-1, content=content, s_order=EditorSlide.selectBy(capsule=capsuleid).count(), template=template, capsule=capsuleid) list(capsule.slides).append(s) for asset in assets: mapping = AssetSlideMapping(assetID=asset.id, slideID=s.id) raise seeother(channel.id, '/capsules/' + str(capsuleid))
def POST_AUTH(self, capsule_id, channel): try: post_data = json.loads(web.data().decode()) except JSONDecodeError: raise web.badrequest() try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() except SQLObjectNotFound: raise web.notfound() if {'duration', 'template', 'content'} != set(post_data.keys()): raise web.badrequest() try: s = EditorSlide(s_order=c.slides.count(), capsule=c, **post_data) except SQLObjectIntegrityError: raise web.badrequest() EditorSlide.rectify_s_order(c.id) web.header( 'Location', '/channels/{}/api/capsules/{}/slides/{}'.format( channel.id, capsule_id, s.id)) raise web.created()
def GET(self, slide_id, channel): try: slide_id = int(slide_id) s = EditorSlide.get(slide_id) if s.contains_video: raise seeother(channel.id, '/capsules/%d' % s.capsule.id) plugin_s = s.to_plugin_slide() except ValueError: return "this is not a valid slide id" except SQLObjectNotFound: return "there is no slide with the id " + str( slide_id) + " in the channel " + str(channel.id) capsule = s.capsule.to_plugin_capsule() PluginManager.dereference_assets([capsule]) capsule_theme = capsule.get_theme() if capsule.get_theme( ) in Themes else self.app.config['default_theme'] content = Themes.get_slide_defaults(capsule_theme) deep_update(content, s.get_content()) vertical = channel.get_config_param('vertical') templates = self.slide_templates template = s.get_template() if vertical: templates = { 'template-image-bg': templates['template-image-bg'], 'template-background-text-center': self.slide_templates['template-background-text-center'] } default_template = channel.get_config_param('default_template') template = template if default_template in [ "template-image-bg", 'template-background-text-center' ] else 'template-background-text-center' return self.renderer.editor( capsule=s.capsule, channel=channel, slide=s, template=template, old_content=json.dumps(content), templates=templates, theme=capsule_theme, theme_palette=Themes[capsule_theme].get('ckeditor_palette'), themes=Themes.prepare_for_css_inclusion([capsule_theme]), theme_defaults=json.dumps( Themes.get_slide_defaults(capsule_theme)), vertical=channel.get_config_param('vertical'))
def PATCH_AUTH(self, capsule_id, slide_id, channel): try: post_data = json.loads(web.data().decode()) except JSONDecodeError: raise web.badrequest() try: c = EditorCapsule.selectBy(id=int(capsule_id), channel=channel).getOne() s = EditorSlide.selectBy(id=int(slide_id), capsule=c).getOne() except SQLObjectNotFound: raise web.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) raise web.nocontent()
def GET(self, capsule_id, slide_id=None, template=None): capsule = EditorCapsule.get(capsule_id) theme = capsule.theme if capsule.theme in Themes else self.config[ 'default_theme'] if slide_id: slide = EditorSlide.get(slide_id) capsule = type('DummyCapsule', (PluginCapsule, object), { 'get_slides': lambda: [slide], 'get_theme': lambda: theme }) self.plugin_manager.dereference_assets([capsule]) content = slide.get_content() duration = slide.duration t = template if template else slide.get_template() slide = type( 'DummySlide', (PluginSlide, object), { 'get_duration': lambda: duration, 'get_content': lambda: content, 'get_template': lambda: t }) else: slide = type( 'DummySlide', (PluginSlide, object), { 'get_duration': lambda: 5000, 'get_content': lambda: {}, 'get_template': lambda: template }) if template: content = self.get_slide_defaults(slide) deep_update(content, Themes.get_slide_defaults(theme)) deep_update(content, slide.get_content()) slide.get_content = lambda: content return self.ictv_renderer.preview_slide(slide, theme, small_size=True)
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 = web.input( pdf={}, video={} ) # Force CGI FieldStorage object to be created for large files 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.file.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: raise web.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)