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

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