예제 #1
0
    def exec(self):
        model = self.arg('model')

        for row in self.arg('rows'):
            e = odm.dispense(model, row['__id'])
            e.f_set_multiple({
                '_parent': odm.dispense(model, row['__parent']) if row['__parent'] else None,
                'order': row['order'],
            })
            e.save()

        return {'status': True}
예제 #2
0
    def add_to_field(self, field_name: str, value):
        if field_name == 'follows':
            if not self.is_follows(value):
                odm.dispense('follower').f_set('follower',
                                               self).f_set('follows',
                                                           value).save()
        elif field_name == 'blocked_users':
            if not self.is_blocks(value):
                odm.dispense('blocked_user').f_set('blocker', self).f_set(
                    'blocked', value).save()
        else:
            self._entity.f_add(field_name, value)

        return self
예제 #3
0
    def create_comment(
            self,
            thread_uid: str,
            body: str,
            author: auth.model.AbstractUser,
            status: str = 'published',
            parent_uid: str = None) -> comments.model.AbstractComment:
        """Create a new comment
        """
        body = body.strip()

        comment = odm.dispense('comment')  # type: _model.ODMComment
        comment.f_set('thread_uid', thread_uid)
        comment.f_set('body', body)
        comment.f_set('author', author.uid)
        comment.f_set('status', status)
        comment.save()

        if parent_uid:
            parent = odm.get_by_ref('comment:' + parent_uid)
            if parent.depth == comments.get_comment_max_depth():
                raise RuntimeError('Comment depth is too big')

            try:
                auth.switch_user_to_system()
                parent.append_child(comment).save()
            finally:
                auth.restore_user()

        return _model.Comment(comment)
예제 #4
0
    def _find_entities(args: dict) -> Iterable[_model.UIEntity]:
        models = args['model']
        sort_by = args['sort_by']
        sort_order = args['sort_order']
        f = odm.mfind(models)

        if sort_by:
            f.sort([(sort_by, sort_order)])

        exclude = args.get('exclude')
        if exclude:
            f.ninc('_ref', exclude)

        # Let model's class adjust finder
        for model in models:
            mock = odm.dispense(model)  # type: _model.UIEntity
            mock.odm_ui_widget_select_search_entities(f, args)

        # Collect entities
        entities = []
        for entity in f.get():  # type:  _model.UIEntity
            if entity.odm_ui_widget_select_search_entities_is_visible(args):
                entities.append(entity)
            if len(entities) - 1 == args['limit']:
                break

        # Do additional sorting, because MongoDB does not sort all languages properly
        if entities and sort_by and isinstance(entities[0].get_field(sort_by), odm.field.String):
            entities = sorted(entities, key=lambda e: _pyuca_col.sort_key(e.f_get(sort_by)),
                              reverse=sort_order == odm.I_DESC)

        return entities
예제 #5
0
def plugin_update(v_from: _Version):
    # Field 'uid' added to users and roles
    if v_from <= '2.3':
        from pytsite import console, mongodb
        from plugins import odm

        for c in ('users', 'roles'):
            col = mongodb.get_collection(c)
            for d in col.find():
                col.update_one({'_id': d['_id']}, {'$set': {'uid': str(d['_id'])}})
                console.print_info('Document updated: {}:{}'.format(c, d['_id']))

        odm.clear_cache('role')
        odm.clear_cache('user')
        odm.reindex('role')
        odm.reindex('user')

    if v_from <= '3.2':
        import re
        from pytsite import console, mongodb
        from plugins import odm

        for c in ('users', 'roles'):
            col = mongodb.get_collection(c)
            for d in col.find():
                col.update_one({'_id': d['_id']}, {'$set': {'uid': d['_ref']}})
                console.print_info('Document updated: {}:{}'.format(c, d['_id']))

        odm.clear_cache('role')
        odm.clear_cache('user')
        odm.reindex('role')
        odm.reindex('user')

        db_obj_id_re = re.compile('[a-z:]+([0-9a-f]{24})$')
        for m in odm.get_registered_models():
            mock = odm.dispense(m)
            for f_name, f in mock.fields.items():
                for d in mock.collection.find():
                    f_new_value = None

                    if isinstance(f, field.User) and d[f_name]:
                        f_new_value = '{}:{}'.format('user', db_obj_id_re.sub('\\1', d[f_name]))

                    if isinstance(f, (field.Users, field.Roles)) and d[f_name]:
                        auth_model = 'role' if isinstance(f, field.Roles) else 'user'
                        f_new_value = ['{}:{}'.format(auth_model, db_obj_id_re.sub('\\1', v)) for v in d[f_name]]

                    if f_new_value:
                        mock.collection.update_one({'_id': d['_id']}, {'$set': {f_name: f_new_value}})
                        console.print_info('Document updated: {}:{}'.format(m, d['_id']))

            odm.clear_cache(m)

    if v_from <= '3.4':
        from plugins import odm

        odm.reindex('role')
        odm.reindex('user')
