from quixote import get_publisher, get_response, get_request, redirect, get_session
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext

from wcs.qommon import _
from wcs.qommon.misc import get_variadic_url, simplify

import os
import re
import string
import urlparse

try:
    import lasso
except ImportError:
    pass

import wcs
import wcs.root
from wcs import qommon
from wcs.qommon import get_cfg, get_logger
from wcs.qommon import template
from wcs.qommon import errors
from wcs.qommon.form import *
from wcs.qommon import logger
from wcs.roles import logged_users_role

from wcs.qommon import emails
from wcs.qommon.sms import SMS
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.data_sources import NamedDataSource
from wcs.qommon.tokens import Token
from wcs.qommon.admin.emails import EmailsDirectory
from wcs.qommon.admin.texts import TextsDirectory

from .links import Link
from .announces import Announce, AnnounceSubscription
from .myspace import MyspaceDirectory
from .agenda import AgendaDirectory
from .events import Event, get_default_event_tags
from .payments import PublicPaymentDirectory
from .payments_ui import InvoicesDirectory

from . import admin

import wcs.forms.root
from wcs.workflows import Workflow
from wcs.forms.preview import PreviewDirectory

from .saml2 import Saml2Directory

OldRootDirectory = wcs.root.RootDirectory

import wcs.qommon.ident.password
import wcs.qommon.ident.idp


def category_get_homepage_position(self):
    if hasattr(self, 'homepage_position') and self.homepage_position:
        return self.homepage_position
    if self.url_name == 'consultations':
        return '2nd'
    return '1st'
Category.get_homepage_position = category_get_homepage_position

def category_get_limit(self):
    if hasattr(self, 'limit') and self.limit is not None:
        return self.limit
    return 7
