0001-workflows-add-notification-action-33817.patch
tests/test_workflows.py | ||
---|---|---|
45 | 45 |
from wcs.wf.geolocate import GeolocateWorkflowStatusItem |
46 | 46 |
from wcs.wf.backoffice_fields import SetBackofficeFieldsWorkflowStatusItem |
47 | 47 |
from wcs.wf.redirect_to_url import RedirectToUrlWorkflowStatusItem |
48 |
from wcs.wf.notification import SendNotificationWorkflowStatusItem |
|
48 | 49 | |
49 | 50 |
from utilities import (create_temporary_pub, MockSubstitutionVariables, |
50 | 51 |
clean_temporary_pub) |
... | ... | |
3878 | 3879 |
assert logged_error.exception_message == "name 'foobar' is not defined" |
3879 | 3880 |
assert logged_error.expression == 'foobar == barfoo' |
3880 | 3881 |
assert logged_error.expression_type == 'python' |
3882 | ||
3883 |
def test_notifications(pub, http_requests): |
|
3884 |
formdef = FormDef() |
|
3885 |
formdef.name = 'baz' |
|
3886 |
formdef.fields = [] |
|
3887 |
formdef.store() |
|
3888 | ||
3889 |
formdata = formdef.data_class()() |
|
3890 |
formdata.just_created() |
|
3891 |
formdata.store() |
|
3892 | ||
3893 |
assert not SendNotificationWorkflowStatusItem.is_available() |
|
3894 | ||
3895 |
if not pub.site_options.has_section('variables'): |
|
3896 |
pub.site_options.add_section('variables') |
|
3897 |
pub.site_options.set('variables', 'portal_url', 'https://portal/') |
|
3898 |
assert SendNotificationWorkflowStatusItem.is_available() |
|
3899 | ||
3900 |
item = SendNotificationWorkflowStatusItem() |
|
3901 |
item.to = ['_submitter'] |
|
3902 |
item.title = 'xxx' |
|
3903 |
item.body = 'XXX' |
|
3904 | ||
3905 |
# no user |
|
3906 |
http_requests.empty() |
|
3907 |
item.perform(formdata) |
|
3908 |
assert http_requests.count() == 0 |
|
3909 | ||
3910 |
# user |
|
3911 |
http_requests.empty() |
|
3912 |
user = pub.user_class() |
|
3913 |
user.name_identifiers = ['xxx'] |
|
3914 |
user.store() |
|
3915 |
formdata.user_id = user.id |
|
3916 |
formdata.store() |
|
3917 | ||
3918 |
item.perform(formdata) |
|
3919 |
assert http_requests.count() == 1 |
|
3920 |
assert http_requests.get_last('url') == 'https://portal/api/notification/add/?NameID=xxx' |
|
3921 |
assert json.loads(http_requests.get_last('body')) == { |
|
3922 |
'body': 'XXX', |
|
3923 |
'url': formdata.get_url(), |
|
3924 |
'id': 'formdata:%s' % formdata.get_display_id(), |
|
3925 |
'origin': '', |
|
3926 |
'summary': 'xxx' |
|
3927 |
} |
|
3928 | ||
3929 |
# roles |
|
3930 |
http_requests.empty() |
|
3931 | ||
3932 |
role = Role(name='blah') |
|
3933 |
role.store() |
|
3934 | ||
3935 |
user1 = pub.user_class() |
|
3936 |
user1.roles = [role.id] |
|
3937 |
user1.name_identifiers = ['xxy1'] |
|
3938 |
user1.store() |
|
3939 |
user2 = pub.user_class() |
|
3940 |
user2.roles = [role.id] |
|
3941 |
user2.name_identifiers = ['xxy2'] |
|
3942 |
user2.store() |
|
3943 | ||
3944 |
formdef.workflow_roles = {'_receiver': role.id} |
|
3945 | ||
3946 |
item.to = ['_receiver'] |
|
3947 |
item.perform(formdata) |
|
3948 |
assert http_requests.count() == 2 |
|
3949 |
assert set(x['url'] for x in http_requests.requests) == set([ |
|
3950 |
'https://portal/api/notification/add/?NameID=xxy1', |
|
3951 |
'https://portal/api/notification/add/?NameID=xxy2']) |
tests/utilities.py | ||
---|---|---|
378 | 378 |
def empty(self): |
379 | 379 |
self.requests = [] |
380 | 380 | |
381 |
def count(self): |
|
382 |
return len(self.requests) |
|
383 | ||
381 | 384 | |
382 | 385 |
class SMSMocking(wcs.qommon.sms.MobytSMS): |
383 | 386 |
def get_sms_class(self, mode): |
wcs/wf/notification.py | ||
---|---|---|
1 |
# w.c.s. - web application for online forms |
|
2 |
# Copyright (C) 2005-2019 Entr'ouvert |
|
3 |
# |
|
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, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from quixote import get_publisher |
|
18 | ||
19 |
from qommon import _, ezt |
|
20 |
from qommon.form import * |
|
21 |
from qommon.template import TemplateError |
|
22 |
from qommon import get_logger |
|
23 | ||
24 |
from wcs.roles import Role |
|
25 |
from wcs.workflows import (WorkflowStatusItem, register_item_class, |
|
26 |
template_on_formdata, get_role_translation) |
|
27 |
from .wscall import WebserviceCallStatusItem |
|
28 | ||
29 | ||
30 |
class SendNotificationWorkflowStatusItem(WebserviceCallStatusItem): |
|
31 |
description = N_('Notification') |
|
32 |
key = 'notification' |
|
33 |
category = 'interaction' |
|
34 |
support_substitution_variables = True |
|
35 | ||
36 |
# parameters |
|
37 |
to = [] |
|
38 |
title = None |
|
39 |
body = None |
|
40 |
origin = None |
|
41 | ||
42 |
# webservice parameters |
|
43 |
varname = 'notification' |
|
44 |
post = False |
|
45 |
_method = 'POST' |
|
46 |
response_type = 'json' |
|
47 | ||
48 |
action_on_app_error = ':pass' |
|
49 |
action_on_4xx = ':pass' |
|
50 |
action_on_5xx = ':pass' |
|
51 |
action_on_bad_data = ':pass' |
|
52 |
action_on_network_errors = ':pass' |
|
53 |
notify_on_errors = True |
|
54 |
record_errors = False |
|
55 | ||
56 |
@classmethod |
|
57 |
def is_available(cls, workflow=None): |
|
58 |
return bool(cls.get_api_url() is not None) |
|
59 | ||
60 |
@classmethod |
|
61 |
def get_api_url(cls): |
|
62 |
url = get_publisher().get_site_option('portal_url', 'variables') |
|
63 |
if not url: |
|
64 |
return None |
|
65 |
return url + 'api/notification/add/' |
|
66 | ||
67 |
def get_jump_label(self, target_id): |
|
68 |
return self.description |
|
69 | ||
70 |
def fill_admin_form(self, form): |
|
71 |
self.add_parameters_widgets(form, self.get_parameters()) |
|
72 | ||
73 |
def get_parameters(self): |
|
74 |
return ('to', 'title', 'body', 'origin', 'condition') |
|
75 | ||
76 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): |
|
77 |
if 'to' in parameters: |
|
78 |
form.add(WidgetList, '%sto' % prefix, title=_('To'), |
|
79 |
element_type=SingleSelectWidget, |
|
80 |
value=self.to, |
|
81 |
add_element_label=_('Add Role'), |
|
82 |
element_kwargs={'render_br': False, |
|
83 |
'options': [(None, '---', None)] + |
|
84 |
self.get_list_of_roles(include_logged_in_users=False)}) |
|
85 |
if 'title' in parameters: |
|
86 |
form.add(StringWidget, '%stitle' % prefix, title=_('Title'), |
|
87 |
value=self.title, size=80, |
|
88 |
validation_function=ComputedExpressionWidget.validate_template) |
|
89 |
if 'body' in parameters: |
|
90 |
form.add(TextWidget, '%sbody' % prefix, title=_('Body'), |
|
91 |
value=self.body, cols=80, rows=5, |
|
92 |
validation_function=ComputedExpressionWidget.validate_template) |
|
93 |
if 'origin' in parameters: |
|
94 |
form.add(StringWidget, '%sorigin' % prefix, title=_('Origin'), |
|
95 |
value=self.origin, required=False, |
|
96 |
advanced=not(self.origin)) |
|
97 |
WorkflowStatusItem.add_parameters_widgets(self, form, parameters, |
|
98 |
prefix=prefix, formdef=formdef) |
|
99 | ||
100 |
def perform(self, formdata): |
|
101 |
if not (self.is_available() and self.to and self.title and self.body): |
|
102 |
return |
|
103 | ||
104 |
try: |
|
105 |
title = template_on_formdata(formdata, |
|
106 |
self.compute(self.title, render=False), |
|
107 |
autoescape=False) |
|
108 |
except TemplateError as e: |
|
109 |
get_logger().error('error in template for notification title [%s], ' |
|
110 |
'mail could not be generated: %s' % (notified_url, str(e))) |
|
111 |
return |
|
112 | ||
113 |
try: |
|
114 |
body = template_on_formdata(formdata, |
|
115 |
self.compute(self.body, render=False), |
|
116 |
autoescape=False) |
|
117 |
except TemplateError as e: |
|
118 |
get_logger().error('error in template for notification body [%s], ' |
|
119 |
'mail could not be generated: %s' % (notified_url, str(e))) |
|
120 |
return |
|
121 | ||
122 |
self.post_data = { |
|
123 |
'summary': title, |
|
124 |
'body': body, |
|
125 |
'url': formdata.get_url(), |
|
126 |
'origin': self.origin or '', |
|
127 |
'id': 'formdata:%s' % formdata.get_display_id(), |
|
128 |
} |
|
129 |
self.url = self.get_api_url() |
|
130 | ||
131 |
users = [] |
|
132 |
for dest in self.to: |
|
133 |
if dest == '_submitter': |
|
134 |
users.append(formdata.get_user()) |
|
135 |
continue |
|
136 | ||
137 |
dest = get_role_translation(formdata, dest) |
|
138 |
try: |
|
139 |
role = Role.get(dest) |
|
140 |
except KeyError: |
|
141 |
continue |
|
142 |
users.extend(get_publisher().user_class.get_users_with_role(role.id)) |
|
143 | ||
144 |
for user in users: |
|
145 |
if not user: |
|
146 |
continue |
|
147 |
for name_id in (user.name_identifiers or []): |
|
148 |
self.qs_data = {'NameID': name_id} |
|
149 |
super(SendNotificationWorkflowStatusItem, self).perform(formdata) |
|
150 | ||
151 |
register_item_class(SendNotificationWorkflowStatusItem) |
wcs/workflows.py | ||
---|---|---|
2794 | 2794 |
import wf.profile |
2795 | 2795 |
import wf.backoffice_fields |
2796 | 2796 |
import wf.redirect_to_url |
2797 |
import wf.notification |
|
2797 | 2798 | |
2798 | 2799 |
from wf.export_to_model import ExportToModel |
2799 |
- |