class Category(object): """Represents a category.""" query = db.query_property(CategoryQuery) def __init__(self, name, description='', slug=None): self.name = name if slug is None: self.set_auto_slug() else: self.slug = slug self.description = description def set_auto_slug(self): """Generate a slug for this category.""" full_slug = gen_slug(self.name) if not full_slug: # if slug generation failed we select the highest category # id as base for slug generation. category = Category.query.autoflush(False) \ .order_by(Category.id.desc()).first() full_slug = unicode(category and category.id or u'1') if full_slug != self.slug: while Category.query.autoflush(False) \ .filter_by(slug=full_slug).limit(1).count(): full_slug = increment_string(full_slug) self.slug = full_slug def get_url_values(self): return 'blog/show_category', {'slug': self.slug} def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.name)
class SummarizedPost(_PostBase): """Like a regular post but without text and parser data.""" query = db.query_property(SummarizedPostQuery) def __init__(self): raise TypeError('You cannot create %r instance' % type(self).__name__)
class Post(_PostBase, _ZEMLDualContainer): """A full blown post.""" query = db.query_property(PostQuery) parser_reason = 'post' def __init__(self, title, author, text, slug=None, pub_date=None, last_update=None, comments_enabled=True, pings_enabled=True, status=STATUS_PUBLISHED, parser=None, uid=None, content_type='entry', extra=None): app = get_application() self.content_type = content_type self.title = title self.author = author if parser is None: parser = app.cfg['default_parser'] self.parser = parser self.text = text or u'' if extra: self.extra = dict(extra) else: self.extra = {} self.comments_enabled = comments_enabled self.pings_enabled = pings_enabled self.status = status # set times now, they depend on status being set self.touch_times(pub_date) if last_update is not None: self.last_update = last_update # now bind the slug for which we need the times set. self.bind_slug(slug) # generate a UID if none is given if uid is None: uid = build_tag_uri(app, self.pub_date, content_type, self.slug) self.uid = uid @property def comments_closed(self): """True if commenting is no longer possible.""" app = get_application() open_for = app.cfg['comments_open_for'] if open_for == 0: return False return self.pub_date + timedelta(days=open_for) < datetime.utcnow()
class SchemaVersion(object): """Represents a database schema version.""" query = db.query_property(db.Query) def __init__(self, repos, version=0): self.repository_id = repos.config.get('repository_id') self.repository_path = repos.path self.version = version
class Tag(object): """A single tag.""" query = db.query_property(TagQuery) def __init__(self, name, slug=None): self.name = name if slug is None: self.set_auto_slug() else: self.slug = slug @staticmethod def get_or_create(name): tag = Tag.query.filter_by(name=name).first() if tag is not None: return tag return Tag(name) def set_auto_slug(self): full_slug = gen_slug(self.name) if not full_slug: # if slug generation failed we select the highest category # id as base for slug generation. tag = Tag.query.autoflush(False).order_by(Tag.id.desc()).first() full_slug = unicode(tag and tag.id or u'1') if full_slug != self.slug: while Tag.query.autoflush(False) \ .filter_by(slug=full_slug).limit(1).count(): full_slug = increment_string(full_slug) self.slug = full_slug def get_url_values(self): return 'blog/show_tag', {'slug': self.slug} def __repr__(self): return u'<%s %r>' % (self.__class__.__name__, self.name)
class Comment(_ZEMLContainer): """Represent one comment.""" query = db.query_property(CommentQuery) parser_reason = 'comment' def __init__(self, post, author, text, email=None, www=None, parent=None, pub_date=None, submitter_ip='0.0.0.0', parser=None, is_pingback=False, status=COMMENT_MODERATED): self.post = post if isinstance(author, basestring): self.user = None self._author = author self._email = email self._www = www else: assert email is www is None, \ 'email and www can only be provided if the author is ' \ 'an anonymous user' self.user = author if parser is None: parser = get_application().cfg['comment_parser'] self.parser = parser self.text = text or '' self.parent = parent if pub_date is None: pub_date = datetime.utcnow() self.pub_date = pub_date self.blocked_msg = None self.submitter_ip = submitter_ip self.is_pingback = is_pingback self.status = status def _union_property(attribute, user_attribute=None): """An attribute that can exist on a user and the comment.""" user_attribute = user_attribute or attribute attribute = '_' + attribute def get(self): if self.user: return getattr(self.user, user_attribute) return getattr(self, attribute) def set(self, value): if self.user: raise TypeError('can\'t set this attribute if the comment ' 'does not belong to an anonymous user') setattr(self, attribute, value) return property(get, set) email = _union_property('email') www = _union_property('www') author = _union_property('author', 'display_name') del _union_property def unbind_user(self): """If a user is deleted, the cascading rules would also delete all the comments created by this user, or cause a transaction error because the relation is set to restrict, depending on the current phase of the moon (this changed a lot in the past, i expect it to continue to change). This method unsets the user and updates the comment builtin columns to the values from the user object. """ assert self.user is not None self._email = self.user.email self._www = self.user.www self._author = self.user.display_name self.user = None def _get_status(self): return self._status def _set_status(self, value): was_blocked = self.blocked self._status = value now_blocked = self.blocked # update the comment count on the post if the moderation flag # changed from blocked to unblocked or vice versa if was_blocked != now_blocked: self.post._comment_count = (self.post._comment_count or 0) + \ (now_blocked and -1 or +1) status = property(_get_status, _set_status) del _get_status, _set_status @property def anonymous(self): """True if this comment is an anonymous comment.""" return self.user is None @property def requires_moderation(self): """This is `True` if the comment requires moderation with the current moderation settings. This does not check if the comment is already moderated. """ if not self.anonymous: return False moderate = get_application().cfg['moderate_comments'] if moderate == MODERATE_ALL: return True elif moderate == MODERATE_NONE: return False return db.execute( comments.select( (comments.c.author == self._author) & (comments.c.email == self._email) & (comments.c.status == COMMENT_MODERATED))).fetchone() is None def make_visible_for_request(self, request=None): """Make the comment visible for the current request.""" if request is None: request = get_request() comments = set(request.session.get('visible_comments', ())) comments.add(self.id) request.session['visible_comments'] = tuple(comments) def visible_for_user(self, user=None): """Check if the current user or the user given can see this comment""" request = get_request() if user is None: user = request.user if self.post.author is user and \ user.has_privilege(MODERATE_OWN_ENTRIES | MODERATE_OWN_PAGES): return True elif user.has_privilege(MODERATE_COMMENTS): # User is able to manage comments. It's visible. return True elif self.id in request.session.get('visible_comments', ()): # Comment was made visible for current request. It's visible return True # Finally, comment is visible if not blocked return not self.blocked @property def visible(self): """Check the current session it can see the comment or check against the current user. To display a comment for a request you can use the `make_visible_for_request` function. This is useful to show a comment to a user that submitted a comment which is not yet moderated. """ request = get_request() if request is None: return True return self.visible_for_user(request.user) @property def visible_children(self): """Only the children that are visible for the current user.""" return [x for x in self.children if x.visible] @property def blocked(self): """This is true if the status is anything but moderated.""" return self.status != COMMENT_MODERATED @property def is_spam(self): """This is true if the comment is currently flagged as spam.""" return self.status == COMMENT_BLOCKED_SPAM @property def is_unmoderated(self): """True if the comment is not yet approved.""" return self.status == COMMENT_UNMODERATED @property def is_deleted(self): """True if the comment has been deleted.""" return self.status == COMMENT_DELETED def get_url_values(self): return url_for(self.post) + '#comment-%d' % self.id def summarize(self, chars=140, ellipsis=u'…'): """Summarizes the comment to the given number of characters.""" words = self.body.to_text(simple=True).split() words.reverse() length = 0 result = [] while words: word = words.pop() length += len(word) + 1 if length >= chars: break result.append(word) if words: result.append(ellipsis) return u' '.join(result) def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.author)
class User(object): """Represents an user. If you change something on this model, even default values, keep in mind that the websetup does not use this model to create the admin account because at that time the Rezine system is not yet ready. Also update the code in `rezine.websetup.WebSetup.start_setup`. """ query = db.query_property(UserQuery) is_somebody = True def __init__(self, username, password, email, real_name=u'', description=u'', www=u'', is_author=False): self.username = username if password is not None: self.set_password(password) else: self.disable() self.email = email self.www = www self.real_name = real_name self.description = description self.extra = {} self.display_name = u'$username' self.is_author = is_author @property def is_manager(self): return self.has_privilege(ENTER_ADMIN_PANEL) @property def is_admin(self): return self.has_privilege(BLOG_ADMIN) def _set_display_name(self, value): self._display_name = value def _get_display_name(self): from string import Template return Template(self._display_name).safe_substitute( username=self.username, real_name=self.real_name) display_name = property(_get_display_name, _set_display_name) own_privileges = privilege_attribute('_own_privileges') @property def privileges(self): """A read-only set with all privileges.""" result = set(self.own_privileges) for group in self.groups: result.update(group.privileges) return frozenset(result) def has_privilege(self, privilege): """Check if the user has a given privilege. If the user has the BLOG_ADMIN privilege he automatically has all the other privileges as well. """ return add_admin_privilege(privilege)(self.privileges) def set_password(self, password): self.pw_hash = gen_pwhash(password) def check_password(self, password): if self.pw_hash == '!': return False return check_pwhash(self.pw_hash, password) def disable(self): self.pw_hash = '!' @property def disabled(self): return self.pw_hash == '!' def get_url_values(self): if self.is_author: return 'blog/show_author', {'username': self.username} return self.www or '#' def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.username)