-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathComments.py
150 lines (122 loc) · 5.11 KB
/
Comments.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# PageCommentsSupport mixin
import sys, os, string, re, email, email.Errors
from mailbox import UnixMailbox
from urllib import quote
from cStringIO import StringIO
from AccessControl import getSecurityManager, ClassSecurityInfo
from App.Common import absattr
from DateTime import DateTime
from Globals import InitializeClass
import Permissions
from Regexps import fromlineexpr
from Utils import BLATHER, html_quote, DateTimeSyntaxError, \
stringBefore, stringBeforeAndIncluding, stringAfter, \
stringAfterAndIncluding, safe_hasattr
class PageCommentsSupport:
"""
I manage comments stored as rfc2822 messages in a wiki page.
This mixin class looks for comments in mbox/RFC2822 format in the page
text, and provides services for parsing and displaying them.
Everything above the first comment is considered the document part,
the rest of the page is considered the discussion part.
Prior to 0.30 we called this MessageSupport. When working with
comments, we represent them as email.Message.Message objects.
So that we can recognize comments/messages without extra markup or too
many false positives, we require each message to begin with BAW's "strict"
From line regexp from the python mailbox module.
See also Editing.py.
"""
security = ClassSecurityInfo()
# accessors
security.declareProtected(Permissions.View, 'supportsComments')
def supportsComments(self):
"""does this page parse embedded rfc2822 messages ?"""
return re.search(r'(?i)(msg)',self.pageTypeId()) is not None
security.declareProtected(Permissions.View, 'hasComments')
def hasComments(self):
"""does this page have one or more rfc2822-style comments ?"""
return self.messageCount() > 0
security.declareProtected(Permissions.View, 'commentCount')
def commentCount(self):
"""
The number of comments in this page.
"""
return len(re.findall(fromlineexpr,self.discussionPart()))
security.declareProtected(Permissions.View, 'documentPart')
def documentPart(self):
"""
This page's text from beginning up to the first message, if any.
"""
return re.split(fromlineexpr,self.text(),1)[0]
document = documentPart
security.declareProtected(Permissions.View, 'discussionPart')
def discussionPart(self):
"""
This page's text from the first comment to the end (or '').
"""
return stringAfterAndIncluding(fromlineexpr,self.text())
security.declareProtected(Permissions.View, 'mailbox')
def mailbox(self):
"""
Return the messages on this page as a Mailbox (iterator of Message)
"""
# from mailbox docs: this is defensive against ill-formed MIME
# messages in the mailbox, but you have to be prepared to receive
# the empty string from the mailbox's `next()' method
def msgfactory(fp):
try:
return email.message_from_file(fp)
except email.Errors.MessageParseError:
BLATHER('message parsing error in',self.id())
return ''
return UnixMailbox(StringIO(self.toencoded(self.text())), msgfactory)
def comments(self):
"""
Return this page's comments as a list of email Messages.
Warning, the email lib's Messages contain encoded text and you
must remember to convert their data to unicode when
appropriate.
"""
msgs = []
mbox = self.mailbox()
m = mbox.next()
while m is not None:
msgs.append(m)
m = mbox.next()
return msgs
# utilities
def fromLineFrom(self,email,date):
"""
Generate a conformant mbox From line from email and date strings.
(unless date is unparseable, in which case we omit that part)
"""
# "email" is in fact a real name or zwiki username - adapt it
email = re.sub(r'\s','',email) or 'unknown'
try:
d = DateTime(date)
return 'From %s %s %s %d %02d:%02d:%02d %s %d\n' % (
email,d.aDay(),d.aMonth(),d.day(),d.hour(),
d.minute(),d.second(),d.timezone(),d.year())
except (DateTimeSyntaxError,AttributeError,IndexError):
return 'From %s\n' % email
def messageIdFromTime(self,time):
"""
Generate a somewhat unique email message-id based on a DateTime
"""
msgid = time.strftime('%Y%m%d%H%M%S')+time.rfc822()[-5:]+'@'
if safe_hasattr(self,'REQUEST'):
msgid += re.sub(r'http://','',self.REQUEST.get('SERVER_URL',''))
msgid = '<%s>' % msgid
return msgid
security.declareProtected(Permissions.View, 'upgradeComments')
def upgradeComments(self,REQUEST=None):
"""Update the format of any comments on this page."""
pass # all current zwikis use the rfc2822 format
# backwards compatibility
supportsMessages = supportsComments
hasMessages = hasComments
messageCount = commentCount
messagesPart = discussionPart
messages = comments
upgradeMessages = upgradeComments
InitializeClass(PageCommentsSupport)