Ejemplo n.º 1
0
def _update_pytsite(v_from: _semver.Version):
    if v_from <= '7.9':
        from os import path
        from shutil import rmtree

        # New dict key added to all file storage items, so entire file cache must be cleared
        rmtree(_reg.get('cache.file_driver_storage', path.join(_reg.get('paths.storage'), 'cache')))
Ejemplo n.º 2
0
    def _dump(self):
        if _subprocess.call('which mongodump', stdout=_subprocess.DEVNULL, stderr=_subprocess.DEVNULL, shell=True) != 0:
            raise RuntimeError('Cannot find mongodump executable.')

        _maintenance.enable()

        db_name = _reg.get('db.database')
        target_dir = _path.join(_reg.get('paths.root'), 'misc', 'dbdump')
        target_subdir = _path.join(target_dir, db_name)

        if _path.exists(target_subdir):
            ctime = _datetime.fromtimestamp(_path.getctime(target_subdir))
            target_subdir_move = '{}-{}'.format(target_subdir, ctime.strftime('%Y%m%d-%H%M%S'))
            _shutil.move(target_subdir, target_subdir_move)

        from . import _api
        config = _api.get_config()

        command = 'mongodump -h {}:{} --gzip -o {} -d {}'.format(config['host'], config['port'], target_dir, db_name)

        if config['user']:
            command += ' -u {} -p {}'.format(config['user'], config['password'])
        if config['ssl']:
            command += ' --ssl --sslAllowInvalidCertificates'

        r = _subprocess.call(command, shell=True)

        _maintenance.disable()

        return r
Ejemplo n.º 3
0
    def _restore(self):
        if _subprocess.call('which mongorestore', stdout=_subprocess.DEVNULL, stderr=_subprocess.DEVNULL, shell=True):
            raise RuntimeError('Cannot find mongorestore executable.')

        _maintenance.enable()

        db_name = _reg.get('db.database')
        source_dir = _path.join(_reg.get('paths.root'), 'misc', 'dbdump', db_name)

        from . import _api
        config = _api.get_config()

        command = 'mongorestore -h {}:{} --drop --gzip --stopOnError --dir {} -d {}'. \
            format(config['host'], config['port'], source_dir, db_name)

        if config['user']:
            command += ' -u {} -p {}'.format(config['user'], config['password'])
        if config['ssl']:
            command += ' --ssl --sslAllowInvalidCertificates'

        r = _subprocess.call(command, shell=True)

        _events.fire('pytsite.mongodb@restore')

        _maintenance.disable()

        return r
Ejemplo n.º 4
0
def router_dispatch():
    """'pytsite.router.dispatch' event handler.
    """
    if _auth.get_current_user().has_role('dev'):
        if not _reg.get('tumblr.app_key') or not _reg.get('tumblr.app_secret'):
            _router.session().add_warning_message(
                _lang.t('tumblr@plugin_setup_required_warning'))
Ejemplo n.º 5
0
    def exec(self) -> str:
        theme_v = reg.get('theme.version', '1')
        assetman.preload('v1/css/content-entity-modify.css'.format(theme_v))

        return tpl.render(
            'v1/content-entity-modify'.format(reg.get('theme.version', '1')),
            self.args)
Ejemplo n.º 6
0
    def exec(self):
        verbosity = 2 if _reg.get('debug') else 1

        if not self.args:
            raise _console.error.MissingArgument('pytsite.testing@target_is_not_specified')

        for target in self.args:
            argv = [_sys.argv[0]]

            if self.opt('no-discover') or '.tests.' in target:
                argv.append(target)
                _unittest.TestProgram(None, argv=argv, failfast=True, verbosity=verbosity)

            else:
                argv += ['discover', '-s']

                if target.startswith('pytsite.'):
                    target = _path.join(_reg.get('paths.pytsite'), target.replace('pytsite.', ''), 'tests')
                else:
                    target = _path.join(_reg.get('paths.root'), target.replace('.', _path.sep), 'tests')

                if not _path.exists(target):
                    raise _console.error.CommandExecutionError('Directory {} is not found'.format(target))

                argv.append(target)
                _unittest.TestProgram(None, argv=argv, failfast=True, verbosity=verbosity)
Ejemplo n.º 7
0
    def get_comments_count(self, thread_uid: str) -> int:
        """Get comments count for particular thread.
        """
        short_name = reg.get('disqus.short_name')
        secret_key = reg.get('disqus.secret_key')

        if not short_name or not secret_key:
            return 0

        count = 0

        try:
            r = requests.get(
                'https://disqus.com/api/3.0/forums/listThreads.json', {
                    'api_secret': secret_key,
                    'forum': short_name,
                    'thread': 'ident:' + thread_uid,
                    'limit': 1,
                }).json()

            if r['code'] == 0 and r['response']:
                count = r['response'][0]['posts']

        except Exception as e:
            logger.error(e)

        return count
Ejemplo n.º 8
0
def cleanup():
    success, failed = _util.cleanup_files(_reg.get('paths.log'), _reg.get('logger.file_ttl', 2592000))  # 30d

    for f_path in success:
        _logger.debug('Obsolete log file removed: {}'.format(f_path))

    for f_path, e in failed:
        _logger.error('Error while removing obsolete log file {}: {}'.format(f_path, e))
