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

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

Change document_id generation algorithm. Use Universally Unique IDentifiers instead of consecutive numbers.

  • Property svn:keywords set to Id
File size: 8.7 KB
Line 
1## $Id: document.py 12256 2014-12-18 12:58:12Z 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)
41
42class Document(grok.Container):
43    """This is a document.
44    """
45    grok.implements(IDocument)
46    grok.provides(IDocument)
47    grok.baseclass()
48
49    form_fields_interface = None
50
51    local_roles = [
52        'waeup.local.DocumentManager',
53        ]
54
55    user_id = None
56    state = None
57    translated_state = None
58    translated_class_name = None
59
60    @property
61    def history(self):
62        history = IObjectHistory(self)
63        return history
64
65    @property
66    def class_name(self):
67        return self.__class__.__name__
68
69    @property
70    def formatted_transition_date(self):
71        try:
72            return self.history.messages[-1].split(' - ')[0]
73        except IndexError:
74            return
75
76    @property
77    def connected_files(self):
78        return
79
80    @property
81    def is_verifiable(self):
82        return True, None
83
84    def setMD5(self):
85        """Determine md5 checksum of all files and store checksums as
86        document attributes.
87        """
88        return
89       
90class PublicDocumentBase(Document):
91    """This is a customer document baseclass.
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.ikoba.','')
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
132    form_fields_interface = IPDFDocument
133
134PDFDocument = attrs_to_fields(PDFDocument)
135
136
137class HTMLDocument(PublicDocumentBase):
138    """This is a  document to render html-coded text.
139    """
140    grok.implements(IHTMLDocument)
141    grok.provides(IHTMLDocument)
142
143    form_fields_interface = IHTMLDocument
144
145    def __init__(self, *args, **kw):
146        super(HTMLDocument, self).__init__(*args, **kw)
147        self.html_dict = {}
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.