コード例 #1
0
class CodeImportSetView(LaunchpadView):
    """The default view for `ICodeImportSet`.

    We present the CodeImportSet as a list of all imports.
    """

    page_title = 'Code Imports'

    def initialize(self):
        """See `LaunchpadView.initialize`."""
        review_status_field = copy_field(
            ICodeImport['review_status'], required=False, default=None)
        self.review_status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
        setUpWidget(self, 'review_status',  review_status_field, IInputWidget)

        rcs_type_field = copy_field(
            ICodeImport['rcs_type'], required=False, default=None)
        self.rcs_type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
        setUpWidget(self, 'rcs_type',  rcs_type_field, IInputWidget)

        # status should be None if either (a) there were no query arguments
        # supplied, i.e. the user browsed directly to this page (this is when
        # hasValidInput returns False) or (b) the user chose 'Any' in the
        # status widget (this is when hasValidInput returns True but
        # getInputValue returns None).
        review_status = None
        if self.review_status_widget.hasValidInput():
            review_status = self.review_status_widget.getInputValue()
        # Similar for 'type'
        rcs_type = None
        if self.rcs_type_widget.hasValidInput():
            rcs_type = self.rcs_type_widget.getInputValue()

        imports = self.context.search(
            review_status=review_status, rcs_type=rcs_type)

        self.batchnav = BatchNavigator(imports, self.request)
