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

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

We need different workflows for customer documents and central 'public' documents.

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