Source

bloodhound-mq / t162 / t162_r1433074_bootstrap_revlog.diff

Full commit
# HG changeset patch
# Parent 53ff149b1a1186fa1849b0a001038e925035460d
BH Theme #162 : Convert revision log web UI to Bootstrap styling

diff -r 53ff149b1a11 bloodhound_theme/bhtheme/htdocs/bloodhound.css
--- a/bloodhound_theme/bhtheme/htdocs/bloodhound.css	Mon Jan 14 19:36:32 2013 +0000
+++ b/bloodhound_theme/bhtheme/htdocs/bloodhound.css	Wed Jan 16 01:49:14 2013 -0500
@@ -480,6 +480,10 @@
   cursor: help;
 }
 
+.breadcrumb .dropdown-menu li {
+  display: block;
+}
+
 /* Revert some changes introduced in 2.1.0 */
 
 h6 {
@@ -703,6 +707,11 @@
   padding: 0.1em 0.25em;
   background-color: #F7F7F7;
 }
+
+.more:before {
+  content: "... "
+}
+
 /* @end */
 
 /* @group Wiki Toolbar */
@@ -741,3 +750,11 @@
 
 /* @end */
 
+/* @group Version control */
+
+.log .age {
+  white-space: nowrap;
+}
+
+/* @end */
+
diff -r 53ff149b1a11 bloodhound_theme/bhtheme/templates/bh_path_links.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bloodhound_theme/bhtheme/templates/bh_path_links.html	Wed Jan 16 01:49:14 2013 -0500
@@ -0,0 +1,54 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:py="http://genshi.edgewall.org/" py:strip="">
+  <!--!  Display a sequence of path components.
+  -
+  -      Each component is a link to the corresponding location in the browser.
+  -
+  -      We expect the following variables to be available in the calling
+  -      context:
+  -       * path_links, a list of dicts each having the following keys:
+  -         * name, the path component name
+  -         * href, a link pointing to this path
+  -       * stickyrev, the optional revision information to show
+  -       * reponame, the optional repository name
+  -       * path_depth_limit, number of path components to show (all if empty) 
+  -->
+<py:def function="part_ui(part, last)"><li><a
+    class="pathentry" href="${part.href}"
+    title="${_('View %(name)s', name=part.name)}">${part.name}</a><span 
+    py:if="not last" class="pathentry sep divider">/</span></li></py:def>
+
+<py:if test="path_links">
+<!--! <realm>: -->
+<li><a class="pathentry first" href="${path_links[0].href}"
+   title="${_('Go to repository index') if reponame else _('Go to repository root')}"><i class="icon-home"></i>${path_links[0].name}</a></li>
+<!--! the/path/.../...
+  -
+  -   Note: in the <py:for>...</py:for> below, we take great care of
+  -         not introducing any space character, so that one can easily
+  -         copy the path to the clipboard.
+  -->
+<py:with vars="_depth = path_depth_limit or 9999;
+  path_lead = path_links[1:2];
+  path_hidden = path_links[2:][:-_depth];
+  path_visible = path_links[2:][-_depth:]"><py:if
+    test="path_lead">${part_ui(path_lead[0], False)}</py:if><py:if
+    test="path_hidden"><li class="dropdown"><a 
+    href="#" class="btn btn-mini dropdown-toggle more" 
+    data-toggle="dropdown"><b class="caret"></b></a><ul
+    class="dropdown-menu"><li 
+    py:for="idx, part in enumerate(path_hidden)"
+    py:with="last = idx == len(path_hidden) - 1"><a 
+      class="pathentry" href="${part.href}"
+      title="${_('View %(name)s', name=part.name)}">${part.name}<span 
+      py:if="not last" class="pathentry sep">/</span></a></li></ul></li><span 
+    class="pathentry sep divider">/</span></py:if><py:for 
+    each="idx, part in enumerate(path_visible)"><py:with 
+    vars="last = idx == len(path_visible) - 1;
+    ">${part_ui(part, last)}</py:with></py:for></py:with></py:if>
+<py:if test="stickyrev" py:with="drev = display_rev(stickyrev)"><span class="pathentry sep">@</span>
+  <a class="pathentry" href="${href.changeset(stickyrev, reponame)}"
+     title="${_('View changeset %(rev)s', rev=drev)}">$drev</a>
+</py:if>
+
+</div>
diff -r 53ff149b1a11 bloodhound_theme/bhtheme/templates/bh_revisionlog.html
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bloodhound_theme/bhtheme/templates/bh_revisionlog.html	Wed Jan 16 01:49:14 2013 -0500
@@ -0,0 +1,253 @@
+<!--!
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:i18n="http://genshi.edgewall.org/i18n"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="layout.html" />
+  <head>
+    <title>${'/'.join(part.name for part in path_links[1:]) or '/'} (log)</title>
+    <script py:if="graph" type="text/javascript">//<![CDATA[
+      jQuery(document).ready(function($) {
+        $('th.trac-graph, td.trac-graph').show();
+        var canvas = $('<canvas>').css({width: '${graph.columns * 2}em',
+                                        height: '${len(items) * 2}em'})
+                                  .appendTo('td.trac-graph')[0];
+        canvas.width = $(canvas).width();
+        canvas.height = $(canvas).height();
+        if (typeof(G_vmlCanvasManager) != 'undefined')
+          canvas = G_vmlCanvasManager.initElement(canvas);
+        $.paintLogGraph(graph, canvas);
+      });
+    //]]></script>
+  </head>
+
+  <body>
+    <div id="content" class="log row">
+      <div class="span4">
+        <h1>Revision log</h1>
+
+        <div class="diff">
+          <div class="legend" id="file-legend">
+            <h3>Legend:</h3>
+            <dl>
+              <dt class="add"></dt><dd>Added</dd>
+              <py:if test="mode == 'path_history'">
+                <dt class="rem" ></dt><dd>Removed</dd>
+              </py:if>
+              <dt class="mod"></dt><dd>Modified</dd>
+              <dt class="cp"></dt><dd>Copied or renamed</dd>
+            </dl>
+          </div>
+        </div>
+      </div>
+      <div class="span8">
+        <div class="well">
+          <form id="prefs" action="" method="get">
+            <div class="form-horizontal">
+              <input type="hidden" name="action" value="$mode" />
+              <input type="hidden" name="revs" value="${str(revranges)}" py:if="revranges" />
+              <div class="choice">
+                <fieldset>
+                  <legend>Revision Log Mode:</legend>
+                  <label class="radio inline">
+                    <input type="radio" id="stop_on_copy" name="mode"
+                           value="stop_on_copy"
+                           checked="${mode in (None, 'stop_on_copy') or None}" />
+                    Stop on copy
+                  </label>
+                  <label class="radio inline">
+                    <input type="radio" id="follow_copy" name="mode"
+                           value="follow_copy"
+                           checked="${mode == 'follow_copy' or None}" />
+                    Follow copies
+                  </label>
+                  <label class="radio inline">
+                    <input type="radio" id="path_history" name="mode"
+                           value="path_history"
+                           checked="${mode == 'path_history' or None}" />
+                    Show only adds and deletes
+                  </label>
+                </fieldset>
+              </div>
+              <div class="control-group">
+                <i18n:msg>
+                  <label class="control-label">View log starting at</label>
+                  <div class="controls">
+                    <input type="text" id="rev" name="rev" value="$rev" size="5" />
+                  </div>
+                  <label class="control-label">and back to</label>
+                  <div class="controls">
+                    <input type="text" id="stop_rev" name="stop_rev" value="$stop_rev" size="5" />
+                  </div>
+                </i18n:msg>
+                <i18n:msg>
+                  <label class="control-label">Show at most</label>
+                  <div class="controls">
+                    <input type="text" id="limit" name="limit" value="$limit" 
+                        class="input-mini" size="3" maxlength="3" />
+                    revisions per page.
+                  </div>
+                </i18n:msg>
+                <div class="controls">
+                  <label>
+                    <input type="checkbox" id="verbose" name="verbose" checked="${verbose or None}" />
+                    Show full log messages
+                  </label>
+                </div>
+              </div>
+              <div class="buttons controls">
+                <input class="btn" type="submit" value="${_('Update')}" />
+              </div>
+            </div>
+          </form>
+        </div>
+      </div>
+
+      <form py:for="items in item_ranges" class="printableform" action="${href.changeset()}" method="get">
+        <div class="buttons">
+          <input type="hidden" name="reponame" value="$reponame" />
+          <input class="btn" type="submit" value="${_('View changes')}"
+            title="Diff from Old Revision to New Revision (as selected in the Diff column)" />
+        </div>
+        <table class="table table-condensed table-striped listing chglist${' trac-graph' if graph else None}">
+          <thead>
+            <tr>
+              <th py:if="graph" class="trac-graph">Graph</th>
+              <th class="diff" title="Old / New" 
+                  style="width: 30px">Diff</th>
+              <th class="change"></th>
+              <th class="rev">Rev</th>
+              <th class="age">Age</th>
+              <th class="author">Author</th>
+              <th class="summary"><py:if test="not verbose">Log Message</py:if></th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr py:if="not items" class="even">
+              <td py:if="graph" class="trac-graph"/>
+              <td />
+              <td />
+              <td class="copyfrom_path" colspan="6">
+                No revisions found
+              </td>
+            </tr>
+
+            <py:for each="idx, item in enumerate(items)">
+              <py:with vars="change = changes[item.rev];
+                             is_separator = item.change is None;
+                             chgset_context = context.child('changeset', change.rev, parent=repos.resource);
+                             odd_even = 'odd' if idx % 2 else 'even'">
+                <!--! highlight copy or rename operations -->
+                <tr py:if="not is_separator and item.get('copyfrom_path')" class="$odd_even">
+                  <td />
+                  <td class="copyfrom_path" colspan="6" style="padding-left: ${item.depth - 1}em">
+                    <i class="icon-info-sign"></i>
+                    <i18n:msg params="path">copied from <a href="${href.browser(reponame, item.path, rev=item.rev)}">$item.copyfrom_path</a>:</i18n:msg>
+                  </td>
+                </tr>
+
+                <tr class="${classes(odd_even,verbose=verbose)}" py:choose="">
+                  <td py:if="graph and idx == 0" class="trac-graph" rowspan="${len(items)}"/>
+                  <td class="diff">
+                    <input type="radio" name="old" value="${item.rev}@${item.path}"
+                           checked="${idx == (len(items) - 1) or None}" 
+                           title="${_('From [%(rev)s]', rev=display_rev(item.rev))}"
+                           class="pull-left" />
+                    <input type="radio" name="new" value="${item.rev}@${item.path}"
+                           checked="${idx == 0 or None}" 
+                           title="${_('To [%(rev)s]', rev=display_rev(item.rev))}" 
+                           class="pull-left" />
+                  </td>
+                  <py:when test="not is_separator">
+                    <td class="change" style="padding-left: ${item.depth}em">
+                      <a href="${href.log(reponame, item.path, rev=item.rev)}"
+                         title="View log starting at this revision">
+                        <span class="$item.change"></span>
+                        <span class="label comment">$item.change</span>
+                      </a>
+                    </td>
+                    <td class="rev">
+                      <a href="${href.browser(reponame, item.path, rev=item.existing_rev)}"
+                         title="${_('Browse at revision %(rev)s', rev=display_rev(item.existing_rev))}">
+                        @${display_rev(item.existing_rev)}</a>
+                      <py:choose test="item.change">
+                        <a py:when="'delete'" class="chgset" href="${href.changeset(item.rev)}"
+                           title="${_('View removal changeset [%(rev)s]', rev=display_rev(item.rev))}">&nbsp;</a>
+                        <a py:otherwise="" class="chgset" href="${href.changeset(item.rev, reponame, item.path)}"
+                           title="${_('View changeset [%(rev)s] restricted to %(path)s',
+                                      rev=display_rev(item.rev), path=item.path or '/')}">&nbsp;</a>
+                      </py:choose>
+                    </td>
+                    <td class="age" py:content="pretty_dateinfo(change.date, dateonly=True)" />
+                    <td class="author" py:content="authorinfo_short(change.author)" />
+                    <td class="summary" py:choose=""
+                        style="${graph and ('border-left: 2px solid '
+                                            + graph.colors[graph.vertices[idx][1] % len(graph.colors)]) or None}">
+                      <py:when test="verbose"></py:when>
+                      <py:when test="wiki_format_messages">
+                        ${wiki_to_oneliner(chgset_context, change.message, shorten=True)}
+                      </py:when>
+                      <py:otherwise>${shorten_line(change.message)}</py:otherwise>
+                      <py:for each="branch, head in change.get_branches()" >
+                        <span py:if="branch not in ('default', 'master')"
+                            class="label label-inverse branch${' head' if head else ''}"
+                            title="${_('Branch head') if head else _('Branch')}">${branch}</span>
+                      </py:for>
+                      <py:for each="tag in change.get_tags()">
+                        <span class="tag label label" title="Tag">${tag}</span>
+                      </py:for>
+                    </td>
+                  </py:when>
+                  <td colspan="5" py:otherwise="" />
+                </tr>
+
+                <tr py:if="verbose and not is_separator" class="summary verbose $odd_even">
+                  <td class="filler" colspan="2" />
+                  <td class="log" colspan="4" py:choose="" xml:space="preserve">
+                    <py:when test="wiki_format_messages">
+                      ${wiki_to_html(chgset_context, change.message, escape_newlines=True)}
+                    </py:when>
+                    <py:otherwise><pre>${change.message}</pre></py:otherwise>
+                  </td>
+                </tr>
+
+              </py:with>
+            </py:for>
+          </tbody>
+        </table>
+
+        <div py:if="len(items) &gt; 10 and len(item_ranges) == 1" class="buttons">
+          <input class="btn" type="submit" value="${_('View changes')}"
+            title="Diff from Old Revision to New Revision (as selected in the Diff column)" />
+        </div>
+      </form>
+
+      <div id="help" class="pull-right" i18n:msg="">
+        <span class="label label-info">Note:</span> See <a href="${href.wiki('TracRevisionLog')}">TracRevisionLog</a>
+        for help on using the revision log.
+      </div>
+
+    </div>
+  </body>
+</html>
diff -r 53ff149b1a11 bloodhound_theme/bhtheme/theme.py
--- a/bloodhound_theme/bhtheme/theme.py	Mon Jan 14 19:36:32 2013 +0000
+++ b/bloodhound_theme/bhtheme/theme.py	Wed Jan 16 01:49:14 2013 -0500
@@ -104,6 +104,9 @@
         'ticket.html' : ('bh_ticket.html', '_modify_ticket'),
         'ticket_preview.html' : ('bh_ticket_preview.html', None),
 
+        # Version control
+        'revisionlog.html' : ('bh_revisionlog.html', '_modify_generic_vcs'),
+
         # Multi Product
         'product_view.html' : ('bh_product_view.html', None),
 
@@ -315,6 +318,14 @@
         """
         add_script(req, 'dashboard/js/bootstrap-scrollspy.js')
 
+    def _modify_generic_vcs(self, req, template, data, content_type, is_active):
+        """Locate path to file in breadcrumbs area rather than title.
+        """
+        data.update(dict(
+                resourcepath_template='bh_path_links.html',
+                path_depth_limit=2
+            ))
+
     # INavigationContributor methods
 
     def get_active_navigation_item(self, req):