Hi Danilo, This is really nice :) My only comment really is that you probably ought to document the new cancel links you've added. Thanks, Gavin. > === modified file 'lib/lp/blueprints/browser/configure.zcml' > --- lib/lp/blueprints/browser/configure.zcml 2009-09-08 15:03:11 +0000 > +++ lib/lp/blueprints/browser/configure.zcml 2009-09-16 12:38:58 +0000 > @@ -68,7 +68,7 @@ > class="lp.blueprints.browser.sprint.SprintEditView" > permission="launchpad.Edit" > facet="overview" > - template="../templates/sprint-edit.pt"/> > + template="../../app/templates/generic-edit.pt"/> > name="+branding" > for="lp.blueprints.interfaces.sprint.ISprint" > @@ -203,7 +203,7 @@ > class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionEditView" > facet="specifications" > permission="launchpad.Edit" > - template="../templates/specificationsubscription-edit.pt"/> > + template="../../app/templates/generic-edit.pt"/> > for="lp.blueprints.interfaces.sprintattendance.ISprintAttendance" > name="+edit"/> > @@ -306,19 +306,19 @@ > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specificationsubscription.SpecificationSubscriptionAddView" > permission="launchpad.AnyPerson" > - template="../templates/specification-addsubscriber.pt"/> > + template="../../app/templates/generic-edit.pt"/> > name="+retarget" > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specification.SpecificationRetargetingView" > permission="launchpad.Edit" > - template="../templates/specification-retargeting.pt"/> > + template="../../app/templates/generic-edit.pt"/> > name="+supersede" > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specification.SpecificationSupersedingView" > permission="launchpad.Edit" > - template="../templates/specification-superseding.pt"/> > + template="../../app/templates/generic-edit.pt"/> > name="+edit" > for="lp.blueprints.interfaces.specification.ISpecification" > @@ -342,14 +342,14 @@ > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specification.SpecificationGoalProposeView" > permission="launchpad.Edit" > - template="../templates/specification-distroseries.pt"> > + template="../../app/templates/generic-edit.pt"> > > name="+setproductseries" > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specification.SpecificationProductSeriesGoalProposeView" > permission="launchpad.Edit" > - template="../templates/specification-productseries.pt"> > + template="../../app/templates/generic-edit.pt"> > > name="+decide" > @@ -411,20 +411,20 @@ > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specificationdependency.SpecificationDependencyAddView" > permission="launchpad.Edit" > - template="../templates/specification-dependency.pt"/> > + template="../../app/templates/generic-edit.pt"/> > name="+removedependency" > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specificationdependency.SpecificationDependencyRemoveView" > permission="launchpad.Edit" > - template="../templates/specification-removedep.pt"> > + template="../../app/templates/generic-edit.pt"> > > name="+linksprint" > for="lp.blueprints.interfaces.specification.ISpecification" > class="lp.blueprints.browser.specification.SpecificationSprintAddView" > permission="launchpad.AnyPerson" > - template="../templates/specification-linksprint.pt"/> > + template="../../app/templates/generic-edit.pt"/> > for="lp.blueprints.interfaces.specification.ISpecification" > name="deptree.png" > @@ -447,7 +447,7 @@ > for="lp.blueprints.interfaces.specification.ISpecification" > name="+linkbranch" > class="lp.blueprints.browser.specification.SpecificationLinkBranchView" > - template="../templates/specification-linkbranch.pt" > + template="../../app/templates/generic-edit.pt" > permission="launchpad.AnyPerson"/> > for="lp.blueprints.interfaces.specification.ISpecificationSet" > @@ -492,7 +492,7 @@ > name="+new" > for="lp.blueprints.interfaces.specification.ISpecificationSet" > permission="launchpad.AnyPerson" > - template="../templates/specification-new.pt" > + template="../../app/templates/generic-edit.pt" > class="lp.blueprints.browser.specification.NewSpecificationFromRootView"/> > > > === modified file 'lib/lp/blueprints/browser/specification.py' > --- lib/lp/blueprints/browser/specification.py 2009-09-08 15:03:11 +0000 > +++ lib/lp/blueprints/browser/specification.py 2009-09-16 12:38:58 +0000 > @@ -111,6 +111,10 @@ > # Set the default value for the next URL. > self._next_url = canonical_url(spec) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > def transform(self, data): > """Transforms the given form data. > > @@ -568,6 +572,10 @@ > self.context, data['distroseries'], self.user) > self.next_url = canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SpecificationProductSeriesGoalProposeView(SpecificationGoalProposeView): > label = 'Target to a product series' > @@ -580,6 +588,10 @@ > self.context, data['productseries'], self.user) > self.next_url = canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > def propose_goal_with_automatic_approval(specification, series, user): > """Proposes the given specification as a goal for the given series. If > @@ -664,6 +676,10 @@ > def next_url(self): > return self._nextURL > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SupersededByWidget(DropdownWidget): > """Custom select widget for specification superseding. > @@ -733,6 +749,10 @@ > 'Specification is now considered "%s".' % newstate.title) > self.next_url = canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SpecGraph: > """A directed linked graph of nodes representing spec dependencies.""" > @@ -923,6 +943,10 @@ > self.context.linkSprint(data["sprint"], self.user) > self.next_url = canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SpecGraphNode: > """Node in the spec dependency graph. > @@ -1160,14 +1184,14 @@ > self.context.linkBranch(branch=data['branch'], > registrant=self.user) > > - @action(_('Cancel'), name='cancel', validator='validate_cancel') > - def cancel_action(self, action, data): > - """Do nothing and go back to the blueprint page.""" > - > @property > def next_url(self): > return canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SpecificationSetView(AppFrontPageSearchView, HasSpecificationsView): > """View for the Blueprints index page.""" > > === modified file 'lib/lp/blueprints/browser/specificationdependency.py' > --- lib/lp/blueprints/browser/specificationdependency.py 2009-07-27 18:06:21 +0000 > +++ lib/lp/blueprints/browser/specificationdependency.py 2009-09-16 12:38:58 +0000 > @@ -62,6 +62,10 @@ > def next_url(self): > return canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) > + > > class SpecificationDependencyRemoveView(LaunchpadFormView): > schema = ISpecificationDependencyRemoval > @@ -73,3 +77,7 @@ > def continue_action(self, action, data): > self.context.removeDependency(data['dependency']) > self.next_url = canonical_url(self.context) > + > + @property > + def cancel_url(self): > + return canonical_url(self.context) > > === modified file 'lib/lp/blueprints/browser/specificationsubscription.py' > --- lib/lp/blueprints/browser/specificationsubscription.py 2009-06-25 00:00:26 +0000 > +++ lib/lp/blueprints/browser/specificationsubscription.py 2009-09-16 12:38:58 +0000 > @@ -44,3 +44,7 @@ > def change_action(self, action, data): > self.updateContextFromData(data) > self.next_url = canonical_url(self.context.specification) > + > + @property > + def cancel_url(self): > + return canonical_url(self.context.specification) > > === modified file 'lib/lp/blueprints/browser/sprint.py' > --- lib/lp/blueprints/browser/sprint.py 2009-09-03 15:04:13 +0000 > +++ lib/lp/blueprints/browser/sprint.py 2009-09-16 12:38:58 +0000 > @@ -321,6 +321,10 @@ > def next_url(self): > return canonical_url(self.context) > > + @property > + def cancel_url(self): > + return canonical_url(self.context) This pattern is used soooo much, but it's such a triviality. DRY would lead us to defining a new superclass, or a mixin, etc, but the savings are small compared to the clarity of just doing this inline. I guess what I'm saying is: this is fine. > + > > class SprintTopicSetView(HasSpecificationsView, LaunchpadView): > """Custom view class to process the results of this unusual page. > > === modified file 'lib/lp/blueprints/browser/tests/test_breadcrumbs.py' > --- lib/lp/blueprints/browser/tests/test_breadcrumbs.py 2009-08-24 20:28:33 +0000 > +++ lib/lp/blueprints/browser/tests/test_breadcrumbs.py 2009-09-16 12:38:58 +0000 > @@ -41,6 +41,28 @@ > 'Blueprints involving %s' % self.person.displayname) > > > +class TestSpecificationBreadcrumb(BaseBreadcrumbTestCase): > + """Test breadcrumbs for an `ISpecification`.""" > + > + def setUp(self): > + super(TestSpecificationBreadcrumb, self).setUp() > + self.product = self.factory.makeProduct( > + name='crumb-tester', displayname="Crumb Tester") > + self.specification = self.factory.makeSpecification( > + title="Crumby Specification", product=self.product) > + self.specification_url = canonical_url( > + self.specification, rootsite='blueprints') > + > + def test_specification(self): > + crumbs = self._getBreadcrumbs( > + self.specification_url, > + [self.root, self.product, self.specification]) > + last_crumb = crumbs[-1] > + self.assertEquals(last_crumb.url, self.specification_url) > + self.assertEquals( > + last_crumb.text, self.specification.title) > + > + > def test_suite(): > return unittest.TestLoader().loadTestsFromName(__name__) > > > === modified file 'lib/lp/blueprints/configure.zcml' > --- lib/lp/blueprints/configure.zcml 2009-09-03 15:04:13 +0000 > +++ lib/lp/blueprints/configure.zcml 2009-09-16 12:38:58 +0000 > @@ -35,6 +35,11 @@ > for="lp.blueprints.interfaces.sprint.ISprintSet" > factory="lp.blueprints.browser.sprint.SprintSetBreadcrumb" > permission="zope.Public"/> > + + provides="canonical.launchpad.webapp.interfaces.IBreadcrumb" > + for="lp.blueprints.interfaces.specification.ISpecification" > + factory="canonical.launchpad.webapp.breadcrumb.TitleBreadcrumb" > + permission="zope.Public"/> > >