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

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

Add RESTDocument.

  • Property svn:keywords set to Id
File size: 9.5 KB
RevLine 
[12004]1## $Id: document.py 12408 2015-01-06 09:15:21Z henrik $
[11982]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 document tickets.
20"""
[12204]21import os
[11982]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
[12204]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)
[11982]36from waeup.ikoba.interfaces import MessageFactory as _
37from waeup.ikoba.utils.helpers import attrs_to_fields, get_current_principal
[12032]38from waeup.ikoba.documents.interfaces import (
[12213]39    IDocument, IPublicDocument, IDocumentsUtils,
[12408]40    IPDFDocument, IHTMLDocument, IRESTDocument)
[11982]41
[12063]42class Document(grok.Container):
[11982]43    """This is a document.
44    """
45    grok.implements(IDocument)
46    grok.provides(IDocument)
47    grok.baseclass()
48
[12214]49    form_fields_interface = None
50
[12211]51    local_roles = [
52        'waeup.local.DocumentManager',
53        ]
54
[12126]55    user_id = None
[12213]56    state = None
57    translated_state = None
[12214]58    translated_class_name = None
[12126]59
[11983]60    @property
61    def history(self):
62        history = IObjectHistory(self)
63        return history
64
[12017]65    @property
[12056]66    def class_name(self):
[12053]67        return self.__class__.__name__
68
69    @property
70    def formatted_transition_date(self):
71        try:
[12210]72            return self.history.messages[-1].split(' - ')[0]
73        except IndexError:
[12053]74            return
[12160]75
76    @property
77    def connected_files(self):
78        return
79
[12168]80    @property
81    def is_verifiable(self):
82        return True, None
83
[12161]84    def setMD5(self):
85        """Determine md5 checksum of all files and store checksums as
86        document attributes.
[12160]87        """
88        return
[12053]89       
[12213]90class PublicDocumentBase(Document):
91    """This is a customer document baseclass.
92    """
93    grok.implements(IPublicDocument)
94    grok.provides(IPublicDocument)
95    grok.baseclass()
[11982]96
[12213]97    @property
98    def state(self):
99        state = IWorkflowState(self).getState()
100        return state
[12161]101
[12213]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
[12214]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
[12213]118
[12225]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
[12214]124
[12225]125
[12213]126class PDFDocument(PublicDocumentBase):
[12200]127    """This is a  document for a single pdf upload file.
128    """
129    grok.implements(IPDFDocument)
130    grok.provides(IPDFDocument)
131
[12214]132    form_fields_interface = IPDFDocument
133
[12200]134PDFDocument = attrs_to_fields(PDFDocument)
135
136
[12213]137class HTMLDocument(PublicDocumentBase):
[12200]138    """This is a  document to render html-coded text.
139    """
140    grok.implements(IHTMLDocument)
141    grok.provides(IHTMLDocument)
142
[12214]143    form_fields_interface = IHTMLDocument
144
[12227]145    def __init__(self, *args, **kw):
146        super(HTMLDocument, self).__init__(*args, **kw)
[12235]147        self.html_dict = {}
[12227]148
[12200]149HTMLDocument = attrs_to_fields(HTMLDocument)
150
151
[12408]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
[12200]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
[12408]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
[12204]212#: The file id marker for files
213DOCUMENT_FILE_STORE_NAME = 'file-document'
[12200]214
[12204]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'`` stored in
255        the documents container this chooser would create:
256
257          ``'__file-document__documents/nice_image_d123.jpeg'``
258
259        meaning that the nice image of this document would be
260        stored in the site-wide file storage in path:
261
262          ``documents/nice_image_d123.jpeg``
263
264        """
265        basename, ext = os.path.splitext(attr)
266        doc_id = self.context.document_id
267        marked_filename = '__%s__documents/%s_%s%s' % (
268            DOCUMENT_FILE_STORE_NAME,
269            basename, doc_id, ext)
270        return marked_filename
271
272
273class DocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
274    """ Document specific file handling.
275
276    This handler knows in which path in a filestore to store document
277    files and how to turn this kind of data into some (browsable)
278    file object.
279
280    It is called from the global file storage, when it wants to
281    get/store a file with a file id starting with
282    ``__file-document__`` (the marker string for customer files).
283
284    Like each other file store handler it does not handle the files
285    really (this is done by the global file store) but only computes
286    paths and things like this.
287    """
288    grok.implements(IFileStoreHandler)
289    grok.name(DOCUMENT_FILE_STORE_NAME)
290
291    def pathFromFileID(self, store, root, file_id):
292        """All document files are put in directory ``documents``.
293        """
294        marker, filename, basename, ext = store.extractMarker(file_id)
295        sub_root = os.path.join(root, 'documents')
296        return super(DocumentFileStoreHandler, self).pathFromFileID(
297            store, sub_root, basename)
298
299    def createFile(self, store, root, filename, file_id, file):
300        """Create a browsable file-like object.
301        """
302        # call super method to ensure that any old files with
303        # different filename extension are deleted.
304        file, path, file_obj = super(
305            DocumentFileStoreHandler, self).createFile(
306            store, root,  filename, file_id, file)
307        return file, path, IkobaImageFile(
308            file_obj.filename, file_obj.data)
309
310
[11982]311@grok.subscribe(IDocument, grok.IObjectAddedEvent)
312def handle_document_added(document, event):
313    """If a document is added the transition create is fired.
314    The latter produces a logging message.
315    """
316    if IWorkflowState(document).getState() is None:
317        IWorkflowInfo(document).fireTransition('create')
318    return
Note: See TracBrowser for help on using the repository browser.