source: main/waeup.ikoba/trunk/src/waeup/ikoba/customers/browser.py @ 12017

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

Add more customer document components.

  • Property svn:keywords set to Id
File size: 26.6 KB
Line 
1## $Id: browser.py 12016 2014-11-21 05:08:19Z 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"""UI components for customers and related components.
19"""
20
21import sys
22import grok
23import pytz
24from urllib import urlencode
25from datetime import datetime
26from zope.event import notify
27from zope.i18n import translate
28from zope.catalog.interfaces import ICatalog
29from zope.component import queryUtility, getUtility, createObject
30from zope.schema.interfaces import ConstraintNotSatisfied, RequiredMissing
31from zope.formlib.textwidgets import BytesDisplayWidget
32from zope.security import checkPermission
33from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
34from waeup.ikoba.interfaces import MessageFactory as _
35from waeup.ikoba.interfaces import (
36    IContactForm, IObjectHistory, IIkobaObject, IIkobaUtils,
37    IPasswordValidator, IUserAccount)
38from waeup.ikoba.browser.layout import (
39    IkobaPage, IkobaEditFormPage, IkobaAddFormPage, IkobaDisplayFormPage,
40    IkobaForm, NullValidator, jsaction, action, UtilityView)
41from waeup.ikoba.browser.pages import ContactAdminForm
42from waeup.ikoba.browser.breadcrumbs import Breadcrumb
43from waeup.ikoba.browser.interfaces import ICaptchaManager
44from waeup.ikoba.mandates.mandate import PasswordMandate
45from waeup.ikoba.utils.helpers import get_current_principal, to_timezone, now
46from waeup.ikoba.customers.interfaces import (
47    ICustomer, ICustomersContainer, ICustomerRequestPW, ICustomersUtils,
48    ICustomerDocument, ICustomerDocumentsContainer
49    )
50from waeup.ikoba.customers.catalog import search
51
52grok.context(IIkobaObject)
53
54
55# Save function used for save methods in pages
56def msave(view, **data):
57    changed_fields = view.applyData(view.context, **data)
58    # Turn list of lists into single list
59    if changed_fields:
60        changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
61    fields_string = ' + '.join(changed_fields)
62    view.flash(_('Form has been saved.'))
63    if fields_string:
64        view.context.writeLogMessage(view, 'saved: %s' % fields_string)
65    return
66
67
68def emit_lock_message(view):
69    """Flash a lock message.
70    """
71    view.flash(_('The requested form is locked (read-only).'), type="warning")
72    view.redirect(view.url(view.context))
73    return
74
75
76class CustomersBreadcrumb(Breadcrumb):
77    """A breadcrumb for the customers container.
78    """
79    grok.context(ICustomersContainer)
80    title = _('Customers')
81
82    @property
83    def target(self):
84        user = get_current_principal()
85        if getattr(user, 'user_type', None) == 'customer':
86            return None
87        return self.viewname
88
89
90class CustomerBreadcrumb(Breadcrumb):
91    """A breadcrumb for the customer container.
92    """
93    grok.context(ICustomer)
94
95    def title(self):
96        return self.context.display_fullname
97
98
99class CustomersContainerPage(IkobaPage):
100    """The standard view for customer containers.
101    """
102    grok.context(ICustomersContainer)
103    grok.name('index')
104    grok.require('waeup.viewCustomersContainer')
105    grok.template('containerpage')
106    label = _('Find customers')
107    search_button = _('Find customer(s)')
108    pnav = 4
109
110    def update(self, *args, **kw):
111        form = self.request.form
112        self.hitlist = []
113        if form.get('searchtype', None) == 'suspended':
114            self.searchtype = form['searchtype']
115            self.searchterm = None
116        elif 'searchterm' in form and form['searchterm']:
117            self.searchterm = form['searchterm']
118            self.searchtype = form['searchtype']
119        elif 'old_searchterm' in form:
120            self.searchterm = form['old_searchterm']
121            self.searchtype = form['old_searchtype']
122        else:
123            if 'search' in form:
124                self.flash(_('Empty search string'), type="warning")
125            return
126        if self.searchtype == 'current_session':
127            try:
128                self.searchterm = int(self.searchterm)
129            except ValueError:
130                self.flash(_('Only year dates allowed (e.g. 2011).'),
131                           type="danger")
132                return
133        self.hitlist = search(query=self.searchterm,
134            searchtype=self.searchtype, view=self)
135        if not self.hitlist:
136            self.flash(_('No customer found.'), type="warning")
137        return
138
139
140class CustomersContainerManagePage(IkobaPage):
141    """The manage page for customer containers.
142    """
143    grok.context(ICustomersContainer)
144    grok.name('manage')
145    grok.require('waeup.manageCustomer')
146    grok.template('containermanagepage')
147    pnav = 4
148    label = _('Manage customer section')
149    search_button = _('Find customer(s)')
150    remove_button = _('Remove selected')
151
152    def update(self, *args, **kw):
153        form = self.request.form
154        self.hitlist = []
155        if form.get('searchtype', None) == 'suspended':
156            self.searchtype = form['searchtype']
157            self.searchterm = None
158        elif 'searchterm' in form and form['searchterm']:
159            self.searchterm = form['searchterm']
160            self.searchtype = form['searchtype']
161        elif 'old_searchterm' in form:
162            self.searchterm = form['old_searchterm']
163            self.searchtype = form['old_searchtype']
164        else:
165            if 'search' in form:
166                self.flash(_('Empty search string'), type="warning")
167            return
168        if self.searchtype == 'current_session':
169            try:
170                self.searchterm = int(self.searchterm)
171            except ValueError:
172                self.flash(_('Only year dates allowed (e.g. 2011).'),
173                           type="danger")
174                return
175        if not 'entries' in form:
176            self.hitlist = search(query=self.searchterm,
177                searchtype=self.searchtype, view=self)
178            if not self.hitlist:
179                self.flash(_('No customer found.'), type="warning")
180            if 'remove' in form:
181                self.flash(_('No item selected.'), type="warning")
182            return
183        entries = form['entries']
184        if isinstance(entries, basestring):
185            entries = [entries]
186        deleted = []
187        for entry in entries:
188            if 'remove' in form:
189                del self.context[entry]
190                deleted.append(entry)
191        self.hitlist = search(query=self.searchterm,
192            searchtype=self.searchtype, view=self)
193        if len(deleted):
194            self.flash(_('Successfully removed: ${a}',
195                mapping={'a': ','.join(deleted)}))
196        return
197
198
199class CustomerAddFormPage(IkobaAddFormPage):
200    """Add-form to add a customer.
201    """
202    grok.context(ICustomersContainer)
203    grok.require('waeup.manageCustomer')
204    grok.name('addcustomer')
205    form_fields = grok.AutoFields(ICustomer).select(
206        'firstname', 'middlename', 'lastname', 'reg_number')
207    label = _('Add customer')
208    pnav = 4
209
210    @action(_('Create customer record'), style='primary')
211    def addCustomer(self, **data):
212        customer = createObject(u'waeup.Customer')
213        self.applyData(customer, **data)
214        self.context.addCustomer(customer)
215        self.flash(_('Customer record created.'))
216        self.redirect(self.url(self.context[customer.customer_id], 'index'))
217        return
218
219
220class LoginAsCustomerStep1(IkobaEditFormPage):
221    """ View to temporarily set a customer password.
222    """
223    grok.context(ICustomer)
224    grok.name('loginasstep1')
225    grok.require('waeup.loginAsCustomer')
226    grok.template('loginasstep1')
227    pnav = 4
228
229    def label(self):
230        return _(u'Set temporary password for ${a}',
231            mapping={'a': self.context.display_fullname})
232
233    @action('Set password now', style='primary')
234    def setPassword(self, *args, **data):
235        ikoba_utils = getUtility(IIkobaUtils)
236        password = ikoba_utils.genPassword()
237        self.context.setTempPassword(self.request.principal.id, password)
238        self.context.writeLogMessage(
239            self, 'temp_password generated: %s' % password)
240        args = {'password': password}
241        self.redirect(self.url(self.context) +
242            '/loginasstep2?%s' % urlencode(args))
243        return
244
245
246class LoginAsCustomerStep2(IkobaPage):
247    """ View to temporarily login as customer with a temporary password.
248    """
249    grok.context(ICustomer)
250    grok.name('loginasstep2')
251    grok.require('waeup.Public')
252    grok.template('loginasstep2')
253    login_button = _('Login now')
254    pnav = 4
255
256    def label(self):
257        return _(u'Login as ${a}',
258            mapping={'a': self.context.customer_id})
259
260    def update(self, SUBMIT=None, password=None):
261        self.password = password
262        if SUBMIT is not None:
263            self.flash(_('You successfully logged in as customer.'))
264            self.redirect(self.url(self.context))
265        return
266
267
268class CustomerBaseDisplayFormPage(IkobaDisplayFormPage):
269    """ Page to display customer base data
270    """
271    grok.context(ICustomer)
272    grok.name('index')
273    grok.require('waeup.viewCustomer')
274    grok.template('basepage')
275    form_fields = grok.AutoFields(ICustomer).omit(
276        'password', 'suspended', 'suspended_comment')
277    pnav = 4
278
279    @property
280    def label(self):
281        if self.context.suspended:
282            return _('${a}: Base Data (account deactivated)',
283                mapping={'a': self.context.display_fullname})
284        return  _('${a}: Base Data',
285            mapping={'a': self.context.display_fullname})
286
287    @property
288    def hasPassword(self):
289        if self.context.password:
290            return _('set')
291        return _('unset')
292
293
294class ContactCustomerForm(ContactAdminForm):
295    grok.context(ICustomer)
296    grok.name('contactcustomer')
297    grok.require('waeup.viewCustomer')
298    pnav = 4
299    form_fields = grok.AutoFields(IContactForm).select('subject', 'body')
300
301    def update(self, subject=u'', body=u''):
302        super(ContactCustomerForm, self).update()
303        self.form_fields.get('subject').field.default = subject
304        self.form_fields.get('body').field.default = body
305        return
306
307    def label(self):
308        return _(u'Send message to ${a}',
309            mapping={'a': self.context.display_fullname})
310
311    @action('Send message now', style='primary')
312    def send(self, *args, **data):
313        try:
314            email = self.request.principal.email
315        except AttributeError:
316            email = self.config.email_admin
317        usertype = getattr(self.request.principal,
318                           'user_type', 'system').title()
319        ikoba_utils = getUtility(IIkobaUtils)
320        success = ikoba_utils.sendContactForm(
321                self.request.principal.title, email,
322                self.context.display_fullname, self.context.email,
323                self.request.principal.id,usertype,
324                self.config.name,
325                data['body'], data['subject'])
326        if success:
327            self.flash(_('Your message has been sent.'))
328        else:
329            self.flash(_('An smtp server error occurred.'), type="danger")
330        return
331
332
333class CustomerBaseManageFormPage(IkobaEditFormPage):
334    """ View to manage customer base data
335    """
336    grok.context(ICustomer)
337    grok.name('manage_base')
338    grok.require('waeup.manageCustomer')
339    form_fields = grok.AutoFields(ICustomer).omit(
340        'customer_id', 'adm_code', 'suspended')
341    grok.template('basemanagepage')
342    label = _('Manage base data')
343    pnav = 4
344
345    def update(self):
346        super(CustomerBaseManageFormPage, self).update()
347        self.wf_info = IWorkflowInfo(self.context)
348        return
349
350    @action(_('Save'), style='primary')
351    def save(self, **data):
352        form = self.request.form
353        password = form.get('password', None)
354        password_ctl = form.get('control_password', None)
355        if password:
356            validator = getUtility(IPasswordValidator)
357            errors = validator.validate_password(password, password_ctl)
358            if errors:
359                self.flash(' '.join(errors), type="danger")
360                return
361        changed_fields = self.applyData(self.context, **data)
362        # Turn list of lists into single list
363        if changed_fields:
364            changed_fields = reduce(lambda x,y: x+y, changed_fields.values())
365        else:
366            changed_fields = []
367        if password:
368            # Now we know that the form has no errors and can set password
369            IUserAccount(self.context).setPassword(password)
370            changed_fields.append('password')
371        fields_string = ' + '.join(changed_fields)
372        self.flash(_('Form has been saved.'))
373        if fields_string:
374            self.context.writeLogMessage(self, 'saved: % s' % fields_string)
375        return
376
377
378class CustomerTriggerTransitionFormPage(IkobaEditFormPage):
379    """ View to manage customer base data
380    """
381    grok.context(ICustomer)
382    grok.name('trigtrans')
383    grok.require('waeup.triggerTransition')
384    grok.template('trigtrans')
385    label = _('Trigger registration transition')
386    pnav = 4
387
388    def getTransitions(self):
389        """Return a list of dicts of allowed transition ids and titles.
390
391        Each list entry provides keys ``name`` and ``title`` for
392        internal name and (human readable) title of a single
393        transition.
394        """
395        wf_info = IWorkflowInfo(self.context)
396        allowed_transitions = [t for t in wf_info.getManualTransitions()]
397        return [dict(name='', title=_('No transition'))] +[
398            dict(name=x, title=y) for x, y in allowed_transitions]
399
400    @action(_('Save'), style='primary')
401    def save(self, **data):
402        form = self.request.form
403        if 'transition' in form and form['transition']:
404            transition_id = form['transition']
405            wf_info = IWorkflowInfo(self.context)
406            wf_info.fireTransition(transition_id)
407        return
408
409
410class CustomerActivatePage(UtilityView, grok.View):
411    """ Activate customer account
412    """
413    grok.context(ICustomer)
414    grok.name('activate')
415    grok.require('waeup.manageCustomer')
416
417    def update(self):
418        self.context.suspended = False
419        self.context.writeLogMessage(self, 'account activated')
420        history = IObjectHistory(self.context)
421        history.addMessage('Customer account activated')
422        self.flash(_('Customer account has been activated.'))
423        self.redirect(self.url(self.context))
424        return
425
426    def render(self):
427        return
428
429
430class CustomerDeactivatePage(UtilityView, grok.View):
431    """ Deactivate customer account
432    """
433    grok.context(ICustomer)
434    grok.name('deactivate')
435    grok.require('waeup.manageCustomer')
436
437    def update(self):
438        self.context.suspended = True
439        self.context.writeLogMessage(self, 'account deactivated')
440        history = IObjectHistory(self.context)
441        history.addMessage('Customer account deactivated')
442        self.flash(_('Customer account has been deactivated.'))
443        self.redirect(self.url(self.context))
444        return
445
446    def render(self):
447        return
448
449
450class CustomerHistoryPage(IkobaPage):
451    """ Page to display customer history
452    """
453    grok.context(ICustomer)
454    grok.name('history')
455    grok.require('waeup.viewCustomer')
456    grok.template('customerhistory')
457    pnav = 4
458
459    @property
460    def label(self):
461        return _('${a}: History', mapping={'a':self.context.display_fullname})
462
463
464class CustomerRequestPasswordPage(IkobaAddFormPage):
465    """Captcha'd registration page for applicants.
466    """
467    grok.name('requestpw')
468    grok.require('waeup.Anonymous')
469    grok.template('requestpw')
470    form_fields = grok.AutoFields(ICustomerRequestPW).select(
471        'firstname','number','email')
472    label = _('Request password for first-time login')
473
474    def update(self):
475        # Handle captcha
476        self.captcha = getUtility(ICaptchaManager).getCaptcha()
477        self.captcha_result = self.captcha.verify(self.request)
478        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
479        return
480
481    def _redirect(self, email, password, customer_id):
482        # Forward only email to landing page in base package.
483        self.redirect(self.url(self.context, 'requestpw_complete',
484            data=dict(email=email)))
485        return
486
487    def _pw_used(self):
488        # XXX: False if password has not been used. We need an extra
489        #      attribute which remembers if customer logged in.
490        return True
491
492    @action(_('Send login credentials to email address'), style='primary')
493    def get_credentials(self, **data):
494        if not self.captcha_result.is_valid:
495            # Captcha will display error messages automatically.
496            # No need to flash something.
497            return
498        number = data.get('number','')
499        firstname = data.get('firstname','')
500        cat = getUtility(ICatalog, name='customers_catalog')
501        results = list(
502            cat.searchResults(reg_number=(number, number)))
503        if results:
504            customer = results[0]
505            if getattr(customer,'firstname',None) is None:
506                self.flash(_('An error occurred.'), type="danger")
507                return
508            elif customer.firstname.lower() != firstname.lower():
509                # Don't tell the truth here. Anonymous must not
510                # know that a record was found and only the firstname
511                # verification failed.
512                self.flash(_('No customer record found.'), type="warning")
513                return
514            elif customer.password is not None and self._pw_used:
515                self.flash(_('Your password has already been set and used. '
516                             'Please proceed to the login page.'),
517                           type="warning")
518                return
519            # Store email address but nothing else.
520            customer.email = data['email']
521            notify(grok.ObjectModifiedEvent(customer))
522        else:
523            # No record found, this is the truth.
524            self.flash(_('No customer record found.'), type="warning")
525            return
526
527        ikoba_utils = getUtility(IIkobaUtils)
528        password = ikoba_utils.genPassword()
529        mandate = PasswordMandate()
530        mandate.params['password'] = password
531        mandate.params['user'] = customer
532        site = grok.getSite()
533        site['mandates'].addMandate(mandate)
534        # Send email with credentials
535        args = {'mandate_id':mandate.mandate_id}
536        mandate_url = self.url(site) + '/mandate?%s' % urlencode(args)
537        url_info = u'Confirmation link: %s' % mandate_url
538        msg = _('You have successfully requested a password for the')
539        if ikoba_utils.sendCredentials(IUserAccount(customer),
540            password, url_info, msg):
541            email_sent = customer.email
542        else:
543            email_sent = None
544        self._redirect(email=email_sent, password=password,
545            customer_id=customer.customer_id)
546        ob_class = self.__implemented__.__name__.replace('waeup.ikoba.','')
547        self.context.logger.info(
548            '%s - %s (%s) - %s' % (ob_class, number, customer.customer_id, email_sent))
549        return
550
551
552class CustomerRequestPasswordEmailSent(IkobaPage):
553    """Landing page after successful password request.
554
555    """
556    grok.name('requestpw_complete')
557    grok.require('waeup.Public')
558    grok.template('requestpwmailsent')
559    label = _('Your password request was successful.')
560
561    def update(self, email=None, customer_id=None, password=None):
562        self.email = email
563        self.password = password
564        self.customer_id = customer_id
565        return
566
567
568class CustomerFilesUploadPage(IkobaPage):
569    """ View to upload files by customer
570    """
571    grok.context(ICustomer)
572    grok.name('change_portrait')
573    grok.require('waeup.uploadCustomerFile')
574    grok.template('filesuploadpage')
575    label = _('Upload files')
576    pnav = 4
577
578    def update(self):
579        PWCHANGE_STATES = getUtility(ICustomersUtils).PWCHANGE_STATES
580        if self.context.customer.state not in PWCHANGE_STATES:
581            emit_lock_message(self)
582            return
583        super(CustomerFilesUploadPage, self).update()
584        return
585
586# Pages for customers
587
588
589class CustomerBaseEditFormPage(IkobaEditFormPage):
590    """ View to edit customer base data
591    """
592    grok.context(ICustomer)
593    grok.name('edit_base')
594    grok.require('waeup.handleCustomer')
595    form_fields = grok.AutoFields(ICustomer).select(
596        'email', 'phone')
597    label = _('Edit base data')
598    pnav = 4
599
600    @action(_('Save'), style='primary')
601    def save(self, **data):
602        msave(self, **data)
603        return
604
605
606class CustomerChangePasswordPage(IkobaEditFormPage):
607    """ View to edit customer passords
608    """
609    grok.context(ICustomer)
610    grok.name('changepassword')
611    grok.require('waeup.handleCustomer')
612    grok.template('changepassword')
613    label = _('Change password')
614    pnav = 4
615
616    @action(_('Save'), style='primary')
617    def save(self, **data):
618        form = self.request.form
619        password = form.get('change_password', None)
620        password_ctl = form.get('change_password_repeat', None)
621        if password:
622            validator = getUtility(IPasswordValidator)
623            errors = validator.validate_password(password, password_ctl)
624            if not errors:
625                IUserAccount(self.context).setPassword(password)
626                self.context.writeLogMessage(self, 'saved: password')
627                self.flash(_('Password changed.'))
628            else:
629                self.flash(' '.join(errors), type="warning")
630        return
631
632# Pages for customer documents
633
634class DocumentsBreadcrumb(Breadcrumb):
635    """A breadcrumb for the documents container.
636    """
637    grok.context(ICustomerDocumentsContainer)
638    title = _('Documents')
639
640
641class DocumentBreadcrumb(Breadcrumb):
642    """A breadcrumb for the customer container.
643    """
644    grok.context(ICustomerDocument)
645
646    @property
647    def title(self):
648        return self.context.document_id
649
650
651class DocumentsManageFormPage(IkobaEditFormPage):
652    """ Page to manage the customer documents
653
654    This manage form page is for both customers and customers officers.
655    """
656    grok.context(ICustomerDocumentsContainer)
657    grok.name('index')
658    grok.require('waeup.viewCustomer')
659    form_fields = grok.AutoFields(ICustomerDocumentsContainer)
660    grok.template('documentsmanagepage')
661    pnav = 4
662
663    @property
664    def manage_documents_allowed(self):
665        return checkPermission('waeup.editCustomerDocuments', self.context)
666
667    def unremovable(self, document):
668        usertype = getattr(self.request.principal, 'user_type', None)
669        if not usertype:
670            return False
671        if not self.manage_documents_allowed:
672            return True
673        return (self.request.principal.user_type == 'customer' and \
674            document.state in (VERIFIED, REJECTED, OUTDATED))
675
676    @property
677    def label(self):
678        return _('${a}: Documents',
679            mapping = {'a':self.context.__parent__.display_fullname})
680
681    @jsaction(_('Remove selected documents'))
682    def delDocument(self, **data):
683        form = self.request.form
684        if 'val_id' in form:
685            child_id = form['val_id']
686        else:
687            self.flash(_('No document selected.'), type="warning")
688            self.redirect(self.url(self.context))
689            return
690        if not isinstance(child_id, list):
691            child_id = [child_id]
692        deleted = []
693        for id in child_id:
694            # Customers are not allowed to remove used documents
695            document = self.context.get(id, None)
696            if document is not None and not self.unremovable(document):
697                del self.context[id]
698                deleted.append(id)
699        if len(deleted):
700            self.flash(_('Successfully removed: ${a}',
701                mapping = {'a': ', '.join(deleted)}))
702            self.context.writeLogMessage(
703                self,'removed: %s' % ', '.join(deleted))
704        self.redirect(self.url(self.context))
705        return
706
707
708class DocumentAddFormPage(IkobaAddFormPage):
709    """ Page to add an document
710    """
711    grok.context(ICustomerDocumentsContainer)
712    grok.name('adddoc')
713    grok.template('documentaddform')
714    grok.require('waeup.editCustomerDocuments')
715    form_fields = grok.AutoFields(ICustomerDocument)
716    label = _('Add document')
717    pnav = 4
718
719    @property
720    def selectable_doctypes(self):
721        doctypes = getUtility(IIkobaUtils).DOCUMENT_TYPES
722        return sorted(doctypes.items())
723
724    @action(_('Create document'), style='primary')
725    def createDocument(self, **data):
726        form = self.request.form
727        customer = self.context.__parent__
728        doctype = form.get('doctype', None)
729        # Here we can create various instances of CustomerDocument derived
730        # classes depending on the doctype parameter given in form.
731        # So far we only have one CustomerDocument class and no derived
732        # classes.
733        document = createObject(u'waeup.CustomerDocument')
734        self.context.addDocument(document)
735        doctype = getUtility(IIkobaUtils).DOCUMENT_TYPES[doctype]
736        self.flash(_('${a} created.',
737            mapping = {'a': doctype}))
738        self.context.writeLogMessage(
739            self,'added: %s %s' % (doctype, document.document_id))
740        self.redirect(self.url(self.context))
741        return
742
743    @action(_('Cancel'), validator=NullValidator)
744    def cancel(self, **data):
745        self.redirect(self.url(self.context))
746
747
748class DocumentDisplayFormPage(IkobaDisplayFormPage):
749    """ Page to view a document
750    """
751    grok.context(ICustomerDocument)
752    grok.name('index')
753    grok.require('waeup.viewCustomer')
754    grok.template('documentpage')
755    form_fields = grok.AutoFields(ICustomerDocument)
756    pnav = 4
757
758    @property
759    def label(self):
760        return _('${a}: Document ${b}', mapping = {
761            'a':self.context.customer.display_fullname,
762            'b':self.context.document_id})
763        return self.context.document_id
764
765
766class DocumentManageFormPage(IkobaEditFormPage):
767    """ Page to edit a document
768    """
769    grok.context(ICustomerDocument)
770    grok.name('manage')
771    grok.require('waeup.manageCustomer')
772    form_fields = grok.AutoFields(ICustomerDocument)
773    pnav = 4
774
775    @property
776    def label(self):
777        return _('${a}: Document ${b}', mapping = {
778            'a':self.context.customer.display_fullname,
779            'b':self.context.document_id})
780        return self.context.document_id
781
782    @action(_('Save'), style='primary')
783    def save(self, **data):
784        msave(self, **data)
785        return
Note: See TracBrowser for help on using the repository browser.