# -*- 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 Votes Web Proxy"""

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


import cgi
import copy

import glasnost.common.context as context
import glasnost.common.faults as faults
import glasnost.common.slots as slots
import glasnost.common.tools_new as commonTools

from glasnost.proxy.VotesProxy import *

from ObjectsWeb import register, AdminWithoutWritersMixin, ObjectWebMixin, \
     ObjectsWebMixin
from tools import *
import widgets


class AdminVotes(AdminWithoutWritersMixin, AdminVotes):
    pass
register(AdminVotes)


class MarksWidget(widgets.BaseWidget):
    def getModelHiddenLayout(self, slot, fields):
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields)
        layout = X.array()
        marks = slot.getValue()
        if objectIds:
            for objectId in objectIds:
                fieldName = slot.getFieldOptionName(objectId)
                mark = ''
                if marks and marks.has_key(objectId):
                    mark = str(marks[objectId])
                layout += X.input(name = fieldName, type = 'hidden',
                                  value = mark),
        return layout

    def submit(self, slot, fields):
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields) or []
        isBlank = isButtonSelected(
                    slot.getFieldOptionName('blankButton'), fields)
        result = {}
        if isBlank:
            # why not returning an empty dictionary ?
            for objectId in objectIds:
                result[objectId] = 0
            return result

        for objectId in objectIds:
            fieldName = slot.getFieldOptionName(objectId)
            mark = self.submitOneMark(slot, objectId, fields)
            result[objectId] = mark
        return result

    def submitOneMark(self, slot, objectId, fields):
        mark = slot.getFieldOption(fields, objectId)
        try:
            return int(mark)
        except ValueError:
            return None

class VoteApprovalMarksWidget(MarksWidget):
    def submitOneMark(self, slot, objectId, fields):
        mark = slot.getFieldOption(fields, objectId)
        if not mark:
            mark = '0'
        return int(mark)
        
    def getHtmlFieldValue(self, slot, fields, **keywords):
        marks = slot.getValue()
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields)
        if not self.isInForm():
            if container.isAbstention():
                return X.strong(_('Abstention'))
            elif container.isBlank(objectIds):
                return X.strong(_('Blank Vote'))
        tdAttributes = {}
        if self.colSpan:
            tdAttributes['colspan'] = self.colSpan
        layout = X.array()
        if self.isReadOnly(slot):
            layout += self.getModelHiddenLayout(slot, fields)
        table = X.table(_class = 'vote')
        layout += table
        if objectIds:
            for objectId in objectIds:
                fieldName = slot.getFieldOptionName(objectId)
                mark = 0
                if marks and marks.has_key(objectId) and marks[objectId]:
                    mark = marks[objectId]
                    
                if mark:
                    markSymbol = 'x'
                else:
                    markSymbol = X.nbsp

                tr = X.tr()
                table += tr
                if not self.isInForm() or self.isReadOnly(slot):
                    tr += X.td( '[', markSymbol, ']',)
                else:
                    td = X.td()
                    tr += td
                    error = slot.getObject().getError(slot.getPath())
                    if error and not context.getVar('hideErrors'):
                        td += X.span(_class = 'error-message')(
                                _(error.uiFaultString))
                    inputAttributes = {}
                    if mark:
                        inputAttributes['checked'] = 'checked'
                    td += X.input(name = fieldName, type = 'checkbox',
                                  value = 1, **inputAttributes)
                tr += X.td()(X.objectHypertextLabel(objectId))
        return X.div(_class = 'cell')(layout)
widgets.register(VoteApprovalMarksWidget)


