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

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

Enable assignment of waeup.local.DocumentManager? role.

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