예제 #6
0
def dispense_entity(model: str, entity_id: str = None) -> _model.UIEntity:
    """Dispense entity.
    """
    entity = odm.dispense(model, entity_id)

    if not isinstance(entity, _model.UIEntity):
        raise TypeError("Model '{}' must extend 'odm_ui.model.UIEntity'".format(model))

    return entity
예제 #7
0
def dispense(model: str, eid: str = None) -> Content:
    """Dispense a content entity
    """
    e = odm.dispense(model, eid)

    if not isinstance(e, Content):
        raise TypeError("Model '{}' is not registered as a content model".format(model))

    return e
예제 #8
0
    def exec(self):
        """Unsubscribe from content digest
        """
        s = _odm.dispense('content_digest_subscriber', self.arg('sid'))
        if s:
            s.f_set('enabled', False).save()
            _router.session().add_success_message(
                _lang.t('content_digest@unsubscription_successful'))

        return self.redirect(_router.base_url())
예제 #9
0
    def create_role(self,
                    name: str,
                    description: str = '') -> auth.AbstractRole:
        """Create a new role.
        """
        role_entity = odm.dispense('role')  # type: _model.ODMRole
        role_entity.f_set('name', name).f_set('description',
                                              description).save()

        return self._role_cls(role_entity)
예제 #10
0
    def create_user(self,
                    login: str,
                    password: str = None) -> auth.AbstractUser:
        user_entity = odm.dispense('user')  # type: _model.ODMUser
        user_entity.f_set_multiple({
            'login': login,
            'password': password,
        })

        return self._user_cls(user_entity)
예제 #11
0
def _dispense_entity(ref: str = None, model: str = None) -> _Entity:
    # Load entity
    try:
        entity = _odm.get_by_ref(ref) if ref else _odm.dispense(model)

        if not entity:
            raise _http.error.NotFound('Entity not found')

        if not isinstance(entity, _model.HTTPAPIEntityMixin):
            raise _http.error.Forbidden("Model '{}' does not support transfer via HTTP".format(entity.model))

        return entity

    except _odm.error.ModelNotRegistered as e:
        raise _http.error.NotFound(e)
예제 #12
0
    def create(self, file_path: str, mime: str, name: str = None, description: str = None, propose_path: str = None,
               **kwargs) -> _file.model.AbstractFile:

        # Generate unique file path in storage
        # Determine extension from MIME
        if not _os.path.splitext(name):
            name += _guess_extension(mime)

        abs_target_path = _build_store_path(name, mime, propose_path)

        # Make sure that directory on the filesystem exists
        target_dir = _os.path.dirname(abs_target_path)
        if not _os.path.exists(target_dir):
            _os.makedirs(target_dir, 0o755, True)

        # Copy file to the storage
        _shutil.copy(file_path, abs_target_path)

        # Create ODM entity
        if _IMG_MIME_RE.search(mime):
            odm_entity = _odm.dispense('file_image')  # type: _model.ImageFileODMEntity
        else:
            odm_entity = _odm.dispense('file')  # type: _model.AnyFileODMEntity

        storage_dir = _reg.get('paths.storage')
        odm_entity.f_set('path', abs_target_path.replace(storage_dir + '/', ''))
        odm_entity.f_set('name', name)
        odm_entity.f_set('description', description)
        odm_entity.f_set('mime', mime)
        odm_entity.f_set('length', _os.path.getsize(file_path))
        odm_entity.save()

        if isinstance(odm_entity, _model.ImageFileODMEntity):
            return _model.ImageFile(odm_entity)
        elif isinstance(odm_entity, _model.AnyFileODMEntity):
            return _model.AnyFile(odm_entity)
예제 #13
0
    def _get_entities(self) -> List[odm.Entity]:
        entities = []
        for e in self._get_finder():
            if str(e) not in self._exclude:
                entities.append(e)

        # Do additional sorting of string fields, because MongoDB does not sort properly all languages
        if self._sort_field and isinstance(
                odm.dispense(self._model).get_field(self._sort_field),
                odm.field.String):
            rev = True if self._sort_order == odm.I_DESC else False
            entities = sorted(entities,
                              key=lambda ent: _pyuca_col.sort_key(
                                  ent.f_get(self._sort_field)),
                              reverse=rev)

        return entities
