~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# Copyright 2009 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""Widgets related to IProjectGroup."""

__metaclass__ = type

from textwrap import dedent

from zope.app.form import InputWidget
from zope.app.form.browser.widget import (
    BrowserWidget,
    renderElement,
    )
from zope.app.form.interfaces import (
    ConversionError,
    IInputWidget,
    InputErrors,
    )
from zope.app.form.utility import setUpWidget
from zope.interface import implements
from zope.schema import Choice

from lp.app.errors import UnexpectedFormData
from lp.app.validators import LaunchpadValidationError
from lp.services.webapp.interfaces import IAlwaysSubmittedWidget


class ProjectScopeWidget(BrowserWidget, InputWidget):
    """Widget for selecting a scope. Either 'All projects' or only one."""

    implements(IAlwaysSubmittedWidget, IInputWidget)

    default_option = "all"
    _error = None

    def __init__(self, field, vocabulary, request):
        super(ProjectScopeWidget, self).__init__(field, request)

        # We copy the title, description and vocabulary from the main
        # field since it determines the valid target types.
        # XXX flacoste 2007-02-21 bug=86861: We must
        # use field.vocabularyName instead of the vocabulary parameter
        # otherwise VocabularyPickerWidget will fail.
        target_field = Choice(
            __name__='target', title=field.title,
            description=field.description, vocabulary=field.vocabularyName,
            required=True)
        setUpWidget(
            self, target_field.__name__, target_field, IInputWidget,
            prefix=self.name)
        self.setUpOptions()

    def setUpOptions(self):
        """Set up options to be rendered."""
        self.options = {}
        for option in ['all', 'project']:
            attributes = dict(
                type='radio', name=self.name, value=option,
                id='%s.option.%s' % (self.name, option))
            if self.request.form_ng.getOne(
                     self.name, self.default_option) == option:
                attributes['checked'] = 'checked'
            if option == 'project':
                attributes['onclick'] = (
                    "document.getElementById('field.scope.target').focus();")
            self.options[option] = renderElement('input', **attributes)
        self.target_widget.onKeyPress = (
            "selectWidget('%s.option.project', event)" % self.name)

    def hasInput(self):
        """See zope.app.form.interfaces.IInputWidget."""
        return self.name in self.request.form

    def hasValidInput(self):
        """See zope.app.form.interfaces.IInputWidget."""
        try:
            self.getInputValue()
            return self.hasInput()
        except (InputErrors, UnexpectedFormData, LaunchpadValidationError):
            return False

    def getInputValue(self):
        """See zope.app.form.interfaces.IInputWidget."""
        scope = self.request.form_ng.getOne(self.name)
        if scope == 'all':
            return None
        elif scope == 'project':
            if not self.request.form_ng.getOne(self.target_widget.name):
                self._error = LaunchpadValidationError(
                    'Please enter a project name')
                raise self._error
            try:
                return self.target_widget.getInputValue()
            except ConversionError:
                entered_name = self.request.form_ng.getOne(
                     "%s.target" % self.name)
                self._error = LaunchpadValidationError(
                    "There is no project named '%s' registered in"
                    " Launchpad" % entered_name)
                raise self._error
        elif self.required:
            raise UnexpectedFormData("No valid option was selected.")
        else:
            return None

    def getScope(self):
        """Return the selected scope or None if it isn't selected."""
        return self.request.form_ng.getOne(self.name)

    def setRenderedValue(self, value):
        """See IWidget."""
        if value is None:
            self.default_option = 'all'
            self.target_widget.setRenderedValue(None)
        else:
            self.default_option = 'project'
            self.target_widget.setRenderedValue(value)
        self.setUpOptions()

    def __call__(self):
        """See zope.app.form.interfaces.IBrowserWidget."""
        return "\n".join([
            self.renderScopeOptions(),
            self.target_widget()])

    def renderScopeOptions(self):
        """Render the HTML for the scope radio widgets."""
        return dedent('''\
        <label>
          %(all)s All projects
        </label>
        <label>
          %(project)s One project:
        </label>
        ''' % self.options)

    def error(self):
        """See zope.app.form.browser.interfaces.IBrowserWidget"""
        if self._error:
            return self._error.doc()
        else:
            return u""