Ejemplo n.º 9
0
def on_cleanup():
    success, failed = _util.cleanup_files(_reg.get('paths.session'), _reg.get('router.session_ttl', 86400))

    for f_path in success:
        _logger.debug('Obsolete session file removed: {}'.format(f_path))

    for f_path, e in failed:
        _logger.error('Error while removing obsolete session file {}: {}'.format(f_path, e))
Ejemplo n.º 10
0
def get_app_secret() -> str:
    """Get application's secret key.
    """
    app_secret = _reg.get('facebook.app_secret') or _reg.get('facebook.app_secret')

    if not app_secret:
        raise _error.AppSecretNotSet("Configuration parameter 'facebook.app_secret' is not set")

    return app_secret
Ejemplo n.º 11
0
def get_app_id() -> str:
    """Get application's ID.
    """
    app_id = _reg.get('facebook.app_id') or _reg.get('facebook.app_id')

    if not app_id:
        raise _error.AppIdNotSet("Configuration parameter 'facebook.app_id' is not set")

    return app_id
Ejemplo n.º 12
0
def _generate_feeds():
    # For each language we have separate feed
    for lng in lang.langs():
        # Generate RSS feed for each model
        for model in reg.get('content.rss_models', ()):
            filename = 'rss-{}'.format(model)
            _api.generate_rss(model,
                              filename,
                              lng,
                              length=reg.get('content.feed_length', 20))
Ejemplo n.º 13
0
def get_app_key() -> str:
    """Get application's key.
    """
    app_key = _reg.get('tumblr.app_key') or _reg.get('tumblr.app_key')

    if not app_key:
        raise _error.AppKeyNotSet(
            "Configuration parameter 'tumblr.app_key' is not set")

    return app_key
Ejemplo n.º 14
0
def get_app_id() -> str:
    """Get application's key.
    """
    app_id = _reg.get('vkontakte.app_id') or _reg.get('vkontakte.app_id')

    if not app_id:
        raise _error.AppIdNotSet(
            "Configuration parameter 'vkontakte.app_id' is not set")

    return app_id
Ejemplo n.º 15
0
def pytsite_cleanup():
    root = _path.join(_reg.get('paths.static'), 'image', 'resize')
    ttl = _reg.get('file_storage_odm.static_ttl', 2592000)  # 1 month

    success, failed = _util.cleanup_files(root, ttl)

    for f_path in success:
        _logger.debug('Obsolete static file removed: {}'.format(f_path))

    for f_path, e in failed:
        _logger.error('Error while removing obsolete static file {}: {}'.format(f_path, e))
Ejemplo n.º 16
0
    def __init__(self):
        """Init
        """
        from pytsite import router as _router

        self._server_name = _router.server_name()
        self._path = _reg.get('cache.file_driver_storage', _path.join(_reg.get('paths.storage'), 'cache'))

        # Create cache directory
        if not _path.exists(self._path):
            _makedirs(self._path, 0o755, True)
Ejemplo n.º 17
0
    def __init__(self, access_token: str, app_id: str = None, app_secret: str = None):
        """Init.
        """
        self._access_token = access_token

        self._app_id = app_id or _reg.get('vkontakte.app_id')
        if not self._app_id:
            raise RuntimeError('Application ID is not defined.')

        self._app_secret = app_secret or _reg.get('vkontakte.app_secret')
        if not self._app_id:
            raise RuntimeError('Application secret key is not defined.')
Ejemplo n.º 18
0
def plugin_load():
    from pytsite import reg, util
    from plugins import auth, odm
    from . import _driver

    # ODM models
    role_cls = reg.get('auth_storage_odm.role_odm_class', 'plugins.auth_storage_odm.ODMRole')
    user_cls = reg.get('auth_storage_odm.user_odm_class', 'plugins.auth_storage_odm.ODMUser')
    odm.register_model('role', util.get_module_attr(role_cls))
    odm.register_model('user', util.get_module_attr(user_cls))
    odm.register_model('follower', ODMFollower)
    odm.register_model('blocked_user', ODMBlockedUser)

    # Register storage driver
    auth.register_storage_driver(_driver.Storage())
Ejemplo n.º 19
0
    def content_on_status_change(self):
        """Hook
        """
        # Update publish time if entity is being published
        now = datetime.now()
        if self.prev_status == CONTENT_STATUS_UNPUBLISHED and \
                self.status in (CONTENT_STATUS_WAITING, CONTENT_STATUS_PUBLISHED) and \
                self.publish_time < now:
            self.f_set('publish_time', now).save(fast=True)

        if reg.get('content.waiting_status_admin_notification', True):
            self._content_notify_admins_waiting_status()

        if reg.get('content.status_change_author_notification', True):
            self._content_notify_author_status_change()
