1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
#
# Copyright 2009, Niels Sascha Reedijk <niels.reedijk@gmail.com>
# All rights reserved. Distributed under the terms of the MIT License.
#
from pkg_resources import resource_filename
from genshi import HTML
from genshi.builder import tag
from genshi.filters.transform import Transformer
from trac.core import *
from trac.ticket import model
from trac.util.text import unicode_quote_plus
from trac.web.api import IRequestFilter
from trac.web.chrome import ITemplateProvider, ITemplateStreamFilter, add_notice, add_script
from trac.ticket.roadmap import TicketGroupStats
from trac.util.translation import _
class SubComponentsModule(Component):
"""Implements subcomponents in Trac's interface."""
implements(IRequestFilter, ITemplateProvider, ITemplateStreamFilter)
# IRequestFilter methods
def pre_process_request(self, req, handler):
if req.path_info.startswith('/admin/ticket/components/'):
if req.method == "POST" and 'renamechildren' in req.args:
if req.args.get('renamechildren') != 'on':
return handler # Let trac handle this update
# First process the parent component.
parentcomponentname = req.path_info[25:]
parentcomponent = model.Component(self.env, parentcomponentname)
parentcomponent.name = req.args.get('name')
parentcomponent.owner = req.args.get('owner')
parentcomponent.description = req.args.get('description')
try:
parentcomponent.update()
except self.env.db_exc.IntegrityError:
raise TracError(_('The component "%(name)s" already '
'exists.', name=parentcomponentname))
# Now update the child components
childcomponents = self._get_component_children(parentcomponentname)
for component in childcomponents:
component.name = component.name.replace(parentcomponentname, req.args.get('name'), 1)
component.update()
add_notice(req, _('Your changes have been saved.'))
req.redirect(req.href.admin('ticket', 'components'))
return handler
def post_process_request(self, req, template, data, content_type):
# The /query paths are handled in filter_stream()
if req.path_info.startswith('/ticket/') or \
req.path_info.startswith('/newticket'):
add_script(req, 'subcomponents/componentselect.js')
if template == "query.html":
# Allow users to query for parent components and include all subs
data['modes']['select'].insert(0, {'name': "begins with", 'value': "^"})
if template == "milestone_view.html":
# Group components in the milestone view by base component.
if data['grouped_by'] == "component":
componentname = ''
newgroups = []
newcomponents = []
for component in data['groups']:
componentname = component['name'].split('/')[0]
if componentname not in newcomponents:
# This component is not yet in the new list of components, add it.
newcomponents.append(componentname)
# Fix URLs to the querys (we use unicode_quote_plus to replace the '/'
# with something URL safe (like the hrefs are)
new_hrefs = []
for interval_href in component['interval_hrefs']:
new_hrefs.append(interval_href.replace(unicode_quote_plus(component['name']), '^' + componentname))
component['stats_href'] = component['stats_href'].replace(unicode_quote_plus(component['name']), '^' + componentname)
component['interval_hrefs'] = new_hrefs
# Set the name to the base name (in case this originally
# is a subcomponent.
component['name'] = componentname
newgroups.append(component)
else:
# This is a subcomponent. Add the stats to the main component.
# Note that above two lists are created. Whenever an
# item is added to one, an analogous one is added to
# the other. This code uses that logic.
corecomponent = newgroups[newcomponents.index(componentname)]
mergedstats = corecomponent['stats'] #TicketGroupStats from trac.ticket.roadmap
newstats = component['stats']
# Bear with me as we go to this mess that is the group stats
# (or of course this hack, depending on who's viewpoint).
# First merge the totals
mergedstats.count += newstats.count
# The stats are divided in intervals, merge these.
i = 0
for interval in mergedstats.intervals:
newinterval = newstats.intervals[i]
interval['count'] += newinterval['count']
i += 1
mergedstats.refresh_calcs()
# Now store the new milestone component groups
data['groups'] = newgroups
return template, data, content_type
# ITemplateProvider methods
def get_htdocs_dirs(self):
"""Return the absolute path of a directory containing additional
static resources (such as images, style sheets, etc).
"""
return [('subcomponents', resource_filename(__name__, 'htdocs'))]
def get_templates_dirs(self):
"""Return the absolute path of the directory containing the provided
ClearSilver templates.
"""
return ""
# ITemplateStreamFilter methods
def filter_stream(self, req, method, filename, stream, data):
if filename == "admin_components.html":
# If we are at detail editing of a component, and it has
# children, then add a checkbox to rename those.
if data['view'] == 'detail':
if len(self._get_component_children(data['component'].name)) > 0:
stream |= Transformer("//div[@class='field'][1]").after(self._build_renamechildren_field())
elif req.path_info.startswith('/query'):
# We need to load our script after the initializeFilters() call done by Trac
html = tag.script(type='text/javascript', charset='utf-8',
src=req.href.chrome('subcomponents/componentselect.js'))
stream |= Transformer('//head').append(html)
return stream
# Other functions
def _get_component_children(self, name):
components = model.Component.select(self.env)
result = []
for component in components:
if component.name.startswith(name) and component.name != name:
result.append(component)
return result
def _build_renamechildren_field(self):
return tag.div(tag.label(tag.input(_("Also rename children"), \
type='checkbox', id='renamechildren', \
name='renamechildren', checked='checked') \
), \
class_='field')
|