def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CLASSIFIER).validated(data, update=True) classifier = self.model if 'name' in data: from mini_fiction.models import Classifier exist_classifier = Classifier.get(name=data['name']) if exist_classifier and exist_classifier.id != classifier.id: raise ValidationError( {'name': [lazy_gettext('Classifier already exists')]}) changed_fields = set() for key, value in data.items(): if getattr(classifier, key) != value: setattr(classifier, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=classifier, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return classifier
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(TAG_CATEGORY).validated(data, update=True) tag_category = self.model if 'name' in data: from mini_fiction.models import TagCategory exist_tag_category = TagCategory.get(name=data['name']) if exist_tag_category and exist_tag_category.id != tag_category.id: raise ValidationError( {'name': [lazy_gettext('Tag category already exists')]}) changed_fields = set() for key, value in data.items(): if getattr(tag_category, key) != value: setattr(tag_category, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=tag_category, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return tag_category
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER).validated(data) errors = {} exist_character = self.model.get(name=data['name']) if exist_character: errors['name'] = [lazy_gettext('Character already exists')] from mini_fiction.models import CharacterGroup group = CharacterGroup.get(id=data['group']) if not group: errors['group'] = [lazy_gettext('Group not found')] if errors: raise ValidationError(errors) picture = self.validate_and_get_picture_data(data.pop('picture')) character = self.model(picture='pending', sha256sum='pending', **data) character.flush() character.bl.set_picture_data(picture) AdminLog.bl.create(user=author, obj=character, action=AdminLog.ADDITION) return character
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CATEGORY).validated(data, update=True) category = self.model if 'name' in data: from mini_fiction.models import Category exist_category = Category.get(name=data['name']) if exist_category and exist_category.id != category.id: raise ValidationError({'name': [lazy_gettext('Category already exists')]}) changed_fields = set() for key, value in data.items(): if getattr(category, key) != value: setattr(category, key, value) changed_fields |= {key,} if changed_fields: AdminLog.bl.create( user=author, obj=category, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return category
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(LOGOPIC_FOR_UPDATE).validated(data, update=True) logopic = self.model changed_fields = set() for key, value in data.items(): if key == 'picture': if value: self.set_picture_data(value) changed_fields |= {'picture',} else: if key == 'original_link_label': value = value.replace('\r', '') if getattr(logopic, key) != value: setattr(logopic, key, value) changed_fields |= {key,} if changed_fields: logopic.updated_at = datetime.utcnow() current_app.cache.delete('logopics') AdminLog.bl.create( user=author, obj=logopic, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return logopic
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER_GROUP).validated(data, update=True) group = self.model if 'name' in data: from mini_fiction.models import CharacterGroup exist_group = CharacterGroup.get(name=data['name']) if exist_group and exist_group.id != group.id: raise ValidationError({'name': [lazy_gettext('Group already exists')]}) changed_fields = set() for key, value in data.items(): if getattr(group, key) != value: setattr(group, key, value) changed_fields |= {key,} if changed_fields: AdminLog.bl.create( user=author, obj=group, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return group
def authenticate_by_username(self, data, remote_addr=None): raw_data = data data = Validator(LOGIN).validated(data) user = None # Сначала достаём пользователя из базы # (без чувствительности к регистру) user = self.get_by_username(data.get('username')) # Пресекаем перебор пароля капчей (по IP и по юзеру) if remote_addr: if current_app.captcha and self.need_captcha_for_auth(remote_addr, user.id if user else None): current_app.captcha.check_or_raise(raw_data) self.track_auth(remote_addr, user.id if user else None) # Проверяем пароль if not user or not user.bl.check_password(data['password']): if remote_addr and current_app.config['AUTH_LOG']: if not user: current_app.logger.info('%s tried to log in, but account not found (ID: N/A, IP: %s)', data.get('username', '?'), remote_addr) else: current_app.logger.info('%s tried to log in with incorrect password (ID: %s, IP: %s)', user.username, user.id, remote_addr) raise ValidationError({'username': [lazy_gettext('Please enter a correct username and password.')]}) # Проверяем бан if not user.is_active: if remote_addr and current_app.config['AUTH_LOG']: current_app.logger.info('%s tried to log in, but account is disabled (ID: %s, IP: %s)', user.username, user.id, remote_addr) raise ValidationError({'username': [user.ban_reason or lazy_gettext('Account is disabled')]}) # Если дошли сюда, значит всё хорошо if remote_addr and current_app.config['AUTH_LOG']: current_app.logger.info('%s logged in (ID: %s, IP: %s)', user.username, user.id, remote_addr) return user
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(STATIC_PAGE).validated(data, update=True) staticpage = self.model if not author.is_superuser and (staticpage.is_template or data.get('is_template')): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if ('name' in data and data['name'] != staticpage.name) or ('lang' in data and data['lang'] != staticpage.lang): raise ValidationError({ 'name': [lazy_gettext('Cannot change primary key')], 'lang': [lazy_gettext('Cannot change primary key')], }) if data.get('is_template', staticpage.is_template) and 'content' in data: self.check_renderability(author, data.get('name', staticpage.name), data['content']) changed_fields = set() for key, value in data.items(): if getattr(staticpage, key) != value: setattr(staticpage, key, value) changed_fields |= {key,} if changed_fields: AdminLog.bl.create( user=author, obj=staticpage, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return staticpage
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER_GROUP).validated(data, update=True) group = self.model if 'name' in data: from mini_fiction.models import CharacterGroup exist_group = CharacterGroup.get(name=data['name']) if exist_group and exist_group.id != group.id: raise ValidationError( {'name': [lazy_gettext('Group already exists')]}) changed_fields = set() for key, value in data.items(): if getattr(group, key) != value: setattr(group, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=group, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return group
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(STATIC_PAGE).validated(data) if not author.is_superuser and data.get('is_template'): raise ValidationError( {'is_template': [lazy_gettext('Access denied')]}) if data.get('is_template'): self.check_renderability(author, data['name'], data['content']) if not data.get('lang'): data['lang'] = 'none' exist_staticpage = self.model.get(name=data['name'], lang=data['lang']) if exist_staticpage: raise ValidationError( {'name': [lazy_gettext('Page already exists')]}) staticpage = self.model(**data) staticpage.flush() AdminLog.bl.create(user=author, obj=staticpage, action=AdminLog.ADDITION) return staticpage
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER_FOR_UPDATE).validated(data, update=True) character = self.model errors = {} if 'name' in data: from mini_fiction.models import Character exist_character = Character.get(name=data['name']) if exist_character and exist_character.id != character.id: errors['name'] = [lazy_gettext('Character already exists')] if 'group' in data: from mini_fiction.models import CharacterGroup group = CharacterGroup.get(id=data['group']) if not group: errors['group'] = [lazy_gettext('Group not found')] else: group = None if errors: raise ValidationError(errors) changed_fields = set() if data.get('picture'): picture = self.validate_and_get_picture_data(data['picture']) else: picture = None for key, value in data.items(): if key == 'picture': if picture is not None: self.set_picture_data(picture) changed_fields |= {'picture',} elif key == 'group': if character.group.id != value: setattr(character, key, value) changed_fields |= {key,} elif getattr(character, key) != value: setattr(character, key, value) changed_fields |= {key,} if changed_fields: AdminLog.bl.create( user=author, obj=character, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return character
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(LOGOPIC).validated(data) picture = data.pop('picture') logopic = self.model(picture='pending', sha256sum='pending', **data) logopic.flush() logopic.bl.set_picture_data(picture) current_app.cache.delete('logopics') AdminLog.bl.create(user=author, obj=logopic, action=AdminLog.ADDITION) return logopic
def authenticate_by_username(self, data, remote_addr=None): raw_data = data data = Validator(LOGIN).validated(data) user = None # Сначала достаём пользователя из базы # (без чувствительности к регистру) user = self.get_by_username(data.get('username')) # Пресекаем перебор пароля капчей (по IP и по юзеру) if remote_addr: if current_app.captcha and self.need_captcha_for_auth( remote_addr, user.id if user else None): current_app.captcha.check_or_raise(raw_data) self.track_auth(remote_addr, user.id if user else None) # Проверяем пароль if not user or not user.bl.check_password(data['password']): if remote_addr and current_app.config['AUTH_LOG']: if not user: current_app.logger.info( '%s tried to log in, but account not found (ID: N/A, IP: %s)', data.get('username', '?'), remote_addr) else: current_app.logger.info( '%s tried to log in with incorrect password (ID: %s, IP: %s)', user.username, user.id, remote_addr) raise ValidationError({ 'username': [ lazy_gettext( 'Please enter a correct username and password.') ] }) # Проверяем бан if not user.is_active: if remote_addr and current_app.config['AUTH_LOG']: current_app.logger.info( '%s tried to log in, but account is disabled (ID: %s, IP: %s)', user.username, user.id, remote_addr) raise ValidationError({ 'username': [user.ban_reason or lazy_gettext('Account is disabled')] }) # Если дошли сюда, значит всё хорошо if remote_addr and current_app.config['AUTH_LOG']: current_app.logger.info('%s logged in (ID: %s, IP: %s)', user.username, user.id, remote_addr) return user
def create(self, user, data): from mini_fiction.models import Tag, AdminLog if not user or not user.is_staff: raise ValueError('Not authorized') data = Validator(TAG).validated(data) errors = {} bad_reason = self.validate_tag_name(data['name']) if bad_reason: errors['name'] = [bad_reason] iname = normalize_tag(data['name']) if not bad_reason and Tag.get(iname=iname): errors['name'] = [lazy_gettext('Tag already exists')] canonical_tag = None if data.get('is_alias_for'): canonical_tag = Tag.get(iname=normalize_tag(data['is_alias_for'])) if not canonical_tag: errors['is_alias_for'] = [lazy_gettext('Tag not found')] if errors: raise ValidationError(errors) tag = Tag( name=data['name'], iname=iname, category=data.get('category'), color=data.get('color') or '', description=data.get('description') or '', is_main_tag=data.get('is_main_tag', False), created_by=user, is_alias_for=None, reason_to_blacklist='', ) tag.flush() AdminLog.bl.create(user=user, obj=tag, action=AdminLog.ADDITION) if data.get('reason_to_blacklist'): tag.bl.set_blacklist(user, data['reason_to_blacklist']) elif canonical_tag: tag.bl.make_alias_for(user, canonical_tag, hidden=data.get('is_hidden_alias', False)) return tag
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(NEWS_ITEM).validated(data) if not author.is_superuser and data.get('is_template'): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if data.get('is_template'): self.check_renderability(author, data['name'], data['content']) exist_newsitem = self.model.get(name=data['name']) if exist_newsitem: raise ValidationError({'name': [lazy_gettext('Page already exists')]}) if data.get('show'): self.hide_shown_newsitem() newsitem = self.model(author=author, **data) newsitem.flush() AdminLog.bl.create(user=author, obj=newsitem, action=AdminLog.ADDITION) return newsitem
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(STATIC_PAGE).validated(data) if not author.is_superuser and data.get('is_template'): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if data.get('is_template'): self.check_renderability(author, data['name'], data['content']) if not data.get('lang'): data['lang'] = 'none' exist_staticpage = self.model.get(name=data['name'], lang=data['lang']) if exist_staticpage: raise ValidationError({'name': [lazy_gettext('Page already exists')]}) staticpage = self.model(**data) staticpage.flush() AdminLog.bl.create(user=author, obj=staticpage, action=AdminLog.ADDITION) return staticpage
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(HTML_BLOCK).validated(data) if not author.is_superuser and data.get('is_template'): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if data.get('is_template'): self.check_renderability(author, data['name'], data['content']) if not data.get('lang'): data['lang'] = 'none' exist_htmlblock = self.model.get(name=data['name'], lang=data['lang']) if exist_htmlblock: raise ValidationError({'name': [lazy_gettext('Block already exists')]}) htmlblock = self.model(**data) htmlblock.flush() AdminLog.bl.create(user=author, obj=htmlblock, action=AdminLog.ADDITION) later(self.clear_cache, htmlblock.name) # avoid race condition by `later` (runs after commit) return htmlblock
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER_GROUP).validated(data) exist_group = self.model.get(name=data['name']) if exist_group: raise ValidationError( {'name': [lazy_gettext('Group already exists')]}) group = self.model(**data) group.flush() AdminLog.bl.create(user=author, obj=group, action=AdminLog.ADDITION) return group
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(LOGOPIC_FOR_UPDATE).validated(data, update=True) logopic = self.model changed_fields = set() for key, value in data.items(): if key == 'picture': if value: self.set_picture_data(value) changed_fields |= { 'picture', } else: if key == 'original_link_label': value = value.replace('\r', '') if getattr(logopic, key) != value: setattr(logopic, key, value) changed_fields |= { key, } if changed_fields: logopic.updated_at = datetime.utcnow() current_app.cache.delete('logopics') AdminLog.bl.create( user=author, obj=logopic, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return logopic
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(NEWS_ITEM).validated(data) if not author.is_superuser and data.get('is_template'): raise ValidationError( {'is_template': [lazy_gettext('Access denied')]}) if data.get('is_template'): self.check_renderability(author, data['name'], data['content']) exist_newsitem = self.model.get(name=data['name']) if exist_newsitem: raise ValidationError( {'name': [lazy_gettext('Page already exists')]}) if data.get('show'): self.hide_shown_newsitem() newsitem = self.model(author=author, **data) newsitem.flush() AdminLog.bl.create(user=author, obj=newsitem, action=AdminLog.ADDITION) return newsitem
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(CATEGORY).validated(data) exist_category = self.model.get(name=data['name']) if exist_category: raise ValidationError( {'name': [lazy_gettext('Category already exists')]}) category = self.model(**data) category.flush() AdminLog.bl.create(user=author, obj=category, action=AdminLog.ADDITION) return category
def create(self, author, data): from mini_fiction.models import AdminLog data = Validator(CLASSIFIER).validated(data) exist_classifier = self.model.get(name=data['name']) if exist_classifier: raise ValidationError( {'name': [lazy_gettext('Classifier already exists')]}) classifier = self.model(**data) classifier.flush() AdminLog.bl.create(user=author, obj=classifier, action=AdminLog.ADDITION) return classifier
def register(self, data): from mini_fiction.models import RegistrationProfile if current_app.captcha: current_app.captcha.check_or_raise(data) data = Validator(REGISTRATION).validated(data) errors = self._check_existence(data['username'], data['email']) if errors: raise ValidationError(errors) old_rp = RegistrationProfile.get(username=data['username'], email=data['email']) if old_rp: old_rp.delete() del old_rp rp = RegistrationProfile( activation_key=utils_random.random_string(40), email=data['email'], password=self.generate_password_hash(data['password']), username=data['username'], ) rp.flush() later( current_app.tasks['sendmail'].delay, data['email'], render_template('email/activation_subject.txt'), body={ 'plain': render_template('email/activation.txt', activation_key=rp.activation_key), 'html': render_template('email/activation.html', activation_key=rp.activation_key), }, headers={ 'X-Postmaster-Msgtype': current_app.config['EMAIL_MSGTYPES']['registration'] }, ) return rp
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(NEWS_ITEM).validated(data, update=True) newsitem = self.model if not author.is_superuser and (newsitem.is_template or data.get('is_template')): raise ValidationError( {'is_template': [lazy_gettext('Access denied')]}) if 'name' in data: from mini_fiction.models import NewsItem exist_newsitem = NewsItem.get(name=data['name']) if exist_newsitem and exist_newsitem.id != newsitem.id: raise ValidationError( {'name': [lazy_gettext('Page already exists')]}) if data.get('is_template', newsitem.is_template) and 'content' in data: self.check_renderability(author, data.get('name', newsitem.name), data['content']) if data.get('show') and not newsitem.show: self.hide_shown_newsitem() changed_fields = set() for key, value in data.items(): if getattr(newsitem, key) != value: setattr(newsitem, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=newsitem, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return newsitem
def update(self, author, ip, data): if not self.can_update: raise ValueError('Not available') if self.schema is None or not hasattr(self.model, 'edits'): raise NotImplementedError if not self.can_update_by(author): raise ValueError('Permission denied') # TODO: refactor exceptions if self.schema: data = Validator(self.schema).validated(data) comment = self.model old_text = comment.text new_text = data['text'] if old_text == new_text: return None comment.text = new_text comment.edits_count += 1 comment.last_edited_at = datetime.utcnow() comment.last_edited_by = author comment.flush() editlog = comment.edits.create( editor=author, date=comment.last_edited_at, old_text=old_text, new_text=new_text, ip=ipaddress.ip_address(ip).exploded, ) editlog.flush() current_app.cache.delete('index_comments_html') return editlog
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(STATIC_PAGE).validated(data, update=True) staticpage = self.model if not author.is_superuser and (staticpage.is_template or data.get('is_template')): raise ValidationError( {'is_template': [lazy_gettext('Access denied')]}) if ('name' in data and data['name'] != staticpage.name) or ( 'lang' in data and data['lang'] != staticpage.lang): raise ValidationError({ 'name': [lazy_gettext('Cannot change primary key')], 'lang': [lazy_gettext('Cannot change primary key')], }) if data.get('is_template', staticpage.is_template) and 'content' in data: self.check_renderability(author, data.get('name', staticpage.name), data['content']) changed_fields = set() for key, value in data.items(): if getattr(staticpage, key) != value: setattr(staticpage, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=staticpage, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return staticpage
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(HTML_BLOCK).validated(data, update=True) htmlblock = self.model if not author.is_superuser and (htmlblock.is_template or data.get('is_template')): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if ('name' in data and data['name'] != htmlblock.name) or ('lang' in data and data['lang'] != htmlblock.lang): raise ValidationError({ 'name': [lazy_gettext('Cannot change primary key')], 'lang': [lazy_gettext('Cannot change primary key')], }) if data.get('is_template', htmlblock.is_template) and 'content' in data: self.check_renderability(author, data.get('name', htmlblock.name), data['content']) changed_fields = set() old_name = htmlblock.name for key, value in data.items(): if getattr(htmlblock, key) != value: setattr(htmlblock, key, value) changed_fields |= {key,} later(self.clear_cache, old_name) if htmlblock.name != old_name: later(self.clear_cache, htmlblock.name) if changed_fields: AdminLog.bl.create( user=author, obj=htmlblock, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return htmlblock
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(NEWS_ITEM).validated(data, update=True) newsitem = self.model if not author.is_superuser and (newsitem.is_template or data.get('is_template')): raise ValidationError({'is_template': [lazy_gettext('Access denied')]}) if 'name' in data: from mini_fiction.models import NewsItem exist_newsitem = NewsItem.get(name=data['name']) if exist_newsitem and exist_newsitem.id != newsitem.id: raise ValidationError({'name': [lazy_gettext('Page already exists')]}) if data.get('is_template', newsitem.is_template) and 'content' in data: self.check_renderability(author, data.get('name', newsitem.name), data['content']) if data.get('show') and not newsitem.show: self.hide_shown_newsitem() changed_fields = set() for key, value in data.items(): if getattr(newsitem, key) != value: setattr(newsitem, key, value) changed_fields |= {key,} if changed_fields: AdminLog.bl.create( user=author, obj=newsitem, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return newsitem
def update(self, user, data): from mini_fiction.models import Tag, AdminLog if not user or not user.is_staff: raise ValueError('Not authorized') tag = self.model data = Validator(TAG).validated(data, update=True) changes = {} errors = {} if 'name' in data and data['name'] != tag.name: bad_reason = self.validate_tag_name(data['name']) if bad_reason: errors['name'] = [bad_reason] iname = normalize_tag(data['name']) if not bad_reason and iname != tag.iname and Tag.get(iname=iname): errors['name'] = [lazy_gettext('Tag already exists')] changes['name'] = data['name'] if iname != tag.iname: changes['iname'] = iname if 'category' in data: old_category_id = tag.category.id if tag.category else None if old_category_id != data['category']: changes['category'] = data['category'] for key in ('color', 'description', 'is_main_tag'): if key in data and data[key] != getattr(tag, key): changes[key] = data[key] canonical_tag = tag.is_alias_for if 'is_alias_for' in data: if data.get('is_alias_for'): canonical_tag = Tag.get(iname=normalize_tag(data['is_alias_for'])) if not canonical_tag: errors['is_alias_for'] = [lazy_gettext('Tag not found')] elif canonical_tag == tag or canonical_tag.is_alias_for and canonical_tag.is_alias_for == tag: errors['is_alias_for'] = [lazy_gettext('Tag cannot refer to itself')] else: canonical_tag = None if errors: raise ValidationError(errors) if changes: changes['updated_at'] = datetime.utcnow() tag.set(**changes) AdminLog.bl.create( user=user, obj=tag, action=AdminLog.CHANGE, fields=set(changes) - {'updated_at'}, ) if 'reason_to_blacklist' in data: self.set_blacklist(user, data['reason_to_blacklist']) if not tag.is_blacklisted and ('is_alias_for' in data or 'is_hidden_alias' in data): self.make_alias_for(user, canonical_tag, data.get('is_hidden_alias', tag.is_hidden_alias))
def update(self, author, data): from mini_fiction.models import AdminLog data = Validator(CHARACTER_FOR_UPDATE).validated(data, update=True) character = self.model errors = {} if 'name' in data: from mini_fiction.models import Character exist_character = Character.get(name=data['name']) if exist_character and exist_character.id != character.id: errors['name'] = [lazy_gettext('Character already exists')] if 'group' in data: from mini_fiction.models import CharacterGroup group = CharacterGroup.get(id=data['group']) if not group: errors['group'] = [lazy_gettext('Group not found')] else: group = None if errors: raise ValidationError(errors) changed_fields = set() if data.get('picture'): picture = self.validate_and_get_picture_data(data['picture']) else: picture = None for key, value in data.items(): if key == 'picture': if picture is not None: self.set_picture_data(picture) changed_fields |= { 'picture', } elif key == 'group': if character.group.id != value: setattr(character, key, value) changed_fields |= { key, } elif getattr(character, key) != value: setattr(character, key, value) changed_fields |= { key, } if changed_fields: AdminLog.bl.create( user=author, obj=character, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return character
def create(self, target, author, ip, data): if self.schema is None: raise NotImplementedError reqs = self.access_for_commenting_by(target, author) if not reqs: # нет доступа raise ValueError('Permission denied') # TODO: refactor exceptions if reqs.get('captcha'): if current_app.captcha: current_app.captcha.check_or_raise(data) if self.schema: data = Validator(self.schema).validated(data) if data.get('parent'): parent = target.comments.select(lambda x: x.local_id == data[ 'parent'] and not x.deleted).first() if not parent: raise ValidationError( {'parent': [lazy_gettext('Parent comment not found')]}) if not parent.bl.access_for_answer_by(author): raise ValueError('Permission denied') else: parent = None tm = datetime.utcnow() data = { self.target_attr: target, 'author': author if author and author.is_authenticated else None, 'author_username': author.username if author and author.is_authenticated else '', 'ip': ipaddress.ip_address(ip).exploded, 'parent': parent, 'tree_depth': parent.tree_depth + 1 if parent else 0, 'text': data['text'], 'date': tm, 'edits_count': 0, 'last_edited_at': tm, 'last_edited_by': author if author and author.is_authenticated else None, } if parent: assert parent.root_id data['root_id'] = parent.root_id else: data['root_id'] = 0 # заполним после flush last_comment = target.comments.select().order_by( self.model.id.desc()).first() data['local_id'] = (last_comment.local_id + 1) if last_comment else 1 data.update(self._attributes_for(data)) comment = self.model(**data) comment.flush() assert comment.id if not parent: comment.root_id = comment.id if hasattr(target, 'comments_count'): target.comments_count += 1 if hasattr(target, 'last_comment_id'): target.last_comment_id = comment.id if parent: parent.answers_count += 1 current_app.cache.delete('index_comments_html') return comment
def create(self, target, author, ip, data): if self.schema is None: raise NotImplementedError reqs = self.access_for_commenting_by(target, author) if not reqs: # нет доступа raise ValueError('Permission denied') # TODO: refactor exceptions if reqs.get('captcha'): if current_app.captcha: current_app.captcha.check_or_raise(data) if self.schema: data = Validator(self.schema).validated(data) if data.get('parent'): parent = target.comments.select(lambda x: x.local_id == data['parent'] and not x.deleted).first() if not parent: raise ValidationError({'parent': [lazy_gettext('Parent comment not found')]}) if not parent.bl.access_for_answer_by(author): raise ValueError('Permission denied') else: parent = None tm = datetime.utcnow() data = { self.target_attr: target, 'author': author if author and author.is_authenticated else None, 'author_username': author.username if author and author.is_authenticated else '', 'ip': ipaddress.ip_address(ip).exploded, 'parent': parent, 'tree_depth': parent.tree_depth + 1 if parent else 0, 'text': data['text'], 'date': tm, 'edits_count': 0, 'last_edited_at': tm, 'last_edited_by': author if author and author.is_authenticated else None, } if parent: assert parent.root_id data['root_id'] = parent.root_id else: data['root_id'] = 0 # заполним после flush last_comment = target.comments.select().order_by(self.model.id.desc()).first() data['local_id'] = (last_comment.local_id + 1) if last_comment else 1 data.update(self._attributes_for(data)) comment = self.model(**data) comment.flush() assert comment.id if not parent: comment.root_id = comment.id if hasattr(target, 'comments_count'): target.comments_count += 1 if hasattr(target, 'last_comment_id'): target.last_comment_id = comment.id if parent: parent.answers_count += 1 current_app.cache.delete('index_comments_html') return comment
def update(self, data, modified_by_user=None, fill_admin_log=False): from mini_fiction.models import ChangeEmailProfile user = self.model changed_fields = set() if 'email' in data and data['email'] != user.email: user.email = data['email'] cep = ChangeEmailProfile.get(user=user) if cep: cep.delete() changed_fields |= {'email',} for field in ('bio', 'premoderation_mode', 'ban_reason'): if field in data and getattr(user, field) != data[field]: setattr(user, field, data[field]) changed_fields |= {field,} for field in ('is_staff', 'is_active', 'is_superuser', 'detail_view', 'nsfw'): if field in data and getattr(user, field) != bool(data[field]): setattr(user, field, bool(data[field])) changed_fields |= {field,} if 'excluded_categories' in data: user.excluded_categories = ','.join(str(x) for x in data['excluded_categories']) changed_fields |= {'excluded_categories',} if 'comments_per_page' in data: if user.comments_per_page is None and data['comments_per_page'] == current_app.config['COMMENTS_COUNT']['page']: pass # Если бралось значение из настроек проекта, то его и оставляем else: value = min(1000, max(1, int(data['comments_per_page']))) if user.comments_per_page != value: user.comments_per_page = value changed_fields |= {'comments_per_page',} if 'comments_maxdepth' in data: if user.comments_maxdepth is None and data['comments_maxdepth'] == current_app.config['COMMENTS_TREE_MAXDEPTH']: pass # Если бралось значение из настроек проекта, то его и оставляем elif user.comments_maxdepth != int(data['comments_maxdepth']): user.comments_maxdepth = int(data['comments_maxdepth']) changed_fields |= {'comments_maxdepth',} if 'comment_spoiler_threshold' in data: if user.comment_spoiler_threshold is None and data['comment_spoiler_threshold'] == current_app.config['COMMENT_SPOILER_THRESHOLD']: pass # Если бралось значение из настроек проекта, то его и оставляем elif user.comment_spoiler_threshold != int(data['comment_spoiler_threshold']): user.comment_spoiler_threshold = int(data['comment_spoiler_threshold']) changed_fields |= {'comment_spoiler_threshold',} if data.get('header_mode') in ('off', 'l', 'ls'): # off - не показывать картинку в шапке # l - показывать только картинку # ls - показывать картинку с блоком случайных рассказов # Если в базе пусто, то значение по умолчанию ls if (user.header_mode or 'ls') != data['header_mode']: user.header_mode = data['header_mode'] changed_fields |= {'header_mode',} if 'contacts' in data: contacts = [x for x in data['contacts'] if x.get('name') and x.get('value')] lenc = len(contacts) schemas = {} for x in current_app.config['CONTACTS']: schema = dict(x.get('schema') or {}) schema['type'] = 'string' schema['maxlength'] = 255 schemas[x['name']] = schema errors = {} for i, x in enumerate(contacts): if x.get('name') not in schemas: errors[i] = {'name': [lazy_gettext('Invalid contact type')]} continue schema = dict(schemas[x['name']]) v = Validator({'value': schema}) v.validate({'value': x['value']}) if v.errors: errors[i] = v.errors if errors: raise ValidationError({'contacts': errors}) from mini_fiction.models import Contact old_contacts = list(Contact.select(lambda x: x.author == user).order_by(Contact.id)) while len(old_contacts) > lenc: old_contacts.pop().delete() for oldc, newc in zip(old_contacts, contacts): if oldc.name != newc['name']: oldc.name = newc['name'] if oldc.value != newc['value']: oldc.value = newc['value'] i = len(old_contacts) while i < lenc: old_contacts.append(Contact( author=user, name=contacts[i]['name'], value=contacts[i]['value'], )) i = len(old_contacts) changed_fields |= {'contacts',} # TODO: надо ли? if data.get('delete_avatar'): self.delete_avatar() changed_fields |= {'avatar_small', 'avatar_medium', 'avatar_large'} elif current_app.config['AVATARS_UPLOADING'] and data.get('avatar'): from PIL import Image image_data = data['avatar'].stream.read(256 * 1024 + 1) if len(image_data) > 256 * 1024: raise ValidationError({'avatar': [lazy_gettext('Too big avatar; must be {value} KiB or smaller').format(value=256)]}) try: image = Image.open(BytesIO(image_data)) except Exception: raise ValidationError({'avatar': [lazy_gettext('Cannot read avatar')]}) else: with image: self.validate_and_set_avatar(image, image_data) changed_fields |= {'avatar_small', 'avatar_medium', 'avatar_large'} if data.get('extra') is not None: if isinstance(data['extra'], str): try: json.loads(data['extra']) except Exception: raise ValidationError({'extra': ['Invalid extra JSON']}) user.extra = data['extra'] changed_fields |= {'extra',} elif not isinstance(data['extra'], dict): raise ValidationError({'extra': ['extra must be dict or JSON string']}) else: user.extra = json.dumps(data['extra'], ensure_ascii=False) changed_fields |= {'extra',} if modified_by_user and fill_admin_log and changed_fields: from mini_fiction.models import AdminLog AdminLog.bl.create( user=modified_by_user, obj=user, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return changed_fields
def update(self, user, data): from mini_fiction.models import Tag, AdminLog if not user or not user.is_staff: raise ValueError('Not authorized') tag = self.model data = Validator(TAG).validated(data, update=True) changes = {} errors = {} if 'name' in data and data['name'] != tag.name: bad_reason = self.validate_tag_name(data['name']) if bad_reason: errors['name'] = [bad_reason] iname = normalize_tag(data['name']) if not bad_reason and iname != tag.iname and Tag.get(iname=iname): errors['name'] = [lazy_gettext('Tag already exists')] changes['name'] = data['name'] if iname != tag.iname: changes['iname'] = iname if 'category' in data: old_category_id = tag.category.id if tag.category else None if old_category_id != data['category']: changes['category'] = data['category'] for key in ('color', 'description', 'is_main_tag'): if key in data and data[key] != getattr(tag, key): changes[key] = data[key] canonical_tag = tag.is_alias_for if 'is_alias_for' in data: if data.get('is_alias_for'): canonical_tag = Tag.get( iname=normalize_tag(data['is_alias_for'])) if not canonical_tag: errors['is_alias_for'] = [lazy_gettext('Tag not found')] elif canonical_tag == tag or canonical_tag.is_alias_for and canonical_tag.is_alias_for == tag: errors['is_alias_for'] = [ lazy_gettext('Tag cannot refer to itself') ] else: canonical_tag = None if errors: raise ValidationError(errors) if changes: changes['updated_at'] = datetime.utcnow() tag.set(**changes) AdminLog.bl.create( user=user, obj=tag, action=AdminLog.CHANGE, fields=set(changes) - {'updated_at'}, ) if 'reason_to_blacklist' in data: self.set_blacklist(user, data['reason_to_blacklist']) if not tag.is_blacklisted and ('is_alias_for' in data or 'is_hidden_alias' in data): self.make_alias_for( user, canonical_tag, data.get('is_hidden_alias', tag.is_hidden_alias))
def update(self, data, modified_by_user=None, fill_admin_log=False): from mini_fiction.models import ChangeEmailProfile user = self.model changed_fields = set() if 'email' in data and data['email'] != user.email: user.email = data['email'] cep = ChangeEmailProfile.get(user=user) if cep: cep.delete() changed_fields |= { 'email', } for field in ('bio', 'premoderation_mode', 'ban_reason'): if field in data and getattr(user, field) != data[field]: setattr(user, field, data[field]) changed_fields |= { field, } for field in ('is_staff', 'is_active', 'is_superuser', 'detail_view', 'nsfw'): if field in data and getattr(user, field) != bool(data[field]): setattr(user, field, bool(data[field])) changed_fields |= { field, } if 'excluded_categories' in data: user.excluded_categories = ','.join( str(x) for x in data['excluded_categories']) changed_fields |= { 'excluded_categories', } if 'comments_per_page' in data: if user.comments_per_page is None and data[ 'comments_per_page'] == current_app.config[ 'COMMENTS_COUNT']['page']: pass # Если бралось значение из настроек проекта, то его и оставляем else: value = min(1000, max(1, int(data['comments_per_page']))) if user.comments_per_page != value: user.comments_per_page = value changed_fields |= { 'comments_per_page', } if 'comments_maxdepth' in data: if user.comments_maxdepth is None and data[ 'comments_maxdepth'] == current_app.config[ 'COMMENTS_TREE_MAXDEPTH']: pass # Если бралось значение из настроек проекта, то его и оставляем elif user.comments_maxdepth != int(data['comments_maxdepth']): user.comments_maxdepth = int(data['comments_maxdepth']) changed_fields |= { 'comments_maxdepth', } if 'comment_spoiler_threshold' in data: if user.comment_spoiler_threshold is None and data[ 'comment_spoiler_threshold'] == current_app.config[ 'COMMENT_SPOILER_THRESHOLD']: pass # Если бралось значение из настроек проекта, то его и оставляем elif user.comment_spoiler_threshold != int( data['comment_spoiler_threshold']): user.comment_spoiler_threshold = int( data['comment_spoiler_threshold']) changed_fields |= { 'comment_spoiler_threshold', } if data.get('header_mode') in ('off', 'l', 'ls'): # off - не показывать картинку в шапке # l - показывать только картинку # ls - показывать картинку с блоком случайных рассказов # Если в базе пусто, то значение по умолчанию ls if (user.header_mode or 'ls') != data['header_mode']: user.header_mode = data['header_mode'] changed_fields |= { 'header_mode', } if 'contacts' in data: contacts = [ x for x in data['contacts'] if x.get('name') and x.get('value') ] lenc = len(contacts) schemas = {} for x in current_app.config['CONTACTS']: schema = dict(x.get('schema') or {}) schema['type'] = 'string' schema['maxlength'] = 255 schemas[x['name']] = schema errors = {} for i, x in enumerate(contacts): if x.get('name') not in schemas: errors[i] = { 'name': [lazy_gettext('Invalid contact type')] } continue schema = dict(schemas[x['name']]) v = Validator({'value': schema}) v.validate({'value': x['value']}) if v.errors: errors[i] = v.errors if errors: raise ValidationError({'contacts': errors}) from mini_fiction.models import Contact old_contacts = list( Contact.select(lambda x: x.author == user).order_by( Contact.id)) while len(old_contacts) > lenc: old_contacts.pop().delete() for oldc, newc in zip(old_contacts, contacts): if oldc.name != newc['name']: oldc.name = newc['name'] if oldc.value != newc['value']: oldc.value = newc['value'] i = len(old_contacts) while i < lenc: old_contacts.append( Contact( author=user, name=contacts[i]['name'], value=contacts[i]['value'], )) i = len(old_contacts) changed_fields |= { 'contacts', } # TODO: надо ли? if data.get('delete_avatar'): self.delete_avatar() changed_fields |= {'avatar_small', 'avatar_medium', 'avatar_large'} elif current_app.config['AVATARS_UPLOADING'] and data.get('avatar'): from PIL import Image image_data = data['avatar'].stream.read(256 * 1024 + 1) if len(image_data) > 256 * 1024: raise ValidationError({ 'avatar': [ lazy_gettext( 'Too big avatar; must be {value} KiB or smaller'). format(value=256) ] }) try: image = Image.open(BytesIO(image_data)) except Exception: raise ValidationError( {'avatar': [lazy_gettext('Cannot read avatar')]}) else: with image: self.validate_and_set_avatar(image, image_data) changed_fields |= { 'avatar_small', 'avatar_medium', 'avatar_large' } if data.get('extra') is not None: if isinstance(data['extra'], str): try: json.loads(data['extra']) except Exception: raise ValidationError({'extra': ['Invalid extra JSON']}) user.extra = data['extra'] changed_fields |= { 'extra', } elif not isinstance(data['extra'], dict): raise ValidationError( {'extra': ['extra must be dict or JSON string']}) else: user.extra = json.dumps(data['extra'], ensure_ascii=False) changed_fields |= { 'extra', } if modified_by_user and fill_admin_log and changed_fields: from mini_fiction.models import AdminLog AdminLog.bl.create( user=modified_by_user, obj=user, action=AdminLog.CHANGE, fields=sorted(changed_fields), ) return changed_fields