You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by as...@apache.org on 2013/07/09 11:24:58 UTC

svn commit: r1501157 - in /bloodhound/trunk: bloodhound_dashboard/bhdashboard/widgets/templates/ bloodhound_relations/bhrelations/ bloodhound_relations/bhrelations/templates/ bloodhound_relations/bhrelations/widgets/

Author: astaric
Date: Tue Jul  9 09:24:58 2013
New Revision: 1501157

URL: http://svn.apache.org/r1501157
Log:
EOL normalization.

Converted CRLF to LF to match the rest of the files.

Modified:
    bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_relations.html
    bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html
    bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py
    bloodhound/trunk/bloodhound_relations/bhrelations/widgets/relations.py

Modified: bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_relations.html
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_relations.html?rev=1501157&r1=1501156&r2=1501157&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_relations.html (original)
+++ bloodhound/trunk/bloodhound_dashboard/bhdashboard/widgets/templates/widget_relations.html Tue Jul  9 09:24:58 2013
@@ -1,64 +1,64 @@
-<!--!
-  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.
--->
-
-<div
-  xmlns="http://www.w3.org/1999/xhtml"
-  xmlns:py="http://genshi.edgewall.org/"
-  xmlns:xi="http://www.w3.org/2001/XInclude">
-
-  <py:choose test="">
-    <py:when test="relations">
-      <table class="table table-condensed table-bordered">
-        <thead>
-          <tr>
-            <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
-          </tr>
-        </thead>
-
-        <tbody py:for="relgroup,items in relations.iteritems()">
-          <tr py:for="item in items" class="relation">
-            <td>${relgroup if items.index(item) == 0 else None}</td>
-            <td>
-              <a href="${href.products(item['destticket'].env.product.prefix)}">
-                <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
-                <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
-              </a>
-            </td>
-            <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
-            <td>$item.comment</td>
-            <td>$item.author</td>
-            <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
-          </tr>
-        </tbody>
-      </table>
-    </py:when>
-    <py:otherwise>
-      <div class="alert alert-info">
-        No defined relations for this ticket.
-      </div>
-    </py:otherwise>
-  </py:choose>
-
-  <div class="btn-group">
-    <form method="get" action="${href.ticket(ticket.id, 'relations')}">
-      <button type="submit" class="btn" id="manage-relations"><i class="icon-retweet"></i> Manage relations</button>
-    </form>
-  </div>
-</div>
-
+<!--!
+  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.
+-->
+
+<div
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:py="http://genshi.edgewall.org/"
+  xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <py:choose test="">
+    <py:when test="relations">
+      <table class="table table-condensed table-bordered">
+        <thead>
+          <tr>
+            <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
+          </tr>
+        </thead>
+
+        <tbody py:for="relgroup,items in relations.iteritems()">
+          <tr py:for="item in items" class="relation">
+            <td>${relgroup if items.index(item) == 0 else None}</td>
+            <td>
+              <a href="${href.products(item['destticket'].env.product.prefix)}">
+                <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
+                <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
+              </a>
+            </td>
+            <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
+            <td>$item.comment</td>
+            <td>$item.author</td>
+            <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
+          </tr>
+        </tbody>
+      </table>
+    </py:when>
+    <py:otherwise>
+      <div class="alert alert-info">
+        No defined relations for this ticket.
+      </div>
+    </py:otherwise>
+  </py:choose>
+
+  <div class="btn-group">
+    <form method="get" action="${href.ticket(ticket.id, 'relations')}">
+      <button type="submit" class="btn" id="manage-relations"><i class="icon-retweet"></i> Manage relations</button>
+    </form>
+  </div>
+</div>
+

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html?rev=1501157&r1=1501156&r2=1501157&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/templates/manage.html Tue Jul  9 09:24:58 2013
@@ -1,127 +1,127 @@
-<!--!
-  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:xi="http://www.w3.org/2001/XInclude"
-      xmlns:py="http://genshi.edgewall.org/"
-      xmlns:i18n="http://genshi.edgewall.org/i18n"
-      xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard">
-  <xi:include href="layout.html" />
-  <xi:include href="widget_macros.html" />
-
-  <head>
-    <title py:choose="">Ticket relations for #${ticket.id}</title>
-  </head>
-
-  <body>
-    <h1>Manage relations for ticket <a href="${href.ticket(ticket.id)}">#$ticket.id</a></h1>
-
-    <div class="row">
-      <div class="span8">
-        <py:if test='error'>
-          <div class="alert alert-error">
-            <span class="label label-important">Oops !</span>
-            Could not create relation.
-            $error
-          </div>
-        </py:if>
-
-        <form id="addrelation" class="well form-horizontal" method="post" action="">
-          <fieldset>
-            <legend>Add relation:</legend>
-          </fieldset>
-
-          <div class="control-group">
-            <label class="control-label" for="dest_tid">Related ticket:</label>
-            <div class="controls">
-              <input type="text" id="dest_tid" class="span4" name="dest_tid" value="$relation.destination" />
-            </div>
-          </div>
-
-          <div class="control-group">
-            <label class="control-label" for="reltype">Relation type:</label>
-            <div class="controls">
-              <select class="span4" id="reltype" name="reltype">
-                <option py:for="reltype,label in reltypes.iteritems()" value="$reltype" selected="${True if reltype == relation.type else None}">$label</option>
-              </select>
-            </div>
-          </div>
-
-          <div class="control-group">
-            <label class="control-label" for="comment">Comment:</label>
-            <div class="controls">
-              <textarea name="comment" rows="3" class="span4">${relation.comment}</textarea>
-            </div>
-          </div>
-
-          <div class="control-group">
-            <div class="controls">
-              <input type="submit" class="btn" name="add" value="${_('Add')}" />
-            </div>
-          </div>
-        </form>
-      </div>
-    </div>
-
-    <div class="row">
-      <div class="span8">
-        <py:choose>
-          <form py:when="relations" id="rmrelations" class="form-horizontal" method="post" action="">
-            <table class="table table-condensed table-bordered">
-              <thead>
-                <tr>
-                  <th class="sel"><i class="icon-check"></i></th>
-                  <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
-                </tr>
-              </thead>
-              <tbody py:for="relgroup,items in relations.iteritems()">
-                <tr py:for="item in items">
-                  <td class="sel"><input type="checkbox" name="sel" value="${item.relation_id}" /></td>
-                  <td>${relgroup if items.index(item) == 0 else None}</td>
-                  <td>
-                    <a href="${href.products(item['destticket'].env.product.prefix)}">
-                      <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
-                      <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
-                    </a>
-                  </td>
-                  <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
-                  <td>$item.comment</td>
-                  <td>$item.author</td>
-                  <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
-                </tr>
-              </tbody>
-            </table>
-
-            <div class="control-group">
-              <input type="submit" class="btn" name="remove" value="${_('Remove selected relations')}" />
-            </div>
-          </form>
-
-          <div py:otherwise="" class="alert alert-info">
-            No defined relations for this ticket.
-          </div>
-        </py:choose>
-      </div>
-    </div>
-  </body>
-</html>
-
+<!--!
+  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:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:i18n="http://genshi.edgewall.org/i18n"
+      xmlns:bh="http://issues.apache.org/bloodhound/wiki/Ui/Dashboard">
+  <xi:include href="layout.html" />
+  <xi:include href="widget_macros.html" />
+
+  <head>
+    <title py:choose="">Ticket relations for #${ticket.id}</title>
+  </head>
+
+  <body>
+    <h1>Manage relations for ticket <a href="${href.ticket(ticket.id)}">#$ticket.id</a></h1>
+
+    <div class="row">
+      <div class="span8">
+        <py:if test='error'>
+          <div class="alert alert-error">
+            <span class="label label-important">Oops !</span>
+            Could not create relation.
+            $error
+          </div>
+        </py:if>
+
+        <form id="addrelation" class="well form-horizontal" method="post" action="">
+          <fieldset>
+            <legend>Add relation:</legend>
+          </fieldset>
+
+          <div class="control-group">
+            <label class="control-label" for="dest_tid">Related ticket:</label>
+            <div class="controls">
+              <input type="text" id="dest_tid" class="span4" name="dest_tid" value="$relation.destination" />
+            </div>
+          </div>
+
+          <div class="control-group">
+            <label class="control-label" for="reltype">Relation type:</label>
+            <div class="controls">
+              <select class="span4" id="reltype" name="reltype">
+                <option py:for="reltype,label in reltypes.iteritems()" value="$reltype" selected="${True if reltype == relation.type else None}">$label</option>
+              </select>
+            </div>
+          </div>
+
+          <div class="control-group">
+            <label class="control-label" for="comment">Comment:</label>
+            <div class="controls">
+              <textarea name="comment" rows="3" class="span4">${relation.comment}</textarea>
+            </div>
+          </div>
+
+          <div class="control-group">
+            <div class="controls">
+              <input type="submit" class="btn" name="add" value="${_('Add')}" />
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+
+    <div class="row">
+      <div class="span8">
+        <py:choose>
+          <form py:when="relations" id="rmrelations" class="form-horizontal" method="post" action="">
+            <table class="table table-condensed table-bordered">
+              <thead>
+                <tr>
+                  <th class="sel"><i class="icon-check"></i></th>
+                  <th>Type</th><th>Product</th><th>Ticket</th><th>Comment</th><th>Author</th><th class="hidden-phone">Changed</th>
+                </tr>
+              </thead>
+              <tbody py:for="relgroup,items in relations.iteritems()">
+                <tr py:for="item in items">
+                  <td class="sel"><input type="checkbox" name="sel" value="${item.relation_id}" /></td>
+                  <td>${relgroup if items.index(item) == 0 else None}</td>
+                  <td>
+                    <a href="${href.products(item['destticket'].env.product.prefix)}">
+                      <span class="hidden-phone">${item['destticket'].env.product.name} (${item['destticket'].env.product.prefix})</span>
+                      <span class="visible-phone">${item['destticket'].env.product.prefix}</span>
+                    </a>
+                  </td>
+                  <td><a href="${item['desthref']}">#${item['destticket'].id}</a> - ${item['destticket'].summary}</td>
+                  <td>$item.comment</td>
+                  <td>$item.author</td>
+                  <td class="hidden-phone">${pretty_dateinfo(item.when)}</td>
+                </tr>
+              </tbody>
+            </table>
+
+            <div class="control-group">
+              <input type="submit" class="btn" name="remove" value="${_('Remove selected relations')}" />
+            </div>
+          </form>
+
+          <div py:otherwise="" class="alert alert-info">
+            No defined relations for this ticket.
+          </div>
+        </py:choose>
+      </div>
+    </div>
+  </body>
+</html>
+

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py?rev=1501157&r1=1501156&r2=1501157&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/web_ui.py Tue Jul  9 09:24:58 2013
@@ -1,171 +1,171 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-
-#  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.
-
-
-r"""Ticket relations for Apache(TM) Bloodhound
-
-Ticket relations user interface.
-"""
-
-import re
-import pkg_resources
-
-from trac.core import Component, implements, TracError
-from trac.resource import get_resource_url, Resource
-from trac.ticket.model import Ticket
-from trac.util.translation import _
-from trac.web import IRequestHandler, IRequestFilter
-from trac.web.chrome import ITemplateProvider, add_warning
-
-from bhrelations.api import RelationsSystem, ResourceIdSerializer, \
-    TicketRelationsSpecifics, UnknownRelationType, NoSuchTicketError
-from bhrelations.model import Relation
-from bhrelations.validation import ValidationError
-
-
-class RelationManagementModule(Component):
-    implements(IRequestFilter, IRequestHandler, ITemplateProvider)
-
-    # IRequestHandler methods
-    def match_request(self, req):
-        match = re.match(r'/ticket/([0-9]+)/relations/*$', req.path_info)
-        if not match:
-            return False
-
-        req.args['id'] = match.group(1)
-        return True
-
-    def process_request(self, req):
-        tid = req.args.get('id')
-        if not tid:
-            raise TracError(_('No ticket id provided.'))
-
-        try:
-            ticket = Ticket(self.env, tid)
-        except ValueError:
-            raise TracError(_('Invalid ticket id.'))
-
-        req.perm.require('TICKET_VIEW')
-        relsys = RelationsSystem(self.env)
-
-        data = {
-            'relation': {},
-        }
-        if req.method == 'POST':
-            # for modifying the relations TICKET_MODIFY is required for
-            # both the source and the destination tickets
-            req.perm.require('TICKET_MODIFY')
-
-            if 'remove' in req.args:
-                rellist = req.args.get('sel')
-                if rellist:
-                    if isinstance(rellist, basestring):
-                        rellist = [rellist, ]
-                    self.remove_relations(req, rellist)
-            elif 'add' in req.args:
-                relation = dict(
-                    destination=req.args.get('dest_tid', ''),
-                    type=req.args.get('reltype', ''),
-                    comment=req.args.get('comment', ''),
-                )
-                try:
-                    trs = TicketRelationsSpecifics(self.env)
-                    dest_ticket = trs.find_ticket(relation['destination'])
-                except NoSuchTicketError:
-                    data['error'] = _('Invalid ticket ID.')
-                else:
-                    req.perm.require('TICKET_MODIFY', Resource(dest_ticket.id))
-
-                    try:
-                        relsys.add(ticket, dest_ticket,
-                            relation['type'],
-                            relation['comment'],
-                            req.authname)
-                    except NoSuchTicketError:
-                        data['error'] = _('Invalid ticket ID.')
-                    except UnknownRelationType:
-                        data['error'] = _('Unknown relation type.')
-                    except ValidationError as ex:
-                        data['error'] = ex.message
-
-                if 'error' in data:
-                    data['relation'] = relation
-            else:
-                raise TracError(_('Invalid operation.'))
-
-        data.update({
-            'ticket': ticket,
-            'reltypes': relsys.get_relation_types(),
-            'relations': self.get_ticket_relations(ticket),
-        })
-        return 'manage.html', data, None
-
-    # ITemplateProvider methods
-    def get_htdocs_dirs(self):
-        resource_filename = pkg_resources.resource_filename
-        return [('relations', resource_filename('bhrelations', 'htdocs')), ]
-
-    def get_templates_dirs(self):
-        resource_filename = pkg_resources.resource_filename
-        return [resource_filename('bhrelations', 'templates'), ]
-
-    # IRequestFilter methods
-    def pre_process_request(self, req, handler):
-        return handler
-
-    def post_process_request(self, req, template, data, content_type):
-        if 'ticket' in data:
-            ticket = data['ticket']
-            rls = RelationsSystem(self.env)
-            resid = ResourceIdSerializer.get_resource_id_from_instance(
-                self.env, ticket)
-
-            if rls.duplicate_relation_type:
-                duplicate_relations = \
-                    rls._select_relations(resid, rls.duplicate_relation_type)
-                if duplicate_relations:
-                    data['ticket_duplicate_of'] = \
-                        duplicate_relations[0].destination
-        return template, data, content_type
-
-    # utility functions
-    def get_ticket_relations(self, ticket):
-        grouped_relations = {}
-        relsys = RelationsSystem(self.env)
-        reltypes = relsys.get_relation_types()
-        trs = TicketRelationsSpecifics(self.env)
-        for r in relsys.get_relations(ticket):
-            r['desthref'] = get_resource_url(self.env, r['destination'],
-                self.env.href)
-            r['destticket'] = trs._create_ticket_by_full_id(r['destination'])
-            grouped_relations.setdefault(reltypes[r['type']], []).append(r)
-        return grouped_relations
-
-    def remove_relations(self, req, rellist):
-        relsys = RelationsSystem(self.env)
-        for relid in rellist:
-            relation = Relation.load_by_relation_id(self.env, relid)
-            resource = ResourceIdSerializer.get_resource_by_id(
-                relation.destination)
-            if 'TICKET_MODIFY' in req.perm(resource):
-                relsys.delete(relid)
-            else:
-                add_warning(req,
-                    _('Not enough permissions to remove relation "%s"' % relid))
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  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.
+
+
+r"""Ticket relations for Apache(TM) Bloodhound
+
+Ticket relations user interface.
+"""
+
+import re
+import pkg_resources
+
+from trac.core import Component, implements, TracError
+from trac.resource import get_resource_url, Resource
+from trac.ticket.model import Ticket
+from trac.util.translation import _
+from trac.web import IRequestHandler, IRequestFilter
+from trac.web.chrome import ITemplateProvider, add_warning
+
+from bhrelations.api import RelationsSystem, ResourceIdSerializer, \
+    TicketRelationsSpecifics, UnknownRelationType, NoSuchTicketError
+from bhrelations.model import Relation
+from bhrelations.validation import ValidationError
+
+
+class RelationManagementModule(Component):
+    implements(IRequestFilter, IRequestHandler, ITemplateProvider)
+
+    # IRequestHandler methods
+    def match_request(self, req):
+        match = re.match(r'/ticket/([0-9]+)/relations/*$', req.path_info)
+        if not match:
+            return False
+
+        req.args['id'] = match.group(1)
+        return True
+
+    def process_request(self, req):
+        tid = req.args.get('id')
+        if not tid:
+            raise TracError(_('No ticket id provided.'))
+
+        try:
+            ticket = Ticket(self.env, tid)
+        except ValueError:
+            raise TracError(_('Invalid ticket id.'))
+
+        req.perm.require('TICKET_VIEW')
+        relsys = RelationsSystem(self.env)
+
+        data = {
+            'relation': {},
+        }
+        if req.method == 'POST':
+            # for modifying the relations TICKET_MODIFY is required for
+            # both the source and the destination tickets
+            req.perm.require('TICKET_MODIFY')
+
+            if 'remove' in req.args:
+                rellist = req.args.get('sel')
+                if rellist:
+                    if isinstance(rellist, basestring):
+                        rellist = [rellist, ]
+                    self.remove_relations(req, rellist)
+            elif 'add' in req.args:
+                relation = dict(
+                    destination=req.args.get('dest_tid', ''),
+                    type=req.args.get('reltype', ''),
+                    comment=req.args.get('comment', ''),
+                )
+                try:
+                    trs = TicketRelationsSpecifics(self.env)
+                    dest_ticket = trs.find_ticket(relation['destination'])
+                except NoSuchTicketError:
+                    data['error'] = _('Invalid ticket ID.')
+                else:
+                    req.perm.require('TICKET_MODIFY', Resource(dest_ticket.id))
+
+                    try:
+                        relsys.add(ticket, dest_ticket,
+                            relation['type'],
+                            relation['comment'],
+                            req.authname)
+                    except NoSuchTicketError:
+                        data['error'] = _('Invalid ticket ID.')
+                    except UnknownRelationType:
+                        data['error'] = _('Unknown relation type.')
+                    except ValidationError as ex:
+                        data['error'] = ex.message
+
+                if 'error' in data:
+                    data['relation'] = relation
+            else:
+                raise TracError(_('Invalid operation.'))
+
+        data.update({
+            'ticket': ticket,
+            'reltypes': relsys.get_relation_types(),
+            'relations': self.get_ticket_relations(ticket),
+        })
+        return 'manage.html', data, None
+
+    # ITemplateProvider methods
+    def get_htdocs_dirs(self):
+        resource_filename = pkg_resources.resource_filename
+        return [('relations', resource_filename('bhrelations', 'htdocs')), ]
+
+    def get_templates_dirs(self):
+        resource_filename = pkg_resources.resource_filename
+        return [resource_filename('bhrelations', 'templates'), ]
+
+    # IRequestFilter methods
+    def pre_process_request(self, req, handler):
+        return handler
+
+    def post_process_request(self, req, template, data, content_type):
+        if 'ticket' in data:
+            ticket = data['ticket']
+            rls = RelationsSystem(self.env)
+            resid = ResourceIdSerializer.get_resource_id_from_instance(
+                self.env, ticket)
+
+            if rls.duplicate_relation_type:
+                duplicate_relations = \
+                    rls._select_relations(resid, rls.duplicate_relation_type)
+                if duplicate_relations:
+                    data['ticket_duplicate_of'] = \
+                        duplicate_relations[0].destination
+        return template, data, content_type
+
+    # utility functions
+    def get_ticket_relations(self, ticket):
+        grouped_relations = {}
+        relsys = RelationsSystem(self.env)
+        reltypes = relsys.get_relation_types()
+        trs = TicketRelationsSpecifics(self.env)
+        for r in relsys.get_relations(ticket):
+            r['desthref'] = get_resource_url(self.env, r['destination'],
+                self.env.href)
+            r['destticket'] = trs._create_ticket_by_full_id(r['destination'])
+            grouped_relations.setdefault(reltypes[r['type']], []).append(r)
+        return grouped_relations
+
+    def remove_relations(self, req, rellist):
+        relsys = RelationsSystem(self.env)
+        for relid in rellist:
+            relation = Relation.load_by_relation_id(self.env, relid)
+            resource = ResourceIdSerializer.get_resource_by_id(
+                relation.destination)
+            if 'TICKET_MODIFY' in req.perm(resource):
+                relsys.delete(relid)
+            else:
+                add_warning(req,
+                    _('Not enough permissions to remove relation "%s"' % relid))

