Projet

Général

Profil

Télécharger (50,1 ko) Statistiques
| Branche: | Tag: | Révision:

root / extra / modules / root.py @ fdcc4265

1
from quixote import get_publisher, get_response, get_request, redirect, get_session
2
from quixote.directory import Directory
3
from quixote.html import TemplateIO, htmltext
4

    
5
from wcs.qommon.misc import get_variadic_url, simplify
6

    
7
import os
8
import re
9
import string
10
import urlparse
11

    
12
try:
13
    import lasso
14
except ImportError:
15
    pass
16

    
17
import wcs
18
import wcs.root
19
import qommon
20
from qommon import get_cfg, get_logger
21
from qommon import template
22
from qommon import errors
23
from qommon.form import *
24
from qommon import logger
25
from wcs.roles import logged_users_role
26

    
27
from qommon import emails
28
from qommon.sms import SMS
29
from wcs.categories import Category
30
from wcs.formdef import FormDef
31
from wcs.data_sources import NamedDataSource
32
from qommon.tokens import Token
33
from qommon.admin.emails import EmailsDirectory
34
from qommon.admin.texts import TextsDirectory
35

    
36
from links import Link
37
from announces import Announce, AnnounceSubscription
38
from myspace import MyspaceDirectory
39
from agenda import AgendaDirectory
40
from events import Event, get_default_event_tags
41
from payments import PublicPaymentDirectory
42
from payments_ui import InvoicesDirectory
43

    
44
import admin
45

    
46
import wcs.forms.root
47
from wcs.workflows import Workflow
48
from wcs.forms.preview import PreviewDirectory
49

    
50
from saml2 import Saml2Directory
51

    
52
OldRootDirectory = wcs.root.RootDirectory
53

    
54
import qommon.ident.password
55
import qommon.ident.idp
56

    
57

    
58
def category_get_homepage_position(self):
59
    if hasattr(self, 'homepage_position') and self.homepage_position:
60
        return self.homepage_position
61
    if self.url_name == 'consultations':
62
        return '2nd'
63
    return '1st'
64
Category.get_homepage_position = category_get_homepage_position
65

    
66
def category_get_limit(self):
67
    if hasattr(self, 'limit') and self.limit is not None:
68
        return self.limit
69
    return 7
70
Category.get_limit = category_get_limit
71

    
72
Category.TEXT_ATTRIBUTES = ['name', 'url_name', 'description', 'homepage_position']
73
Category.INT_ATTRIBUTES = ['position', 'limit']
74

    
75

    
76
class FormsRootDirectory(wcs.forms.root.RootDirectory):
77

    
78
    def _q_index(self, *args):
79
        get_response().filter['is_index'] = True
80
        return wcs.forms.root.RootDirectory._q_index(self, *args)
81

    
82
    def user_forms(self, user_forms):
83
        r = TemplateIO(html=True)
84
        base_url = get_publisher().get_root_url()
85

    
86
        draft = [x for x in user_forms if x.is_draft() and not x.formdef.is_disabled()]
87
        if draft:
88
            r += htmltext('<h4 id="drafts">%s</h4>') % _('My Current Drafts')
89
            r += htmltext('<ul>')
90
            for f in draft:
91
                if f.formdef.category:
92
                    category_url = '%s' % f.formdef.category.url_name
93
                else:
94
                    category_url = '.'
95
                r += htmltext('<li><a href="%s%s/%s/%s">%s</a>, %s') % (base_url,
96
                    category_url,
97
                    f.formdef.url_name, f.id, f.formdef.name,
98
                    misc.localstrftime(f.receipt_time))
99
                r += htmltext(' (<a href="%s%s/%s/%s?remove-draft">%s</a>)') % (base_url,
100
                    category_url,
101
                    f.formdef.url_name, f.id, _('delete'))
102
                r += htmltext('</li>')
103
            r += htmltext('</ul>')
104

    
105
        forms_by_status_name = {}
106
        for f in user_forms:
107
            if f.is_draft():
108
                continue
109
            status = f.get_visible_status()
110
            if status:
111
                status_name = status.name
112
            else:
113
                status_name = None
114
            if status_name in forms_by_status_name:
115
                forms_by_status_name[status_name].append(f)
116
            else:
117
                forms_by_status_name[status_name] = [f]
118
        for status_name in forms_by_status_name:
119
            if status_name:
120
                r += htmltext('<h4>%s</h4>') % _('My forms with status "%s"') % status_name
121
            else:
122
                r += htmltext('<h4>%s</h4>') % _('My forms with an unknown status') % status_name
123
            r += htmltext('<ul>')
124
            forms_by_status_name[status_name].sort(lambda x,y: cmp(x.receipt_time, y.receipt_time))
125
            for f in forms_by_status_name[status_name]:
126
                if f.formdef.category_id:
127
                    category_url = f.formdef.category.url_name
128
                else:
129
                    category_url = '.'
130
                r += htmltext('<li><a href="%s%s/%s/%s/">%s</a>, %s</li>') % (
131
                        base_url,
132
                        category_url,
133
                        f.formdef.url_name, f.id, f.formdef.name, 
134
                        misc.localstrftime(f.receipt_time))
135
            r += htmltext('</ul>')
136
        return r.getvalue()
137

    
138

    
139
class AnnounceDirectory(Directory):
140
    _q_exports = ['', 'edit', 'delete', 'email']
141

    
142
    def __init__(self, announce):
143
        self.announce = announce
144

    
145
    def _q_index(self):
146
        template.html_top(_('Announces to citizens'))
147
        r = TemplateIO(html=True)
148

    
149
        if self.announce.publication_time:
150
            date_heading = '%s - ' % time.strftime(misc.date_format(), self.announce.publication_time)
151
        else:
152
            date_heading = ''
153

    
154
        r += htmltext('<h3>%s%s</h3>') % (date_heading, self.announce.title)
155

    
156
        r += htmltext('<p>')
157
        r += self.announce.text
158
        r += htmltext('</p>')
159

    
160
        r += htmltext('<p>')
161
        r += htmltext('<a href="../">%s</a>') % _('Back')
162
        r += htmltext('</p>')
163
        return r.getvalue()
164

    
165

    
166
class AnnouncesDirectory(Directory):
167
    _q_exports = ['', 'subscribe', 'email', 'atom', 'sms', 'emailconfirm',
168
            'email_unsubscribe', 'sms_unsubscribe', 'smsconfirm', 'rawlist']
169

    
170
    def _q_traverse(self, path):
171
        get_response().breadcrumb.append(('announces/', _('Announces')))
172
        return Directory._q_traverse(self, path)
173

    
174
    def _q_index(self):
175
        template.html_top(_('Announces to citizens'))
176
        r = TemplateIO(html=True)
177
        r += self.announces_list()
178
        r += htmltext('<ul id="announces-links">')