コード例 #2
0
class LaunchpadTargetWidget(BrowserWidget, InputWidget):
    """Widget for selecting a product, distribution or package target."""

    implements(IAlwaysSubmittedWidget, IMultiLineWidgetLayout, IInputWidget)

    template = ViewPageTemplateFile('templates/launchpad-target.pt')
    default_option = "package"
    _widgets_set_up = False

    def getDistributionVocabulary(self):
        return 'Distribution'
    
    def getProductVocabulary(self):
        return 'Product'

    def setUpSubWidgets(self):
        if self._widgets_set_up:
            return
        fields = [
            Choice(
                __name__='product', title=u'Project',
                required=True, vocabulary=self.getProductVocabulary()),
            Choice(
                __name__='distribution', title=u"Distribution",
                required=True, vocabulary=self.getDistributionVocabulary(),
                default=getUtility(ILaunchpadCelebrities).ubuntu),
            Choice(
                __name__='package', title=u"Package",
                required=False, vocabulary='BinaryAndSourcePackageName'),
            ]
        self.distribution_widget = CustomWidgetFactory(
            LaunchpadDropdownWidget)
        for field in fields:
            setUpWidget(
                self, field.__name__, field, IInputWidget, prefix=self.name)
        self._widgets_set_up = True

    def setUpOptions(self):
        """Set up options to be rendered."""
        self.options = {}
        for option in ['package', 'product']:
            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'
            self.options[option] = renderElement('input', **attributes)
        self.package_widget.onKeyPress = (
            "selectWidget('%s.option.package', event)" % self.name)
        self.product_widget.onKeyPress = (
            "selectWidget('%s.option.product', event)" % self.name)

    def hasInput(self):
        return self.name in self.request.form

    def hasValidInput(self):
        """See zope.formlib.interfaces.IInputWidget."""
        try:
            self.getInputValue()
            return True
        except (InputErrors, UnexpectedFormData):
            return False

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget."""
        self.setUpSubWidgets()
        form_value = self.request.form_ng.getOne(self.name)
        if form_value == 'product':
            try:
                return self.product_widget.getInputValue()
            except MissingInputError:
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError('Please enter a project name'))
                raise self._error
            except ConversionError:
                entered_name = self.request.form_ng.getOne(
                    "%s.product" % self.name)
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError(
                        "There is no project named '%s' registered in"
                        " Launchpad" % entered_name))
                raise self._error
        elif form_value == 'package':
            try:
                distribution = self.distribution_widget.getInputValue()
            except ConversionError:
                entered_name = self.request.form_ng.getOne(
                    "%s.distribution" % self.name)
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError(
                        "There is no distribution named '%s' registered in"
                        " Launchpad" % entered_name))
                raise self._error
            if self.package_widget.hasInput():
                try:
                    package_name = self.package_widget.getInputValue()
                    if package_name is None:
                        return distribution
                    if IDistributionSourcePackage.providedBy(package_name):
                        dsp = package_name
                    else:
                        source_name = (
                            distribution.guessPublishedSourcePackageName(
                                package_name.name))
                        dsp = distribution.getSourcePackage(source_name)
                except (ConversionError, NotFoundError):
                    entered_name = self.request.form_ng.getOne(
                        '%s.package' % self.name)
                    self._error = WidgetInputError(
                        self.name, self.label,
                        LaunchpadValidationError(
                            "There is no package named '%s' published in %s."
                            % (entered_name, distribution.displayname)))
                    raise self._error
                return dsp
            else:
                return distribution
        else:
            raise UnexpectedFormData("No valid option was selected.")

    def setRenderedValue(self, value):
        """See IWidget."""
        self.setUpSubWidgets()
        if IProduct.providedBy(value):
            self.default_option = 'product'
            self.product_widget.setRenderedValue(value)
        elif IDistribution.providedBy(value):
            self.default_option = 'package'
            self.distribution_widget.setRenderedValue(value)
        elif IDistributionSourcePackage.providedBy(value):
            self.default_option = 'package'
            self.distribution_widget.setRenderedValue(value.distribution)
            self.package_widget.setRenderedValue(value.sourcepackagename)
        else:
            raise AssertionError('Not a valid value: %r' % value)

    def __call__(self):
        """See zope.formlib.interfaces.IBrowserWidget."""
        self.setUpSubWidgets()
        self.setUpOptions()
        return self.template()
コード例 #3
0
ファイル: bugtask.py プロジェクト: pombreda/UnnaturalCodeFork
class BugTaskBugWatchWidget(RadioWidget):
    """A widget for linking a bug watch to a bug task."""
    def __init__(self, field, vocabulary, request):
        RadioWidget.__init__(self, field, vocabulary, request)
        self.url_widget = CustomWidgetFactory(URIWidget)
        setUpWidget(self,
                    'url',
                    BugWatchEditForm['url'],
                    IInputWidget,
                    context=field.context)
        self.setUpJavascript()

    def setUpJavascript(self):
        """Set up JS to select the "new bugwatch" option automatically."""
        select_js = "selectWidget('%s.%s', event)" % (self.name,
                                                      self._new_bugwatch_value)
        self.url_widget.extra = 'onKeyPress="%s"' % select_js

    def setPrefix(self, prefix):
        RadioWidget.setPrefix(self, prefix)
        self.url_widget.setPrefix(prefix)
        self.setUpJavascript()

    _messageNoValue = "None, the status of the bug is updated manually."
    _new_bugwatch_value = 'NEW'

    def _toFieldValue(self, form_value):
        """Convert the textual token to a field value.

        If the form value is _new_bugwatch_value, create a new bug
        watch, otherwise look up an existing one.
        """
        if form_value == self._new_bugwatch_value:
            try:
                url = self.url_widget.getInputValue()
                bugtracker, remote_bug = getUtility(
                    IBugWatchSet).extractBugTrackerAndBug(url)
                bugtask = self.context.context
                return bugtask.bug.addWatch(bugtracker, remote_bug,
                                            getUtility(ILaunchBag).user)
            except WidgetInputError as error:
                # Prefix the error with the widget name, since the error
                # will be display at the top of the page, and not right
                # next to the widget.
                raise WidgetInputError(self.context.__name__, self.label,
                                       'Remote Bug: %s' % error.doc())
            except (NoBugTrackerFound, UnrecognizedBugTrackerURL) as error:
                raise WidgetInputError(self.context.__name__, self.label,
                                       'Invalid bug tracker URL.')
        else:
            return RadioWidget._toFieldValue(self, form_value)

    def _getFormValue(self):
        """Return the form value.

        We have to override this method in this class since the original
        one uses getInputValue(), which it shouldn't do.
        """
        if not self._renderedValueSet():
            return self.request.form_ng.getOne(self.name, self._missing)
        else:
            return self._toFormValue(self._data)

    def _div(self, cssClass, contents, **kw):
        """Don't render a div tag."""
        return contents

    def _joinButtonToMessage(self, option_tag, label, input_id):
        """Join the input tag with the label."""
        row_template = get_widget_template('bugtask-bugwatch-widget.txt')
        return row_template % {
            'input_tag': option_tag,
            'input_id': input_id,
            'input_label': label
        }

    #XXX: Bjorn Tillenius 2006-04-26:
    #     This method is mostly copied from RadioWidget.renderItems() and
    #     modified to actually work. RadioWidget.renderItems() should be
    #     fixed upstream so that we can override it and only do the last
    #     part locally, the part after "# Add an option for creating...".
    #     http://www.zope.org/Collectors/Zope3-dev/592
    def renderItems(self, value):
        """Render the items with with the correct radio button selected."""
        # XXX: Bjorn Tillenius 2006-04-26
        #      This works around the fact that we incorrectly gets the form
        #      value instead of a valid field value.
        if value == self._missing:
            value = self.context.missing_value
        elif (isinstance(value, basestring)
              and value != self._new_bugwatch_value):
            value = self._toFieldValue(value)
        # check if we want to select first item, the previously selected item
        # or the "nothing selected" item.
        nothing_selected = None
        if (value == self.context.missing_value
                and getattr(self, 'firstItem', False)
                and len(self.vocabulary) > 0 and self.context.required):
            # Grab the first item from the iterator:
            values = [iter(self.vocabulary).next().value]
        elif value != self.context.missing_value:
            values = [value]
        else:
            # the "nothing selected" option will be checked
            nothing_selected = 'checked'
            values = []

        items = self.renderItemsWithValues(values)
        if not self.context.required:
            kwargs = {
                'index': None,
                'text': self.translate(self._messageNoValue),
                'value': '',
                'name': self.name,
                'cssClass': self.cssClass
            }
            if nothing_selected:
                option = self.renderSelectedItem(**kwargs)
            else:
                option = self.renderItem(**kwargs)
            items.insert(0, option)

        # Add an option for creating a new bug watch.
        option_text = ('<div>URL: %s</div>' % self.url_widget())
        if value == self._new_bugwatch_value:
            option = self.renderSelectedItem(self._new_bugwatch_value,
                                             option_text,
                                             self._new_bugwatch_value,
                                             self.name, self.cssClass)
        else:
            option = self.renderItem(self._new_bugwatch_value, option_text,
                                     self._new_bugwatch_value, self.name,
                                     self.cssClass)
        items.append(option)

        return items

    def renderItem(self, index, text, value, name, cssClass):
        """Render an item.

        We override this method to use the _joinButtonToMessage method
        instead of the _joinButtonToMessageTemplate which doesn't have
        access to the id.
        """
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             type='radio')
        return self._joinButtonToMessage(elem, text, input_id=id)

    def renderSelectedItem(self, index, text, value, name, cssClass):
        """Render a selected item.

        We override this method to use the _joinButtonToMessage method
        instead of the _joinButtonToMessageTemplate which doesn't have
        access to the id.
        """
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             checked="checked",
                             type='radio')
        return self._joinButtonToMessage(elem, text, input_id=id)

    def renderValue(self, value):
        """Render the widget with the selected value.

        The original renderValue separates the items with either
        '&nbsp;' or '<br />' which isn't suitable for us.
        """
        rendered_items = self.renderItems(value)
        return renderElement('table',
                             cssClass=self.cssClass,
                             contents='\n'.join(rendered_items))