Ejemplo n.º 20
0
def _init():
    from os import mkdir
    from pytsite import reg, lang, tpl, console, update, on_pytsite_load
    from . import _cc, _eh

    # Resources
    lang.register_package(__name__)
    tpl.register_global('plugins', _PluginsTplGlobal())

    # Create 'plugins' package if it doesn't exist
    plugins_dir = plugins_dir_path()
    if not _path.exists(plugins_dir):
        mkdir(plugins_dir, 0o755)
        with open(_path.join(plugins_dir, '__init__.py'), 'wt') as f:
            f.write('"""Pytsite Plugins\n"""\n')

    # Register console commands
    console.register_command(_cc.Install())
    console.register_command(_cc.Uninstall())

    # Enable imports checking
    _meta_path.insert(0, _MetaPathHook())

    # Load installed plugins
    if reg.get('plugman.autoload', True):
        maint_was_enabled = _maintenance.is_enabled()
        disabled_plugins = reg.get('plugman.disabled_plugins', [])

        try:
            _maintenance.enable(True)

            for p_name in local_plugins_info():
                if p_name.startswith('_') or p_name in disabled_plugins:
                    continue

                try:
                    if not is_loaded(p_name):
                        load(p_name)
                except (error.PluginLoadError, error.PluginNotInstalled) as e:
                    console.print_warning(e)

        finally:
            if not maint_was_enabled:
                _maintenance.disable(True)

    # Event handlers
    on_pytsite_load(_eh.on_pytsite_load)
    update.on_update_stage_2(_eh.on_pytsite_update_stage_2)
Ejemplo n.º 21
0
    def exec(self) -> str:
        theme_v = reg.get('theme.version', '1')
        e = self.arg('entity')
        exclude_ids = [e.id]

        self.args.update({
            'entity_tags':
            tag.widget.EntityTagCloud('entity-tag-cloud',
                                      entity=e,
                                      term_css=''),
            'related_1':
            _get_articles(exclude_ids, 3, e.section, 'views_count')
            if e.model == 'article' else [],
            'related_2':
            _get_articles(exclude_ids, 2, e.section, 'views_count')
            if e.model == 'article' else [],
            'related_3':
            _get_articles(exclude_ids, 2, e.section)
            if e.model == 'article' else [],
        })

        if e.images:
            self.args.update({
                'page_header_article':
                e,
                'page_header_article_tags':
                tag.widget.EntityTagCloud('header-article-tags',
                                          entity=e,
                                          term_css=''),
                'fullscreen_page_header':
                True,
            })

        if plugman.is_installed('addthis'):
            from plugins import addthis
            self.args.update({
                'share_widget':
                addthis.widget.AddThis('add-this-share')
                if reg.get('addthis.pub_id') else '',
            })

        if plugman.is_installed('disqus'):
            self.args.update(
                {'comments_widget': comments.get_widget(driver_name='disqus')})

        assetman.preload('v{}/css/content-entity-view.css'.format(theme_v))

        return tpl.render('v{}/content-entity-view'.format(theme_v), self.args)
Ejemplo n.º 22
0
def _init():
    from os import path, makedirs
    from pytsite import tpl, lang, reg, cleanup
    from . import _eh

    # Resources
    tpl.register_package(__name__)
    lang.register_package(__name__)

    # Create directory to store session data
    session_storage_path = reg.get('paths.session')
    if not path.exists(session_storage_path):
        makedirs(session_storage_path, 0o755, True)

    # Lang globals
    lang.register_global('base_url', lambda language, args: base_url(language))

    # Tpl globals
    tpl.register_global('url', url)
    tpl.register_global('is_rule_defined', has_rule)
    tpl.register_global('rule_url', rule_url)
    tpl.register_global('current_url', current_url)
    tpl.register_global('current_path', current_path)
    tpl.register_global('base_url', base_url)
    tpl.register_global('is_base_url', is_base_url)
    tpl.register_global('is_main_host', is_main_host)
    tpl.register_global('session_messages', lambda x: session().get_messages(x) if session() else ())

    # Clear flash messages from all sessions
    s_store = get_session_store()
    for sid in s_store.list():
        s_store.save_if_modified(s_store.get(sid).clear_messages())

    # Events handlers
    cleanup.on_cleanup(_eh.on_cleanup)
Ejemplo n.º 23
0
    def exec(self) -> str:
        theme_v = reg.get('theme.version', '1')
        self.args.update(content.paginate(self.arg('finder')))
        assetman.preload('v{}/css/content-entity-index.css'.format(theme_v))

        return tpl.render('v{}/content-entity-index'.format(theme_v),
                          self.args)
Ejemplo n.º 24
0
def register_package(package_name: str, assets_dir: str = 'res/assets'):
    """Register PytSite package which contains assets
    """
    pkg_spec = find_spec(package_name)
    if not pkg_spec:
        raise RuntimeError("Package '{}' is not found".format(package_name))

    # Check whether assetman's package is already registered
    if package_name in _packages:
        raise _error.PackageAlreadyRegistered(package_name)

    # Absolute path to package's assets source directory
    src_path = path.abspath(
        path.join(path.dirname(pkg_spec.origin), assets_dir))
    if not path.isdir(src_path):
        FileNotFoundError("Directory '{}' is not found".format(src_path))

    # Absolute path to package's assets destination directory
    assets_path = reg.get('paths.assets')
    if not assets_path:
        raise RuntimeError(
            "It seems you call register_package('{}') too early".format(
                package_name))
    dst_path = path.join(assets_path, package_name)

    _packages[package_name] = (src_path, dst_path)
