コード例 #1
0
ファイル: database.py プロジェクト: flaskbb/flaskbb
class HideableMixin(object):
    query_class = HideableQuery

    hidden = db.Column(db.Boolean, default=False, nullable=True)
    hidden_at = db.Column(UTCDateTime(timezone=True), nullable=True)

    @declared_attr
    def hidden_by_id(cls):  # noqa: B902
        return db.Column(
            db.Integer,
            db.ForeignKey("users.id",
                          name="fk_{}_hidden_by".format(cls.__name__)),
            nullable=True,
        )

    @declared_attr
    def hidden_by(cls):  # noqa: B902
        return db.relationship("User",
                               uselist=False,
                               foreign_keys=[cls.hidden_by_id])

    def hide(self, user, *args, **kwargs):
        from flaskbb.utils.helpers import time_utcnow

        self.hidden_by = user
        self.hidden = True
        self.hidden_at = time_utcnow()
        return self

    def unhide(self, *args, **kwargs):
        self.hidden_by = None
        self.hidden = False
        self.hidden_at = None
        return self
コード例 #2
0
ファイル: models.py プロジェクト: rollovercable/flaskbb
class TopicsRead(db.Model):
    __tablename__ = "topicsread"

    user_id = db.Column(db.Integer,
                        db.ForeignKey("users.id"),
                        primary_key=True)
    topic_id = db.Column(db.Integer,
                         db.ForeignKey("topics.id",
                                       use_alter=True,
                                       name="fk_tr_topic_id"),
                         primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id",
                                       use_alter=True,
                                       name="fk_tr_forum_id"),
                         primary_key=True)
    last_read = db.Column(db.DateTime, default=datetime.utcnow())

    def save(self):
        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return self
コード例 #3
0
class ForumsRead(db.Model):
    __tablename__ = "forumsread"

    user_id = db.Column(db.Integer,
                        db.ForeignKey("users.id"),
                        primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id",
                                       use_alter=True,
                                       name="fk_fr_forum_id"),
                         primary_key=True)
    last_read = db.Column(db.DateTime, default=datetime.utcnow())
    cleared = db.Column(db.DateTime)

    def __repr__(self):
        return "<{}>".format(self.__class__.__name__)

    def save(self):
        """Saves a ForumsRead entry."""
        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        """Deletes a ForumsRead entry."""
        db.session.delete(self)
        db.session.commit()
        return self