コード例 #4
0
class StoreChannelsWidget(BrowserWidget, InputWidget):

    template = ViewPageTemplateFile("templates/storechannels.pt")
    _separator = channel_components_delimiter
    _default_track = 'latest'
    _widgets_set_up = False

    def __init__(self, field, value_type, request):
        # We don't use value_type.
        super(StoreChannelsWidget, self).__init__(field, request)
        # disable help_text for the global widget
        self.hint = None

    def setUpSubWidgets(self):
        if self._widgets_set_up:
            return
        fields = [
            TextLine(__name__="track",
                     title=u"Track",
                     required=False,
                     description=_(
                         "Track defines a series for your software. "
                         "If not specified, the default track ('latest') is "
                         "assumed.")),
            List(
                __name__="risks",
                title=u"Risk",
                required=False,
                value_type=Choice(vocabulary="SnapStoreChannel"),
                description=_("Risks denote the stability of your software.")),
            TextLine(
                __name__="branch",
                title=u"Branch",
                required=False,
                description=_(
                    "Branches provide users with an easy way to test bug "
                    "fixes.  They are temporary and created on demand.  If "
                    "not specified, no branch is used.")),
        ]

        self.risks_widget = CustomWidgetFactory(LabeledMultiCheckBoxWidget)
        for field in fields:
            setUpWidget(self,
                        field.__name__,
                        field,
                        IInputWidget,
                        prefix=self.name)
        self.risks_widget.orientation = 'horizontal'
        self._widgets_set_up = True

    @property
    def has_risks_vocabulary(self):
        risks_widget = getattr(self, 'risks_widget', None)
        return risks_widget and bool(risks_widget.vocabulary)

    def buildChannelName(self, track, risk, branch):
        """Return channel name composed from given track, risk, and branch."""
        channel = risk
        if track and track != self._default_track:
            channel = self._separator.join((track, channel))
        if branch:
            channel = self._separator.join((channel, branch))
        return channel

    def splitChannelName(self, channel):
        """Return extracted track, risk, and branch from given channel name."""
        try:
            track, risk, branch = split_channel_name(channel)
        except ValueError:
            raise AssertionError("Not a valid value: %r" % channel)
        return track, risk, branch

    def setRenderedValue(self, value):
        """See `IWidget`."""
        self.setUpSubWidgets()
        if value:
            # NOTE: atm target channels must belong to the same track and
            # branch
            tracks = set()
            branches = set()
            risks = []
            for channel in value:
                track, risk, branch = self.splitChannelName(channel)
                tracks.add(track)
                risks.append(risk)
                branches.add(branch)
            if len(tracks) != 1 or len(branches) != 1:
                raise AssertionError("Not a valid value: %r" % value)
            track = tracks.pop()
            self.track_widget.setRenderedValue(track)
            self.risks_widget.setRenderedValue(risks)
            branch = branches.pop()
            self.branch_widget.setRenderedValue(branch)
        else:
            self.track_widget.setRenderedValue(None)
            self.risks_widget.setRenderedValue(None)
            self.branch_widget.setRenderedValue(None)

    def hasInput(self):
        """See `IInputWidget`."""
        return ("%s.risks" % self.name) in self.request.form

    def hasValidInput(self):
        """See `IInputWidget`."""
        try:
            self.getInputValue()
            return True
        except (InputErrors, UnexpectedFormData):
            return False

    def getInputValue(self):
        """See `IInputWidget`."""
        self.setUpSubWidgets()
        track = self.track_widget.getInputValue()
        risks = self.risks_widget.getInputValue()
        branch = self.branch_widget.getInputValue()
        if track and self._separator in track:
            error_msg = "Track name cannot include '%s'." % self._separator
            raise WidgetInputError(self.name, self.label,
                                   LaunchpadValidationError(error_msg))
        if branch and self._separator in branch:
            error_msg = "Branch name cannot include '%s'." % self._separator
            raise WidgetInputError(self.name, self.label,
                                   LaunchpadValidationError(error_msg))
        channels = [
            self.buildChannelName(track, risk, branch) for risk in risks
        ]
        return channels

    def error(self):
        """See `IBrowserWidget`."""
        try:
            if self.hasInput():
                self.getInputValue()
        except InputErrors as error:
            self._error = error
        return super(StoreChannelsWidget, self).error()

    def __call__(self):
        """See `IBrowserWidget`."""
        self.setUpSubWidgets()
        return self.template()