179
        r += htmltext('<li><a href="subscribe">%s</a></li>') % _('Receiving those Announces')
180
        r += htmltext('</ul>')
181
        return r.getvalue()
182

    
183
    def _get_announce_subscription(self):
184
        """ """
185
        sub = None
186
        if get_request().user:
187
            subs = AnnounceSubscription.select(lambda x: x.user_id == get_request().user.id)
188
            if subs:
189
                sub = subs[0]
190
        return sub
191

    
192
    def rawlist(self):
193
        get_response().filter = None
194
        return self.announces_list()
195

    
196
    def announces_list(self):
197
        announces = Announce.get_published_announces()
198
        if not announces:
199
            raise errors.TraversalError()
200

    
201
        # XXX: will need pagination someday
202
        r = TemplateIO(html=True)
203
        for item in announces:
204
            r += htmltext('<div class="announce-item">\n')
205
            r += htmltext('<h4>')
206
            if item.publication_time:
207
                r += time.strftime(misc.date_format(), item.publication_time)
208
                r += ' - '
209
            r += item.title
210
            r += htmltext('</h4>\n')
211
            r += htmltext('<p>\n')
212
            r += item.text
213
            r += htmltext('\n</p>\n')
214
            r += htmltext('</div>\n')
215
        return r.getvalue()
216

    
217

    
218
    def sms(self):
219
        sms_mode = get_cfg('sms', {}).get('mode', 'none')
220

    
221
        if sms_mode == 'none':
222
            raise errors.TraversalError()
223

    
224
        get_response().breadcrumb.append(('sms', _('SMS')))
225
        template.html_top(_('Receiving announces by SMS'))
226
        r = TemplateIO(html=True)
227

    
228
        if sms_mode == 'demo':
229
            r += TextsDirectory.get_html_text('aq-sms-demo')
230
        else:
231
            announces_cfg = get_cfg('announces',{})
232
            mobile_mask = announces_cfg.get('mobile_mask')
233
            if mobile_mask:
234
                mobile_mask = ' (' + mobile_mask + ')'
235
            else:
236
                mobile_mask = ''
237
            form = Form(enctype='multipart/form-data')
238
            form.add(StringWidget, 'mobile', title = _('Mobile number %s') % mobile_mask, size=12, required=True)
239
            form.add_submit('submit', _('Subscribe'))
240
            form.add_submit('cancel', _('Cancel'))
241

    
242
            if form.get_submit() == 'cancel':
243
                return redirect('subscribe')
244

    
245
            if form.is_submitted() and not form.has_errors():
246
                s = self.sms_submit(form)
247
                if s == False:
248
                    r += form.render()
249
                else:
250
                    return redirect("smsconfirm")
251
            else:
252
                r += form.render()
253
        return r.getvalue()
254

    
255
    def sms_submit(self, form):
256
        mobile = form.get_widget("mobile").parse()
257
        # clean the string, remove any extra character
258
        mobile = re.sub('[^0-9+]','',mobile)
259
        # if a mask was set, validate
260
        announces_cfg = get_cfg('announces',{})
261
        mobile_mask = announces_cfg.get('mobile_mask')
262
        if mobile_mask:
263
            mobile_regexp = re.sub('X','[0-9]', mobile_mask) + '$'
264
            if not re.match(mobile_regexp, mobile):
265
                form.set_error("mobile", _("Phone number invalid ! It must match ") + mobile_mask)
266
                return False
267
        if mobile.startswith('00'):
268
            mobile = '+' + mobile[2:]
269
        else:
270
            # Default to france international prefix
271
            if not mobile.startswith('+'):
272
                mobile = re.sub("^0", "+33", mobile)
273
        sub = self._get_announce_subscription()
274
        if not sub:
275
            sub = AnnounceSubscription()
276
        if get_request().user:
277
            sub.user_id = get_request().user.id
278

    
279
        if mobile:
280
            sub.sms = mobile
281

    
282
        if not get_request().user:
283
            sub.enabled = False
284

    
285
        sub.store()
286

    
287
        # Asking sms confirmation
288
        token = Token(3 * 86400, 4, string.digits)
289
        token.type = 'announces-subscription-confirmation'
290
        token.subscription_id = sub.id
291
        token.store()
292

    
293
        message = _("Confirmation code : %s") % str(token.id)
294
        sms_cfg = get_cfg('sms', {})
295
        sender = sms_cfg.get('sender', 'AuQuotidien')[:11]
296
        mode = sms_cfg.get('mode', 'none')
297
        sms = SMS.get_sms_class(mode)
298
        try:
299
            sms.send(sender, [mobile], message)
300
        except errors.SMSError, e:
301
            get_logger().error(e)
302
            form.set_error("mobile", _("Send SMS confirmation failed"))
303
            sub.remove("sms")
304
            return False
305

    
306
    def smsconfirm(self):
307
        template.html_top(_('Receiving announces by SMS confirmation'))
308
        r = TemplateIO(html=True)
309
        r += htmltext("<p>%s</p>") % _("You will receive a confirmation code by SMS.")
310
        form = Form(enctype='multipart/form-data')
311
        form.add(StringWidget, 'code', title = _('Confirmation code (4 characters)'), size=12, required=True)
312
        form.add_submit('submit', _('Subscribe'))
313
        form.add_submit('cancel', _('Cancel'))
314

    
315
        if form.get_submit() == 'cancel':
316
            return redirect('..')
317

    
318
        if form.is_submitted() and not form.has_errors():
319
            token = None
320
            id = form.get_widget("code").parse()
321
            try:
322
                token = Token.get(id)
323
            except KeyError:
324
                form.set_error("code",  _('Invalid confirmation code.'))
325
            else:
326
                if token.type != 'announces-subscription-confirmation':
327
                    form.set_error("code",  _('Invalid confirmation code.'))
328
                else:
329
                    sub = AnnounceSubscription.get(token.subscription_id)
330
                    token.remove_self()
331
                    sub.enabled_sms = True
332
                    sub.store()
333
                    return redirect('.')
334
            r += form.render()
335
        else:
336
            r += form.render()
337

    
338
        return r.getvalue()
339

    
340
    def sms_unsubscribe(self):
341
        sub = self._get_announce_subscription()
342

    
343
        form = Form(enctype='multipart/form-data')
344
        if not sub:
345
            return redirect('..')
346

    
347
        form.add_submit('submit', _('Unsubscribe'))
348
        form.add_submit('cancel', _('Cancel'))
349

    
350
        if form.get_submit() == 'cancel':
351
            return redirect('..')
352

    
353
        get_response().breadcrumb.append(('sms', _('SMS Unsubscription')))
354
        template.html_top()
355
        r = TemplateIO(html=True)
356

    
357
        if form.is_submitted() and not form.has_errors():
358
            if sub:
359
                sub.remove("sms")
360

    
361
            root_url = get_publisher().get_root_url()