コード例 #4
0
class UserRank(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    user_id = db.Column(db.ForeignKey("users.id"), nullable=False)
    rank_id = db.Column(db.ForeignKey("ranks.id"))
    user = db.relationship(
        User,
        backref=db.backref("user_rank",
                           uselist=False,
                           cascade="all, delete-orphan",
                           lazy="joined"),
        uselist=False,
        lazy="joined",
        foreign_keys=[user_id],
    )

    rank = db.relationship(
        Rank,
        backref=db.backref("user_ranks",
                           lazy="joined",
                           cascade="all, delete-orphan"),
        uselist=False,
        lazy="joined",
        foreign_keys=[rank_id],
    )

    name = association_proxy("rank", "rank_name")
    code = association_proxy("rank", "rank_code")

    def is_custom(self):
        return self.rank.is_custom()

    def __repr__(self):
        return "<UserRank user={} name={}>".format(self.user.username,
                                                   self.name)
コード例 #5
0
class Message(db.Model, CRUDMixin):
    __tablename__ = "messages"

    id = db.Column(db.Integer, primary_key=True)
    conversation_id = db.Column(db.Integer,
                                db.ForeignKey("conversations.id"),
                                nullable=False)

    # the user who wrote the message
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
    message = db.Column(db.Text, nullable=False)
    date_created = db.Column(UTCDateTime(timezone=True),
                             default=time_utcnow,
                             nullable=False)

    user = db.relationship("User", lazy="joined")

    def save(self, conversation=None):
        """Saves a private message.

        :param conversation: The  conversation to which the message
                             belongs to.
        """
        if conversation is not None:
            self.conversation = conversation
            conversation.date_modified = time_utcnow()
            self.date_created = time_utcnow()

        db.session.add(self)
        db.session.commit()
        return self
コード例 #6
0
ファイル: models.py プロジェクト: marvinwu/flaskbb
class PrivateMessage(db.Model):
    __tablename__ = "privatemessages"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
    from_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    to_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    subject = db.Column(db.String(255))
    message = db.Column(db.Text)
    date_created = db.Column(db.DateTime, default=datetime.utcnow())
    trash = db.Column(db.Boolean, nullable=False, default=False)
    draft = db.Column(db.Boolean, nullable=False, default=False)
    unread = db.Column(db.Boolean, nullable=False, default=True)

    user = db.relationship("User",
                           backref="pms",
                           lazy="joined",
                           foreign_keys=[user_id])
    from_user = db.relationship("User",
                                lazy="joined",
                                foreign_keys=[from_user_id])
    to_user = db.relationship("User", lazy="joined", foreign_keys=[to_user_id])

    def save(self, from_user=None, to_user=None, user_id=None, draft=False):
        """Saves a private message.

        :param from_user: The user who has sent the message

        :param to_user: The user who should recieve the message

        :param user_id: The senders user id - This is the id to which user the
                        Inbox belongs.

        :param draft: If the message is a draft. Defaults to ``False``.
        """

        if self.id:
            db.session.add(self)
            db.session.commit()
            return self

        # Defaults to ``False``.
        self.draft = draft

        # Add the message to the user's pm box
        self.user_id = user_id
        self.from_user_id = from_user
        self.to_user_id = to_user
        self.date_created = datetime.utcnow()

        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        """Deletes a private message"""

        db.session.delete(self)
        db.session.commit()
        return self
コード例 #7
0
class Conversation(db.Model, CRUDMixin):
    __tablename__ = "conversations"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
    from_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    to_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    shared_id = db.Column(UUIDType, nullable=False)
    subject = db.Column(db.String(255))
    date_created = db.Column(UTCDateTime(timezone=True), default=time_utcnow)
    trash = db.Column(db.Boolean, nullable=False, default=False)
    draft = db.Column(db.Boolean, nullable=False, default=False)
    unread = db.Column(db.Boolean, nullable=False, default=True)

    messages = db.relationship(
        "Message",
        lazy="joined",
        backref="conversation",
        primaryjoin="Message.conversation_id == Conversation.id",
        order_by="asc(Message.id)",
        cascade="all, delete-orphan")

    # this is actually the users message box
    user = db.relationship("User", lazy="joined", foreign_keys=[user_id])
    # the user to whom the conversation is addressed
    to_user = db.relationship("User", lazy="joined", foreign_keys=[to_user_id])
    # the user who sent the message
    from_user = db.relationship("User",
                                lazy="joined",
                                foreign_keys=[from_user_id])

    @property
    def first_message(self):
        """Returns the first message object."""
        return self.messages[0]

    @property
    def last_message(self):
        """Returns the last message object."""
        return self.messages[-1]

    def save(self, message=None):
        """Saves a conversation and returns the saved conversation object.

        :param message: If given, it will also save the message for the
                        conversation. It expects a Message object.
        """
        if message is not None:
            # create the conversation
            self.date_created = time_utcnow()
            db.session.add(self)
            db.session.commit()

            # create the actual message for the conversation
            message.save(self)
            return self

        db.session.add(self)
        db.session.commit()
        return self
コード例 #8
0
class SubscriptionSettings(db.Model, CRUDMixin):

    'Subscription settings for user'

    __tablename__ = 'subby_settings'

    #: ID of the user
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id', ondelete='CASCADE'),
                        primary_key=True,
                        nullable=False)
    #: Include tracked topics
    tracked_topics = db.Column(db.Boolean, nullable=False, default=False)
    #: Enable email notifications
    email = db.Column(db.Boolean, nullable=False, default=True)
    #: RSS key
    rss_key = db.Column(db.String)
    #: Settings owner
    user = db.relationship('User', lazy='joined', foreign_keys=(user_id, ))

    def save(self):
        'Saves subscription settings'

        if not self.rss_key:
            self.rss_key = SubscriptionSettings._regenerate_rss_key()

        db.session.add(self)
        db.session.commit()

    @staticmethod
    def _regenerate_rss_key():
        'Regenerates unique RSS key'

        return str(uuid4())
コード例 #9
0
class Subscription(db.Model, CRUDMixin):

    'Forum subscriptions for user'

    __tablename__ = 'subby_subscriptions'

    #: ID of the user
    user_id = db.Column(db.Integer,
                        db.ForeignKey('users.id', ondelete='CASCADE'),
                        primary_key=True,
                        nullable=False)
    #: ID of a forum they subscribe to
    forum_id = db.Column(db.Integer,
                         db.ForeignKey('forums.id', ondelete='CASCADE'),
                         primary_key=True,
                         nullable=False)
    #: Subscription owner
    user = db.relationship('User', lazy='joined', foreign_keys=(user_id, ))
    #: Forum subscribed to
    forum = db.relationship('Forum', lazy='joined', foreign_keys=(forum_id, ))

    def save(self):
        'Saves forum subscription'

        db.session.add(self)
        db.session.commit()

        return self
コード例 #10
0
ファイル: models.py プロジェクト: yonglehou/flaskbb
class SettingsGroup(db.Model, CRUDMixin):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String(255), primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text, nullable=False)
    settings = db.relationship("Setting",
                               lazy="dynamic",
                               backref="group",
                               cascade="all, delete-orphan")
