|
a/bitergiametrics/model/blog.py |
|
b/bitergiametrics/model/blog.py |
|
... |
|
... |
14 |
from allura.lib import utils, patience
|
14 |
from allura.lib import utils, patience
|
15 |
|
15 |
|
16 |
config = utils.ConfigProxy(
|
16 |
config = utils.ConfigProxy(
|
17 |
common_suffix='forgemail.domain')
|
17 |
common_suffix='forgemail.domain')
|
18 |
|
18 |
|
19 |
class BlogPostSnapshot(M.Snapshot):
|
19 |
class MetricSnapshot(M.Snapshot):
|
20 |
class __mongometa__:
|
20 |
class __mongometa__:
|
21 |
name='blog_post_snapshot'
|
21 |
name='metric_snapshot'
|
22 |
type_s='Blog Post Snapshot'
|
22 |
type_s='Metric Snapshot'
|
23 |
|
23 |
|
24 |
def original(self):
|
24 |
def original(self):
|
25 |
return BlogPost.query.get(_id=self.artifact_id)
|
25 |
return Metric.query.get(_id=self.artifact_id)
|
26 |
|
26 |
|
27 |
def shorthand_id(self):
|
27 |
def shorthand_id(self):
|
28 |
return '%s#%s' % (self.original().shorthand_id(), self.version)
|
28 |
return '%s#%s' % (self.original().shorthand_id(), self.version)
|
29 |
|
29 |
|
30 |
def url(self):
|
30 |
def url(self):
|
31 |
return self.original().url() + '?version=%d' % self.version
|
31 |
return self.original().url() + '?version=%d' % self.version
|
32 |
|
32 |
|
33 |
def index(self):
|
33 |
def index(self):
|
34 |
result = super(BlogPostSnapshot, self).index()
|
34 |
result = super(MetricSnapshot, self).index()
|
35 |
result.update(
|
35 |
result.update(
|
36 |
title_s='Version %d of %s' % (
|
36 |
title_s='Version %d of %s' % (
|
37 |
self.version, self.original().shorthand_id()),
|
37 |
self.version, self.original().shorthand_id()),
|
38 |
type_s=self.type_s,
|
38 |
type_s=self.type_s,
|
39 |
text=self.data.text)
|
39 |
text=self.data.text)
|
|
... |
|
... |
43 |
def html_text(self):
|
43 |
def html_text(self):
|
44 |
"""A markdown processed version of the page text"""
|
44 |
"""A markdown processed version of the page text"""
|
45 |
return g.markdown_wiki.convert(self.data.text)
|
45 |
return g.markdown_wiki.convert(self.data.text)
|
46 |
|
46 |
|
47 |
@property
|
47 |
@property
|
48 |
def attachments(self):
|
|
|
49 |
return self.original().attachments
|
|
|
50 |
|
|
|
51 |
@property
|
|
|
52 |
def email_address(self):
|
48 |
def email_address(self):
|
53 |
return self.original().email_address
|
49 |
return self.original().email_address
|
54 |
|
50 |
|
55 |
class BlogPost(M.VersionedArtifact):
|
51 |
class MetricPost(M.VersionedArtifact):
|
56 |
class __mongometa__:
|
52 |
class __mongometa__:
|
57 |
name='blog_post'
|
53 |
name='metric'
|
58 |
history_class = BlogPostSnapshot
|
54 |
history_class = MetricSnapshot
|
59 |
unique_indexes = [ ('project_id', 'app_config_id', 'slug') ]
|
55 |
unique_indexes = [ ('project_id', 'app_config_id', 'slug') ]
|
60 |
type_s = 'Blog Post'
|
56 |
type_s = 'Metric'
|
61 |
|
57 |
|
62 |
title = FieldProperty(str, if_missing='Untitled')
|
58 |
title = FieldProperty(str, if_missing='Untitled')
|
63 |
text = FieldProperty(str, if_missing='')
|
59 |
text = FieldProperty(str, if_missing='')
|
64 |
timestamp = FieldProperty(datetime, if_missing=datetime.utcnow)
|
60 |
timestamp = FieldProperty(datetime, if_missing=datetime.utcnow)
|
65 |
slug = FieldProperty(str)
|
61 |
slug = FieldProperty(str)
|
66 |
state = FieldProperty(schema.OneOf('draft', 'published'), if_missing='draft')
|
62 |
state = FieldProperty(schema.OneOf('draft', 'published'), if_missing='draft')
|
67 |
neighborhood_id = ForeignIdProperty('Neighborhood', if_missing=None)
|
63 |
neighborhood_id = ForeignIdProperty('Neighborhood', if_missing=None)
|
68 |
|
64 |
|
69 |
def author(self):
|
65 |
def author(self):
|
70 |
'''The author of the first snapshot of this BlogPost'''
|
66 |
'''The author of the first snapshot of this Metric'''
|
71 |
return M.User.query.get(_id=self.get_version(1).author.id) or M.User.anonymous()
|
67 |
return M.User.query.get(_id=self.get_version(1).author.id) or M.User.anonymous()
|
72 |
|
68 |
|
73 |
def _get_date(self):
|
69 |
def _get_date(self):
|
74 |
return self.timestamp.date()
|
70 |
return self.timestamp.date()
|
75 |
def _set_date(self, value):
|
71 |
def _set_date(self, value):
|
|
... |
|
... |
84 |
|
80 |
|
85 |
@property
|
81 |
@property
|
86 |
def html_text(self):
|
82 |
def html_text(self):
|
87 |
return g.markdown.convert(self.text)
|
83 |
return g.markdown.convert(self.text)
|
88 |
|
84 |
|
89 |
@property
|
|
|
90 |
def html_text_preview(self):
|
|
|
91 |
"""Return an html preview of the BlogPost text.
|
|
|
92 |
|
|
|
93 |
Truncation happens at paragraph boundaries to avoid chopping markdown
|
|
|
94 |
in inappropriate places.
|
|
|
95 |
|
|
|
96 |
If the entire post is one paragraph, the full text is returned.
|
|
|
97 |
If the entire text is <= 400 chars, the full text is returned.
|
|
|
98 |
Else, at least 400 chars are returned, rounding up to the nearest
|
|
|
99 |
whole paragraph.
|
|
|
100 |
|
|
|
101 |
If truncation occurs, a hyperlink to the full text is appended.
|
|
|
102 |
|
|
|
103 |
"""
|
|
|
104 |
# Splitting on spaces or single lines breaks isn't sufficient as some
|
|
|
105 |
# markup can span spaces and single line breaks. Converting to HTML
|
|
|
106 |
# first and *then* truncating doesn't work either, because the
|
|
|
107 |
# ellipsis tag ends up orphaned from the main text.
|
|
|
108 |
ellipsis = '... [read more](%s)' % self.url()
|
|
|
109 |
paragraphs = self.text.replace('\r','').split('\n\n')
|
|
|
110 |
total_length = 0
|
|
|
111 |
for i, p in enumerate(paragraphs):
|
|
|
112 |
total_length += len(p)
|
|
|
113 |
if total_length >= 400:
|
|
|
114 |
break
|
|
|
115 |
text = '\n\n'.join(paragraphs[:i+1])
|
|
|
116 |
return g.markdown.convert(text + (ellipsis if i + 1 < len(paragraphs)
|
|
|
117 |
else ''))
|
|
|
118 |
|
85 |
|
119 |
@property
|
86 |
@property
|
120 |
def email_address(self):
|
87 |
def email_address(self):
|
121 |
domain = '.'.join(reversed(self.app.url[1:-1].split('/'))).replace('_', '-')
|
88 |
domain = '.'.join(reversed(self.app.url[1:-1].split('/'))).replace('_', '-')
|
122 |
return '%s@%s%s' % (self.title.replace('/', '.'), domain, config.common_suffix)
|
89 |
return '%s@%s%s' % (self.title.replace('/', '.'), domain, config.common_suffix)
|
|
... |
|
... |
143 |
|
110 |
|
144 |
def shorthand_id(self):
|
111 |
def shorthand_id(self):
|
145 |
return self.slug
|
112 |
return self.slug
|
146 |
|
113 |
|
147 |
def index(self):
|
114 |
def index(self):
|
148 |
result = super(BlogPost, self).index()
|
115 |
result = super(Metric, self).index()
|
149 |
result.update(
|
116 |
result.update(
|
150 |
title_s=self.slug,
|
117 |
title_s=self.slug,
|
151 |
type_s=self.type_s,
|
118 |
type_s=self.type_s,
|
152 |
state_s=self.state,
|
119 |
state_s=self.state,
|
153 |
snippet_s='%s: %s' % (self.title, h.text.truncate(self.text, 200)),
|
120 |
snippet_s='%s: %s' % (self.title, h.text.truncate(self.text, 200)),
|
|
... |
|
... |
158 |
HC = self.__mongometa__.history_class
|
125 |
HC = self.__mongometa__.history_class
|
159 |
return HC.query.find({'artifact_id':self._id, 'version':int(version)}).one()
|
126 |
return HC.query.find({'artifact_id':self._id, 'version':int(version)}).one()
|
160 |
|
127 |
|
161 |
def commit(self):
|
128 |
def commit(self):
|
162 |
self.subscribe()
|
129 |
self.subscribe()
|
163 |
super(BlogPost, self).commit()
|
130 |
super(Metric, self).commit()
|
164 |
if self.version > 1:
|
|
|
165 |
v1 = self.get_version(self.version-1)
|
|
|
166 |
v2 = self
|
|
|
167 |
la = [ line + '\n' for line in v1.text.splitlines() ]
|
|
|
168 |
lb = [ line + '\n' for line in v2.text.splitlines() ]
|
|
|
169 |
diff = ''.join(patience.unified_diff(
|
|
|
170 |
la, lb,
|
|
|
171 |
'v%d' % v1.version,
|
|
|
172 |
'v%d' % v2.version))
|
|
|
173 |
description = diff
|
|
|
174 |
if v1.state != 'published' and v2.state == 'published':
|
|
|
175 |
M.Feed.post(self, self.title, self.text, author=self.author())
|
|
|
176 |
description = self.text
|
|
|
177 |
subject = '%s created post %s' % (
|
|
|
178 |
c.user.username, self.title)
|
|
|
179 |
elif v1.title != v2.title:
|
|
|
180 |
subject = '%s renamed post %s to %s' % (
|
|
|
181 |
c.user.username, v2.title, v1.title)
|
|
|
182 |
else:
|
|
|
183 |
subject = '%s modified post %s' % (
|
|
|
184 |
c.user.username, self.title)
|
|
|
185 |
else:
|
|
|
186 |
description = self.text
|
131 |
description = self.text
|
187 |
subject = '%s created post %s' % (
|
132 |
subject = '%s created metric %s' % (
|
188 |
c.user.username, self.title)
|
133 |
c.user.username, self.title)
|
189 |
if self.state == 'published':
|
|
|
190 |
M.Feed.post(self, self.title, self.text, author=self.author())
|
|
|
191 |
if self.state == 'published':
|
134 |
if self.state == 'published':
|
192 |
M.Notification.post(
|
135 |
M.Notification.post(
|
193 |
artifact=self, topic='metadata', text=description, subject=subject)
|
136 |
artifact=self, topic='metadata', text=description, subject=subject)
|
194 |
|
137 |
|
195 |
class Attachment(M.BaseAttachment):
|
|
|
196 |
ArtifactClass=BlogPost
|
|
|
197 |
class __mongometa__:
|
|
|
198 |
polymorphic_identity='BlogAttachment'
|
|
|
199 |
attachment_type=FieldProperty(str, if_missing='BlogAttachment')
|
|
|
200 |
|
|
|
201 |
|
|
|
202 |
Mapper.compile_all()
|
138 |
Mapper.compile_all() |