コード例 #5
0
ファイル: product.py プロジェクト: pombreda/UnnaturalCodeFork
class ProductBugTrackerWidget(LaunchpadRadioWidget):
    """Widget for selecting a product bug tracker."""

    _joinButtonToMessageTemplate = u'%s&nbsp;%s'
    template = ViewPageTemplateFile('templates/product-bug-tracker.pt')

    def __init__(self, field, vocabulary, request):
        LaunchpadRadioWidget.__init__(self, field, vocabulary, request)

        # Bug tracker widget.
        self.bugtracker = Choice(vocabulary="WebBugTracker",
                                 __name__='bugtracker')
        self.bugtracker_widget = CustomWidgetFactory(BugTrackerPickerWidget)
        setUpWidget(self,
                    'bugtracker',
                    self.bugtracker,
                    IInputWidget,
                    prefix=self.name,
                    value=field.context.bugtracker,
                    context=field.context)
        self.bugtracker_widget.onKeyPress = ("selectWidget('%s.2', event);" %
                                             self.name)

        # Upstream email address field and widget.
        ## This is to make email address bug trackers appear
        ## separately from the main bug tracker list.
        self.upstream_email_address = StrippedTextLine(
            required=False,
            constraint=email_validator,
            __name__='upstream_email_address')
        self.upstream_email_address_widget = (
            CustomWidgetFactory(StrippedTextWidget))
        setUpWidget(self,
                    'upstream_email_address',
                    self.upstream_email_address,
                    IInputWidget,
                    prefix=self.name,
                    value='',
                    context=self.upstream_email_address.context)
        ## Select the corresponding radio option automatically if
        ## the user starts typing.
        if self.upstream_email_address_widget.extra is None:
            self.upstream_email_address_widget.extra = ''
        self.upstream_email_address_widget.extra += (
            ''' onkeypress="selectWidget('%s.3', event);"\n''' % self.name)

    def _renderItem(self, index, text, value, name, cssClass, checked=False):
        # This form has a custom need to render their labels separately,
        # because of a Firefox problem: see comment in renderItems.
        kw = {}
        if checked:
            kw['checked'] = 'checked'
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             type='radio',
                             **kw)
        return '%s&nbsp;%s' % (elem, text)

    def _toFieldValue(self, form_value):
        if form_value == "malone":
            return self.context.malone_marker
        elif form_value == "external":
            return self.bugtracker_widget.getInputValue()
        elif form_value == "external-email":
            email_address = self.upstream_email_address_widget.getInputValue()
            if email_address is None or len(email_address) == 0:
                self.upstream_email_address_widget._error = (
                    LaunchpadValidationError('Please enter an email address.'))
                raise self.upstream_email_address_widget._error
            bugtracker = getUtility(IBugTrackerSet).ensureBugTracker(
                'mailto:%s' % email_address,
                getUtility(ILaunchBag).user, BugTrackerType.EMAILADDRESS)
            return bugtracker
        elif form_value == "project":
            return None

    def getInputValue(self):
        return self._toFieldValue(self._getFormInput())

    def setRenderedValue(self, value):
        self._data = value
        if value is not self.context.malone_marker:
            self.bugtracker_widget.setRenderedValue(value)

    def _renderLabel(self, text, index):
        """Render a label for the option with the specified index."""
        option_id = '%s.%s' % (self.name, index)
        return u'<label for="%s" style="font-weight: normal">%s</label>' % (
            option_id, text)

    def error(self):
        """Concatenate errors from this widget and sub-widgets."""
        errors = [
            super(ProductBugTrackerWidget, self).error(),
            self.upstream_email_address_widget.error()
        ]
        return '; '.join(err for err in errors if len(err) > 0)

    def renderItems(self, value):
        """Custom-render the radio-buttons and dependent widgets.

        Some of the radio options have dependent widgets: the bug
        tracker drop-down box, and the email address text field. To
        render these in the correct place we must override the default
        rendering of `LaunchpadRadioWidget`.

        We must also make sure that these dependent widgets are
        populated with the correct information, specifically the bug
        tracker selected, or the email address where bugs must be
        reported.
        """
        field = self.context
        product = field.context
        if value == self._missing:
            value = field.missing_value

        # Bugs tracked in Launchpad Bugs.
        malone_item_arguments = dict(index=0,
                                     text=self._renderLabel("In Launchpad", 0),
                                     value="malone",
                                     name=self.name,
                                     cssClass=self.cssClass)

        # Project or somewhere else.
        project = product.project
        if project is None or project.bugtracker is None:
            project_bugtracker_caption = "Somewhere else"
        else:
            project_bugtracker_caption = structured(
                'In the %s bug tracker (<a href="%s">%s</a>)</label>',
                project.displayname, canonical_url(project.bugtracker),
                project.bugtracker.title).escapedtext
        project_bugtracker_arguments = dict(index=1,
                                            text=self._renderLabel(
                                                project_bugtracker_caption, 1),
                                            value="project",
                                            name=self.name,
                                            cssClass=self.cssClass)

        # External bug tracker.
        ## The bugtracker widget can't be within the <label> tag,
        ## since Firefox doesn't cope with it well.
        external_bugtracker_text = "%s %s" % (self._renderLabel(
            "In a registered bug tracker:", 2), self.bugtracker_widget())
        external_bugtracker_arguments = dict(index=2,
                                             text=external_bugtracker_text,
                                             value="external",
                                             name=self.name,
                                             cssClass=self.cssClass)

        # Upstream email address (special-case bug tracker).
        if (IBugTracker.providedBy(value)
                and value.bugtrackertype == BugTrackerType.EMAILADDRESS):
            self.upstream_email_address_widget.setRenderedValue(
                value.baseurl.lstrip('mailto:'))
        external_bugtracker_email_text = "%s %s" % (self._renderLabel(
            "By emailing an upstream bug contact:\n",
            3), self.upstream_email_address_widget())
        external_bugtracker_email_arguments = dict(
            index=3,
            text=external_bugtracker_email_text,
            value="external-email",
            name=self.name,
            cssClass=self.cssClass)

        # All the choices arguments in order.
        all_arguments = {
            'launchpad': malone_item_arguments,
            'external_bugtracker': external_bugtracker_arguments,
            'external_email': external_bugtracker_email_arguments,
            'unknown': project_bugtracker_arguments,
        }

        # Figure out the selected choice.
        if value == field.malone_marker:
            selected = malone_item_arguments
        elif value != self.context.missing_value:
            # value will be 'external-email' if there was an error on
            # upstream_email_address_widget.
            if (value == 'external-email' or
                (IBugTracker.providedBy(value)
                 and value.bugtrackertype == BugTrackerType.EMAILADDRESS)):
                selected = external_bugtracker_email_arguments
            else:
                selected = external_bugtracker_arguments
        else:
            selected = project_bugtracker_arguments

        # Render.
        for name, arguments in all_arguments.items():
            if arguments is selected:
                render = self.renderSelectedItem
            else:
                render = self.renderItem
            yield (name, render(**arguments))

    def renderValue(self, value):
        # Render the items with subordinate fields and support markup.
        self.bug_trackers = dict(self.renderItems(value))
        self.product = self.context.context
        # The view must also use GhostWidget for the 'remote_product' field.
        self.remote_product = copy_field(IProduct['remote_product'])
        self.remote_product_widget = CustomWidgetFactory(TextWidget)
        setUpWidget(self,
                    'remote_product',
                    self.remote_product,
                    IInputWidget,
                    prefix='field',
                    value=self.product.remote_product,
                    context=self.product)
        # The view must also use GhostWidget for the 'enable_bug_expiration'
        # field.
        self.enable_bug_expiration = copy_field(
            IProduct['enable_bug_expiration'])
        self.enable_bug_expiration_widget = CustomWidgetFactory(CheckBoxWidget)
        setUpWidget(self,
                    'enable_bug_expiration',
                    self.enable_bug_expiration,
                    IInputWidget,
                    prefix='field',
                    value=self.product.enable_bug_expiration,
                    context=self.product)
        return self.template()