Category.get_limit = category_get_limit

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


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

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

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

        draft = [x for x in user_forms if x.is_draft() and not x.formdef.is_disabled()]
        if draft:
            r += htmltext('<h4 id="drafts">%s</h4>') % _('My Current Drafts')
            r += htmltext('<ul>')
            for f in draft:
                if f.formdef.category:
                    category_url = '%s' % f.formdef.category.url_name
                else:
                    category_url = '.'
                r += htmltext('<li><a href="%s%s/%s/%s">%s</a>, %s') % (base_url,
                    category_url,
                    f.formdef.url_name, f.id, f.formdef.name,
                    misc.localstrftime(f.receipt_time))
                r += htmltext(' (<a href="%s%s/%s/%s?remove-draft">%s</a>)') % (base_url,
                    category_url,
                    f.formdef.url_name, f.id, _('delete'))
                r += htmltext('</li>')
            r += htmltext('</ul>')

        forms_by_status_name = {}
        for f in user_forms:
            if f.is_draft():
                continue
            status = f.get_visible_status()
            if status:
                status_name = status.name
            else:
                status_name = None
            if status_name in forms_by_status_name:
                forms_by_status_name[status_name].append(f)
            else:
                forms_by_status_name[status_name] = [f]
        for status_name in forms_by_status_name:
            if status_name:
                r += htmltext('<h4>%s</h4>') % _('My forms with status "%s"') % status_name
            else:
                r += htmltext('<h4>%s</h4>') % _('My forms with an unknown status') % status_name
            r += htmltext('<ul>')
            forms_by_status_name[status_name].sort(lambda x,y: cmp(x.receipt_time, y.receipt_time))
            for f in forms_by_status_name[status_name]:
                if f.formdef.category_id:
                    category_url = f.formdef.category.url_name
                else:
                    category_url = '.'
                r += htmltext('<li><a href="%s%s/%s/%s/">%s</a>, %s</li>') % (
                        base_url,
                        category_url,
                        f.formdef.url_name, f.id, f.formdef.name, 
                        misc.localstrftime(f.receipt_time))
            r += htmltext('</ul>')
        return r.getvalue()


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

    def __init__(self, announce):
        self.announce = announce

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

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

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

        r += htmltext('<p>')
        r += self.announce.text
        r += htmltext('</p>')

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


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

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

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

    def _get_announce_subscription(self):
        """ """
        sub = None
        if get_request().user:
            subs = AnnounceSubscription.select(lambda x: x.user_id == get_request().user.id)
            if subs:
                sub = subs[0]
        return sub

    def rawlist(self):
        get_response().filter = None
        return self.announces_list()

    def announces_list(self):
        announces = Announce.get_published_announces()
        if not announces:
            raise errors.TraversalError()

        # XXX: will need pagination someday
        r = TemplateIO(html=True)
        for item in announces:
            r += htmltext('<div class="announce-item">\n')
            r += htmltext('<h4>')
            if item.publication_time:
                r += time.strftime(misc.date_format(), item.publication_time)
                r += ' - '
            r += item.title
            r += htmltext('</h4>\n')
            r += htmltext('<p>\n')
            r += item.text
            r += htmltext('\n</p>\n')
            r += htmltext('</div>\n')
        return r.getvalue()


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

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

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

        if sms_mode == 'demo':
            r += TextsDirectory.get_html_text('aq-sms-demo')
        else:
            announces_cfg = get_cfg('announces',{})
            mobile_mask = announces_cfg.get('mobile_mask')
            if mobile_mask:
                mobile_mask = ' (' + mobile_mask + ')'
            else:
                mobile_mask = ''
            form = Form(enctype='multipart/form-data')
            form.add(StringWidget, 'mobile', title = _('Mobile number %s') % mobile_mask, size=12, required=True)
            form.add_submit('submit', _('Subscribe'))
            form.add_submit('cancel', _('Cancel'))

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

            if form.is_submitted() and not form.has_errors():
                s = self.sms_submit(form)
                if s == False:
                    r += form.render()
                else:
                    return redirect("smsconfirm")
            else:
                r += form.render()
        return r.getvalue()

    def sms_submit(self, form):
        mobile = form.get_widget("mobile").parse()
        # clean the string, remove any extra character
        mobile = re.sub('[^0-9+]','',mobile)
        # if a mask was set, validate
        announces_cfg = get_cfg('announces',{})
        mobile_mask = announces_cfg.get('mobile_mask')
        if mobile_mask:
            mobile_regexp = re.sub('X','[0-9]', mobile_mask) + '$'
            if not re.match(mobile_regexp, mobile):
                form.set_error("mobile", _("Phone number invalid ! It must match ") + mobile_mask)
                return False
        if mobile.startswith('00'):
            mobile = '+' + mobile[2:]
        else:
            # Default to france international prefix
            if not mobile.startswith('+'):
                mobile = re.sub("^0", "+33", mobile)
        sub = self._get_announce_subscription()
        if not sub:
            sub = AnnounceSubscription()
        if get_request().user:
            sub.user_id = get_request().user.id

        if mobile:
            sub.sms = mobile

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

        sub.store()

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

        message = _("Confirmation code : %s") % str(token.id)
        sms_cfg = get_cfg('sms', {})
        sender = sms_cfg.get('sender', 'AuQuotidien')[:11]
        mode = sms_cfg.get('mode', 'none')
        sms = SMS.get_sms_class(mode)
        try:
            sms.send(sender, [mobile], message)
        except errors.SMSError, e:
            get_logger().error(e)
            form.set_error("mobile", _("Send SMS confirmation failed"))
            sub.remove("sms")
            return False

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

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

        if form.is_submitted() and not form.has_errors():
            token = None
            id = form.get_widget("code").parse()
            try:
                token = Token.get(id)
            except KeyError:
                form.set_error("code",  _('Invalid confirmation code.'))
            else:
                if token.type != 'announces-subscription-confirmation':
                    form.set_error("code",  _('Invalid confirmation code.'))
                else:
                    sub = AnnounceSubscription.get(token.subscription_id)
                    token.remove_self()
                    sub.enabled_sms = True
                    sub.store()
                    return redirect('.')
            r += form.render()
        else:
            r += form.render()

        return r.getvalue()

    def sms_unsubscribe(self):
        sub = self._get_announce_subscription()

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

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

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

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

        if form.is_submitted() and not form.has_errors():
            if sub:
                sub.remove("sms")

            root_url = get_publisher().get_root_url()
            r += htmltext('<p>')
            r += _('You have been unsubscribed from announces')
            r += htmltext('</p>')
            r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
        else:
            r += htmltext('<p>')
            r += _('Do you want to stop receiving announces by sms ?')
            r += htmltext('</p>')
            r += form.render()

        return r.getvalue()


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

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

        sub = self._get_announce_subscription()

        r += htmltext('<ul id="announce-modes">')
        if sub and sub.email:
            r += htmltext(' <li>')
            r += htmltext('<span id="par_mail">%s</span>') % _('Email (currently subscribed)')
            r += htmltext(' <a href="email_unsubscribe" rel="popup">%s</a></li>') % _('Unsubscribe')
        else:
            r += htmltext(' <li><a href="email" id="par_mail" rel="popup">%s</a></li>') % _('Email')
        if sub and sub.sms:
            r += htmltext(' <li>')
            if sub.enabled_sms:
                r += htmltext('<span id="par_sms">%s</span>') % _('SMS %s (currently subscribed)') % sub.sms
            else:
                r += htmltext('<span id="par_sms">%s</span>') % _('SMS %s (currently not confirmed)') % sub.sms
                r += htmltext(' <a href="smsconfirm" rel="popup">%s</a> ') % _('Confirmation')
            r += htmltext(' <a href="sms_unsubscribe" rel="popup">%s</a></li>') % _('Unsubscribe')
        elif get_cfg('sms', {}).get('mode', 'none') != 'none':
            r += htmltext(' <li><a href="sms" id="par_sms">%s</a></li>') % _('SMS')
        r += htmltext(' <li><a class="feed-link" href="atom" id="par_rss">%s</a>') % _('Feed')
        r += htmltext('</ul>')
        return r.getvalue()

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

        form = Form(enctype='multipart/form-data')
        if get_request().user:
            if get_request().user.email:
                r += htmltext('<p>')
                r += _('You are logged in and your email is %s, ok to subscribe ?') % \
                        get_request().user.email
                r += htmltext('</p>')
                form.add_submit('submit', _('Subscribe'))
            else:
                r += htmltext('<p>')
                r += _("You are logged in but there is no email address in your profile.")
                r += htmltext('</p>')
                form.add(EmailWidget, 'email', title = _('Email'), required = True)
                form.add_submit('submit', _('Subscribe'))
                form.add_submit('submit-remember', _('Subscribe and add this email to my profile'))
        else:
            r += htmltext('<p>')
            r += _('FIXME will only be used for this purpose etc.')
            r += htmltext('</p>')
            form.add(EmailWidget, 'email', title = _('Email'), required = True)
            form.add_submit('submit', _('Subscribe'))

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

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

        if form.is_submitted() and not form.has_errors():
            s = self.email_submit(form)
            if s is not False:
                return s
        else:
            r += form.render()

        return r.getvalue()

    def email_submit(self, form):
        sub = self._get_announce_subscription()
        if not sub:
            sub = AnnounceSubscription()

        if get_request().user:
            sub.user_id = get_request().user.id

        if form.get_widget('email'):
            sub.email = form.get_widget('email').parse()
        elif get_request().user.email:
            sub.email = get_request().user.email

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

        sub.store()

        if get_request().user:
            r = TemplateIO(html=True)
            root_url = get_publisher().get_root_url()
            r += htmltext('<p>')
            r += _('You have been subscribed to the announces.')
            r += htmltext('</p>')
            r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
            return r.getvalue()

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

        emails.custom_template_email('announces-subscription-confirmation',
                data, sub.email, exclude_current_user = False)

        r = TemplateIO(html=True)
        root_url = get_publisher().get_root_url()
        r += htmltext('<p>')
        r += _('You have been sent an email for confirmation')
        r += htmltext('</p>')
        r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))
        return r.getvalue()

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

        root_url = get_publisher().get_root_url()

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

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

        sub = AnnounceSubscription.get(token.subscription_id)

        if action == 'cxl':
            r = TemplateIO(html=True)
            root_url = get_publisher().get_root_url()
            template.html_top(_('Email Subscription'))
            r += htmltext('<h1>%s</h1>') % _('Request Cancelled')
            r += htmltext('<p>%s</p>') % _('The request for subscription has been cancelled.')
            r += htmltext('<p>')
            r += htmltext(_('Continue to <a href="%s">home page</a>') % root_url)
            r += htmltext('</p>')
            token.remove_self()
            sub.remove_self()
            return r.getvalue()

        if action == 'cfm':
            token.remove_self()
            sub.enabled = True
            sub.store()
            r = TemplateIO(html=True)
            root_url = get_publisher().get_root_url()
            template.html_top(_('Email Subscription'))
            r += htmltext('<h1>%s</h1>') % _('Subscription Confirmation')
            r += htmltext('<p>%s</p>') % _('Your subscription to announces is now effective.')
            r += htmltext('<p>')
            r += htmltext(_('Continue to <a href="%s">home page</a>') % root_url)
            r += htmltext('</p>')
            return r.getvalue()

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

        from pyatom import pyatom
        xmldoc = pyatom.XMLDoc()
        feed = pyatom.Feed()
        xmldoc.root_element = feed
        feed.title = get_cfg('misc', {}).get('sitename') or 'Publik'
        feed.id = get_request().get_url()

        author_email = get_cfg('emails', {}).get('reply_to')
        if not author_email:
            author_email = get_cfg('emails', {}).get('from')
        if author_email:
            feed.authors.append(pyatom.Author(author_email))

        announces = Announce.get_published_announces()

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

        for item in announces:
            entry = item.get_atom_entry()
            if entry:
                feed.entries.append(entry)

        return str(feed)

    def email_unsubscribe(self):
        sub = self._get_announce_subscription()

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

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

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

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

        if form.is_submitted() and not form.has_errors():
            if sub:
                sub.remove("email")
            else:
                email = form.get_widget('email').parse()
                for s in AnnounceSubscription.select():
                    if s.email == email:
                        s.remove("email")

            root_url = get_publisher().get_root_url()
            r += htmltext('<p>')
            r += _('You have been unsubscribed from announces')
            r += htmltext('</p>')
            r += htmltext('<a href="%s">%s</a>') % (root_url, _('Back Home'))

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

        return r.getvalue()

    def _q_lookup(self, component):
        try:
            announce = Announce.get(component)
        except KeyError:
            raise errors.TraversalError()

        if announce.hidden:
            raise errors.TraversalError()

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

