launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #22756
[Merge] lp:~cjwatson/launchpad/custom-widget-no-class-advice-1 into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/custom-widget-no-class-advice-1 into lp:launchpad.
Commit message:
Start removing Zope class advice from custom widget registration.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/custom-widget-no-class-advice-1/+merge/349631
As in https://code.launchpad.net/~cjwatson/launchpad/traversal-no-class-advice/+merge/349625, Zope class advice doesn't work in Python 3. The replacement is less obvious here. I considered and rejected some alternatives:
* Adding methods just in order to be able to decorate them was far too verbose.
* Writing out custom_widgets dictionaries in each view almost worked, but it got cumbersome in the cases where one view inherits from another that also has custom widgets.
In the end I decided that the most concise and readable option was to use separate attributes for each custom widget with formulaic names. With this, just using CustomWidgetFactory directly isn't too bad, although I added a bit of sugar to avoid needing to write that out in views in cases where no arguments need to be passed.
After this, I'll have a few more branches to convert batches of views, and then we can remove the class advisor.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/custom-widget-no-class-advice-1 into lp:launchpad.
=== modified file 'lib/lp/answers/browser/faqtarget.py'
--- lib/lp/answers/browser/faqtarget.py 2012-01-01 02:58:52 +0000
+++ lib/lp/answers/browser/faqtarget.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""`IFAQTarget` browser views."""
@@ -14,7 +14,6 @@
from lp.answers.interfaces.faq import IFAQ
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadFormView,
)
from lp.app.errors import NotFoundError
@@ -45,7 +44,7 @@
label = _('Create a new FAQ')
field_names = ['title', 'keywords', 'content']
- custom_widget('keywords', TokensTextWidget)
+ custom_widget_keywords = TokensTextWidget
@property
def page_title(self):
=== modified file 'lib/lp/answers/browser/question.py'
--- lib/lp/answers/browser/question.py 2016-01-26 15:47:37 +0000
+++ lib/lp/answers/browser/question.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Question views."""
@@ -37,7 +37,10 @@
from zope.component import getUtility
from zope.event import notify
from zope.formlib import form
-from zope.formlib.widget import renderElement
+from zope.formlib.widget import (
+ CustomWidgetFactory,
+ renderElement,
+ )
from zope.formlib.widgets import (
TextAreaWidget,
TextWidget,
@@ -78,7 +81,6 @@
from lp.answers.vocabulary import UsesAnswersDistributionVocabulary
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadEditFormView,
LaunchpadFormView,
safe_action,
@@ -274,7 +276,7 @@
"""View for the Answer Tracker index page."""
schema = IAnswersFrontPageSearchForm
- custom_widget('scope', ProjectScopeWidget)
+ custom_widget_scope = ProjectScopeWidget
page_title = 'Launchpad Answers'
label = 'Questions and Answers'
@@ -560,7 +562,8 @@
# The fields displayed on the search page.
search_field_names = ['language', 'title']
- custom_widget('title', TextWidget, displayWidth=40, displayMaxWidth=250)
+ custom_widget_title = CustomWidgetFactory(
+ TextWidget, displayWidth=40, displayMaxWidth=250)
search_template = ViewPageTemplateFile(
'../templates/question-add-search.pt')
@@ -603,8 +606,9 @@
else:
fields = self.form_fields
for field in fields:
- if field.__name__ in self.custom_widgets:
- field.custom_widget = self.custom_widgets[field.__name__]
+ widget = getattr(self, 'custom_widget_%s' % field.__name__, None)
+ if widget is not None:
+ field.custom_widget = widget
return fields
def setUpWidgets(self):
@@ -755,9 +759,9 @@
"language", "title", "description", "target", "assignee",
"whiteboard"]
- custom_widget('title', TextWidget, displayWidth=40)
- custom_widget('whiteboard', TextAreaWidget, height=5)
- custom_widget('target', QuestionTargetWidget)
+ custom_widget_title = CustomWidgetFactory(TextWidget, displayWidth=40)
+ custom_widget_whiteboard = CustomWidgetFactory(TextAreaWidget, height=5)
+ custom_widget_target = QuestionTargetWidget
@property
def page_title(self):
@@ -1239,8 +1243,8 @@
field_names = ['title', 'keywords', 'content']
- custom_widget('keywords', TokensTextWidget)
- custom_widget("message", TextAreaWidget, height=5)
+ custom_widget_keywords = TokensTextWidget
+ custom_widget_message = CustomWidgetFactory(TextAreaWidget, height=5)
@property
def initial_values(self):
@@ -1262,8 +1266,7 @@
copy_field(IQuestionLinkFAQForm['message']))
self.form_fields['message'].field.title = _(
'Additional comment for question #%s' % self.context.id)
- self.form_fields['message'].custom_widget = (
- self.custom_widgets['message'])
+ self.form_fields['message'].custom_widget = self.custom_widget_message
@action(_('Create and Link'), name='create_and_link')
def create_and_link_action(self, action, data):
@@ -1418,9 +1421,9 @@
schema = IQuestionLinkFAQForm
- custom_widget('faq', SearchableFAQRadioWidget)
+ custom_widget_faq = SearchableFAQRadioWidget
- custom_widget("message", TextAreaWidget, height=5)
+ custom_widget_message = CustomWidgetFactory(TextAreaWidget, height=5)
label = _('Is this a FAQ?')
=== modified file 'lib/lp/answers/browser/questiontarget.py'
--- lib/lp/answers/browser/questiontarget.py 2016-01-26 15:47:37 +0000
+++ lib/lp/answers/browser/questiontarget.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""IQuestionTarget browser views."""
@@ -35,6 +35,7 @@
queryMultiAdapter,
)
from zope.formlib import form
+from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.widgets import DropdownWidget
from zope.schema import (
Bool,
@@ -62,7 +63,6 @@
)
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadFormView,
safe_action,
)
@@ -174,11 +174,12 @@
schema = ISearchQuestionsForm
- custom_widget('language', LabeledMultiCheckBoxWidget,
- orientation='horizontal')
- custom_widget('sort', DropdownWidget, cssClass='inlined-widget')
- custom_widget('status', LabeledMultiCheckBoxWidget,
- orientation='horizontal')
+ custom_widget_language = CustomWidgetFactory(
+ LabeledMultiCheckBoxWidget, orientation='horizontal')
+ custom_widget_sort = CustomWidgetFactory(
+ DropdownWidget, cssClass='inlined-widget')
+ custom_widget_status = CustomWidgetFactory(
+ LabeledMultiCheckBoxWidget, orientation='horizontal')
default_template = ViewPageTemplateFile(
'../templates/question-listing.pt')
@@ -597,7 +598,8 @@
for the QuestionTarget context.
"""
- custom_widget('language', LabeledMultiCheckBoxWidget, visible=False)
+ custom_widget_language = CustomWidgetFactory(
+ LabeledMultiCheckBoxWidget, visible=False)
# No point showing a matching FAQs link on this report.
matching_faqs_count = 0
@@ -672,7 +674,7 @@
return 'Answer contact for %s' % self.context.title
label = page_title
- custom_widget('answer_contact_teams', LabeledMultiCheckBoxWidget)
+ custom_widget_answer_contact_teams = LabeledMultiCheckBoxWidget
def setUpFields(self):
"""See `LaunchpadFormView`."""
=== modified file 'lib/lp/app/browser/doc/launchpadform-view.txt'
--- lib/lp/app/browser/doc/launchpadform-view.txt 2018-03-28 19:31:02 +0000
+++ lib/lp/app/browser/doc/launchpadform-view.txt 2018-07-15 17:24:03 +0000
@@ -1,18 +1,18 @@
-Launchpadform views
+LaunchpadForm views
===================
-The custom_widget accepts arbitrary attribute assignments for the
+CustomWidgetFactory accepts arbitrary attribute assignments for the
widget. One that launchpadform utilizes is 'widget_class'. The
widget rendering is wrapped with a <div> using the widget_class, which
can be used for subordinate field indentation, for example.
+ >>> from zope.formlib.widget import CustomWidgetFactory
>>> from zope.formlib.widgets import TextWidget
>>> from zope.interface import Interface
>>> from zope.schema import TextLine
>>> from lp.services.config import config
>>> from z3c.ptcompat import ViewPageTemplateFile
- >>> from lp.app.browser.launchpadform import (
- ... custom_widget, LaunchpadFormView)
+ >>> from lp.app.browser.launchpadform import LaunchpadFormView
>>> from lp.testing.pages import find_tags_by_class
>>> from lp.services.webapp.servers import LaunchpadTestRequest
@@ -25,8 +25,8 @@
... template = ViewPageTemplateFile(
... config.root + '/lib/lp/app/templates/generic-edit.pt')
... schema = ITestSchema
- ... custom_widget('nickname', TextWidget,
- ... widget_class="field subordinate")
+ ... custom_widget_nickname = CustomWidgetFactory(
+ ... TextWidget, widget_class='field subordinate')
>>> login('foo.bar@xxxxxxxxxxxxx')
>>> person = factory.makePerson()
=== modified file 'lib/lp/app/browser/launchpad.py'
--- lib/lp/app/browser/launchpad.py 2016-06-22 21:04:30 +0000
+++ lib/lp/app/browser/launchpad.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2016 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Browser code for the launchpad application."""
@@ -68,10 +68,7 @@
ExportedFolder,
ExportedImageFolder,
)
-from lp.app.browser.launchpadform import (
- custom_widget,
- LaunchpadFormView,
- )
+from lp.app.browser.launchpadform import LaunchpadFormView
from lp.app.browser.tales import (
DurationFormatterAPI,
MenuAPI,
@@ -1135,7 +1132,7 @@
class AppFrontPageSearchView(LaunchpadFormView):
schema = IAppFrontPageSearchForm
- custom_widget('scope', ProjectScopeWidget)
+ custom_widget_scope = ProjectScopeWidget
@property
def scope_css_class(self):
=== modified file 'lib/lp/app/browser/launchpadform.py'
--- lib/lp/app/browser/launchpadform.py 2015-07-08 16:05:11 +0000
+++ lib/lp/app/browser/launchpadform.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Launchpad Form View Classes
@@ -25,7 +25,10 @@
from zope.formlib import form
# imported so it may be exported
from zope.formlib.form import action
-from zope.formlib.interfaces import IInputWidget
+from zope.formlib.interfaces import (
+ IInputWidget,
+ IWidgetFactory,
+ )
from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.widgets import (
CheckBoxWidget,
@@ -78,7 +81,7 @@
# Subset of fields to use
field_names = None
# Dictionary mapping field names to custom widgets
- custom_widgets = ()
+ custom_widgets = {}
# The next URL to redirect to on successful form submission
next_url = None
@@ -197,12 +200,20 @@
If no context is given, the view's context is used."""
for field in self.form_fields:
- if (field.custom_widget is None and
- field.__name__ in self.custom_widgets):
- # The check for custom_widget is None means that we honor the
- # value if previously set. This is important for some existing
- # forms.
- field.custom_widget = self.custom_widgets[field.__name__]
+ # Honour the custom_widget value if it was already set. This is
+ # important for some existing forms.
+ if field.custom_widget is None:
+ widget = getattr(
+ self, 'custom_widget_%s' % field.__name__, None)
+ if widget is None:
+ widget = self.custom_widgets.get(field.__name__)
+ if widget is not None:
+ field.custom_widget = widget
+ if IWidgetFactory.providedBy(widget):
+ field.custom_widget = widget
+ else:
+ # Allow views to save some typing in common cases.
+ field.custom_widget = CustomWidgetFactory(widget)
if context is None:
context = self.context
self.widgets = form.setUpWidgets(
=== modified file 'lib/lp/app/browser/multistep.py'
--- lib/lp/app/browser/multistep.py 2013-04-10 08:09:05 +0000
+++ lib/lp/app/browser/multistep.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Multiple step views."""
@@ -11,6 +11,7 @@
from zope.formlib import form
+from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.widgets import TextWidget
from zope.interface import Interface
from zope.schema import TextLine
@@ -18,7 +19,6 @@
from lp import _
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadFormView,
)
from lp.services.webapp import (
@@ -148,7 +148,8 @@
override `main_action_label`.
"""
# Use a custom widget in order to make it invisible.
- custom_widget('__visited_steps__', TextWidget, visible=False)
+ custom_widget___visited_steps__ = CustomWidgetFactory(
+ TextWidget, visible=False)
_field_names = []
step_name = ''
=== modified file 'lib/lp/app/browser/tests/test_launchpadform_doc.py'
--- lib/lp/app/browser/tests/test_launchpadform_doc.py 2015-10-26 14:54:43 +0000
+++ lib/lp/app/browser/tests/test_launchpadform_doc.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2010 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
import doctest
@@ -95,14 +95,13 @@
def doctest_custom_widget_with_setUpFields_override():
- """As a regression test, it is important to note that the custom_widget
- class advisor should still work when setUpFields is overridden. For
- instance, consider this custom widget and view:
+ """As a regression test, it is important to note that custom widgets
+ should still work when setUpFields is overridden. For instance,
+ consider this custom widget and view:
>>> from zope.formlib.interfaces import IDisplayWidget, IInputWidget
>>> from zope.interface import directlyProvides, implements
- >>> from lp.app.browser.launchpadform import (
- ... LaunchpadFormView, custom_widget)
+ >>> from lp.app.browser.launchpadform import LaunchpadFormView
>>> from zope.schema import Bool
>>> from zope.publisher.browser import TestRequest
>>> from zope.formlib import form
@@ -121,7 +120,7 @@
... self.value = value
...
>>> class CustomView(LaunchpadFormView):
- ... custom_widget('my_bool', CustomStubWidget)
+ ... custom_widget_my_bool = CustomStubWidget
... def setUpFields(self):
... self.form_fields = form.Fields(Bool(__name__='my_bool'))
...
=== modified file 'lib/lp/app/doc/launchpadform.txt'
--- lib/lp/app/doc/launchpadform.txt 2017-10-21 18:14:14 +0000
+++ lib/lp/app/doc/launchpadform.txt 2018-07-15 17:24:03 +0000
@@ -12,9 +12,8 @@
* if only a subset of the fields are to be displayed in the form, the
"field_names" attribute should be set.
- * if any fields require custom widgets, the "custom_widgets"
- attribute should be set to a dictionary mapping field names to
- widget factories.
+ * if any fields require custom widgets, the "custom_widget_NAME"
+ attribute for each field NAME should be set to a widget factory.
* one or more actions must be provided by the form if it is to
support submission.
@@ -127,15 +126,16 @@
== Custom Widgets ==
In some cases we will want to use a custom widget for a particular
-field. These can be installed easily with the "custom_widgets"
+field. These can be installed easily with a "custom_widget_NAME"
attribute:
+ >>> from zope.formlib.widget import CustomWidgetFactory
>>> from zope.formlib.widgets import TextWidget
- >>> from lp.app.browser.launchpadform import custom_widget
>>> class FormTestView3(LaunchpadFormView):
... schema = IFormTest
- ... custom_widget('displayname', TextWidget, displayWidth=50)
+ ... custom_widget_displayname = CustomWidgetFactory(
+ ... TextWidget, displayWidth=50)
>>> context = FormTest()
>>> request = LaunchpadTestRequest()
@@ -462,7 +462,7 @@
Any widget can be hidden in a LaunchpadFormView while still having its
value POSTed with the values of the other (visible) ones. The widget's
visibility is controlled by its 'visible' attribute, which can be set
-through a custom_widget() call.
+using a custom widget.
First we'll create a fake pagetemplate which doesn't use Launchpad's main
template and thus is way simpler.
@@ -496,7 +496,8 @@
using its hidden() method, which should return a hidden <input> tag.
>>> class TestWidgetVisibility2(TestWidgetVisibility):
- ... custom_widget('displayname', TextWidget, visible=False)
+ ... custom_widget_displayname = CustomWidgetFactory(
+ ... TextWidget, visible=False)
>>> view = TestWidgetVisibility2(context, request)
=== modified file 'lib/lp/app/widgets/doc/image-widget.txt'
--- lib/lp/app/widgets/doc/image-widget.txt 2017-10-21 18:14:14 +0000
+++ lib/lp/app/widgets/doc/image-widget.txt 2018-07-15 17:24:03 +0000
@@ -18,7 +18,7 @@
Whenever you have a form in which you want to use the image widget, you
have to explicitly say whether you want to use its ADD_STYLE or
EDIT_STYLE incarnation, by passing an extra argument to the widget's
-constructor (or to custom_widget(), if you're using it).
+constructor (or to CustomWidgetFactory(), if you're using it).
Our policy is not to ask people to upload images when creating a record,
but instead to expose this as an edit form after the object is created.
=== modified file 'lib/lp/blueprints/browser/specification.py'
--- lib/lp/blueprints/browser/specification.py 2018-01-26 14:38:31 +0000
+++ lib/lp/blueprints/browser/specification.py 2018-07-15 17:24:03 +0000
@@ -62,6 +62,7 @@
from zope.event import notify
from zope.formlib import form
from zope.formlib.form import Fields
+from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.widgets import (
TextAreaWidget,
TextWidget,
@@ -82,7 +83,6 @@
from lp.app.browser.launchpad import AppFrontPageSearchView
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadEditFormView,
LaunchpadFormView,
safe_action,
@@ -206,8 +206,8 @@
page_title = 'Register a blueprint in Launchpad'
label = "Register a new blueprint"
- custom_widget('specurl', TextWidget, displayWidth=60)
- custom_widget('information_type', LaunchpadRadioWidgetWithDescription)
+ custom_widget_specurl = CustomWidgetFactory(TextWidget, displayWidth=60)
+ custom_widget_information_type = LaunchpadRadioWidgetWithDescription
def append_info_type(self, fields):
"""Append an InformationType field for creating a Specification.
@@ -790,9 +790,9 @@
schema = SpecificationEditSchema
field_names = ['name', 'title', 'specurl', 'summary', 'whiteboard']
label = 'Edit specification'
- custom_widget('summary', TextAreaWidget, height=5)
- custom_widget('whiteboard', TextAreaWidget, height=10)
- custom_widget('specurl', TextWidget, displayWidth=60)
+ custom_widget_summary = CustomWidgetFactory(TextAreaWidget, height=5)
+ custom_widget_whiteboard = CustomWidgetFactory(TextAreaWidget, height=10)
+ custom_widget_specurl = CustomWidgetFactory(TextWidget, displayWidth=60)
@property
def adapters(self):
@@ -815,13 +815,14 @@
class SpecificationEditWhiteboardView(SpecificationEditView):
label = 'Edit specification status whiteboard'
field_names = ['whiteboard']
- custom_widget('whiteboard', TextAreaWidget, height=15)
+ custom_widget_whiteboard = CustomWidgetFactory(TextAreaWidget, height=15)
class SpecificationEditWorkItemsView(SpecificationEditView):
label = 'Edit specification work items'
field_names = ['workitems_text']
- custom_widget('workitems_text', TextAreaWidget, height=15)
+ custom_widget_workitems_text = CustomWidgetFactory(
+ TextAreaWidget, height=15)
@action(_('Change'), name='change')
def change_action(self, action, data):
@@ -863,7 +864,7 @@
field_names = ['information_type']
- custom_widget('information_type', LaunchpadRadioWidgetWithDescription)
+ custom_widget_information_type = LaunchpadRadioWidgetWithDescription
@property
def schema(self):
@@ -913,7 +914,7 @@
schema = ISpecification
label = 'Target to a distribution series'
field_names = ['distroseries', 'whiteboard']
- custom_widget('whiteboard', TextAreaWidget, height=5)
+ custom_widget_whiteboard = CustomWidgetFactory(TextAreaWidget, height=5)
@property
def initial_values(self):
=== modified file 'lib/lp/blueprints/browser/sprint.py'
--- lib/lp/blueprints/browser/sprint.py 2017-04-10 11:17:52 +0000
+++ lib/lp/blueprints/browser/sprint.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2017 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Sprint views."""
@@ -30,13 +30,13 @@
from lazr.restful.utils import smartquote
import pytz
from zope.component import getUtility
+from zope.formlib.widget import CustomWidgetFactory
from zope.formlib.widgets import TextAreaWidget
from zope.interface import implementer
from lp import _
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadEditFormView,
LaunchpadFormView,
)
@@ -261,10 +261,12 @@
'time_zone', 'time_starts', 'time_ends', 'is_physical',
'address',
]
- custom_widget('summary', TextAreaWidget, height=5)
- custom_widget('time_starts', DateTimeWidget, display_zone=False)
- custom_widget('time_ends', DateTimeWidget, display_zone=False)
- custom_widget('address', TextAreaWidget, height=3)
+ custom_widget_summary = CustomWidgetFactory(TextAreaWidget, height=5)
+ custom_widget_time_starts = CustomWidgetFactory(
+ DateTimeWidget, display_zone=False)
+ custom_widget_time_ends = CustomWidgetFactory(
+ DateTimeWidget, display_zone=False)
+ custom_widget_address = CustomWidgetFactory(TextAreaWidget, height=3)
sprint = None
@@ -331,10 +333,12 @@
'time_zone', 'time_starts', 'time_ends', 'is_physical',
'address',
]
- custom_widget('summary', TextAreaWidget, height=5)
- custom_widget('time_starts', DateTimeWidget, display_zone=False)
- custom_widget('time_ends', DateTimeWidget, display_zone=False)
- custom_widget('address', TextAreaWidget, height=3)
+ custom_widget_summary = CustomWidgetFactory(TextAreaWidget, height=5)
+ custom_widget_time_starts = CustomWidgetFactory(
+ DateTimeWidget, display_zone=False)
+ custom_widget_time_ends = CustomWidgetFactory(
+ DateTimeWidget, display_zone=False)
+ custom_widget_address = CustomWidgetFactory(TextAreaWidget, height=3)
def setUpWidgets(self):
LaunchpadEditFormView.setUpWidgets(self)
=== modified file 'lib/lp/blueprints/browser/sprintattendance.py'
--- lib/lp/blueprints/browser/sprintattendance.py 2014-11-24 12:22:05 +0000
+++ lib/lp/blueprints/browser/sprintattendance.py 2018-07-15 17:24:03 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Views for SprintAttendance."""
@@ -12,11 +12,11 @@
from datetime import timedelta
import pytz
+from zope.formlib.widget import CustomWidgetFactory
from lp import _
from lp.app.browser.launchpadform import (
action,
- custom_widget,
LaunchpadFormView,
)
from lp.app.widgets.date import DateTimeWidget
@@ -28,10 +28,10 @@
class BaseSprintAttendanceAddView(LaunchpadFormView):
schema = ISprintAttendance
- custom_widget('time_starts', DateTimeWidget)
- custom_widget('time_ends', DateTimeWidget)
- custom_widget(
- 'is_physical', LaunchpadBooleanRadioWidget, orientation='vertical',
+ custom_widget_time_starts = DateTimeWidget
+ custom_widget_time_ends = DateTimeWidget
+ custom_widget_is_physical = CustomWidgetFactory(
+ LaunchpadBooleanRadioWidget, orientation='vertical',
true_label="Physically", false_label="Remotely", hint=None)
@property
Follow ups