コード例 #6
0
ファイル: bugtask.py プロジェクト: pombreda/UnnaturalCodeFork
class BugTaskBugWatchWidget(RadioWidget):
    """A widget for linking a bug watch to a bug task."""

    def __init__(self, field, vocabulary, request):
        RadioWidget.__init__(self, field, vocabulary, request)
        self.url_widget = CustomWidgetFactory(URIWidget)
        setUpWidget(
            self, 'url', BugWatchEditForm['url'], IInputWidget,
            context=field.context)
        self.setUpJavascript()

    def setUpJavascript(self):
        """Set up JS to select the "new bugwatch" option automatically."""
        select_js = "selectWidget('%s.%s', event)" % (
            self.name, self._new_bugwatch_value)
        self.url_widget.extra = 'onKeyPress="%s"' % select_js

    def setPrefix(self, prefix):
        RadioWidget.setPrefix(self, prefix)
        self.url_widget.setPrefix(prefix)
        self.setUpJavascript()

    _messageNoValue = "None, the status of the bug is updated manually."
    _new_bugwatch_value = 'NEW'

    def _toFieldValue(self, form_value):
        """Convert the textual token to a field value.

        If the form value is _new_bugwatch_value, create a new bug
        watch, otherwise look up an existing one.
        """
        if form_value == self._new_bugwatch_value:
            try:
                url = self.url_widget.getInputValue()
                bugtracker, remote_bug = getUtility(
                    IBugWatchSet).extractBugTrackerAndBug(url)
                bugtask = self.context.context
                return bugtask.bug.addWatch(
                    bugtracker, remote_bug, getUtility(ILaunchBag).user)
            except WidgetInputError as error:
                # Prefix the error with the widget name, since the error
                # will be display at the top of the page, and not right
                # next to the widget.
                raise WidgetInputError(
                    self.context.__name__, self.label,
                    'Remote Bug: %s' % error.doc())
            except (NoBugTrackerFound, UnrecognizedBugTrackerURL) as error:
                raise WidgetInputError(
                    self.context.__name__, self.label,
                    'Invalid bug tracker URL.')
        else:
            return RadioWidget._toFieldValue(self, form_value)

    def _getFormValue(self):
        """Return the form value.

        We have to override this method in this class since the original
        one uses getInputValue(), which it shouldn't do.
        """
        if not self._renderedValueSet():
            return self.request.form_ng.getOne(self.name, self._missing)
        else:
            return self._toFormValue(self._data)

    def _div(self, cssClass, contents, **kw):
        """Don't render a div tag."""
        return contents

    def _joinButtonToMessage(self, option_tag, label, input_id):
        """Join the input tag with the label."""
        row_template = get_widget_template('bugtask-bugwatch-widget.txt')
        return row_template % {
            'input_tag': option_tag,
            'input_id': input_id,
            'input_label': label}

    #XXX: Bjorn Tillenius 2006-04-26:
    #     This method is mostly copied from RadioWidget.renderItems() and
    #     modified to actually work. RadioWidget.renderItems() should be
    #     fixed upstream so that we can override it and only do the last
    #     part locally, the part after "# Add an option for creating...".
    #     http://www.zope.org/Collectors/Zope3-dev/592
    def renderItems(self, value):
        """Render the items with with the correct radio button selected."""
        # XXX: Bjorn Tillenius 2006-04-26
        #      This works around the fact that we incorrectly gets the form
        #      value instead of a valid field value.
        if value == self._missing:
            value = self.context.missing_value
        elif (isinstance(value, basestring) and
              value != self._new_bugwatch_value):
            value = self._toFieldValue(value)
        # check if we want to select first item, the previously selected item
        # or the "nothing selected" item.
        nothing_selected = None
        if (value == self.context.missing_value
            and getattr(self, 'firstItem', False)
            and len(self.vocabulary) > 0
            and self.context.required):
            # Grab the first item from the iterator:
            values = [iter(self.vocabulary).next().value]
        elif value != self.context.missing_value:
            values = [value]
        else:
            # the "nothing selected" option will be checked
            nothing_selected = 'checked'
            values = []

        items = self.renderItemsWithValues(values)
        if not self.context.required:
            kwargs = {
                'index': None,
                'text': self.translate(self._messageNoValue),
                'value': '',
                'name': self.name,
                'cssClass': self.cssClass}
            if nothing_selected:
                option = self.renderSelectedItem(**kwargs)
            else:
                option = self.renderItem(**kwargs)
            items.insert(0, option)

        # Add an option for creating a new bug watch.
        option_text = (
            '<div>URL: %s</div>' % self.url_widget())
        if value == self._new_bugwatch_value:
            option = self.renderSelectedItem(
                self._new_bugwatch_value, option_text,
                self._new_bugwatch_value, self.name, self.cssClass)
        else:
            option = self.renderItem(
                self._new_bugwatch_value, option_text,
                self._new_bugwatch_value, self.name, self.cssClass)
        items.append(option)

        return items

    def renderItem(self, index, text, value, name, cssClass):
        """Render an item.

        We override this method to use the _joinButtonToMessage method
        instead of the _joinButtonToMessageTemplate which doesn't have
        access to the id.
        """
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             type='radio')
        return self._joinButtonToMessage(elem, text, input_id=id)

    def renderSelectedItem(self, index, text, value, name, cssClass):
        """Render a selected item.

        We override this method to use the _joinButtonToMessage method
        instead of the _joinButtonToMessageTemplate which doesn't have
        access to the id.
        """
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             checked="checked",
                             type='radio')
        return self._joinButtonToMessage(elem, text, input_id=id)

    def renderValue(self, value):
        """Render the widget with the selected value.

        The original renderValue separates the items with either
        '&nbsp;' or '<br />' which isn't suitable for us.
        """
        rendered_items = self.renderItems(value)
        return renderElement(
            'table', cssClass=self.cssClass,
            contents='\n'.join(rendered_items))
