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

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

Use a baseclass for customer documents. That eases file viewlet configuration.

  • Property svn:keywords set to Id
File size: 8.7 KB
Line 
1## $Id: documents.py 12057 2014-11-25 13:15:27Z 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, ICustomerPDFDocument)
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 CustomerDocumentBase(Document):
62    """This is a customer document baseclass.
63    """
64    grok.implements(ICustomerDocument, ICustomerNavigation)
65    grok.provides(ICustomerDocument)
66    grok.baseclass()
67
68    @property
69    def customer(self):
70        try:
71            return self.__parent__.__parent__
72        except AttributeError:
73            return None
74
75    def writeLogMessage(self, view, message):
76        return self.__parent__.__parent__.writeLogMessage(view, message)
77
78    @property
79    def is_editable(self):
80        try:
81            # Customer must be approved
82            cond1 = self.customer.state in getUtility(
83                ICustomersUtils).DOCMANAGE_STATES
84            # Document must be in state created
85            cond2 = self.state in getUtility(
86                IDocumentsUtils).DOCMANAGE_STATES
87            if not (cond1 and cond2):
88                return False
89        except AttributeError:
90            pass
91        return True
92
93    @property
94    def translated_class_name(self):
95        try:
96            DOCTYPES_DICT = getUtility(ICustomersUtils).DOCTYPES_DICT
97            return DOCTYPES_DICT[self.class_name]
98        except KeyError:
99            return
100
101
102class CustomerSampleDocument(CustomerDocumentBase):
103    """This is a sample customer document.
104    """
105
106CustomerSampleDocument = attrs_to_fields(CustomerSampleDocument)
107
108
109class CustomerPDFDocument(CustomerDocumentBase):
110    """This is a customer document for a single pdf upload file.
111    """
112    grok.implements(ICustomerPDFDocument, ICustomerNavigation)
113    grok.provides(ICustomerPDFDocument)
114
115CustomerPDFDocument = attrs_to_fields(CustomerPDFDocument)
116
117
118# Customer documents must be importable. So we might need a factory.
119class CustomerDocumentFactory(grok.GlobalUtility):
120    """A factory for customer documents.
121    """
122    grok.implements(IFactory)
123    grok.name(u'waeup.CustomerSampleDocument')
124    title = u"Create a new document.",
125    description = u"This factory instantiates new sample document instances."
126
127    def __call__(self, *args, **kw):
128        return CustomerSampleDocument(*args, **kw)
129
130    def getInterfaces(self):
131        return implementedBy(CustomerSampleDocument)
132
133# Customer documents must be importable. So we might need a factory.
134class CustomerPDFDocumentFactory(grok.GlobalUtility):
135    """A factory for customer pdf documents.
136    """
137    grok.implements(IFactory)
138    grok.name(u'waeup.CustomerPDFDocument')
139    title = u"Create a new document.",
140    description = u"This factory instantiates new pdf document instances."
141
142    def __call__(self, *args, **kw):
143        return CustomerPDFDocument(*args, **kw)
144
145    def getInterfaces(self):
146        return implementedBy(CustomerPDFDocument)
147
148#: The file id marker for customer files
149CUSTOMERDOCUMENT_FILE_STORE_NAME = 'file-customerdocument'
150
151
152class CustomerDocumentFileNameChooser(grok.Adapter):
153    """A file id chooser for :class:`CustomerDocument` objects.
154
155    `context` is an :class:`CustomerDocument` instance.
156
157    The :class:`CustomerDocumentFileNameChooser` can build/check file ids for
158    :class:`Customer` objects suitable for use with
159    :class:`ExtFileStore` instances. The delivered file_id contains
160    the file id marker for :class:`CustomerDocument` object and the customer id
161    of the context customer.
162
163    This chooser is registered as an adapter providing
164    :class:`waeup.ikoba.interfaces.IFileStoreNameChooser`.
165
166    File store name choosers like this one are only convenience
167    components to ease the task of creating file ids for customer document
168    objects. You are nevertheless encouraged to use them instead of
169    manually setting up filenames for customer documents.
170
171    .. seealso:: :mod:`waeup.ikoba.imagestorage`
172
173    """
174
175    grok.context(ICustomerDocument)
176    grok.implements(IFileStoreNameChooser)
177
178    def checkName(self, name=None, attr=None):
179        """Check whether the given name is a valid file id for the context.
180
181        Returns ``True`` only if `name` equals the result of
182        :meth:`chooseName`.
183
184        """
185        return name == self.chooseName()
186
187    def chooseName(self, attr, name=None):
188        """Get a valid file id for customer document context.
189
190        *Example:*
191
192        For a customer with customer id ``'A123456'``
193        and document with id 'd123'
194        with attr ``'nice_image.jpeg'`` stored in
195        the customers container this chooser would create:
196
197          ``'__file-customerdocument__customers/A/A123456/nice_image_d123_A123456.jpeg'``
198
199        meaning that the nice image of this customer document would be
200        stored in the site-wide file storage in path:
201
202          ``customers/A/A123456/nice_image_d123_A123456.jpeg``
203
204        """
205        basename, ext = os.path.splitext(attr)
206        cust_id = self.context.customer.customer_id
207        doc_id = self.context.document_id
208        marked_filename = '__%s__%s/%s_%s_%s%s' % (
209            CUSTOMERDOCUMENT_FILE_STORE_NAME, path_from_custid(cust_id),
210            basename, doc_id, cust_id, ext)
211        return marked_filename
212
213
214class CustomerDocumentFileStoreHandler(DefaultFileStoreHandler, grok.GlobalUtility):
215    """Customer document specific file handling.
216
217    This handler knows in which path in a filestore to store customer document
218    files and how to turn this kind of data into some (browsable)
219    file object.
220
221    It is called from the global file storage, when it wants to
222    get/store a file with a file id starting with
223    ``__file-customerdocument__`` (the marker string for customer files).
224
225    Like each other file store handler it does not handle the files
226    really (this is done by the global file store) but only computes
227    paths and things like this.
228    """
229    grok.implements(IFileStoreHandler)
230    grok.name(CUSTOMERDOCUMENT_FILE_STORE_NAME)
231
232    def pathFromFileID(self, store, root, file_id):
233        """All customer document files are put in directory ``customers``.
234        """
235        marker, filename, basename, ext = store.extractMarker(file_id)
236        sub_root = os.path.join(root, 'customers')
237        return super(CustomerDocumentFileStoreHandler, self).pathFromFileID(
238            store, sub_root, basename)
239
240    def createFile(self, store, root, filename, file_id, file):
241        """Create a browsable file-like object.
242        """
243        # call super method to ensure that any old files with
244        # different filename extension are deleted.
245        file, path, file_obj = super(
246            CustomerDocumentFileStoreHandler, self).createFile(
247            store, root,  filename, file_id, file)
248        return file, path, IkobaImageFile(
249            file_obj.filename, file_obj.data)
250
Note: See TracBrowser for help on using the repository browser.