Ejemplo n.º 25
0
    def __init__(self, uid: str, **kwargs):
        """Init
        """
        super().__init__(uid, **kwargs)

        self._form_group = False
        self._has_messages = False

        self._pub_id = reg.get('addthis.pub_id')
        if not self._pub_id:
            raise RuntimeError("Setting 'addthis.pub_id' is not defined")

        self._box_type = kwargs.get('box_type', _valid_box_types[0])
        if self._box_type not in _valid_box_types:
            raise RuntimeError(
                "Invalid type: '{}'. Valid types are: {}.".format(
                    self._box_type, str(_valid_box_types)))

        if not self._box_type.startswith('addthis_'):
            self._box_type = 'addthis_' + self._box_type

        # To support previous version of AddThis container naming convention
        if self._box_type == 'addthis_inline_share_toolbox':
            self._box_type += ' addthis_sharing_toolbox'

        self._url = kwargs.get('url')
        self._css += ' widget-addthis'
Ejemplo n.º 26
0
def register_model(model: str,
                   cls,
                   menu_title: str = None,
                   menu_weight: int = 0,
                   menu_icon: str = 'fa fas fa-tags',
                   menu_sid: str = 'taxonomy',
                   menu_roles: Union[str, list, tuple] = ('admin', 'dev'),
                   menu_permissions: Union[str, list, tuple] = None):
    """Register a taxonomy model
    """
    if model in _models:
        raise RuntimeError(
            "Taxonomy model '{}' is already registered".format(model))

    if isinstance(cls, str):
        cls = util.get_module_attr(cls)

    if not issubclass(cls, Term):
        raise TypeError('Subclass of {} expected'.format(Term))

    odm.register_model(model, cls)
    _models.append(model)

    if reg.get('env.type') == 'wsgi' and menu_title:
        menu_url = router.rule_path('odm_ui@admin_browse', {'model': model})
        admin.sidebar.add_menu(
            menu_sid,
            model,
            menu_title,
            menu_url,
            menu_icon,
            weight=menu_weight,
            roles=menu_roles,
            permissions=menu_permissions,
        )
Ejemplo n.º 27
0
def _build_store_path(name: str, mime: str, propose: str = None) -> str:
    """Build unique path to store file on the filesystem.
    """
    storage_dir = _os.path.join(_reg.get('paths.storage'), 'file', mime.split('/')[0])
    ext = _os.path.splitext(name)[1]
    rnd_str = _util.random_str

    # Possible (but not final) path
    possible_target_path = _os.path.join(storage_dir, rnd_str(2), rnd_str(2), rnd_str()) + ext

    # Check if the proposed path suits the requirements
    if propose:
        m = _re.match('(\w{2})/(\w{2})/(\w{16})(\.\w+)$', propose)
        if m:
            ext = m.group(4)
            possible_target_path = _os.path.join(storage_dir, m.group(1), m.group(2), m.group(3)) + ext

    # Search for path which doesn't exist on the filesystem
    while True:
        if not _os.path.exists(possible_target_path):
            store_path = possible_target_path
            break
        else:
            possible_target_path = _os.path.join(storage_dir, rnd_str(2), rnd_str(2), rnd_str()) + ext

    return store_path
Ejemplo n.º 28
0
def get_client_id() -> str:
    client_id = reg.get('auth_id_gov_ua.client_id')
    if not client_id:
        raise RuntimeError(
            'auth_id_gov_ua.client_id configuration parameter is not defined')

    return client_id
Ejemplo n.º 29
0
    def __init__(self):
        self._role_cls = util.get_module_attr(
            reg.get(_REG_ROLE_CLS, 'plugins.auth_storage_odm.Role'))
        if not issubclass(self._role_cls, _model.Role):
            raise TypeError(
                "Subclass of {} expected, got {}. Please check the '{}' configuration parameter"
                .format(auth.AbstractRole, type(self._role_cls),
                        _REG_ROLE_CLS))

        self._user_cls = util.get_module_attr(
            reg.get(_REG_USER_CLS, 'plugins.auth_storage_odm.User'))
        if not issubclass(self._user_cls, _model.User):
            raise TypeError(
                "Subclass of {} expected, got {}. Please check the '{}' configuration parameter"
                .format(auth.AbstractUser, type(self._user_cls),
                        _REG_USER_CLS))
Ejemplo n.º 30
0
def register_model(model: str, cls: Union[str, ContentModelClass], title: str, menu_weight: int = 0,
                   menu_icon: str = 'fa fa-file-text-o', menu_sid: str = 'content', replace: bool = False):
    """Register content model
    """
    # Resolve class
    if isinstance(cls, str):
        cls = util.get_module_attr(cls)  # type: ContentModelClass

    if not issubclass(cls, Content):
        raise TypeError('Subclass of {} expected, got {}'.format(Content, type(cls)))

    if not replace and is_model_registered(model):
        raise KeyError("Content model '{}' is already registered".format(model))

    # Register ODM model
    odm.register_model(model, cls, replace)

    # Saving info about registered _content_ model
    _models[model] = (cls, title)

    if reg.get('env.type') == 'wsgi':
        mock = dispense(model)
        perms = ['odm_auth@{}.{}'.format(p, model) for p in mock.odm_auth_permissions()],

        admin.sidebar.add_menu(
            sid=menu_sid,
            mid=model,
            title=title,
            path=router.rule_path('odm_ui@admin_browse', {'model': model}),
            icon=menu_icon,
            weight=menu_weight,
            permissions=perms,
            replace=replace,
        )