コード例 #11
0
ファイル: models.py プロジェクト: rollovercable/flaskbb
class Category(db.Model):
    __tablename__ = "categories"

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(63), nullable=False)
    description = db.Column(db.String(255))
    position = db.Column(db.Integer, default=1, nullable=False)

    # One-to-many
    forums = db.relationship("Forum",
                             backref="category",
                             lazy="dynamic",
                             primaryjoin='Forum.category_id == Category.id',
                             order_by='asc(Forum.position)',
                             cascade="all, delete-orphan")

    # Properties
    @property
    def slug(self):
        """Returns a slugified version from the category title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the url for the category"""
        return url_for("forum.view_category",
                       category_id=self.id,
                       slug=self.slug)

    # Methods
    def save(self):
        """Saves a category"""

        db.session.add(self)
        db.session.commit()
        return self

    def delete(self, users=None):
        """Deletes a category. If a list with involved user objects is passed,
        it will also update their post counts

        :param users: A list with user objects
        """

        # Update the users post count
        if users:
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                db.session.commit()

        # and finally delete the category itself
        db.session.delete(self)
        db.session.commit()
        return self
コード例 #12
0
ファイル: models.py プロジェクト: yes7rose/flaskbb
class ForumsRead(db.Model, CRUDMixin):
    __tablename__ = "forumsread"

    user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
                        primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id", use_alter=True,
                                       name="fk_fr_forum_id"),
                         primary_key=True)
    last_read = db.Column(UTCDateTime(timezone=True), default=time_utcnow)
    cleared = db.Column(UTCDateTime(timezone=True))
コード例 #13
0
class SettingsGroup(db.Model, CRUDMixin):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String(255), primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def __repr__(self):
        return "<{} {}>".format(self.__class__.__name__, self.key)
コード例 #14
0
ファイル: models.py プロジェクト: 1panpan1/tj_dnalab
class ForumsRead(db.Model, CRUDMixin):
    __tablename__ = "forumsread"

    user_id = db.Column(db.Integer,
                        db.ForeignKey("users.id", ondelete="CASCADE"),
                        primary_key=True)
    user = db.relationship('User', uselist=False, foreign_keys=[user_id])
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id", ondelete="CASCADE"),
                         primary_key=True)
    forum = db.relationship('Forum', uselist=False, foreign_keys=[forum_id])
    last_read = db.Column(UTCDateTime(timezone=True), default=time_utcnow,
                          nullable=False)
    cleared = db.Column(UTCDateTime(timezone=True), nullable=True)
コード例 #15
0
ファイル: models.py プロジェクト: 1panpan1/tj_dnalab
class PluginStore(CRUDMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    key = db.Column(db.Unicode(255), nullable=False)
    value = db.Column(db.PickleType, nullable=False)
    # Available types: string, integer, float, boolean, select, selectmultiple
    value_type = db.Column(db.Enum(SettingValueType), nullable=False)
    # Extra attributes like, validation things (min, max length...)
    # For Select*Fields required: choices
    extra = db.Column(db.PickleType, nullable=True)
    plugin_id = db.Column(
        db.Integer, db.ForeignKey("plugin_registry.id", ondelete="CASCADE"))

    # Display stuff
    name = db.Column(db.Unicode(255), nullable=False)
    description = db.Column(db.Text, nullable=True)

    __table_args__ = (UniqueConstraint('key',
                                       'plugin_id',
                                       name='plugin_kv_uniq'), )

    def __repr__(self):
        return '<PluginSetting plugin={} key={} value={}>'.format(
            self.plugin.name, self.key, self.value)

    @classmethod
    def get_or_create(cls, plugin_id, key):
        """Returns the PluginStore object or an empty one.
        The created object still needs to be added to the database session
        """
        obj = cls.query.filter_by(plugin_id=plugin_id, key=key).first()

        if obj is not None:
            return obj
        return PluginStore()
コード例 #16
0
class RawData(db.Model, CRUDMixin):
    __tablename__ = "rawdata"

    id = db.Column(db.Integer, primary_key=True)
    thread_id = db.Column(db.Integer, nullable=False)
    forum_id = db.Column(db.Integer, nullable=False)
    thread_name = db.Column(db.String, nullable=False)
    post_num = db.Column(db.Integer, nullable=False)
    username = db.Column(db.String, nullable=False)
    message = db.Column(db.String, nullable=False)

    date_created = db.Column(UTCDateTime(timezone=True),
                             default=time_utcnow,
                             nullable=False)

    def save(self):
        """Saves a raw_data and returns the saved raw_data object.

        :param raw_data: If given, it will also save the raw_data for the
                        raw_data. It expects a Message object.
        """

        db.session.add(self)
        db.session.commit()
        return self
コード例 #17
0
ファイル: database.py プロジェクト: flaskbb/flaskbb
 def hidden_by_id(cls):  # noqa: B902
     return db.Column(
         db.Integer,
         db.ForeignKey("users.id",
                       name="fk_{}_hidden_by".format(cls.__name__)),
         nullable=True,
     )
コード例 #18
0
class TopicsRead(db.Model, CRUDMixin):
    __tablename__ = "topicsread"

    user_id = db.Column(db.Integer,
                        db.ForeignKey("users.id"),
                        primary_key=True)
    topic_id = db.Column(db.Integer,
                         db.ForeignKey("topics.id",
                                       use_alter=True,
                                       name="fk_tr_topic_id"),
                         primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id",
                                       use_alter=True,
                                       name="fk_tr_forum_id"),
                         primary_key=True)
    last_read = db.Column(db.DateTime, default=datetime.utcnow())
