Projet

Général

Profil

0001-workflows-implement-status-change-after-editable-act.patch

Frédéric Péters, 23 décembre 2015 11:26

Télécharger (21,1 ko)

Voir les différences:

Subject: [PATCH] workflows: implement status change after "editable" action
 (#9329)

 tests/test_form_pages.py     |  25 ++++++++++-
 wcs/backoffice/management.py |  18 +-------
 wcs/backoffice/submission.py |   6 +--
 wcs/forms/common.py          |  24 ++++++++++
 wcs/forms/root.py            | 101 ++++++++++++++++++++-----------------------
 wcs/workflows.py             |   2 +-
 6 files changed, 100 insertions(+), 76 deletions(-)
tests/test_form_pages.py
931 931
    assert 'barXYZ' in page.body
932 932

  
933 933
    resp = page.forms[0].submit('button_editable')
934
    assert resp.location == 'http://example.net/test/%s/wfedit' % data_id
934
    assert resp.location.startswith('http://example.net/test/%s/wfedit-' % data_id)
935 935
    resp = resp.follow()
936 936
    assert resp.forms[0]['f1'].value == 'foo'
937 937
    resp.forms[0]['f1'] = 'foo2'
......
948 948
    assert 'foo2' in resp.body # modified value is there
949 949
    assert 'barXYZ' in resp.body # unchanged value is still there
950 950

  
951
    # modify workflow to jump to another status after the edition
952
    st2 = workflow.add_status('Status2', 'st2')
953
    editable.status = st2.id
954
    workflow.store()
955

  
956
    assert formdef.data_class().get(data_id).status == 'wf-%s' % st1.id
957
    page = login(get_app(pub), username='foo', password='foo').get('/test/%s/' % data_id)
958
    assert 'button_editable-button' in page.body
959
    assert 'barXYZ' in page.body
960

  
961
    resp = page.forms[0].submit('button_editable')
962
    assert resp.location.startswith('http://example.net/test/%s/wfedit-' % data_id)
963
    resp = resp.follow()
964
    assert resp.forms[0]['f1'].value == 'foo2'
965
    resp.forms[0]['f1'] = 'foo3'
966
    resp = resp.forms[0].submit('submit')
967
    assert resp.forms[0]['f3'].value == 'barXYZ'
968
    resp = resp.forms[0].submit('submit')
969
    assert resp.location == 'http://example.net/test/%s/' % data_id
970
    resp = resp.follow()
971
    assert 'foo3' in resp.body # modified value is there
972
    assert 'barXYZ' in resp.body # unchanged value is still there
973
    assert formdef.data_class().get(data_id).status == 'wf-%s' % st2.id
951 974

  
952 975
def test_form_count_dispatching(pub):
953 976
    user = create_user(pub)
wcs/backoffice/management.py
51 51
from wcs.categories import Category
52 52
from wcs.formdef import FormDef
53 53
from wcs.roles import logged_users_role
54
from wcs.workflows import EditableWorkflowStatusItem
55 54

  
56 55
from .submission import FormFillPage
57 56

  
......
1580 1579

  
1581 1580

  
1582 1581
class FormBackOfficeStatusPage(FormStatusPage):
1583
    _q_exports = ['', 'download', 'json', 'wfedit', 'action']
1582
    _q_exports = ['', 'download', 'json', 'action']
1583
    form_page_class = FormFillPage
1584 1584

  
1585 1585
    def html_top(self, title = None):
1586 1586
        return html_top('management', title)
......
1704 1704

  
1705 1705
        return r.getvalue()
1706 1706

  
1707
    def wfedit(self):
1708
        wf_status = self.filled.get_status()
1709
        for item in wf_status.items:
1710
            if not isinstance(item, EditableWorkflowStatusItem):
1711
                continue
1712
            if item.check_auth(self.filled, get_request().user):
1713
                f = FormFillPage(self.formdef.url_name)
1714
                f.edit_mode = True
1715
                get_response().breadcrumb = get_response().breadcrumb[:-1]
1716
                get_response().breadcrumb.append( ('wfedit', _('Edit')) )
1717
                return f._q_index(editing=self.filled)
1718

  
1719
        raise errors.AccessForbiddenError()
1720

  
1721 1707

  
1722 1708
class FakeField(object):
1723 1709
    def __init__(self, id, type_, label):
wcs/backoffice/submission.py
27 27

  
28 28

  
29 29
class FormFillPage(PublicFormFillPage):
30
    edit_mode = False
31

  
32 30
    def html_top(self, *args, **kwargs):
33 31
        return html_top('submission', *args, **kwargs)
34 32

  
......
64 62

  
65 63
        return r.getvalue()
66 64

  
67
    def form_side(self, step_no, page_no=0, log_detail=None, data=None, editing=None):
65
    def form_side(self, step_no, page_no=0, log_detail=None, data=None):
68 66
        r = TemplateIO(html=True)
69 67
        get_response().filter['sidebar'] = self.get_sidebar(data)
70 68
        r += htmltext('<div id="side">')
71
        r += self.step(step_no, page_no, log_detail, data=data, editing=editing)
69
        r += self.step(step_no, page_no, log_detail, data=data)
72 70
        r += htmltext('</div> <!-- #side -->')
73 71
        return r.getvalue()
74 72

  
wcs/forms/common.py
24 24

  
25 25
from wcs.fields import WidgetField, FileField
26 26
from wcs import file_validation
27
from wcs.workflows import EditableWorkflowStatusItem
27 28

  
28 29
from qommon import template
29 30
from qommon import get_logger
......
91 92
class FormStatusPage(Directory):
92 93
    _q_exports = ['', 'download', 'json', 'action']
93 94
    _q_extra_exports = []
95
    form_page_class = None
94 96

  
95 97
    def html_top(self, title = None):
96 98
        template.html_top(title = title, default_org = _('Forms'))
......
586 588
        if component == 'files':
587 589
            self.check_receiver()
588 590
            return FilesDirectory(self.filled)
591
        if component.startswith('wfedit-'):
592
            return self.wfedit(component[len('wfedit-'):])
589 593
        return Directory._q_lookup(self, component)
590 594

  
591 595
    def _q_traverse(self, path):
592 596
        get_response().breadcrumb.append(
593 597
            (str(self.filled.id) + '/',str(self.filled.id)))
594 598
        return super(FormStatusPage, self)._q_traverse(path)
599

  
600
    def wfedit(self, action_id):
601
        wf_status = self.filled.get_status()
602
        for item in wf_status.items:
603
            if item.id != action_id:
604
                continue
605
            if not isinstance(item, EditableWorkflowStatusItem):
606
                break
607
            if not item.check_auth(self.filled, get_request().user):
608
                break
609
            f = self.form_page_class(self.formdef.url_name)
610
            f.edit_mode = True
611
            f.edited_data = self.filled
612
            f.edit_action_id = action_id
613
            f.action_url = 'wfedit-%s' % action_id
614
            get_response().breadcrumb = get_response().breadcrumb[:-1]
615
            get_response().breadcrumb.append((f.action_url, _('Edit')))
616
            return f._q_index()
617

  
618
        raise errors.AccessForbiddenError()
wcs/forms/root.py
48 48
from wcs.formdef import FormDef
49 49
from wcs.formdata import FormData
50 50
from wcs.roles import logged_users_role
51
from wcs.workflows import Workflow, EditableWorkflowStatusItem
51
from wcs.workflows import Workflow
52 52

  
53 53
from qommon.admin.texts import TextsDirectory
54 54

  
......
221 221

  
222 222
        self.tokens = TokensDirectory(self.formdef)
223 223
        self.code = TrackingCodesDirectory(self.formdef)
224
        self.action_url = '.'
225
        self.edit_mode = False
224 226

  
225 227
        self.page_number = len([
226 228
                x for x in self.formdef.fields[1:] if x.type == 'page']) + 1
......
255 257
                if not user_roles.intersection(other_roles):
256 258
                    raise errors.AccessForbiddenError()
257 259

  
258
    def step(self, step_no, page_no = 0, log_detail = None, data = None, editing = None):
260
    def step(self, step_no, page_no=0, log_detail=None, data=None):
259 261
        if step_no == 0:
260 262
            self.substvars['current_page_no'] = str(page_no + 1)
261 263
        if log_detail:
......
282 284
        if step_no > 0:
283 285
            current_position = len(page_labels) + step_no
284 286

  
285
        if self.formdef.confirmation and not editing:
287
        if self.formdef.confirmation and not self.edit_mode:
286 288
            page_labels.append(_('Validating'))
287 289

  
288 290
        r = TemplateIO(html=True)
......
327 329

  
328 330
        return self.page(0)
329 331

  
330
    def page(self, page_no, page_change = True, log_detail = None, editing = None):
332
    def page(self, page_no, page_change=True, log_detail=None):
331 333
        r = TemplateIO(html=True)
332 334
        displayed_fields = []
333 335

  
......
344 346
        form = self.formdef.create_form(page_no, displayed_fields)
345 347
        if get_request().is_in_backoffice():
346 348
            form.attrs['data-is-backoffice'] = 'true'
347
        if editing:
348
            form.action = 'wfedit'
349
        else:
350
            form.action = '.'
349
        form.action = self.action_url
351 350
        # include a data-has-draft attribute on the <form> element when a draft
352 351
        # already exists for the form; this will activate the autosave.
353 352
        magictoken = get_request().form.get('magictoken')
354 353
        if magictoken:
355 354
            form_data = session.get_by_magictoken(magictoken, {})
356
            if self.formdef.enable_tracking_codes and not editing:
355
            if self.formdef.enable_tracking_codes and not self.edit_mode:
357 356
                form.attrs['data-has-draft'] = 'yes'
358 357
        else:
359 358
            form_data = {}
......
369 368
            cancelurl = get_request().form['cancelurl']
370 369
            session.add_magictoken(magictoken, {'__cancelurl': cancelurl})
371 370

  
372
        if editing and page_no == self.page_number - 1:
371
        if self.edit_mode and page_no == self.page_number - 1:
373 372
            form.add_submit('submit', _('Save Changes'))
374 373
        elif not self.formdef.confirmation and page_no == self.page_number - 1:
375 374
            form.add_submit('submit', _('Submit'))
......
432 431
                req.form = {}
433 432

  
434 433
        self.html_top(self.formdef.name)
435
        r += self.form_side(0, page_no, log_detail=log_detail, data=data, editing=editing)
434
        r += self.form_side(0, page_no, log_detail=log_detail, data=data)
436 435
        r += get_session().display_message()
437 436

  
438 437
        form.add_hidden('step', '0')
439 438
        form.add_hidden('page', page_no)
440 439

  
441 440
        form.add_submit('cancel', _('Cancel'), css_class = 'cancel')
442
        if self.formdef.enable_tracking_codes and not editing:
441
        if self.formdef.enable_tracking_codes and not self.edit_mode:
443 442
            form.add_submit('savedraft', _('Save Draft'), css_class = 'save-draft',
444 443
                    attrs={'style': 'display: none'})
445 444

  
446 445
        r += form.render()
447 446
        return r.getvalue()
448 447

  
449
    def form_side(self, step_no, page_no=0, log_detail=None, data=None, editing=None):
448
    def form_side(self, step_no, page_no=0, log_detail=None, data=None):
450 449
        '''Create the elements that typically appear aside the main form
451 450
           (tracking code and steps).'''
452 451
        r = TemplateIO(html=True)
453 452
        r += htmltext('<div id="side">')
454 453
        if self.formdef.enable_tracking_codes:
455
            r += self.tracking_code_box(data, editing=editing)
456
        r += self.step(step_no, page_no, log_detail, data=data, editing=editing)
454
            r += self.tracking_code_box(data)
455
        r += self.step(step_no, page_no, log_detail, data=data)
457 456
        r += htmltext('</div> <!-- #side -->')
458 457
        return r.getvalue()
459 458

  
460
    def tracking_code_box(self, data, editing=False):
459
    def tracking_code_box(self, data):
461 460
        '''Create the tracking code box, it displays the current tracking code
462 461
           or a 'save' button if it has not yet been created.'''
463 462
        r = TemplateIO(html=True)
......
477 476
            r += htmltext('<a rel="popup" href="%s">%s</a>') % (
478 477
                    'code/%s/' % tracking_code, tracking_code)
479 478
        else:
480
            if editing:
479
            if self.edit_mode:
481 480
                return ''
482 481
            r += htmltext('<button>%s</button>') % _('Save')
483 482
        r += TextsDirectory.get_html_text('tracking-code-short-text')
......
507 506
                raise errors.AccessForbiddenError()
508 507
        return False
509 508

  
510
    def _q_index(self, log_detail = None, editing = None):
509
    def _q_index(self, log_detail=None):
511 510
        self.check_role()
512 511
        if self.check_disabled():
513 512
            return redirect(self.check_disabled())
......
533 532
            # first hit on first page, if tracking code are enabled and we
534 533
            # are not editing an existing formdata, generate a new tracking
535 534
            # code.
536
            if not editing and self.formdef.enable_tracking_codes and not get_request().form.has_key('mt'):
535
            if not self.edit_mode and self.formdef.enable_tracking_codes and not get_request().form.has_key('mt'):
537 536
                tracking_code = get_publisher().tracking_code_class()
538 537
                tracking_code.store()
539 538
                token = randbytes(8)
......
541 540
                session.add_magictoken(token, {'future_tracking_code': tracking_code.id})
542 541

  
543 542
        existing_formdata = None
544
        if editing:
545
            existing_formdata = editing.data
543
        if self.edit_mode:
544
            existing_formdata = self.edited_data.data
546 545
            if not get_request().form:
547 546
                # on the initial visit editing the form (i.e. not after
548 547
                # clicking for previous or next page), we need to load the
549 548
                # existing data into the session
550
                editing.feed_session()
549
                self.edited_data.feed_session()
551 550
                token = randbytes(8)
552 551
                get_request().form['magictoken'] = token
553
                session.add_magictoken(token, editing.data)
552
                session.add_magictoken(token, self.edited_data.data)
554 553
        elif self.formdef.only_allow_one:
555 554
            user_forms = get_user_forms(self.formdef)
556 555
            if [x for x in user_forms if not x.is_draft()]:
......
593 592
                    self.feed_current_data(magictoken)
594 593
                    return self.page(page_no, True)
595 594
            self.feed_current_data(None)
596
            return self.page(0, editing = editing)
595
            return self.page(0)
597 596

  
598 597
        if form.get_submit() == 'cancel':
599 598
            get_logger().info('form %s - cancel' % (self.formdef.name))
600
            if editing:
599
            if self.edit_mode:
601 600
                return redirect('.')
602 601
            try:
603 602
                magictoken = form.get_widget('magictoken').parse()
......
634 633
                form.add_submit('savedraft')
635 634
            form.add_submit('submit')
636 635
            if page_no > 0 and form.get_submit() == 'previous':
637
                return self.previous_page(page_no, magictoken, editing = editing)
636
                return self.previous_page(page_no, magictoken)
638 637

  
639 638
            if self.formdef.enable_tracking_codes and form.get_submit() == 'removedraft':
640 639
                self.remove_draft()
......
657 656
                    # page hidden field had an error in its submission), in
658 657
                    # that case we just fall back to the first page.
659 658
                    page_no = 0
660
                return self.page(page_no, page_change = False, editing = editing)
659
                return self.page(page_no, page_change=False)
661 660

  
662 661
            form_data = session.get_by_magictoken(magictoken, {})
663 662
            data = self.formdef.get_data(form)
......
693 692
                        if field.convert_value_to_str:
694 693
                            v = field.convert_value_to_str(v)
695 694
                        req.form['f%s' % k] = v
696
                if editing:
697
                    form = self.formdef.create_view_form(form_data, use_tokens = False)
698
                    return self.submitted_existing(form, editing)
695
                if self.edit_mode:
696
                    form = self.formdef.create_view_form(form_data, use_tokens=False)
697
                    return self.submitted_existing(form)
699 698
                if self.formdef.confirmation:
700 699
                    return self.validating(form_data)
701 700
                else:
......
711 710
                        form.add_submit('savedraft')
712 711

  
713 712
            else:
714
                return self.page(page_no, editing = editing)
713
                return self.page(page_no)
715 714

  
716 715
        if step == 1:
717 716
            form.add_submit('previous')
718 717
            magictoken = form.get_widget('magictoken').parse()
719 718

  
720 719
            if form.get_submit() == 'previous':
721
                return self.previous_page(self.page_number, magictoken, editing = editing)
720
                return self.previous_page(self.page_number, magictoken)
722 721
            magictoken = form.get_widget('magictoken').parse()
723 722
            form_data = session.get_by_magictoken(magictoken, {})
724 723
            data = self.formdef.get_data(form)
......
739 738
            form_data = session.get_by_magictoken(magictoken, {})
740 739

  
741 740
            if form.get_submit() == 'previous':
742
                return self.previous_page(self.page_number, magictoken, editing = editing)
741
                return self.previous_page(self.page_number, magictoken)
743 742

  
744 743
            if self.formdef.enable_tracking_codes and form.get_submit() == 'removedraft':
745 744
                self.remove_draft()
......
761 760
            return self.submitted(form, existing_formdata)
762 761

  
763 762

  
764
    def previous_page(self, page_no, magictoken, editing = None):
763
    def previous_page(self, page_no, magictoken):
765 764
        session = get_session()
766 765
        form_data = session.get_by_magictoken(magictoken, {})
767 766

  
......
777 776
            if previous_page.is_visible(form_data, self.formdef):
778 777
                break
779 778

  
780
        return self.page(page_no, page_change = True, editing = editing)
779
        return self.page(page_no, page_change=True)
781 780

  
782 781
    def remove_draft(self):
783 782
        magictoken = get_request().form.get('magictoken')
......
930 929
            code.id = magictoken_data['future_tracking_code']
931 930
        code.formdata = formdata # this will .store() the code
932 931

  
933
    def submitted_existing(self, form, editing):
934
        old_data = editing.data
935
        editing.data = self.formdef.get_data(form)
936
        editing.store()
937
        return redirect('.')
932
    def submitted_existing(self, form):
933
        self.edited_data.data = self.formdef.get_data(form)
934
        self.edited_data.store()
935
        wf_status = self.edited_data.get_status()
936
        url = None
937
        for item in wf_status.items:
938
            if item.id == self.edit_action_id and item.status:
939
                self.edited_data.jump_status(item.status)
940
                url = self.edited_data.perform_workflow()
941
                break
942
        return redirect(url or '.')
938 943

  
939 944
    def tempfile(self):
940 945
        self.check_role()
......
1386 1391

  
1387 1392

  
1388 1393
class PublicFormStatusPage(FormStatusPage):
1389
    _q_exports = ['', 'download', 'status', 'wfedit']
1394
    _q_exports = ['', 'download', 'status']
1395
    form_page_class = FormPage
1390 1396

  
1391 1397
    def __init__(self, *args, **kwargs):
1392 1398
        FormStatusPage.__init__(self, *args, **kwargs)
......
1409 1415
            r += htmltext('</div>')
1410 1416
            return r.getvalue()
1411 1417

  
1412
    def wfedit(self):
1413
        wf_status = self.filled.get_status()
1414
        for item in wf_status.items:
1415
            if not isinstance(item, EditableWorkflowStatusItem):
1416
                continue
1417
            if item.check_auth(self.filled, get_request().user):
1418
                f = FormPage(self.formdef.url_name)
1419
                get_response().breadcrumb = get_response().breadcrumb[:-1]
1420
                get_response().breadcrumb.append( ('wfedit', _('Edit')) )
1421
                return f._q_index(editing = self.filled)
1422

  
1423
        raise errors.AccessForbiddenError()
1424

  
1425 1418

  
1426 1419
TextsDirectory.register('welcome-logged',
1427 1420
        N_('Welcome text on home page for logged users'))
wcs/workflows.py
1862 1862

  
1863 1863
    def submit_form(self, form, formdata, user, evo):
1864 1864
        if form.get_submit() == 'button%s' % self.id:
1865
            return formdata.get_url(backoffice=get_request().is_in_backoffice()) + 'wfedit'
1865
            return formdata.get_url(backoffice=get_request().is_in_backoffice()) + 'wfedit-%s' % self.id
1866 1866

  
1867 1867
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
1868 1868
        if 'by' in parameters:
1869
-