Ejemplo n.º 31
0
 def logo_url(width: int = 0, height: int = 0, enlarge: bool = False):
     s = reg.get('theme.logo')
     try:
         return file.get(s).get_url(width=width, height=height, enlarge=enlarge) if s else \
             assetman.url('$theme@img/appicon.png')
     except file.error.FileNotFound:
         return assetman.url('$theme@img/appicon.png')
Ejemplo n.º 32
0
def install_npm_deps(package_names: Union[str, List[str]]):
    """Install NPM packages required by locally installed plugins
    """
    is_dev_host = path.isdir(path.join(reg.get('paths.root'), 'npm_packages'))

    if isinstance(package_names, str):
        package_names = [package_names]

    # Build list of NPM packages required by plugins
    npm_pkgs_to_install = []
    for pkg_name in package_names:
        # Skip package if it does not provide package.json
        json_path = path.join(assets_src(pkg_name), 'package.json')
        if not path.exists(json_path):
            continue

        # Collect dependencies
        json = util.load_json(json_path)
        for name, ver in json.get('dependencies', {}).items():
            if name.startswith('@pytsite') and is_dev_host:
                continue
            npm_pkg_spec = '{}@{}'.format(name, ver)
            if npm_pkg_spec not in npm_pkgs_to_install:
                npm_pkgs_to_install.append(npm_pkg_spec)

    # Install required NPM packages
    npm_install(npm_pkgs_to_install)
Ejemplo n.º 33
0
def plugin_load():
    from pytsite import reg
    from plugins import menu
    from . import _model

    if reg.get('content_menu.register_default_model', True):
        menu.register_model('content_menu', _model.ContentMenu, __name__ + '@content')
Ejemplo n.º 34
0
def on_tpl_render(tpl_name: str, args: dict):
    args.update({
        'theme_v':
        reg.get('theme.version', '1'),
        'top_navbar_items_num':
        int(reg.get('theme.top_navbar_items_num', '5')),
        'language_nav':
        widget.select.LanguageNav('language-nav'),
    })

    if plugman.is_installed(['page', 'section']):
        from plugins import content, section

        args['content_sections'] = list(section.get())

        if content.is_model_registered('page'):
            args['content_pages'] = list(content.find('page').get())
Ejemplo n.º 35
0
def update_stage_2():
    # Install/update pip packages
    _console.print_info(_lang.t('pytsite.pip@updating_packages'))
    for pkg_name, pkg_ver in _package_info.requires_packages('app').items():
        try:
            _api.install(pkg_name, pkg_ver, True, _reg.get('debug'))
        except _error.PackageInstallError as e:
            raise _console.error.CommandExecutionError(e)
Ejemplo n.º 36
0
def _cleanup_tmp_files():
    success, failed = _util.cleanup_files(_reg.get('paths.tmp'), 86400)  # 24h

    for f_path in success:
        _logger.debug('Obsolete tmp file removed: {}'.format(f_path))

    for f_path, e in failed:
        _logger.error('Error while removing obsolete tmp file {}: {}'.format(f_path, e))
Ejemplo n.º 37
0
    def exec(self):
        del self.args['_pytsite_router_rule_name']
        endpoint = '/' + self.args.pop('http_api_endpoint')
        current_path = router.current_path(False)
        request_method = router.request().method

        # Switch language
        language = router.request().headers.get('Accept-Language')  # type: str
        if language:
            for lng in language.split(','):
                lng = lng.strip()
                if not lng.startswith('q=') and lang.is_defined(language):
                    lang.set_current(language)
                    break
        try:
            events.fire('http_api@pre_request')
            rule = _api.match(router.request().method, endpoint)
            events.fire('http_api@request')

            controller = rule.controller_class()  # type: routing.Controller
            controller.request = self.request
            controller.args.update(self.args)
            controller.args.update(rule.args)
            controller.args['_pytsite_http_api_rule_name'] = rule.name
            controller.args.validate()

            response = controller.exec()
            return response if isinstance(
                response, http.Response) else http.JSONResponse(response)

        except (http.error.Base, errors.ForbidOperation) as e:
            if reg.get('debug'):
                logger.error(e)
            else:
                logger.error('{} {}: {}'.format(request_method, current_path,
                                                e.description))

            if isinstance(e, errors.ForbidOperation):
                e = http.error.Forbidden(e)

            if e.response and isinstance(e.response, http.JSONResponse):
                response = e.response
                response.status_code = e.code
            else:
                # It is important to do `str(e.description)`, because `e.description` might be an exception
                response = http.JSONResponse({'error': str(e.description)},
                                             e.code)

            return response

        except UserWarning as e:
            logger.warn('{} {}: {}'.format(request_method, current_path, e))
            return http.JSONResponse({'warning': str(e)}, 200)

        except Exception as e:
            logger.error('{} {}: {}'.format(request_method, current_path, e),
                         exc_info=e)
            return http.JSONResponse({'error': str(e)}, 500)