Modified: bloodhound/trunk/bloodhound_relations/bhrelations/widgets/relations.py
URL: http://svn.apache.org/viewvc/bloodhound/trunk/bloodhound_relations/bhrelations/widgets/relations.py?rev=1501157&r1=1501156&r2=1501157&view=diff
==============================================================================
--- bloodhound/trunk/bloodhound_relations/bhrelations/widgets/relations.py (original)
+++ bloodhound/trunk/bloodhound_relations/bhrelations/widgets/relations.py Tue Jul  9 09:24:58 2013
@@ -1,75 +1,75 @@
-#!/usr/bin/env python
-# -*- coding: UTF-8 -*-
-
-#  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.
-
-
-r"""Project dashboard for Apache(TM) Bloodhound
-
-Widgets displaying ticket relations.
-"""
-
-from trac.util.translation import _
-from trac.ticket.model import Ticket
-
-from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper
-from bhrelations.web_ui import RelationManagementModule
-
-__metaclass__ = type
-
-
-class TicketRelationsWidget(WidgetBase):
-    """Display ticket relations.
-    """
-    def get_widget_params(self, name):
-        """Return a dictionary containing arguments specification for
-        the widget with specified name.
-        """
-        return {
-                'tid' : {
-                        'desc' : """Source ticket id""",
-                        'type' : int
-                    },
-
-                'max' : {
-                        'desc' : """Limit the number of relations displayed""",
-                        'type' : int
-                    },
-            }
-
-    get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
-
-    def render_widget(self, name, context, options):
-        """Gather list of relations and render data in compact view
-        """
-        req = context.req
-        title = _('Related tickets')
-        params = ('tid', 'max')
-        tid, max_ = self.bind_params(name, options, *params)
-
-        ticket = Ticket(self.env, tid)
-        data = {
-            'ticket': ticket,
-            'relations': \
-                RelationManagementModule(self.env).get_ticket_relations(ticket),
-        }
-        return 'widget_relations.html', \
-            {'title': title, 'data': data, }, context
-
-    render_widget = pretty_wrapper(render_widget, check_widget_name)
-
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+
+#  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.
+
+
+r"""Project dashboard for Apache(TM) Bloodhound
+
+Widgets displaying ticket relations.
+"""
+
+from trac.util.translation import _
+from trac.ticket.model import Ticket
+
+from bhdashboard.util import WidgetBase, check_widget_name, pretty_wrapper
+from bhrelations.web_ui import RelationManagementModule
+
+__metaclass__ = type
+
+
+class TicketRelationsWidget(WidgetBase):
+    """Display ticket relations.
+    """
+    def get_widget_params(self, name):
+        """Return a dictionary containing arguments specification for
+        the widget with specified name.
+        """
+        return {
+                'tid' : {
+                        'desc' : """Source ticket id""",
+                        'type' : int
+                    },
+
+                'max' : {
+                        'desc' : """Limit the number of relations displayed""",
+                        'type' : int
+                    },
+            }
+
+    get_widget_params = pretty_wrapper(get_widget_params, check_widget_name)
+
+    def render_widget(self, name, context, options):
+        """Gather list of relations and render data in compact view
+        """
+        req = context.req
+        title = _('Related tickets')
+        params = ('tid', 'max')
+        tid, max_ = self.bind_params(name, options, *params)
+
+        ticket = Ticket(self.env, tid)
+        data = {
+            'ticket': ticket,
+            'relations': \
+                RelationManagementModule(self.env).get_ticket_relations(ticket),
+        }
+        return 'widget_relations.html', \
+            {'title': title, 'data': data, }, context
+
+    render_widget = pretty_wrapper(render_widget, check_widget_name)
+