コード例 #19
0
class TopicsRead(db.Model, CRUDMixin):
    __tablename__ = "topicsread"

    user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
                        primary_key=True)
    user = db.relationship('User', uselist=False, foreign_keys=[user_id])
    topic_id = db.Column(db.Integer,
                         db.ForeignKey("topics.id", use_alter=True,
                                       name="fk_tr_topic_id"),
                         primary_key=True)
    topic = db.relationship('Topic', uselist=False, foreign_keys=[topic_id])
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id", use_alter=True,
                                       name="fk_tr_forum_id"),
                         primary_key=True)
    forum = db.relationship('Forum', uselist=False, foreign_keys=[forum_id])
    last_read = db.Column(UTCDateTime(timezone=True), default=time_utcnow,
                          nullable=False)
コード例 #20
0
class SettingsGroup(db.Model):
    __tablename__ = "settingsgroup"

    key = db.Column(db.String, primary_key=True)
    name = db.Column(db.String, nullable=False)
    description = db.Column(db.String, nullable=False)
    settings = db.relationship("Setting", lazy="dynamic", backref="group",
                               cascade="all, delete-orphan")

    def save(self):
        """Saves a settingsgroup."""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a settingsgroup."""
        db.session.delete(self)
        db.session.commit()
コード例 #21
0
class HubLog(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    server_id = db.Column(String(50), nullable=False)
    datetime = db.Column(UTCDateTime(timezone=True),
                         default=time_utcnow,
                         nullable=False)
    user_id = db.Column(
        db.Integer,
        db.ForeignKey("users.id", ondelete="SET NULL"),
        nullable=True,
    )
    message = db.Column(db.Text, nullable=False)

    user = db.relationship("User", lazy="joined", foreign_keys=[user_id])

    def save(self):
        db.session.add(self)
        db.session.commit()
        return self
コード例 #22
0
ファイル: models.py プロジェクト: marcelotduarte/flaskbb
class TopicsRead(db.Model, CRUDMixin):
    __tablename__ = "topicsread"

    user_id = db.Column(db.Integer,
                        db.ForeignKey("users.id"),
                        primary_key=True)
    topic_id = db.Column(db.Integer,
                         db.ForeignKey("topics.id",
                                       use_alter=True,
                                       name="fk_tr_topic_id"),
                         primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forums.id",
                                       use_alter=True,
                                       name="fk_tr_forum_id"),
                         primary_key=True)
    last_read = db.Column(UTCDateTime(timezone=True),
                          default=time_utcnow,
                          nullable=False)
コード例 #23
0
class Rank(db.Model):
    __tablename__ = "ranks"
    id = db.Column(db.Integer(), primary_key=True)
    rank_code = db.Column(db.String(255), nullable=False)
    rank_name = db.Column(db.String(255), default="")
    requirement = db.Column(db.Integer(), nullable=True, unique=True)

    users = association_proxy("user_ranks",
                              "user",
                              creator=lambda user: UserRank(user=user))

    def is_custom(self):
        return self.requirement is None

    @staticmethod
    def has_rank(user):
        if user.is_anonymous:
            return False

        return user.rank is not None

    @classmethod
    def has_custom_rank(cls, user):
        return cls.has_rank(user) and user.rank.is_custom()

    def __repr__(self):
        return "<Rank name={} requirement={}>".format(self.rank_name,
                                                      self.requirement)

    @classmethod
    def partition_ranks(cls, ranks):
        r = {"custom": [], "requirement": []}

        for rank in ranks:
            if cls.is_custom(rank):
                r["custom"].append(rank)
            else:
                r["requirement"].append(rank)

        return r
コード例 #24
0
class PostLike(db.Model):
    __tablename__ = 'vanity_association'

    post_id = db.Column(db.ForeignKey('posts.id'), primary_key=True)
    post = db.relationship(
        Post,
        backref=db.backref(
            "liked_by_users",
            lazy='joined',
            cascade='all, delete-orphan'
        )
    )
    user_id = db.Column(db.ForeignKey('users.id'), primary_key=True)
    user = db.relationship(
        User,
        uselist=False,
        #lazy='joined',  # see: https://github.com/flaskbb/flaskbb/issues/503#issuecomment-415713742
        backref=db.backref(
            "user_liked_posts",
            cascade="all, delete-orphan",
        ),
    )
コード例 #25
0
ファイル: models.py プロジェクト: nginth/c2c_comm_forum
class Report(db.Model, CRUDMixin):
    __tablename__ = "reports"

    # TODO: Store in addition to the info below topic title and username
    # as well. So that in case a user or post gets deleted, we can
    # still view the report

    id = db.Column(db.Integer, primary_key=True)
    reporter_id = db.Column(db.Integer,
                            db.ForeignKey("users.id"),
                            nullable=True)
    reported = db.Column(UTCDateTime(timezone=True),
                         default=time_utcnow,
                         nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=True)
    zapped = db.Column(UTCDateTime(timezone=True), nullable=True)
    zapped_by = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
    reason = db.Column(db.Text, nullable=True)

    post = db.relationship("Post",
                           lazy="joined",
                           backref=db.backref('report',
                                              cascade='all, delete-orphan'))
    reporter = db.relationship("User",
                               lazy="joined",
                               foreign_keys=[reporter_id])
    zapper = db.relationship("User", lazy="joined", foreign_keys=[zapped_by])

    def __repr__(self):
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def save(self, post=None, user=None):
        """Saves a report.

        :param post: The post that should be reported
        :param user: The user who has reported the post
        :param reason: The reason why the user has reported the post
        """

        if self.id:
            db.session.add(self)
            db.session.commit()
            return self

        if post and user:
            self.reporter = user
            self.reported = time_utcnow()
            self.post = post

        db.session.add(self)
        db.session.commit()
        return self
コード例 #26
0
class Report(db.Model):
    __tablename__ = "reports"

    id = db.Column(db.Integer, primary_key=True)
    reporter_id = db.Column(db.Integer,
                            db.ForeignKey("users.id"),
                            nullable=False)
    reported = db.Column(db.DateTime, default=datetime.utcnow())
    post_id = db.Column(db.Integer, db.ForeignKey("posts.id"), nullable=False)
    zapped = db.Column(db.DateTime)
    zapped_by = db.Column(db.Integer, db.ForeignKey("users.id"))
    reason = db.Column(db.Text)

    post = db.relationship("Post", backref="report", lazy="joined")
    reporter = db.relationship("User",
                               lazy="joined",
                               foreign_keys=[reporter_id])
    zapper = db.relationship("User", lazy="joined", foreign_keys=[zapped_by])

    def __repr__(self):
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def save(self, post=None, user=None):
        """Saves a report.

        :param post: The post that should be reported

        :param user: The user who has reported the post

        :param reason: The reason why the user has reported the post
        """

        if self.id:
            db.session.add(self)
            db.session.commit()
            return self

        if post and user:
            self.reporter_id = user.id
            self.reported = datetime.utcnow()
            self.post_id = post.id

        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        """Deletes a report."""
        db.session.delete(self)
        db.session.commit()
        return self
コード例 #27
0
ファイル: models.py プロジェクト: vlttnv/flaskbb
class Message(db.Model):
    __tablename__ = "messages"

    id = db.Column(db.Integer, primary_key=True)
    conversation_id = db.Column(db.Integer,
                                db.ForeignKey("conversations.id"),
                                nullable=False)

    # the user who wrote the message
    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
    message = db.Column(db.Text, nullable=False)
    date_created = db.Column(db.DateTime, default=datetime.utcnow())

    user = db.relationship("User", lazy="joined")

    def save(self, conversation=None):
        """Saves a private message.

        :param conversation_id: The id of the conversation to which the message
                                belongs to.
        """
        if conversation is not None:
            self.conversation_id = conversation.id
            self.user_id = conversation.from_user_id
            self.date_created = datetime.utcnow()

        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        """Deletes a private message"""

        db.session.delete(self)
        db.session.commit()
        return self
コード例 #28
0
class Setting(db.Model):
    __tablename__ = "settings"

    key = db.Column(db.String, primary_key=True)
    _value = db.Column("value", db.String, nullable=False)
    settingsgroup = db.Column(db.String,
                              db.ForeignKey('settingsgroup.key',
                                            use_alter=True,
                                            name="fk_settingsgroup"),
                              nullable=False)

    # The name (displayed in the form)
    name = db.Column(db.String, nullable=False)

    # The description (displayed in the form)
    description = db.Column(db.String, nullable=False)

    # Available types: string, integer, boolean, array, float
    value_type = db.Column(db.String, nullable=False)

    # Available types: text, number, choice, yesno
    # They are used in the form creation process
    input_type = db.Column(db.String, nullable=False)

    # Extra attributes like, validation things (min, max length...)
    _extra = db.Column("extra", db.String)

    # Properties
    @property
    def value(self):
        return normalize_to(self._value, self.value_type)

    @value.setter
    def value(self, value):
        self._value = normalize_to(value, self.value_type, reverse=True)

    @property
    def extra(self):
        return pickle.loads(base64.decodestring(self._extra))

    @extra.setter
    def extra(self, extra):
        self._extra = base64.encodestring(
            pickle.dumps((extra), pickle.HIGHEST_PROTOCOL)
        )

    @classmethod
    def get_form(cls, group):
        """Returns a Form for all settings found in :class:`SettingsGroup`.

        :param group: The settingsgroup name. It is used to get the settings
                      which are in the specified group.
        """

        class SettingsForm(Form):
            pass

        # now parse that shit
        for setting in group.settings:
            field_validators = []

            # generate the validators
            # TODO: Do this in another function
            if "min" in setting.extra:
                # Min number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(min=setting.extra["min"])
                    )

                # Min text length validator
                elif setting.value_type in ("string", "array"):
                    field_validators.append(
                        validators.Length(min=setting.extra["min"])
                    )

            if "max" in setting.extra:
                # Max number validator
                if setting.value_type in ("integer", "float"):
                    field_validators.append(
                        validators.NumberRange(max=setting.extra["max"])
                    )

                # Max text length validator
                elif setting.value_type in ("string", "array"):
                    field_validators.append(
                        validators.Length(max=setting.extra["max"])
                    )

            # Generate the fields based on input_type and value_type
            if setting.input_type == "number":
                # IntegerField
                if setting.value_type == "integer":
                    setattr(
                        SettingsForm, setting.key,
                        IntegerField(setting.name, validators=field_validators,
                                     description=setting.description)
                    )
                # FloatField
                elif setting.value_type == "float":
                    setattr(
                        SettingsForm, setting.key,
                        FloatField(setting.name, validators=field_validators,
                                   description=setting.description)
                    )

            # TextField
            if setting.input_type == "text":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            if setting.input_type == "array":
                setattr(
                    SettingsForm, setting.key,
                    TextField(setting.name, validators=field_validators,
                              description=setting.description)
                )

            # SelectField
            if setting.input_type == "choice" and "choices" in setting.extra:
                setattr(
                    SettingsForm, setting.key,
                    SelectField(setting.name, choices=setting.extra['choices'],
                                description=setting.description)
                )

            # BooleanField
            if setting.input_type == "yesno":
                setattr(
                    SettingsForm, setting.key,
                    BooleanField(setting.name, description=setting.description)
                )

        return SettingsForm

    @classmethod
    def get_all(cls):
        return cls.query.all()

    @classmethod
    def update(cls, settings, app=None):
        """Updates the current_app's config and stores the changes in the
        database.

        :param config: A dictionary with configuration items.
        """
        updated_settings = {}
        for key, value in settings.iteritems():
            setting = cls.query.filter(Setting.key == key.lower()).first()

            setting.value = value

            updated_settings[setting.key.upper()] = setting.value

            db.session.add(setting)
            db.session.commit()

        if app is not None:
            app.config.update(updated_settings)

    @classmethod
    def as_dict(cls, from_group=None, upper=False):
        """Returns the settings key and value as a dict.

        :param from_group: Returns only the settings from the group as a dict.
        :param upper: If upper is ``True``, the key will use upper-case
                      letters. Defaults to ``False``.
        """
        settings = {}
        result = None
        if from_group is not None:
            result = SettingsGroup.query.filter_by(key=from_group).\
                first_or_404()
            result = result.settings
        else:
            result = cls.query.all()

        for setting in result:
            if upper:
                settings[setting.key.upper()] = setting.value
            else:
                settings[setting.key] = setting.value

        return settings

    def save(self):
        """Saves a setting"""
        db.session.add(self)
        db.session.commit()

    def delete(self):
        """Deletes a setting"""
        db.session.delete(self)
        db.session.commit()
コード例 #29
0
class Category(db.Model, CRUDMixin):
    __tablename__ = "categories"
    __searchable__ = ['title', 'description']

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    position = db.Column(db.Integer, default=1, nullable=False)

    # One-to-many
    forums = db.relationship("Forum",
                             backref="category",
                             lazy="dynamic",
                             primaryjoin='Forum.category_id == Category.id',
                             order_by='asc(Forum.position)',
                             cascade="all, delete-orphan")

    # Properties
    @property
    def slug(self):
        """Returns a slugified version from the category title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the slugified url for the category"""
        return url_for("forum.view_category",
                       category_id=self.id,
                       slug=self.slug)

    # Methods
    def __repr__(self):
        """Set to a unique key specific to the object in the database.
        Required for cache.memoize() to work across requests.
        """
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def delete(self, users=None):
        """Deletes a category. If a list with involved user objects is passed,
        it will also update their post counts

        :param users: A list with user objects
        """

        # and finally delete the category itself
        db.session.delete(self)
        db.session.commit()

        # Update the users post count
        if users:
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                db.session.commit()

        return self

    # Classmethods
    @classmethod
    def get_all(cls, user):
        """Get all categories with all associated forums.
        It returns a list with tuples. Those tuples are containing the category
        and their associated forums (whose are stored in a list).

        For example::

            [(<Category 1>, [(<Forum 2>, <ForumsRead>), (<Forum 1>, None)]),
             (<Category 2>, [(<Forum 3>, None), (<Forum 4>, None)])]

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        # import Group model locally to avoid cicular imports
        from flaskbb.user.models import Group
        if user.is_authenticated:
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.\
                filter(Forum.groups.any(Group.id.in_(user_groups))).\
                subquery()

            forum_alias = aliased(Forum, user_forums)
            # get all
            forums = cls.query.\
                join(forum_alias, cls.id == forum_alias.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(forum_alias).\
                add_entity(ForumsRead).\
                order_by(Category.position, Category.id,
                         forum_alias.position).\
                all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.\
                filter(Forum.groups.any(Group.id == guest_group.id)).\
                subquery()

            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.\
                join(forum_alias, cls.id == forum_alias.category_id).\
                add_entity(forum_alias).\
                order_by(Category.position, Category.id,
                         forum_alias.position).\
                all()

        return get_categories_and_forums(forums, user)

    @classmethod
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        from flaskbb.user.models import Group
        if user.is_authenticated:
            # get list of user group ids
            user_groups = [gr.id for gr in user.groups]
            # filter forums by user groups
            user_forums = Forum.query.\
                filter(Forum.groups.any(Group.id.in_(user_groups))).\
                subquery()

            forum_alias = aliased(Forum, user_forums)
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(forum_alias, cls.id == forum_alias.category_id).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(forum_alias).\
                add_entity(ForumsRead).\
                order_by(forum_alias.position).\
                all()
        else:
            guest_group = Group.get_guest_group()
            # filter forums by guest groups
            guest_forums = Forum.query.\
                filter(Forum.groups.any(Group.id == guest_group.id)).\
                subquery()

            forum_alias = aliased(Forum, guest_forums)
            forums = cls.query.\
                filter(cls.id == category_id).\
                join(forum_alias, cls.id == forum_alias.category_id).\
                add_entity(forum_alias).\
                order_by(forum_alias.position).\
                all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
コード例 #30
0
class Forum(db.Model, CRUDMixin):
    __tablename__ = "forums"
    __searchable__ = ['title', 'description']

    id = db.Column(db.Integer, primary_key=True)
    category_id = db.Column(db.Integer,
                            db.ForeignKey("categories.id"),
                            nullable=False)
    title = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    position = db.Column(db.Integer, default=1, nullable=False)
    locked = db.Column(db.Boolean, default=False, nullable=False)
    show_moderators = db.Column(db.Boolean, default=False, nullable=False)
    external = db.Column(db.String(200))

    post_count = db.Column(db.Integer, default=0, nullable=False)
    topic_count = db.Column(db.Integer, default=0, nullable=False)

    # One-to-one
    last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id"))
    last_post = db.relationship("Post",
                                backref="last_post_forum",
                                uselist=False,
                                foreign_keys=[last_post_id])

    # Not nice, but needed to improve the performance
    last_post_title = db.Column(db.String(255))
    last_post_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
    last_post_username = db.Column(db.String(255))
    last_post_created = db.Column(UTCDateTime(timezone=True),
                                  default=time_utcnow)

    # One-to-many
    topics = db.relationship("Topic",
                             backref="forum",
                             lazy="dynamic",
                             cascade="all, delete-orphan")

    # Many-to-many
    moderators = db.relationship("User",
                                 secondary=moderators,
                                 primaryjoin=(moderators.c.forum_id == id),
                                 backref=db.backref("forummoderator",
                                                    lazy="dynamic"),
                                 lazy="joined")
    groups = db.relationship(
        "Group",
        secondary=forumgroups,
        primaryjoin=(forumgroups.c.forum_id == id),
        backref="forumgroups",
        lazy="joined",
    )

    # Properties
    @property
    def slug(self):
        """Returns a slugified version from the forum title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the slugified url for the forum"""
        if self.external:
            return self.external
        return url_for("forum.view_forum", forum_id=self.id, slug=self.slug)

    @property
    def last_post_url(self):
        """Returns the url for the last post in the forum"""
        return url_for("forum.view_post", post_id=self.last_post_id)

    # Methods
    def __repr__(self):
        """Set to a unique key specific to the object in the database.
        Required for cache.memoize() to work across requests.
        """
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def update_last_post(self):
        """Updates the last post in the forum."""
        last_post = Post.query.\
            filter(Post.topic_id == Topic.id,
                   Topic.forum_id == self.id).\
            order_by(Post.date_created.desc()).\
            first()

        # Last post is none when there are no topics in the forum
        if last_post is not None:

            # a new last post was found in the forum
            if not last_post.id == self.last_post_id:
                self.last_post_id = last_post.id
                self.last_post_title = last_post.topic.title
                self.last_post_user_id = last_post.user_id
                self.last_post_username = last_post.username
                self.last_post_created = last_post.date_created

        # No post found..
        else:
            self.last_post_id = None
            self.last_post_title = None
            self.last_post_user_id = None
            self.last_post_username = None
            self.last_post_created = None

        db.session.commit()

    def update_read(self, user, forumsread, topicsread):
        """Updates the ForumsRead status for the user. In order to work
        correctly, be sure that `topicsread is **not** `None`.

        :param user: The user for whom we should check if he has read the
                     forum.

        :param forumsread: The forumsread object. It is needed to check if
                           if the forum is unread. If `forumsread` is `None`
                           and the forum is unread, it will create a new entry
                           in the `ForumsRead` relation, else (and the forum
                           is still unread) we are just going to update the
                           entry in the `ForumsRead` relation.

        :param topicsread: The topicsread object is used in combination
                           with the forumsread object to check if the
                           forumsread relation should be updated and
                           therefore is unread.
        """
        if not user.is_authenticated or topicsread is None:
            return False

        read_cutoff = None
        if flaskbb_config['TRACKER_LENGTH'] > 0:
            read_cutoff = time_utcnow() - timedelta(
                days=flaskbb_config['TRACKER_LENGTH'])

        # fetch the unread posts in the forum
        unread_count = Topic.query.\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == user.id)).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Topic.forum_id,
                              ForumsRead.user_id == user.id)).\
            filter(Topic.forum_id == self.id,
                   Topic.last_updated > read_cutoff,
                   db.or_(TopicsRead.last_read == None,
                          TopicsRead.last_read < Topic.last_updated)).\
            count()

        # No unread topics available - trying to mark the forum as read
        if unread_count == 0:

            if forumsread and forumsread.last_read > topicsread.last_read:
                return False

            # ForumRead Entry exists - Updating it because a new topic/post
            # has been submitted and has read everything (obviously, else the
            # unread_count would be useless).
            elif forumsread:
                forumsread.last_read = time_utcnow()
                forumsread.save()
                return True

            # No ForumRead Entry existing - creating one.
            forumsread = ForumsRead()
            forumsread.user_id = user.id
            forumsread.forum_id = self.id
            forumsread.last_read = time_utcnow()
            forumsread.save()
            return True

        # Nothing updated, because there are still more than 0 unread
        # topicsread
        return False

    def recalculate(self, last_post=False):
        """Recalculates the post_count and topic_count in the forum.
        Returns the forum with the recounted stats.

        :param last_post: If set to ``True`` it will also try to update
                          the last post columns in the forum.
        """
        topic_count = Topic.query.filter_by(forum_id=self.id).count()
        post_count = Post.query.\
            filter(Post.topic_id == Topic.id,
                   Topic.forum_id == self.id).\
            count()
        self.topic_count = topic_count
        self.post_count = post_count

        if last_post:
            self.update_last_post()

        self.save()
        return self

    def save(self, groups=None):
        """Saves a forum

        :param moderators: If given, it will update the moderators in this
                           forum with the given iterable of user objects.
        :param groups: A list with group objects.
        """
        if self.id:
            db.session.merge(self)
        else:
            if groups is None:
                # importing here because of circular dependencies
                from flaskbb.user.models import Group
                self.groups = Group.query.order_by(Group.name.asc()).all()
            db.session.add(self)

        db.session.commit()
        return self

    def delete(self, users=None):
        """Deletes forum. If a list with involved user objects is passed,
        it will also update their post counts

        :param users: A list with user objects
        """
        # Delete the forum
        db.session.delete(self)
        db.session.commit()

        # Delete the entries for the forum in the ForumsRead and TopicsRead
        # relation
        ForumsRead.query.filter_by(forum_id=self.id).delete()
        TopicsRead.query.filter_by(forum_id=self.id).delete()

        # Update the users post count
        if users:
            users_list = []
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                users_list.append(user)
            db.session.add_all(users_list)
            db.session.commit()

        return self

    def move_topics_to(self, topics):
        """Moves a bunch a topics to the forum. Returns ``True`` if all
        topics were moved successfully to the forum.

        :param topics: A iterable with topic objects.
        """
        status = False
        for topic in topics:
            status = topic.move(self)
        return status

    # Classmethods
    @classmethod
    def get_forum(cls, forum_id, user):
        """Returns the forum and forumsread object as a tuple for the user.

        :param forum_id: The forum id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if user.is_authenticated:
            forum, forumsread = Forum.query.\
                filter(Forum.id == forum_id).\
                options(db.joinedload("category")).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(ForumsRead).\
                first_or_404()
        else:
            forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
            forumsread = None

        return forum, forumsread

    @classmethod
    def get_topics(cls, forum_id, user, page=1, per_page=20):
        """Get the topics for the forum. If the user is logged in,
        it will perform an outerjoin for the topics with the topicsread and
        forumsread relation to check if it is read or unread.

        :param forum_id: The forum id
        :param user: The user object
        :param page: The page whom should be loaded
        :param per_page: How many topics per page should be shown
        """
        if user.is_authenticated:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                add_entity(TopicsRead).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)
        else:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)

            topics.items = [(topic, None) for topic in topics.items]

        return topics