362
            r += htmltext('<p>')
363
            r += _('You have been unsubscribed from announces')
364
            r += htmltext('</p>')
365
            if not get_response().iframe_mode:
366
                r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
367
        else:
368
            r += htmltext('<p>')
369
            r += _('Do you want to stop receiving announces by sms ?')
370
            r += htmltext('</p>')
371
            r += form.render()
372

    
373
        return r.getvalue()
374

    
375

    
376
    def subscribe(self):
377
        get_response().breadcrumb.append(('subscribe', _('Subscription')))
378
        template.html_top(_('Receiving Announces'))
379
        r = TemplateIO(html=True)
380

    
381
        r += TextsDirectory.get_html_text('aq-announces-subscription')
382

    
383
        sub = self._get_announce_subscription()
384

    
385
        r += htmltext('<ul id="announce-modes">')
386
        if sub and sub.email:
387
            r += htmltext(' <li>')
388
            r += htmltext('<span id="par_mail">%s</span>') % _('Email (currently subscribed)')
389
            r += htmltext(' <a href="email_unsubscribe" rel="popup">%s</a></li>') % _('Unsubscribe')
390
        else:
391
            r += htmltext(' <li><a href="email" id="par_mail" rel="popup">%s</a></li>') % _('Email')
392
        if sub and sub.sms:
393
            r += htmltext(' <li>')
394
            if sub.enabled_sms:
395
                r += htmltext('<span id="par_sms">%s</span>') % _('SMS %s (currently subscribed)') % sub.sms
396
            else:
397
                r += htmltext('<span id="par_sms">%s</span>') % _('SMS %s (currently not confirmed)') % sub.sms
398
                r += htmltext(' <a href="smsconfirm" rel="popup">%s</a> ') % _('Confirmation')
399
            r += htmltext(' <a href="sms_unsubscribe" rel="popup">%s</a></li>') % _('Unsubscribe')
400
        elif get_cfg('sms', {}).get('mode', 'none') != 'none':
401
            r += htmltext(' <li><a href="sms" id="par_sms">%s</a></li>') % _('SMS')
402
        r += htmltext(' <li><a class="feed-link" href="atom" id="par_rss">%s</a>') % _('Feed')
403
        r += htmltext('</ul>')
404
        return r.getvalue()
405

    
406
    def email(self):
407
        get_response().breadcrumb.append(('email', _('Email Subscription')))
408
        template.html_top(_('Receiving Announces by email'))
409
        r = TemplateIO(html=True)
410

    
411
        form = Form(enctype='multipart/form-data')
412
        if get_request().user:
413
            if get_request().user.email:
414
                r += htmltext('<p>')
415
                r += _('You are logged in and your email is %s, ok to subscribe ?') % \
416
                        get_request().user.email
417
                r += htmltext('</p>')
418
                form.add_submit('submit', _('Subscribe'))
419
            else:
420
                r += htmltext('<p>')
421
                r += _("You are logged in but there is no email address in your profile.")
422
                r += htmltext('</p>')
423
                form.add(EmailWidget, 'email', title = _('Email'), required = True)
424
                form.add_submit('submit', _('Subscribe'))
425
                form.add_submit('submit-remember', _('Subscribe and add this email to my profile'))
426
        else:
427
            r += htmltext('<p>')
428
            r += _('FIXME will only be used for this purpose etc.')
429
            r += htmltext('</p>')
430
            form.add(EmailWidget, 'email', title = _('Email'), required = True)
431
            form.add_submit('submit', _('Subscribe'))
432

    
433
        form.add_submit('cancel', _('Cancel'))
434

    
435
        if form.get_submit() == 'cancel':
436
            return redirect('subscribe')
437

    
438
        if form.is_submitted() and not form.has_errors():
439
            s = self.email_submit(form)
440
            if s is not False:
441
                return s
442
        else:
443
            r += form.render()
444

    
445
        return r.getvalue()
446

    
447
    def email_submit(self, form):
448
        sub = self._get_announce_subscription()
449
        if not sub:
450
            sub = AnnounceSubscription()
451

    
452
        if get_request().user:
453
            sub.user_id = get_request().user.id
454

    
455
        if form.get_widget('email'):
456
            sub.email = form.get_widget('email').parse()
457
        elif get_request().user.email:
458
            sub.email = get_request().user.email
459

    
460
        if not get_request().user:
461
            sub.enabled = False
462

    
463
        sub.store()
464

    
465
        if get_request().user:
466
            r = TemplateIO(html=True)
467
            root_url = get_publisher().get_root_url()
468
            r += htmltext('<p>')
469
            r += _('You have been subscribed to the announces.')
470
            r += htmltext('</p>')
471
            if not get_response().iframe_mode:
472
                r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
473
            return r.getvalue()
474

    
475
        # asking email confirmation before subscribing someone
476
        token = Token(3 * 86400)
477
        token.type = 'announces-subscription-confirmation'
478
        token.subscription_id = sub.id
479
        token.store()
480
        data = {
481
            'confirm_url': get_request().get_url() + 'confirm?t=%s&a=cfm' % token.id,
482
            'cancel_url': get_request().get_url() + 'confirm?t=%s&a=cxl' % token.id,
483
            'time': misc.localstrftime(time.localtime(token.expiration)),
484
        }
485

    
486
        emails.custom_ezt_email('announces-subscription-confirmation',
487
                data, sub.email, exclude_current_user = False)
488

    
489
        r = TemplateIO(html=True)
490
        root_url = get_publisher().get_root_url()
491
        r += htmltext('<p>')
492
        r += _('You have been sent an email for confirmation')
493
        r += htmltext('</p>')
494
        if not get_response().iframe_mode:
495
            r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
496
        return r.getvalue()
497

    
498
    def emailconfirm(self):
499
        tokenv = get_request().form.get('t')
500
        action = get_request().form.get('a')
501

    
502
        root_url = get_publisher().get_root_url()
503

    
504
        try:
505
            token = Token.get(tokenv)
506
        except KeyError:
507
            return template.error_page(
508
                    _('The token you submitted does not exist, has expired, or has been cancelled.'),
509
                    continue_to = (root_url, _('home page')))
510

    
511
        if token.type != 'announces-subscription-confirmation':
512
            return template.error_page(
513
                    _('The token you submitted is not appropriate for the requested task.'),
514
                    continue_to = (root_url, _('home page')))
515

    
516
        sub = AnnounceSubscription.get(token.subscription_id)
517

    
518
        if action == 'cxl':
519
            r = TemplateIO(html=True)
520
            root_url = get_publisher().get_root_url()
521
            template.html_top(_('Email Subscription'))
522
            r += htmltext('<h1>%s</h1>') % _('Request Cancelled')
523
            r += htmltext('<p>%s</p>') % _('The request for subscription has been cancelled.')
524
            r += htmltext('<p>')
