# -*- coding: iso-8859-15 -*-


# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
#     Romain Chantereau <rchantereau@entrouvert.com>
#     Nicolas Clapiès <nclapies@easter-eggs.org>
#     Pierre-Antoine Dejace <padejace@entrouvert.be>
#     Thierry Dulieu <tdulieu@easter-eggs.com>
#     Florent Monnier <monnier@codelutin.com>
#     Cédric Musso <cmusso@easter-eggs.org>
#     Frédéric Péters <fpeters@entrouvert.be>
#     Benjamin Poussin <poussin@codelutin.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#     Sébastien Régnier <regnier@codelutin.com>
#     Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
#     Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
#     Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
#     Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
#     Régnier, Emmanuel Saracco, Théridion & Vecam
#
# 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.


__doc__ = """Glasnost Web Things"""

__version__ = '$Revision$'[11:-2]


import glasnost.common.context as context
import glasnost.common.faults as faults
import glasnost.common.things as commonThings
import glasnost.common.tools_new as commonTools
import glasnost.common.xhtmlgenerator as X

import glasnost.proxy.things as proxyThings

import kinds

from tools import getWebForServerRole, isButtonSelected

class ThingClasses(commonThings.ThingClasses):
    def loadObjectModule(self, serverRoleDotobjectName):
        serverRole, objectName = serverRoleDotobjectName.split('.', 1)
        web = getWebForServerRole(serverRole)
        return web is not None


thingClasses = ThingClasses('web', proxyThings.thingClasses)
context.setVar('thingClasses', thingClasses)


def register(thingClass):
    thingClasses.register(thingClass)


