diff -r 7d30b0c5b937 setup.py
--- a/setup.py Sun Jul 22 09:47:18 2012 +0200
+++ b/setup.py Tue Aug 13 13:21:06 2013 +0200
@@ -9,9 +9,9 @@
setup(
name = 'TracSubcomponents',
- version = '1.2.0',
+ version = '1.3.0',
packages = ['subcomponents'],
- package_data = { 'subcomponents': ['htdocs/*.js'] },
+ package_data = { 'subcomponents': ['htdocs/*.js', 'htdocs/*.css', 'templates/*.html'] },
author = 'Niels Sascha Reedijk',
author_email = 'niels.reedijk@gmail.com',
@@ -39,6 +39,7 @@
entry_points = {
'trac.plugins': [
+ 'subcomponents.components_view = subcomponents.components_view',
'subcomponents.web_ui = subcomponents.web_ui',
]
}
diff -r 7d30b0c5b937 subcomponents/components_view.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/subcomponents/components_view.py Tue Aug 13 13:21:06 2013 +0200
@@ -0,0 +1,101 @@
+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.util.html import html
+from trac.web.api import IRequestFilter
+from trac.web.chrome import ITemplateProvider, ITemplateStreamFilter, INavigationContributor, ITemplateProvider, add_stylesheet
+from trac.ticket.roadmap import TicketGroupStats
+from trac.util.translation import _
+from trac.perm import IPermissionRequestor, PermissionError, PermissionCache
+from trac.web import IRequestHandler
+from trac.db import DatabaseManager, Table, Column, with_transaction
+from trac.wiki import format_to_html
+from trac.mimeview.api import Context
+
+class ComponentsViewModule(Component):
+ """Adds a separate end-user page that lists all components."""
+
+ implements(IPermissionRequestor, INavigationContributor, ITemplateProvider, IRequestHandler, INavigationContributor)
+
+ # IPermissionRequestor methods.
+ def get_permission_actions(self):
+ return ['COMPONENT_VIEW']
+
+ # INavigationContributor methods
+ def get_active_navigation_item(self, req):
+ return 'components'
+
+ # Return the items that should be added to the navigation bar.
+ def get_navigation_items(self, req):
+ # Only add something if the correct permission to view it is available
+ # so that users don't even have entries for stuff they can't see anyway.
+ if req.perm.has_permission('COMPONENT_VIEW'):
+ yield ('mainnav', 'components',
+ html.A('Components', href= req.href.components()))
+
+ # IRequestHandler methods
+ def match_request(self, req):
+ return req.path_info == '/components'
+
+ def process_request(self, req):
+ # Check permissions
+ req.perm.require('COMPONENT_VIEW')
+
+ self.log.info('SubComponents: Showing components view page')
+ component_names = []
+ subcomponents = []
+ for component in model.Component.select(self.env):
+ component_names.append(component.name)
+ tickets = []
+ active_tickets = 0
+ active_tickets_without_milestone = 0
+
+ for (id, milestone) in self.env.db_query("""SELECT id, milestone FROM ticket WHERE status <> 'closed' AND component = "%s" """ % component.name ):
+ active_tickets += 1
+ if not milestone:
+ active_tickets_without_milestone += 1
+
+ (subname, sublevel) = self.get_subcomponent_name(component.name, component_names)
+
+ subcomponents.append({
+ 'name' : component.name,
+ 'subname' : subname,
+ 'subcomponent_level' : sublevel,
+ 'description' : format_to_html(self.env, Context.from_request(req), component.description, True),
+ 'active_tickets' : active_tickets,
+ 'active_tickets_without_milestone' : active_tickets_without_milestone,
+ })
+
+ data = { 'components' : subcomponents,
+ 'no_milestone' : req.args.has_key('no_milestone'),
+ 'hide_description' : req.args.has_key('hide_description') }
+ add_stylesheet(req, 'subcomponents/subcomponents.css')
+
+ return 'components.html', data, None
+
+ def get_htdocs_dirs(self):
+ from pkg_resources import resource_filename
+ return [('usermanual', resource_filename(__name__, 'htdocs'))]
+
+ def get_templates_dirs(self):
+ from pkg_resources import resource_filename
+ return [resource_filename(__name__, 'templates')]
+
+ def get_subcomponent_name(self, name, component_names):
+ subname = name
+ for component in component_names:
+ if not name.startswith(component + "/"):
+ continue
+
+ sub = name[len(component):].lstrip('/')
+ if len(sub) < len(subname):
+ subname = sub
+
+ sublevel = name[:-len(subname)].count('/')
+ return (subname, sublevel)
+
diff -r 7d30b0c5b937 subcomponents/htdocs/subcomponents.css
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/subcomponents/htdocs/subcomponents.css Tue Aug 13 13:21:06 2013 +0200
@@ -0,0 +1,55 @@
+table.subcomponents tbody tr.fullrow
+{
+ font-size: 85%;
+ border: medium none;
+}
+
+table.subcomponents tbody tr.fullrow td
+{
+ vertical-align: middle;
+ border: medium none;
+}
+
+table.subcomponents tbody tr.fullrow th
+{
+ vertical-align: middle;
+ text-align: center;
+ border: medium none;
+}
+
+.subcomponents tbody td.meta
+{
+ font-size: 85%;
+ color: rgb(153, 153, 153);
+}
+
+.subcomponents tbody td.meta p
+{
+ margin: 0;
+}
+
+.subcomponents tbody td.ticketcount
+{
+ text-align:right;
+}
+
+.subcomponents tbody td.ticketadd
+{
+ text-align:center;
+ font-size: 85%;
+}
+
+
+#query .option
+{
+ float: left;
+ line-height: 2em;
+ margin: 0.9em 2.5em 0px 0.5em;
+ padding: 0px 0px 0.1em;
+ font-size: 11px;
+}
+
+#query .buttons
+{
+ float: right;
+}
\ No newline at end of file
diff -r 7d30b0c5b937 subcomponents/templates/components.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/subcomponents/templates/components.html Tue Aug 13 13:21:06 2013 +0200
@@ -0,0 +1,89 @@
+
+
+
+
+ Components
+
+
+ Components
+
+
+
+
+
+ Name |
+ Tickets |
+ Description |
+
+
+
+
+
+
+ ${component.subname}
+
+ |
+
+
+
+ ${component.active_tickets_without_milestone}
+
+
+ ${component.active_tickets}
+
+
+ |
+
+
+ add ticket
+
+ |
+
+ ${component.description}
+ |
+
+
+
+
+ ${componentrow("even")}
+
+
+ ${componentrow("odd")}
+
+
+
+
+
+
+
+