525
            r += htmltext(_('Continue to <a href="%s">home page</a>') % root_url)
526
            r += htmltext('</p>')
527
            token.remove_self()
528
            sub.remove_self()
529
            return r.getvalue()
530

    
531
        if action == 'cfm':
532
            token.remove_self()
533
            sub.enabled = True
534
            sub.store()
535
            r = TemplateIO(html=True)
536
            root_url = get_publisher().get_root_url()
537
            template.html_top(_('Email Subscription'))
538
            r += htmltext('<h1>%s</h1>') % _('Subscription Confirmation')
539
            r += htmltext('<p>%s</p>') % _('Your subscription to announces is now effective.')
540
            r += htmltext('<p>')
541
            r += htmltext(_('Continue to <a href="%s">home page</a>') % root_url)
542
            r += htmltext('</p>')
543
            return r.getvalue()
544

    
545
    def atom(self):
546
        response = get_response()
547
        response.set_content_type('application/atom+xml')
548

    
549
        from pyatom import pyatom
550
        xmldoc = pyatom.XMLDoc()
551
        feed = pyatom.Feed()
552
        xmldoc.root_element = feed
553
        feed.title = get_cfg('misc', {}).get('sitename') or 'Publik'
554
        feed.id = get_request().get_url()
555

    
556
        author_email = get_cfg('emails', {}).get('reply_to')
557
        if not author_email:
558
            author_email = get_cfg('emails', {}).get('from')
559
        if author_email:
560
            feed.authors.append(pyatom.Author(author_email))
561

    
562
        announces = Announce.get_published_announces()
563

    
564
        if announces and announces[0].modification_time:
565
            feed.updated = misc.format_time(announces[0].modification_time,
566
                        '%(year)s-%(month)02d-%(day)02dT%(hour)02d:%(minute)02d:%(second)02dZ',
567
                        gmtime = True)
568
        feed.links.append(pyatom.Link(get_request().get_url(1) + '/'))
569

    
570
        for item in announces:
571
            entry = item.get_atom_entry()
572
            if entry:
573
                feed.entries.append(entry)
574

    
575
        return str(feed)
576

    
577
    def email_unsubscribe(self):
578
        sub = self._get_announce_subscription()
579

    
580
        form = Form(enctype='multipart/form-data')
581
        if not sub:
582
            form.add(EmailWidget, 'email', title = _('Email'), required = True)
583

    
584
        form.add_submit('submit', _('Unsubscribe'))
585
        form.add_submit('cancel', _('Cancel'))
586

    
587
        if form.get_submit() == 'cancel':
588
            return redirect('..')
589

    
590
        get_response().breadcrumb.append(('email', _('Email Unsubscription')))
591
        template.html_top()
592
        r = TemplateIO(html=True)
593

    
594
        if form.is_submitted() and not form.has_errors():
595
            if sub:
596
                sub.remove("email")
597
            else:
598
                email = form.get_widget('email').parse()
599
                for s in AnnounceSubscription.select():
600
                    if s.email == email:
601
                        s.remove("email")
602

    
603
            root_url = get_publisher().get_root_url()
604
            r += htmltext('<p>')
605
            r += _('You have been unsubscribed from announces')
606
            r += htmltext('</p>')
607
            if not get_response().iframe_mode:
608
                r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
609

    
610
        else:
611
            r += htmltext('<p>')
612
            r += _('Do you want to stop receiving announces by email?')
613
            r += htmltext('</p>')
614
            r += form.render()
615

    
616
        return r.getvalue()
617

    
618
    def _q_lookup(self, component):
619
        try:
620
            announce = Announce.get(component)
621
        except KeyError:
622
            raise errors.TraversalError()
623

    
624
        if announce.hidden:
625
            raise errors.TraversalError()
626

    
627
        get_response().breadcrumb.append((str(announce.id), announce.title))
628
        return AnnounceDirectory(announce)
629

    
630
OldRegisterDirectory = wcs.root.RegisterDirectory
631

    
632
class AlternateRegisterDirectory(OldRegisterDirectory):
633
    def _q_traverse(self, path):
634
        get_response().filter['bigdiv'] = 'new_member'
635
        return OldRegisterDirectory._q_traverse(self, path)
636

    
637
    def _q_index(self):
638
        get_logger().info('register')
639
        ident_methods = get_cfg('identification', {}).get('methods', [])
640

    
641
        if len(ident_methods) == 0:
642
            idps = get_cfg('idp', {})
643
            if len(idps) == 0:
644
                return template.error_page(_('Authentication subsystem is not yet configured.'))
645
            ident_methods = ['idp'] # fallback to old behaviour; saml.
646

    
647
        if len(ident_methods) == 1:
648
            method = ident_methods[0]
649
        else:
650
            method = 'password'
651

    
652
        return qommon.ident.register(method)
653

    
654
OldLoginDirectory = wcs.root.LoginDirectory
655

    
656
class AlternateLoginDirectory(OldLoginDirectory):
657
    def _q_traverse(self, path):
658
        get_response().filter['bigdiv'] = 'member'
659
        return OldLoginDirectory._q_traverse(self, path)
660

    
661
    def _q_index(self):
662
        get_logger().info('login')
663
        ident_methods = get_cfg('identification', {}).get('methods', [])
664

    
665
        if get_request().form.get('ReturnUrl'):
666
            get_request().form['next'] = get_request().form.pop('ReturnUrl')
667

    
668
        if 'IsPassive' in get_request().form and 'idp' in ident_methods:
669
            # if isPassive is given in query parameters, we restrict ourselves
670
            # to saml login.
671
            ident_methods = ['idp']
672

    
673
        if len(ident_methods) > 1 and 'idp' in ident_methods:
674
            # if there is more than one identification method, and there is a
675
            # possibility of SSO, if we got there as a consequence of an access
676
            # unauthorized url on admin/ or backoffice/, then idp auth method
677
            # is chosen forcefully.
678
            after_url = get_request().form.get('next')
679
            if after_url:
680
                root_url = get_publisher().get_root_url()
681
                after_path = urlparse.urlparse(after_url)[2]
682
                after_path = after_path[len(root_url):]
683
                if after_path.startswith(str('admin')) or \
684
                        after_path.startswith(str('backoffice')):
685
                    ident_methods = ['idp']
686

    
687
        # don't display authentication system choice
688
        if len(ident_methods) == 1:
689
            method = ident_methods[0]
690
            try:
691
                return qommon.ident.login(method)
692
            except KeyError:
693
                get_logger().error('failed to login with method %s' % method)
694
                return errors.TraversalError()
695

    
696
        if sorted(ident_methods) == ['idp', 'password']:
697
            r = TemplateIO(html=True)
698
            get_response().breadcrumb.append(('login', _('Login')))
699
            identities_cfg = get_cfg('identities', {})
