← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~sinzui/launchpad/project-review-0 into lp:launchpad

 

Curtis Hovey has proposed merging lp:~sinzui/launchpad/project-review-0 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~sinzui/launchpad/project-review-0/+merge/60696

Project review listing can show enough info to be a form.

    Launchpad bug: https://bugs.launchpad.net/bugs/721454
    Pre-implementation: jcsackett, deryck, wgrant

Provide enough information on the project review listing to do the review,
and use lazrjs widgets in the listing.

--------------------------------------------------------------------

RULES

    * Update the UI to show the information a review needs to make a
      decision.
    * Use the Boolean lazrjs widgets as used by blueprints to allow inline
      editing of project statues.


QA

    * Visit https://qastaging.launchpad.net/projects/+review-licenses
    * Drive the queue to zero without leaving the page:
      * Review "I Don't Know" and commercial projects
      * Deactivate assetless projects maintained by ~registry
      * Deactivate test and spam projects
      * Approve the remaining projects projects


LINT

    lib/lp/registry/browser/product.py
    lib/lp/registry/browser/tests/test_product.py
    lib/lp/registry/stories/product/xx-productset.txt
    lib/lp/registry/templates/product-listing-for-review.pt
    lib/lp/registry/templates/product-review-license.pt
    lib/lp/registry/templates/products-review-licenses.pt


TEST

    ./bin/test -vv -t TestProductView \
        -t ProductSetReviewLicensesViewTestCase -t xx-productset.txt


IMPLEMENTATION

Added properties to tell the template what to show.
Added lazrjs widgets for the three project review field.
Reduced the batch size to 50 because 100 often time's out in production
Removed a mediocre story that did not really demonstrate the default
behaviour of the project review form.
    lib/lp/registry/browser/product.py
    lib/lp/registry/browser/tests/test_product.py
    lib/lp/registry/stories/product/xx-productset.txt

Updated the templates to include enough information to review a new or
old project using only the list.
    lib/lp/registry/templates/product-listing-for-review.pt
    lib/lp/registry/templates/products-review-licenses.pt

Removed the instruction that imply project review is about judging the
value of a project.
    lib/lp/registry/templates/product-review-license.pt
-- 
https://code.launchpad.net/~sinzui/launchpad/project-review-0/+merge/60696
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~sinzui/launchpad/project-review-0 into lp:launchpad.
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2011-03-29 22:34:04 +0000
+++ lib/lp/registry/browser/product.py	2011-05-11 21:02:33 +0000
@@ -129,7 +129,10 @@
     ReturnToReferrerMixin,
     safe_action,
     )
-from lp.app.browser.lazrjs import TextLineEditorWidget
+from lp.app.browser.lazrjs import (
+    BooleanChoiceWidget,
+    TextLineEditorWidget,
+    )
 from lp.app.browser.tales import MenuAPI
 from lp.app.enums import ServiceUsage
 from lp.app.errors import NotFoundError
@@ -1133,6 +1136,49 @@
             License.OTHER_OPEN_SOURCE in self.context.licenses
             or License.OTHER_PROPRIETARY in self.context.licenses)
 
+    @cachedproperty
+    def is_proprietary(self):
+        """Is the project proprietary."""
+        return License.OTHER_PROPRIETARY in self.context.licenses
+
+    @property
+    def active_widget(self):
+        return BooleanChoiceWidget(
+            self.context, IProduct['active'],
+            content_box_id='%s-edit-active' % self.context.name,
+            edit_view='+review-license',
+            tag='span',
+            false_text='Deactivted',
+            true_text='Active',
+            header='Is this project active and usable by the community?')
+
+    @property
+    def license_reviewed_widget(self):
+        return BooleanChoiceWidget(
+            self.context, IProduct['license_reviewed'],
+            content_box_id='%s-edit-license-reviewed' % self.context.name,
+            edit_view='+review-license',
+            tag='span',
+            false_text='Unreviewed',
+            true_text='Reviewed',
+            header='Have you reviewed the project?')
+
+    @property
+    def license_approved_widget(self):
+        licenses = list(self.context.licenses)
+        if License.OTHER_PROPRIETARY in licenses:
+            return 'Commercial subscription required'
+        elif [License.DONT_KNOW] == licenses or [] == licenses:
+            return 'License required'
+        return BooleanChoiceWidget(
+            self.context, IProduct['license_approved'],
+            content_box_id='%s-edit-license-approved' % self.context.name,
+            edit_view='+review-license',
+            tag='span',
+            false_text='Unapproved',
+            true_text='Approved',
+            header='Does the license qualifiy the project for free hosting?')
+
 
 class ProductPackagesView(LaunchpadView):
     """View for displaying product packaging"""
