launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06889
[Merge] lp:~jcsackett/launchpad/sharing-details-branches-mustache into lp:launchpad
j.c.sackett has proposed merging lp:~jcsackett/launchpad/sharing-details-branches-mustache into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~jcsackett/launchpad/sharing-details-branches-mustache/+merge/99428
Summary
=======
This branch adds the first bit of moving parts to the sharing details view. It
adds branch and bug counts, as well as adding non-dynamic (i.e. not
deleteable) branch listings. Subsequent branches will add the bugs, which are
substantially more complicated, and dynamic (deleteable) controls.
Preimp
======
Spoke with Curtis Hovey
Implementation
==============
Bug and branch data is loaded in a method within the initializer for the
pillar details view; branch data is put into the JSON cache.
A sharing details table widget has been created, which can get branch data as
part of config, and uses mustache to render the rows for the branches and drop
it in the table body in the TAL template.
Tests
=====
bin/test -vvct test_pillar_sharing
bin/test --layer=YUI -vvct test_sharing_details
QA
==
Make sure the enhanced_sharing_details feature flag is set; check the page for
launchpad with a user shared with some branches; the data should render in the
table cleanly, and the counts should be set correctly.
Lint
====
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/registry/javascript/sharing/tests/test_sharing_details.js
lib/lp/registry/templates/pillar-sharing-details.pt
lib/lp/registry/javascript/sharing/tests/test_sharing_details.html
lib/lp/registry/browser/pillar.py
lib/lp/registry/browser/tests/test_pillar_sharing.py
lib/lp/registry/services/sharingservice.py
lib/lp/services/features/flags.py
lib/lp/registry/javascript/sharing/sharingdetails.js
lib/lp/registry/interfaces/sharingservice.py
--
https://code.launchpad.net/~jcsackett/launchpad/sharing-details-branches-mustache/+merge/99428
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/sharing-details-branches-mustache into lp:launchpad.
=== modified file 'lib/lp/registry/browser/pillar.py'
--- lib/lp/registry/browser/pillar.py 2012-03-22 23:21:24 +0000
+++ lib/lp/registry/browser/pillar.py 2012-03-26 21:45:24 +0000
@@ -41,6 +41,8 @@
from lp.bugs.browser.structuralsubscription import (
StructuralSubscriptionMenuMixin,
)
+from lp.bugs.interfaces.bug import IBug
+from lp.code.interfaces.branch import IBranch
from lp.registry.interfaces.accesspolicy import (
IAccessPolicyGrantFlatSource,
IAccessPolicySource,
@@ -68,6 +70,7 @@
NavigationMenu,
)
from lp.services.webapp.publisher import (
+ canonical_url,
LaunchpadView,
nearest,
stepthrough,
@@ -360,7 +363,7 @@
label = "Information shared with person or team"
def initialize(self):
- enabled_flag = 'disclosure.enhanced_sharing.enabled'
+ enabled_flag = 'disclosure.enhanced_sharing_details.enabled'
enabled = bool(getFeatureFlag(enabled_flag))
if not enabled:
raise Unauthorized("This feature is not yet available.")
@@ -370,3 +373,35 @@
self.label = "Information shared with %s" % self.person.displayname
self.page_title = "%s" % self.person.displayname
+ self.sharing_service = getUtility(IService, 'sharing')
+
+ self._loadSharedArtifacts()
+
+ cache = IJSONRequestCache(self.request)
+ branch_data = self._build_branch_template_data(self.branches)
+ cache.objects['branches'] = branch_data
+
+ def _loadSharedArtifacts(self):
+ bugs = []
+ branches = []
+ for artifact in self.sharing_service.getSharedArtifacts(
+ self.pillar, self.person):
+ concrete = artifact.concrete_artifact
+ if IBug.providedBy(concrete):
+ bugs.append(artifact)
+ elif IBranch.providedBy(concrete):
+ branches.append(artifact)
+
+ self.bugs = bugs
+ self.branches = branches
+ self.shared_bugs_count = len(bugs)
+ self.shared_branches_count = len(branches)
+
+ def _build_branch_template_data(self, branches):
+ branch_data = []
+ for branch in branches:
+ branch_data.append(dict(
+ branch_link=canonical_url(branch),
+ branch_name=branch.unique_name,
+ branch_id=branch.id))
+ return branch_data
=== modified file 'lib/lp/registry/browser/tests/test_pillar_sharing.py'
--- lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-03-22 23:21:24 +0000
+++ lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-03-26 21:45:24 +0000
@@ -40,6 +40,7 @@
)
+DETAILS_ENABLED_FLAG = {'disclosure.enhanced_sharing_details.enabled': 'true'}
ENABLED_FLAG = {'disclosure.enhanced_sharing.enabled': 'true'}
WRITE_FLAG = {'disclosure.enhanced_sharing.writable': 'true'}
@@ -69,7 +70,7 @@
def test_view_traverses_plus_sharingdetails(self):
# The traversed url in the app is pillar/+sharingdetails/person
- with FeatureFixture(ENABLED_FLAG):
+ with FeatureFixture(DETAILS_ENABLED_FLAG):
# We have to do some fun url hacking to force the traversal a user
# encounters.
pillarperson = self.getPillarPerson()
@@ -82,7 +83,7 @@
def test_not_found_without_sharing(self):
# If there is no sharing between pillar and person, NotFound is the
# result.
- with FeatureFixture(ENABLED_FLAG):
+ with FeatureFixture(DETAILS_ENABLED_FLAG):
# We have to do some fun url hacking to force the traversal a user
# encounters.
pillarperson = self.getPillarPerson(with_sharing=False)
@@ -99,7 +100,7 @@
def test_init_with_feature_flag(self):
# The view works with a feature flag.
- with FeatureFixture(ENABLED_FLAG):
+ with FeatureFixture(DETAILS_ENABLED_FLAG):
pillarperson = self.getPillarPerson()
view = create_initialized_view(pillarperson, '+index')
self.assertEqual(pillarperson.person.displayname, view.page_title)
=== modified file 'lib/lp/registry/interfaces/sharingservice.py'
--- lib/lp/registry/interfaces/sharingservice.py 2012-03-22 09:00:36 +0000
+++ lib/lp/registry/interfaces/sharingservice.py 2012-03-26 21:45:24 +0000
@@ -43,6 +43,9 @@
# version 'devel'
export_as_webservice_entry(publish_web_link=False, as_of='beta')
+ def getSharedArtifacts(pillar, person):
+ """Return the artifacts shared between the pillar and person."""
+
def getInformationTypes(pillar):
"""Return the allowed information types for the given pillar."""
=== added file 'lib/lp/registry/javascript/sharing/sharingdetails.js'
--- lib/lp/registry/javascript/sharing/sharingdetails.js 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/sharing/sharingdetails.js 2012-03-26 21:45:24 +0000
@@ -0,0 +1,90 @@
+/* Copyright 2012 Canonical Ltd. This software is licensed under the
+ * GNU Affero General Public License version 3 (see the file LICENSE).
+ *
+ * Sharee table widget.
+ *
+ * @module lp.registry.sharing.details
+ */
+
+YUI.add('lp.registry.sharing.details', function(Y) {
+
+var namespace = Y.namespace('lp.registry.sharing.details');
+/*
+ * Sharing details table widget.
+ * This widget displays the details of a specific person's shared artifacts.
+ */
+function SharingDetailsTable(config) {
+ SharingDetailsTable.superclass.constructor.apply(this, arguments);
+}
+
+SharingDetailsTable.ATTRS = {
+
+ bug_details_row_template: {
+ value: null
+ },
+
+ branch_details_row_template: {
+ value: null
+ },
+
+ bugs: {
+ value: []
+ },
+
+ branches: {
+ value: []
+ }
+};
+
+Y.extend(SharingDetailsTable, Y.Widget, {
+
+ initializer: function(config) {
+ if (Y.Lang.isValue(config.branches)) {
+ this.set('branches', config.branches);
+ }
+
+ this.set(
+ 'branch_details_row_template',
+ this._branch_details_row_template());
+ },
+
+ renderUI: function() {
+ var template = this.get('branch_details_row_template');
+ var branch_data = this.get('branches');
+ var html = Y.lp.mustache.to_html(template, {branches: branch_data});
+ var table = Y.one('#sharing-table-body');
+ table.set('innerHTML', html);
+ },
+
+ _bug_details_row_template: function() {
+ return [].join(' ');
+ },
+
+ _branch_details_row_template: function() {
+ return [
+ '{{#branches}}',
+ '<tr>',
+ ' <td colspan="3">',
+ ' <a class="sprite branch" href="{{ branch_link }}">',
+ ' {{ branch_name }}',
+ ' </a>',
+ ' </td>',
+ ' <td>--</td>',
+ ' <td class="actions" id="remove-button-{{ branch_id }}">',
+ ' <a class="sprite remove" href="#"',
+ ' title="Unshare this with the user"></a>',
+ ' </td>',
+ '</tr>',
+ '{{/branches}}'
+ ].join(' ');
+ }
+});
+
+SharingDetailsTable.NAME = 'sharingDetailsTable';
+
+namespace.SharingDetailsTable = SharingDetailsTable;
+
+}, "0.1", { "requires": [
+ 'node',
+ 'lp.mustache'
+] });
=== added file 'lib/lp/registry/javascript/sharing/tests/test_sharing_details.html'
--- lib/lp/registry/javascript/sharing/tests/test_sharing_details.html 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/sharing/tests/test_sharing_details.html 2012-03-26 21:45:24 +0000
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<!--
+Copyright 2012 Canonical Ltd. This software is licensed under the
+GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+
+<html>
+ <head>
+ <title>Test sharing details</title>
+
+ <!-- YUI and test setup -->
+ <script type="text/javascript"
+ src="../../../../../../build/js/yui/yui/yui.js">
+ </script>
+ <link rel="stylesheet"
+ href="../../../../../../build/js/yui/console/assets/console-core.css" />
+ <link rel="stylesheet"
+ href="../../../../../../build/js/yui/console/assets/skins/sam/console.css" />
+ <link rel="stylesheet"
+ href="../../../../../../build/js/yui/test/assets/skins/sam/test.css" />
+
+ <script type="text/javascript"
+ src="../../../../../../build/js/lp/app/testing/testrunner.js"></script>
+
+ <link rel="stylesheet" href="../../../../app/javascript/testing/test.css" />
+
+ <!-- Dependencies -->
+ <script type="text/javascript"
+ src="../../../../../../build/js/lp/app/mustache.js"></script>
+
+ <!-- The module under test. -->
+ <script type="text/javascript" src="../sharingdetails.js"></script>
+
+ <!-- The test suite. -->
+ <script type="text/javascript" src="test_sharing_details.js"></script>
+
+ </head>
+ <body class="yui3-skin-sam">
+ <!-- The example markup required by the script to run -->
+ <table>
+ <tbody id="sharing-table-body">
+ </tbody>
+ </table>
+
+ </body>
+</html>
=== added file 'lib/lp/registry/javascript/sharing/tests/test_sharing_details.js'
--- lib/lp/registry/javascript/sharing/tests/test_sharing_details.js 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/javascript/sharing/tests/test_sharing_details.js 2012-03-26 21:45:24 +0000
@@ -0,0 +1,68 @@
+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
+YUI({
+ base: '../../../../../canonical/launchpad/icing/yui/',
+ filter: 'raw',
+ combine: false,
+ fetchCSS: false
+}).use('event', 'lp.mustache', 'node', 'node-event-simulate', 'test',
+ 'widget-stack', 'console', 'lp.registry.sharing.details',
+ function(Y) {
+
+// Local aliases
+var Assert = Y.Assert,
+ ArrayAssert = Y.ArrayAssert;
+var sharing_details = Y.lp.registry.sharing.details;
+var suite = new Y.Test.Suite("sharing.details Tests");
+
+suite.add(new Y.Test.Case({
+
+ name: 'Sharing Details',
+
+ setUp: function() {
+ window.LP = {
+ links: {},
+ cache: {}
+ };
+ },
+
+ tearDown: function() {
+ },
+
+ test_render: function () {
+ var config = {
+ branches: [
+ {
+ branch_link:'/~someone/+junk/somebranch',
+ branch_id: '2',
+ branch_name:'lp:~someone/+junk/somebranch'
+ }
+ ]
+ };
+ table_constructor = Y.lp.registry.sharing.details.SharingDetailsTable;
+ var details_widget = new table_constructor(config);
+ details_widget.render();
+ var table = Y.one('#sharing-table-body');
+ var expected = "lp:~someone/+junk/somebranch--";
+ var actual_text = table.get('text').replace(/\s+/g, '');
+ Assert.areEqual(expected, actual_text);
+ }
+
+}));
+
+
+var handle_complete = function(data) {
+ window.status = '::::' + JSON.stringify(data);
+ };
+Y.Test.Runner.on('complete', handle_complete);
+Y.Test.Runner.add(suite);
+
+var yconsole = new Y.Console({
+ newestOnTop: false
+});
+yconsole.render('#log');
+
+Y.on('domready', function() {
+ Y.Test.Runner.run();
+});
+
+});
=== modified file 'lib/lp/registry/services/sharingservice.py'
--- lib/lp/registry/services/sharingservice.py 2012-03-23 09:09:28 +0000
+++ lib/lp/registry/services/sharingservice.py 2012-03-26 21:45:24 +0000
@@ -52,6 +52,12 @@
return bool(getFeatureFlag(
'disclosure.enhanced_sharing.writable'))
+ def getSharedArtifacts(self, pillar, person):
+ policies = getUtility(IAccessPolicySource).findByPillar([pillar])
+ flat_source = getUtility(IAccessPolicyGrantFlatSource)
+ return [a for a in
+ flat_source.findArtifactsByGrantee(person, policies)]
+
def getInformationTypes(self, pillar):
"""See `ISharingService`."""
allowed_types = [
=== modified file 'lib/lp/registry/templates/pillar-sharing-details.pt'
--- lib/lp/registry/templates/pillar-sharing-details.pt 2012-03-14 00:53:29 +0000
+++ lib/lp/registry/templates/pillar-sharing-details.pt 2012-03-26 21:45:24 +0000
@@ -7,8 +7,56 @@
i18n:domain="launchpad"
>
+<head>
+ <metal:block fill-slot="head_epilogue">
+ <script>
+ LPJS.use('base', 'node', 'event', 'lp.registry.sharing.details',
+ function(Y) {
+ Y.on('domready', function() {
+ var config = {branches: LP.cache.branches};
+ var details_widget = new Y.lp.registry.sharing.details.SharingDetailsTable(config);
+ details_widget.render();
+ });
+ });
+ </script>
+ </metal:block>
+</head>
+
<body>
<div metal:fill-slot="main">
+ <div id="observer-summary">
+ <p>
+ <tal:bugs replace="view/shared_bugs_count">0</tal:bugs> bugs and
+ <tal:branches replace="view/shared_branches_count">0</tal:branches>
+ branches shared with <tal:name replace="view/person/displayname">
+ sharee</tal:name>.<br />
+
+ <tal:is-team condition="view/person/is_team">
+ <tal:members>3</tal:members> team members can view these bugs and
+ branches.
+ </tal:is-team>
+ </p>
+ </div>
+
+ <table id="shared-table" class="listing sortable">
+ <col width="20px"/>
+ <col width="auto"/>
+ <col width="auto"/>
+ <col width="30%"/>
+ <col width="auto"/>
+ <thead>
+ <tr>
+ <th colspan="3" width="">
+ <a href="#" class="sortheader">Bug Report or Branch</a>
+ </th>
+ <th colspan="2" width="">
+ <a href="#" class="sortheader">Via</a>
+ </th>
+ </tr>
+ </thead>
+ <tbody id="sharing-table-body"></tbody>
+ </table>
+
</div>
</body>
</html>
=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py 2012-03-16 06:36:58 +0000
+++ lib/lp/services/features/flags.py 2012-03-26 21:45:24 +0000
@@ -294,6 +294,13 @@
'',
'Sharing overview',
''),
+ ('disclosure.enhanced_sharing_details.enabled',
+ 'boolean',
+ ('If true, enables the details page for viewing the `Some` things that'
+ 'shared with a user or team.'),
+ '',
+ '',
+ ''),
('disclosure.enhanced_sharing.writable',
'boolean',
('If true, will allow the use of the new sharing view and apis used '
Follow ups