class VoteDistributionMarksWidget(MarksWidget):
    def getHtmlFieldValue(self, slot, fields, **keywords):
        marks = slot.getValue()
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields)
        if not self.isInForm():
            if container.isAbstention():
                return X.strong(_('Abstention'))
            elif container.isBlank(objectIds):
                return X.strong(_('Blank Vote'))
        tdAttributes = {}
        if self.colSpan:
            tdAttributes['colspan'] = self.colSpan
        layout = X.array()
        if self.isReadOnly(slot):
            layout += self.getModelHiddenLayout(slot, fields)
        table = X.table(_class = 'vote')
        layout += table
        if objectIds:
            for objectId in objectIds:
                fieldName = slot.getFieldOptionName(objectId)
                mark = ''
                if marks and marks.has_key(objectId) and marks[objectId]:
                    mark = '%.2f' % (marks[objectId]*100)
                tr = X.tr()
                table += tr
                tr += X.td(X.objectHypertextLabel(objectId), X.asIs(_(':')))
                if not self.isInForm() or self.isReadOnly(slot):
                    tr += X.td()( mark, X.nbsp, '%',)
                else:
                    td = X.td()
                    tr += td
                    error = slot.getObject().getError(slot.getPath())
                    if error and not context.getVar('hideErrors'):
                        td += X.span(_class = 'error-message')(
                                _(error.uiFaultString))
                    td += X.input(maxlength = 6, name = fieldName, size = 6,
                                  type = 'text', value = mark)
                    td += X.nbsp
                    td += '%'
        return X.div(_class = 'cell')(layout)
    
    def submitOneMark(self, slot, objectId, fields):
        mark = slot.getFieldOption(fields, objectId) or ''
        if mark and mark.strip():
            try:
                mark = float(mark) / 100.
            except ValueError:
                raise faults.BadValue()
                mark = 0
        else:
            mark = 0
        
        return mark

widgets.register(VoteDistributionMarksWidget)


class VoteRankingMarksWidget(MarksWidget):
    def getHtmlFieldValue(self, slot, fields, **keywords):
        marks = slot.getValue()
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields)
        if not self.isInForm():
            if container.isAbstention():
                return X.strong(_('Abstention'))
            elif container.isBlank(objectIds):
                return X.strong(_('Blank Vote'))
        layout = X.array()
        if self.isReadOnly(slot):
            layout += self.getModelHiddenLayout(slot, fields)
        table = X.table(_class = 'vote')
        layout += table
        tdAttributes = {}
        if self.colSpan:
            tdAttributes['colspan'] = self.colSpan
        if objectIds:
            for objectId in objectIds:
                fieldName = slot.getFieldOptionName(objectId)
                
                mark = ''
                if marks and marks.has_key(objectId) and marks[objectId]:
                    mark = marks[objectId]
                mark = str(mark)
                
                tr = X.tr()
                table += tr
                if not self.isInForm() or self.isReadOnly(slot):
                    tr += X.td( '[', X.nbsp, mark, X.nbsp, ']',)
                else:
                    td = X.td()
                    tr += td
                    error = slot.getObject().getError(slot.getPath())
                    if error and not context.getVar('hideErrors'):
                        td += X.span(_class = 'error-message')(
                                _(error.uiFaultString))
                    maxLength = len(str(len(objectIds)))
                    td += X.input(
                        maxlength = maxLength, name = fieldName,
                        size = maxLength, type = 'text', value = mark)
                tr += X.td(_class = 'field-label')(
                    X.objectHypertextLabel(objectId))
        return X.div(_class = 'cell')(layout)
widgets.register(VoteRankingMarksWidget)


class VoteRatingMarksWidget(MarksWidget):
    def getHtmlFieldValue(self, slot, fields, **keywords):
        marks = slot.getValue()
        container = slot.getContainer()
        idsGetterName = slot.getKind().idsGetterName
        idsGetter = getattr(container, idsGetterName)
        objectIds = idsGetter(fields)
        if not self.isInForm():
            if container.isAbstention():
                return X.strong(_('Abstention'))
            elif container.isBlank(objectIds):
                return X.strong(_('Blank Vote'))
        tdAttributes = {}
        if self.colSpan:
            tdAttributes['colspan'] = self.colSpan
        layout = X.array()
        if self.isReadOnly(slot):
            layout += self.getModelHiddenLayout(slot, fields)
        table = X.table(_class = 'vote')
        layout += table
        if objectIds:
            for objectId in objectIds:
                fieldName = slot.getFieldOptionName(objectId)
                mark = ''
                if marks and marks.has_key(objectId) and marks[objectId]:
                    mark = marks[objectId]
                tr = X.tr()
                table += tr
                tr += X.td()(X.objectHypertextLabel(objectId), X.asIs(_(':')))
                if not self.isInForm() or self.isReadOnly(slot):
                    tr += X.td()(mark)
                else:
                    td = X.td()
                    tr += td
                    error = slot.getObject().getError(slot.getPath())
                    if error and not context.getVar('hideErrors'):
                        td += X.span(_class = 'error-message')(
                                _(error.uiFaultString))
                    td += X.input(maxlength = 6, name = fieldName, size = 6,
                                  type = 'text', value = mark)
        return X.div(_class = 'cell')(layout)
