File: Posting.py

package info (click to toggle)
squishdot 1.3.0-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 896 kB
  • ctags: 349
  • sloc: python: 2,313; makefile: 56; sh: 54
file content (329 lines) | stat: -rw-r--r-- 11,697 bytes parent folder | download
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
##############################################################################  
#   
# This software is released under the Zope Public License (ZPL) Version 1.0
#
# Copyright (c) Digital Creations.  All rights reserved.  
# Portions Copyright (c) 1999 by Butch Landingin.
# Portions Copyright (c) 2000-2001 by Chris Withers.
#   
##############################################################################  
     
__version__='$Revision: 1.27 $'[11:-2]     
from Globals import Persistent     
from Globals import HTMLFile
import Globals
from intSet import intSet     
from Squishfile import Squishfile     
from Acquisition import Implicit
from time import time, localtime, strftime, gmtime
from string import strip,split,join
from string import lower,atoi
from urllib import quote, unquote
from Utility import CRLF, tagRegex, Stack, doAddPosting, getitem
from SquishPermissions import ModeratePostings,AddPostings,View
from stripogram import html2safehtml
from DateTime import DateTime
from OFS.Traversable import Traversable

from AccessControl import ClassSecurityInfo

class Posting(Persistent, Implicit, Traversable):     
    """Squishdot Posting"""
    
    security = ClassSecurityInfo()

    security.setDefaultAccess("allow")
    
    meta_type='Posting'     
    icon   ='misc_/Squishdot/posting_img'     
    root=0
    
    # Default encoding for old postings
    encoding = 'HTML'
    # fields in this type of posting
    _fields=[]
    
    manage_editForm=HTMLFile('editPostingForm', globals())
    
    security.declareProtected(ModeratePostings, 'manage_editForm')
    if hasattr(manage_editForm,'_setName'):
        manage_editForm._setName('manage_editForm')
    
    # Aliases for manage_editForm
    manage         =manage_editForm     
    manage_main    =manage_editForm

    security.declarePrivate('__init__')
    def __init__(self, id, thread,level,reviewed):     
        self.id      =str(id)     
        self.ids     =intSet()     
        self.thread  =thread
        self.created =id     
        self.modified=id     
        self.level =level
        self.revsub  =0     
        self.reply_cnt =0
        self.reviewed=reviewed
     
    security.declarePrivate('index')
    def index(self):
        # index this posting is the Squishdot site that contains it.
        # getPhysicalPath should be acquired from this posting's SquishSite
        self.catalog_object(self,join(self.getPhysicalPath(),'/'))

        # recatalog all the postings in our thread path
        if self.thread:
            self.data[self.thread[-1]].__of__(self).index()
        
    security.declareProtected(View, 'getFields')
    def getFields(self):
        """Return a list of fields that this posting has"""
        return self._fields

    security.declareProtected(View, 'getThread')
    def getThread(self, index):
        """A better abstaction rather than accessing the list directly"""
        return self.thread[index]

    security.declarePublic('__len__')
    def __len__(self): return 1     
     
    security.declareProtected(View, '__getitem__')
    __getitem__ = getitem     
     
    security.declarePrivate('setItem')
    def setItem(self,id,obj):     
        # Make sure the object we store is not wrapped with     
        # an acquisition wrapper, since we will be wrapping     
        # it again manually in __getitem__ when it is needed.
        bobj = getattr(obj,'aq_base',obj)
        self.ids.insert(id)     
        self.data[id]=bobj

        #index the new posting
        obj.index()
     
    security.declarePrivate('textToSearch')
    def textToSearch(self):
        # returns the text to search for a ZCatalog
        text=''
        for line in self.body:
            # strip out HTML and append a newline to each line.
            text = text+tagRegex.sub("",line)+'\n'
        return text

    security.declareProtected(View, 'date_posted')
    def date_posted(self,fmstr='%A %B %d, @%I:%M%p'):     
        # """ date when article was posted """     
        ltime = localtime(self.created)         
        return strftime(fmstr,ltime)     
             
    # deprecated methods
    date_created = time_created = date_posted

    security.declareProtected(View, 'date')
    def date(self):
        """return the date of creation for indexing purposes"""
        return DateTime(self.created)
    
    security.declareProtected(View, 'body_len')
    def body_len(self,divisor=None):     
        # """ total body length of text """    
        tlen = 0     
        if not self.body:     
            tlen = 0    
        else:    
            for line in self.body:     
                tlen = tlen + len(line)    
    
        if divisor is None:    
            if tlen == 0:    
                return ''    
            if tlen > 51200:     
                tlen = tlen / 1024     
                return str(tlen) + ' Kb'     
            else:     
                return str(tlen) + ' bytes'     
    
        if divisor < 1:    
            return tlen    
        else:    
            return tlen/divisor    
                    
    security.declareProtected(ModeratePostings, 'postingValues')
    def postingValues(self):     
        # """ return all replies """     
        return self.data_map(self.ids)     
     
    security.declareProtected(View, 'tpId')
    def tpId(self):     
        return self.id     
     
    security.declareProtected(View, 'tpURL')
    def tpURL(self):     
        return self.id     
     
    security.declareProtected(View, 'this')
    def this(self): return self     
         
    security.declareProtected(View, 'has_items')
    def has_items(self):     
        return len(self.ids)     
     
    security.declarePrivate('sub_ids')
    def sub_ids(self,ids):     
        map(ids.insert, self.ids)     
        for item in self.data_map(self.ids):     
            ids=item.sub_ids(ids)     
        return ids     
     
    security.declareProtected(View, 'desc_items')
    def desc_items(self):     
        # """ return latest list of replies """     
        mlist = []     
        mstack = Stack()     
        if self.has_items():     
            plist = []     
            for id in self.ids:     
                plist.append(id)     
            plist.reverse()     
            for id in plist:     
                mstack.push(id)     
            while not mstack.isEmpty():     
                item_id = mstack.pop()     
                item = self.data[item_id]     
                mlist.append(item)     
                if item.has_items():     
                    plist = []     
                    for id in item.ids:     
                        plist.append(id)     
                    plist.reverse()     
                    for id in plist:     
                        mstack.push(id)     
        return map((lambda x, p=self: x.__of__(p)), mlist)     
     
    security.declareProtected(View, 'attachment')
    def attachment(self):     
        # """ file attachment """     
        file=self.file
        return file and (file,) or None     
     
    security.declareProtected(AddPostings, 'suggest_title')
    def suggest_title(self):     
        # """ suggested title of reply """     
        t=self.title     
        return (lower(t[:3])=='re:') and t or 'Re: %s' % t     
     
    security.declareProtected(View, 'thread_path')
    def thread_path(self):     
        return join(map(lambda x: '/%s' % x, self.thread), '')     
     
    security.declareProtected(View, 'index_html')
    def index_html(self,REQUEST):     
        """ squishdot article main page (the read more page) """    
        return self.posting_html(self,REQUEST)     
     
    security.declarePrivate('doNotify')
    def doNotify(self, msg, REQUEST):     
        # """ sends mail to notify person being replied to """     
        if self.notify and self.email:
            self.sendEmail(msg,self.email,REQUEST)

    security.declarePublic('cancelNotify')
    def cancelNotify(self, REQUEST):     
        """ cancels email notification of replies """     
        self.notify=''     
        return self.showMessage(self, REQUEST=REQUEST, title='Cancelled Notification',     
                             message='You will no longer be notified of replies to this message',     
                             action=self.absolute_url()     
                            )     
     
    security.declareProtected(AddPostings, 'dummyPosting')
    def dummyPosting(self):
        """ returns a dummy posting for the previewPosting method """
        return Comment(0,[],0,1).__of__(self)

    security.declareProtected(AddPostings, 'addPosting')
    def addPosting(self, file='', REQUEST=None,RESPONSE=None):     
        """ add a Comment """
        return doAddPosting(self,file,REQUEST,RESPONSE,
                            moderated='mod_comment',
                            message  ='Your reply has been posted',
                            klass    =Comment)
     
    security.declareProtected(ModeratePostings, 'edit')
    def edit(self,REQUEST=None,RESPONSE=None,delete_attachment=None,new_attachment='',reviewed=''):     
        """ edit replies """     
    
        processed,message=self.validatePosting(raw=REQUEST)
        
        if message is not None:
            return self.showError(self, values=processed,title='Data Missing',     
                                  message=message,     
                                  action=self.REQUEST.HTTP_REFERER
                                  )     

        for field in self.getFields():
            value = processed.get(field,'')
            if field in ['body','summary']:
                value=split(CRLF.sub('\n',value),'\n')
            setattr(self,field,value)
            
        self.notify  = processed['notify']
        self.encoding= processed['encoding']

        have_new_file = (hasattr(new_attachment,'filename') and new_attachment.filename)
     
        if delete_attachment or have_new_file:
            # delete the old file
            try:
                delattr(self,self.aq_base.file.file_name())
            except AttributeError:
                pass
            self.file=''

        if have_new_file:
            # store the new file
            file=Squishfile(new_attachment)     
            setattr(self,file.file_name(),file)     
            self.file=file

        if self.mod_comment and (not self.reviewed) and reviewed:     
            self.set_reviewed(self)
     
        # re-catalog this posting
        self.index()
     
        # should only get here if someone is editing a posting during moderation
        if RESPONSE:     
            RESPONSE.redirect(self.REQUEST.HTTP_REFERER)     
     
    # Used to display the body of the posting with the appropriate formatting    
    security.declareProtected(View, 'showBody')
    def showBody(self):
        return self.render(self.body,self.encoding)
     
    # Return the plain text body of a posting suitable for mailing...
    security.declareProtected(View, 'plain_text')
    def plain_text(self):
        if self.encoding == 'Plain':
            return join(self.body,'\n')
        else:
            return self.html2text(join(self.body,''))
        
    security.declarePublic('getId')
    def getId(self):
        return self.id

Globals.InitializeClass(Posting)

# Comment has to be in this file to stop import infinite recursion
class Comment(Posting):     
    """ Kindof small, isn't it ;-)"""     
    meta_type  ='Comment'     
    icon       ='misc_/Squishdot/comment_img'
    _fields    =['title','author','body','email']
     
Globals.InitializeClass(Comment)