MozReview: Add DiffViewerDropdownActionHook to add dropdown menus to diff view (Bug 1232703). r?glob draft
authorDavid Walsh <dwalsh@mozilla.com>
Thu, 18 Aug 2016 11:17:52 -0500
changeset 94 233e8a43e7bbf0d50a4120bafc36d6f73440fd89
parent 93 802db1ddea954f44bbaeb09af8030a70b335860b
push idunknown
push userunknown
push dateunknown
reviewersglob
bugs1232703
MozReview: Add DiffViewerDropdownActionHook to add dropdown menus to diff view (Bug 1232703). r?glob MozReview-Commit-ID: HPYJBX4Kzj
reviewboard/docs/manual/extending/extensions/hooks/action-hooks.rst
reviewboard/reviewboard/extensions/hooks.py
reviewboard/reviewboard/extensions/templatetags/rb_extensions.py
reviewboard/reviewboard/extensions/tests.py
reviewboard/reviewboard/templates/diffviewer/view_diff_mozreview.html
--- a/reviewboard/docs/manual/extending/extensions/hooks/action-hooks.rst
+++ b/reviewboard/docs/manual/extending/extensions/hooks/action-hooks.rst
@@ -50,16 +50,19 @@ the actions you'd like to insert. These 
 There are also two hooks to provide drop-down menus in the action bars:
 
 +---------------------------------------------+-------------------------+
 | Class                                       | Location                |
 +=============================================+=========================+
 | :py:class:`ReviewRequestDropdownActionHook` | The bar at the top of a |
 |                                             | review request.         |
 +---------------------------------------------+-------------------------+
+| :py:class:`DiffViewerDropdownActionHook`    | The bar at the top of a |
+|                                             | diff view.              |
++---------------------------------------------+-------------------------+
 | :py:class:`HeaderDropdownActionHook`        | The page header.        |
 +---------------------------------------------+-------------------------+
 
 These work like the basic ActionHooks, except instead of a **url** field, they
 contain an **items** field which is another list of dictionaries. Only one
 level of nesting is possible.
 
 