700
            form = Form(enctype = 'multipart/form-data', id = 'login-form', use_tokens = False)
701
            if identities_cfg.get('email-as-username', False):
702
                form.add(StringWidget, 'username', title = _('Email'), size=25, required=True)
703
            else:
704
                form.add(StringWidget, 'username', title = _('Username'), size=25, required=True)
705
            form.add(PasswordWidget, 'password', title = _('Password'), size=25, required=True)
706
            form.add_submit('submit', _('Connect'))
707
            if form.is_submitted() and not form.has_errors():
708
                tmp = qommon.ident.password.MethodDirectory().login_submit(form)
709
                if not form.has_errors():
710
                    return tmp
711

    
712
            r += htmltext('<div id="login-password">')
713
            r += get_session().display_message()
714
            r += form.render()
715

    
716
            base_url = get_publisher().get_root_url()
717
            r += htmltext('<p><a href="%sident/password/forgotten">%s</a></p>') % (
718
                    base_url, _('Forgotten password ?'))
719

    
720
            r += htmltext('</div>')
721

    
722
            # XXX: this part only supports a single IdP
723
            r += htmltext('<div id="login-sso">')
724
            r += TextsDirectory.get_html_text('aq-sso-text')
725
            form = Form(enctype='multipart/form-data',
726
                    action = '%sident/idp/login' % base_url)
727
            form.add_hidden('method', 'idp')
728
            for kidp, idp in get_cfg('idp', {}).items():
729
                p = lasso.Provider(lasso.PROVIDER_ROLE_IDP,
730
                        misc.get_abs_path(idp['metadata']),
731
                        misc.get_abs_path(idp.get('publickey')), None)
732
                form.add_hidden('idp', p.providerId)
733
                break
734
            form.add_submit('submit', _('Connect'))
735

    
736
            r += form.render()
737
            r += htmltext('</div>')
738

    
739
            get_request().environ['REQUEST_METHOD'] = 'GET'
740

    
741
            r += htmltext("""<script type="text/javascript">
742
              document.getElementById('login-form')['username'].focus();
743
            </script>""")
744
            return r.getvalue()
745
        else:
746
            return OldLoginDirectory._q_index(self)
747

    
748

    
749
OldIdentDirectory = wcs.root.IdentDirectory
750
class AlternateIdentDirectory(OldIdentDirectory):
751
    def _q_traverse(self, path):
752
        get_response().filter['bigdiv'] = 'member'
753
        return OldIdentDirectory._q_traverse(self, path)
754

    
755

    
756
class AlternatePreviewDirectory(PreviewDirectory):
757
    def _q_traverse(self, path):
758
        get_response().filter['bigdiv'] = 'rub_service'
759
        return super(AlternatePreviewDirectory, self)._q_traverse(path)
760

    
761

    
762
class AlternateRootDirectory(OldRootDirectory):
763
    _q_exports = ['', 'admin', 'backoffice', 'forms', 'login', 'logout',
764
            'token', 'saml', 'register', 'ident', 'afterjobs',
765
            ('informations-editeur', 'informations_editeur'),
766
            ('announces', 'announces_dir'),
767
            'accessibility', 'contact', 'help',
768
            'myspace', 'services', 'agenda', 'categories', 'user',
769
            ('tmp-upload', 'tmp_upload'), 'json', '__version__',
770
            'themes', 'pages', 'payment', 'invoices', 'accesscode', 'roles',
771
            'api', 'code', 'fargo', 'tryauth', 'auth', 'preview',
772
            ('reload-top', 'reload_top'),
773
            ('i18n.js', 'i18n_js')]
774

    
775
    admin = admin.AdminRootDirectory()
776
    announces_dir = AnnouncesDirectory()
777
    register = AlternateRegisterDirectory()
778
    login = AlternateLoginDirectory()
779
    ident = AlternateIdentDirectory()
780
    myspace = MyspaceDirectory()
781
    agenda = AgendaDirectory()
782
    saml = Saml2Directory()
783
    payment = PublicPaymentDirectory()
784
    invoices = InvoicesDirectory()
785
    code = wcs.forms.root.TrackingCodesDirectory()
786
    preview = AlternatePreviewDirectory()
787

    
788
    def get_substitution_variables(self):
789
        d = {}
790
        def print_links(fd):
791
            fd.write(str(self.links()))
792
        d['links'] = print_links
793
        return d
794

    
795
    def _q_traverse(self, path):
796
        get_publisher().substitutions.feed(get_session())
797
        get_publisher().substitutions.feed(get_request().user)
798
        get_publisher().substitutions.feed(NamedDataSource)
799

    
800
        # set app_label to Publik if none was specified (this is used in
801
        # backoffice header top line)
802
        if not get_publisher().get_site_option('app_label'):
803
            if not get_publisher().site_options.has_section('options'):
804
                get_publisher().site_options.add_section('options')
805
            get_publisher().site_options.set('options', 'app_label', 'Publik')
806

    
807
        response = get_response()
808
        if not hasattr(response, 'filter'):
809
            response.filter = {}
810

    
811
        response.filter['auquotidien'] = True
812
        response.filter['gauche'] = self.box_side(path)
813
        response.filter['keywords'] = template.get_current_theme().get('keywords')
814
        get_publisher().substitutions.feed(self)
815

    
816
        response.breadcrumb = [ ('', _('Home')) ]
817

    
818
        if not self.admin:
819
            self.admin = get_publisher().admin_directory_class()
820

    
821
        if not self.backoffice:
822
            self.backoffice = get_publisher().backoffice_directory_class()
823

    
824
        if path and path[0] in self.static_directories:
825
            return self.serve_statics(path)
826

    
827
        try:
828
            return Directory._q_traverse(self, path)
829
        except errors.TraversalError, e:
830
            try:
831
                f = FormDef.get_by_urlname(path[0])
832
            except KeyError:
833
                pass
834
            else:
835
                base_url = get_publisher().get_root_url()
836

    
837
                uri_rest = get_request().environ.get('REQUEST_URI')
838
                if not uri_rest:
839
                    # REQUEST_URI doesn't exist when using internal HTTP server
840
                    # (--http)
841
                    uri_rest = get_request().get_path()
842
                    if get_request().get_query():
843
                        uri_rest += '?' + get_request().get_query()
844
                if uri_rest.startswith(base_url):
845
                    uri_rest = uri_rest[len(base_url):]
846
                if f.category:
847
                    return redirect('%s%s/%s' % (base_url, f.category.url_name, uri_rest))
848

    
849
            raise e
850

    
851

    
852
    def _q_lookup(self, component):
853
        # is this a category ?
854
        try:
855
            category = Category.get_by_urlname(component)
856
        except KeyError:
857
            pass
858
        else:
859
            return FormsRootDirectory(category)
860

    
861
        # is this a formdef ?
862
        try:
863
            formdef = FormDef.get_by_urlname(component)