@@ -1804,6 +1850,7 @@
 
     schema = IProductReviewSearch
     label = 'Review projects'
+    page_title = label
 
     full_row_field_names = [
         'search_text',
@@ -1858,21 +1905,25 @@
         """Return all widgets that span all columns."""
         return (self.widgets[name] for name in self.full_row_field_names)
 
+    @property
+    def initial_values(self):
+        """See `ILaunchpadFormView`."""
+        search_params = {}
+        for name in self.schema:
+            search_params[name] = self.schema[name].default
+        return search_params
+
     def forReviewBatched(self):
         """Return a `BatchNavigator` to review the matching projects."""
         # Calling _validate populates the data dictionary as a side-effect
         # of validation.
         data = {}
         self._validate(None, data)
-        # Get default values from the schema since the form defaults
-        # aren't available until the search button is pressed.
-        search_params = {}
-        for name in self.schema:
-            search_params[name] = self.schema[name].default
+        search_params = self.initial_values
         # Override the defaults with the form values if available.
         search_params.update(data)
         return BatchNavigator(self.context.forReview(**search_params),
-                              self.request, size=100)
+                              self.request, size=50)
 
 
 class ProductAddViewBase(ProductLicenseMixin, LaunchpadFormView):

=== modified file 'lib/lp/registry/browser/tests/test_product.py'
--- lib/lp/registry/browser/tests/test_product.py	2011-03-23 15:42:28 +0000
+++ lib/lp/registry/browser/tests/test_product.py	2011-05-11 21:02:33 +0000
@@ -16,6 +16,7 @@
 from canonical.launchpad.testing.pages import (
     find_tag_by_id,
     )
