source: main/waeup.ikoba/trunk/src/waeup/ikoba/documents/document.py @ 12436

Last change on this file since 12436 was 12436, checked in by Henrik Bettermann, 10 years ago

Store public documents in right place.

  • Property svn:keywords set to Id
File size: 9.4 KB
Line 
1## $Id: document.py 12436 2015-01-10 06:42:35Z henrik $
2##
3## Copyright (C) 2014 Uli Fouquet & Henrik Bettermann
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 2 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, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19These are the public documents.
20"""
21import os
22import grok
23from grok import index
24from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
25from zope.event import notify
26from zope.component import getUtility
27from zope.component.interfaces import IFactory
28from zope.interface import implementedBy
29from zope.i18n import translate
30
31from waeup.ikoba.image import IkobaImageFile
32from waeup.ikoba.imagestorage import DefaultFileStoreHandler
33from waeup.ikoba.interfaces import (
34    IIkobaUtils, IObjectHistory,
35    IFileStoreNameChooser, IFileStoreHandler, IExtFileStore)
36from waeup.ikoba.interfaces import MessageFactory as _
37from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
38from waeup.ikoba.documents.interfaces import (
39    IDocument, IPublicDocument, IDocumentsUtils,
40    IPDFDocument, IHTMLDocument, IRESTDocument)
41
42class Document(grok.Container):
43    """This is a document.
44    """
45    grok.implements(IDocument)
46    grok.provides(IDocument)
47    grok.baseclass()
48
49    form_fields_interface = None
50
51    local_roles = [
52        'waeup.local.DocumentManager',
53        ]
54
55    user_id = None
56    state = None
57    translated_state = None
58    translated_class_name = None
59
60    @property
61    def history(self):
62        history = IObjectHistory(self)
63        return history
64
65    @property
66    def class_name(self):
67        return self.__class__.__name__
68
69    @property
70    def formatted_transition_date(self):
71        try:
72            return self.history.messages[-1].split(' - ')[0]
73        except IndexError:
74            return
75
76    @property
77    def connected_files(self):
78        return
79
80    @property
81    def is_verifiable(self):
82        return True, None
83
84    def setMD5(self):
85        """Determine md5 checksum of all files and store checksums as
86        document attributes.
87        """
88        return
89       
90class PublicDocumentBase(Document):
91    """This is a customer document baseclass.
92    """
93    grok.implements(IPublicDocument)
94    grok.provides(IPublicDocument)
95    grok.baseclass()
96
97    @property
98    def state(self):
99        state = IWorkflowState(self).getState()
100        return state
101
102    @property
103    def translated_state(self):
104        try:
105            TRANSLATED_STATES = getUtility(
106                IDocumentsUtils).TRANSLATED_DOCUMENT_STATES
107            return TRANSLATED_STATES[self.state]
108        except KeyError:
109            return
110
111    @property
112    def translated_class_name(self):
113        try:
114            DOCTYPES_DICT = getUtility(IDocumentsUtils).DOCTYPES_DICT
115            return DOCTYPES_DICT[self.class_name]
116        except KeyError:
117            return
118
119    def writeLogMessage(self, view, message):
120        ob_class = view.__implemented__.__name__.replace('waeup.ikoba.','')
121        self.__parent__.__parent__.logger.info(
122            '%s - %s - %s' % (ob_class, self.__name__, message))
123        return
124
125
126class PDFDocument(PublicDocumentBase):
127    """This is a  document for a single pdf upload file.
128    """
129    grok.implements(IPDFDocument)
130    grok.provides(IPDFDocument)
131
132    form_fields_interface = IPDFDocument
133
134PDFDocument = attrs_to_fields(PDFDocument)
135
136
137class HTMLDocument(PublicDocumentBase):
138    """This is a  document to render html-coded text.
139    """
140    grok.implements(IHTMLDocument)
141    grok.provides(IHTMLDocument)
142
143    form_fields_interface = IHTMLDocument
144
145    def __init__(self, *args, **kw):
146        super(HTMLDocument, self).__init__(*args, **kw)
147        self.html_dict = {}
148
149HTMLDocument = attrs_to_fields(HTMLDocument)
150
151
152class RESTDocument(PublicDocumentBase):
153    """This is a  document to render html-coded text.
154    """
155    grok.implements(IRESTDocument)
156    grok.provides(IRESTDocument)
157
158    form_fields_interface = IRESTDocument
159
160    def __init__(self, *args, **kw):
161        super(RESTDocument, self).__init__(*args, **kw)
162        self.html_dict = {}
163
164RESTDocument = attrs_to_fields(RESTDocument)
165
166
167class PDFDocumentFactory(grok.GlobalUtility):
168    """A factory for documents.
169    """
170    grok.implements(IFactory)
171    grok.name(u'waeup.PDFDocument')
172    title = u"Create a new PDF document.",
173    description = u"This factory instantiates new PDF documents."
174
175    def __call__(self, *args, **kw):
176        return PDFDocument(*args, **kw)
177
178    def getInterfaces(self):
179        return implementedBy(PDFDocument)
180
181
182class HTMLDocumentFactory(grok.GlobalUtility):
183    """A factory for HTML documents.
184    """
185    grok.implements(IFactory)
186    grok.name(u'waeup.HTMLDocument')
187    title = u"Create a new HTML document.",
188    description = u"This factory instantiates new HTML documents."
189
190    def __call__(self, *args, **kw):
191        return HTMLDocument(*args, **kw)
192
193    def getInterfaces(self):
194        return implementedBy(HTMLDocument)
195
196
197class RESTDocumentFactory(grok.GlobalUtility):
198    """A factory for REST documents.
199    """
200    grok.implements(IFactory)
201    grok.name(u'waeup.RESTDocument')
202    title = u"Create a new REST document.",
203    description = u"This factory instantiates new REST documents."
204
205    def __call__(self, *args, **kw):
206        return RESTDocument(*args, **kw)
207
208    def getInterfaces(self):
209        return implementedBy(RESTDocument)
210
211
212#: The file id marker for files
213DOCUMENT_FILE_STORE_NAME = 'file-document'
214
215
216class DocumentFileNameChooser(grok.Adapter):
217    """A file id chooser for :class:`Document` objects.
218
219    `context` is an :class:`Document` instance.
220
221    The delivered file_id contains the file id marker for
222    :class:`Document` objects in the central :class:`DocumentsContainer`.
223
224    This chooser is registered as an adapter providing
225    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
226
227    File store name choosers like this one are only convenience
228    components to ease the task of creating file ids for customer document
229    objects. You are nevertheless encouraged to use them instead of
230    manually setting up filenames for customer documents.
231
232    .. seealso:: :mod:`waeup.ikoba.imagestorage`
233
234    """
235
236    grok.context(IDocument)
237    grok.implements(IFileStoreNameChooser)
238
239    def checkName(self, name=None, attr=None):
240        """Check whether the given name is a valid file id for the context.
241
242        Returns ``True`` only if `name` equals the result of
243        :meth:`chooseName`.
244
245        """
246        return name == self.chooseName()
247
248    def chooseName(self, attr, name=None):
249        """Get a valid file id for customer document context.
250
251        *Example:*
252
253        For document with id 'd123'
254        with attr ``'nice_image.jpeg'`` this chooser would create:
255
256          ``'__file-document__nice_image_d123.jpeg'``
257
258        meaning that the nice image of this document would be
259        stored in the site-wide file storage in path:
260
261          ``nice_image_d123.jpeg``
262
263        """
264        basename, ext = os.path.splitext(attr)
265        doc_id = self.context.document_id
266        marked_filename = '__%s__%s_%s%s' % (
267            DOCUMENT_FILE_STORE_NAME,
268            basename, doc_id, ext)
269        return marked_filename
270
271
272class DocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
273    """ Document specific file handling.
274
275    This handler knows in which path in a filestore to store document
276    files and how to turn this kind of data into some (browsable)
277    file object.
278
279    It is called from the global file storage, when it wants to
280    get/store a file with a file id starting with
281    ``__file-document__`` (the marker string for customer files).
282
283    Like each other file store handler it does not handle the files
284    really (this is done by the global file store) but only computes
285    paths and things like this.
286    """
287    grok.implements(IFileStoreHandler)
288    grok.name(DOCUMENT_FILE_STORE_NAME)
289
290    def pathFromFileID(self, store, root, file_id):
291        """All document files are put in directory ``documents``.
292        """
293        marker, filename, basename, ext = store.extractMarker(file_id)
294        sub_root = os.path.join(root, 'documents')
295        return super(DocumentFileStoreHandler, self).pathFromFileID(
296            store, sub_root, basename)
297
298    def createFile(self, store, root, filename, file_id, file):
299        """Create a browsable file-like object.
300        """
301        # call super method to ensure that any old files with
302        # different filename extension are deleted.
303        file, path, file_obj = super(
304            DocumentFileStoreHandler, self).createFile(
305            store, root,  filename, file_id, file)
306        return file, path, IkobaImageFile(
307            file_obj.filename, file_obj.data)
308
309
310@grok.subscribe(IDocument, grok.IObjectAddedEvent)
311def handle_document_added(document, event):
312    """If a document is added the transition create is fired.
313    The latter produces a logging message.
314    """
315    if IWorkflowState(document).getState() is None:
316        IWorkflowInfo(document).fireTransition('create')
317    return
Note: See TracBrowser for help on using the repository browser.