## $Id: layout.py 7700 2012-02-24 23:04:05Z henrik $
##
## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""Basic layout components.
"""
import os
import grok
from datetime import date, datetime
from grokcore.view import PageTemplateFile
from grokcore.formlib.formlib import Action
from cgi import escape
from zope.i18n import translate
from zope.i18nmessageid import Message
from hurry.resource import mode
from megrok.layout import Page, Layout, Form, EditForm, DisplayForm, AddForm
from z3c.flashmessage.interfaces import IMessageSource, IMessageReceiver
from zope.component import getUtility, queryUtility, ComponentLookupError
from zope.formlib.utility import setUpWidgets
from zope.interface import Interface
from zope.site.hooks import getSite
from waeup.sirp.interfaces import ISIRPObject, IUserAccount
from waeup.sirp.browser.interfaces import ITheme
from waeup.sirp.browser.theming import get_all_themes, SIRPThemeBase
from waeup.sirp.students.interfaces import IStudentNavigation
from waeup.sirp.applicants.interfaces import IApplicant
from waeup.sirp.authentication import get_principal_role_manager

grok.templatedir('templates')
default_waeup_display_template = PageTemplateFile(
    os.path.join('templates', 'default_waeup_display_form.pt'))
default_waeup_display_template.__grok_name__ = 'default_waeup_display_form'

default_waeup_edit_template = PageTemplateFile(
    os.path.join('templates', 'default_waeup_edit_form.pt'))
default_waeup_edit_template.__grok_name__ = 'default_waeup_edit_form'

default_primary_nav_template = PageTemplateFile(
    os.path.join('templates', 'primarynavtab.pt'))
default_primary_nav_template.__grok_name__ = 'default_primary_nav'

class action(grok.action):

    def __call__(self, success):
        action = SIRPAction(self.label, success=success, **self.options)
        self.actions.append(action)
        return action

class SIRPAction(Action):

    def __init__(self, label, style='', **options):
        super(SIRPAction, self).__init__(label, **options)
        self.style = style

    def render(self):
        if not self.available():
            return ''
        label = self.label
        if isinstance(label, Message):
            label = translate(self.label, context=self.form.request)
        return ('<input type="submit" id="%s" name="%s" value="%s"'
                ' class="btn %s"/>' %
                (self.__name__, self.__name__, escape(label, quote=True),
                self.style))

class jsaction(grok.action):

    def __call__(self, success):
        action = JSAction(self.label, success=success, **self.options)
        self.actions.append(action)
        return action

class JSAction(Action):

    msg = '\'Are you sure?\''

    def render(self):
        if not self.available():
            return ''
        label = self.label
        if isinstance(label, Message):
            label = translate(self.label, context=self.form.request)
        return ('<input type="submit" id="%s" name="%s" value="%s"'
                ' class="btn" onclick="return confirmPost(%s)" />' %
                (self.__name__, self.__name__, escape(label, quote=True), self.msg)
                )

def NullValidator(*args, **kw):
    """A validator that does not validate.

    This is needed especially for cancel buttons. We don't want data
    to be validated that will be thrown away in the next step.

    You can use it with ``grok.action`` decorator like this::

      @grok.action('Cancel', validator=NullValidator)
      def cancel(self, **data):
        self.redirect(<whereever-you-go>)
    """
    return dict()

class Messages(grok.View):
    """Display messages of message receivers.
    """

    grok.context(Interface)

    @property
    def messages(self):
        receiver = getUtility(IMessageReceiver)
        return receiver.receive()

class UtilityView(object):
    """A view mixin with useful methods.

    The ``pnav`` attribute (a number) tells, to which primary
    navigation tab a page declares to belong.
    """
    title = u'' # What appears in the content title...
    pnav = 0 # Primary navigation index...

    def application_url(self, name=None):
        """Return the URL of the nearest site.
        """
        site = getSite()
        #if not site:
        #    raise ComponentLookupError("No site found.")
        return self.url(site, name)

    def flash(self, message, type='alert-message warning'):
        """Send a short message to the user.
        """
        source = queryUtility(IMessageSource, name='session')
        if source is None:
            return None
        source.send(message, type)
        return True

class SIRPLayout(UtilityView,Layout):
    """A megrok.layout.Layout with additional methods.
    """
    grok.baseclass()

class SIRPForm(UtilityView,Form):
    """A megrok.layout.Form with additional methods.
    """
    grok.baseclass()

    def setUpWidgets(self,ignore_request=False):
        super(SIRPForm,self).setUpWidgets(ignore_request)
        # Width parameters will be overridden by Bootstrap
        # so we have to set the css class
        if self.widgets.get('subject'):
            self.widgets['subject'].cssClass = 'span9'
        if self.widgets.get('body'):
            self.widgets['body'].height = 10
        if self.widgets.get('body'):
            self.widgets['body'].cssClass = 'span9'

class SIRPPage(UtilityView,Page):
    """A megrok.layout page with additional methods.
    """
    grok.baseclass()

class SIRPDisplayFormPage(UtilityView,DisplayForm):
    """A megrok.layout.DisplayForm with additional methods.
    """
    grok.baseclass()
    template = default_waeup_display_template

class SIRPEditFormPage(UtilityView,EditForm):
    """A megrok.layout.EditForm with additional methods.
    """
    grok.baseclass()
    template = default_waeup_edit_template

    def setUpWidgets(self,ignore_request=False):
        super(SIRPEditFormPage,self).setUpWidgets(ignore_request)
        for widget in self.widgets:
            if widget.__class__.__name__ == 'TextWidget':
                widget.cssClass = 'span8'
            elif widget.__class__.__name__ == 'IntWidget':
                widget.cssClass = 'span2'
        if self.widgets.get('title'):
            self.widgets['title'].cssClass = 'span12'
        if self.widgets.get('frontpage'):
            self.widgets['frontpage'].cssClass = 'span12'
        if self.widgets.get('phone'):
            self.widgets['phone'].cssClass = 'span4'
        if self.widgets.get('notice'):
            self.widgets['notice'].height = 3
        if self.widgets.get('perm_address'):
            self.widgets['perm_address'].cssClass = 'span8'
            self.widgets['perm_address'].height = 10

class SIRPAddFormPage(UtilityView,AddForm):
    """A megrok.layout.AddForm with additional methods.
    """
    grok.baseclass()
    template = default_waeup_edit_template

class SiteLayout(SIRPLayout):
    """ The general site layout.
    """
    grok.context(ISIRPObject)

    #: An instance of the default theme to use for the site layout
    default_theme = SIRPThemeBase()
    stafftemp = grok.PageTemplateFile('templates/staffsitelayout.pt')
    studenttemp = grok.PageTemplateFile('templates/studentsitelayout.pt')

    @property
    def site(self):
        return grok.getSite()

    def getAppTitle(self):
        return getattr(grok.getSite()['configuration'], 'name', u'Sample University')

    def getAppAcronym(self):
        return getattr(grok.getSite()['configuration'], 'acronym', u'Acronym')

    def isAuthenticated(self):
        """Return True if the calling user is authenticated.
        """
        usertitle = self.request.principal.title
        return usertitle != 'Unauthenticated User'

    def getUserTitle(self):
        """Return principal title of current user.
        """
        usertitle = self.request.principal.title
        if usertitle == 'Unauthenticated User':
            return u'Anonymous User'
        return usertitle

    def getUserId(self):
        """Return id of current user.
        """
        userid = self.request.principal.id
        return userid

    def isStudent(self):
        usertype = getattr(self.request.principal, 'user_type', None)
        if not usertype:
            return False
        return self.request.principal.user_type == 'student'

    def isApplicant(self):
        usertype = getattr(self.request.principal, 'user_type', None)
        if not usertype:
            return False
        return self.request.principal.user_type == 'applicant'

    def getStudentName(self):
        """Return the student name.
        """
        if IStudentNavigation.providedBy(self.context):
            return self.context.getStudent().display_fullname
        return

    def getApplicantName(self):
        """Return the applicant name.
        """
        if IApplicant.providedBy(self.context):
            return self.context.fullname
        return

    def formatDatetime(self,datetimeobj):
        if isinstance(datetimeobj, datetime):
            return datetimeobj.strftime("%Y-%m-%d %H:%M:%S")
        else:
            return None

    def formatDate(self,dateobj):
        if isinstance(dateobj, date):
            return dateobj.strftime("%d/%m/%Y")
        else:
            return None

    def update(self):
        """Include the resources required by the chosen skin/theme.

        University instances provide a 'skin' attribute, that should
        hold the internal name of a theme.

        A theme in the waeup.sirp sense consists of a list of
        CSS/JavaScript resources defined in the
        :mod:`waeup.sirp.browser.resources` module.

        If the context University object has no such attribute or the
        set value is not a valid theme name, we pick 'gray waeup
        theme' as default.
        """
        mode('minified')
        theme_name = getattr(grok.getSite()['configuration'], 'skin', '')
        theme = queryUtility(ITheme, name=theme_name,
                             default=self.default_theme)
        for resource in theme.getResources():
            resource.need()
        return

    def render(self):
        if self.isStudent() or self.isApplicant() or not self.isAuthenticated():
            return self.studenttemp.render(self)
        return self.stafftemp.render(self)