864
        except KeyError:
865
            pass
866
        else:
867
            # if there's no category, or the request is a POST, directly call
868
            # into FormsRootDirectory.
869
            if formdef.category_id is None or get_request().get_method() == 'POST':
870
                get_response().filter['bigdiv'] = 'rub_service'
871
                return FormsRootDirectory()._q_lookup(component)
872

    
873
            # if there is category, let it fall back to raise TraversalError,
874
            # it will get caught in _q_traverse that will redirect it to an
875
            # URL embedding the category
876

    
877
        return None
878

    
879
    def json(self):
880
        return FormsRootDirectory().json()
881

    
882
    def categories(self):
883
        return FormsRootDirectory().categories()
884

    
885
    def _q_index(self):
886
        if get_request().is_json():
887
            return FormsRootDirectory().json()
888

    
889
        root_url = get_publisher().get_root_url()
890
        if get_request().user and get_request().user.anonymous and get_request().user.lasso_dump:
891
            return redirect('%smyspace/new' % root_url)
892

    
893
        redirect_url = get_cfg('misc', {}).get('homepage-redirect-url')
894
        if redirect_url:
895
            return redirect(misc.get_variadic_url(redirect_url,
896
                get_publisher().substitutions.get_context_variables()))
897

    
898
        if get_response().iframe_mode:
899
            # never display home page in an iframe
900
            return redirect('%sservices' % root_url)
901

    
902
        template.html_top()
903
        r = TemplateIO(html=True)
904
        get_response().filter['is_index'] = True
905

    
906
        if not 'auquotidien-welcome-in-services' in get_response().filter.get('keywords', []):
907
            t = TextsDirectory.get_html_text('aq-home-page')
908
            if not t:
909
                if get_request().user:
910
                    t = TextsDirectory.get_html_text('welcome-logged')
911
                else:
912
                    t = TextsDirectory.get_html_text('welcome-unlogged')
913
            if t:
914
                r += htmltext('<div id="home-page-intro">')
915
                r += t
916
                r += htmltext('</div>')
917

    
918
        r += htmltext('<div id="centre">')
919
        r += self.box_services(position='1st')
920
        r += htmltext('</div>')
921
        r += htmltext('<div id="droite">')
922
        r += self.myspace_snippet()
923
        r += self.box_services(position='2nd')
924
        r += self.consultations()
925
        r += self.announces()
926
        r += htmltext('</div>')
927

    
928
        user = get_request().user
929
        if user and user.can_go_in_backoffice():
930
            get_response().filter['backoffice'] = True
931

    
932
        return r.getvalue()
933

    
934
    def services(self):
935
        template.html_top()
936
        get_response().filter['bigdiv'] = 'rub_service'
937
        return self.box_services(level = 2)
938

    
939
    def box_services(self, level=3, position=None):
940
        ## Services
941
        if get_request().user and get_request().user.roles:
942
            accepted_roles = get_request().user.roles
943
        else:
944
            accepted_roles = []
945

    
946
        cats = Category.select(order_by = 'name')
947
        cats = [x for x in cats if x.url_name != 'consultations']
948
        Category.sort_by_position(cats)
949

    
950
        all_formdefs = FormDef.select(lambda x: not x.is_disabled() or x.disabled_redirection,
951
                order_by = 'name')
952
        if get_response().page_template_key == 'mobile':
953
            # if we are in 'mobile' mode, and some formdefs have a 'mobile'
954
            # keyword, we limit the display to those
955
            if any((x for x in all_formdefs if x.keywords and 'mobile' in x.keywords)):
956
                all_formdefs = [x for x in all_formdefs if x.keywords and 'mobile' in x.keywords]
957

    
958
        if position:
959
            t = self.display_list_of_formdefs(
960
                            [x for x in cats if x.get_homepage_position() == position],
961
                            all_formdefs, accepted_roles)
962
        else:
963
            t = self.display_list_of_formdefs(cats, all_formdefs, accepted_roles)
964

    
965
        if not t:
966
            return
967

    
968
        r = TemplateIO(html=True)
969

    
970
        if position == '2nd':
971
            r += htmltext('<div id="services-2nd">')
972
        else:
973
            r += htmltext('<div id="services">')
974
        if level == 2:
975
            r += htmltext('<h2>%s</h2>') % _('Services')
976
        else:
977
            r += htmltext('<h3>%s</h3>') % _('Services')
978

    
979
        if get_response().iframe_mode:
980
            if get_request().user:
981
                message = TextsDirectory.get_html_text('welcome-logged')
982
            else:
983
                message = TextsDirectory.get_html_text('welcome-unlogged')
984

    
985
            if message:
986
                r += htmltext('<div id="welcome-message">')
987
                r += message
988
                r += htmltext('</div>')
989
        elif 'auquotidien-welcome-in-services' in get_response().filter.get('keywords', []):
990
            homepage_text = TextsDirectory.get_html_text('aq-home-page')
991
            if homepage_text:
992
                r += htmltext('<div id="home-page-intro">')
993
                r += homepage_text
994
                r += htmltext('</div>')
995

    
996
        r += htmltext('<ul>')
997
        r += t
998
        r += htmltext('</ul>')
999

    
1000
        r += htmltext('</div>')
1001
        return r.getvalue()
1002

    
1003
    def display_list_of_formdefs(self, cats, all_formdefs, accepted_roles):
1004
        r = TemplateIO(html=True)
1005
        for category in cats:
1006
            if category.url_name == 'consultations':
1007
                self.consultations_category = category
1008
                continue
1009
            formdefs = [x for x in all_formdefs if str(x.category_id) == str(category.id)]
1010
            formdefs_advertise = []
1011

    
1012
            for formdef in formdefs[:]:
1013
                if formdef.is_disabled(): # is a redirection
1014
                    continue
1015
                if not formdef.roles:
1016
                    continue
1017
                if not get_request().user:
1018
                    if formdef.always_advertise:
1019
                        formdefs_advertise.append(formdef)
1020
                    formdefs.remove(formdef)
1021
                    continue
1022
                if logged_users_role().id in formdef.roles:
1023
                    continue
1024
                for q in accepted_roles:
1025
                    if q in formdef.roles:
1026
                        break
1027
                else:
1028
                    if formdef.always_advertise:
1029
                        formdefs_advertise.append(formdef)
1030
                    formdefs.remove(formdef)
1031

    
1032
            if not formdefs and not formdefs_advertise:
1033
                continue
1034

    
1035
            keywords = {}
1036
            for formdef in formdefs:
1037
                for keyword in formdef.keywords_list:
1038
                    keywords[keyword] = True
1039

    
1040
            r += htmltext('<li id="category-%s" data-keywords="%s">') % (
1041
                    category.url_name, ' '.join(keywords))
1042
            r += htmltext('<strong>')
1043
            r += htmltext('<a href="%s/">') % category.url_name
