Projet

Général

Profil

0001-workflows-add-action-to-update-user-profile-10622.patch

Frédéric Péters, 18 avril 2016 18:01

Télécharger (11,7 ko)

Voir les différences:

Subject: [PATCH] workflows: add action to update user profile (#10622)

 tests/test_workflow_import.py |  16 ++++
 tests/test_workflows.py       |  35 ++++++++
 wcs/qommon/misc.py            |   3 +
 wcs/wf/profile.py             | 193 ++++++++++++++++++++++++++++++++++++++++++
 wcs/workflows.py              |   1 +
 5 files changed, 248 insertions(+)
 create mode 100644 wcs/wf/profile.py
tests/test_workflow_import.py
11 11
from wcs.wf.wscall import WebserviceCallStatusItem
12 12
from wcs.wf.dispatch import DispatchWorkflowStatusItem
13 13
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
14
from wcs.wf.profile import UpdateUserProfileStatusItem
14 15
from wcs.roles import Role
15 16
from wcs.fields import StringField
16 17

  
......
450 451
    wf2 = assert_import_export_works(wf, include_id=True)
451 452
    assert wf2.global_actions[0].triggers[-1].id == trigger.id
452 453
    assert wf2.global_actions[0].triggers[-1].anchor == trigger.anchor
454

  
455

  
456
def test_profile_action():
457
    wf = Workflow(name='status')
458
    st1 = wf.add_status('Status1', 'st1')
459

  
460
    item = UpdateUserProfileStatusItem()
461
    item.id = '_item'
462
    item.fields = [{'field_id': '__email', 'value': '=form_var_foo'}]
463
    st1.items.append(item)
464
    item.parent = st1
465

  
466
    wf2 = assert_import_export_works(wf)
467
    item2 = wf2.possible_status[0].items[0]
468
    assert item2.fields == [{'field_id': '__email', 'value': '=form_var_foo'}]
tests/test_workflows.py
21 21
from wcs.wf.dispatch import DispatchWorkflowStatusItem
22 22
from wcs.wf.form import FormWorkflowStatusItem, WorkflowFormFieldsFormDef
23 23
from wcs.wf.jump import JumpWorkflowStatusItem, _apply_timeouts
24
from wcs.wf.profile import UpdateUserProfileStatusItem
24 25
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
25 26
from wcs.wf.remove import RemoveWorkflowStatusItem
26 27
from wcs.wf.roles import AddRoleWorkflowStatusItem, RemoveRoleWorkflowStatusItem
......
1216 1217
    pub.apply_global_action_timeouts()
1217 1218
    assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'green'
1218 1219
    formdata1.store()
1220

  
1221
def test_profile(pub):
1222
    user = pub.user_class()
1223
    user.store()
1224

  
1225
    formdef = FormDef()
1226
    formdef.name = 'baz'
1227
    formdef.fields = [
1228
        StringField(id='1', label='Test', type='string', varname='foo'),
1229
    ]
1230
    formdef.store()
1231

  
1232
    formdata = formdef.data_class()()
1233
    formdata.user_id = user.id
1234
    formdata.data = {'1': 'bar@localhost'}
1235

  
1236
    item = UpdateUserProfileStatusItem()
1237
    item.fields = [{'field_id': '__email', 'value': '=form_var_foo'}]
1238
    item.perform(formdata)
1239
    assert pub.user_class.get(user.id).email == 'bar@localhost'
1240

  
1241
    formdata.data = {'1': 'Plop'}
1242
    item.fields = [{'field_id': '__name', 'value': '=form_var_foo'}]
1243
    item.perform(formdata)
1244
    assert pub.user_class.get(user.id).name == 'Plop'
1245

  
1246
    from wcs.admin.settings import UserFieldsFormDef
1247
    formdef = UserFieldsFormDef(pub)
1248
    formdef.fields = [StringField(id='3', label='test', type='string', varname='plop')]
1249
    formdef.store()
1250

  
1251
    item.fields = [{'field_id': 'plop', 'value': '=form_var_foo'}]
1252
    item.perform(formdata)
1253
    assert pub.user_class.get(user.id).form_data == {'3': 'Plop'}
wcs/qommon/misc.py
285 285
def http_get_page(url, headers={}, timeout=None):
286 286
    return _http_request(url, headers=headers, timeout=timeout)
287 287

  
288
def http_put_request(url, body=None, headers={}, timeout=None):
289
    return _http_request(url, 'PUT', body, headers, timeout=timeout)
290

  
288 291
def http_post_request(url, body=None, headers={}, timeout=None):
289 292
    return _http_request(url, 'POST', body, headers, timeout=timeout)
290 293

  
wcs/wf/profile.py
1
# w.c.s. - web application for online forms
2
# Copyright (C) 2005-2016  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
import json
18
import urlparse
19
import xml.etree.ElementTree as ET
20

  
21
from quixote import get_publisher, get_response
22

  
23
from qommon.form import (CompositeWidget, SingleSelectWidget, StringWidget,
24
        WidgetListAsTable)
25
from qommon.ident.idp import is_idp_managing_user_attributes
26
from qommon.misc import http_put_request
27
from qommon.publisher import get_cfg, get_logger
28

  
29
from wcs.api_utils import sign_url, get_secret_and_orig, MissingSecret
30
from wcs.workflows import XmlSerialisable, WorkflowStatusItem, register_item_class
31

  
32

  
33
def user_ws_url(user_uuid):
34
    idps = get_cfg('idp', {})
35
    entity_id = idps.values()[0]['metadata_url']
36
    base_url = entity_id.split('idp/saml2/metadata')[0]
37
    url = urlparse.urljoin(base_url, '/api/users/%s/' % user_uuid)
38
    secret, orig = get_secret_and_orig(url)
39
    url += '?orig=%s' % orig
40
    return sign_url(url, secret)
41

  
42

  
43
class ProfileUpdateRowWidget(CompositeWidget):
44
    def __init__(self, name, value=None, **kwargs):
45
        CompositeWidget.__init__(self, name, value, **kwargs)
46
        if not value:
47
            value = {}
48

  
49
        fields = []
50
        users_cfg = get_cfg('users', {})
51
        user_formdef = get_publisher().user_class.get_formdef()
52
        if not user_formdef or not users_cfg.get('field_name'):
53
            fields.append(('__name', _('Name'), '__name'))
54
        if not user_formdef or not users_cfg.get('field_email'):
55
            fields.append(('__email', _('Email'), '__email'))
56
        if user_formdef and user_formdef.fields:
57
            for field in user_formdef.fields:
58
                if field.varname:
59
                    fields.append((field.varname, field.label, field.varname))
60

  
61
        self.add(SingleSelectWidget, name='field_id', title=_('Field'),
62
                value=value.get('field_id'),
63
                options=fields, **kwargs)
64
        self.add(StringWidget, name='value', title=_('Value'),
65
                value=value.get('value'))
66

  
67
    def _parse(self, request):
68
        if self.get('value') and self.get('field_id'):
69
            self.value = {
70
                'value': self.get('value'),
71
                'field_id': self.get('field_id')
72
            }
73
        else:
74
            self.value = None
75

  
76

  
77
class ProfileUpdateTableWidget(WidgetListAsTable):
78
    readonly = False
79
    def __init__(self, name, **kwargs):
80
        super(ProfileUpdateTableWidget, self).__init__(name,
81
                element_type=ProfileUpdateRowWidget, **kwargs)
82

  
83
class FieldNode(XmlSerialisable):
84
    node_name = 'field'
85

  
86
    def __init__(self, rule={}):
87
        self.field_id = rule.get('field_id')
88
        self.value = rule.get('value')
89

  
90
    def as_dict(self):
91
        return {
92
            'field_id': self.field_id,
93
            'value': self.value
94
        }
95

  
96
    def get_parameters(self):
97
        return ('field_id', 'value')
98

  
99

  
100
class UpdateUserProfileStatusItem(WorkflowStatusItem):
101
    description = N_('Update User Profile')
102
    key = 'update_user_profile'
103

  
104
    fields = None
105

  
106
    def get_parameters(self):
107
        return ('fields',)
108

  
109
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
110
        if 'fields' in parameters:
111
            form.add(ProfileUpdateTableWidget, '%sfields' % prefix,
112
                    title=_('Profile Update'),
113
                    value=self.fields)
114

  
115
    def fields_export_to_xml(self, item, charset, include_id=False):
116
        if not self.fields:
117
            return
118

  
119
        fields_node = ET.SubElement(item, 'fields')
120
        for field in self.fields:
121
            fields_node.append(FieldNode(field).export_to_xml(charset=charset,
122
                include_id=include_id))
123

  
124
        return fields_node
125

  
126
    def fields_init_with_xml(self, elem, charset, include_id=False):
127
        fields = []
128
        if elem is None:
129
            return
130
        for field_xml_node in elem.findall('field'):
131
            field_node = FieldNode()
132
            field_node.init_with_xml(field_xml_node, charset,
133
                    include_id=include_id)
134
            fields.append(field_node.as_dict())
135
        if fields:
136
            self.fields = fields
137

  
138
    def perform(self, formdata):
139
        if not self.fields:
140
            return
141
        user = formdata.get_user()
142
        if not user:
143
            return
144
        get_publisher().substitutions.feed(formdata)
145
        new_data = {}
146
        for field in self.fields:
147
            new_data[field.get('field_id')] = self.compute(field.get('value'))
148

  
149
        user_formdef = get_publisher().user_class.get_formdef()
150
        new_user_data = {}
151
        for field in user_formdef.fields:
152
            if field.varname in new_data:
153
                new_user_data[field.id] = new_data.get(field.varname)
154

  
155
        if '__name' in new_data:
156
            user.name = new_data.get('__name')
157
        if '__email' in new_data:
158
            user.email = new_data.get('__email')
159
        if not user.form_data and new_user_data:
160
            user.form_data = {}
161
        if new_user_data:
162
            user.form_data.update(new_user_data)
163
        if user.form_data:
164
            user.set_attributes_from_formdata(user.form_data)
165
        user.store()
166

  
167
        if user.name_identifiers and is_idp_managing_user_attributes():
168
            self.perform_idp(user, new_data)
169

  
170
    def perform_idp(self, user, new_data):
171
        user_uuid = user.name_identifiers[0]
172
        try:
173
            url = user_ws_url(user_uuid)
174
        except MissingSecret:
175
            get_publisher().notify_of_exception(sys.exc_info(), context='[PROFILE]')
176
            return
177

  
178
        payload = new_data.copy()
179
        if '__email' in new_data:
180
            payload['email'] = new_data.get('__email')
181

  
182
        payload = json.dumps(payload)
183

  
184
        def after_job(job):
185
            response, status, data, auth_header = http_put_request(url,
186
                    payload, headers={'Content-type': 'application/json'})
187
            if status != 200:
188
                get_logger().error('failed to update profile for user %r', user)
189

  
190
        get_response().add_after_job(str(N_('Updating user profile')), after_job)
191

  
192

  
193
register_item_class(UpdateUserProfileStatusItem)
wcs/workflows.py
2215 2215
    import wf.export_to_model
2216 2216
    import wf.resubmit
2217 2217
    import wf.criticality
2218
    import wf.profile
2218 2219

  
2219 2220
from wf.export_to_model import ExportToModel
2220
-