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

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

Add DocumentFileNameChooser? and DocumentFileStoreHandler? with functional tests.

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