예제 #14
0
    def on_register(cls, model: str):
        super().on_register(model)

        mock = odm.dispense(model)  # type: OwnedEntity

        def _on_user_pre_delete(user: auth.AbstractUser):
            if mock.has_field('author'):
                e = odm.find(model).eq('author', user).first()
                if e:
                    raise errors.ForbidDeletion(
                        lang.t('odm_auth@forbid_user_deletion', {
                            'user': user.login,
                            'entity': e,
                        }))

        # Check for registered lang package
        lang_pkg_name = cls.lang_package_name()
        if not lang.is_package_registered(lang_pkg_name):
            raise RuntimeError(
                f"In order to use '{model}' ODM model the '{lang_pkg_name}' lang package must be registered"
            )

        # Register permissions group
        perm_group = cls.odm_auth_permissions_group()

        # Register permissions
        if perm_group:
            for perm_name in mock.odm_auth_permissions():
                # Per-user permission can be registered only if entity has 'author' field
                if perm_name.endswith('_own') and not mock.has_field('author'):
                    raise ValueError(
                        f"Permission '{perm_name}' cannot be registered for model '{model}' "
                        f"because model does not define 'author' field")

                p_name = 'odm_auth@' + perm_name + '.' + model
                p_description = cls.resolve_lang_msg_id('odm_auth_' +
                                                        perm_name + '_' +
                                                        model)
                permissions.define_permission(p_name, p_description,
                                              perm_group)

        # Event handlers
        auth.on_user_pre_delete(_on_user_pre_delete)
예제 #15
0
def dispense(model: str,
             title: str,
             alias: str = None,
             language: str = None,
             parent: Term = None) -> Term:
    """Dispense a new term or raise exception if term with specified alias already exists
    """
    if alias and get(model, alias=alias, language=language):
        raise _error.TermExists(model, alias, language or lang.get_current())

    term = odm.dispense(model)  # type: Term
    term.title = title.strip()
    term.parent = parent
    if term.has_field('language'):
        term.language = language or lang.get_current()
    if term.has_field('alias') and alias:
        term.alias = alias or util.transform_str_2(title, language)

    return term
예제 #16
0
    def exec(self) -> dict:
        lng = _lang.get_current()
        email = _validation.rule.Email(value=self.arg('email')).validate()

        # Search for subscriber
        s = _odm.find('content_digest_subscriber').eq('email', email).eq(
            'language', lng).first()

        # Create new subscriber
        if not s:
            s = _odm.dispense('content_digest_subscriber').f_set(
                'email', email).f_set('language', lng).save()

        # Enable subscriber
        s.f_set('enabled', True).save()

        return {
            '__alert': _lang.t('content_digest@digest_subscription_success'),
            '__reset': True,
        }
예제 #17
0
    def _on_setup_form(self):
        """Hook.
        """
        super()._on_setup_form()

        model = self.attr('model')

        if not self.name:
            self.name = 'odm_ui_delete_' + model

        # Check permissions
        for eid in self.attr('eids', self.attr('ids', [])):
            e = odm.dispense(model, eid)  # type: odm_auth.OwnedEntity
            if not e.odm_auth_check_entity_permissions(PERM_DELETE):
                raise http.error.Forbidden()

        # Form title
        model_class = odm.get_model_class(model)  # type: _model.UIEntity
        self.title = model_class.t('odm_ui_form_title_delete_' + model)

        # Form CSS
        self.css += ' odm-ui-mass-d-form'
예제 #18
0
    def _put(self, key: str, value: Any):
        """Set setting's value
        """
        key_split = key.split('.')
        key_split_len = len(key_split)

        if key_split_len > 2:
            raise RuntimeError(
                'No more than one dot is currently supported by this registry driver: {}'
                .format(key))

        entity = odm.find('setting').eq('uid', key_split[0]).first()
        if not entity:
            entity = odm.dispense('setting').f_set('uid', key_split[0])

        if key_split_len == 2:
            stored_value = dict(entity.f_get('value'))
            stored_value[key_split[1]] = value
            entity.f_set('value', stored_value)
        else:
            entity.f_set('value', value)

        entity.save()
예제 #19
0
    def on_register(cls, model: str):
        """Hook
        """
        super().on_register(model)

        def on_content_generate(entity: content.model.Content):
            # Section
            if entity.has_field('section') and entity.has_field('language'):
                sections = list(section.get(entity.language))
                if not len(sections):
                    raise RuntimeError('No sections found')

                shuffle(sections)
                entity.f_set('section', sections[0])

            # Tags
            if entity.has_field('tags') and entity.has_field('language'):
                # Generate tags
                tags = list(tag.get(5, entity.language))
                if tags:
                    shuffle(tags)
                    entity.f_set('tags', tags)

            # Counters
            for cnt_name in ('views', 'comments', 'likes', 'bookmarks'):
                f_name = cnt_name + '_count'
                if entity.has_field(f_name):
                    entity.f_set(f_name, int(random() * 1000))

        # Define 'set_starred' permission
        if odm.dispense(model).has_field('starred'):
            perm_name = 'article@set_starred.' + model
            perm_description = cls.resolve_lang_msg_id('content_perm_set_starred_' + model)
            permissions.define_permission(perm_name, perm_description, cls.odm_auth_permissions_group())

        events.listen('content@generate', on_content_generate)