class ThingMixin:
    errors = None

    def fillWithDefaultValues(self, parentSlot = None):
        for slotName in self.getSlotNames(parentSlot = parentSlot):
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            kind = slot.getKind()
            kind.setToDefaultValue(slot)

    def getCompactEditLayout(self, fields, parentSlot = None):
        if parentSlot is None:
            level  = 'getCompactEditLayout'
        else:
            fieldName = parentSlot.getFieldName()
            level  = 'getCompactEditLayout %s' % fieldName
        context.push(_level = level, useCompactLayout = 1)
        try:
            return self.getEditLayout(fields, parentSlot = parentSlot)
        finally:
            context.pull(_level = level)

    def getCompactViewLayout(self, fields, parentSlot = None):
        if parentSlot is None:
            level  = 'getCompactViewLayout'
        else:
            fieldName = parentSlot.getFieldName()
            level  = 'getCompactViewLayout %s' % fieldName
        context.push(_level = level, useCompactLayout = 1)
        try:
            return self.getViewLayout(fields, parentSlot = parentSlot)
        finally:
            context.pull(_level = level)

    def getEditLayout(self, fields = None, parentSlot = None):
        context.push(_level = 'getEditLayout',
                     layoutMode = 'edit')
        try:
            layout = X.array()
            slotNames = self.getEditLayoutSlotNames(
                fields, parentSlot = parentSlot)
            hiddenSlotNames = self.getEditLayoutHiddenSlotNames(
                fields, parentSlot = parentSlot)
            readOnlySlotNames = self.getEditLayoutReadOnlySlotNames(
                fields, parentSlot = parentSlot)
            for slotName in slotNames:
                slot = self.getSlot(slotName, parentSlot = parentSlot)
                kind = slot.getKind()
                widget = kind.getModelWidget(slot)
                if slotName in hiddenSlotNames:
                    if kind.isRequiredInEditMode or kind.isExportable():
                        hidden = widget.getModelHiddenLayout(slot, fields)
                        layout += hidden
                else:
                    context.push(inForm = 0,
                                 readOnly = 0)
                    try:
                        if kind.isRequiredInEditMode or kind.isExportable():
                            context.setVar('inForm', 1)
                            context.setVar('readOnly',
                                           slotName in readOnlySlotNames)
                        layout += widget.getModelFieldLayout(slot, fields)
                    finally:
                        context.pull()
        finally:
            context.pull(_level = 'getEditLayout')
        return layout

    def getEditLayoutHiddenSlotNames(self, fields, parentSlot = None):
        # FIXME: To replace by getHiddenSlotNames.
        slotNames = self.getEditLayoutSlotNames(
                fields, parentSlot = parentSlot)
        hiddenSlotNames = []
        for slotName in slotNames:
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            if slot.getKind().isHidden(slot):
                hiddenSlotNames.append(slotName)
        return hiddenSlotNames

    def getEditLayoutReadOnlySlotNames(self, fields, parentSlot = None):
        # FIXME: To replace by getReadOnlySlotNames.
        slotNames = self.getEditLayoutSlotNames(
                fields, parentSlot = parentSlot)
        readOnlySlotNames = []
        for slotName in slotNames:
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            if slot.getKind().isReadOnly(slot):
                readOnlySlotNames.append(slotName)
        return readOnlySlotNames

    def getEditLayoutSlotNames(self, fields, parentSlot = None):
        slotNames = self.getLayoutSlotNames(fields, parentSlot = parentSlot)
        if hasattr(self, 'id') and not self.id:
            slotNamesToRemove = ['creationTime', 'lastEditorId',
                                 'modificationTime']
            for slotName in slotNamesToRemove:
                if slotName in slotNames:
                    slotNames.remove(slotName)
        return slotNames

    def getErrorLayout(self):
        if context.getVar('hideErrors'):
            return None

        div = X.div(_class = 'error-message')
        div += X.h2(_('Error!'))
        if self.getError('version'):
            div += X.p()(
                _("""\
The informations have been changed while you were editing them.
Please backup your changes and redo the edition.\
"""))
            if self.id and self.id.endswith('/__admin__'):
                url = X.roleUrl(self.serverRole, 'admin')
            else:
                url = X.idUrl(self.id)
            div += X.buttonStandalone(_('View the new version'), url)
        return div

    def getFieldSlotNames(self, fields, parentSlot = None):
        allSlotNames = self.getSlotNames(parentSlot = parentSlot)
        orderedSlotNames = self.getOrderedFieldSlotNames(
            fields, parentSlot = parentSlot)
        slotNames = []
        for slotName in orderedSlotNames:
            if not slotName in allSlotNames:
                continue
            slotNames.append(slotName)
        for slotName in allSlotNames:
            if slotName in slotNames:
                continue
            slotNames.append(slotName)
        return slotNames

    def getFieldValueChildrenEditLayout(self, fields, parentSlot = None):
        return self.getCompactEditLayout(fields, parentSlot = parentSlot)

    def getFieldValueChildrenViewLayout(self, fields, parentSlot = None):
        return self.getCompactViewLayout(fields, parentSlot = parentSlot)

    def getHiddenLayout(self, fields, parentSlot = None):
        layout = X.array()
        slotNames = self.getHiddenLayoutSlotNames(
            fields, parentSlot = parentSlot)
        for slotName in slotNames:
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            kind = slot.getKind()
            if not (kind.isRequiredInEditMode or kind.isExportable()):
                continue
            widget = kind.getModelWidget(slot)
            layout += widget.getModelHiddenLayout(slot, fields)
        return layout

    def getHiddenLayoutSlotNames(self, fields, parentSlot = None):
        return self.getSlotNames(parentSlot = parentSlot)

    def getLayoutSlotNames(self, fields, parentSlot = None):
        allSlotNames = self.getSlotNames(parentSlot = parentSlot)
        orderedSlotNames = self.getOrderedLayoutSlotNames(
            parentSlot = parentSlot)
        slotNames = []
        for slotName in orderedSlotNames:
            if not slotName in allSlotNames:
                continue
            slotNames.append(slotName)
        for slotName in allSlotNames:
            if slotName in slotNames:
                continue
            slotNames.append(slotName)
        return slotNames

    def getOrderedFieldSlotNames(self, fields, parentSlot = None):
        return self.getOrderedLayoutSlotNames(parentSlot)

    def getViewLayout(self, fields, parentSlot = None):
        context.push(_level = 'getViewLayout',
                     layoutMode = 'view')
        try:
            layout = X.array()
            slotNames = self.getViewLayoutSlotNames(
                    fields, parentSlot = parentSlot)
            hiddenSlotNames = self.getViewLayoutHiddenSlotNames(
                fields, parentSlot = parentSlot)
            context.push(inForm = 0,
                         layoutMode = 'view')
            try:
                for slotName in slotNames:
                    slot = self.getSlot(slotName, parentSlot = parentSlot)
                    kind = slot.getKind()
                    if slotName in hiddenSlotNames:
                        continue
                    widget = kind.getModelWidget(slot)
                    layout += widget.getModelFieldLayout(slot, fields)
            finally:
                context.pull()
        finally:
            context.pull(_level = 'getViewLayout')
        return layout

    def getViewLayoutHiddenSlotNames(self, fields, parentSlot = None):
        # FIXME: To replace by getHiddenSlotNames.
        slotNames = self.getViewLayoutSlotNames(
                fields, parentSlot = parentSlot)
        hiddenSlotNames = []
        for slotName in slotNames:
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            if slot.getKind().isHidden(slot):
                hiddenSlotNames.append(slotName)
        return hiddenSlotNames

    def getViewLayoutSlotNames(self, fields, parentSlot = None):
        return self.getLayoutSlotNames(fields, parentSlot = parentSlot)

    def newWidget(self, parentSlot = None):
        return commonTools.newThing('widget', 'Thing')

    def getError(self, slotName):
        if not self.errors or not self.errors.has_key(slotName):
            return None
        return self.errors[slotName]

    def hasErrors(self):
        # not not so we get a boolean
        return not not self.errors

    def setError(self, slotPath, error):
        if not self.errors:
            self.errors = {}
        self.errors[slotPath] = error

    def delErrors(self):
        self.errors = {}

    def submitFields(self, fields, parentSlot = None):
        #self.delErrors()

        if isButtonSelected('applyButton', fields):
            context.setVar('again', 1)
            context.setVar('hideErrors', 1)

        # Change the class of the thing if needed.
        for slotName in ['thingCategory', 'thingName']:
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            widget = slot.getWidget()
            kind = slot.getKind()
            if (kind.isRequiredInEditMode or kind.isExportable()) \
                   and kind.hasToSubmitField:
                value = widget.submit(slot, fields)
                slot.setValue(value)
        thingCategory = self.thingCategory
        if self.__dict__.has_key('thingCategory'):
            del self.thingCategory
        if thingCategory is None:
            thingCategory = self.thingCategory
        thingName = self.getThingName()
        if self.__dict__.has_key('thingName'):
            del self.thingName
        if thingCategory != self.thingCategory \
               or thingName != self.getThingName():
            try:
                self.__class__ = commonTools.getThingClass(
                    thingCategory, thingName)
            except:
                pass
                # FIXME: I don't know what I'm doing
                # (was necessary to get properties (in cards) working

        for slotName in self.getFieldSlotNames(
                fields, parentSlot = parentSlot):
            if slotName in ['thingCategory', 'thingName']:
                continue
            slot = self.getSlot(slotName, parentSlot = parentSlot)
            widget = slot.getWidget()
            kind = slot.getKind()
            if (kind.isRequiredInEditMode or kind.isExportable()) \
                   and kind.hasToSubmitField:
                try:
                    value = widget.submit(slot, fields)
                    if value is not None and \
                            type(value) != kind.pythonStorageType:
                        value = kind.convertValueFromOtherType(value)
                    if kind.isEmptyModelValue(slot, value):
                        value = None
                    kind.checkModelValue(slot, value)
                except faults.BaseFault, f:
                    self.setError(slot.getPath(), f)
                    context.setVar('again', 1)
                    context.setVar('error', 1)
                    #continue

                # so that half-defined values (like a kind only defined by its
                # name) are not used if the old value was of the same type
                if isinstance(kind, kinds.ThingMixin):
                    if slot.hasValue():
                        oldValue = slot.getValue()
                        if oldValue and oldValue.getThingName() == \
                                            value.getThingName():
                            value = oldValue

                if not slot.hasValue() or value != slot.getValue():
                    slot.setValue(value)


class BaseThing(ThingMixin, proxyThings.BaseThing):
    pass

