Modified: bloodhound/vendor/trac/current/trac/ticket/roadmap.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/roadmap.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/roadmap.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/roadmap.py Fri Nov 14 11:06:23 
2014
@@ -350,6 +350,13 @@ def grouped_stats_data(env, stats_provid
                 group_names = field['options']
                 if field.get('optional'):
                     group_names.insert(0, '')
+            elif field.get('custom'):
+                group_names = [name for name, in env.db_query("""
+                    SELECT DISTINCT COALESCE(c.value, '') FROM ticket_custom c
+                    WHERE c.name=%s ORDER BY COALESCE(c.value, '')
+                    """, (by, ))]
+                if '' not in group_names:
+                    group_names.insert(0, '')
             else:
                 group_names = [name for name, in env.db_query("""
                     SELECT DISTINCT COALESCE(%s, '') FROM ticket
@@ -854,6 +861,9 @@ class MilestoneModule(Component):
                 'TICKET_ADMIN' in req.perm)
         else:
             req.perm(milestone.resource).require('MILESTONE_CREATE')
+            if milestone.name:
+                add_notice(req, _("Milestone %(name)s does not exist. You can"
+                                  " create it here.", name=milestone.name))
 
         chrome = Chrome(self.env)
         chrome.add_jquery_ui(req)
@@ -1027,7 +1037,7 @@ class MilestoneModule(Component):
         milestone_realm = Resource('milestone')
         for name, due, completed, description \
                 in MilestoneCache(self.env).milestones.itervalues():
-            if any(r.search(description) or r.search(name)
+            if all(r.search(description) or r.search(name)
                    for r in term_regexps):
                 milestone = milestone_realm(id=name)
                 if 'MILESTONE_VIEW' in req.perm(milestone):

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/batch_modify.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/batch_modify.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/batch_modify.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/batch_modify.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2012-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <form xmlns="http://www.w3.org/1999/xhtml";
      xmlns:py="http://genshi.edgewall.org/";
      xmlns:i18n="http://genshi.edgewall.org/i18n";
@@ -12,7 +22,7 @@
         <label for="batchmod_value_comment">Comment:</label>
       </th>
       <td class="fullrow"><textarea
-          id="batchmod_value_comment" name="batchmod_value_comment" cols="70" 
rows="5"/>
+          id="batchmod_value_comment" name="batchmod_value_comment" 
class="trac-fullwidth" cols="70" rows="5"/>
       </td>
     </tr>
 

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/milestone_delete.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/milestone_delete.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/milestone_delete.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/milestone_delete.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/milestone_edit.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/milestone_edit.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/milestone_edit.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/milestone_edit.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -103,9 +113,10 @@
         </fieldset>
         <div class="field">
           <fieldset>
-            <label for="description" i18n:msg="">Description (you may use <a 
tabindex="42"
-                   href="${href.wiki('WikiFormatting')}">WikiFormatting</a> 
here):</label>
-            <p><textarea id="description" name="description" class="wikitext 
trac-resizable" rows="10" cols="78">
+            <label for="description" i18n:msg="">
+              Description: (you may use <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
+            </label>
+            <p><textarea id="description" name="description" class="wikitext 
trac-fullwidth trac-resizable" rows="10" cols="78">
 ${milestone.description}</textarea></p>
           </fieldset>
         </div>

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/milestone_view.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/milestone_view.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/milestone_view.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/milestone_view.html 
Fri Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/query.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/query.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/query.html (original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/query.html Fri Nov 14 
11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/query_results.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/query_results.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/query_results.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/query_results.html Fri 
Nov 14 11:06:23 2014
@@ -1,17 +1,26 @@
-<!--!
-       groups    - a dict, where:
-                     key       - is the value shared by all results in this 
group
-                     value     - is the list of corresponding tickets
-
-       headers   - a sequence of header structure:
-                     .name     - field name for this header
-                     .label    - what to display for this header
+<!--!  Copyright (C) 2006-2014 Edgewall Software
 
-       fields    - dict of field name to field structure:
-                     .label    - field label
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
 
-       query     - the actual Query instance used to perform the query
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
 
+Arguments:
+ - groups    - a dict, where:
+                key       - is the value shared by all results in this group
+                value     - is the list of corresponding tickets
+
+ - headers   - a sequence of header structure:
+                .name     - field name for this header
+                .label    - what to display for this header
+
+ - fields    - dict of field name to field structure:
+                .label    - field label
+
+ - query     - the actual Query instance used to perform the query
 -->
 <div xmlns="http://www.w3.org/1999/xhtml";
      xmlns:py="http://genshi.edgewall.org/";

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/report_delete.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/report_delete.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/report_delete.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/report_delete.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/report_edit.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/report_edit.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/report_edit.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/report_edit.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -11,38 +21,45 @@
   </head>
 
   <body>
-    <div id="content" class="report">
+    <div id="content" class="report edit"
+         py:with="new_report = action == 'new'">
 
-      <h1>${_('New Report') if action == 'new' else report.title}</h1>
-      <form action="${href.report(report.id)}" method="post" id="edit_report">
-        <div>
+      <h1 py:choose="">
+        <py:when test="new_report">New Report</py:when>
+        <py:otherwise>$report.title</py:otherwise>
+      </h1>
+      <form id="edit_report" method="post" action="${href.report(report.id)}">
+        <fieldset>
+          <legend py:choose="">
+            <py:when test="new_report">Create Report:</py:when>
+            <py:otherwise>Modify Report:</py:otherwise>
+          </legend>
           <input type="hidden" name="action" value="$action" />
           <div class="field">
-            <label for="title">Report Title:</label><br />
-            <input type="text" id="title" name="title" class="trac-autofocus" 
value="$report.title" size="50" /><br />
+            <label for="title">Title:</label>
+            <input type="text" id="title" name="title" class="trac-fullwidth 
trac-autofocus" value="$report.title" />
           </div>
           <div class="field">
             <label for="description" i18n:msg="">
               Description: (you may use <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here)
             </label>
-            <br />
-            <textarea id="description" name="description" class="wikitext 
trac-resizable" rows="10" cols="78">
+            <textarea id="description" name="description" class="wikitext 
trac-fullwidth trac-resizable" rows="10" cols="78">
 $report.description</textarea>
           </div>
           <div class="field">
             <div class="system-message" py:if="error">
               <strong>Error:</strong> $error
             </div>
-            <label for="query" i18n:msg="">Query for Report: (can be either 
SQL or, if starting with <tt>query:</tt>,
+            <label for="query" i18n:msg="">Query: (can be either SQL or, if 
starting with <tt>query:</tt>,
               a <a tabindex="42" href="${href.wiki('TracQuery') + 
'#QueryLanguage'}">TracQuery</a> expression)
-            </label><br />
-            <textarea id="query" name="query" class="trac-resizable" cols="85" 
rows="20">
+            </label>
+            <textarea id="query" name="query" class="trac-fullwidth 
trac-resizable" rows="20" cols="78">
 $report.sql</textarea>
           </div>
-          <div class="buttons">
-            <input type="submit" class="trac-disable-on-submit" 
value="${_('Save report')}"/>
-            <input type="submit" name="cancel" value="${_('Cancel')}"/>
-          </div>
+        </fieldset>
+        <div class="buttons">
+          <input type="submit" class="trac-disable-on-submit" value="${_('Save 
report')}" />
+          <input type="submit" name="cancel" value="${_('Cancel')}" />
         </div>
       </form>
 

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/report_list.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/report_list.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/report_list.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/report_list.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2009-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -96,7 +106,7 @@
               </a></h3>
             <span class="foldable" />
             <div py:if="description" class="description" xml:space="preserve">
-              ${wiki_to_html(context, description)}
+              ${wiki_to_html(context.child('report', id), description)}
             </div>
           </div>
         </py:when>

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/report_view.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/report_view.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/report_view.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/report_view.html Fri 
Nov 14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/roadmap.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/roadmap.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/roadmap.html (original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/roadmap.html Fri Nov 
14 11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2006-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -60,7 +70,7 @@
             </py:choose>
             <xi:include href="progress_bar.html" py:if="mstats.stats.count"
                         py:with="stats = mstats.stats; interval_hrefs = 
mstats.interval_hrefs;
-                                 stats_href = mstats.stats_href"/>
+                                 stats_href = mstats.stats_href" />
           </div>
 
           <div class="description" xml:space="preserve">
@@ -71,10 +81,10 @@
       </div>
 
       <div py:if="'MILESTONE_CREATE' in perm" class="buttons">
-       <form id="add" method="get" action="${href.milestone()}"><div>
-        <input type="hidden" name="action" value="new" />
-        <input type="submit" value="${_('Add new milestone')}" />
-       </div></form>
+        <form id="add" method="get" action="${href.milestone()}"><div>
+          <input type="hidden" name="action" value="new" />
+          <input type="submit" value="${_('Add new milestone')}" />
+        </div></form>
       </div>
 
       <div id="help" i18n:msg=""><strong>Note:</strong> See

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/ticket.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/ticket.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/ticket.html (original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/ticket.html Fri Nov 14 
11:06:23 2014
@@ -1,3 +1,13 @@
+<!--!  Copyright (C) 2007-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+-->
 <!DOCTYPE html
     PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";>
@@ -194,7 +204,7 @@
                 <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a>
                 here.
               </label>
-              <textarea id="comment" name="comment" class="wikitext 
trac-resizable" rows="10" cols="78">
+              <textarea id="comment" name="comment" class="wikitext 
trac-fullwidth trac-resizable" rows="10" cols="78">
 ${comment}</textarea>
             </fieldset>
           </div>
@@ -211,38 +221,35 @@ ${comment}</textarea>
                   <py:otherwise>Properties</py:otherwise>
               </legend>
               <table>
+                <col class="th" /><col class="td" /><col class="th" /><col 
class="td" />
                 <tr py:if="can_modify or can_create">
                   <th><label for="field-summary">Summary:</label></th>
                   <td class="fullrow" colspan="3">
                     <input type="text" id="field-summary" name="field_summary"
                            class="${'trac-autofocus' if not ticket.exists and 
not preview_mode else None}"
-                           value="$ticket.summary" size="70" />
+                           value="$ticket.summary" />
                   </td>
                 </tr>
-                <py:if test="only_for_admin">
-                  <tr>
-                    <th><label for="field-reporter">Reporter:</label></th>
-                    <td class="fullrow" colspan="3">
-                      <input type="text" id="field-reporter" 
name="field_reporter"
-                             value="${ticket.reporter}" size="70" />
-                    </td>
-                  </tr>
-                </py:if>
-                <py:if test="can_edit or can_create">
-                  <tr>
-                    <th><label 
for="field-description">Description:</label></th>
-                    <td class="fullrow" colspan="3">
-                      <fieldset>
-                        <label for="field-description" 
id="field-description-help" i18n:msg="">You may use
-                          <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here.
-                        </label>
-                        <textarea id="field-description" 
name="field_description"
-                                  class="wikitext trac-resizable" rows="10" 
cols="68">
+                <tr py:if="only_for_admin">
+                  <th><label for="field-reporter">Reporter:</label></th>
+                  <td class="fullrow" colspan="3">
+                    <input type="text" id="field-reporter" 
name="field_reporter"
+                           value="${ticket.reporter}" />
+                  </td>
+                </tr>
+                <tr py:if="can_edit or can_create">
+                  <th><label for="field-description">Description:</label></th>
+                  <td class="fullrow" colspan="3">
+                    <fieldset>
+                      <label for="field-description" 
id="field-description-help" i18n:msg="">You may use
+                        <a tabindex="42" 
href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here.
+                      </label>
+                      <textarea id="field-description" name="field_description"
+                                class="wikitext trac-fullwidth trac-resizable" 
rows="10" cols="68">
 ${ticket.description}</textarea>
-                      </fieldset>
-                    </td>
-                  </tr>
-                </py:if>
+                    </fieldset>
+                  </td>
+                </tr>
                 <tr py:for="row in group(fields, 2, lambda f: f.type != 
'textarea')"
                     py:if="can_modify or can_create"
                     py:with="fullrow = len(row) == 1">

Modified: bloodhound/vendor/trac/current/trac/ticket/templates/ticket_box.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/ticket_box.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/ticket_box.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/ticket_box.html Fri 
Nov 14 11:06:23 2014
@@ -1,4 +1,13 @@
-<!--!
+<!--!  Copyright (C) 2010-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
 Ticket Box (ticket fields along with description).
 
 Arguments:

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/ticket_change.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/ticket_change.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/ticket_change.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/ticket_change.html Fri 
Nov 14 11:06:23 2014
@@ -1,4 +1,13 @@
-<!--!
+<!--!  Copyright (C) 2010-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
 Render a ticket comment.
 
 Arguments:
@@ -106,7 +115,7 @@ Arguments:
   <form py:if="show_editor" id="trac-comment-editor" method="post"
         action="${href.ticket(ticket.id) + '#comment:%d' % cnum}">
     <div>
-      <textarea name="edited_comment" class="wikitext trac-resizable" 
rows="10" cols="78">
+      <textarea name="edited_comment" class="wikitext trac-fullwidth 
trac-resizable" rows="10" cols="78">
 ${edited_comment if edited_comment is not None else change.comment}</textarea>
       <input type="hidden" name="cnum_edit" value="${cnum}"/>
     </div>

Modified: 
bloodhound/vendor/trac/current/trac/ticket/templates/ticket_preview.html
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/templates/ticket_preview.html?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/templates/ticket_preview.html 
(original)
+++ bloodhound/vendor/trac/current/trac/ticket/templates/ticket_preview.html 
Fri Nov 14 11:06:23 2014
@@ -1,4 +1,13 @@
-<!--!
+<!--!  Copyright (C) 2011-2014 Edgewall Software
+
+  This software is licensed as described in the file COPYING, which
+  you should have received as part of this distribution. The terms
+  are also available at http://trac.edgewall.com/license.html.
+
+  This software consists of voluntary contributions made by many
+  individuals. For the exact contribution history, see the revision
+  history and logs, available at http://trac.edgewall.org/.
+
 Render data relevant to automatic ticket preview.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml";

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/api.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/api.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/api.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/api.py Fri Nov 14 11:06:23 
2014
@@ -12,9 +12,9 @@
 # history and logs, available at http://trac.edgewall.org/log/.
 
 from trac.perm import PermissionCache, PermissionSystem
+from trac.test import EnvironmentStub, Mock
 from trac.ticket.api import TicketSystem
 from trac.ticket.model import Ticket
-from trac.test import EnvironmentStub, Mock
 
 import unittest
 

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/conversion.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/conversion.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/conversion.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/conversion.py Fri Nov 14 
11:06:23 2014
@@ -15,10 +15,10 @@ import os
 import unittest
 
 from trac import __version__ as TRAC_VERSION
-from trac.test import EnvironmentStub, Mock
+from trac.mimeview.api import Mimeview
+from trac.test import EnvironmentStub, Mock, MockPerm
 from trac.ticket.model import Ticket
 from trac.ticket.web_ui import TicketModule
-from trac.mimeview.api import Mimeview
 from trac.web.href import Href
 
 
@@ -50,6 +50,16 @@ class TicketConversionTestCase(unittest.
         ticket.insert()
         return ticket
 
+    def _create_a_ticket_with_email(self):
+        ticket = Ticket(self.env)
+        ticket['owner'] = 'j...@example.org'
+        ticket['reporter'] = 'sa...@example.org'
+        ticket['cc'] = 'cc1, c...@example.org'
+        ticket['summary'] = 'Foo'
+        ticket['description'] = 'Bar'
+        ticket.insert()
+        return ticket
+
     def test_conversions(self):
         conversions = self.mimeview.get_supported_conversions(
             'trac.ticket.Ticket')
@@ -74,6 +84,26 @@ class TicketConversionTestCase(unittest.
                           'keywords,cc\r\n1,Foo,santa,,Bar,,,\r\n',
                           'text/csv;charset=utf-8', 'csv'), csv)
 
+    def test_csv_conversion_with_obfuscation(self):
+        ticket = self._create_a_ticket_with_email()
+        csv = self.mimeview.convert_content(self.req, 'trac.ticket.Ticket',
+                                            ticket, 'csv')
+        self.assertEqual(
+            ('\xef\xbb\xbf'
+             'id,summary,reporter,owner,description,status,keywords,cc\r\n'
+             '1,Foo,santa@…,joe@…,Bar,,,cc1 cc2@…\r\n',
+             'text/csv;charset=utf-8', 'csv'),
+            csv)
+        self.req.perm = MockPerm()
+        csv = self.mimeview.convert_content(self.req, 'trac.ticket.Ticket',
+                                            ticket, 'csv')
+        self.assertEqual(
+            ('\xef\xbb\xbf'
+             'id,summary,reporter,owner,description,status,keywords,cc\r\n'
+             '1,Foo,sa...@example.org,j...@example.org,Bar,,,'
+             'cc1 c...@example.org\r\n',
+             'text/csv;charset=utf-8', 'csv'),
+            csv)
 
     def test_tab_conversion(self):
         ticket = self._create_a_ticket()
@@ -85,6 +115,29 @@ class TicketConversionTestCase(unittest.
                           'text/tab-separated-values;charset=utf-8', 'tsv'),
                          csv)
 
+    def test_tab_conversion_with_obfuscation(self):
+        ticket = self._create_a_ticket_with_email()
+        csv = self.mimeview.convert_content(self.req, 'trac.ticket.Ticket',
+                                            ticket, 'tab')
+        self.assertEqual(
+            ('\xef\xbb\xbf'
+             'id\tsummary\treporter\towner\tdescription\tstatus\tkeywords\t'
+             'cc\r\n'
+             '1\tFoo\tsanta@…\tjoe@…\tBar\t\t\tcc1 cc2@…\r\n',
+             'text/tab-separated-values;charset=utf-8', 'tsv'),
+            csv)
+        self.req.perm = MockPerm()
+        csv = self.mimeview.convert_content(self.req, 'trac.ticket.Ticket',
+                                            ticket, 'tab')
+        self.assertEqual(
+            ('\xef\xbb\xbf'
+             'id\tsummary\treporter\towner\tdescription\tstatus\tkeywords\t'
+             'cc\r\n'
+             '1\tFoo\tsa...@example.org\t...@example.org\tBar\t\t\t'
+             'cc1 c...@example.org\r\n',
+             'text/tab-separated-values;charset=utf-8', 'tsv'),
+            csv)
+
     def test_rss_conversion(self):
         ticket = self._create_a_ticket()
         content, mimetype, ext = self.mimeview.convert_content(

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/functional.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/functional.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/functional.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/functional.py Fri Nov 14 
11:06:23 2014
@@ -20,8 +20,8 @@ from datetime import datetime, timedelta
 from trac.admin.tests.functional import AuthorizationTestCaseSetup
 from trac.test import locale_en
 from trac.tests.functional import *
-from trac.util.datefmt import utc, localtz, format_date, format_datetime, \
-                              pretty_timedelta
+from trac.util import create_file
+from trac.util.datefmt import utc, localtz, format_date, format_datetime
 from trac.util.text import to_utf8
 
 try:
@@ -116,6 +116,53 @@ class TestTicketNoSummary(FunctionalTwil
         tc.find('ticket not yet created')
 
 
+class TestTicketManipulator(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        plugin_name = self.__class__.__name__
+        env = self._testenv.get_trac_environment()
+        env.config.set('components', plugin_name + '.*', 'enabled')
+        env.config.save()
+        create_file(os.path.join(env.path, 'plugins', plugin_name + '.py'),
+"""\
+from genshi.builder import tag
+from trac.core import Component, implements
+from trac.ticket.api import ITicketManipulator
+from trac.util.translation import tag_
+
+
+class TicketManipulator(Component):
+    implements(ITicketManipulator)
+
+    def prepare_ticket(self, req, ticket, fields, actions):
+        pass
+
+    def validate_ticket(self, req, ticket):
+        field = 'reporter'
+        yield None, tag_("A ticket with the summary %(summary)s"
+                         " already exists.",
+                          summary=tag.em("Testing ticket manipulator"))
+        yield field, tag_("The ticket %(field)s is %(status)s.",
+                          field=tag.strong(field),
+                          status=tag.em("invalid"))
+""")
+        self._testenv.restart()
+
+        try:
+            self._tester.go_to_front()
+            tc.follow("New Ticket")
+            tc.formvalue('propertyform', 'field-description',
+                         "Testing ticket manipulator")
+            tc.submit('submit')
+            tc.url(self._tester.url + '/newticket$')
+            tc.find("A ticket with the summary <em>Testing ticket "
+                    "manipulator</em> already exists.")
+            tc.find("The ticket field 'reporter' is invalid: The"
+                    " ticket <strong>reporter</strong> is <em>invalid</em>.")
+        finally:
+            env.config.set('components', plugin_name + '.*', 'disabled')
+            env.config.save()
+
+
 class TestTicketAltFormats(FunctionalTestCaseSetup):
     def runTest(self):
         """Download ticket in alternative formats"""
@@ -1811,9 +1858,8 @@ class RegressionTestTicket6048(Functiona
     def runTest(self):
         """Test for regression of http://trac.edgewall.org/ticket/6048""";
         # Setup the DeleteTicket plugin
-        plugin = open(os.path.join(self._testenv.command_cwd,
-                                   'sample-plugins', 'workflow',
-                                   'DeleteTicket.py')).read()
+        plugin = open(os.path.join(self._testenv.trac_src, 'sample-plugins',
+                                   'workflow', 'DeleteTicket.py')).read()
         open(os.path.join(self._testenv.tracdir, 'plugins',
                           'DeleteTicket.py'), 'w').write(plugin)
         env = self._testenv.get_trac_environment()
@@ -2025,7 +2071,7 @@ class RegressionTestTicket8247(Functiona
         tc.find('<strong class="trac-field-milestone">Milestone</strong>'
                 '[ \n\t]*<em>%s</em> deleted' % name)
         tc.find('Changed <a.* ago</a> by admin')
-        tc.notfind('anonymous')
+        tc.notfind('</a> ago by anonymous')
 
 
 class RegressionTestTicket8861(FunctionalTwillTestCaseSetup):
@@ -2108,9 +2154,9 @@ class RegressionTestTicket11028(Function
                                      ('ROADMAP_VIEW', 'MILESTONE_VIEW'))
 
 
-class RegressionTestTicket11153(FunctionalTwillTestCaseSetup):
+class RegressionTestTicket11152(FunctionalTwillTestCaseSetup):
     def runTest(self):
-        """Test for regression of http://trac.edgewall.org/ticket/11153""";
+        """Test for regression of http://trac.edgewall.org/ticket/11152""";
         # Check that "View Tickets" mainnav entry links to the report page
         self._tester.go_to_view_tickets()
 
@@ -2143,6 +2189,19 @@ class RegressionTestTicket11153(Function
             env.config.remove('components', 'trac.ticket.report.ReportModule')
             env.config.save()
 
+        # Disable the QueryModule component and check that "View Tickets"
+        # mainnav entry links to the `/report` page
+        env.config.set('components', 'trac.ticket.query.QueryModule',
+                       'disabled')
+        env.config.save()
+
+        try:
+            self._tester.go_to_view_tickets('report')
+            tc.notfind('<li class="last first">Available Reports</li>')
+        finally:
+            env.config.remove('components', 'trac.ticket.query.QueryModule')
+            env.config.save()
+
 
 class RegressionTestTicket11176(FunctionalTestCaseSetup):
     def runTest(self):
@@ -2197,6 +2256,40 @@ class RegressionTestTicket11176(Function
             self._testenv.disable_authz_permpolicy()
 
 
+class RegressionTestTicket11590(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11590""";
+        report_id = self._tester.create_report('#11590', 'SELECT 1',
+                                               '[./ this report]')
+        self._tester.go_to_view_tickets()
+        tc.notfind(internal_error)
+        tc.find('<a class="report" href="[^>"]*?/report/%s">this report</a>' %
+                report_id)
+
+
+class RegressionTestTicket11618(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for regression of http://trac.edgewall.org/ticket/11618
+        fix for malformed `readonly="True"` attribute in milestone admin page
+        """
+        name = "11618Milestone"
+        self._tester.create_milestone(name)
+        try:
+            self._testenv.grant_perm('user', 'TICKET_ADMIN')
+            self._tester.go_to_front()
+            self._tester.logout()
+            self._tester.login('user')
+            tc.go(self._tester.url + "/admin/ticket/milestones/" + name)
+            tc.notfind('No administration panels available')
+            tc.find(' readonly="readonly"')
+            tc.notfind(' readonly="True"')
+        finally:
+            self._testenv.revoke_perm('user', 'TICKET_ADMIN')
+            self._tester.go_to_front()
+            self._tester.logout()
+            self._tester.login('admin')
+
+
 def functionalSuite(suite=None):
     if not suite:
         import trac.tests.functional
@@ -2206,6 +2299,7 @@ def functionalSuite(suite=None):
     suite.addTest(TestTicketAddAttachment())
     suite.addTest(TestTicketPreview())
     suite.addTest(TestTicketNoSummary())
+    suite.addTest(TestTicketManipulator())
     suite.addTest(TestTicketAltFormats())
     suite.addTest(TestTicketCSVFormat())
     suite.addTest(TestTicketTabFormat())
@@ -2313,7 +2407,9 @@ def functionalSuite(suite=None):
     suite.addTest(RegressionTestTicket9084())
     suite.addTest(RegressionTestTicket9981())
     suite.addTest(RegressionTestTicket11028())
-    suite.addTest(RegressionTestTicket11153())
+    suite.addTest(RegressionTestTicket11152())
+    suite.addTest(RegressionTestTicket11590())
+    suite.addTest(RegressionTestTicket11618())
     if ConfigObj:
         suite.addTest(RegressionTestTicket11176())
     else:

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/model.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/model.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/model.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/model.py Fri Nov 14 
11:06:23 2014
@@ -19,18 +19,19 @@ import tempfile
 import shutil
 import unittest
 
+import trac.tests.compat
 from trac import core
 from trac.attachment import Attachment
 from trac.core import TracError, implements
 from trac.resource import ResourceNotFound
-from trac.tests import compat
+from trac.test import EnvironmentStub
 from trac.ticket.model import (
     Ticket, Component, Milestone, Priority, Type, Version
 )
+from trac.ticket.roadmap import MilestoneModule
 from trac.ticket.api import (
     IMilestoneChangeListener, ITicketChangeListener, TicketSystem
 )
-from trac.test import EnvironmentStub
 from trac.util.datefmt import from_utimestamp, to_utimestamp, utc
 
 
@@ -53,6 +54,36 @@ class TestTicketChangeListener(core.Comp
         self.action = 'deleted'
         self.ticket = ticket
 
+    # the listener has no ticket_comment_modified and ticket_change_deleted
+
+
+class TestTicketChangeListener_2(core.Component):
+    implements(ITicketChangeListener)
+
+    def ticket_created(self, ticket):
+        pass
+
+    def ticket_changed(self, ticket, comment, author, old_values):
+        pass
+
+    def ticket_deleted(self, ticket):
+        pass
+
+    def ticket_comment_modified(self, ticket, cdate, author, comment,
+                                old_comment):
+        self.action = 'comment_modified'
+        self.ticket = ticket
+        self.cdate = cdate
+        self.author = author
+        self.comment = comment
+        self.old_comment = old_comment
+
+    def ticket_change_deleted(self, ticket, cdate, changes):
+        self.action = 'change_deleted'
+        self.ticket = ticket
+        self.cdate = cdate
+        self.changes = changes
+
 
 class TicketTestCase(unittest.TestCase):
 
@@ -611,6 +642,19 @@ class TicketCommentEditTestCase(TicketCo
             self.assertEqual((i, t[i], 'joe (%d)' % i,
                              'Comment 1 (%d)' % i), history[i])
 
+    def test_change_listener_comment_modified(self):
+        listener = TestTicketChangeListener_2(self.env)
+        ticket = Ticket(self.env, self.id)
+        ticket.modify_comment(cdate=self.t2, author='jack',
+                              comment='New Comment 2', when=datetime.now(utc))
+
+        self.assertEqual('comment_modified', listener.action)
+        self.assertEqual(ticket, listener.ticket)
+        self.assertEqual(self.t2, listener.cdate)
+        self.assertEqual('jack', listener.author)
+        self.assertEqual('New Comment 2', listener.comment)
+        self.assertEqual('Comment 2', listener.old_comment)
+
 
 class TicketCommentDeleteTestCase(TicketCommentTestCase):
 
@@ -746,6 +790,25 @@ class TicketCommentDeleteTestCase(Ticket
         ticket.delete_change(1, when=t)
         self.assertEqual(t, ticket.time_changed)
 
+    def test_ticket_change_deleted(self):
+        listener = TestTicketChangeListener_2(self.env)
+        ticket = Ticket(self.env, self.id)
+
+        ticket.delete_change(cdate=self.t3, when=datetime.now(utc))
+        self.assertEqual('change_deleted', listener.action)
+        self.assertEqual(ticket, listener.ticket)
+        self.assertEqual(self.t3, listener.cdate)
+        self.assertEqual(dict(keywords=('a, b, c', 'a, b'),
+                              foo=('change2', 'change3')),
+                         listener.changes)
+
+        ticket.delete_change(cnum=2, when=datetime.now(utc))
+        self.assertEqual('change_deleted', listener.action)
+        self.assertEqual(ticket, listener.ticket)
+        self.assertEqual(self.t2, listener.cdate)
+        self.assertEqual(dict(owner=('john', 'jack'),
+                              foo=('change 1', 'change2')),
+                         listener.changes)
 
 class EnumTestCase(unittest.TestCase):
 
@@ -974,6 +1037,20 @@ class MilestoneTestCase(unittest.TestCas
         self.assertEqual(tkt1['changetime'], tkt2['changetime'])
         self.assertNotEqual(self.updated_at, tkt1['changetime'])
 
+    def test_delete_milestone_with_attachment(self):
+        milestone = Milestone(self.env)
+        milestone.name = 'MilestoneWithAttachment'
+        milestone.insert()
+        
+        attachment = Attachment(self.env, 'milestone', milestone.name)
+        attachment.insert('foo.txt', StringIO(), 0, 1)
+
+        milestone.delete()
+        self.assertEqual(False, milestone.exists)
+
+        attachments = Attachment.select(self.env, 'milestone', milestone.name)
+        self.assertRaises(StopIteration, attachments.next)
+
     def test_delete_milestone_retarget_tickets(self):
         self.env.db_transaction.executemany(
             "INSERT INTO milestone (name) VALUES (%s)",

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/notification.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/notification.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/notification.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/notification.py Fri Nov 14 
11:06:23 2014
@@ -23,10 +23,10 @@ import re
 import unittest
 from datetime import datetime
 
+import trac.tests.compat
 from trac.test import EnvironmentStub, Mock, MockPerm
-from trac.tests import compat
 from trac.tests.notification import SMTP_TEST_PORT, SMTPThreadedServer,\
-                                    parse_smtp_message, smtp_address
+                                    parse_smtp_message
 from trac.ticket.model import Ticket
 from trac.ticket.notification import TicketNotifyEmail
 from trac.ticket.web_ui import TicketModule
@@ -36,85 +36,202 @@ MAXBODYWIDTH = 76
 notifysuite = None
 
 
-class NotificationTestCase(unittest.TestCase):
-    """Notification test cases that send email over SMTP"""
+class RecipientTestCase(unittest.TestCase):
+    """Notification test cases for email recipients."""
 
     def setUp(self):
         self.env = EnvironmentStub(default_data=True)
         self.env.config.set('project', 'name', 'TracTest')
         self.env.config.set('notification', 'smtp_enabled', 'true')
-        self.env.config.set('notification', 'always_notify_owner', 'true')
-        self.env.config.set('notification', 'always_notify_reporter', 'true')
-        self.env.config.set('notification', 'smtp_always_cc',
-                            'joe.u...@example.net, joe....@example.net')
-        self.env.config.set('notification', 'use_public_cc', 'true')
         self.env.config.set('notification', 'smtp_port', str(SMTP_TEST_PORT))
-        self.env.config.set('notification', 'smtp_server', 'localhost')
-        self.req = Mock(href=self.env.href, abs_href=self.env.abs_href, tz=utc,
-                        perm=MockPerm())
 
     def tearDown(self):
-        """Signal the notification test suite that a test is over"""
         notifysuite.tear_down()
         self.env.reset_db()
 
-    def test_recipients(self):
-        """To/Cc recipients"""
+    def test_no_recipients(self):
+        """No recipient case"""
         ticket = Ticket(self.env)
-        ticket['reporter'] = '"Joe User" < joe.u...@example.org >'
-        ticket['owner'] = 'joe.u...@example.net'
-        ticket['cc'] = 'joe.u...@example.com, joe....@example.org, ' \
-                       'joe....@example.net'
+        ticket['reporter'] = 'anonymous'
         ticket['summary'] = 'Foo'
         ticket.insert()
         tn = TicketNotifyEmail(self.env)
         tn.notify(ticket, newticket=True)
         recipients = notifysuite.smtpd.get_recipients()
-        # checks there is no duplicate in the recipient list
-        rcpts = []
-        for r in recipients:
-            self.assertNotIn(r, rcpts)
-            rcpts.append(r)
-        # checks that all cc recipients have been notified
-        cc_list = self.env.config.get('notification', 'smtp_always_cc')
-        cc_list = "%s, %s" % (cc_list, ticket['cc'])
-        for r in cc_list.replace(',', ' ').split():
-            self.assertIn(r, recipients)
-        # checks that owner has been notified
-        self.assertIn(smtp_address(ticket['owner']), recipients)
-        # checks that reporter has been notified
-        self.assertIn(smtp_address(ticket['reporter']), recipients)
+        sender = notifysuite.smtpd.get_sender()
+        message = notifysuite.smtpd.get_message()
+        self.assertEqual(0, len(recipients))
+        self.assertIsNone(sender)
+        self.assertIsNone(message)
 
-    def test_no_recipient(self):
-        """No recipient case"""
-        self.env.config.set('notification', 'smtp_always_cc', '')
+    def test_new_ticket_recipients(self):
+        """Report and CC list should be in recipient list for new tickets."""
+        always_cc = ('joe.u...@example.net', 'joe....@example.net')
+        ticket_cc = ('joe.u...@example.com', 'joe....@example.org')
+        self.env.config.set('notification', 'smtp_always_cc',
+                            ', '.join(always_cc))
         ticket = Ticket(self.env)
-        ticket['reporter'] = 'anonymous'
-        ticket['summary'] = 'Foo'
+        ticket['reporter'] = 'joe....@example.org'
+        ticket['owner'] = 'joe.u...@example.net'
+        ticket['cc'] = ' '.join(ticket_cc)
+        ticket['summary'] = 'New ticket recipients'
         ticket.insert()
         tn = TicketNotifyEmail(self.env)
         tn.notify(ticket, newticket=True)
-        sender = notifysuite.smtpd.get_sender()
         recipients = notifysuite.smtpd.get_recipients()
-        message = notifysuite.smtpd.get_message()
-        # checks that no message has been sent
-        self.assertEqual([], recipients)
-        self.assertIsNone(sender)
-        self.assertIsNone(message)
+        for r in always_cc + ticket_cc + \
+                (ticket['owner'], ticket['reporter']):
+            self.assertIn(r, recipients)
 
     def test_cc_only(self):
         """Notification w/o explicit recipients but Cc: (#3101)"""
+        always_cc = ('joe.u...@example.net', 'joe....@example.net')
+        self.env.config.set('notification', 'smtp_always_cc',
+                            ', '.join(always_cc))
         ticket = Ticket(self.env)
         ticket['summary'] = 'Foo'
         ticket.insert()
         tn = TicketNotifyEmail(self.env)
         tn.notify(ticket, newticket=True)
         recipients = notifysuite.smtpd.get_recipients()
-        # checks that all cc recipients have been notified
-        cc_list = self.env.config.get('notification', 'smtp_always_cc')
-        for r in cc_list.replace(',', ' ').split():
+        for r in always_cc:
             self.assertIn(r, recipients)
 
+    def test_always_notify_updater(self):
+        """The `always_notify_updater` option."""
+        def _test_updater(enabled):
+            self.env.config.set('notification', 'always_notify_updater',
+                                enabled)
+            ticket = Ticket(self.env)
+            ticket['reporter'] = 'joe.u...@example.org'
+            ticket['summary'] = u'This is a súmmäry'
+            ticket.insert()
+            now = datetime.now(utc)
+            ticket.save_changes('joe.b...@example.com', 'This is a change',
+                                when=now)
+            tn = TicketNotifyEmail(self.env)
+            tn.notify(ticket, newticket=False, modtime=now)
+            recipients = notifysuite.smtpd.get_recipients()
+            if enabled:
+                self.assertEqual(1, len(recipients))
+                self.assertIn('joe.b...@example.com', recipients)
+            else:
+                self.assertEqual(0, len(recipients))
+                self.assertNotIn('joe.b...@example.com', recipients)
+
+        # Validate with and without a default domain
+        for enable in False, True:
+            _test_updater(enable)
+
+    def test_always_notify_owner(self):
+        """The `always_notify_owner` option."""
+        def _test_reporter(enabled):
+            self.env.config.set('notification', 'always_notify_owner',
+                                enabled)
+            self.env.config.set('notification', 'always_notify_updater',
+                                'false')
+            ticket = Ticket(self.env)
+            ticket['summary'] = 'Foo'
+            ticket['reporter'] = u'j...@example.org'
+            ticket['owner'] = u'j...@example.org'
+            ticket.insert()
+            now = datetime.now(utc)
+            ticket.save_changes('j...@example.org', 'this is my comment',
+                                when=now)
+            tn = TicketNotifyEmail(self.env)
+            tn.notify(ticket, newticket=True, modtime=now)
+            recipients = notifysuite.smtpd.get_recipients()
+            if enabled:
+                self.assertEqual(1, len(recipients))
+                self.assertEqual('j...@example.org', recipients[0])
+            else:
+                self.assertEqual(0, len(recipients))
+
+        for enable in False, True:
+            _test_reporter(enable)
+
+    def test_always_notify_reporter(self):
+        """Notification to reporter w/ updater option disabled (#3780)"""
+        def _test_reporter(enabled):
+            self.env.config.set('notification', 'always_notify_updater',
+                                'false')
+            self.env.config.set('notification', 'always_notify_reporter',
+                                enabled)
+            ticket = Ticket(self.env)
+            ticket['summary'] = 'Foo'
+            ticket['reporter'] = u'j...@example.org'
+            ticket.insert()
+            now = datetime.now(utc)
+            ticket.save_changes('j...@example.org', 'this is my comment',
+                                when=now)
+            tn = TicketNotifyEmail(self.env)
+            tn.notify(ticket, newticket=True, modtime=now)
+            recipients = notifysuite.smtpd.get_recipients()
+            if enabled:
+                self.assertEqual(1, len(recipients))
+                self.assertEqual('j...@example.org', recipients[0])
+            else:
+                self.assertEqual(0, len(recipients))
+
+        for enable in False, True:
+            _test_reporter(enable)
+
+    def test_no_duplicates(self):
+        """Email addresses should be found only once in the recipient list."""
+        self.env.config.set('notification', 'smtp_always_cc',
+                            'joe.u...@example.com')
+        ticket = Ticket(self.env)
+        ticket['reporter'] = 'joe.u...@example.com'
+        ticket['owner'] = 'joe.u...@example.com'
+        ticket['cc'] = 'joe.u...@example.com'
+        ticket['summary'] = 'No duplicates'
+        ticket.insert()
+        tn = TicketNotifyEmail(self.env)
+        tn.notify(ticket, newticket=True)
+        recipients = notifysuite.smtpd.get_recipients()
+        self.assertEqual(1, len(recipients))
+        self.assertIn('joe.u...@example.com', recipients)
+
+    def test_long_forms(self):
+        """Long forms of SMTP email addresses 'Display Name <address>'"""
+        self.env.config.set('notification', 'always_notify_owner', True)
+        ticket = Ticket(self.env)
+        ticket['reporter'] = '"Joe" <joe.u...@example.com>'
+        ticket['owner'] = 'Joe <joe.u...@example.net>'
+        ticket['cc'] = 'Joe < joe.u...@example.org >'
+        ticket['summary'] = 'Long form'
+        ticket.insert()
+        tn = TicketNotifyEmail(self.env)
+        tn.notify(ticket, newticket=True)
+        recipients = notifysuite.smtpd.get_recipients()
+        self.assertEqual(3, len(recipients))
+        self.assertIn('joe.u...@example.com', recipients)
+        self.assertIn('joe.u...@example.net', recipients)
+        self.assertIn('joe.u...@example.org', recipients)
+
+
+class NotificationTestCase(unittest.TestCase):
+    """Notification test cases that send email over SMTP"""
+
+    def setUp(self):
+        self.env = EnvironmentStub(default_data=True)
+        self.env.config.set('project', 'name', 'TracTest')
+        self.env.config.set('notification', 'smtp_enabled', 'true')
+        self.env.config.set('notification', 'always_notify_owner', 'true')
+        self.env.config.set('notification', 'always_notify_reporter', 'true')
+        self.env.config.set('notification', 'smtp_always_cc',
+                            'joe.u...@example.net, joe....@example.net')
+        self.env.config.set('notification', 'use_public_cc', 'true')
+        self.env.config.set('notification', 'smtp_port', str(SMTP_TEST_PORT))
+        self.env.config.set('notification', 'smtp_server', 'localhost')
+        self.req = Mock(href=self.env.href, abs_href=self.env.abs_href, tz=utc,
+                        perm=MockPerm())
+
+    def tearDown(self):
+        """Signal the notification test suite that a test is over"""
+        notifysuite.tear_down()
+        self.env.reset_db()
+
     def test_structure(self):
         """Basic SMTP message structure (headers, body)"""
         ticket = Ticket(self.env)
@@ -174,10 +291,9 @@ class NotificationTestCase(unittest.Test
 
     def test_bcc_privacy(self):
         """Visibility of recipients"""
-        def run_bcc_feature(public):
+        def run_bcc_feature(public_cc):
             # CC list should be private
-            self.env.config.set('notification', 'use_public_cc',
-                                'true' if public else 'false')
+            self.env.config.set('notification', 'use_public_cc', public_cc)
             self.env.config.set('notification', 'smtp_always_bcc',
                                 'joe.foo...@example.net')
             ticket = Ticket(self.env)
@@ -188,7 +304,7 @@ class NotificationTestCase(unittest.Test
             tn.notify(ticket, newticket=True)
             message = notifysuite.smtpd.get_message()
             headers, body = parse_smtp_message(message)
-            if public:
+            if public_cc:
                 # Msg should have a To list
                 self.assertIn('To', headers)
                 # Extract the list of 'To' recipients from the message
@@ -220,8 +336,8 @@ class NotificationTestCase(unittest.Test
                 self.assertNotIn(rcpt, to)
                 # Check the message has actually been sent to the recipients
                 self.assertIn(rcpt, rcptlist)
-        run_bcc_feature(True)
-        run_bcc_feature(False)
+        for public in False, True:
+            run_bcc_feature(public)
 
     def test_short_login(self):
         """Email addresses without a FQDN"""
@@ -234,8 +350,7 @@ class NotificationTestCase(unittest.Test
             # send a notification even if other addresses are not valid
             self.env.config.set('notification', 'smtp_always_cc',
                                 'joe....@example.net')
-            if enabled:
-                self.env.config.set('notification', 'use_short_addr', 'true')
+            self.env.config.set('notification', 'use_short_addr', enabled)
             tn = TicketNotifyEmail(self.env)
             tn.notify(ticket, newticket=True)
             message = notifysuite.smtpd.get_message()
@@ -289,12 +404,12 @@ class NotificationTestCase(unittest.Test
             cclist = [addr.strip() for addr in headers['Cc'].split(',')]
             self.assertIn('joewith...@example.com', cclist)
             self.assertIn('joe....@example.net', cclist)
-            if not enabled:
-                self.assertEqual(2, len(cclist))
-                self.assertNotIn('joenodom', cclist)
-            else:
+            if enabled:
                 self.assertEqual(3, len(cclist))
                 self.assertIn('joeno...@example.org', cclist)
+            else:
+                self.assertEqual(2, len(cclist))
+                self.assertNotIn('joenodom', cclist)
 
         # Validate with and without a default domain
         for enable in False, True:
@@ -517,88 +632,47 @@ class NotificationTestCase(unittest.Test
         tn.notify(ticket, newticket=True)
         message = notifysuite.smtpd.get_message()
         headers, body = parse_smtp_message(message)
+        self.assertEqual('joe.u...@example.org', headers['To'])
+
+    def test_previous_cc_list(self):
+        """Members removed from CC list receive notifications"""
+        ticket = Ticket(self.env)
+        ticket['summary'] = 'Foo'
+        ticket['cc'] = 'joe.us...@example.net'
+        ticket.insert()
+        ticket['cc'] = 'joe.us...@example.net'
+        now = datetime.now(utc)
+        ticket.save_changes('joe....@example.com', 'Removed from cc', now)
+        tn = TicketNotifyEmail(self.env)
+        tn.notify(ticket, newticket=False, modtime=now)
+        recipients = notifysuite.smtpd.get_recipients()
+        self.assertIn('joe.us...@example.net', recipients)
+        self.assertIn('joe.us...@example.net', recipients)
 
-    def test_updater(self):
-        """No-self-notification option"""
-        def _test_updater(disabled):
-            if disabled:
-                self.env.config.set('notification', 'always_notify_updater',
-                                    'false')
+    def test_previous_owner(self):
+        """Previous owner is notified when ticket is reassigned (#2311)
+           if always_notify_owner is set to True"""
+        def _test_owner(enabled):
+            self.env.config.set('notification', 'always_notify_owner', enabled)
             ticket = Ticket(self.env)
-            ticket['reporter'] = 'joe.u...@example.org'
-            ticket['summary'] = u'This is a súmmäry'
-            ticket['cc'] = 'joe....@example.com'
+            ticket['summary'] = 'Foo'
+            ticket['owner'] = prev_owner = 'joe.us...@example.net'
             ticket.insert()
-            ticket['component'] = 'dummy'
+            ticket['owner'] = new_owner = 'joe.us...@example.net'
             now = datetime.now(utc)
-            ticket.save_changes('joe.b...@example.com', 'This is a change',
-                                when=now)
+            ticket.save_changes('joe....@example.com', 'Changed owner', now)
             tn = TicketNotifyEmail(self.env)
             tn.notify(ticket, newticket=False, modtime=now)
-            message = notifysuite.smtpd.get_message()
-            headers, body = parse_smtp_message(message)
-            # checks for header existence
-            self.assertTrue(headers)
-            # checks for updater in the 'To' recipient list
-            self.assertIn('To', headers)
-            tolist = [addr.strip() for addr in headers['To'].split(',')]
-            if disabled:
-                self.assertNotIn('joe.b...@example.com', tolist)
+            recipients = notifysuite.smtpd.get_recipients()
+            if enabled:
+                self.assertIn(prev_owner, recipients)
+                self.assertIn(new_owner, recipients)
             else:
-                self.assertIn('joe.b...@example.com', tolist)
-
-        # Validate with and without a default domain
-        for disable in False, True:
-            _test_updater(disable)
-
-    def test_updater_only(self):
-        """Notification w/ updater, w/o any other recipient (#4188)"""
-        self.env.config.set('notification', 'always_notify_owner', 'false')
-        self.env.config.set('notification', 'always_notify_reporter', 'false')
-        self.env.config.set('notification', 'always_notify_updater', 'true')
-        self.env.config.set('notification', 'smtp_always_cc', '')
-        self.env.config.set('notification', 'smtp_always_bcc', '')
-        self.env.config.set('notification', 'use_public_cc', 'false')
-        self.env.config.set('notification', 'use_short_addr', 'false')
-        self.env.config.set('notification', 'smtp_replyto',
-                            'joeu...@example.net')
-        ticket = Ticket(self.env)
-        ticket['summary'] = 'Foo'
-        ticket.insert()
-        ticket['summary'] = 'Bar'
-        ticket['component'] = 'New value'
-        ticket.save_changes('j...@example.com', 'this is my comment')
-        tn = TicketNotifyEmail(self.env)
-        tn.notify(ticket, newticket=True)
-        recipients = notifysuite.smtpd.get_recipients()
-        self.assertIsNotNone(recipients)
-        self.assertEqual(1, len(recipients))
-        self.assertEqual(recipients[0], 'j...@example.com')
+                self.assertNotIn(prev_owner, recipients)
+                self.assertNotIn(new_owner, recipients)
 
-    def test_updater_is_reporter(self):
-        """Notification to reporter w/ updater option disabled (#3780)"""
-        self.env.config.set('notification', 'always_notify_owner', 'false')
-        self.env.config.set('notification', 'always_notify_reporter', 'true')
-        self.env.config.set('notification', 'always_notify_updater', 'false')
-        self.env.config.set('notification', 'smtp_always_cc', '')
-        self.env.config.set('notification', 'smtp_always_bcc', '')
-        self.env.config.set('notification', 'use_public_cc', 'false')
-        self.env.config.set('notification', 'use_short_addr', 'false')
-        self.env.config.set('notification', 'smtp_replyto',
-                            'joeu...@example.net')
-        ticket = Ticket(self.env)
-        ticket['summary'] = 'Foo'
-        ticket['reporter'] = u'j...@example.org'
-        ticket.insert()
-        ticket['summary'] = 'Bar'
-        ticket['component'] = 'New value'
-        ticket.save_changes('j...@example.org', 'this is my comment')
-        tn = TicketNotifyEmail(self.env)
-        tn.notify(ticket, newticket=True)
-        recipients = notifysuite.smtpd.get_recipients()
-        self.assertIsNotNone(recipients)
-        self.assertEqual(1, len(recipients))
-        self.assertEqual('j...@example.org', recipients[0])
+        for enable in False, True:
+            _test_owner(enable)
 
     def _validate_mimebody(self, mime, ticket, newtk):
         """Body of a ticket notification message"""
@@ -1128,6 +1202,53 @@ Security sensitive:  0                  
         tn.ticket = ticket
         tn.get_message_id('foo')
 
+    def test_mime_meta_characters_in_from_header(self):
+        """MIME encoding with meta characters in From header"""
+
+        self.env.config.set('notification', 'smtp_from', 't...@example.com')
+        self.env.config.set('notification', 'mime_encoding', 'base64')
+        ticket = Ticket(self.env)
+        ticket['reporter'] = 'joeuser'
+        ticket['summary'] = 'This is a summary'
+        ticket.insert()
+        tn = TicketNotifyEmail(self.env)
+
+        def notify(from_name):
+            self.env.config.set('notification', 'smtp_from_name', from_name)
+            tn.notify(ticket, newticket=True)
+            message = notifysuite.smtpd.get_message()
+            headers, body = parse_smtp_message(message)
+            return message, headers, body
+
+        message, headers, body = notify(u'Träc')
+        self.assertEqual(r'"=?utf-8?b?VHLDpGM=?=" <t...@example.com>',
+                         headers['From'])
+        message, headers, body = notify(u'Trac\\')
+        self.assertEqual(r'"Trac\\" <t...@example.com>', headers['From'])
+        message, headers, body = notify(u'Trac"')
+        self.assertEqual(r'"Trac\"" <t...@example.com>', headers['From'])
+        message, headers, body = notify(u'=?utf-8?b?****?=')
+        self.assertEqual('"=?utf-8?b?PT91dGYtOD9iPyoqKio/PQ==?=" '
+                         '<t...@example.com>', headers['From'])
+
+    def test_mime_meta_characters_in_subject_header(self):
+        """MIME encoding with meta characters in Subject header"""
+
+        self.env.config.set('notification', 'smtp_from', 't...@example.com')
+        self.env.config.set('notification', 'mime_encoding', 'base64')
+        summary = u'=?utf-8?q?****?='
+        ticket = Ticket(self.env)
+        ticket['reporter'] = 'joeuser'
+        ticket['summary'] = summary
+        ticket.insert()
+        tn = TicketNotifyEmail(self.env)
+        tn.notify(ticket, newticket=True)
+        message = notifysuite.smtpd.get_message()
+        headers, body = parse_smtp_message(message)
+        self.assertIn('\nSubject: =?utf-8?b?', message)  # is mime-encoded
+        self.assertEqual(summary,
+                         re.split(r' #[0-9]+: ', headers['Subject'], 1)[1])
+
 
 class NotificationTestSuite(unittest.TestSuite):
     """Thin test suite wrapper to start and stop the SMTP test server"""
@@ -1137,6 +1258,7 @@ class NotificationTestSuite(unittest.Tes
         unittest.TestSuite.__init__(self)
         self.smtpd = SMTPThreadedServer(SMTP_TEST_PORT)
         self.smtpd.start()
+        self.addTest(unittest.makeSuite(RecipientTestCase))
         self.addTest(unittest.makeSuite(NotificationTestCase))
         self.remaining = self.countTestCases()
 

Modified: bloodhound/vendor/trac/current/trac/ticket/tests/query.py
URL: 
http://svn.apache.org/viewvc/bloodhound/vendor/trac/current/trac/ticket/tests/query.py?rev=1639602&r1=1639601&r2=1639602&view=diff
==============================================================================
--- bloodhound/vendor/trac/current/trac/ticket/tests/query.py (original)
+++ bloodhound/vendor/trac/current/trac/ticket/tests/query.py Fri Nov 14 
11:06:23 2014
@@ -18,6 +18,7 @@ from trac.util.datefmt import utc
 from trac.web.chrome import web_context
 from trac.web.href import Href
 from trac.wiki.formatter import LinkFormatter
+from trac.wiki.tests import formatter
 
 import unittest
 import difflib
@@ -581,6 +582,31 @@ ORDER BY COALESCE(t.id,0)=0,t.id""")
         self.assertEqual('\xef\xbb\xbfcol1\r\n"value, needs escaped"\r\n',
                          content)
 
+    def test_csv_obfuscation(self):
+        class NoEmailView(MockPerm):
+            def has_permission(self, action, realm_or_resource=None, id=False,
+                               version=False):
+                return action != 'EMAIL_VIEW'
+            __contains__ = has_permission
+
+        query = Mock(get_columns=lambda: ['owner', 'reporter', 'cc'],
+                     execute=lambda r: [{'id': 1,
+                                         'owner': 'j...@example.org',
+                                         'reporter': 'f...@example.org',
+                                         'cc': 'c...@example.org, cc2'}],
+                     time_fields=['time', 'changetime'])
+        req = Mock(href=self.env.href, perm=NoEmailView())
+        content, mimetype = QueryModule(self.env).export_csv(req, query)
+        self.assertEqual(u'\uFEFFowner,reporter,cc\r\n'
+                         u'joe@…,foo@…,"cc1@…, cc2"\r\n',
+                         content.decode('utf-8'))
+        req = Mock(href=self.env.href, perm=MockPerm())
+        content, mimetype = QueryModule(self.env).export_csv(req, query)
+        self.assertEqual(
+            u'\uFEFFowner,reporter,cc\r\n'
+            u'j...@example.org,f...@example.org,"c...@example.org, cc2"\r\n',
+            content.decode('utf-8'))
+
     def test_template_data(self):
         req = Mock(href=self.env.href, perm=MockPerm(), authname='anonymous',
                    tz=None, locale=None)
@@ -654,12 +680,267 @@ class TicketQueryMacroTestCase(unittest.
                            dict(col='status|summary', max='0', order='id'),
                            'list')
 
+QUERY_TEST_CASES = u"""
+============================== TicketQuery(format=progress)
+[[TicketQuery(format=progress)]]
+------------------------------
+<p>
+</p><div class="trac-progress">
+
+  <table xmlns="http://www.w3.org/1999/xhtml"; class="progress">
+    <tr>
+      <td class="closed" style="width: 33%">
+        <a 
href="/query?status=closed&amp;group=resolution&amp;max=0&amp;order=time" 
title="1/3 closed"></a>
+      </td><td class="open" style="width: 67%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id"
 title="2/3 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">33%</p>
+
+  <p class="legend">
+    <span class="first interval">
+      <a href="/query?max=0&amp;order=id">Total number of tickets: 3</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;group=resolution&amp;max=0&amp;order=time">closed:
 1</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id">active:
 2</a>
+    </span>
+  </p>
+</div><p>
+</p>
+------------------------------
+============================== TicketQuery(reporter=santa, format=progress)
+[[TicketQuery(reporter=santa, format=progress)]]
+------------------------------
+<p>
+</p><div class="trac-progress">
+
+  <table xmlns="http://www.w3.org/1999/xhtml"; class="progress">
+    <tr>
+      <td class="closed" style="display: none">
+        <a 
href="/query?status=closed&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;order=id"
 title="1/1 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">0%</p>
+
+  <p class="legend">
+    <span class="first interval">
+      <a href="/query?reporter=santa&amp;max=0&amp;order=id">Total number of 
tickets: 1</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time">closed:
 0</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;order=id">active:
 1</a>
+    </span>
+  </p>
+</div><p>
+</p>
+------------------------------
+============================== TicketQuery(reporter=santa&or&owner=santa, 
format=progress)
+[[TicketQuery(reporter=santa&or&owner=santa, format=progress)]]
+------------------------------
+<p>
+</p><div class="trac-progress">
+
+  <table xmlns="http://www.w3.org/1999/xhtml"; class="progress">
+    <tr>
+      <td class="closed" style="width: 50%">
+        <a 
href="/query?status=closed&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id"
 title="1/2 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">50%</p>
+
+  <p class="legend">
+    <span class="first interval">
+      <a 
href="/query?reporter=santa&amp;or&amp;owner=santa&amp;max=0&amp;order=id">Total
 number of tickets: 2</a>
+    </span>
+    <span class="interval">
+      - <a 
href="/query?status=closed&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time">closed:
 1</a>
+    </span><span class="interval">
+      - <a 
href="/query?status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id">active:
 1</a>
+    </span>
+  </p>
+</div><p>
+</p>
+------------------------------
+============================== TicketQuery(format=progress, group=project)
+[[TicketQuery(format=progress, group=project)]]
+------------------------------
+<p>
+</p><div class="trac-groupprogress">
+  <table xmlns="http://www.w3.org/1999/xhtml"; summary="Ticket completion 
status for each project">
+    <tr>
+      <th scope="row">
+        <i><a href="/query?project=&amp;max=0&amp;order=id">(none)</a></i>
+
+
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 40%">
+    <tr>
+      <td class="closed" style="display: none">
+        <a 
href="/query?project=&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?project=&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id"
 title="1/1 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">0 / 1</p>
+
+
+
+      </td>
+    </tr><tr>
+      <th scope="row">
+
+
+        <a href="/query?project=xmas&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id"
 title="1/2 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">1 / 2</p>
+
+
+
+      </td>
+    </tr>
+  </table>
+</div><p>
+</p>
+------------------------------
+============================== TicketQuery(reporter=santa, format=progress, 
group=project)
+[[TicketQuery(reporter=santa, format=progress, group=project)]]
+------------------------------
+<p>
+</p><div class="trac-groupprogress">
+  <table xmlns="http://www.w3.org/1999/xhtml"; summary="Ticket completion 
status for each project">
+    <tr>
+      <th scope="row">
+
+
+        <a 
href="/query?project=xmas&amp;reporter=santa&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="display: none">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;reporter=santa&amp;group=resolution&amp;max=0&amp;order=time"
 title="0/1 closed"></a>
+      </td><td class="open" style="width: 100%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;max=0&amp;order=id"
 title="1/1 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">0 / 1</p>
+
+
+
+      </td>
+    </tr>
+  </table>
+</div><p>
+</p>
+------------------------------
+============================== TicketQuery(reporter=santa&or&owner=santa, 
format=progress, group=project)
+[[TicketQuery(reporter=santa&or&owner=santa, format=progress, group=project)]]
+------------------------------
+<p>
+</p><div class="trac-groupprogress">
+  <table xmlns="http://www.w3.org/1999/xhtml"; summary="Ticket completion 
status for each project">
+    <tr>
+      <th scope="row">
+
+
+        <a 
href="/query?project=xmas&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;max=0&amp;order=id">xmas</a>
+      </th>
+      <td>
+
+
+  <table class="progress" style="width: 80%">
+    <tr>
+      <td class="closed" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=closed&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;status=closed&amp;group=resolution&amp;max=0&amp;order=time"
 title="1/2 closed"></a>
+      </td><td class="open" style="width: 50%">
+        <a 
href="/query?project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;reporter=santa&amp;or&amp;owner=santa&amp;project=xmas&amp;status=assigned&amp;status=new&amp;status=accepted&amp;status=reopened&amp;max=0&amp;order=id"
 title="1/2 active"></a>
+      </td>
+    </tr>
+  </table>
+
+  <p class="percent">1 / 2</p>
+
+
+
+      </td>
+    </tr>
+  </table>
+</div><p>
+</p>
+------------------------------
+"""
+
+def ticket_setup(tc):
+    tc.env.config.set('ticket-custom', 'project', 'text')
+    ticket = Ticket(tc.env)
+    ticket.values.update({'reporter': 'santa',
+                          'summary': 'This is the summary',
+                          'status': 'new',
+                          'project': 'xmas'})
+    ticket.insert()
+    ticket = Ticket(tc.env)
+    ticket.values.update({'owner': 'elf',
+                          'summary': 'This is another summary',
+                          'status': 'assigned'})
+    ticket.insert()
+    ticket = Ticket(tc.env)
+    ticket.values.update({'owner': 'santa',
+                          'summary': 'This is th third summary',
+                          'status': 'closed',
+                          'project': 'xmas'})
+    ticket.insert()
+
+    tc.env.config.set('milestone-groups', 'closed.status', 'closed')
+    tc.env.config.set('milestone-groups', 'closed.query_args', 
'group=resolution,order=time')
+    tc.env.config.set('milestone-groups', 'closed.overall_completion', 'true')
+    tc.env.config.set('milestone-groups', 'active.status', '*')
+    tc.env.config.set('milestone-groups', 'active.css_class', 'open')
+
+def ticket_teardown(tc):
+    tc.env.reset_db()
 
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(QueryTestCase))
     suite.addTest(unittest.makeSuite(QueryLinksTestCase))
     suite.addTest(unittest.makeSuite(TicketQueryMacroTestCase))
+    suite.addTest(formatter.suite(QUERY_TEST_CASES, ticket_setup, __file__,
+                                  ticket_teardown))
     return suite
 
 if __name__ == '__main__':


Reply via email to