source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/documents.py @ 12036

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

Add document file viewlets. Tests will follow.

  • Property svn:keywords set to Id
File size: 7.6 KB
Line 
1## $Id: documents.py 12035 2014-11-22 10:14:38Z 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"""
19Customer document components.
20"""
21import os
22import grok
23from zope.component import queryUtility, getUtility
24from zope.component.interfaces import IFactory
25from zope.interface import implementedBy
26
27from waeup.ikoba.image import IkobaImageFile
28from waeup.ikoba.imagestorage import DefaultFileStoreHandler
29from waeup.ikoba.interfaces import MessageFactory as _
30from waeup.ikoba.interfaces import (
31    IFileStoreNameChooser, IFileStoreHandler,
32    IIkobaUtils, IExtFileStore)
33from waeup.ikoba.customers.interfaces import (
34    ICustomerDocumentsContainer, ICustomerNavigation, ICustomerDocument,
35    ICustomersUtils)
36from waeup.ikoba.documents import DocumentsContainer, Document
37from waeup.ikoba.documents.interfaces import IDocumentsUtils
38from waeup.ikoba.utils.helpers import attrs_to_fields
39
40from waeup.ikoba.customers.utils import path_from_custid
41
42class CustomerDocumentsContainer(DocumentsContainer):
43    """This is a container for customer documents.
44    """
45    grok.implements(ICustomerDocumentsContainer, ICustomerNavigation)
46    grok.provides(ICustomerDocumentsContainer)
47
48    def __init__(self):
49        super(CustomerDocumentsContainer, self).__init__()
50        return
51
52    @property
53    def customer(self):
54        return self.__parent__
55
56    def writeLogMessage(self, view, message):
57        return self.__parent__.writeLogMessage(view, message)
58
59CustomerDocumentsContainer = attrs_to_fields(CustomerDocumentsContainer)
60
61class CustomerDocument(Document):
62    """This is an document.
63    """
64    grok.implements(ICustomerDocument, ICustomerNavigation)
65    grok.provides(ICustomerDocument)
66
67    def __init__(self):
68        super(CustomerDocument, self).__init__()
69        return
70
71    @property
72    def customer(self):
73        try:
74            return self.__parent__.__parent__
75        except AttributeError:
76            return None
77
78    def writeLogMessage(self, view, message):
79        return self.__parent__.__parent__.writeLogMessage(view, message)
80
81    @property
82    def is_editable(self):
83        try:
84            # Customer must be approved
85            cond1 = self.customer.state in getUtility(
86                ICustomersUtils).DOCMANAGE_STATES
87            # Document must be in state created
88            cond2 = self.state in getUtility(
89                IDocumentsUtils).DOCMANAGE_STATES
90            if not (cond1 and cond2):
91                return False
92        except AttributeError:
93            pass
94        return True
95
96CustomerDocument = attrs_to_fields(CustomerDocument)
97
98
99# Customer documents must be importable. So we might need a factory.
100class CustomerDocumentFactory(grok.GlobalUtility):
101    """A factory for customer documents.
102    """
103    grok.implements(IFactory)
104    grok.name(u'waeup.CustomerDocument')
105    title = u"Create a new document.",
106    description = u"This factory instantiates new document instances."
107
108    def __call__(self, *args, **kw):
109        return CustomerDocument(*args, **kw)
110
111    def getInterfaces(self):
112        return implementedBy(CustomerDocument)
113
114#: The file id marker for customer files
115CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
116
117
118class CustomerDocumentFileNameChooser(grok.Adapter):
119    """A file id chooser for :class:`CustomerDocument` objects.
120
121    `context` is an :class:`CustomerDocument` instance.
122
123    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
124    :class:`Customer` objects suitable for use with
125    :class:`ExtFileStore` instances. The delivered file_id contains
126    the file id marker for :class:`CustomerDocument` object and the customer id
127    of the context customer.
128
129    This chooser is registered as an adapter providing
130    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
131
132    File store name choosers like this one are only convenience
133    components to ease the task of creating file ids for customer document
134    objects. You are nevertheless encouraged to use them instead of
135    manually setting up filenames for customer documents.
136
137    .. seealso:: :mod:`waeup.ikoba.imagestorage`
138
139    """
140
141    grok.context(ICustomerDocument)
142    grok.implements(IFileStoreNameChooser)
143
144    def checkName(self, name=None, attr=None):
145        """Check whether the given name is a valid file id for the context.
146
147        Returns ``True`` only if `name` equals the result of
148        :meth:`chooseName`.
149
150        """
151        return name == self.chooseName()
152
153    def chooseName(self, attr, name=None):
154        """Get a valid file id for customer document context.
155
156        *Example:*
157
158        For a customer with customer id ``'A123456'``
159        and document with id 'd123'
160        with attr ``'nice_image.jpeg'`` stored in
161        the customers container this chooser would create:
162
163          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
164
165        meaning that the nice image of this customer document would be
166        stored in the site-wide file storage in path:
167
168          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
169
170        """
171        basename, ext = os.path.splitext(attr)
172        cust_id = self.context.customer.customer_id
173        doc_id = self.context.document_id
174        marked_filename = '__%s__%s/%s_%s_%s%s' % (
175            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
176            basename, doc_id, cust_id, ext)
177        return marked_filename
178
179
180class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
181    """Customer document specific file handling.
182
183    This handler knows in which path in a filestore to store customer document
184    files and how to turn this kind of data into some (browsable)
185    file object.
186
187    It is called from the global file storage, when it wants to
188    get/store a file with a file id starting with
189    ``__file-customerdocument__`` (the marker string for customer files).
190
191    Like each other file store handler it does not handle the files
192    really (this is done by the global file store) but only computes
193    paths and things like this.
194    """
195    grok.implements(IFileStoreHandler)
196    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
197
198    def pathFromFileID(self, store, root, file_id):
199        """All customer document files are put in directory ``customers``.
200        """
201        marker, filename, basename, ext = store.extractMarker(file_id)
202        sub_root = os.path.join(root, 'customers')
203        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
204            store, sub_root, basename)
205
206    def createFile(self, store, root, filename, file_id, file):
207        """Create a browsable file-like object.
208        """
209        # call super method to ensure that any old files with
210        # different filename extension are deleted.
211        file, path, file_obj = super(
212            CustomerDocumentFileStoreHandler, self).createFile(
213            store, root,  filename, file_id, file)
214        return file, path, IkobaImageFile(
215            file_obj.filename, file_obj.data)
216
Note: See TracBrowser for help on using the repository browser.