1044
            r += category.name
1045
            r += htmltext('</a></strong>\n')
1046
            r += category.get_description_html_text()
1047
            r += htmltext('<ul>')
1048
            limit = category.get_limit()
1049
            for formdef in formdefs[:limit]:
1050
                r += htmltext('<li data-keywords="%s">') % ' '.join(formdef.keywords_list)
1051
                classes = []
1052
                if formdef.is_disabled() and formdef.disabled_redirection:
1053
                    classes.append('redirection')
1054
                r += htmltext('<a class="%s" href="%s/%s/">%s</a>') % (
1055
                        ' '.join(classes), category.url_name, formdef.url_name, formdef.name)
1056
                r += htmltext('</li>\n')
1057
            if len(formdefs) < limit:
1058
                for formdef in formdefs_advertise[:limit-len(formdefs)]:
1059
                    r += htmltext('<li class="required-authentication">')
1060
                    r += htmltext('<a href="%s/%s/">%s</a>') % (category.url_name, formdef.url_name, formdef.name)
1061
                    r += htmltext('<span> (%s)</span>') % _('authentication required')
1062
                    r += htmltext('</li>\n')
1063
            if (len(formdefs)+len(formdefs_advertise)) > limit:
1064
                r += htmltext('<li class="all-forms"><a href="%s/" title="%s">%s</a></li>') % (category.url_name,
1065
                        _('Access to all forms of the "%s" category') % category.name,
1066
                        _('Access to all forms in this category'))
1067
            r += htmltext('</ul>')
1068
            r += htmltext('</li>\n')
1069

    
1070
        return r.getvalue()
1071

    
1072
    def consultations(self):
1073
        cats = [x for x in Category.select() if x.url_name == 'consultations']
1074
        if not cats:
1075
            return
1076
        consultations_category = cats[0]
1077
        formdefs = FormDef.select(lambda x: (
1078
                    str(x.category_id) == str(consultations_category.id) and
1079
                        (not x.is_disabled() or x.disabled_redirection)),
1080
                    order_by = 'name')
1081
        if not formdefs:
1082
            return
1083
        ## Consultations
1084
        r = TemplateIO(html=True)
1085
        r += htmltext('<div id="consultations">')
1086
        r += htmltext('<h3>%s</h3>') % _('Consultations')
1087
        r += consultations_category.get_description_html_text()
1088
        r += htmltext('<ul>')
1089
        for formdef in formdefs:
1090
            r += htmltext('<li>')
1091
            r += htmltext('<a href="%s/%s/">%s</a>') % (consultations_category.url_name,
1092
                formdef.url_name, formdef.name)
1093
            r += htmltext('</li>')
1094
        r += htmltext('</ul>')
1095
        r += htmltext('</div>')
1096
        return r.getvalue()
1097

    
1098
    def box_side(self, path):
1099
        r = TemplateIO(html=True)
1100
        root_url = get_publisher().get_root_url()
1101

    
1102
        if self.has_anonymous_access_codes():
1103
            r += htmltext('<form id="follow-form" action="%saccesscode">') % root_url
1104
            r += htmltext('<h3>%s</h3>') % _('Tracking')
1105
            r += htmltext('<label>%s</label> ') % _('Code:')
1106
            r += htmltext('<input name="code" size="10"/>')
1107
            r += htmltext('</form>')
1108

    
1109
        r += self.links()
1110

    
1111
        cats = Category.select(order_by = 'name')
1112
        cats = [x for x in cats if x.url_name != 'consultations' and x.get_homepage_position() == 'side']
1113
        Category.sort_by_position(cats)
1114
        if cats:
1115
            r += htmltext('<div id="side-services">')
1116
            r += htmltext('<h3>%s</h3>') % _('Services')
1117
            r += htmltext('<ul>')
1118
            for cat in cats:
1119
                r += htmltext('<li><a href="%s/">%s</a></li>') % (cat.url_name, cat.name)
1120
            r += htmltext('</ul>')
1121
            r += htmltext('</div>')
1122

    
1123
        if Event.keys(): # if there are events, add a link to the agenda
1124
            tags = get_cfg('misc', {}).get('event_tags')
1125
            if not tags:
1126
                tags = get_default_event_tags()
1127
            r += htmltext('<h3 id="agenda-link"><a href="%sagenda/">%s</a></h3>') % (root_url, _('Agenda'))
1128

    
1129
            if path and path[0] == 'agenda':
1130
                r += htmltext('<p class="tags">')
1131
                for tag in tags:
1132
                    r += htmltext('<a href="%sagenda/tag/%s">%s</a> ') % (root_url, tag, tag)
1133
                r += htmltext('</p>')
1134
                r += self.agenda.display_remote_calendars()
1135

    
1136
                r += htmltext('<p>')
1137
                r += htmltext('  <a href="%sagenda/filter">%s</a>') % (root_url, _('Advanced Filter'))
1138
                r += htmltext('</p>')
1139

    
1140
        v = r.getvalue()
1141
        if v:
1142
            r = TemplateIO(html=True)
1143
            r += htmltext('<div id="sidebox">')
1144
            r += v
1145
            r += htmltext('</div>')
1146
            return r.getvalue()
1147

    
1148
        return None
1149

    
1150
    def has_anonymous_access_codes(self):
1151
        for workflow in Workflow.select():
1152
            for wfstatus in workflow.possible_status:
1153
                for wfitem in wfstatus.items:
1154
                    if wfitem.key == 'create-anonymous-access-code':
1155
                        return True
1156
        return False
1157

    
1158
    def accesscode(self):
1159
        code = get_request().form.get('code')
1160
        if not code:
1161
            return redirect(get_publisher().get_root_url())
1162
        try:
1163
            token = Token.get(code)
1164
        except KeyError:
1165
            return redirect(get_publisher().get_root_url())
1166
        if token.type != 'anonymous-access-code':
1167
            return redirect(get_publisher().get_root_url())
1168
        formdef_urlname, formdata_id = token.formdata_reference
1169
        try:
1170
            formdata = FormDef.get_by_urlname(formdef_urlname).data_class().get(formdata_id)
1171
        except KeyError:
1172
            return redirect(get_publisher().get_root_url())
1173
        session = get_session()
1174
        if not hasattr(session, '_wf_anonymous_access_authorized'):
1175
            session._wf_anonymous_access_authorized = []
1176
        session._wf_anonymous_access_authorized.append(formdata.get_url())
1177
        return redirect(formdata.get_url() + 'access/')
1178

    
1179
    def links(self):
1180
        links = Link.select()
1181
        if not links:
1182
            return ''
1183

    
1184
        Link.sort_by_position(links)
1185

    
1186
        r = TemplateIO(html=True)
1187

    
1188
        r += htmltext('<div id="links">')
1189
        if links[0].url:
