summaryrefslogtreecommitdiff
path: root/quoins/blog_model.py
diff options
context:
space:
mode:
Diffstat (limited to 'quoins/blog_model.py')
-rw-r--r--quoins/blog_model.py274
1 files changed, 274 insertions, 0 deletions
diff --git a/quoins/blog_model.py b/quoins/blog_model.py
new file mode 100644
index 0000000..9a84010
--- /dev/null
+++ b/quoins/blog_model.py
@@ -0,0 +1,274 @@
1# Quoins - A TurboGears blogging system.
2# Copyright (C) 2008-2009 James E. Blair <corvus@gnu.org>
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17from sqlalchemy import *
18from sqlalchemy.orm import mapper, relation
19from sqlalchemy import Table, ForeignKey, Column
20from sqlalchemy.types import Integer, Unicode
21#from sqlalchemy.orm import relation, backref
22import tg
23from datetime import datetime
24
25#from bonglonglong.model import DeclarativeBase, metadata, DBSession
26metadata = tg.config['model'].metadata
27DBSession = tg.config['model'].DBSession
28
29TGUser = tg.config['sa_auth']['user_class']
30tguser_table = TGUser.__table__
31
32# Blog schema
33
34blog_table = Table('blog', metadata,
35 Column('id', Integer, primary_key=True),
36 Column('title', Unicode(255)),
37 Column('subtitle', Unicode(255)),
38 Column('allow_comments', Boolean, default=True, nullable=False),
39)
40
41post_table = Table('post', metadata,
42 Column('id', Integer, primary_key=True),
43 Column('blog_id', Integer, ForeignKey('blog.id',
44 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
45 Column('user_id', Integer, ForeignKey(tguser_table.c.user_id,
46 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
47 Column('title', Unicode(255)),
48 Column('teaser', TEXT),
49 Column('body', TEXT),
50 Column('created', DateTime, nullable=False, default=datetime.now),
51 Column('allow_comments', Boolean, nullable=False),
52 Column('published', Boolean, nullable=False, default=False, index=True),
53)
54
55media_table = Table('media', metadata,
56 Column('id', Integer, primary_key=True),
57 Column('post_id', Integer, ForeignKey('post.id',
58 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
59 Column('name', String(255), nullable=False, index=True),
60 Column('mimetype', String(255)),
61 Column('data', BLOB),
62 UniqueConstraint('post_id', 'name'),
63)
64
65tag_table = Table('tag', metadata,
66 Column('id', Integer, primary_key=True),
67 Column('name', Unicode(255)),
68)
69
70post_tag_table = Table('post_tag', metadata,
71 Column('id', Integer, primary_key=True),
72 Column('post_id', Integer, ForeignKey('post.id',
73 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
74 Column('tag_id', Integer, ForeignKey('tag.id',
75 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
76)
77
78comment_table = Table('comment', metadata,
79 Column('id', Integer, primary_key=True),
80 Column('parent_comment_id', Integer, ForeignKey('comment.id',
81 onupdate="CASCADE", ondelete="CASCADE"), index=True),
82 Column('post_id', Integer, ForeignKey('post.id',
83 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
84 Column('user_id', Integer, ForeignKey(tguser_table.c.user_id,
85 onupdate="CASCADE"), index=True),
86 Column('name', Unicode(255)),
87 Column('openid', String(255)),
88 Column('url', String(255)),
89 Column('title', Unicode(255)),
90 Column('body', TEXT),
91 Column('created', DateTime, nullable=False, default=datetime.now),
92 Column('approved', Boolean, nullable=False, default=False, index=True),
93)
94
95linkback_table = Table('linkback', metadata,
96 Column('id', Integer, primary_key=True),
97 Column('post_id', Integer, ForeignKey('post.id',
98 onupdate="CASCADE", ondelete="CASCADE"), nullable=False, index=True),
99 Column('url', String(255)),
100 Column('title', Unicode(255)),
101 Column('body', Unicode(255)),
102 Column('name', Unicode(255)),
103 Column('created', DateTime, nullable=False, default=datetime.now),
104)
105
106class Blog(object):
107 def get_tags(self):
108 # XXX this assumes that only one blog exists in the schema
109 return DBSession.query(Tag).all()
110 tags = property(get_tags)
111
112 def get_users(self):
113 return [user for user in TGUser.query.all() if 'blog-post' in [p.permission_name for p in user.permissions]]
114 authors = property(get_users)
115
116 def getPostsByTag(self, tagname):
117 posts = Post.query.filter(and_(post_table.c.blog_id==self.id,
118 post_table.c.id==post_tag_table.c.post_id,
119 post_tag_table.c.tag_id==tag_table.c.id,
120 tag_table.c.name==tagname,
121 post_table.c.published==True)).all()
122 return posts
123
124 def getYears(self):
125 years = {}
126 for p in self.published_posts:
127 x = years.get(p.created.year, 0)
128 years[p.created.year] = x+1
129 years = years.items()
130 years.sort(lambda a,b: cmp(a[0],b[0]))
131 years.reverse()
132 return years
133
134 def getPostsByDate(self, year=None, month=None, day=None):
135 posts = []
136 for p in self.published_posts:
137 if year and p.created.year!=year:
138 continue
139 if month and p.created.month!=month:
140 continue
141 if day and p.created.day!=day:
142 continue
143 posts.append(p)
144 return posts
145
146 def getPostsByAuthor(self, name):
147 posts = []
148 for p in self.published_posts:
149 if p.author.user_name==name:
150 posts.append(p)
151 return posts
152
153
154class Post(object):
155 def get_teaser_or_body(self):
156 if self.teaser: return self.teaser
157 return self.body
158 short_body = property(get_teaser_or_body)
159
160 def get_teaser_and_body(self):
161 if self.teaser:
162 return self.teaser + self.body
163 return self.body
164 long_body = property(get_teaser_and_body)
165
166 def tag(self, name):
167 t = Tag.query.filter_by(name=name).first()
168 if not t:
169 t = Tag()
170 t.name = name
171 self.tags.append(t)
172
173 def untag(self, name):
174 t = Tag.query.filter_by(name=name).first()
175 if len(t.posts)<2:
176 session.delete(t)
177 self.tags.remove(t)
178
179 def get_comments_and_linkbacks(self, trackbacks=1, pingbacks=0):
180 objects = self.approved_comments[:]
181 for x in self.linkbacks:
182 if (trackbacks and x.body) or (pingbacks and not x.body):
183 objects.append(x)
184 objects.sort(lambda a,b: cmp(a.created, b.created))
185 return objects
186 comments_and_links = property(get_comments_and_linkbacks)
187
188
189class Media(object):
190 pass
191
192class Tag(object):
193 pass
194
195class BaseComment(object):
196 def get_author_name(self):
197 if hasattr(self, 'author') and self.author:
198 return self.author.display_name
199 if self.name:
200 return self.name
201 return 'Anonymous'
202 author_name = property(get_author_name)
203
204 def get_author_url(self):
205 if hasattr(self, 'author') and self.author:
206 return self.author.url
207 if self.url:
208 return self.url
209 return None
210 author_url = property(get_author_url)
211
212class Comment(BaseComment):
213 pass
214
215class LinkBack(BaseComment):
216 pass
217
218mapper(Blog, blog_table,
219 properties=dict(posts=relation(Post,
220 order_by=desc(post_table.c.created)),
221 published_posts=relation(Post,
222 primaryjoin=and_(post_table.c.blog_id==blog_table.c.id,
223 post_table.c.published==True),
224 order_by=desc(post_table.c.created)),
225 unpublished_posts=relation(Post,
226 primaryjoin=and_(post_table.c.blog_id==blog_table.c.id,
227 post_table.c.published==False),
228 order_by=desc(post_table.c.created)),
229 unapproved_comments=relation(Comment, secondary=post_table,
230 primaryjoin=post_table.c.blog_id==blog_table.c.id,
231 secondaryjoin=and_(comment_table.c.post_id==post_table.c.id,
232 comment_table.c.approved==False))))
233
234mapper(Tag, tag_table,
235 order_by=desc(tag_table.c.name))
236
237mapper(Media, media_table,
238 properties=dict(post=relation(Post)))
239
240mapper(Post, post_table,
241 order_by=desc(post_table.c.created),
242 properties=dict(blog=relation(Blog),
243 author=relation(TGUser),
244 tags=relation(Tag, secondary=post_tag_table, backref='posts'),
245 comments=relation(Comment, cascade="all, delete-orphan"),
246 approved_comments=relation(Comment,
247 primaryjoin=and_(comment_table.c.post_id==post_table.c.id,
248 comment_table.c.approved==True)),
249 unapproved_comments=relation(Comment,
250 primaryjoin=and_(comment_table.c.post_id==post_table.c.id,
251 comment_table.c.approved==False)),
252 media=relation(Media),
253 linkbacks=relation(LinkBack)))
254
255mapper(Comment, comment_table,
256 order_by=comment_table.c.created,
257 properties=dict(post=relation(Post),
258 author=relation(TGUser)))
259
260mapper(LinkBack, linkback_table,
261 properties=dict(post=relation(Post)))
262
263def init_model(engine):
264 """Call me before using any of the tables or classes in the model."""
265
266 maker = sessionmaker(autoflush=True, autocommit=False,
267 extension=ZopeTransactionExtension())
268 DBSession = scoped_session(maker)
269
270 DeclarativeBase = declarative_base()
271 metadata = DeclarativeBase.metadata
272
273 DBSession.configure(bind=engine)
274 return DBSession