コード例 #7
0
ファイル: product.py プロジェクト: pombreda/UnnaturalCodeFork
class ProductBugTrackerWidget(LaunchpadRadioWidget):
    """Widget for selecting a product bug tracker."""

    _joinButtonToMessageTemplate = u'%s&nbsp;%s'
    template = ViewPageTemplateFile('templates/product-bug-tracker.pt')

    def __init__(self, field, vocabulary, request):
        LaunchpadRadioWidget.__init__(self, field, vocabulary, request)

        # Bug tracker widget.
        self.bugtracker = Choice(
            vocabulary="WebBugTracker",
            __name__='bugtracker')
        self.bugtracker_widget = CustomWidgetFactory(BugTrackerPickerWidget)
        setUpWidget(
            self, 'bugtracker', self.bugtracker, IInputWidget,
            prefix=self.name, value=field.context.bugtracker,
            context=field.context)
        self.bugtracker_widget.onKeyPress = (
            "selectWidget('%s.2', event);" % self.name)

        # Upstream email address field and widget.
        ## This is to make email address bug trackers appear
        ## separately from the main bug tracker list.
        self.upstream_email_address = StrippedTextLine(
            required=False, constraint=email_validator,
            __name__='upstream_email_address')
        self.upstream_email_address_widget = (
            CustomWidgetFactory(StrippedTextWidget))
        setUpWidget(
            self, 'upstream_email_address', self.upstream_email_address,
            IInputWidget, prefix=self.name, value='',
            context=self.upstream_email_address.context)
        ## Select the corresponding radio option automatically if
        ## the user starts typing.
        if self.upstream_email_address_widget.extra is None:
            self.upstream_email_address_widget.extra = ''
        self.upstream_email_address_widget.extra += (
            ''' onkeypress="selectWidget('%s.3', event);"\n''' % self.name)

    def _renderItem(self, index, text, value, name, cssClass, checked=False):
        # This form has a custom need to render their labels separately,
        # because of a Firefox problem: see comment in renderItems.
        kw = {}
        if checked:
            kw['checked'] = 'checked'
        id = '%s.%s' % (name, index)
        elem = renderElement(u'input',
                             value=value,
                             name=name,
                             id=id,
                             cssClass=cssClass,
                             type='radio',
                             **kw)
        return '%s&nbsp;%s' % (elem, text)

    def _toFieldValue(self, form_value):
        if form_value == "malone":
            return self.context.malone_marker
        elif form_value == "external":
            return self.bugtracker_widget.getInputValue()
        elif form_value == "external-email":
            email_address = self.upstream_email_address_widget.getInputValue()
            if email_address is None or len(email_address) == 0:
                self.upstream_email_address_widget._error = (
                    LaunchpadValidationError(
                        'Please enter an email address.'))
                raise self.upstream_email_address_widget._error
            bugtracker = getUtility(IBugTrackerSet).ensureBugTracker(
                'mailto:%s' % email_address, getUtility(ILaunchBag).user,
                BugTrackerType.EMAILADDRESS)
            return bugtracker
        elif form_value == "project":
            return None

    def getInputValue(self):
        return self._toFieldValue(self._getFormInput())

    def setRenderedValue(self, value):
        self._data = value
        if value is not self.context.malone_marker:
            self.bugtracker_widget.setRenderedValue(value)

    def _renderLabel(self, text, index):
        """Render a label for the option with the specified index."""
        option_id = '%s.%s' % (self.name, index)
        return u'<label for="%s" style="font-weight: normal">%s</label>' % (
            option_id, text)

    def error(self):
        """Concatenate errors from this widget and sub-widgets."""
        errors = [super(ProductBugTrackerWidget, self).error(),
                  self.upstream_email_address_widget.error()]
        return '; '.join(err for err in errors if len(err) > 0)

    def renderItems(self, value):
        """Custom-render the radio-buttons and dependent widgets.

        Some of the radio options have dependent widgets: the bug
        tracker drop-down box, and the email address text field. To
        render these in the correct place we must override the default
        rendering of `LaunchpadRadioWidget`.

        We must also make sure that these dependent widgets are
        populated with the correct information, specifically the bug
        tracker selected, or the email address where bugs must be
        reported.
        """
        field = self.context
        product = field.context
        if value == self._missing:
            value = field.missing_value

        # Bugs tracked in Launchpad Bugs.
        malone_item_arguments = dict(
            index=0, text=self._renderLabel("In Launchpad", 0),
            value="malone", name=self.name, cssClass=self.cssClass)

        # Project or somewhere else.
        project = product.project
        if project is None or project.bugtracker is None:
            project_bugtracker_caption = "Somewhere else"
        else:
            project_bugtracker_caption = structured(
                'In the %s bug tracker (<a href="%s">%s</a>)</label>',
                project.displayname, canonical_url(project.bugtracker),
                project.bugtracker.title).escapedtext
        project_bugtracker_arguments = dict(
            index=1, text=self._renderLabel(project_bugtracker_caption, 1),
            value="project", name=self.name, cssClass=self.cssClass)

        # External bug tracker.
        ## The bugtracker widget can't be within the <label> tag,
        ## since Firefox doesn't cope with it well.
        external_bugtracker_text = "%s %s" % (
            self._renderLabel("In a registered bug tracker:", 2),
            self.bugtracker_widget())
        external_bugtracker_arguments = dict(
            index=2, text=external_bugtracker_text,
            value="external", name=self.name, cssClass=self.cssClass)

        # Upstream email address (special-case bug tracker).
        if (IBugTracker.providedBy(value) and
            value.bugtrackertype == BugTrackerType.EMAILADDRESS):
            self.upstream_email_address_widget.setRenderedValue(
                value.baseurl.lstrip('mailto:'))
        external_bugtracker_email_text = "%s %s" % (
            self._renderLabel("By emailing an upstream bug contact:\n", 3),
            self.upstream_email_address_widget())
        external_bugtracker_email_arguments = dict(
            index=3, text=external_bugtracker_email_text,
            value="external-email", name=self.name, cssClass=self.cssClass)

        # All the choices arguments in order.
        all_arguments = {
            'launchpad': malone_item_arguments,
            'external_bugtracker': external_bugtracker_arguments,
            'external_email': external_bugtracker_email_arguments,
            'unknown': project_bugtracker_arguments,
            }

        # Figure out the selected choice.
        if value == field.malone_marker:
            selected = malone_item_arguments
        elif value != self.context.missing_value:
            # value will be 'external-email' if there was an error on
            # upstream_email_address_widget.
            if (value == 'external-email' or (
                    IBugTracker.providedBy(value) and
                    value.bugtrackertype == BugTrackerType.EMAILADDRESS)):
                selected = external_bugtracker_email_arguments
            else:
                selected = external_bugtracker_arguments
        else:
            selected = project_bugtracker_arguments

        # Render.
        for name, arguments in all_arguments.items():
            if arguments is selected:
                render = self.renderSelectedItem
            else:
                render = self.renderItem
            yield (name, render(**arguments))

    def renderValue(self, value):
        # Render the items with subordinate fields and support markup.
        self.bug_trackers = dict(self.renderItems(value))
        self.product = self.context.context
        # The view must also use GhostWidget for the 'remote_product' field.
        self.remote_product = copy_field(IProduct['remote_product'])
        self.remote_product_widget = CustomWidgetFactory(TextWidget)
        setUpWidget(
            self, 'remote_product', self.remote_product, IInputWidget,
            prefix='field', value=self.product.remote_product,
            context=self.product)
        # The view must also use GhostWidget for the 'enable_bug_expiration'
        # field.
        self.enable_bug_expiration = copy_field(
            IProduct['enable_bug_expiration'])
        self.enable_bug_expiration_widget = CustomWidgetFactory(
            CheckBoxWidget)
        setUpWidget(
            self, 'enable_bug_expiration', self.enable_bug_expiration,
            IInputWidget, prefix='field',
            value=self.product.enable_bug_expiration, context=self.product)
        return self.template()