widgets.register(VoteRatingMarksWidget)


class VoteMixin(ObjectWebMixin):
    ballotKind_kind_widget_fieldLabel = N_('Kind of Ballot')
    ballotKind_kind_widget_labels = {
        'public': N_('Public Ballot'),
        'secret': N_('Secret Ballot'),
        }
    ballotKind_kind_widgetName = 'Select'

    comment_kind_widget_fieldLabel = N_('Comment')
    comment_kind_widget_cols = 75
    comment_kind_widget_colSpan = 2
    comment_kind_widget_rows = 4
    comment_kind_widgetName = 'TextArea'

    #electionId_kind_hasToInitField = 0
    #electionId_kind_hasToMakeFieldFromValue = 0
    electionId_kind_hasToRepairField = 0
    electionId_kind_hasToSubmitField = 0
    electionId_kind_stateInEditMode = 'hidden'
    electionId_kind_stateInViewMode = 'hidden'
    electionId_kindName = 'Id'
    electionId_kind_serverRoles = ['elections']
    electionId_kind_widget_fieldLabel = N_('Election')
    electionId_kind_widgetName = 'SelectId'

    electionToken_kind_stateInEditMode = 'hidden'
    electionToken_kind_stateInViewMode = 'hidden'
    electionToken_kind_widget_fieldLabel = N_('Election Token')
    electionToken_kind_widgetName = 'Token'

    subject = None
    subject_kind_isTranslatable = 0
    subject_kind_stateInEditMode = 'read-only'
    subject_kindName = 'String'

    token_kind_stateInEditMode = 'read-only'
    token_kind_widget_fieldLabel = N_('Vote Token')
    token_kind_widgetName = 'Token'

    #voterId_kind_hasToInitField = 0
    #voterId_kind_hasToMakeFieldFromValue = 0
    voterId_kind_hasToRepairField = 0
    voterId_kind_hasToSubmitField = 0
    voterId_kind_stateInEditMode = 'read-only'
    voterId_kindName = 'Id'
    voterId_kind_serverRoles = ['identities']
    voterId_kind_widget_fieldLabel = N_('Voter')
    voterId_kind_widgetName = 'SelectId'

    voterToken_kind_stateInEditMode = 'read-only'
    voterToken_kind_widget_fieldLabel = N_('Voter Token')
    voterToken_kind_widgetName = 'Token'

    def getElectionCandidateIds(self, fields):
        return getObject(self.electionId).candidateIds

    def getEditLayout(self, fields, parentSlot = None):
        election = getObject(self.electionId)
        slot = election.getSlot('subject')
        translationsProxy = getProxyForServerRole('translations')
        if translationsProxy:
            subject, destinationLanguage, state = \
                     translationsProxy.getTranslationInfos(
                election.subject, election.getId(), slot.getPath(),
                election.getLanguage(), context.getVar('readLanguages'))
        else:
            subject = election.subject
        self.subject = subject
        self.subject_kind = copy.copy(election.subject_kind)
        self.subject_kind.stateInEditMode = 'read-only'
        self.subject_kind.isTranslatable = 0
        self.subject_kind.widget = widgets.TextArea(
            fieldLabel = N_('Subject'),
            cols = 75,
            colSpan = 2,
            rows = 4)

        layout = ObjectWebMixin.getEditLayout(
            self, fields, parentSlot = parentSlot)

        del self.subject
        del self.subject_kind

        return layout

    def getEditLayoutHiddenSlotNames(self, fields, parentSlot = None):
        hiddenSlotNames = ObjectWebMixin.getEditLayoutHiddenSlotNames(
            self, fields, parentSlot = parentSlot)
        hiddenSlotNames = hiddenSlotNames[:]
        slot = self.getSlot('voterId')
        if slot.getValue():
            hiddenSlotNames.append('voterToken')
        else:
            hiddenSlotNames.append('voterId')
        election = getObject(self.electionId)
        if election.ballotKind != 'voterChoice':
            hiddenSlotNames.append('ballotKind')
        return hiddenSlotNames

    def getViewLayout(self, fields, parentSlot = None):
        election = getObject(self.electionId)
        slot = election.getSlot('subject')
        translationsProxy = getProxyForServerRole('translations')
        if translationsProxy:
            subject, destinationLanguage, state = \
                     translationsProxy.getTranslationInfos(
                election.subject, election.getId(), slot.getPath(),
                election.getLanguage(), context.getVar('readLanguages'))
        else:
            subject = election.subject
        self.subject = subject
        self.subject_kind = copy.copy(election.subject_kind)
        self.subject_kind.isTranslatable = 0
        self.subject_kind.widget = widgets.TextArea(
            fieldLabel = N_('Subject'),
            cols = 75,
            colSpan = 2,
            rows = 4)

        layout = ObjectWebMixin.getViewLayout(
            self, fields, parentSlot = parentSlot)

        del self.subject
        del self.subject_kind

        return layout

    def getViewLayoutHiddenSlotNames(self, fields, parentSlot = None):
        hiddenSlotNames = ObjectWebMixin.getViewLayoutHiddenSlotNames(
            self, fields, parentSlot = parentSlot)
        hiddenSlotNames = hiddenSlotNames[:]
        slot = self.getSlot('voterId')
        if slot.getValue():
            hiddenSlotNames.append('voterToken')
        else:
            hiddenSlotNames.append('voterId')
        election = getObject(self.electionId)
        if election.ballotKind != 'voterChoice':
            hiddenSlotNames.append('ballotKind')
        return hiddenSlotNames


