6dc6bfdfd1389e30c77fc667f44682e59dfda5aa
[zzz-spline-forum.git] / splinext / forum / model / __init__.py
1 from datetime import datetime
2
3 from sqlalchemy import and_, Column, ForeignKey, Index
4 from sqlalchemy.orm import relation
5 from sqlalchemy.types import DateTime, Enum, Integer, Unicode
6
7 from spline.model.meta import TableBase
8 from splinext.users import model as users_model
9
10
11 ### Core
12
13 class Forum(TableBase):
14 __tablename__ = 'forums'
15 id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
16 name = Column(Unicode(133), nullable=False)
17 description = Column(Unicode(1024), nullable=False, default=u'', server_default=u'')
18 access_level = Column(Enum(u'normal', u'soapbox', u'archive', name='forums_access_level'), nullable=False, default=u'normal', server_default=u'normal')
19
20 def can_create_thread(self, user):
21 """Returns True ifff the named user can make a new thread in this
22 forum.
23 """
24 if not user.can('forum:create-thread'):
25 return False
26
27 if self.access_level == u'soapbox' and \
28 not user.can('forum:override-soapbox'):
29 return False
30
31 if self.access_level == u'archive' and \
32 not user.can('forum:override-archive'):
33 return False
34
35 return True
36
37 class Thread(TableBase):
38 __tablename__ = 'threads'
39 id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
40 forum_id = Column(Integer, ForeignKey('forums.id'), nullable=False)
41 icon = Column(Unicode(32), nullable=True)
42 subject = Column(Unicode(133), nullable=False)
43 post_count = Column(Integer, nullable=False, default=0, index=True)
44
45 def specific_post(self, position):
46 """Returns post number `position` in this thread.
47
48 Positions are one-indexed. Negative indexes are allowed.
49 """
50
51 # Handle negative indexes
52 if position < 0:
53 position = self.post_count + position + 1
54
55 return self.posts.filter_by(position=position).one()
56
57 def can_create_post(self, user):
58 """Returns True ifff the named user can make a new post in this thread.
59 """
60 if not user.can('forum:create-post'):
61 return False
62
63 if self.forum.access_level == u'archive' and \
64 not user.can('forum:override-archive'):
65 return False
66
67 return True
68
69 class Post(TableBase):
70 __tablename__ = 'posts'
71 id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
72 thread_id = Column(Integer, ForeignKey('threads.id'), nullable=False)
73 position = Column(Integer, nullable=False)
74 author_user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
75 posted_time = Column(DateTime, nullable=False, index=True, default=datetime.now)
76 raw_content = Column(Unicode(5120), nullable=False)
77 content = Column(Unicode(5120), nullable=False)
78
79 Index('thread_position', Post.thread_id, Post.position, unique=True)
80
81
82 # XXX sort by time, how?
83 Forum.threads = relation(Thread, order_by=Thread.id.desc(), lazy='dynamic', backref='forum')
84
85 Thread.posts = relation(Post, order_by=Post.position.asc(), lazy='dynamic', backref='thread')
86 Thread.first_post = relation(Post, primaryjoin=and_(Post.thread_id == Thread.id, Post.position == 1), uselist=False)
87 # XXX THIS WILL NEED TO CHANGE when posts can be deleted! Or change what 'position' means
88 Thread.last_post = relation(Post, primaryjoin=and_(Post.thread_id == Thread.id, Post.position == Thread.post_count), uselist=False)
89
90 Post.author = relation(users_model.User, backref='posts')