Project

General

Profile

« Previous | Next » 

Revision 721bd8e5

Added by Frédéric Péters (de retour le 27 mai) over 1 year ago

misc: remove custom myspace page (#72820)

View differences:

auquotidien/modules/myspace.py
1
try:
2
    import lasso
3
except ImportError:
4
    pass
5

  
6
import json
7
import time
8

  
9
from quixote import get_publisher, get_request, redirect, get_response, get_session_manager, get_session
10
from quixote.directory import AccessControlled, Directory
11
from quixote.html import TemplateIO, htmltext
12
from quixote.util import StaticFile, FileStream
13

  
14
from wcs.qommon import _, N_
15
from wcs.qommon import template
16
from wcs.qommon.form import *
17
from wcs.qommon import get_cfg, get_logger
18
from wcs.qommon import errors
19
from wcs.api import get_user_from_api_query_string
20

  
21
import wcs.qommon.ident.password
22
from wcs.qommon.ident.password_accounts import PasswordAccount
23

  
24
from wcs.qommon.admin.texts import TextsDirectory
25

  
26
from wcs.formdef import FormDef
27
import wcs.myspace
28

  
29

  
30
class JsonDirectory(Directory):
31
    """Export of several lists in json, related to the current user or the
32
    SAMLv2 NameID we'd get in the URL"""
33

  
34
    _q_exports = ['forms']
35

  
36
    user = None
37

  
38
    def _q_traverse(self, path):
39
        self.user = get_user_from_api_query_string() or get_request().user
40
        if not self.user:
41
            raise errors.AccessUnauthorizedError()
42
        return Directory._q_traverse(self, path)
43

  
44
    def forms(self):
45
        formdefs = FormDef.select(order_by='name', ignore_errors=True)
46
        user_forms = []
47
        for formdef in formdefs:
48
            user_forms.extend(formdef.data_class().get_with_indexed_value('user_id', self.user.id))
49
        epoch = time.localtime(1970)
50
        user_forms.sort(key=lambda x: x.receipt_time or epoch)
51

  
52
        get_response().set_content_type('application/json')
53

  
54
        forms_output = []
55
        for form in user_forms:
56
            visible_status = form.get_visible_status(user=self.user)
57
            # skip drafts and hidden forms
58
            if not visible_status:
59
                continue
60
            name = form.formdef.name
61
            id = form.get_display_id()
62
            status = visible_status.name
63
            title = _('%(name)s #%(id)s (%(status)s)') % {'name': name, 'id': id, 'status': status}
64
            url = form.get_url()
65
            d = {'title': title, 'url': url}
66
            d.update(form.get_substitution_variables(minimal=True))
67
            forms_output.append(d)
68
        return json.dumps(forms_output, cls=misc.JSONEncoder)
69

  
70

  
71
class MyspaceDirectory(wcs.myspace.MyspaceDirectory):
72
    _q_exports = ['', 'profile', 'new', 'password', 'remove', 'drafts', 'forms', 'json']
73

  
74
    json = JsonDirectory()
75

  
76
    def _q_traverse(self, path):
77
        get_response().filter['bigdiv'] = 'profile'
78
        get_response().breadcrumb.append(('myspace/', _('My Space')))
79

  
80
        # Migrate custom text settings
81
        texts_cfg = get_cfg('texts', {})
82
        if 'text-aq-top-of-profile' in texts_cfg and (not 'text-top-of-profile' in texts_cfg):
83
            texts_cfg['text-top-of-profile'] = texts_cfg['text-aq-top-of-profile']
84
            del texts_cfg['text-aq-top-of-profile']
85
            get_publisher().write_cfg()
86

  
87
        return Directory._q_traverse(self, path)
88

  
89
    def _q_index(self):
90
        user = get_request().user
91
        if not user:
92
            raise errors.AccessUnauthorizedError()
93
        template.html_top(_('My Space'))
94
        r = TemplateIO(html=True)
95
        if user.anonymous:
96
            return redirect('new')
97

  
98
        user_formdef = user.get_formdef()
99

  
100
        user_forms = []
101
        if user:
102
            formdefs = FormDef.select(order_by='name', ignore_errors=True)
103
            user_forms = []
104
            for formdef in formdefs:
105
                user_forms.extend(formdef.data_class().get_with_indexed_value('user_id', user.id))
106
            epoch = time.localtime(1970)
107
            user_forms.sort(key=lambda x: x.receipt_time or epoch)
108

  
109
        profile_links = []
110
        if not get_cfg('sp', {}).get('idp-manage-user-attributes', False):
111
            if user_formdef:
112
                profile_links.append('<a href="#my-profile">%s</a>' % _('My Profile'))
113
        if user_forms:
114
            profile_links.append('<a href="#my-forms">%s</a>' % _('My Forms'))
115

  
116
        root_url = get_publisher().get_root_url()
117
        if user.can_go_in_backoffice():
118
            profile_links.append('<a href="%sbackoffice/">%s</a>' % (root_url, _('Back office')))
119
        if user.is_admin:
120
            profile_links.append('<a href="%sadmin/">%s</a>' % (root_url, _('Admin')))
121

  
122
        if profile_links:
123
            r += htmltext('<p id="profile-links">')
124
            r += htmltext(' - '.join(profile_links))
125
            r += htmltext('</p>')
126

  
127
        if not get_cfg('sp', {}).get('idp-manage-user-attributes', False):
128
            if user_formdef:
129
                r += self._my_profile(user_formdef, user)
130

  
131
            r += self._index_buttons(user_formdef)
132

  
133
            try:
134
                x = PasswordAccount.get_on_index(get_request().user.id, str('user_id'))
135
            except KeyError:
136
                pass
137
            else:
138
                r += htmltext('<p>')
139
                r += _(
140
                    'You can delete your account freely from the services portal. '
141
                    'This action is irreversible; it will destruct your personal '
142
                    'datas and destruct the access to your request history.'
143
                )
144
                r += htmltext(' <strong><a href="remove" rel="popup">%s</a></strong>.') % _(
145
                    'Delete My Account'
146
                )
147
                r += htmltext('</p>')
148

  
149
        if user_forms:
150
            r += htmltext('<h3 id="my-forms">%s</h3>') % _('My Forms')
151
            from . import root
152

  
153
            r += root.FormsRootDirectory().user_forms(user_forms)
154

  
155
        return r.getvalue()
156

  
157
    def _my_profile(self, user_formdef, user):
158
        r = TemplateIO(html=True)
159
        r += htmltext('<h3 id="my-profile">%s</h3>') % _('My Profile')
160

  
161
        r += TextsDirectory.get_html_text('top-of-profile')
162

  
163
        if user.form_data:
164
            r += htmltext('<ul>')
165
            for field in user_formdef.fields:
166
                if not hasattr(field, str('get_view_value')):
167
                    continue
168
                value = user.form_data.get(field.id)
169
                r += htmltext('<li>')
170
                r += field.label
171
                r += ' : '
172
                if value:
173
                    r += field.get_view_value(value)
174
                r += htmltext('</li>')
175
            r += htmltext('</ul>')
176
        else:
177
            r += htmltext('<p>%s</p>') % _('Empty profile')
178
        return r.getvalue()
179

  
180
    def _index_buttons(self, form_data):
181
        r = TemplateIO(html=True)
182
        passwords_cfg = get_cfg('passwords', {})
183
        ident_method = get_cfg('identification', {}).get('methods', ['idp'])[0]
184
        if get_session().lasso_session_dump:
185
            ident_method = 'idp'
186

  
187
        if form_data and ident_method != 'idp':
188
            r += htmltext('<p class="command"><a href="profile" rel="popup">%s</a></p>') % _(
189
                'Edit My Profile'
190
            )
191

  
192
        if ident_method == 'password' and passwords_cfg.get('can_change', False):
193
            r += htmltext('<p class="command"><a href="password" rel="popup">%s</a></p>') % _(
194
                'Change My Password'
195
            )
196

  
197
        return r.getvalue()
198

  
199
    def profile(self):
200
        user = get_request().user
201
        if not user or user.anonymous:
202
            raise errors.AccessUnauthorizedError()
203

  
204
        form = Form(enctype='multipart/form-data')
205
        formdef = user.get_formdef()
206
        formdef.add_fields_to_form(form, form_data=user.form_data)
207

  
208
        form.add_submit('submit', _('Apply Changes'))
209
        form.add_submit('cancel', _('Cancel'))
210

  
211
        if form.get_submit() == 'cancel':
212
            return redirect('.')
213

  
214
        if form.is_submitted() and not form.has_errors():
215
            self.profile_submit(form, formdef)
216
            return redirect('.')
217

  
218
        template.html_top(_('Edit Profile'))
219
        return form.render()
220

  
221
    def profile_submit(self, form, formdef):
222
        user = get_request().user
223
        data = formdef.get_data(form)
224

  
225
        user.set_attributes_from_formdata(data)
226
        user.form_data = data
227

  
228
        user.store()
229

  
230
    def password(self):
231
        ident_method = get_cfg('identification', {}).get('methods', ['idp'])[0]
232
        if ident_method != 'password':
233
            raise errors.TraversalError()
234

  
235
        user = get_request().user
236
        if not user or user.anonymous:
237
            raise errors.AccessUnauthorizedError()
238

  
239
        form = Form(enctype='multipart/form-data')
240
        form.add(PasswordWidget, 'new_password', title=_('New Password'), required=True)
241
        form.add(PasswordWidget, 'new2_password', title=_('New Password (confirm)'), required=True)
242

  
243
        form.add_submit('submit', _('Change Password'))
244
        form.add_submit('cancel', _('Cancel'))
245

  
246
        if form.get_submit() == 'cancel':
247
            return redirect('.')
248

  
249
        if form.is_submitted() and not form.has_errors():
250
            wcs.qommon.ident.password.check_password(form, 'new_password')
251
            new_password = form.get_widget('new_password').parse()
252
            new2_password = form.get_widget('new2_password').parse()
253
            if new_password != new2_password:
254
                form.set_error('new2_password', _('Passwords do not match'))
255

  
256
        if form.is_submitted() and not form.has_errors():
257
            self.submit_password(new_password)
258
            return redirect('.')
259

  
260
        template.html_top(_('Change Password'))
261
        return form.render()
262

  
263
    def submit_password(self, new_password):
264
        passwords_cfg = get_cfg('passwords', {})
265
        account = PasswordAccount.get(get_session().username)
266
        account.hashing_algo = passwords_cfg.get('hashing_algo')
267
        account.set_password(new_password)
268
        account.store()
269

  
270
    def new(self):
271
        if not get_request().user or not get_request().user.anonymous:
272
            raise errors.AccessUnauthorizedError()
273

  
274
        form = Form(enctype='multipart/form-data')
275
        formdef = get_publisher().user_class.get_formdef()
276
        if formdef:
277
            formdef.add_fields_to_form(form)
278
        else:
279
            get_logger().error('missing user formdef (in myspace/new)')
280

  
281
        form.add_submit('submit', _('Register'))
282

  
283
        if form.is_submitted() and not form.has_errors():
284
            user = get_publisher().user_class()
285
            data = formdef.get_data(form)
286
            user.set_attributes_from_formdata(data)
287
            user.name_identifiers = get_request().user.name_identifiers
288
            user.lasso_dump = get_request().user.lasso_dump
289
            user.set_attributes_from_formdata(data)
290
            user.form_data = data
291
            user.store()
292
            get_session().set_user(user.id)
293
            root_url = get_publisher().get_root_url()
294
            return redirect('%smyspace' % root_url)
295

  
296
        template.html_top(_('Welcome'))
297
        return form.render()
298

  
299
    def remove(self):
300
        user = get_request().user
301
        if not user or user.anonymous:
302
            raise errors.AccessUnauthorizedError()
303

  
304
        form = Form(enctype='multipart/form-data')
305
        form.widgets.append(
306
            HtmlWidget('<p>%s</p>' % _('Are you really sure you want to remove your account?'))
307
        )
308
        form.add_submit('submit', _('Remove my account'))
309
        form.add_submit('cancel', _('Cancel'))
310

  
311
        if form.get_submit() == 'cancel':
312
            return redirect('.')
313

  
314
        if form.is_submitted() and not form.has_errors():
315
            user = get_request().user
316
            account = PasswordAccount.get_on_index(user.id, str('user_id'))
317
            get_session_manager().expire_session()
318
            account.remove_self()
319
            return redirect(get_publisher().get_root_url())
320

  
321
        template.html_top(_('Removing Account'))
322
        return form.render()
auquotidien/modules/root.py
36 36
from wcs.qommon.admin.emails import EmailsDirectory
37 37
from wcs.qommon.admin.texts import TextsDirectory
38 38

  
39
from .myspace import MyspaceDirectory
40

  
41 39
import wcs.forms.root
42 40
from wcs.workflows import Workflow
43 41
from wcs.forms.preview import PreviewDirectory
......
249 247
    register = AlternateRegisterDirectory()
250 248
    login = AlternateLoginDirectory()
251 249
    ident = AlternateIdentDirectory()
252
    myspace = MyspaceDirectory()
253 250
    saml = Saml2Directory()
254 251
    code = wcs.forms.root.TrackingCodesDirectory()
255 252
    preview = AlternatePreviewDirectory()
tests/test_user_pages.py
69 69
    user = create_user()
70 70
    app = login(get_app(pub), username='user', password='user')
71 71
    resp = app.get('/', status=200)
72
    resp = app.get('/myspace/', status=200)
73

  
74

  
75
def test_myspace_with_user_forms():
76
    user = create_user()
77
    formdef = create_formdef()
78

  
79
    cat = Category(name='cat')
80
    cat.store()
81
    formdef.category_id = cat.id
82
    formdef.enable_tracking_codes = True
83

  
84
    wf = Workflow(name='status')
85
    st1 = wf.add_status('Status1', 'st1')
86
    wf.store()
87
    formdef.workflow_id = wf.id
88
    formdef.store()
89

  
90
    formdef.data_class().wipe()
91
    formdata = formdef.data_class()()
92
    formdata.user_id = user.id
93
    formdata.status = 'wf-st1'
94
    formdata.receipt_time = time.localtime()
95
    formdata.data = {}
96
    formdata.store()
97
    draft = formdef.data_class()()
98
    draft.user_id = user.id
99
    draft.status = 'draft'
100
    draft.receipt_time = time.localtime()
101
    draft.data = {}
102
    draft.store()
103

  
104
    app = login(get_app(pub), username='user', password='user')
105
    resp = app.get('/myspace/')
106
    assert formdata.id != draft.id
107
    assert '<a href="/test/%s/"' % formdata.id in resp
108
    assert '<a href="/test/%s/"' % draft.id in resp
109
    resp = app.get('/test/%s/' % formdata.id, status=302)
110
    assert resp.location == 'http://example.net/cat/test/%s/' % formdata.id
111
    resp = app.get('/test/%s/' % draft.id, status=302)
112
    resp = resp.follow(status=302)
113
    assert resp.location.startswith('http://example.net/cat/test/?mt=')
114
    resp = resp.follow(status=200)
115

  
116
    # disable formdef: formdatas are still visible and accessible, drafts are not
117
    formdef.disabled = True
118
    formdef.store()
119
    resp = app.get('/myspace/')
120
    assert formdata.id != draft.id
121
    assert '<a href="/test/%s/"' % formdata.id in resp
122
    assert not '<a href="/test/%s"' % draft.id in resp
123
    resp = app.get('/cat/test/%s' % formdata.id)
124
    resp.status_int = 200
125
    resp = app.get('/cat/test/%s/' % draft.id, status=302)
126
    resp = resp.follow(status=403)
127 72

  
128 73

  
129 74
def test_form_category_redirection():

Also available in: Unified diff