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

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

Add first browser test.

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