1190
            # first link has an URL, so it's not a title, so we display a
1191
            # generic title
1192
            r += htmltext('<h3>%s</h3>') % _('Useful links')
1193
        has_ul = False
1194
        vars = get_publisher().substitutions.get_context_variables()
1195
        for link in links:
1196
            if not link.url:
1197
                # acting title
1198
                if has_ul:
1199
                    r += htmltext('</ul>')
1200
                r += htmltext('<h3>%s</h3>') % link.title
1201
                r += htmltext('<ul>')
1202
                has_ul = True
1203
            else:
1204
                if not has_ul:
1205
                    r += htmltext('<ul>')
1206
                    has_ul = True
1207
                r += htmltext('<li class="link-%s"><a href="%s">%s</a></li>') % (
1208
                        simplify(link.title), get_variadic_url(link.url, vars), link.title)
1209
        if has_ul:
1210
            r += htmltext('</ul>')
1211
        r += htmltext('</div>')
1212
        return r.getvalue()
1213

    
1214
    def announces(self):
1215
        announces = Announce.get_published_announces()
1216
        if not announces:
1217
            return
1218

    
1219
        r = TemplateIO(html=True)
1220
        r += htmltext('<div id="announces">')
1221
        r += htmltext('<h3>%s</h3>') % _('Announces to citizens')
1222
        for item in announces[:3]:
1223
            r += htmltext('<div class="announce-item">')
1224
            r += htmltext('<h4>')
1225
            if item.publication_time:
1226
                r += time.strftime(misc.date_format(), item.publication_time)
1227
                r += ' - '
1228
            r += item.title
1229
            r += htmltext('</h4>')
1230
            r += htmltext('<p>')
1231
            r += item.text
1232
            r += htmltext('</p>')
1233
            r += htmltext('</div>')
1234

    
1235
        r += htmltext('<ul id="announces-links">')
1236
        r += htmltext('<li><a href="announces/subscribe">%s</a></li>') % _('Receiving those Announces')
1237
        r += htmltext('<li><a href="announces/">%s</a></li>') % _('Previous Announces')
1238
        r += htmltext('</ul>')
1239
        r += htmltext('</div>')
1240
        return r.getvalue()
1241

    
1242
    def myspace_snippet(self):
1243
        r = TemplateIO(html=True)
1244
        r += htmltext('<div id="myspace">')
1245
        r += htmltext('<h3>%s</h3>') % _('My Space')
1246
        r += htmltext('<ul>')
1247
        if get_request().user and not get_request().user.anonymous:
1248
            r += htmltext('  <li><a href="myspace/" id="member">%s</a></li>') % _('Access to your personal space')
1249
            r += htmltext('  <li><a href="logout" id="logout">%s</a></li>') % _('Logout')
1250
        else:
1251
            r += htmltext('  <li><a href="register/" id="inscr">%s</a></li>') % _('Registration')
1252
            r += htmltext('  <li><a href="login/" id="login">%s</a></li>') % _('Login')
1253
        r += htmltext('</ul>')
1254
        r += htmltext('</div>')
1255
        return r.getvalue()
1256

    
1257
    def page_view(self, key, title, urlname = None):
1258
        if not urlname:
1259
            urlname = key[3:].replace(str('_'), str('-'))
1260
        get_response().breadcrumb.append((urlname, title))
1261
        template.html_top(title)
1262
        r = TemplateIO(html=True)
1263
        r += htmltext('<div class="article">')
1264
        r += htmltext(TextsDirectory.get_html_text(key))
1265
        r += htmltext('</div>')
1266
        return r.getvalue()
1267

    
1268
    def informations_editeur(self):
1269
        get_response().filter['bigdiv'] = 'info'
1270
        return self.page_view('aq-editor-info', _('Editor Informations'),
1271
                urlname = 'informations_editeur')
1272

    
1273
    def accessibility(self):
1274
        get_response().filter['bigdiv'] = 'accessibility'
1275
        return self.page_view('aq-accessibility', _('Accessibility Statement'))
1276

    
1277
    def contact(self):
1278
        get_response().filter['bigdiv'] = 'contact'
1279
        return self.page_view('aq-contact', _('Contact'))
1280

    
1281
    def help(self):
1282
        get_response().filter['bigdiv'] = 'help'
1283
        return self.page_view('aq-help', _('Help'))
1284

    
1285

    
1286
from qommon.publisher import get_publisher_class
1287
get_publisher_class().root_directory_class = AlternateRootDirectory
1288
get_publisher_class().after_login_url = 'myspace/'
1289
get_publisher_class().use_sms_feature = True
1290

    
1291
# help links
1292
get_publisher_class().backoffice_help_url = {
1293
    'fr': 'https://doc.entrouvert.org/au-quotidien/stable/guide-gestionnaire.html',
1294
}
1295
get_publisher_class().admin_help_url = {
1296
    'fr': 'https://doc.entrouvert.org/auquotidien/dev/',
1297
}
1298

    
1299

    
1300
EmailsDirectory.register('announces-subscription-confirmation',
1301
        N_('Confirmation of Announces Subscription'),
1302
        N_('Available variables: change_url, cancel_url, time, sitename'),
1303
        default_subject = N_('Announce Subscription Request'),
1304
        default_body = N_("""\
1305
You have (or someone impersonating you has) requested to subscribe to
1306
announces from [sitename].  To confirm this request, visit the
1307
following link:
1308

    
1309
[confirm_url]
1310

    
1311
If you are not the person who made this request, or you wish to cancel
1312
this request, visit the following link:
1313

    
1314
[cancel_url]
1315

    
1316
If you do nothing, the request will lapse after 3 days (precisely on
1317
[time]).
1318
"""))
1319

    
1320

    
1321
TextsDirectory.register('aq-announces-subscription',
1322
        N_('Text on announces subscription page'),
1323
        default = N_('''\
1324
<p>
1325
FIXME
1326
'</p>'''))
1327

    
1328
TextsDirectory.register('aq-sms-demo',
1329
        N_('Text when subscribing to announces SMS and configured as demo'),
1330
        default = N_('''
1331
<p>
1332
Receiving announces by SMS is not possible in this demo
1333
</p>'''))
1334

    
1335
TextsDirectory.register('aq-editor-info', N_('Editor Informations'))
1336
TextsDirectory.register('aq-accessibility', N_('Accessibility Statement'))
1337
TextsDirectory.register('aq-contact', N_('Contact Information'))
1338
TextsDirectory.register('aq-help', N_('Help'))
1339
TextsDirectory.register('aq-sso-text',  N_('Connecting with Identity Provider'),
1340
        default = N_('''<h3>Connecting with Identity Provider</h3>
1341
<p>You can also use your identity provider to connect.
1342
</p>'''))
1343

    
1344
TextsDirectory.register('aq-home-page', N_('Home Page'), wysiwyg = True)
(23-23/27)