Ejemplo n.º 38
0
    def __init__(self, uid: str, **kwargs):
        """Init.
        """
        super().__init__(uid, **kwargs)

        self._short_name = reg.get('disqus.short_name')
        self._thread_uid = kwargs.get('thread_uid')

        self._css += ' widget-disqus'
Ejemplo n.º 39
0
def plugin_load():
    from pytsite import reg
    from plugins import admin
    from . import _api

    admin.sidebar.add_section('menu', __name__ + '@menu')

    if reg.get('menu.register_default_model', True):
        _api.register_model('menu', _model.Menu, __name__ + '@menu')
Ejemplo n.º 40
0
def router_dispatch():
    """'pytsite.router.dispatch' handler.
    """
    t_id = _reg.get('google_analytics.tracking_id')
    if not t_id and _auth.get_current_user().has_role('dev'):
        _router.session().add_warning_message(
            _lang.t('google_analytics@plugin_setup_required_warning'))
    else:
        _assetman.inline_js(
            _tpl.render('google_analytics@counter', {'tracking_id': t_id}))
Ejemplo n.º 41
0
def plugin_load_wsgi():
    from pytsite import router, reg
    from plugins import settings
    from . import _settings_form

    settings.define('mail', _settings_form.Form, 'mail_settings@mail',
                    'fa fa-envelope', 'dev')

    if not reg.get('mail.from'):
        reg.put('mail.from', 'info@' + router.server_name())
Ejemplo n.º 42
0
def _do_reload():
    """Modify 'touch.reload' file
    """
    touch_reload_path = _path.join(_reg.get('paths.storage'), 'touch.reload')

    _events.fire('pytsite.reload@before_reload')

    with open(touch_reload_path, 'wt') as f:
        f.write(_util.w3c_datetime_str())

    _events.fire('pytsite.reload@reload')

    _console.print_info(_lang.t('pytsite.reload@app_is_reloading'))
Ejemplo n.º 43
0
def cron_every_min():
    out = ''
    for r in _events.fire('pytsite.stats@update'):
        if not r or not isinstance(r, str):
            continue

        out += '- ' + r + '\n'

    with open(_path.join(_reg.get('paths.storage'), 'stats.txt'), 'wt') as f:
        f.write(_util.w3c_datetime_str() + '\n\n' + out)

    if out:
        _logger.info('Current stats:\n{}'.format(out[:-1]))
Ejemplo n.º 44
0
def mk_tmp_dir(suffix: str = None, prefix: str = None, subdir: str = None) -> str:
    from pytsite import reg

    tmp_root = reg.get('paths.tmp')
    if not tmp_root:
        raise RuntimeError('Cannot determine temporary directory location')

    if subdir:
        tmp_root = _path.join(tmp_root, subdir)

    if not _path.exists(tmp_root):
        _makedirs(tmp_root, 0o755)

    return _mkdtemp(suffix, prefix, tmp_root)
Ejemplo n.º 45
0
def mk_tmp_file(suffix: str = None, prefix: str = None, subdir: str = None, text: bool = False) -> _Tuple[int, str]:
    """Create temporary file

    Returns tuple of two items: file's descriptor and absolute path.
    """
    from pytsite import reg

    tmp_dir = reg.get('paths.tmp')
    if not tmp_dir:
        raise RuntimeError('Cannot determine temporary directory location')

    if subdir:
        tmp_dir = _path.join(tmp_dir, subdir)

    if not _path.exists(tmp_dir):
        _makedirs(tmp_dir, 0o755)

    return _mkstemp(suffix, prefix, tmp_dir, text)
Ejemplo n.º 46
0
def get_config() -> dict:
    """
    :return:
    """
    return {
        'host': _reg.get('db.host', 'localhost'),
        'port': _reg.get('db.port', 27017),
        'ssl': _reg.get('db.ssl', False),
        'database': _reg.get('db.database', 'test'),
        'user': _reg.get('db.user'),
        'password': _reg.get('db.password'),
        'connect_timeout': 5000,
        'socket_timeout': 5000,
        'server_selection_timeout': 5000,
    }
Ejemplo n.º 47
0
"""PytSite Logger API Functions
"""
__author__ = 'Oleksandr Shepetko'
__email__ = '*****@*****.**'
__license__ = 'MIT'

import logging as _logging
from pytsite import reg as _reg

_logger = _logging.getLogger(_reg.get('env.name', 'default'))


def _log(level: int, msg, *args, **kwargs):
    if isinstance(msg, Exception):
        kwargs['exc_info'] = msg

    _logger.log(level, msg, *args, **kwargs)


def debug(msg, *args, **kwargs):
    _log(_logging.DEBUG, msg, *args, **kwargs)


def info(msg, *args, **kwargs):
    _log(_logging.INFO, msg, *args, **kwargs)


def warn(msg, *args, **kwargs):
    _log(_logging.WARNING, msg, *args, **kwargs)

Ejemplo n.º 48
0
def _init():
    from pytsite import reg, cron

    if reg.get('debug'):
        from . import _eh
        cron.every_min(_eh.cron_every_min)
Ejemplo n.º 49
0
"""PytSite Logger
"""
__author__ = 'Oleksandr Shepetko'
__email__ = '*****@*****.**'
__license__ = 'MIT'