class AbstractVote(VoteMixin, AbstractVote):
    pass
register(AbstractVote)


class VoteApproval(VoteMixin, VoteApproval):
    marks_kind_idsGetterName = 'getElectionCandidateIds'
    marks_kindName = 'ApprovalMarks'
    marks_kind_widget_fieldLabel = N_('Choice')
    marks_kind_widgetName = 'VoteApprovalMarksWidget'

    def getLabelLine(self, election):
        if self.isAbstention():
            line = '<i>%s</i>' % _('Abstention')
        elif self.isBlank(election.candidateIds):
            line = '<i>%s</i>' % _('Blank Vote')
        else:
            marks = self.getMarks(election.candidateIds)
            candidates = [
                getObjectLabelTranslated(
                    candidateId,
                    context.getVar('readLanguages'))
                for candidateId in marks.keys()
                if marks[candidateId]]
            return ', '.join([cgi.escape(x) for x in candidates])

    def getMarksRowLayout(self, candidateIds):
        marks = self.getMarks(candidateIds)
        layout = X.array()
        for candidateId in candidateIds:
            mark = marks[candidateId]
            tdAttributes = {}
            if mark:
                red = 0
                green = 255
                cell = 'x'
            else:
                red = 255
                green = 0
                cell = X.nbsp
            tdAttributes['style'] = 'background-color: #%(red)02x%(green)02x00' % {
                'green': green,
                'red': red,
                }
            layout += X.td(**tdAttributes)(cell)
        return layout
register(VoteApproval)


class VoteDistribution(VoteMixin, VoteDistribution):
    marks_kind_idsGetterName = 'getElectionCandidateIds'
    marks_kindName = 'DistributionMarks'
    marks_kind_widget_fieldLabel = N_('Choice')
    marks_kind_widgetName = 'VoteDistributionMarksWidget'

    def getLabelLine(self, election):
        if self.isAbstention():
            line = '<i>%s</i>' % _('Abstention')
        elif self.isBlank(election.candidateIds):
            line = '<i>%s</i>' % _('Blank Vote')
        else:
            line = ''
            marks = self.getMarks(election.candidateIds)
            candidateIds = marks.keys()[:]
            def candidatesSorter(id1, id2, marks = marks):
                return cmp(marks[id2], marks[id1])
            candidateIds.sort(candidatesSorter)
            for candidateId in candidateIds:
                if line:
                    line = line + ', '
                line = line + '%(candidate)s (%(mark)s)' % {
                    'candidate': cgi.escape(getObjectLabelTranslated(
                            candidateId, context.getVar('readLanguages'))),
                    'mark': marks[candidateId],
                    }
        return line

    def getMarksRowLayout(self, candidateIds):
        marks = self.getMarks(candidateIds)
        minMark = min(marks.values())
        maxMark = max(marks.values())
        if minMark < maxMark:
            averageMark = (maxMark + minMark) / 2.0
            coef = 255 / float(averageMark - minMark)
        layout = X.array()
        for candidateId in candidateIds:
            mark = marks[candidateId]
            tdAttributes = {}
            if minMark < maxMark:
                if mark <= averageMark:
                    red = 255
                    green = coef * (mark - minMark)
                else:
                    red = coef * (maxMark - mark)
                    green = 255
                tdAttributes['style'] = 'background-color: #%(red)02x%(green)02x00' % {
                    'green': green,
                    'red': red,
                    }
            layout += X.td(**tdAttributes)(
                '%.2f' % (mark * 100),
                X.nbsp,
                '%',
                )
        return layout