OldRegisterDirectory = wcs.root.RegisterDirectory

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

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

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

        if len(ident_methods) == 1:
            method = ident_methods[0]
        else:
            method = 'password'

        return wcs.qommon.ident.register(method)

OldLoginDirectory = wcs.root.LoginDirectory

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

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

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

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

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

        # don't display authentication system choice
        if len(ident_methods) == 1:
            method = ident_methods[0]
            try:
                return wcs.qommon.ident.login(method)
            except KeyError:
                get_logger().error('failed to login with method %s' % method)
                return errors.TraversalError()

        if sorted(ident_methods) == ['idp', 'password']:
            r = TemplateIO(html=True)
            get_response().breadcrumb.append(('login', _('Login')))
            identities_cfg = get_cfg('identities', {})
            form = Form(enctype = 'multipart/form-data', id = 'login-form', use_tokens = False)
            if identities_cfg.get('email-as-username', False):
                form.add(StringWidget, 'username', title = _('Email'), size=25, required=True)
            else:
                form.add(StringWidget, 'username', title = _('Username'), size=25, required=True)
            form.add(PasswordWidget, 'password', title = _('Password'), size=25, required=True)
            form.add_submit('submit', _('Connect'))
            if form.is_submitted() and not form.has_errors():
                tmp = wcs.qommon.ident.password.MethodDirectory().login_submit(form)
                if not form.has_errors():
                    return tmp

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

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

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

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

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

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

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


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


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