+from canonical.launchpad.webapp.publisher import canonical_url
 from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.app.enums import ServiceUsage
 from lp.registry.browser.product import (
@@ -26,6 +27,7 @@
     IProductSet,
     )
 from lp.testing import (
+    login_celebrity,
     login_person,
     person_logged_in,
     TestCaseWithFactory,
@@ -183,7 +185,7 @@
 
     def setUp(self):
         super(TestProductView, self).setUp()
-        self.product = self.factory.makeProduct()
+        self.product = self.factory.makeProduct(name='fnord')
 
     def test_show_programming_languages_without_languages(self):
         # show_programming_languages is false when there are no programming
@@ -222,3 +224,158 @@
         with person_logged_in(self.product.owner):
             self.product.licenses = [License.OTHER_PROPRIETARY]
         self.assertTrue(view.show_license_info)
+
+    def test_is_proprietary_with_proprietary_license(self):
+        # is_proprietary is true when the project has a proprietary license.
+        with person_logged_in(self.product.owner):
+            self.product.licenses = [License.OTHER_PROPRIETARY]
+        view = create_initialized_view(self.product, '+index')
+        self.assertTrue(view.is_proprietary)
+
+    def test_is_proprietary_without_proprietary_license(self):
+        # is_proprietary is false when the project has a proprietary license.
+        with person_logged_in(self.product.owner):
+            self.product.licenses = [License.GNU_GPL_V2]
+        view = create_initialized_view(self.product, '+index')
+        self.assertFalse(view.is_proprietary)
+
+    def test_active_widget(self):
+        # The active widget is is unique to the product.
+        view = create_initialized_view(self.product, '+index')
+        widget = view.active_widget
+        self.assertEqual('fnord-edit-active', widget.content_box_id)
+        self.assertEqual(
+            canonical_url(self.product, view_name='+review-license'),
+            widget.edit_url)
+
+    def test_license_reviewed_widget(self):
+        # The license reviewed widget is is unique to the product.
+        login_celebrity('registry_experts')
+        view = create_initialized_view(self.product, '+index')
+        widget = view.license_reviewed_widget
+        self.assertEqual('fnord-edit-license-reviewed', widget.content_box_id)
+        self.assertEqual(
+            canonical_url(self.product, view_name='+review-license'),
+            widget.edit_url)
+
+    def test_license_approved_widget_any_license(self):
+        # The license approved widget is is unique to the product.
+        login_celebrity('registry_experts')
+        view = create_initialized_view(self.product, '+index')
+        widget = view.license_approved_widget
+        self.assertEqual('fnord-edit-license-approved', widget.content_box_id)
+        self.assertEqual(
+            canonical_url(self.product, view_name='+review-license'),
+            widget.edit_url)
+
+    def test_license_approved_widget_prorietary_license(self):
+        # Proprietary projects cannot be approved.
+        with person_logged_in(self.product.owner):
+            self.product.licenses = [License.OTHER_PROPRIETARY]
+        login_celebrity('registry_experts')
+        view = create_initialized_view(self.product, '+index')
+        text = view.license_approved_widget
+        self.assertEqual('Commercial subscription required', text)
+
+    def test_license_approved_widget_no_license(self):
+        # Projects without a license cannot be approved.
+        with person_logged_in(self.product.owner):
+            self.product.licenses = [License.DONT_KNOW]
+        login_celebrity('registry_experts')
+        view = create_initialized_view(self.product, '+index')
+        text = view.license_approved_widget
+        self.assertEqual('License required', text)
+
+
+class ProductSetReviewLicensesViewTestCase(TestCaseWithFactory):
+    """Tests the ProductSetReviewLicensesView."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(ProductSetReviewLicensesViewTestCase, self).setUp()
+        self.product_set = getUtility(IProductSet)
+        self.user = login_celebrity('registry_experts')
+
+    def test_initial_values(self):
+        # The initial values show active, unreviewed, unapproved projects.
+        view = create_initialized_view(self.product_set, '+review-licenses')
+        self.assertContentEqual(
+            {'active': True,
+             'license_reviewed': False,
+             'license_approved': False,
+             'search_text': None,
+             'licenses': set(),
+             'has_zero_licenses': None,
+             'license_info_is_empty': None,
+             'created_after': None,
+             'created_before': None,
+             'subscription_expires_after': None,
+             'subscription_expires_before': None,
+             'subscription_modified_after': None,
+             'subscription_modified_before': None,
+             }.items(),
+            view.initial_values.items())
+
+    def test_forReviewBatched(self):
+        # The projects are batched.
+        view = create_initialized_view(self.product_set, '+review-licenses')
+        batch = view.forReviewBatched()
+        self.assertEqual(50, batch.default_size)
+
+    def test_project_common_data(self):
+        # The each project contains information to complete a review.
+        self.factory.makeProduct(name='fnord')
+        view = create_initialized_view(
+            self.product_set, '+review-licenses', principal=self.user)
+        content = find_tag_by_id(view.render(), 'project-fnord')
+        self.assertTrue(content.find(id='fnord-maintainer') is not None)
+        self.assertTrue(content.find(id='fnord-registrant') is not None)
+        self.assertTrue(content.find(id='fnord-desciption') is not None)
+        self.assertTrue(content.find(id='fnord-packages') is not None)
+        self.assertTrue(content.find(id='fnord-releases') is not None)
+        self.assertTrue(content.find(id='fnord-usage') is not None)
+        self.assertTrue(content.find(id='fnord-licenses') is not None)
+        self.assertTrue(content.find(id='fnord-whiteboard') is not None)
+        self.assertFalse(content.find(
+            id='fnord-commercial-subscription') is not None)
+        self.assertFalse(content.find(id='fnord-license-info') is not None)
+
+    def test_project_license_info_data(self):
+        # The projects with the OTHER_* licenese will show license_info data.
+        product = self.factory.makeProduct(name='fnord')
+        with person_logged_in(product.owner):
+            product.licenses = [License.OTHER_OPEN_SOURCE]
+        view = create_initialized_view(
+            self.product_set, '+review-licenses', principal=self.user)
+        content = find_tag_by_id(view.render(), 'project-fnord')
+        self.assertTrue(content.find(id='fnord-license-info') is not None)
+
+    def test_project_commercial_subscription_data(self):
+        # The projects with the OTHER_Proprietary license show commercial
+        # subscription information.
+        product = self.factory.makeProduct(name='fnord')
+        with person_logged_in(product.owner):
+            product.licenses = [License.OTHER_PROPRIETARY]
+        view = create_initialized_view(
+            self.product_set, '+review-licenses', principal=self.user)
+        content = find_tag_by_id(view.render(), 'project-fnord')
+        self.assertTrue(content.find(
+            id='fnord-commercial-subscription') is not None)
+
+    def test_project_widgets(self):
+        # The active, license_reviewed, and license_approved lazrjs widgets
+        # are used.
+        self.factory.makeProduct(name='fnord')
+        view = create_initialized_view(
+            self.product_set, '+review-licenses', principal=self.user)
+        content = find_tag_by_id(view.render(), 'fnord-statuses')
+        self.assertTrue(
+            'Y.lp.app.choice.addBinaryChoice' in str(
+                content.find(id='fnord-edit-active').parent))
+        self.assertTrue(
+            'Y.lp.app.choice.addBinaryChoice' in str(
+                content.find(id='fnord-edit-license-reviewed').parent))
+        self.assertTrue(
+            'Y.lp.app.choice.addBinaryChoice' in str(
+                content.find(id='fnord-edit-license-approved').parent))

=== modified file 'lib/lp/registry/stories/product/xx-productset.txt'
--- lib/lp/registry/stories/product/xx-productset.txt	2010-09-28 02:39:25 +0000
+++ lib/lp/registry/stories/product/xx-productset.txt	2011-05-11 21:02:33 +0000
@@ -38,79 +38,6 @@
     Review projects...
 
 
-Searching from +review_licenses
-===============================
-
-The search parameters that filter on a boolean value can
-be left unspecified to get results with either value.
-
-    >>> for name in ('active', 'license_reviewed', 'has_zero_licenses',
-    ...              'license_info_is_empty'):
-    ...     control = registry_browser.getControl(name='field.%s' % name)
-    ...     for option in control.displayOptions:
-    ...         print option.decode('utf-8').encode('ascii', 'replace'),
-    ...     print
-    ?(do not filter) ?True ?False
-    ?(do not filter) ?True ?False
-    ?(do not filter) ?True ?False
-    ?(do not filter) ?Empty ?Not Empty
-
-The default form values will search for active projects that have not
-been reviewed.
-
-    >>> registry_browser.getControl(name='field.active').value
-    ['True']
-    >>> registry_browser.getControl(name='field.license_reviewed').value
-    ['False']
-    >>> registry_browser.getControl(name='field.licenses').value
-    []
-
-If there are no products that match the search conditions, a warning
-message will be displayed.
-
-    >>> registry_browser.getControl(name='field.active').value = ['False']
-    >>> registry_browser.getControl(name='search').click()
-    >>> find_tags_by_class(registry_browser.contents, 'warning message')
-    [<div...No items found with these search options...
-
-If enough projects are found, the results are paginated. A link
-is provided for each product pointint to its +review-license page.
-
-    >>> registry_browser.getControl(name='field.active').value = ['True']
-    >>> registry_browser.getControl(name='field.licenses').value = []
-    >>> registry_browser.getControl(
-    ...     name='field.license_info_is_empty').value = ['Empty']
-    >>> registry_browser.getControl(name='field.created_before').value = (
-    ...     '2007-01-01')
-    >>> registry_browser.getControl(name='search').click()
-    >>> pagination = find_tags_by_class(registry_browser.contents,
-    ...                                 'batch-navigation-index')
-    >>> text_in_elements = pagination[0].findAll(text=True)
-    >>> print ' '.join(text.strip() for text in text_in_elements)
-    1 ... 14 of 14 results
-    >>> table = find_tag_by_id(registry_browser.contents, 'product-listing')
-    >>> for tr in table.findAll('tr'):
-    ...     td_list = tr.findAll('td')
-    ...     print td_list[5].a
-    <a href="/tomcat/+review-license">Review</a> ...
-
-Now, we can click on the Review link, which will take us to the
-$project/+review-license page. Then clicking on the Change button
-will take us back to the page we came from.
-
-    >>> projects_link = registry_browser.url
-    >>> review_link = registry_browser.getLink(
-    ...     url='/tomcat/+review-license')
-    >>> review_link.text
-    'Review'
-    >>> review_link.click()
-    >>> print registry_browser.url
-    http://launchpad.dev/tomcat/+review-license
-    >>> registry_browser.getControl(name='field.actions.change').click()
-    >>> print registry_browser.url == projects_link
-    True
-
-
 View all projects
 =================
 

=== modified file 'lib/lp/registry/templates/product-listing-for-review.pt'
--- lib/lp/registry/templates/product-listing-for-review.pt	2009-07-17 17:59:07 +0000
+++ lib/lp/registry/templates/product-listing-for-review.pt	2011-05-11 21:02:33 +0000
@@ -4,45 +4,101 @@
   xmlns:i18n="http://xml.zope.org/namespaces/i18n";
   omit-tag="">
 
-  <tr>
-    <td style="white-space: nowrap"
-        tal:content="structure context/fmt:link" />
-    <tal:XXX condition="nothing">
-     # XXX: Edwin Grubbs 2008-06-17 bug=240759
-     # The @@/yes and @@/no img tags could be handled by a tales formatter.
-    </tal:XXX>
-    <td style="text-align: center">
-      <img src="/@@/yes" tal:condition="context/active"/>
-      <img src="/@@/no" tal:condition="not: context/active"/>
-    </td>
-    <td style="text-align: center">
-      <img src="/@@/yes" tal:condition="context/license_reviewed"/>
-      <img src="/@@/no" tal:condition="not: context/license_reviewed"/>
-    </td>
-    <td style="text-align: center">
-      <img src="/@@/yes" tal:condition="context/license_approved"/>
-      <img src="/@@/no" tal:condition="not: context/license_approved"/>
-    </td>
-    <td style="white-space: nowrap">
-      <a tal:replace="structure context/owner/fmt:link">Foo Bar</a>
-      <span
-        tal:attributes="title context/datecreated/fmt:datetime"
-        tal:content="context/datecreated/fmt:displaydate">2005-10-12</span>
-    </td>
-    <td style="white-space: nowrap">
-      <span tal:condition="not: context/licenses">None<br/></span>
-      <tal:loop repeat="license context/licenses">
-        <tal:title tal:replace="license/title"/><br/>
-      </tal:loop>
-      (<a tal:attributes="href string:${context/fmt:url}/+review-license"
-        >Review</a>)
-    </td>
-    <td tal:content="structure context/license_info/fmt:shorten/80/fmt:text-to-html">
-        License info goes here
-    </td>
-    <td tal:condition="context/commercial_subscription"
-        tal:content="context/commercial_subscription/date_expires/fmt:date" />
-    <td tal:condition="not: context/commercial_subscription" />
-    <td tal:content="context/reviewer_whiteboard/fmt:shorten/80" />
+  <tr tal:attributes="id string:project-${context/name}">
+    <td>
+      <p>
+        <a tal:replace="structure context/fmt:link" />
+        (<span tal:replace="context/name" />)
+        registered
+        <span
+          tal:attributes="title context/datecreated/fmt:datetime"
+          tal:content="context/datecreated/fmt:displaydate">2005-10-12</span>
+      </p>
+
+      <dl class="horizontal">
+        <dt>Maintained by</dt>
+        <dd tal:attributes="id string:${context/name}-maintainer">
+          <a tal:replace="structure context/owner/fmt:link" />
+        </dd>
+        <dt>Registered by</dt>
+        <dd tal:attributes="id string:${context/name}-registrant">
+          <a tal:replace="structure context/registrant/fmt:link" />
+        </dd>
+      </dl>
+
+      <p tal:content="context/summary" />
+
+      <div
+        tal:condition="context/description"
+        tal:attributes="id string:${context/name}-desciption"
+        tal:content="structure context/description/fmt:text-to-html" />
+
+      <dl class="horizontal">
+        <dt>Has packages</dt>
+        <dd
+          tal:attributes="id string:${context/name}-packages"
+          tal:content="structure context/ubuntu_packages/image:boolean" />
+        <dt>Has releases</dt>
+        <dd
+          tal:attributes="id string:${context/name}-releases"
+          tal:content="structure view/latest_release_with_download_files/image:boolean" />
+      </dl>
+
+      <dl class="horizontal"
+        tal:attributes="id string:${context/name}-usage">
+        <dt>Code usage</dt>
+        <dd tal:content="context/codehosting_usage/title" />
+        <dt>Bug usage</dt>
+        <dd tal:content="context/bug_tracking_usage/title" />
+        <dt>Translations usage</dt>
+        <dd tal:content="context/translations_usage/title" />
+      </dl>
+    </td>
+
+    <td>
+      <dl tal:attributes="id string:${context/name}-statuses">
+        <dt>Project enabled</dt>
+        <dd tal:content="structure view/active_widget" />
+        <dt>Project reviewed</dt>
+        <dd tal:content="structure view/license_reviewed_widget" />
+        <dt>License approved</dt>
+        <dd tal:content="structure view/license_approved_widget" />
+      </dl>
+
+      <dl class="horizontal"
+        tal:attributes="id string:${context/name}-licenses">
+        <dt>Licenses</dt>
+        <dd>
+          <ul class="horizontal">
+            <li tal:condition="not: context/licenses">None</li>
+            <li tal:repeat="license context/licenses">
+              <tal:title tal:replace="license/title"/>
+            </li>
+          </ul>
+        </dd>
+      </dl>
+
+      <dl class="horizontal"
+        tal:condition="view/is_proprietary"
+        tal:attributes="id string:${context/name}-commercial-subscription">
+        <dt>Commercial subscription expiration</dt>
+        <dd
+          tal:condition="context/commercial_subscription"
+          tal:content="context/commercial_subscription/date_expires/fmt:date" />
+        <dd
+          tal:condition="not: context/commercial_subscription">None</dd>
+      </dl>
+
+      <dl class="horizontal"
+        tal:condition="view/show_license_info"
+        tal:attributes="id string:${context/name}-license-info">
+        <dt>Licenses info</dt>
+        <dd tal:content="structure context/license_info/fmt:text-to-html" />
+      </dl>
+
+      <div
+        tal:attributes="id string:${context/name}-whiteboard"
+        tal:content="context/reviewer_whiteboard/fmt:text-to-html" />
+    </td>
   </tr>
 </tal:root>

=== modified file 'lib/lp/registry/templates/product-review-license.pt'
--- lib/lp/registry/templates/product-review-license.pt	2009-10-08 15:54:09 +0000
+++ lib/lp/registry/templates/product-review-license.pt	2011-05-11 21:02:33 +0000
@@ -13,50 +13,31 @@
   <div class="top-portlet">
     <div metal:use-macro="context/@@launchpad_form/form">
       <div metal:fill-slot="extra_info" class="application-summary" >
-        <h2>Granting approval</h2>
-
-        <ul class="bulleted">
-          <li>Does the project use any Launchpad services, such as code or
-              bugs?  Mark the <strong>project approved</strong>
-              and <strong>reviewed</strong> now.
-          </li>
-          <li>Are the project's licenses simple and obviously open source
-              and/or free software?  Does the project smell legitimate?  Mark
-              the <strong>project approved</strong>
-              and <strong>reviewed</strong> now.
-          </li>
-          <li>Does the project appear legitimate but incomplete?  That's fine,
-              mark the <strong>project approved</strong>
-              and <strong>reviewed</strong> now.  Consider also sending a
-              request to the
-              owner asking them to update the project with more details, so as
-              to help recruit contributors.
-          </li>
-        </ul>
-
-        <h2>Withholding approval</h2>
-        <p>
-          There are a few reasons you may want to withhold approval.  You
-          should always contact the project owner for clarification if you do
-          not grant approval.
-        </p>
-        <ul class="bulleted">
-          <li>The project has no license.</li>
+        <p>
+          If you have read the project's essential information, mark it
+          Approved, or if there are issues, just Reviewed.
+        </p>
+
+        <p>
+          You may need to without approval for these common reasons:
+        </p>
+
+        <ul class="bulleted">
+          <li>The project license is only "I Don't Know".</li>
           <li>The project has only <strong>Other/Open source</strong> selected
-              as a license and the license info field does not contain a URL
-              to a license, or contains the whole license.
+              as a license and the license info field does not contain a link
+              to the license, or contains the whole license.
           </li>
           <li>The <strong>Other/Open source</strong> license appears to
               discriminate against who can use the project, or how the
               project's work can be used.
           </li>
-          <li>The project details are too vague to know if it is legitimate.
-          </li>
+          <li>The project is <strong>Other/Proprietary</strong></li>
         </ul>
 
         <p>
           <strong>If the project looks like a test, a prank, or spam,
-          deactivate it.</strong>
+          deactivate it.</strong>.
         </p>
 
       <input type="hidden" name="next_url"

=== modified file 'lib/lp/registry/templates/products-review-licenses.pt'
--- lib/lp/registry/templates/products-review-licenses.pt	2010-10-10 21:54:16 +0000
+++ lib/lp/registry/templates/products-review-licenses.pt	2011-05-11 21:02:33 +0000
@@ -11,6 +11,22 @@
   <metal:block fill-slot="head_epilogue">
     <metal:yui-dependencies
       use-macro="context/@@launchpad_widget_macros/yui2calendar-dependencies" />
+    <style type="text/css">
+      dl.horizontal dt {
+          display: inline;
+          margin-right: .5em;
+          }
+      dl.horizontal dd {
+           display: inline;
+           margin-right: 2em;
+           }
+      dl.horizontal dd ul.horizontal {
+           display: inline;
+           }
+      dl + p {
+            margin-top: 1em;
+            }
+    </style>
   </metal:block>
 
 <div metal:fill-slot="main"
@@ -23,15 +39,8 @@
     <tal:navigation replace="structure batch/@@+navigation-links-upper" />
     <table id="product-listing" class="listing">
       <thead>
-        <th>Project</th>
-        <th>Active</th>
-        <th>Reviewed</th>
-        <th>License<br />approved</th>
-        <th>Registered by</th>
-        <th>Licenses</th>
-        <th style="width: 40%">License info</th>
-        <th>Commercial<br />Subscription<br />expiration</th>
-        <th style="width: 40%">Reviewer<br />whiteboard</th>
+        <th style="max-width: 45em">Project information</th>
+        <th>Status and Licenses</th>
       </thead>
       <tbody>
         <div tal:repeat="product batch/currentBatch"