register(VoteDistribution)


class VoteRanking(VoteMixin, VoteRanking):
    marks_kind_idsGetterName = 'getElectionCandidateIds'
    marks_kindName = 'RankingMarks'
    marks_kind_widget_fieldLabel = N_('Choice')
    marks_kind_widgetName = 'VoteRankingMarksWidget'

    def getLabelLine(self, election):
        if self.isAbstention():
            line = '<i>%s</i>' % _('Abstention')
        elif self.isBlank(election.candidateIds):
            line = '<i>%s</i>' % _('Blank Vote')
        else:
            line = ''
            marks = self.getMarks(election.candidateIds)
            candidateIds = marks.keys()[:]
            def candidatesSorter(id1, id2, marks = marks):
                return cmp(marks[id1], marks[id2])
            candidateIds.sort(candidatesSorter)
            for candidateId in candidateIds:
                if line:
                    line = line + ', '
                line = line + '(%(mark)s) %(candidate)s' % {
                    'candidate': cgi.escape(getObjectLabelTranslated(
                            candidateId, context.getVar('readLanguages'))),
                    'mark': marks[candidateId],
                    }
        return line

    def getMarksRowLayout(self, candidateIds):
        marks = self.getMarks(candidateIds)
        scores = self.getScores(candidateIds)
        minScore = 0
        maxScore = 1
        if minScore < maxScore:
            averageScore = (maxScore + minScore) / 2.0
            coef = 255 / float(averageScore - minScore)
        layout = X.array()
        for candidateId in candidateIds:
            mark = marks[candidateId]
            tdAttributes = {}
            if minScore < maxScore:
                score = scores[candidateId]
                if score <= averageScore:
                    red = 255
                    green = coef * (score - minScore)
                else:
                    red = coef * (maxScore - score)
                    green = 255
                tdAttributes['style'] = 'background-color: #%(red)02x%(green)02x00' % {
                    'green': green,
                    'red': red,
                    }
            layout += X.td(**tdAttributes)(mark)
        return layout
register(VoteRanking)


class VoteRating(VoteMixin, VoteRating):
    marks_kind_idsGetterName = 'getElectionCandidateIds'
    marks_kindName = 'RatingMarks'
    marks_kind_widget_fieldLabel = N_('Choice')
    marks_kind_widgetName = 'VoteRatingMarksWidget'

    def getLabelLine(self, election):
        if self.isAbstention():
            line = '<i>%s</i>' % _('Abstention')
        elif self.isBlank(election.candidateIds):
            line = '<i>%s</i>' % _('Blank Vote')
        else:
            line = ''
            marks = self.getMarks(election.candidateIds)
            candidateIds = marks.keys()[:]
            def candidatesSorter(id1, id2, marks = marks):
                return cmp(marks[id2], marks[id1])
            candidateIds.sort(candidatesSorter)
            for candidateId in candidateIds:
                if line:
                    line = line + ', '
                line = line + '%(candidate)s (%(mark)s)' % {
                    'candidate': cgi.escape(getObjectLabelTranslated(
                        candidateId, context.getVar('readLanguages'))),
                    'mark': marks[candidateId],
                    }
        return line

    def getMarksRowLayout(self, candidateIds):
        marks = self.getMarks(candidateIds)
        minMark = 0
        maxMark = 1
        if minMark < maxMark:
            averageMark = (maxMark + minMark) / 2.0
            coef = 255 / float(averageMark - minMark)
        layout = X.array()
        for candidateId in candidateIds:
            mark = marks[candidateId]
            tdAttributes = {}
            if minMark < maxMark:
                if mark <= averageMark:
                    red = 255
                    green = coef * (mark - minMark)
                else:
                    red = coef * (maxMark - mark)
                    green = 255
                tdAttributes['style'] = 'background-color: #%(red)02x%(green)02x00' % {
                    'green': green,
                    'red': red,
                    }
            layout += X.td(**tdAttributes)(
                '%.2f' % (mark * 100))
        return layout
