def __init__(self, renderer_globals, app): """ Initializes a slide renderer with the given globals. :param renderer_globals: a dictionary in the form { '[field-type]': Python function, ... } Each function is responsible of rendering the corresponding field and will be called by the renderer when a corresponding function is parsed in a slide template. """ renderer_globals['base'] = "base.html" self.renderer_globals = renderer_globals self.renderer_globals[ 'get_template_id'] = lambda: utils.generate_secret(digits='') ### Jinja2 ### self.slide_renderer = render_jinja( os.path.join(get_root_path(), 'renderer/templates/')) self.slide_renderer._lookup.globals.update(**self.renderer_globals) self.preview_renderer = render_jinja( os.path.join(get_root_path(), 'renderer/')) self.preview_renderer._lookup.globals.update(**self.renderer_globals) ########### self.app = app super(SlideRenderer, self).__init__()
def main(config_path, address_port): logger = logging.getLogger('app') try: app = get_app(config_path).get_app_dispatcher() if is_test() or app.config['debug']['serve_static']: cwd = os.getcwd() os.chdir(get_root_path()) if not os.path.exists(os.path.join(get_root_path(), 'sessions')): os.mkdir(os.path.join(get_root_path(), 'sessions')) os.chdir(cwd) if not is_test(): address_port = address_port.rsplit(':', 1) address = '0.0.0.0' if len(address_port) == 1 else address_port[0] port = address_port[0] if len( address_port) == 1 else address_port[1] werkzeug.serving.run_simple( address, int(port), app, use_debugger=app.config['debug'] != None) else: return app except Exception as e: logger.error('Exception encountered when starting the application', exc_info=True) raise e
def on_asset_deleted(instance, kwargs): """ Deletes the file associated with this asset when this asset is deleted. """ try: os.remove(os.path.join(get_root_path(), instance.path)) except (OSError, TypeError) as e: # TODO: log message to app.log print(e)
def make_client_zip(): client_zip_path = os.path.join(get_root_path(), 'client', 'ks', 'client' + os.extsep + 'zip') if os.path.exists(client_zip_path): os.unlink(client_zip_path) def add_file(file): client_zip.write(os.path.join(get_root_path(), 'client', file), arcname=file) with ZipFile(client_zip_path, 'w') as client_zip: add_file('cache_daemon.py') add_file('static/bootstrap.html') add_file('static/jquery.min.js') add_file('static/jquery.xmlrpc.min.js') add_file('static/waiting_screen.html') with open(os.path.join(get_root_path(), 'client', 'static', 'config' + os.extsep + 'json')) as client_config_file: client_zip.writestr('static/config.json', client_config_file.read() % flask.g.homedomain)
def get_paths(file_hash): iframe_path = 'plugins/embed/iframe_' + file_hash + '.html' file_prefix = os.path.join(get_root_path(), 'static') full_iframe_path = os.path.join(file_prefix, iframe_path) inlined_page = 'plugins/embed/' + file_hash + '.html' full_inlined_page_path = os.path.join(file_prefix, inlined_page) return iframe_path, full_iframe_path, inlined_page, full_inlined_page_path
def main(config_file): logger = logging.getLogger('app') try: app = get_app(config_file) if is_test() or app.config['debug']['serve_static']: os.chdir(get_root_path()) if not os.path.exists(os.path.join(get_root_path(), 'sessions')): os.mkdir(os.path.join(get_root_path(), 'sessions')) if not is_test(): app.run() else: return app except Exception as e: logger.error('Exception encountered when starting the application', exc_info=True) raise e
def get(self): for name in loggers_stats: try: size = os.path.getsize(os.path.join(get_root_path(), "logs", "%s.log" % name)) except FileNotFoundError: size = 0 loggers_stats[name]["size"] = pretty_print_size(size) return self.renderer.logs(loggers_stats=loggers_stats, time_since=timesince)
def init_logger(logger_name, level=logging.INFO, rotation_interval=7, backup_count=2): if not os.path.exists(os.path.join(get_root_path(), 'logs')): os.mkdir(os.path.join(get_root_path(), 'logs')) logger = logging.getLogger(logger_name) logger.setLevel(level) formatter = logging.Formatter('%(levelname)s : %(asctime)s - %(message)s') logger_file_path = os.path.join(get_root_path(), 'logs', logger_name + os.extsep + 'log') file_handler = logging.handlers.TimedRotatingFileHandler( logger_file_path, when='D', interval=rotation_interval, backupCount=backup_count) file_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.addHandler(StatHandler(logger_name, logger_file_path))
def make_system_zip(): system_zip_path = os.path.join(get_root_path(), 'client', 'ks', 'system' + os.extsep + 'zip') if os.path.exists(system_zip_path): os.unlink(system_zip_path) def add_file(file): system_zip.write(os.path.join(get_root_path(), 'client', 'ks', file), arcname=file) with ZipFile(system_zip_path, 'w') as system_zip: add_file(os.path.join('etc', 'pam.d', 'xserver')) add_file(os.path.join('home', 'ictv', '.xinitrc'))
class FakePluginTestCase(ICTVTestCase): fake_plugin_root = os.path.join(get_root_path(), 'plugins', 'fake_plugin') def setUp(self, fake_plugin_middleware=lambda: None, ictv_middleware=lambda: None): create_fake_plugin(self.fake_plugin_root) fake_plugin_middleware() super(FakePluginTestCase, self).setUp(middleware=ictv_middleware) def tearDown(self): shutil.rmtree(self.fake_plugin_root) super(FakePluginTestCase, self).tearDown()
def get_plugins_modules(): """ Returns a list of Python modules containing all the plugin modules found in the plugin directory. """ dirs = next(os.walk(os.path.join(get_root_path(), 'plugins')))[1] if '__pycache__' in dirs: dirs.remove("__pycache__") modules_list = [] for d in dirs: try: modules_list.append(importlib.import_module("ictv.plugins." + d + "." + d)) except ModuleNotFoundError: raise Warning('Directory %s did not contain a valid plugin module' % d) return modules_list
def count_entries(logger_name): """ Count the number of entries in each log file and return this number for the logger_name """ n_entries = 0 try: with open(os.path.join(get_root_path(), "logs", "%s.log" % logger_name)) as log_file: for line in log_file: if line.startswith("DEBUG : ") or line.startswith( "INFO : ") or line.startswith( "WARNING : ") or line.startswith("ERROR : "): n_entries += 1 except FileNotFoundError: # loggers in db with no log file anymore (the file can be created later): their # number of entries will be 0 pass return n_entries
def runTest(self): """ Tests the Asset SQLObject. """ fake_plugin = Plugin(name='fake_plugin', activated='notfound') asset_channel = PluginChannel(name='Asset Channel', plugin=fake_plugin, subscription_right='public') a1 = Asset(plugin_channel=asset_channel, user=None, filename='path_test', extension='.txt') last_ref_a1 = a1.last_reference time.sleep(1) assert a1.path == os.path.join('static', 'storage', str(asset_channel.id), str(a1.id) + '.txt') assert a1.last_reference > last_ref_a1 a2 = Asset(plugin_channel=asset_channel, user=None, filename='path_test') assert a2.path == os.path.join('static', 'storage', str(asset_channel.id), str(a2.id)) a3 = Asset(plugin_channel=asset_channel, user=None, filename='cache_test', in_flight=True) a3_path = os.path.join('static', 'storage', str(asset_channel.id), str(a3.id)) assert a3.path is None assert a3._get_path(force=True) == a3_path a3.in_flight = False assert a3.path == a3_path a4 = Asset(plugin_channel=asset_channel, user=None, filename='test_write', extension='.txt') a4_path = os.path.join(get_root_path(), 'static', 'storage', str(asset_channel.id), str(a4.id) + '.txt') a4_content = 'a4 file content'.encode() a4.write_to_asset_file(a4_content) with open(a4_path, 'rb') as f: assert f.read() == a4_content a4.destroySelf() assert not os.path.exists(a4_path)
def get(self, file): path = os.path.join(get_root_path(), 'client', 'ks', file) if os.path.exists(path): flags = 'r' if self._path_has_ext(path, 'zip'): flags += 'b' make_client_zip() with open(path, flags) as f: content = f.read() if self._path_has_ext(path, 'ks') or self._path_has_ext(path, 'cfg'): content = content.format(ictv_root_url=flask.g.homedomain, **self.config['client']) response = flask.Response(content) response.headers['Content-Type'] = 'application/zip' return response resp.notfound()
def GET(self, file): path = os.path.join(get_root_path(), 'client', 'ks', file) if os.path.exists(path): flags = 'r' if self._path_has_ext(path, 'zip'): flags += 'b' make_client_zip() web.header('Content-Type', 'application/zip') with open(path, flags) as f: content = f.read() if self._path_has_ext(path, 'ks') or self._path_has_ext( path, 'cfg'): content = content.format(ictv_root_url=web.ctx.homedomain, **self.config['client']) return content raise web.notfound()
def load_default_slides(config_dict): """ Loads the default slides configuration file inside the main configuration dict. Returns the dict. """ default_slides_path = config_dict.get( 'default_slides') or os.path.join( get_root_path(), 'default_slides.default' + os.extsep + 'yaml') default_slides_path = os.path.join(os.path.dirname(config_path), default_slides_path) if not os.path.exists(default_slides_path): raise Exception( 'Default slides config file could not be found in %s' % default_slides_path) with open(default_slides_path) as default_slides_file: config_dict['default_slides'] = yaml.load(default_slides_file) return config_dict
def get_app(ictv_app): app = web.application(urls, globals()) app.renderer = web.template.render( os.path.join(os.path.dirname(__file__), 'templates'), base=os.path.join(get_root_path(), 'templates', 'base'), globals={ 'session': ictv_app.session, 'get_feedbacks': get_feedbacks, 'get_next_feedbacks': get_next_feedbacks, 'pop_previous_form': pop_previous_form, 'UserPermissions': UserPermissions, 'str': str, 'sidebar_collapse': True, 'show_header': False, 'show_footer': False, 'User': User }) RssPage.plugin_app = app return app
def get_templates(): """ Returns a list of templates usable by the editor. """ templates = OrderedDict() for template in Templates: templates[template] = { 'themes': {}, 'name': Templates[template]['name'], 'description': Templates[template]['description'] } for theme in Themes: image_filename = '%s-%s.png' % (template, theme) if os.path.isfile( os.path.join(get_root_path(), 'plugins/editor/static/images', image_filename)): templates[template]['themes'][theme] = image_filename else: templates[template]['themes'][ theme] = 'placeholder_template.png' return templates
def create_iframe_page(src, width, height, file_hash): html = """<html> <head> <meta charset="UTF-8"> <title></title> </head> <body style="margin: 0;"> <iframe scrolling="no" src="{0}" width="{1}" height="{2}" frameborder="0" marginwidth="0" marginheight="0"> </iframe> <script> function resize() {{ var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; var iframe = document.getElementsByTagName('iframe')[0]; var iframe_width = iframe.getAttribute('width'); var iframe_height = iframe.getAttribute('height'); var scale_width = width / iframe_width; var scale_height = height / iframe_height; var scale = Math.min(scale_width, scale_height); var transform_x = -(((width / scale_width) - iframe_width) / 2); var transform_y = -(((height / scale_height) - iframe_height) / 2); iframe.setAttribute('style', 'transform: scale('+scale_width+','+scale_height+'); transform-origin: '+transform_x+'px '+transform_y+'px;-webkit-transform: scale('+scale_width+','+scale_height+'); -webkit-transform-origin: '+transform_x+'px '+transform_y+'px;') }} window.addEventListener('load', resize, true); window.addEventListener('resize', resize, true); </script> </body> </html>""".format(src, width, height) iframe_page = 'plugins/embed/iframe_' + file_hash + '.html' with open(os.path.join(get_root_path(), 'static', iframe_page), 'w') as f: f.write(html) return iframe_page
def instantiate_plugin(self, app, plugin): """ Loads the plugin. Returns whether the plugin could be instantiated or not. Plugin state may be changed to disabled if an error occurs. """ self.missing_dependencies[plugin.name] = self.get_plugin_missing_dependencies(plugin.name) if self.missing_dependencies[plugin.name]: plugin.activated = 'no' logger.warning( 'Plugin %s has missing dependencies, could not instantiate it. Plugin is now disabled' % plugin.name) get_logger(plugin.name).error('Could not instantiate plugin due to the following missing modules: %s' % (', '.join(self.missing_dependencies[plugin.name]))) return False try: plugin_module = self.get_plugin(plugin.name, reload=True) except ImportError: plugin.activated = 'no' logger.error('Encountered an exception when importing plugin %s' % plugin.name, exc_info=True) return False if hasattr(plugin_module, 'install'): getattr(plugin_module, 'install')() if hasattr(plugin_module, 'update'): getattr(plugin_module, 'update')(plugin) if plugin.static: static_dir = os.path.join(plugin.package_path, "static") os.makedirs(static_dir, exist_ok=True) if os.path.isdir(static_dir): link_name = os.path.join(get_root_path(), 'static/plugins', plugin.name) if not os.path.exists(link_name): os.makedirs(os.path.dirname(link_name), exist_ok=True) os.symlink(static_dir, link_name, target_is_directory=True) else: plugin.activated = 'no' logger.warning( 'Plugin %s static directory is a file, could not instantiate it. Plugin is now disabled' % plugin.name) return False if plugin.webapp: self.add_mappings(app, plugin) return True
def get_app(ictv_app): app = FrankenFlask(__name__) template_globals = { 'session': ictv_app.session, 'get_feedbacks': get_feedbacks, 'get_next_feedbacks': get_next_feedbacks, 'pop_previous_form': pop_previous_form, 'UserPermissions': UserPermissions, 'str': str, 'sidebar_collapse': True, 'show_header': False, 'show_footer': False, 'User': User, 'base': 'base.html' } app.renderer = render_jinja([ os.path.join(os.path.dirname(__file__), 'templates/'), os.path.join(get_root_path(), 'templates/') ]) app.renderer._lookup.globals.update(**template_globals) # Registering views app.add_url_rule('/index', view_func=IndexPage.as_view('IndexPage'), methods=get_methods(IndexPage)) app.add_url_rule('/feed', view_func=FeedGetter.as_view('FeedGetter'), methods=get_methods(FeedGetter)) app.add_url_rule('/content', view_func=ContentPage.as_view('ContentPage'), methods=get_methods(ContentPage)) app.add_url_rule('/preview', view_func=PreviewPage.as_view('PreviewPage'), methods=get_methods(PreviewPage)) RssPage.plugin_app = app return app
def __init__(self, *args, **kwargs): templates = {} for template in os.listdir( os.path.join(get_root_path(), 'renderer/templates')): if template != 'base.html': templates[os.path.splitext(template)[0]] = {} for template in templates: def f(type): def g(*args, **kwargs): id = type + '-' + str(kwargs['number']) templates[template][id] = { 'max_chars': kwargs['max_chars'] } if 'max_chars' in kwargs else {} return g dummy_renderer = SlideRenderer( { 'title': f('title'), 'subtitle': f('subtitle'), 'img': f('image'), 'logo': f('logo'), 'text': f('text'), 'background': f('background') }, None) # Useful to set some attribute in the template getattr(dummy_renderer.slide_renderer, template)(slide=None) variables = get_const_in_template(template) templates[template]['name'] = variables['name'] templates[template]['description'] = variables['description'] self._templates = templates super().__init__(self)
def load_templates_and_themes(): Template.deleteMany(None) for template in next( (os.walk(os.path.join(get_root_path(), 'renderer/templates/'))))[2]: Template(name=template.replace('.html', ''))
def get_app(ictv_app): """ Returns the web.py application of the editor. """ urls = ( 'index', 'ictv.plugins.editor.capsules_page.CapsulesPage', 'template/(\d+)/(.+)', 'ictv.plugins.editor.app.ServeTemplate', 'capsules', 'ictv.plugins.editor.capsules_page.CapsulesPage', 'capsules/(\d+)', 'ictv.plugins.editor.slides_page.SlidesPage', 'capsules/(\d+)/newslide', 'ictv.plugins.editor.app.Index', 'edit/(\d+)', 'ictv.plugins.editor.app.Edit', 'preview/expired', 'ictv.plugins.editor.rendering_pages.RenderExpired', 'preview/currentandfuture', 'ictv.plugins.editor.rendering_pages.RenderCurrentAndFuture', 'render/(\d+)/(\d+)?/?(.*)', 'ictv.plugins.editor.app.LocalSlideRender', 'api/capsules', 'ictv.plugins.editor.api.APIIndex', 'api/capsules/(\d+)', 'ictv.plugins.editor.api.APICapsules', 'api/capsules/(\d+)/slides', 'ictv.plugins.editor.api.APIIndexSlides', 'api/capsules/(\d+)/slides/(\d+)', 'ictv.plugins.editor.api.APISlides', 'api/templates', 'ictv.plugins.editor.api.APITemplates', ) app = web.application(urls, globals()) app.renderer = web.template.render( os.path.join(os.path.dirname(__file__), 'templates'), base=os.path.join(get_root_path(), 'templates', 'base'), globals={ 'session': ictv_app.session, 'get_feedbacks': get_feedbacks, 'get_next_feedbacks': get_next_feedbacks, 'pop_previous_form': pop_previous_form, 'json': json, 'UserPermissions': UserPermissions, 'str': str, 'sidebar_collapse': True, 'show_header': False, 'show_footer': False, 'User': User }, cache=not ictv_app.config['debug']['debug_on_error']) def get_templates(): """ Returns a list of templates usable by the editor. """ templates = OrderedDict() for template in Templates: templates[template] = { 'themes': {}, 'name': Templates[template]['name'], 'description': Templates[template]['description'] } for theme in Themes: image_filename = '%s-%s.png' % (template, theme) if os.path.isfile( os.path.join(get_root_path(), 'plugins/editor/static/images', image_filename)): templates[template]['themes'][theme] = image_filename else: templates[template]['themes'][ theme] = 'placeholder_template.png' return templates def get_editor_slide_renderer(): """ Returns an editor slide renderer. """ def make_title(**kwargs): h = HTML() try: args = { 'id': 'title-' + str(kwargs['number']), 'klass': 'title', 'data-editor-type': "text", 'data-editor-placeholder': kwargs['editor_placeholder'], 'data-editor-label': kwargs['editor_label'], 'data-editor-default': kwargs['editor_default'] } except KeyError: args = { 'id': 'title-' + str(kwargs['number']), 'klass': 'title', 'data-editor-type': "text", 'data-editor-placeholder': 'Title', 'data-editor-label': 'Title', 'data-editor-default': 'Title' } if kwargs['content'] is not None: text = kwargs['content']['title-' + str(kwargs['number'])]['text'] args['data-editor-default'] = text if 'max_chars' in kwargs: args['data-editor-max-chars'] = str(kwargs['max_chars']) h.h1('', **args) return str(h) def make_subtitle(**kwargs): h = HTML() try: args = { 'id': 'subtitle-' + str(kwargs['number']), 'klass': 'subtitle', 'data-editor-type': "text", 'data-editor-placeholder': kwargs['editor_placeholder'], 'data-editor-label': kwargs['editor_label'], 'data-editor-default': kwargs['editor_default'], 'data-editor-optional': "true" } except KeyError: args = { 'id': 'subtitle-' + str(kwargs['number']), 'klass': 'subtitle', 'data-editor-type': "text", 'data-editor-placeholder': 'Subtitle', 'data-editor-label': 'Subtitle', 'data-editor-default': 'Subtitle', 'data-editor-optional': "true" } if kwargs['content'] is not None: text = kwargs['content']['subtitle-' + str(kwargs['number'])]['text'] args['data-editor-default'] = text if 'max_chars' in kwargs: args['data-editor-max-chars'] = str(kwargs['max_chars']) h.h4('', **args) return str(h) def make_img(**kwargs): h = HTML() try: args = { 'id': 'image-' + str(kwargs['number']), 'klass': 'sub-image', 'data-editor-type': "image", 'data-editor-placeholder': kwargs['editor_placeholder'], 'data-editor-label': kwargs['editor_label'], 'data-editor-default': kwargs['editor_default'], 'data-editor-mediatype': "image" } except KeyError: args = { 'id': 'image-' + str(kwargs['number']), 'klass': 'sub-image', 'data-editor-type': "image", 'data-editor-placeholder': "/static/plugins/editor/placeholders/270x350.png", 'data-editor-label': 'Image', 'data-editor-default': "/static/plugins/editor/placeholders/270x350.png", 'data-editor-mediatype': "image" } if kwargs['content'] is not None: src = kwargs['content']['image-' + str(kwargs['number'])]['src'] args['data-editor-default'] = src if 'style' in kwargs: args['style'] = kwargs['style'] h.img(src='/static/plugins/editor/placeholders/270x350.png', **args) return str(h) def make_logo(**kwargs): h = HTML() try: args = { 'id': 'logo-' + str(kwargs['number']), 'data-editor-type': "image", 'data-editor-placeholder': kwargs['editor_placeholder'], 'data-editor-label': kwargs['editor_label'], 'data-editor-default': kwargs['editor_default'], 'data-editor-mediatype': "image" } except KeyError: args = { 'id': 'logo-' + str(kwargs['number']), 'data-editor-type': "image", 'data-editor-placeholder': "/static/plugins/editor/placeholders/270x350.png", 'data-editor-label': 'Logo', 'data-editor-default': "/static/plugins/editor/placeholders/270x350.png", 'data-editor-mediatype': "image" } if kwargs['content'] is not None: src = kwargs['content']['logo-' + str(kwargs['number'])]['src'] args['data-editor-default'] = src h.img(src=args['data-editor-default'], **args) return str(h) def make_text(**kwargs): h = HTML() try: args = { 'id': 'text-' + str(kwargs['number']), 'data-editor-type': "textarea", 'data-editor-placeholder': kwargs['editor_placeholder'], 'data-editor-label': kwargs['editor_label'], 'data-editor-default': kwargs['editor_default'], 'style': kwargs.get('style') or 'text-align:justify', 'klass': 'text' } except KeyError: args = { 'id': 'text-' + str(kwargs['number']), 'data-editor-type': "textarea", 'data-editor-placeholder': "Text", 'data-editor-label': 'Text', 'data-editor-default': "Text", 'style': kwargs.get('style') or 'text-align:justify', 'klass': 'text' } if kwargs['content'] is not None: text = kwargs['content']['text-' + str(kwargs['number'])]['text'] args = args['data-editor-default'] = text if 'max_chars' in kwargs: args['data-editor-max-chars'] = str(kwargs['max_chars']) h.div('', **args) return str(h) def make_background(**kwargs): if kwargs['content'] is None: src = kwargs['editor_default'] size = 'cover' color = 'black' else: id = 'background-' + str(kwargs['number']) src = kwargs['content'][id]['src'] size = kwargs['content'][id]['size'] color = kwargs['content'][id]['color'] if 'color' in kwargs[ 'content'][id] else 'black' src = '/static/plugins/editor/%s' % src if not src.startswith( '/static/plugins/editor/') else src return 'data-background-image="' + src + '" data-background-size="' + size + '" data-background-color="' + color + '" ' \ 'data-editor-type="background" ' \ 'data-editor-placeholder="/static/plugins/editor/images/beach.jpg"' \ 'data-editor-default="' + src + '"' \ 'data-editor-label="Arrière-plan"' renderer_globals = { 'title': make_title, 'subtitle': make_subtitle, 'img': make_img, 'logo': make_logo, 'text': make_text, 'background': make_background } return SlideRenderer(renderer_globals=renderer_globals, app=ictv_app) app.slide_templates = get_templates() app.slide_renderer = get_editor_slide_renderer() EditorPage.plugin_app = app return app
def get_app(config_path): """ Returns the flask main application of ICTV. Currently, only one application can be run a time due to how data such as assets, database, config files or plugins is stored. """ config = get_config(config_path) if database.database_path is None: database.database_path = config['database_uri'] # Create a base flask application app = FrankenFlask(__name__) # The following line might be used to speedup queries app.config["SEND_FILE_MAX_AGE_DEFAULT"] = 300 app.config.update(**config) init_flask_url_mapping(app) app.version = ictv.common.__version__ with open(os.path.join(get_root_path(), 'info' + os.extsep + 'yaml')) as f: # Loads ICTV user info texts info_texts = yaml.unsafe_load(f) # Load the SMTP config into web.py smtp_conf = app.config.get('smtp', None) if smtp_conf: app.config['MAIL_DEFAULT_SENDER'] = smtp_conf['sender_name'] app.config['MAIL_SERVER'] = smtp_conf['host'] app.config['MAIL_PORT'] = smtp_conf['port'] app.config['MAIL_USERNAME'] = smtp_conf.get('username', '') app.config['MAIL_PASSWORD'] = smtp_conf.get('password', '') app.config['MAIL_USE_TLS'] = smtp_conf.get('starttls', False) # Create a persistent HTTP session storage for the app app.secret_key = app.config['session_secret_key'] # Populate the jinja templates globals template_globals = { 'session': app.session, 'get_feedbacks': get_feedbacks, 'get_next_feedbacks': get_next_feedbacks, 'pop_previous_form': pop_previous_form, 'UserPermissions': UserPermissions, 'json': json, 'str': str, 'sorted': sorted, 'hasattr': hasattr, 'sidebar_collapse': False, 'show_header': True, 'show_footer': True, 're': re, 'info': info_texts, 'make_tooltip': make_tooltip, 'make_alert': make_alert, 'escape': html.escape, 'show_reset_password': '******' in app.config['authentication'], 'homedomain': lambda: flask.request.url_root[:-1], 'generate_secret': generate_secret, 'version': lambda: app.version, 'pretty_print_size': pretty_print_size, 'timesince': timesince, 'User': User, 'get_user': lambda: User.get(app.session['user']['id']) } ### Jinja2 renderer ### app.renderer = render_jinja(os.path.join(get_root_path(), 'templates/')) app.renderer._lookup.globals.update(base='base.html', **template_globals) app.standalone_renderer = render_jinja( os.path.join(get_root_path(), 'templates/')) app.standalone_renderer._lookup.globals.update(**template_globals) # Init loggers load_loggers_stats() # Determine logging level and user feedback when an internal error occurs based on ICTV core config level = logging.INFO loggers_to_init = [ 'app', 'pages', 'screens', 'plugin_manager', 'storage_manager', 'local_login', 'database', 'transcoding_queue' ] for logger_name in loggers_to_init: init_logger(logger_name, level, rotation_interval=app.config['logs']['rotation_interval'], backup_count=app.config['logs']['backup_count']) # Init the renderer used for slide, capsule, channel and screen rendering app.ictv_renderer = ICTVRenderer(app) # Init the plugin manager, used as a gateway between ICTV core and its plugins. app.plugin_manager = PluginManager(app) # Init the download manager, a download queue which asynchronously downloads assets from the network app.download_manager = DownloadManager() # Init the cleanup manager which will regularly cleanup unused cached assets app.cleanup_scheduler = CleanupScheduler() app.cleanup_scheduler.start() # Init the video transcoding queue which will convert videos to WebM format using FFmpeg app.transcoding_queue = TranscodingQueue() # Add an general authentication processor to handle user authentication app.register_before_request(get_authentication_processor, cascade=True, needs_app=True) # Add a preprocessor to populate flask.g and mimic the old web.ctx app.register_before_request(get_web_ctx_processor, cascade=True) # Add a preprocessor to encapsulate every SQL requests in a transaction on a per HTTP request basis app.register_before_request(get_db_thread_preprocessor, cascade=True) app.prepare_error_handler(DatabaseError, lambda: database_error_handler) app.prepare_error_handler(werkzeug.exceptions.InternalServerError, lambda: internal_error_handler) # Add a hook to clean feedbacks from the previous request and prepare next feedbacks to be shown to the user app.register_after_request(lambda: rotate_feedbacks, cascade=True, needs_app=False) # Instantiate plugins through the plugin manager app.plugin_manager.instantiate_plugins(app) # Load themes and templates into database sqlhub.doInTransaction(load_templates_and_themes) return app
def get_config(config_path): with open(config_path) as f: config = yaml.unsafe_load(f) with open( os.path.join(get_root_path(), 'configuration.metadata' + os.extsep + 'yaml')) as f: metadata = yaml.unsafe_load(f) with open( os.path.join(get_root_path(), 'configuration.default' + os.extsep + 'yaml')) as f: defaults = yaml.unsafe_load(f) def validate_config(config_dict, metadata, defaults, prefix=''): """ Ensures config parameters have valid values. Modifies the given configuration with default values when none can be found in the given config. Returns a list of parameters that are invalid. """ sentinel = [] for key, key_metadata in metadata.items(): key_name = prefix + key if key in config_dict: value = config_dict[key] elif key in defaults: value = defaults[key] else: value = sentinel yield key_name, KeyError, None if value is not sentinel: config_dict[key] = value key_type = key_metadata['type'] if key_type in vars(builtins): if type(value) != vars( builtins)[key_type] and value is not None: yield key_name, TypeError, (key_type, type(value).__name__) elif key_type == 'int': if ('min' in key_metadata and value < key_metadata['min']) \ or ('max' in key_metadata and value > key_metadata['max']): yield key_name, ValueError, None elif key_type == 'dict': yield from validate_config(value, key_metadata['items'], defaults.get(key), prefix=key_name + '.') elif key_type.startswith('list['): if type(value) is not list: yield key_name, TypeError, (key_type, type(value).__name__) else: inner_type = vars(builtins)[key_type[5:-1]] for i, v in enumerate(value): if type(v) is not inner_type and value is not None: yield '%s[%d]' % (key_name, i), TypeError, ( key_type[5:-1], type(v).__name__) def check_for_unused_keys(config_dict, metadata, prefix=''): """ Checks if the given configuration contains parameters that are not used by ICTV and alerts the user. """ for key, value in config_dict.items(): key_name = prefix + key key_metadata = metadata.get(key) if not key_metadata: print( 'Config file specifies parameter %s but this parameter has no influence on ICTV' % key_name, file=sys.stderr) if key_metadata['type'] == 'dict': check_for_unused_keys(value, key_metadata['items'], prefix=key_name + '.') check_for_unused_keys(config, metadata) config_errors = list(validate_config(config, metadata, defaults)) if config_errors: for key, error, info in config_errors: if error is KeyError: print( 'Parameter %s was not specified in config file and no default value could be found' % key, file=sys.stderr) elif error is TypeError: print( 'Parameter %s has an inappropriate type, expected %s but found %s' % (key, *info), file=sys.stderr) elif error is ValueError: print( 'Parameter %s has an inappropriate value, please check the additional constraints on this parameter' % key, file=sys.stderr) raise Exception('Config file is incorrect') def load_default_slides(config_dict): """ Loads the default slides configuration file inside the main configuration dict. Returns the dict. """ default_slides_path = config_dict.get( 'default_slides') or os.path.join( get_root_path(), 'default_slides.default' + os.extsep + 'yaml') default_slides_path = os.path.join(os.path.dirname(config_path), default_slides_path) if not os.path.exists(default_slides_path): raise Exception( 'Default slides config file could not be found in %s' % default_slides_path) with open(default_slides_path) as default_slides_file: config_dict['default_slides'] = yaml.unsafe_load( default_slides_file) return config_dict return load_default_slides(config)
def add_file(file): client_zip.write(os.path.join(get_root_path(), 'client', file), arcname=file)
def __init__(self, *args, **kwargs): self._themes = {} themes = set() self._child_themes = {} themes_directory = os.path.join(get_root_path(), 'renderer', 'themes') for theme in [ p for p in os.listdir(themes_directory) if os.path.isdir(os.path.join(themes_directory, p)) ]: try: with open( os.path.join(themes_directory, theme, 'config' + os.extsep + 'yaml')) as config_file: config = yaml.unsafe_load(config_file) self._themes[theme] = config if 'base_color' in self._themes[theme]: theme_base_color = self._themes[theme]['base_color'] theme_base_color = (theme_base_color['h'], theme_base_color['s'], theme_base_color['v']) self._themes[theme]['palette'] = [ colorsys.hsv_to_rgb( (theme_base_color[0] + (i / 360)) % 1, theme_base_color[1], theme_base_color[2]) for i in range(30, 360, 30) ] self._themes[theme]['ckeditor_palette'] = ','.join([ ''.join('%02X' % round(i * 255) for i in color) for color in self._themes[theme]['palette'] ]) parent = self._themes[theme].get('parent') if parent: self._child_themes[parent] = self._child_themes.get( parent, set()) | {theme} themes.add(theme) except FileNotFoundError: print( 'Theme %s does not have a config.yaml. It will be ignored' % theme, file=sys.stderr) def remove_theme(theme): if theme in self._child_themes: for child in self._child_themes[theme]: print( 'Theme %s referenced %s as its parent, but %s could not be found. It will be ignored.' % (child, theme, theme), file=sys.stderr) remove_theme(child) themes.discard(theme) self._themes.pop(theme) for parent in self._child_themes.keys(): if parent not in themes: remove_theme(parent) themes_static_dir = os.path.join(get_root_path(), 'static', 'themes') if not os.path.exists(themes_static_dir): os.mkdir(themes_static_dir) for theme, config in self._themes.items(): link_name = os.path.join(themes_static_dir, theme) assets_path = os.path.join(get_root_path(), 'renderer', 'themes', theme, 'assets') if os.path.exists(assets_path) and not os.path.exists(link_name): os.symlink(assets_path, link_name, target_is_directory=True) def set_theme_level(theme, level=0): if self._themes[theme].get('level', -1) < level: self._themes[theme]['level'] = level for child in self._child_themes.get(theme, []): set_theme_level(child, level + 1) for theme in [ t for t in self._themes.keys() if self._themes[t].get('parent') is None ]: set_theme_level(theme) super().__init__(self)
def read_raw_template(template): with open( os.path.join(get_root_path(), 'renderer/templates/' + template + ".html")) as f: content = f.read() return content
def write_to_asset_file(self, content): """ Writes the content to the asset file. """ asset_path = os.path.join(get_root_path(), self.path) os.makedirs(os.path.dirname(asset_path), exist_ok=True) with open(asset_path, 'wb') as f: f.write(content)