class AlternateRootDirectory(OldRootDirectory):
    _q_exports = ['', 'admin', 'backoffice', 'forms', 'login', 'logout',
            'saml', 'register', 'ident', 'afterjobs',
            ('informations-editeur', 'informations_editeur'),
            ('announces', 'announces_dir'),
            'myspace', 'services', 'agenda', 'categories', 'user',
            ('tmp-upload', 'tmp_upload'), 'json', '__version__',
            'themes', 'pages', 'payment', 'invoices', 'roles',
            'api', 'code', 'fargo', 'tryauth', 'auth', 'preview',
            ('reload-top', 'reload_top'), 'static',
            ('i18n.js', 'i18n_js'), 'actions',]

    announces_dir = AnnouncesDirectory()
    register = AlternateRegisterDirectory()
    login = AlternateLoginDirectory()
    ident = AlternateIdentDirectory()
    myspace = MyspaceDirectory()
    agenda = AgendaDirectory()
    saml = Saml2Directory()
    payment = PublicPaymentDirectory()
    invoices = InvoicesDirectory()
    code = wcs.forms.root.TrackingCodesDirectory()
    preview = AlternatePreviewDirectory()

    def get_substitution_variables(self):
        d = {}
        def print_links(fd):
            fd.write(str(self.links()))
        d['links'] = print_links
        return d

    def _q_traverse(self, path):
        self.feed_substitution_parts()

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

        response = get_response()
        if not hasattr(response, 'filter'):
            response.filter = {}

        response.filter['auquotidien'] = True
        if not path or (path[0] not in ('api', 'backoffice') and not get_request().is_json()):
            # api & backoffice have no use for a side box
            response.filter['gauche'] = self.box_side(path)
            response.filter['keywords'] = template.get_current_theme().get('keywords')
        get_publisher().substitutions.feed(self)

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

        if not self.admin:
            self.admin = get_publisher().admin_directory_class()

        if not self.backoffice:
            self.backoffice = get_publisher().backoffice_directory_class()

        try:
            return Directory._q_traverse(self, path)
        except errors.TraversalError, e:
            try:
                f = FormDef.get_by_urlname(path[0])
            except KeyError:
                pass
            else:
                base_url = get_publisher().get_root_url()

                uri_rest = get_request().environ.get('REQUEST_URI')
                if not uri_rest:
                    # REQUEST_URI doesn't exist when using internal HTTP server
                    # (--http)
                    uri_rest = get_request().get_path()
                    if get_request().get_query():
                        uri_rest += '?' + get_request().get_query()
                if uri_rest.startswith(base_url):
                    uri_rest = uri_rest[len(base_url):]
                if f.category:
                    if f.category.url_name == f.url_name:
                        return FormsRootDirectory(f.category)._q_traverse(path[1:])
                    return redirect('%s%s/%s' % (base_url, f.category.url_name, uri_rest))

            raise e


    def _q_lookup(self, component):
        # is this a category ?
        try:
            category = Category.get_by_urlname(component)
        except KeyError:
            category = None

        # is this a formdef ?
        try:
            formdef = FormDef.get_by_urlname(component)
        except KeyError:
            if category:
                return FormsRootDirectory(category)
        else:
            # if the form has no category, or the request is a POST, or the
            # slug matches both a category and a formdef, directly call
            # into FormsRootDirectory.
            if formdef.category_id is None or get_request().get_method() == 'POST' or (
                    formdef and category):
                get_response().filter['bigdiv'] = 'rub_service'
                return FormsRootDirectory()._q_lookup(component)

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

        if component in ('accessibility', 'contact', 'help'):
            if TextsDirectory.get_html_text('aq-' + component):
                return getattr(self, component)()

        return None

    def json(self):
        return FormsRootDirectory().json()

    def categories(self):
        return FormsRootDirectory().categories()

    def _q_index(self):
        if get_request().is_json():
            return FormsRootDirectory().json()

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

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

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

        if not 'auquotidien-welcome-in-services' in get_response().filter.get('keywords', []):
            t = TextsDirectory.get_html_text('aq-home-page')
            if not t:
                if get_request().user:
                    t = TextsDirectory.get_html_text('welcome-logged')
                else:
                    t = TextsDirectory.get_html_text('welcome-unlogged')
            if t:
                r += htmltext('<div id="home-page-intro">')
                r += t
                r += htmltext('</div>')

        r += htmltext('<div id="centre">')
        r += self.box_services(position='1st')
        r += htmltext('</div>')
        r += htmltext('<div id="droite">')
        r += self.myspace_snippet()
        r += self.box_services(position='2nd')
        r += self.consultations()
        r += self.announces()
        r += htmltext('</div>')

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

        return r.getvalue()

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

    def box_services(self, level=3, position=None):
        ## Services
        if get_request().user and get_request().user.roles:
            accepted_roles = get_request().user.roles
        else:
            accepted_roles = []

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

        all_formdefs = FormDef.select(lambda x: not x.is_disabled() or x.disabled_redirection,
                order_by = 'name')

        if position:
            t = self.display_list_of_formdefs(
                            [x for x in cats if x.get_homepage_position() == position],
                            all_formdefs, accepted_roles)
        else:
            t = self.display_list_of_formdefs(cats, all_formdefs, accepted_roles)

        if not t:
            return

        r = TemplateIO(html=True)

        if position == '2nd':
            r += htmltext('<div id="services-2nd">')
        else:
            r += htmltext('<div id="services">')
        if level == 2:
            r += htmltext('<h2>%s</h2>') % _('Services')
        else:
            r += htmltext('<h3>%s</h3>') % _('Services')

        if 'auquotidien-welcome-in-services' in get_response().filter.get('keywords', []):
            homepage_text = TextsDirectory.get_html_text('aq-home-page')
            if homepage_text:
                r += htmltext('<div id="home-page-intro">')
                r += homepage_text
                r += htmltext('</div>')

        r += htmltext('<ul>')
        r += t
        r += htmltext('</ul>')

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

    def display_list_of_formdefs(self, cats, all_formdefs, accepted_roles):
        r = TemplateIO(html=True)
        for category in cats:
            if category.url_name == 'consultations':
                self.consultations_category = category
                continue
            formdefs = [x for x in all_formdefs if str(x.category_id) == str(category.id)]
            formdefs_advertise = []

            for formdef in formdefs[:]:
                if formdef.is_disabled(): # is a redirection
                    continue
                if not formdef.roles:
                    continue
                if not get_request().user:
                    if formdef.always_advertise:
                        formdefs_advertise.append(formdef)
                    formdefs.remove(formdef)
                    continue
                if logged_users_role().id in formdef.roles:
                    continue
                for q in accepted_roles:
                    if q in formdef.roles:
                        break
                else:
                    if formdef.always_advertise:
                        formdefs_advertise.append(formdef)
                    formdefs.remove(formdef)

            if not formdefs and not formdefs_advertise:
                continue

            keywords = {}
            for formdef in formdefs:
                for keyword in formdef.keywords_list:
                    keywords[keyword] = True

            r += htmltext('<li id="category-%s" data-keywords="%s">') % (
                    category.url_name, ' '.join(keywords))
            r += htmltext('<strong>')
            r += htmltext('<a href="%s/">') % category.url_name
            r += category.name
            r += htmltext('</a></strong>\n')
            r += category.get_description_html_text()
            r += htmltext('<ul>')
            limit = category.get_limit()
            for formdef in formdefs[:limit]:
                r += htmltext('<li data-keywords="%s">') % ' '.join(formdef.keywords_list)
                classes = []
                if formdef.is_disabled() and formdef.disabled_redirection:
                    classes.append('redirection')
                r += htmltext('<a class="%s" href="%s/%s/">%s</a>') % (
                        ' '.join(classes), category.url_name, formdef.url_name, formdef.name)
                r += htmltext('</li>\n')
            if len(formdefs) < limit:
                for formdef in formdefs_advertise[:limit-len(formdefs)]:
                    r += htmltext('<li class="required-authentication">')
                    r += htmltext('<a href="%s/%s/">%s</a>') % (category.url_name, formdef.url_name, formdef.name)
                    r += htmltext('<span> (%s)</span>') % _('authentication required')
                    r += htmltext('</li>\n')
            if (len(formdefs)+len(formdefs_advertise)) > limit:
                r += htmltext('<li class="all-forms"><a href="%s/" title="%s">%s</a></li>') % (category.url_name,
                        _('Access to all forms of the "%s" category') % category.name,
                        _('Access to all forms in this category'))
            r += htmltext('</ul>')
            r += htmltext('</li>\n')

        return r.getvalue()

    def consultations(self):
        cats = [x for x in Category.select() if x.url_name == 'consultations']
        if not cats:
            return
        consultations_category = cats[0]
        formdefs = FormDef.select(lambda x: (
                    str(x.category_id) == str(consultations_category.id) and
                        (not x.is_disabled() or x.disabled_redirection)),
                    order_by = 'name')
        if not formdefs:
            return
        ## Consultations
        r = TemplateIO(html=True)
        r += htmltext('<div id="consultations">')
        r += htmltext('<h3>%s</h3>') % _('Consultations')
        r += consultations_category.get_description_html_text()
        r += htmltext('<ul>')
        for formdef in formdefs:
            r += htmltext('<li>')
            r += htmltext('<a href="%s/%s/">%s</a>') % (consultations_category.url_name,
                formdef.url_name, formdef.name)
            r += htmltext('</li>')
        r += htmltext('</ul>')
        r += htmltext('</div>')
        return r.getvalue()

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

        if (path == [''] and
                'include-tracking-code-form' in get_response().filter.get('keywords', []) and
                self.has_anonymous_access_codes()):
            r += htmltext('<form id="follow-form" action="%scode/load">') % root_url
            r += htmltext('<h3>%s</h3>') % _('Tracking code')
            r += htmltext('<input size="12" name="code" placeholder="%s"/>') % _('ex: RPQDFVCD')
            r += htmltext('<input type="submit" value="%s"/>') % _('Load')
            r += htmltext('</form>')

        if get_cfg('aq-permissions', {}).get('links'):
            r += self.links()

        cats = Category.select(order_by = 'name')
        cats = [x for x in cats if x.url_name != 'consultations' and x.get_homepage_position() == 'side']
        Category.sort_by_position(cats)
        if cats:
            r += htmltext('<div id="side-services">')
            r += htmltext('<h3>%s</h3>') % _('Services')
            r += htmltext('<ul>')
            for cat in cats:
                r += htmltext('<li><a href="%s/">%s</a></li>') % (cat.url_name, cat.name)
            r += htmltext('</ul>')
            r += htmltext('</div>')

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

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

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

        v = r.getvalue()
        if v:
            r = TemplateIO(html=True)
            r += htmltext('<div id="sidebox">')
            r += v
            r += htmltext('</div>')
            return r.getvalue()

        return None

    def has_anonymous_access_codes(self):
        return any((x for x in FormDef.select() if x.enable_tracking_codes))

    def links(self):
        links = Link.select()
        if not links:
            return ''

        Link.sort_by_position(links)

        r = TemplateIO(html=True)

        r += htmltext('<div id="links">')
        if links[0].url:
            # first link has an URL, so it's not a title, so we display a
            # generic title
            r += htmltext('<h3>%s</h3>') % _('Useful links')
        has_ul = False
        vars = get_publisher().substitutions.get_context_variables()
        for link in links:
            if not link.url:
                # acting title
                if has_ul:
                    r += htmltext('</ul>')
                r += htmltext('<h3>%s</h3>') % link.title
                r += htmltext('<ul>')
                has_ul = True
            else:
                if not has_ul:
                    r += htmltext('<ul>')
                    has_ul = True
                r += htmltext('<li class="link-%s"><a href="%s">%s</a></li>') % (
                        simplify(link.title), get_variadic_url(link.url, vars), link.title)
        if has_ul:
            r += htmltext('</ul>')
        r += htmltext('</div>')
        return r.getvalue()

    def announces(self):
        announces = Announce.get_published_announces()
        if not announces:
            return

        r = TemplateIO(html=True)
        r += htmltext('<div id="announces">')
        r += htmltext('<h3>%s</h3>') % _('Announces to citizens')
        for item in announces[:3]:
            r += htmltext('<div class="announce-item">')
            r += htmltext('<h4>')
            if item.publication_time:
                r += time.strftime(misc.date_format(), item.publication_time)
                r += ' - '
            r += item.title
            r += htmltext('</h4>')
            r += htmltext('<p>')
            r += item.text
            r += htmltext('</p>')
            r += htmltext('</div>')

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

    def myspace_snippet(self):
        r = TemplateIO(html=True)
        r += htmltext('<div id="myspace">')
        r += htmltext('<h3>%s</h3>') % _('My Space')
        r += htmltext('<ul>')
        if get_request().user and not get_request().user.anonymous:
            r += htmltext('  <li><a href="myspace/" id="member">%s</a></li>') % _('Access to your personal space')
            r += htmltext('  <li><a href="logout" id="logout">%s</a></li>') % _('Logout')
        else:
            r += htmltext('  <li><a href="register/" id="inscr">%s</a></li>') % _('Registration')
            r += htmltext('  <li><a href="login/" id="login">%s</a></li>') % _('Login')
        r += htmltext('</ul>')
        r += htmltext('</div>')
        return r.getvalue()

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

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

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

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

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


from qommon.publisher import get_publisher_class
get_publisher_class().root_directory_class = AlternateRootDirectory
get_publisher_class().after_login_url = 'myspace/'
get_publisher_class().use_sms_feature = True


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

[confirm_url]

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

[cancel_url]

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


TextsDirectory.register('aq-announces-subscription',
        N_('Text on announces subscription page'),
        default = N_('''\
<p>
FIXME
'</p>'''))

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

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

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