--- a/reviewboard/reviewboard/extensions/hooks.py
+++ b/reviewboard/reviewboard/extensions/hooks.py
@@ -535,16 +535,46 @@ class ReviewRequestDropdownActionHook(Ac
 
 
 @six.add_metaclass(ExtensionHookPoint)
 class DiffViewerActionHook(ActionHook):
     """A hook for adding an action to the diff viewer page."""
 
 
 @six.add_metaclass(ExtensionHookPoint)
+class DiffViewerDropdownActionHook(ActionHook):
+    """A hook for adding an drop down action to the review request page.
+
+    The actions for a drop down action should contain:
+
+    * ``id``:      The ID of this action (optional).
+    * ``label``:   The label of the drop-down.
+    * ``items``:   A list of ActionHook-style dicts (see ActionHook params).
+
+    For example::
+
+        actions = [{
+            'id': 'id 0',
+            'label': 'Title',
+            'items': [
+                {
+                    'id': 'id 1',
+                    'label': 'Item 1',
+                    'url': '...',
+                },
+                {
+                    'id': 'id 2',
+                    'label': 'Item 2',
+                    'url': '...',
+                }
+            ]
+        }]
+    """
+
+@six.add_metaclass(ExtensionHookPoint)
 class HeaderActionHook(ActionHook):
     """A hook for putting an action in the page header."""
 
 
 @six.add_metaclass(ExtensionHookPoint)
 class HeaderDropdownActionHook(ActionHook):
     """A hook for putting multiple actions into a header dropdown."""
 
@@ -907,16 +937,17 @@ class ReviewRequestPublishedEmailHook(Em
     'AdminWidgetHook',
     'AuthBackendHook',
     'CommentDetailDisplayHook',
     'DashboardColumnsHook',
     'DashboardSidebarItemsHook',
     'DataGridColumnsHook',
     'DataGridSidebarItemsHook',
     'DiffViewerActionHook',
+    'DiffViewerDropdownActionHook',
     'EmailHook',
     'ExtensionHook',
     'FileAttachmentThumbnailHook',
     'HeaderActionHook',
     'HeaderDropdownActionHook',
     'HostingServiceHook',
     'NavigationBarHook',
     'ReviewRequestActionHook',
--- a/reviewboard/reviewboard/extensions/templatetags/rb_extensions.py
+++ b/reviewboard/reviewboard/extensions/templatetags/rb_extensions.py
@@ -3,16 +3,17 @@ from __future__ import unicode_literals
 import logging
 
 from django import template
 from django.template.loader import render_to_string
 from djblets.util.decorators import basictag
 
 from reviewboard.extensions.hooks import (CommentDetailDisplayHook,
                                           DiffViewerActionHook,
+                                          DiffViewerDropdownActionHook,
                                           HeaderActionHook,
                                           HeaderDropdownActionHook,
                                           NavigationBarHook,
                                           ReviewRequestActionHook,
                                           ReviewRequestDropdownActionHook)
 from reviewboard.site.urlresolvers import local_site_reverse
 
 
@@ -53,16 +54,26 @@ def action_hooks(context, hook_cls, acti
 @basictag(takes_context=True)
 def diffviewer_action_hooks(context):
     """Displays all registered action hooks for the diff viewer."""
     return action_hooks(context, DiffViewerActionHook)
 
 
 @register.tag
 @basictag(takes_context=True)
+def diffviewer_dropdown_action_hooks(context):
+    """Displays all registered action hooks for the diff viewer dropdown."""
+    return action_hooks(context,
+                        DiffViewerDropdownActionHook,
+                        "actions",
+                        "extensions/action_dropdown.html")
+
+
+@register.tag
+@basictag(takes_context=True)
 def review_request_action_hooks(context):
     """Displays all registered action hooks for review requests."""
     return action_hooks(context, ReviewRequestActionHook)
 
 
 @register.tag
 @basictag(takes_context=True)
 def review_request_dropdown_action_hooks(context):
--- a/reviewboard/reviewboard/extensions/tests.py
+++ b/reviewboard/reviewboard/extensions/tests.py
@@ -15,16 +15,17 @@ from kgb import SpyAgency
 from reviewboard.admin.widgets import (primary_widgets,
                                        secondary_widgets,
                                        Widget)
 from reviewboard.admin.siteconfig import load_site_config
 from reviewboard.extensions.base import Extension
 from reviewboard.extensions.hooks import (AdminWidgetHook,
                                           CommentDetailDisplayHook,
                                           DiffViewerActionHook,
+                                          DiffViewerDropdownActionHook,
                                           EmailHook,
                                           HeaderActionHook,
                                           HeaderDropdownActionHook,
                                           HostingServiceHook,
                                           NavigationBarHook,
                                           ReviewPublishedEmailHook,
                                           ReviewRequestActionHook,
                                           ReviewRequestApprovalHook,
@@ -95,16 +96,21 @@ class HookTests(TestCase):
         super(HookTests, self).tearDown()
 
         self.extension.shutdown()
 
     def test_diffviewer_action_hook(self):
         """Testing diff viewer action extension hooks"""
         self._test_action_hook('diffviewer_action_hooks', DiffViewerActionHook)
 
+    def test_diffviewer_dropdown_action_hook(self):
+        """Testing diff viewer dropdown-action extension hooks"""
+        self._test_dropdown_action_hook('diffviewer_dropdown_action_hooks',
+                                        DiffViewerDropdownActionHook)
+
     def test_review_request_action_hook(self):
         """Testing review request action extension hooks"""
         self._test_action_hook('review_request_action_hooks',
                                ReviewRequestActionHook)
 
     def test_review_request_dropdown_action_hook(self):
         """Testing review request drop-down action extension hooks"""
         self._test_dropdown_action_hook('review_request_dropdown_action_hooks',
@@ -590,16 +596,19 @@ class NavigationBarTestHook(NavigationBa
     def get_entries(self, context):
         raise Exception
 
 
 class DiffViewerActionTestHook(DiffViewerActionHook):
     def get_actions(self, context):
         raise Exception
 
+class DiffViewerDropdownActionTestHook(DiffViewerDropdownActionHook):
+    def get_actions(self, context):
+        raise Exception
 
 class HeaderActionTestHook(HeaderActionHook):
     def get_actions(self, context):
         raise Exception
 
 
 class HeaderDropdownActionTestHook(HeaderDropdownActionHook):
     def get_actions(self, context):
@@ -749,16 +758,29 @@ class SandboxTests(TestCase):
         context = Context({'comment': 'this is a comment'})
 
         t = Template(
             "{% load rb_extensions %}"
             "{% diffviewer_action_hooks %}")
 
         t.render(context).strip()
 
+    def test_action_hooks_diff_viewer_dropdown_hook(self):
+        """Testing sandboxing DiffViewerDropdownActionHook when
+        action_hooks throws an error"""
+        DiffViewerDropdownActionTestHook(extension=self.extension)
+
+        context = Context({'comment': 'this is a comment'})
+
+        t = Template(
+            "{% load rb_extensions %}"
+            "{% diffviewer_dropdown_action_hooks %}")
+
+        t.render(context).strip()
+
     def test_action_hooks_header_hook(self):
         """Testing sandboxing HeaderActionHook when
         action_hooks throws an error"""
         HeaderActionTestHook(extension=self.extension)
 
         context = Context({'comment': 'this is a comment'})
 
         t = Template(
--- a/reviewboard/reviewboard/templates/diffviewer/view_diff_mozreview.html
+++ b/reviewboard/reviewboard/templates/diffviewer/view_diff_mozreview.html
@@ -34,16 +34,17 @@
   <ul class="actions page-tabs">
    <li><a href="{{review_request.get_absolute_url}}">{% trans "Reviews" %}</a></li>
    <li class="active"><a href="{% url 'view-diff' review_request.display_id %}#index_header">{% trans "Diff" %}</a></li>
   </ul>
 
   <div class="actions-container">
    {% star review_request %}
    <ul class="actions actions-right-container">
+{%    diffviewer_dropdown_action_hooks %}
     <li class="has-menu">
      <a href="#" class="mobile-actions-menu-label"><span class="fa fa-ellipsis-h fa-lg"></span></a>
      <ul class="actions actions-right">
 {%    include "reviews/review_request_actions_secondary.html" %}
 {%    diffviewer_action_hooks %}
       <li id="download-diff" {% if interdiffset %}style="display: none;"{% endif %}><a href="raw/">{% trans "Download Diff" %}</a></li>
 {%    include "reviews/review_request_actions_primary.html" %}
      </ul>