source: main/waeup.kofa/trunk/src/waeup/kofa/documents/document.py @ 15607

Last change on this file since 15607 was 13134, checked in by Henrik Bettermann, 9 years ago

Remove properties which are not used in Kofa.

Adjust to user handbook.

  • Property svn:keywords set to Id
File size: 9.6 KB
RevLine 
[12438]1## $Id: document.py 13134 2015-07-03 10:19:55Z henrik $
[12437]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 public documents.
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.kofa.image import KofaImageFile
32from waeup.kofa.imagestorage import DefaultFileStoreHandler
33from waeup.kofa.interfaces import (
34    IKofaUtils, IObjectHistory,
35    IFileStoreNameChooser, IFileStoreHandler, IExtFileStore)
36from waeup.kofa.interfaces import MessageFactory as _
37from waeup.kofa.utils.helpers import attrs_to_fields, get_current_principal
38from waeup.kofa.documents.interfaces import (
39    IDocument, IPublicDocument, IDocumentsUtils,
40    IPDFDocument, IHTMLDocument, IRESTDocument)
41
[12441]42
43@grok.subscribe(IDocument, grok.IObjectRemovedEvent)
44def handle_document_removed(document, event):
45    store = getUtility(IExtFileStore)
46    for filename in document.filenames:
47        store.deleteFileByContext(document, attr=filename)
48    return
49
50
[12437]51class Document(grok.Container):
52    """This is a document.
53    """
54    grok.implements(IDocument)
55    grok.provides(IDocument)
56    grok.baseclass()
57
58    form_fields_interface = None
59
[12441]60    # Kofa can store any number of files per Document object.
61    # However, we highly recommend to associate and store
62    # only one file per Document object. Thus the following
63    # tuple should contain only a single filename string.
64    filenames = ()
65
[12437]66    local_roles = [
67        'waeup.local.DocumentManager',
68        ]
69
70    state = None
71    translated_state = None
72    translated_class_name = None
73
74    @property
75    def history(self):
76        history = IObjectHistory(self)
77        return history
78
79    @property
80    def class_name(self):
81        return self.__class__.__name__
82
83    @property
84    def formatted_transition_date(self):
85        try:
86            return self.history.messages[-1].split(' - ')[0]
87        except IndexError:
88            return
89
90class PublicDocumentBase(Document):
[13134]91    """This is a public document baseclass.
[12437]92    """
93    grok.implements(IPublicDocument)
94    grok.provides(IPublicDocument)
95    grok.baseclass()
96
97    @property
98    def state(self):
99        state = IWorkflowState(self).getState()
100        return state
101
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
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
118
119    def writeLogMessage(self, view, message):
120        ob_class = view.__implemented__.__name__.replace('waeup.kofa.','')
121        self.__parent__.__parent__.logger.info(
122            '%s - %s - %s' % (ob_class, self.__name__, message))
123        return
124
125
126class PDFDocument(PublicDocumentBase):
127    """This is a  document for a single pdf upload file.
128    """
129    grok.implements(IPDFDocument)
130    grok.provides(IPDFDocument)
131
[12456]132    filenames = ('file.pdf',)
[12441]133
[12437]134    form_fields_interface = IPDFDocument
135
136PDFDocument = attrs_to_fields(PDFDocument)
137
138
139class HTMLDocument(PublicDocumentBase):
140    """This is a  document to render html-coded text.
141    """
142    grok.implements(IHTMLDocument)
143    grok.provides(IHTMLDocument)
144
145    form_fields_interface = IHTMLDocument
146
147    def __init__(self, *args, **kw):
148        super(HTMLDocument, self).__init__(*args, **kw)
149        self.html_dict = {}
150
151HTMLDocument = attrs_to_fields(HTMLDocument)
152
153
154class RESTDocument(PublicDocumentBase):
155    """This is a  document to render html-coded text.
156    """
157    grok.implements(IRESTDocument)
158    grok.provides(IRESTDocument)
159
160    form_fields_interface = IRESTDocument
161
162    def __init__(self, *args, **kw):
163        super(RESTDocument, self).__init__(*args, **kw)
164        self.html_dict = {}
165
166RESTDocument = attrs_to_fields(RESTDocument)
167
168
169class PDFDocumentFactory(grok.GlobalUtility):
170    """A factory for documents.
171    """
172    grok.implements(IFactory)
173    grok.name(u'waeup.PDFDocument')
174    title = u"Create a new PDF document.",
175    description = u"This factory instantiates new PDF documents."
176
177    def __call__(self, *args, **kw):
178        return PDFDocument(*args, **kw)
179
180    def getInterfaces(self):
181        return implementedBy(PDFDocument)
182
183
184class HTMLDocumentFactory(grok.GlobalUtility):
185    """A factory for HTML documents.
186    """
187    grok.implements(IFactory)
188    grok.name(u'waeup.HTMLDocument')
189    title = u"Create a new HTML document.",
190    description = u"This factory instantiates new HTML documents."
191
192    def __call__(self, *args, **kw):
193        return HTMLDocument(*args, **kw)
194
195    def getInterfaces(self):
196        return implementedBy(HTMLDocument)
197
198
199class RESTDocumentFactory(grok.GlobalUtility):
200    """A factory for REST documents.
201    """
202    grok.implements(IFactory)
203    grok.name(u'waeup.RESTDocument')
204    title = u"Create a new REST document.",
205    description = u"This factory instantiates new REST documents."
206
207    def __call__(self, *args, **kw):
208        return RESTDocument(*args, **kw)
209
210    def getInterfaces(self):
211        return implementedBy(RESTDocument)
212
213
214#: The file id marker for files
215DOCUMENT_FILE_STORE_NAME = 'file-document'
216
217
218class DocumentFileNameChooser(grok.Adapter):
219    """A file id chooser for :class:`Document` objects.
220
[13134]221    `context` is a :class:`Document` instance.
[12437]222
223    The delivered file_id contains the file id marker for
224    :class:`Document` objects in the central :class:`DocumentsContainer`.
225
226    This chooser is registered as an adapter providing
227    :class:`waeup.kofa.interfaces.IFileStoreNameChooser`.
228
229    File store name choosers like this one are only convenience
230    components to ease the task of creating file ids for customer document
231    objects. You are nevertheless encouraged to use them instead of
232    manually setting up filenames for customer documents.
233
234    .. seealso:: :mod:`waeup.kofa.imagestorage`
235
236    """
237
238    grok.context(IDocument)
239    grok.implements(IFileStoreNameChooser)
240
241    def checkName(self, name=None, attr=None):
242        """Check whether the given name is a valid file id for the context.
243
244        Returns ``True`` only if `name` equals the result of
245        :meth:`chooseName`.
246
247        """
248        return name == self.chooseName()
249
250    def chooseName(self, attr, name=None):
251        """Get a valid file id for customer document context.
252
253        *Example:*
254
[13134]255        For document with id ``d123`` and
256        with attr ``nice_image.jpeg`` this chooser would create:
[12437]257
[13134]258          ``__file-document__nice_image_d123.jpeg``
[12437]259
260        meaning that the nice image of this document would be
261        stored in the site-wide file storage in path:
262
263          ``nice_image_d123.jpeg``
264
265        """
266        basename, ext = os.path.splitext(attr)
267        doc_id = self.context.document_id
268        marked_filename = '__%s__%s_%s%s' % (
269            DOCUMENT_FILE_STORE_NAME,
270            basename, doc_id, ext)
271        return marked_filename
272
273
274class DocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
275    """ Document specific file handling.
276
277    This handler knows in which path in a filestore to store document
278    files and how to turn this kind of data into some (browsable)
279    file object.
280
281    It is called from the global file storage, when it wants to
282    get/store a file with a file id starting with
283    ``__file-document__`` (the marker string for customer files).
284
285    Like each other file store handler it does not handle the files
286    really (this is done by the global file store) but only computes
287    paths and things like this.
288    """
289    grok.implements(IFileStoreHandler)
290    grok.name(DOCUMENT_FILE_STORE_NAME)
291
292    def pathFromFileID(self, store, root, file_id):
293        """All document files are put in directory ``documents``.
294        """
295        marker, filename, basename, ext = store.extractMarker(file_id)
296        sub_root = os.path.join(root, 'documents')
297        return super(DocumentFileStoreHandler, self).pathFromFileID(
298            store, sub_root, basename)
299
300    def createFile(self, store, root, filename, file_id, file):
301        """Create a browsable file-like object.
302        """
303        # call super method to ensure that any old files with
304        # different filename extension are deleted.
305        file, path, file_obj = super(
306            DocumentFileStoreHandler, self).createFile(
307            store, root,  filename, file_id, file)
308        return file, path, KofaImageFile(
309            file_obj.filename, file_obj.data)
310
311
312@grok.subscribe(IDocument, grok.IObjectAddedEvent)
313def handle_document_added(document, event):
314    """If a document is added the transition create is fired.
315    The latter produces a logging message.
316    """
317    if IWorkflowState(document).getState() is None:
318        IWorkflowInfo(document).fireTransition('create')
319    return
Note: See TracBrowser for help on using the repository browser.