register(VoteRating)


class VotesWeb(ObjectsWebMixin, VotesProxy):
    def canAddObject(self, serverId = None):
        return 0

    def edit(self, id = '', electionId = ''):
        ballotsWeb = getWebForServerRole('ballots')
        electionsWeb = getWebForServerRole('elections')
        identitiesWeb = getWebForServerRole('identities')
        userId = context.getVar('userId', default = '')
        userToken = context.getVar('userToken', default = '')
        if not userToken:
            return accessForbidden()
        if id:
            vote = ballotsWeb.getVote(id)
            if vote == 'secret':
                return accessForbidden()
            rememberObject(id)
            electionId = ballotsWeb.getElectionIdFromToken(
                vote.electionToken, serverId = commonTools.extractServerId(id))
            if not electionId \
               or not electionsWeb.hasObject(electionId):
                return pageNotFound()
            if not electionsWeb.canVote(electionId):
                return accessForbidden()
            election = electionsWeb.getObject(electionId)
            rememberObject(electionId)
        else:
            if not electionId \
               or not electionsWeb.hasObject(electionId):
                return pageNotFound()
            if not electionsWeb.canVote(electionId):
                return accessForbidden()
            election = electionsWeb.getObject(electionId)
            rememberObject(electionId)
            voter = identitiesWeb.getObject(userId)
            if voter.voteTokens is not None \
               and voter.voteTokens.has_key(electionId):
                voteToken = voter.voteTokens[electionId]
                if voteToken == 'secret':
                    return accessForbidden()
                try:
                    vote = ballotsWeb.getVoteFromToken(voteToken)
                except faults.UnknownVoteToken:
                    vote = election.newVote()
                    vote.fillWithDefaultValues()
                else:
                    id = vote.id
                    rememberObject(id)
            else:
                vote = election.newVote()

        return self.editObject(vote, electionId)
    edit.isPublicForWeb = 1

    def editObject(self, vote, electionId = ''):
        electionsWeb = getWebForServerRole('elections')
        election = electionsWeb.getObject(electionId)
        userId = context.getVar('userId', default = '')
        vote.getSlot('electionId').setValue(electionId)
        vote.getSlot('voterId').setValue(userId)
        vote.getSlot('subject').setValue(election.subject)

        if not vote.id:
            headerTitle = self.getTranslatedNewObjectNameCapitalized()
        else:
            headerTitle = _('Editing %s - %s') % (
                self.getTranslatedObjectNameCapitalized(), vote.getLabel())

        context.push(_level = 'edit', layoutMode = 'edit')
        try:
            layout = X.array()
            if context.getVar('error'):
                layout += vote.getErrorLayout()
            form = X.form(action = X.actionUrl('submit'),
                          enctype= 'multipart/form-data', method = 'post')
            layout += form

            slot = slots.Root(vote)
            widget = slot.getWidget()
            form += widget.getModelPageBodyLayout(slot, None)

            buttonsBar = X.div(_class = 'buttons-bar')
            form += buttonsBar
            actionButtonsBar = X.span(_class = 'action-buttons-bar')
            buttonsBar += actionButtonsBar
            actionButtonsBar += X.buttonInForm('abstain', 'abstainButton')
            actionButtonsBar += X.buttonInForm(
                'vote-blank',
                vote.getSlot('marks').getFieldOptionName('blankButton'))
            actionButtonsBar += X.buttonInForm('vote', 'voteButton')

            return writePageLayout(layout, headerTitle)
        finally:
            context.pull(_level = 'edit')

    def submit(self, id = '', electionId = '', **keywords):
        ballotsWeb = getWebForServerRole('ballots')
        electionsWeb = getWebForServerRole('elections')
        if keywords is None:
            keywords = {}
        userId = context.getVar('userId', default = '')
        userToken = context.getVar('userToken', default = '')
        if not userToken:
            return accessForbidden()
        if not electionId \
           or not electionsWeb.hasObject(electionId):
            return pageNotFound()
        if not electionsWeb.canVote(electionId):
            return accessForbidden()
        if isButtonSelected('applyButton', keywords):
            context.setVar('again', 1)
            context.setVar('hideErrors', 1)
        elif isButtonSelected('abstainButton', keywords):
            try:
                ballotsWeb.abstainForVote(electionId)
            except faults.Fault:
                if context.getVar('debug'):
                    raise
                return accessForbidden()
            return redirect(X.actionUrl('view').add(
                    'electionId', electionId).add('voterId', userId))
        election = electionsWeb.getObject(electionId)
        vote = election.newVote()
        vote.getSlot('electionId').setValue(electionId)
        vote.submitFields(keywords)
        if context.getVar('again'):
            return self.editObject(vote, electionId)
        try:
            id = ballotsWeb.vote(electionId, vote)
        except faults.WrongVersion:
            context.setVar('error', 1)
            vote.setError('version', 1)
            return self.editObject(vote, electionId)
        return redirect(X.idUrl(id))
    submit.isPublicForWeb = 1

    def view(self, id = '', electionId = '', voterId = ''):
        ballotsWeb = getWebForServerRole('ballots')
        electionsWeb = getWebForServerRole('elections')
        identitiesWeb = getWebForServerRole('identities')
        userId = context.getVar('userId', default = '')
        if id:
            vote = ballotsWeb.getVote(id)
            if vote == 'secret':
                return accessForbidden()
            rememberObject(id)
            electionId = ballotsWeb.getElectionIdFromToken(
                vote.electionToken, serverId = commonTools.extractServerId(id))
            if ballotsWeb.canGetVoteVoterId(id):
                voterId = ballotsWeb.getVoteVoterId(id)
            else:
                voterId = ''
            if not electionId \
               or not electionsWeb.hasObject(electionId):
                return pageNotFound()
            if not electionsWeb.canGetObject(electionId):
                return accessForbidden()
            election = electionsWeb.getObject(electionId)
            rememberObject(electionId)
        else:
            if not electionId \
               or not electionsWeb.hasObject(electionId):
                return pageNotFound()
            if not voterId \
               or not identitiesWeb.hasObject(voterId):
                return pageNotFound()
            if not electionsWeb.canGetObject(electionId):
                return accessForbidden()
            if not identitiesWeb.canGetObject(voterId):
                return accessForbidden()
            election = electionsWeb.getObject(electionId)
            rememberObject(electionId)
            voter = identitiesWeb.getObject(voterId)
            if voter.voteTokens is not None \
               and voter.voteTokens.has_key(electionId):
                voteToken = voter.voteTokens[electionId]
                if voteToken == 'secret':
                    return accessForbidden()
                try:
                    vote = ballotsWeb.getVoteFromToken(voteToken)
                except faults.UnknownVoteToken:
                    vote = election.newVote()
                else:
                    id = vote.id
                    rememberObject(id)
            else:
                vote = election.newVote()

        vote.getSlot('electionId').setValue(electionId)
        vote.getSlot('voterId').setValue(userId)
        vote.getSlot('subject').setValue(election.subject)

        keywords = {}

        label = election.getLabelTranslated(
            context.getVar('readLanguages'))

        layout = X.array()
        slot = slots.Root(vote)
        widget = slot.getWidget()
        layout += widget.getModelPageBodyLayout(slot, None)
        layout += X.br()
        if voterId and voterId == userId and electionsWeb.canVote(electionId):
            layout += X.buttonStandalone(
                    'edit',X.idUrl(id, 'edit').add('electionId', electionId))
            layout += X.buttonStandalone(
                    _('Go to Election'), X.idUrl(electionId))
        return writePageLayout(layout,
                '%s - %s' % (self.getTranslatedObjectNameCapitalized(), label))
    view.isPublicForWeb = 1

    def viewAll(self):
        context.push(_level = 'viewAll',
                     defaultDispatcherId = context.getVar('dispatcherId'))
        try:
            if not self.canGetObjects():
                return accessForbidden()
            layout = X.array()

            if self.canGetAdmin():
                layout += X.div(_class = 'buttons-bar')(
                        X.buttonStandalone('settings', X.actionUrl('admin')))
        finally:
            context.pull(_level = 'viewAll')
        return writePageLayout(layout, _('Votes'))
    viewAll.isPublicForWeb = 1