import logging as _logging

from os import path as _path, makedirs as _makedirs
from datetime import datetime as _datetime
from pytsite import reg as _reg, cleanup as _cleanup

_log_dir = _reg.get('paths.log')
_log_path = _path.join(_log_dir, _datetime.now().strftime('{}-%Y%m%d.log'.format(_reg.get('env.type'))))
_log_level = _logging.DEBUG if _reg.get('debug') else _logging.INFO

if not _path.exists(_log_dir):
    _makedirs(_log_dir, 0o755, True)

# Create logger
_logger = _logging.getLogger(_reg.get('env.name', 'default'))
_logger.setLevel(_log_level)

# Setup handler
_handler = _logging.FileHandler(_log_path, encoding='utf-8')
if _log_level == _logging.DEBUG:
    fmt = '%(asctime)s %(levelname)7s %(process)d:%(thread)d %(message)s'
else:
    fmt = '%(asctime)s %(levelname)7s %(message)s'
_handler.setFormatter(_logging.Formatter(fmt))
_handler.setLevel(_log_level)
Ejemplo n.º 50
0
"""PytSite Cache
"""
__author__ = 'Oleksandr Shepetko'
__email__ = '*****@*****.**'
__license__ = 'MIT'

# Public API
from ._api import set_driver, has_pool, create_pool, get_pool, get_pools, cleanup
from . import _driver as driver, _error as error
from ._pool import Pool

from pytsite import reg as _reg, threading as _threading, update as _update, semver as _semver

if _reg.get('env.type') == 'wsgi':
    def _cleanup_worker():
        cleanup()
        _threading.run_in_thread(_cleanup_worker, 60)


    _threading.run_in_thread(_cleanup_worker, 60)


def _update_pytsite(v_from: _semver.Version):
    if v_from <= '7.9':
        from os import path
        from shutil import rmtree

        # New dict key added to all file storage items, so entire file cache must be cleared
        rmtree(_reg.get('cache.file_driver_storage', path.join(_reg.get('paths.storage'), 'cache')))

Ejemplo n.º 51
0
"""PytSite Cache API
"""
__author__ = 'Oleksandr Shepetko'
__email__ = '*****@*****.**'
__license__ = 'MIT'

from typing import Dict as _Dict
from pytsite import logger as _logger, reg as _reg
from . import _error
from ._driver import Abstract as _AbstractDriver
from ._pool import Pool as _Pool

_current_driver = None  # type: _AbstractDriver
_pools = {}  # type: _Dict[str, _Pool]
_dbg = _reg.get('cache.debug')


def set_driver(driver: _AbstractDriver):
    """Register a cache driver
    """
    global _current_driver

    if not isinstance(driver, _AbstractDriver):
        raise TypeError('Instance of {} expected, got {}'.format(_AbstractDriver, type(driver)))

    # When switching from one driver to another, it is important to move existing keys to the new storage
    keys_to_move = {}  # type: _Dict[str, list]
    if _current_driver:
        for pool in _pools.values():
            keys_to_move[pool.uid] = []
            for key in pool.keys():
Ejemplo n.º 52
0
 def __init__(self, access_token: str = None):
     self._access_token = access_token or _reg.get('github.access_token')
Ejemplo n.º 53
0
 def _data_path(self) -> str:
     return _path.join(_reg.get('paths.storage'), 'pytsite.update')
Ejemplo n.º 54
0
    def exec(self, args: tuple = (), **kwargs):
        """Execute the command.
        """
        app_path = _reg.get('paths.app')
        config_path = _reg.get('paths.config')
        stage = self.opt('stage')
        stop_after = self.opt('stop-after')

        _chdir(app_path)
        _maintenance.enable()

        d = self._get_update_data()
        if not d['update_in_progress']:
            d['pytsite_version_from'] = str(_package_info.version('pytsite'))
            d['app_version_from'] = str(_package_info.version('app'))
            d['update_in_progress'] = True
            self._set_update_data(d)

        # Stage 1: update pip and PytSite
        if stage == 1:
            _console.print_info(_lang.t('pytsite.update@updating_environment'))

            # Update pip
            _pip.install('pip', None, True, self.opt('debug'))

            # Update PytSite
            _pip.install('pytsite', _package_info.requires_pytsite('app'), True, self.opt('debug'))

            d['pytsite_version_to'] = str(_package_info.version('pytsite', False))
            self._set_update_data(d)

            # Notify listeners
            _events.fire('pytsite.update@stage_1')

            if stop_after != 1:
                _subprocess.call(['./console', 'update', '--stage=2'])
            else:
                _maintenance.disable()

        # Stage 2: update application and configuration
        elif stage == 2:
            # Notify listeners about PytSite update
            _events.fire('pytsite.update@pytsite', v_from=_semver.Version(d['pytsite_version_from']))

            # Update configuration
            if _path.exists(_path.join(config_path, '.git')):
                _console.print_info(_lang.t('pytsite.update@updating_configuration'))
                _subprocess_run(['git', '-C', config_path, 'pull'])

            # Update application
            if _path.exists(_path.join(app_path, '.git')):
                _console.print_info(_lang.t('pytsite.update@updating_application'))
                _subprocess_run(['git', '-C', app_path, 'pull'])
            d['app_version_to'] = str(_package_info.version('app', False))
            self._set_update_data(d)

            # Notify listeners
            _events.fire('pytsite.update@stage_2')

            if stop_after != 2:
                _subprocess.call(['./console', 'update', '--stage=3'])
            else:
                _maintenance.disable()

        # Stage 3: finish update process
        elif stage == 3:
            _console.print_info(_lang.t('pytsite.update@applying_updates'))

            # Notify listeners about application update
            _events.fire('pytsite.update@app', v_from=_semver.Version(d['app_version_from']))

            # Notify listeners
            _events.fire('pytsite.update@update')

            # Application's update hook
            import app
            if hasattr(app, 'app_update') and callable(app.app_update):
                app.app_update(v_from=_semver.Version(d['app_version_from']))

            # Mark that update was finished successfully
            d['update_in_progress'] = False
            self._set_update_data(d)

            # Disable maintenance mode
            _maintenance.disable()

            # Reload the application
            _reload.reload()
