0001-workflows-add-possibility-to-mark-and-jump-to-marked.patch
tests/test_admin_pages.py | ||
---|---|---|
1909 | 1909 |
resp = resp.click('Edit') |
1910 | 1910 |
assert not 'in_listing' in resp.form.fields.keys() |
1911 | 1911 | |
1912 |
def test_workflows_edit_choice_action(pub): |
|
1913 |
create_superuser(pub) |
|
1914 |
role = create_role() |
|
1915 |
Workflow.wipe() |
|
1916 |
workflow = Workflow(name='foo') |
|
1917 |
workflow.add_status(name='baz') |
|
1918 |
workflow.store() |
|
1919 | ||
1920 |
app = login(get_app(pub)) |
|
1921 |
resp = app.get('/backoffice/workflows/1/') |
|
1922 |
resp = resp.click('baz') |
|
1923 | ||
1924 |
resp.forms[0]['type'] = 'Change Status' |
|
1925 |
resp = resp.forms[0].submit() |
|
1926 |
resp = resp.follow() |
|
1927 | ||
1928 |
resp = resp.click(href='items/1/', index=0) |
|
1929 |
assert 'Previously Marked Status' not in [x[2] for x in resp.form['status'].options] |
|
1930 |
resp.form['status'].value = 'baz' |
|
1931 |
resp = resp.form.submit('submit') |
|
1932 |
resp = resp.follow() |
|
1933 |
resp = resp.follow() |
|
1934 | ||
1935 |
resp = resp.click(href='items/1/', index=0) |
|
1936 |
resp.form['set_marker_on_status'].value = True |
|
1937 |
resp = resp.form.submit('submit') |
|
1938 |
resp = resp.follow() |
|
1939 |
resp = resp.follow() |
|
1940 | ||
1941 |
resp = resp.click(href='items/1/', index=0) |
|
1942 |
assert 'Previously Marked Status' in [x[2] for x in resp.form['status'].options] |
|
1943 | ||
1912 | 1944 |
def test_workflows_action_subpath(pub): |
1913 | 1945 |
create_superuser(pub) |
1914 | 1946 |
role = create_role() |
tests/test_backoffice_pages.py | ||
---|---|---|
7 | 7 |
import StringIO |
8 | 8 |
import time |
9 | 9 |
import hashlib |
10 |
import random |
|
10 | 11 | |
11 | 12 |
import pytest |
12 | 13 |
from webtest import Upload |
... | ... | |
3210 | 3211 |
.parents('li').children('div.value span') |
3211 | 3212 |
.text() == '\'\\xed\\xa0\\x00\'') |
3212 | 3213 | |
3214 |
def test_workflow_jump_previous(pub): |
|
3215 |
user = create_user(pub) |
|
3216 |
create_environment(pub) |
|
3217 | ||
3218 |
wf = Workflow(name='jump around') |
|
3219 |
# North |
|
3220 |
# / \ |
|
3221 |
# West <----> East |
|
3222 |
# | | |
|
3223 |
# | autojump |
|
3224 |
# | | |
|
3225 |
# \ / |
|
3226 |
# South |
|
3227 | ||
3228 |
st1 = wf.add_status('North') |
|
3229 |
st1.id = 'north' |
|
3230 |
st2 = wf.add_status('West') |
|
3231 |
st2.id = 'west' |
|
3232 |
st3 = wf.add_status('East') |
|
3233 |
st3.id = 'east' |
|
3234 |
st4 = wf.add_status('Autojump') |
|
3235 |
st4.id = 'autojump' |
|
3236 |
st5 = wf.add_status('South') |
|
3237 |
st5.id = 'south' |
|
3238 | ||
3239 |
button_by_id = {} |
|
3240 | ||
3241 |
def add_jump(label, src, dst_id): |
|
3242 |
jump = ChoiceWorkflowStatusItem() |
|
3243 |
jump.id = str(random.random()) |
|
3244 |
jump.label = label |
|
3245 |
jump.by = ['logged-users'] |
|
3246 |
jump.status = dst_id |
|
3247 |
src.items.append(jump) |
|
3248 |
jump.parent = src |
|
3249 |
if dst_id != '_previous': |
|
3250 |
jump.set_marker_on_status = True |
|
3251 |
button_by_id[label] = 'button%s' % jump.id |
|
3252 |
return jump |
|
3253 | ||
3254 |
add_jump('Go West', st1, st2.id) |
|
3255 |
add_jump('Go East', st1, st3.id) |
|
3256 |
add_jump('Go South', st2, st5.id) |
|
3257 |
add_jump('Go Autojump', st3, st4.id) |
|
3258 |
add_jump('Go Back', st5, '_previous') |
|
3259 | ||
3260 |
add_jump('Jump West', st3, st2.id) |
|
3261 |
add_jump('Jump East', st2, st3.id) |
|
3262 | ||
3263 |
jump = JumpWorkflowStatusItem() |
|
3264 |
jump.id = '_auto-jump' |
|
3265 |
jump.status = st5.id |
|
3266 |
st4.items.append(jump) |
|
3267 |
jump.parent = st4 |
|
3268 | ||
3269 |
wf.store() |
|
3270 | ||
3271 |
formdef = FormDef.get_by_urlname('form-title') |
|
3272 |
formdef.data_class().wipe() |
|
3273 |
formdef.workflow = wf |
|
3274 |
formdef.store() |
|
3275 | ||
3276 |
formdata = formdef.data_class()() |
|
3277 |
formdata.data = {} |
|
3278 |
formdata.just_created() |
|
3279 |
formdata.store() |
|
3280 | ||
3281 |
app = login(get_app(pub)) |
|
3282 |
resp = app.get('/backoffice/management/form-title/%s/' % formdata.id) |
|
3283 | ||
3284 |
# jump around using buttons |
|
3285 |
resp = resp.form.submit(button_by_id['Go West']).follow() # push (north) |
|
3286 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id |
|
3287 |
resp = resp.form.submit(button_by_id['Go South']).follow() # push (north, west) |
|
3288 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id |
|
3289 |
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north) |
|
3290 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id |
|
3291 |
resp = resp.form.submit(button_by_id['Go South']).follow() # push (north, west) |
|
3292 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id |
|
3293 |
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north) |
|
3294 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st2.id |
|
3295 |
resp = resp.form.submit(button_by_id['Jump East']).follow() # push (north, west) |
|
3296 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st3.id |
|
3297 |
resp = resp.form.submit(button_by_id['Go Autojump']).follow() # push (north, west, east) |
|
3298 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st5.id |
|
3299 | ||
3300 |
# check markers are displayed in /inspect page |
|
3301 |
user.is_admin = True |
|
3302 |
user.store() |
|
3303 |
resp2 = app.get('/backoffice/management/form-title/%s/inspect' % formdata.id) |
|
3304 |
assert 'Markers Stack' in resp2.body |
|
3305 |
assert '<span class="status">East</span>' in resp2.body |
|
3306 |
assert '<span class="status">West</span>' in resp2.body |
|
3307 |
assert '<span class="status">North</span>' in resp2.body |
|
3308 |
assert resp2.body.find('<span class="status">East</span>') < resp2.body.find('<span class="status">West</span>') |
|
3309 |
assert resp2.body.find('<span class="status">West</span>') < resp2.body.find('<span class="status">North</span>') |
|
3310 | ||
3311 |
resp = resp.form.submit(button_by_id['Go Back']).follow() # pop (north, west) |
|
3312 |
assert formdef.data_class().get(formdata.id).status == 'wf-%s' % st3.id |
|
3313 | ||
3314 |
# and do a last jump using the API |
|
3315 |
formdata = formdef.data_class().get(formdata.id) |
|
3316 |
formdata.jump_status('_previous') # pop (north) |
|
3317 |
assert formdata.status == 'wf-%s' % st2.id |
|
3318 | ||
3319 |
formdata = formdef.data_class().get(formdata.id) |
|
3320 |
formdata.jump_status('_previous') # pop () |
|
3321 |
assert formdata.status == 'wf-%s' % st1.id |
|
3322 | ||
3213 | 3323 |
def test_backoffice_fields(pub): |
3214 | 3324 |
user = create_user(pub) |
3215 | 3325 |
create_environment(pub) |
wcs/admin/workflows.py | ||
---|---|---|
153 | 153 |
for status in workflow.possible_status: |
154 | 154 |
i = status.id |
155 | 155 |
for item in status.items: |
156 |
next_status_ids = [x.id for x in item.get_target_status() if x.id != status.id] |
|
156 |
next_status_ids = [x.id for x in item.get_target_status() |
|
157 |
if x.id and x.id != status.id] |
|
157 | 158 |
if not next_status_ids: |
158 | 159 |
next_status_ids = [status.id] |
159 | 160 |
done = {} |
wcs/backoffice/management.py | ||
---|---|---|
2088 | 2088 |
if not isinstance(v, basestring): |
2089 | 2089 |
r += htmltext(' <span class="type">(%r)</span>') % type(v) |
2090 | 2090 |
r += htmltext('</div></li>') |
2091 | ||
2092 |
if '_markers_stack' in (self.filled.workflow_data or {}): |
|
2093 |
r += htmltext('<li><h3>%s</h3></li>') % _('Markers Stack') |
|
2094 |
for marker in reversed(self.filled.workflow_data['_markers_stack']): |
|
2095 |
status = self.filled.get_status(marker['status_id']) |
|
2096 |
if status: |
|
2097 |
r += htmltext('<li><span class="status">%s</span></li>') % status.name |
|
2098 |
else: |
|
2099 |
r += htmltext('<li><span class="status">%s</span></li>') % _('Unknown') |
|
2100 | ||
2091 | 2101 |
r += htmltext('</ul>') |
2092 | 2102 |
r += htmltext('</div>') |
2093 | ||
2094 | 2103 |
return r.getvalue() |
2095 | 2104 | |
2096 | 2105 |
wcs/formdata.py | ||
---|---|---|
404 | 404 |
return None |
405 | 405 |
if not self.formdef: |
406 | 406 |
return None |
407 |
if status.startswith('wf-'): |
|
408 |
status = status[3:] |
|
407 | 409 |
try: |
408 |
status_id = status.split('-')[1] |
|
409 |
wf_status = [x for x in self.formdef.workflow.possible_status if x.id == status_id][0] |
|
410 |
wf_status = [x for x in self.formdef.workflow.possible_status if x.id == status][0] |
|
410 | 411 |
except IndexError: |
411 | 412 |
return None |
412 | 413 |
return wf_status |
... | ... | |
453 | 454 |
return None |
454 | 455 |
return wf_status.handle_form(form, self, user) |
455 | 456 | |
457 |
def pop_previous_marked_status(self): |
|
458 |
if not self.workflow_data or not '_markers_stack' in self.workflow_data: |
|
459 |
return None |
|
460 |
try: |
|
461 |
marker_data = self.workflow_data['_markers_stack'].pop() |
|
462 |
status_id = marker_data['status_id'] |
|
463 |
except IndexError: |
|
464 |
return None |
|
465 |
return self.formdef.workflow.get_status(status_id) |
|
466 | ||
456 | 467 |
def jump_status(self, status_id): |
468 |
if status_id == '_previous': |
|
469 |
previous_status = self.pop_previous_marked_status() |
|
470 |
assert previous_status, 'failed to compute previous status' |
|
471 |
status_id = previous_status.id |
|
457 | 472 |
evo = Evolution() |
458 | 473 |
evo.time = time.localtime() |
459 | 474 |
evo.status = 'wf-%s' % status_id |
... | ... | |
631 | 646 |
d.update(klass.get_substitution_variables(self)) |
632 | 647 | |
633 | 648 |
if self.workflow_data: |
634 |
d.update(self.workflow_data)
|
|
635 |
# pass over uploaded files and attach an extra attribute with the
|
|
636 |
# url to the file.
|
|
649 |
# pass over workflow data to:
|
|
650 |
# - attach an extra url attribute to uploaded files
|
|
651 |
# - ignore "private" attributes
|
|
637 | 652 |
for k, v in self.workflow_data.items(): |
653 |
if k[0] == '_': |
|
654 |
continue |
|
655 |
d[k] = v |
|
638 | 656 |
if isinstance(v, Upload): |
639 | 657 |
try: |
640 | 658 |
formvar, fieldvar = re.match('(.*)_var_(.*)_raw$', k).groups() |
wcs/qommon/static/css/dc2/admin.css | ||
---|---|---|
1525 | 1525 |
font-size: 100%; |
1526 | 1526 |
} |
1527 | 1527 | |
1528 |
ul.form-inspector li span.status { |
|
1529 |
padding: 0 1ex; |
|
1530 |
} |
|
1531 | ||
1528 | 1532 |
ul.form-inspector li div.value { |
1529 | 1533 |
display: block; |
1530 | 1534 |
padding: 0 0 0.5ex 1em; |
wcs/wf/jump.py | ||
---|---|---|
145 | 145 |
return _('Change Status Automatically (to %s)') % wf_status[0].name |
146 | 146 | |
147 | 147 |
def get_parameters(self): |
148 |
return ('status', 'condition', 'trigger', 'by', 'timeout') |
|
148 |
return ('status', 'set_marker_on_status', 'condition', 'trigger', 'by', 'timeout')
|
|
149 | 149 | |
150 | 150 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): |
151 | 151 |
WorkflowStatusJumpItem.add_parameters_widgets(self, form, parameters, prefix, formdef) |
... | ... | |
190 | 190 |
return |
191 | 191 | |
192 | 192 |
if self.must_jump(formdata): |
193 |
wf_status = self.get_target_status() |
|
193 |
wf_status = self.get_target_status(formdata)
|
|
194 | 194 |
if wf_status: |
195 | 195 |
formdata.status = 'wf-%s' % wf_status[0].id |
196 | 196 |
wcs/workflows.py | ||
---|---|---|
1634 | 1634 |
def get_substitution_variables(self, formdata): |
1635 | 1635 |
return {} |
1636 | 1636 | |
1637 |
def get_target_status(self): |
|
1637 |
def get_target_status(self, formdata=None):
|
|
1638 | 1638 |
"""Returns a list of status this item can lead to.""" |
1639 | 1639 |
if not getattr(self, 'status', None): |
1640 | 1640 |
return [] |
1641 | 1641 | |
1642 |
if self.status == '_previous': |
|
1643 |
if formdata is None: |
|
1644 |
# must be in a formdata to compute destination, just give a |
|
1645 |
# fake status for presentation purpose |
|
1646 |
return [WorkflowStatus(_('Previously Marked Status'))] |
|
1647 |
previous_status = formdata.pop_previous_marked_status() |
|
1648 |
if previous_status: |
|
1649 |
return [previous_status] |
|
1650 |
return [] |
|
1651 | ||
1642 | 1652 |
try: |
1643 | 1653 |
return [x for x in self.parent.parent.possible_status if x.id == self.status] |
1644 | 1654 |
except IndexError: |
... | ... | |
1685 | 1695 |
class WorkflowStatusJumpItem(WorkflowStatusItem): |
1686 | 1696 |
status = None |
1687 | 1697 |
endpoint = False |
1698 |
set_marker_on_status = False |
|
1688 | 1699 | |
1689 | 1700 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): |
1690 | 1701 |
if 'status' in parameters: |
1702 |
destinations = [(x.id, x.name) for x in self.parent.parent.possible_status] |
|
1703 | ||
1704 |
# look for existing jumps that are dropping a mark |
|
1705 |
for status in self.parent.parent.possible_status: |
|
1706 |
for item in status.items: |
|
1707 |
if getattr(item, 'set_marker_on_status', False): |
|
1708 |
destinations.append(('_previous', _('Previously Marked Status'))) |
|
1709 |
break |
|
1710 |
else: |
|
1711 |
continue |
|
1712 |
break |
|
1713 | ||
1691 | 1714 |
form.add(SingleSelectWidget, '%sstatus' % prefix, title = _('Status'), value = self.status, |
1692 |
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status]) |
|
1715 |
options = [(None, '---')] + destinations) |
|
1716 | ||
1717 |
if 'set_marker_on_status' in parameters: |
|
1718 |
form.add(CheckboxWidget, '%sset_marker_on_status' % prefix, |
|
1719 |
title=_('Set marker to jump back to current status'), |
|
1720 |
value=self.set_marker_on_status, |
|
1721 |
advanced=not(self.set_marker_on_status)) |
|
1693 | 1722 | |
1694 | 1723 |
def get_parameters(self): |
1695 |
return ('status',) |
|
1724 |
return ('status', 'set_marker_on_status')
|
|
1696 | 1725 | |
1697 | 1726 | |
1698 | 1727 |
def get_role_translation(formdata, role_name): |
... | ... | |
1892 | 1921 | |
1893 | 1922 |
def submit_form(self, form, formdata, user, evo): |
1894 | 1923 |
if form.get_submit() == 'button%s' % self.id: |
1895 |
wf_status = self.get_target_status() |
|
1924 |
wf_status = self.get_target_status(formdata)
|
|
1896 | 1925 |
if wf_status: |
1897 | 1926 |
evo.status = 'wf-%s' % wf_status[0].id |
1927 |
if self.set_marker_on_status: |
|
1928 |
if formdata.workflow_data and '_markers_stack' in formdata.workflow_data: |
|
1929 |
markers_stack = formdata.workflow_data.get('_markers_stack') |
|
1930 |
else: |
|
1931 |
markers_stack = [] |
|
1932 |
markers_stack.append({'status_id': formdata.status[3:]}) |
|
1933 |
formdata.update_workflow_data({'_markers_stack': markers_stack}) |
|
1898 | 1934 |
form.clear_errors() |
1899 | 1935 |
return True # get out of processing loop |
1900 | 1936 | |
... | ... | |
1919 | 1955 |
value=self.backoffice_info_text) |
1920 | 1956 | |
1921 | 1957 |
def get_parameters(self): |
1922 |
return ('by', 'status', 'label', 'backoffice_info_text', 'require_confirmation') |
|
1958 |
return ('by', 'status', 'label', 'backoffice_info_text', |
|
1959 |
'require_confirmation', 'set_marker_on_status') |
|
1923 | 1960 | |
1924 | 1961 |
register_item_class(ChoiceWorkflowStatusItem) |
1925 | 1962 | |
... | ... | |
1940 | 1977 | |
1941 | 1978 |
def submit_form(self, form, formdata, user, evo): |
1942 | 1979 |
if form.is_submitted() and not form.has_errors(): |
1943 |
wf_status = self.get_target_status() |
|
1980 |
wf_status = self.get_target_status(formdata)
|
|
1944 | 1981 |
if wf_status: |
1945 | 1982 |
evo.status = 'wf-%s' % wf_status[0].id |
1946 | 1983 | |
1947 | 1984 |
def get_parameters(self): |
1948 |
return ('status',) |
|
1985 |
return ('status', 'set_marker_on_status')
|
|
1949 | 1986 |
register_item_class(JumpOnSubmitWorkflowStatusItem) |
1950 | 1987 | |
1951 | 1988 | |
1952 |
- |