コード例 #8
0
class LaunchpadTargetWidget(BrowserWidget, InputWidget):
    """Widget for selecting a product, distribution or package target."""

    implements(IAlwaysSubmittedWidget, IMultiLineWidgetLayout, IInputWidget)

    template = ViewPageTemplateFile('templates/launchpad-target.pt')
    default_option = "package"
    _widgets_set_up = False

    def getDistributionVocabulary(self):
        return 'Distribution'

    def getProductVocabulary(self):
        return 'Product'

    def setUpSubWidgets(self):
        if self._widgets_set_up:
            return
        fields = [
            Choice(__name__='product',
                   title=u'Project',
                   required=True,
                   vocabulary=self.getProductVocabulary()),
            Choice(__name__='distribution',
                   title=u"Distribution",
                   required=True,
                   vocabulary=self.getDistributionVocabulary(),
                   default=getUtility(ILaunchpadCelebrities).ubuntu),
            Choice(__name__='package',
                   title=u"Package",
                   required=False,
                   vocabulary='BinaryAndSourcePackageName'),
        ]
        self.distribution_widget = CustomWidgetFactory(LaunchpadDropdownWidget)
        for field in fields:
            setUpWidget(self,
                        field.__name__,
                        field,
                        IInputWidget,
                        prefix=self.name)
        self._widgets_set_up = True

    def setUpOptions(self):
        """Set up options to be rendered."""
        self.options = {}
        for option in ['package', 'product']:
            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'
            self.options[option] = renderElement('input', **attributes)
        self.package_widget.onKeyPress = (
            "selectWidget('%s.option.package', event)" % self.name)
        self.product_widget.onKeyPress = (
            "selectWidget('%s.option.product', event)" % self.name)

    def hasInput(self):
        return self.name in self.request.form

    def hasValidInput(self):
        """See zope.formlib.interfaces.IInputWidget."""
        try:
            self.getInputValue()
            return True
        except (InputErrors, UnexpectedFormData):
            return False

    def getInputValue(self):
        """See zope.formlib.interfaces.IInputWidget."""
        self.setUpSubWidgets()
        form_value = self.request.form_ng.getOne(self.name)
        if form_value == 'product':
            try:
                return self.product_widget.getInputValue()
            except MissingInputError:
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError('Please enter a project name'))
                raise self._error
            except ConversionError:
                entered_name = self.request.form_ng.getOne("%s.product" %
                                                           self.name)
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError(
                        "There is no project named '%s' registered in"
                        " Launchpad" % entered_name))
                raise self._error
        elif form_value == 'package':
            try:
                distribution = self.distribution_widget.getInputValue()
            except ConversionError:
                entered_name = self.request.form_ng.getOne("%s.distribution" %
                                                           self.name)
                self._error = WidgetInputError(
                    self.name, self.label,
                    LaunchpadValidationError(
                        "There is no distribution named '%s' registered in"
                        " Launchpad" % entered_name))
                raise self._error
            if self.package_widget.hasInput():
                try:
                    package_name = self.package_widget.getInputValue()
                    if package_name is None:
                        return distribution
                    if IDistributionSourcePackage.providedBy(package_name):
                        dsp = package_name
                    else:
                        source_name = (
                            distribution.guessPublishedSourcePackageName(
                                package_name.name))
                        dsp = distribution.getSourcePackage(source_name)
                except (ConversionError, NotFoundError):
                    entered_name = self.request.form_ng.getOne('%s.package' %
                                                               self.name)
                    self._error = WidgetInputError(
                        self.name, self.label,
                        LaunchpadValidationError(
                            "There is no package named '%s' published in %s." %
                            (entered_name, distribution.displayname)))
                    raise self._error
                return dsp
            else:
                return distribution
        else:
            raise UnexpectedFormData("No valid option was selected.")

    def setRenderedValue(self, value):
        """See IWidget."""
        self.setUpSubWidgets()
        if IProduct.providedBy(value):
            self.default_option = 'product'
            self.product_widget.setRenderedValue(value)
        elif IDistribution.providedBy(value):
            self.default_option = 'package'
            self.distribution_widget.setRenderedValue(value)
        elif IDistributionSourcePackage.providedBy(value):
            self.default_option = 'package'
            self.distribution_widget.setRenderedValue(value.distribution)
            self.package_widget.setRenderedValue(value.sourcepackagename)
        else:
            raise AssertionError('Not a valid value: %r' % value)

    def __call__(self):
        """See zope.formlib.interfaces.IBrowserWidget."""
        self.setUpSubWidgets()
        self.setUpOptions()
        return self.template()