Ejemplo n.º 55
0
def on_pytsite_load():
    update_info = _api.get_update_info()

    if not update_info:
        return

    # If there waiting updates exist, reload the application
    if _reg.get('env.type') == 'wsgi':
        _logger.warn('Application needs to be loaded in console to finish plugins update')
        return

    failed_plugins = []

    # Call 'plugin_pre_install()' hooks
    for p_name, info in update_info.items():
        v_to = _semver.Version(info['version_to'])

        try:
            # Check if the plugin is installed and loaded
            plugin = _api.get(p_name)

            # Call plugin_pre_install() hook
            if hasattr(plugin, 'plugin_pre_install') and callable(plugin.plugin_pre_install):
                plugin.plugin_pre_install()

            # Fire 'pre_install' event
            _events.fire('pytsite.plugman@pre_install', name=p_name, version=v_to)

        except _error.PluginNotLoaded as e:
            _logger.error(e)
            _console.print_warning(_lang.t('pytsite.plugman@plugin_install_error', {
                'plugin': p_name,
                'version': v_to,
                'msg': str(e),
            }))
            failed_plugins.append(p_name)
            continue

    # Finish installing/updating plugins
    for p_name, info in update_info.items():
        if p_name in failed_plugins:
            continue

        plugin = _api.get(p_name)
        v_from = _semver.Version(info['version_from'])
        v_to = _semver.Version(info['version_to'])

        try:
            _logger.info(_lang.t('pytsite.plugman@installing_plugin', {
                'plugin': p_name,
                'version': v_to,
            }))

            # Call plugin_install() hook
            if hasattr(plugin, 'plugin_install') and callable(plugin.plugin_install):
                plugin.plugin_install()

            # Fire 'install' event
            _events.fire('pytsite.plugman@install', name=p_name, version=v_to)

            _console.print_success(_lang.t('pytsite.plugman@plugin_install_success', {
                'plugin': p_name,
                'version': v_to,
            }))

        except Exception as e:
            _logger.error(e)
            _console.print_warning(_lang.t('pytsite.plugman@plugin_install_error', {
                'plugin': p_name,
                'version': v_to,
                'msg': str(e),
            }))
            continue

        # Update plugin
        if v_from != '0.0.0':
            try:
                _console.print_info(_lang.t('pytsite.plugman@updating_plugin', {
                    'plugin': p_name,
                    'v_from': v_from,
                    'v_to': v_to,
                }))

                # Call plugin_update() hook
                if hasattr(plugin, 'plugin_update') and callable(plugin.plugin_update):
                    plugin.plugin_update(v_from=v_from)

                # Fire 'update' event
                _events.fire('pytsite.plugman@update', name=p_name, v_from=v_from)

                _console.print_success(_lang.t('pytsite.plugman@plugin_update_success', {
                    'plugin': p_name,
                    'version': v_to,
                }))

            except Exception as e:
                _console.print_warning(_lang.t('pytsite.plugman@plugin_update_error', {
                    'plugin': p_name,
                    'version': v_to,
                    'msg': str(e),
                }))
                continue

        # Remove info from update queue
        _api.rm_update_info(p_name)
Ejemplo n.º 56
0
"""PytSite pip Console Commands
"""
__author__ = 'Oleksandr Shepetko'
__email__ = '*****@*****.**'
__license__ = 'MIT'

import re as _re
from pytsite import reg as _reg, console as _console, pip as _pip, package_info as _package_info, lang as _lang

_PKG_SPEC_RE = _re.compile('^([a-zA-Z0-9\-_]+)([~=!<>]+[0-9]+(?:\.[0-9]+)*)?')
_DEBUG = _reg.get('debug')


class Install(_console.Command):
    def __init__(self):
        super().__init__()

        self.define_option(_console.option.Bool('upgrade'))
        self.define_option(_console.option.Bool('debug', default=_DEBUG))

    @property
    def name(self) -> str:
        """Get name of the command
        """
        return 'pip:install'

    @property
    def description(self) -> str:
        """Get description of the command.
        """
        return 'pytsite.pip@install_console_command_description'
Ejemplo n.º 57
0
def mail_from() -> tuple:
    """Get default mail sender's address and name.
    """
    return _reg.get('mail.from', 'info@' + _router.server_name())