keithsalisbury-subtrac 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +56 -0
- data/VERSION.yml +4 -0
- data/bin/subtrac +40 -0
- data/lib/subtrac.rb +245 -0
- data/lib/subtrac/common/clients/index.wsgi +89 -0
- data/lib/subtrac/common/favicon.ico +0 -0
- data/lib/subtrac/common/images/trac/banner_bg.jpg +0 -0
- data/lib/subtrac/common/images/trac/bar_bg.gif +0 -0
- data/lib/subtrac/common/images/trac/footer_back.png +0 -0
- data/lib/subtrac/common/images/trac/main_bg.gif +0 -0
- data/lib/subtrac/common/images/trac/saint_logo_small.png +0 -0
- data/lib/subtrac/common/static/404.html +14 -0
- data/lib/subtrac/common/styles/trac.css +222 -0
- data/lib/subtrac/common/trac.ini +178 -0
- data/lib/subtrac/config/config.yml +54 -0
- data/lib/subtrac/passwords +1 -0
- data/lib/subtrac/shared/trac.ini +178 -0
- data/lib/subtrac/templates/location.erb +16 -0
- data/lib/subtrac/templates/projects/blank/svn/branches/README +0 -0
- data/lib/subtrac/templates/projects/blank/svn/tags/README +0 -0
- data/lib/subtrac/templates/projects/blank/svn/trunk/README +0 -0
- data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +57 -0
- data/lib/subtrac/templates/projects/new/svn/trunk/trac/wiki/WikiStart +46 -0
- data/lib/subtrac/templates/projects/new/trac/wiki/WikiStart +23 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/index/index.html +22 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/layout.html +56 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/site.html +27 -0
- data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/theme.html +86 -0
- data/lib/subtrac/templates/projects/trac_theme/trac/wiki/WikiStart +4 -0
- data/lib/subtrac/templates/trac.erb +25 -0
- data/lib/subtrac/templates/vhost.erb +35 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +419 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +3 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +20 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +28 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +168 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +137 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +91 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +199 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +105 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +287 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +71 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +4 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +135 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +70 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +142 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +231 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +27 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +152 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +160 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +124 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +134 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +132 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +97 -0
- data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +161 -0
- data/lib/subtrac/trac-plugins/clientsplugin/setup.py +43 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +4 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +273 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +44 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +36 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +32 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +409 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +30 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +53 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +181 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +66 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +47 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +93 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +86 -0
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +20 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +23 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +128 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +40 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +285 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +173 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +164 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +69 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +1 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +292 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +172 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +178 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +25 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +6 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +356 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +100 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +84 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +1149 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +119 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +73 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +165 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +28 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +221 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +675 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +150 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +25 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +131 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +84 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +104 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +194 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +62 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +28 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +127 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +129 -0
- data/lib/subtrac/trac-plugins/worklogplugin/setup.py +29 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +1 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +187 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +67 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +12 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +7 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +208 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +1439 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.xcf +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/worklogplugin.css +80 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstart.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstop.png +0 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/manager.py +336 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +598 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +45 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +70 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +40 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +59 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +33 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +153 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +96 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +29 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +31 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +47 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +174 -0
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +73 -0
- data/lib/subtrac/version.rb +4 -0
- metadata +191 -0
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
from trac.web.api import ITemplateStreamFilter
|
2
|
+
from trac.core import *
|
3
|
+
from genshi.core import *
|
4
|
+
from genshi.builder import tag
|
5
|
+
from sets import Set as set
|
6
|
+
from genshi.filters.transform import Transformer
|
7
|
+
import re
|
8
|
+
|
9
|
+
from trac.ticket.report import ReportModule
|
10
|
+
from trac.util.datefmt import format_datetime, format_time
|
11
|
+
import csv
|
12
|
+
from trac.web.api import RequestDone
|
13
|
+
|
14
|
+
billing_report_regex = re.compile("\{(?P<reportid>\d*)\}")
|
15
|
+
def report_id_from_text(text):
|
16
|
+
m = billing_report_regex.match(text)
|
17
|
+
if m:
|
18
|
+
return int(m.groupdict()["reportid"])
|
19
|
+
|
20
|
+
def get_billing_reports(comp):
|
21
|
+
cur = comp.env.get_db_cnx().cursor()
|
22
|
+
try:
|
23
|
+
cur.execute("SELECT id FROM custom_report")
|
24
|
+
return set([x[0] for x in cur.fetchall()])
|
25
|
+
except Exception, e:
|
26
|
+
# if we can't get the billing reports (e.g. the
|
27
|
+
# TimingAndEstimationPlugin isn't installed), silently continue
|
28
|
+
# without hiding anything.
|
29
|
+
return set()
|
30
|
+
|
31
|
+
class RowFilter(object):
|
32
|
+
"""A genshi filter that operates on table rows, completely hiding any that
|
33
|
+
are in the billing_reports table."""
|
34
|
+
|
35
|
+
def __init__(self, comp):
|
36
|
+
self.component = comp
|
37
|
+
self.billing_reports = get_billing_reports(comp)
|
38
|
+
self.component.log.debug('self.billing_reports= %r' % self.billing_reports)
|
39
|
+
|
40
|
+
def __call__(self, row_stream):
|
41
|
+
events = list(row_stream)
|
42
|
+
report_url = Stream(events) \
|
43
|
+
.select('td[@class="report"]/a/@href').render()
|
44
|
+
id = int(report_url.split('/')[-1])
|
45
|
+
|
46
|
+
if not id in self.billing_reports:
|
47
|
+
for kind,data,pos in Stream(events):
|
48
|
+
yield kind,data,pos
|
49
|
+
|
50
|
+
class ReportsFilter(Component):
|
51
|
+
"""Remove all billing reports from the reports list."""
|
52
|
+
implements(ITemplateStreamFilter)
|
53
|
+
|
54
|
+
def filter_stream(self, req, method, filename, stream, data):
|
55
|
+
if not filename == 'report_view.html':
|
56
|
+
return stream
|
57
|
+
self.log.debug("Applying Reports Filter to remove T&E reports")
|
58
|
+
return stream | Transformer(
|
59
|
+
'//table[@class="listing reports"]/tbody/tr'
|
60
|
+
).filter(RowFilter(self))
|
61
|
+
|
62
|
+
class ReportScreenFilter(Component):
|
63
|
+
"""Hides TandE reports even when you just go to the url"""
|
64
|
+
implements(ITemplateStreamFilter)
|
65
|
+
def __init__(self):
|
66
|
+
self.billing_reports = get_billing_reports(self)
|
67
|
+
self.log.debug('ReportScreenFilter: self.billing_reports= %r' % self.billing_reports)
|
68
|
+
|
69
|
+
def filter_stream(self, req, method, filename, stream, data):
|
70
|
+
if not filename == 'report_view.html':
|
71
|
+
return stream
|
72
|
+
reportid = [None]
|
73
|
+
|
74
|
+
def idhelper(strm):
|
75
|
+
header = strm[0][1]
|
76
|
+
if not reportid[0]:
|
77
|
+
self.log.debug("ReportScreenFilter: helper: %s %s %s"%(strm,header,report_id_from_text(header)))
|
78
|
+
reportid[0] = report_id_from_text(header)
|
79
|
+
for kind, data, pos in strm:
|
80
|
+
yield kind, data, pos
|
81
|
+
|
82
|
+
def permhelper(strm):
|
83
|
+
id = reportid[0]
|
84
|
+
self.log.debug("ReportScreenFilter: id:%s, in bill: %s has perm:%s" % (id, id in self.billing_reports, req.perm.has_permission("TIME_VIEW")))
|
85
|
+
if id and id in self.billing_reports and not req.perm.has_permission("TIME_VIEW"):
|
86
|
+
self.log.debug("ReportScreenFilter: No time view, prevent render")
|
87
|
+
msg = "YOU MUST HAVE TIME_VIEW PERMSSIONS TO VIEW THIS REPORT"
|
88
|
+
for kind, data, pos in tag.span(msg).generate():
|
89
|
+
yield kind, data, pos
|
90
|
+
else:
|
91
|
+
for kind, data, pos in strm:
|
92
|
+
yield kind, data, pos
|
93
|
+
|
94
|
+
self.log.debug("ReportScreenFilter: About to prevent rendering of billing reports without permissions")
|
95
|
+
stream = stream | Transformer('//div[@id="content"]/h1/text()').filter(idhelper)
|
96
|
+
stream = stream | Transformer('//div[@id="content"]').filter(permhelper)
|
97
|
+
return stream
|
98
|
+
|
99
|
+
## ENFORCE PERMISSIONS ON report exports
|
100
|
+
|
101
|
+
billing_report_fname_regex = re.compile("report_(?P<reportid>\d*)")
|
102
|
+
def report_id_from_filename(text):
|
103
|
+
if text:
|
104
|
+
m = billing_report_fname_regex.match(text)
|
105
|
+
if m:
|
106
|
+
return int(m.groupdict()["reportid"])
|
107
|
+
return -1;
|
108
|
+
|
109
|
+
|
110
|
+
def _send_csv(self, req, cols, rows, sep=',', mimetype='text/plain',
|
111
|
+
filename=None):
|
112
|
+
req.send_response(200)
|
113
|
+
req.send_header('Content-Type', mimetype + ';charset=utf-8')
|
114
|
+
if filename:
|
115
|
+
req.send_header('Content-Disposition', 'filename=' + filename)
|
116
|
+
req.end_headers()
|
117
|
+
|
118
|
+
id = report_id_from_filename(filename)
|
119
|
+
reports = get_billing_reports(self)
|
120
|
+
if id in reports and not req.perm.has_permission("TIME_VIEW"):
|
121
|
+
raise RequestDone
|
122
|
+
|
123
|
+
def iso_time(t):
|
124
|
+
return format_time(t, 'iso8601')
|
125
|
+
|
126
|
+
def iso_datetime(dt):
|
127
|
+
return format_datetime(dt, 'iso8601')
|
128
|
+
|
129
|
+
col_conversions = {
|
130
|
+
'time': iso_time,
|
131
|
+
'datetime': iso_datetime,
|
132
|
+
'changetime': iso_datetime,
|
133
|
+
'date': iso_datetime,
|
134
|
+
'created': iso_datetime,
|
135
|
+
'modified': iso_datetime,
|
136
|
+
}
|
137
|
+
|
138
|
+
converters = [col_conversions.get(c.strip('_'), unicode) for c in cols]
|
139
|
+
|
140
|
+
writer = csv.writer(req, delimiter=sep)
|
141
|
+
writer.writerow([unicode(c).encode('utf-8') for c in cols])
|
142
|
+
for row in rows:
|
143
|
+
row = list(row)
|
144
|
+
for i in xrange(len(row)):
|
145
|
+
row[i] = converters[i](row[i]).encode('utf-8')
|
146
|
+
writer.writerow(row)
|
147
|
+
|
148
|
+
raise RequestDone
|
149
|
+
|
150
|
+
ReportModule._send_csv = _send_csv
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import dbhelper
|
2
|
+
from trac.ticket.default_workflow import get_workflow_config
|
3
|
+
from sets import Set
|
4
|
+
|
5
|
+
def status_variables(statuses):
|
6
|
+
return ', '.join(['$'+i.upper().replace("_","").replace(" ","") for i in list(statuses)])
|
7
|
+
|
8
|
+
def get_statuses(com):
|
9
|
+
config = com.config
|
10
|
+
stats = get_statuses_from_workflow(config)
|
11
|
+
status_sql = """
|
12
|
+
SELECT DISTINCT status FROM ticket WHERE status <> '' ;
|
13
|
+
"""
|
14
|
+
stats |= Set(dbhelper.get_column_as_list(com, status_sql))
|
15
|
+
stats.difference_update(['', None])
|
16
|
+
return stats
|
17
|
+
|
18
|
+
def get_statuses_from_workflow(config):
|
19
|
+
wf = get_workflow_config(config)
|
20
|
+
x = Set()
|
21
|
+
for key, value in wf.items():
|
22
|
+
x.add(value['newstate'])
|
23
|
+
x |= Set(value['oldstates'])
|
24
|
+
x.difference_update([u'*'])
|
25
|
+
return x
|
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
from trac.web.api import ITemplateStreamFilter
|
2
|
+
from trac.perm import IPermissionRequestor
|
3
|
+
from trac.core import *
|
4
|
+
from genshi.core import *
|
5
|
+
from genshi.builder import tag
|
6
|
+
from sets import Set as set
|
7
|
+
from genshi.filters.transform import Transformer
|
8
|
+
from blackmagic import *
|
9
|
+
from trac.ticket.query import QueryModule
|
10
|
+
|
11
|
+
from StringIO import StringIO
|
12
|
+
import csv
|
13
|
+
from trac.mimeview.api import Context
|
14
|
+
from trac.resource import Resource
|
15
|
+
|
16
|
+
## MONKEY PATCH THE QUERY MODULE CSV EXPORT FN TO ENFORCE PERMISSIONS
|
17
|
+
def new_csv_export(self, req, query, sep=',', mimetype='text/plain'):
|
18
|
+
self.log.debug("T&E plugin has overridden QueryModule.csv_export so to enforce field permissions")
|
19
|
+
|
20
|
+
## find the columns that should be hidden
|
21
|
+
hidden_fields = []
|
22
|
+
fields = self.config.getlist(csection, 'fields', [])
|
23
|
+
self.log.debug('QueryModule.csv_export: found : %s' % fields)
|
24
|
+
for field in fields:
|
25
|
+
perms = self.config.getlist(csection, '%s.permission' % field, [])
|
26
|
+
#self.log.debug('QueryModule.csv_export: read permission config: %s has %s' % (field, perms))
|
27
|
+
for (perm, denial) in [s.split(":") for s in perms] :
|
28
|
+
perm = perm.upper()
|
29
|
+
#self.log.debug('QueryModule.csv_export: testing permission: %s:%s should act= %s' %
|
30
|
+
# (field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS")))
|
31
|
+
if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove","hide"]:
|
32
|
+
hidden_fields.append(field)
|
33
|
+
## END find the columns that should be hidden
|
34
|
+
|
35
|
+
content = StringIO()
|
36
|
+
cols = query.get_columns()
|
37
|
+
writer = csv.writer(content, delimiter=sep)
|
38
|
+
writer = csv.writer(content, delimiter=sep, quoting=csv.QUOTE_MINIMAL)
|
39
|
+
writer.writerow([unicode(c).encode('utf-8') for c in cols if c not in hidden_fields])
|
40
|
+
|
41
|
+
context = Context.from_request(req)
|
42
|
+
results = query.execute(req, self.env.get_db_cnx())
|
43
|
+
self.log.debug('QueryModule.csv_export: hidding columns %s' % hidden_fields)
|
44
|
+
for result in results:
|
45
|
+
ticket = Resource('ticket', result['id'])
|
46
|
+
if 'TICKET_VIEW' in req.perm(ticket):
|
47
|
+
values = []
|
48
|
+
for col in cols:
|
49
|
+
if col not in hidden_fields:
|
50
|
+
value = result[col]
|
51
|
+
if col in ('cc', 'reporter'):
|
52
|
+
value = Chrome(self.env).format_emails(context(ticket),
|
53
|
+
value)
|
54
|
+
values.append(unicode(value).encode('utf-8'))
|
55
|
+
writer.writerow(values)
|
56
|
+
return (content.getvalue(), '%s;charset=utf-8' % mimetype)
|
57
|
+
|
58
|
+
QueryModule.export_csv = new_csv_export
|
59
|
+
|
60
|
+
class TicketFormatFilter(Component):
|
61
|
+
"""Filtering the streams to alter the base format of the ticket"""
|
62
|
+
implements(ITemplateStreamFilter)
|
63
|
+
|
64
|
+
def filter_stream(self, req, method, filename, stream, data):
|
65
|
+
self.log.debug("TicketFormatFilter executing")
|
66
|
+
if not filename == 'ticket.html':
|
67
|
+
self.log.debug("TicketFormatFilter not the correct template")
|
68
|
+
return stream
|
69
|
+
|
70
|
+
self.log.debug("TicketFormatFilter disabling totalhours and removing header hours")
|
71
|
+
stream = disable_field(stream, "totalhours")
|
72
|
+
stream = remove_header(stream, "hours")
|
73
|
+
return stream
|
74
|
+
|
75
|
+
|
76
|
+
class QueryColumnPermissionFilter(Component):
|
77
|
+
""" Filtering the stream to remove """
|
78
|
+
implements(ITemplateStreamFilter)
|
79
|
+
|
80
|
+
## ITemplateStreamFilter
|
81
|
+
|
82
|
+
def filter_stream(self, req, method, filename, stream, data):
|
83
|
+
if not filename == "query.html":
|
84
|
+
self.log.debug('Not a query returning')
|
85
|
+
return stream
|
86
|
+
|
87
|
+
def make_col_helper(field):
|
88
|
+
def column_helper (column_stream):
|
89
|
+
s = Stream(column_stream)
|
90
|
+
val = s.select('//input/@value').render()
|
91
|
+
if val.lower() != field.lower(): #if we are the field just skip it
|
92
|
+
#identity stream filter
|
93
|
+
for kind, data, pos in s:
|
94
|
+
yield kind, data, pos
|
95
|
+
return column_helper
|
96
|
+
|
97
|
+
fields = self.config.getlist(csection, 'fields', [])
|
98
|
+
for field in fields:
|
99
|
+
self.log.debug('found : %s' % field)
|
100
|
+
perms = self.config.getlist(csection, '%s.permission' % field, [])
|
101
|
+
self.log.debug('read permission config: %s has %s' % (field, perms))
|
102
|
+
for (perm, denial) in [s.split(":") for s in perms] :
|
103
|
+
perm = perm.upper()
|
104
|
+
self.log.debug('testing permission: %s:%s should act= %s' %
|
105
|
+
(field, perm, (not req.perm.has_permission(perm) or perm == "ALWAYS")))
|
106
|
+
if (not req.perm.has_permission(perm) or perm == "ALWAYS") and denial.lower() in ["remove","hide"]:
|
107
|
+
# remove from the list of addable
|
108
|
+
stream = stream | Transformer(
|
109
|
+
'//select[@id="add_filter"]/option[@value="%s"]' % field
|
110
|
+
).replace(" ")
|
111
|
+
|
112
|
+
# remove from the list of columns
|
113
|
+
stream = stream | Transformer(
|
114
|
+
'//fieldset[@id="columns"]/div/label'
|
115
|
+
).filter(make_col_helper(field))
|
116
|
+
|
117
|
+
#remove from the results table
|
118
|
+
stream = stream | Transformer(
|
119
|
+
'//th[@class="%s"]' % field
|
120
|
+
).replace(" ")
|
121
|
+
stream = stream | Transformer(
|
122
|
+
'//td[@class="%s"]' % field
|
123
|
+
).replace(" ")
|
124
|
+
|
125
|
+
# remove from the filters
|
126
|
+
stream = stream | Transformer(
|
127
|
+
'//tr[@class="%s"]' % field
|
128
|
+
).replace(" ")
|
129
|
+
|
130
|
+
|
131
|
+
return stream
|
@@ -0,0 +1,84 @@
|
|
1
|
+
<?cs include "header.cs"?>
|
2
|
+
<?cs include "macros.cs"?>
|
3
|
+
|
4
|
+
<form method="post" action="<?cs var:billing_info.href ?>" >
|
5
|
+
<div id="content" class="billing">
|
6
|
+
<a href="<?cs var:billing_info.usermanual_href ?>" ><?cs var:billing_info.usermanual_title ?></a>
|
7
|
+
<div id="messages" >
|
8
|
+
<?cs each:item = billing_info.messages ?>
|
9
|
+
<div class="message" ><?cs var:item ?></div>
|
10
|
+
<?cs /each ?>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<table border="0" cellspacing="0" cellpadding="0" class="minorsection">
|
14
|
+
<tr>
|
15
|
+
<td colspan="2" >
|
16
|
+
<div class="minorsection">
|
17
|
+
<div class="label" >Billing Status:</div>
|
18
|
+
<label for="billable">Billable: </label>
|
19
|
+
<input id="billable" name="billable" type="checkbox" checked="true" />
|
20
|
+
|
21
|
+
|
|
22
|
+
<label for="unbillable">Not Billable: </label>
|
23
|
+
<input id="unbillable" name="unbillable" type="checkbox" />
|
24
|
+
</div>
|
25
|
+
<div class="minorsection">
|
26
|
+
<div class="label" >Status:</div>
|
27
|
+
<label for="new">New: </label>
|
28
|
+
<input id="new" name="new" type="checkbox" checked="true" />
|
29
|
+
|
|
30
|
+
<label for="assigned">Assigned: </label>
|
31
|
+
<input id="assigned" name="assigned" type="checkbox" checked="true" />
|
32
|
+
|
|
33
|
+
<label for="reopened">Reopened: </label>
|
34
|
+
<input id="reopened" name="reopened" type="checkbox" checked="true" />
|
35
|
+
|
|
36
|
+
<label for="closed">Closed: </label>
|
37
|
+
<input id="closed" name="closed" type="checkbox" checked="true" />
|
38
|
+
</div>
|
39
|
+
|
40
|
+
</td>
|
41
|
+
</tr><tr class="minorsection">
|
42
|
+
<td class="minorsectionleft" valign="top"><label for="startdate" >Start Date:</label></td>
|
43
|
+
<td class="minorsectionright"><input id="startdate" name="startdate" type="text" /> or:<br />
|
44
|
+
<label for="startbilling" >Choose an old billing date:</label><br />
|
45
|
+
<select id="startbilling" name="startbilling" >
|
46
|
+
<option value="" ></option>
|
47
|
+
<?cs each:item = billing_info.billdates ?>
|
48
|
+
<option value="<?cs var:item.value ?>" ><?cs var:item.text ?></option>
|
49
|
+
<?cs /each ?>
|
50
|
+
</select>
|
51
|
+
</td>
|
52
|
+
</tr><tr class="minorsection">
|
53
|
+
<td class="minorsectionleft" valign="top"><label for="enddate" >End Date:</label>
|
54
|
+
</td>
|
55
|
+
<td class="minorsectionright">
|
56
|
+
<input id="enddate" name="enddate" type="text" /> or:<br />
|
57
|
+
<label for="endbilling" >Choose an old billing date:</label><br />
|
58
|
+
<select id="endbilling" name="endbilling" >
|
59
|
+
<option value="" ></option>
|
60
|
+
<?cs each:item = billing_info.billdates ?>
|
61
|
+
<option value="<?cs var:item.value ?>" ><?cs var:item.text ?></option>
|
62
|
+
<?cs /each ?>
|
63
|
+
</select>
|
64
|
+
</td>
|
65
|
+
</tr>
|
66
|
+
</table>
|
67
|
+
<ul id="reportlinks">
|
68
|
+
<?cs each:report_group = billing_info.reports ?>
|
69
|
+
<li><?cs var:report_group.title ?>
|
70
|
+
<ul>
|
71
|
+
<?cs each:report = report_group.reports ?>
|
72
|
+
<li><a href="" onmouseover="linkify(this, '<?cs var:billing_info.report_base_href ?>/<?cs var:report.id ?>')" >
|
73
|
+
<?cs var:report.title ?>
|
74
|
+
</a></li>
|
75
|
+
<?cs /each ?>
|
76
|
+
</ul>
|
77
|
+
</li>
|
78
|
+
<?cs /each ?>
|
79
|
+
</ul>
|
80
|
+
<input type="submit" name="setbillingtime" value="Set Billing Time" onclick="return confirm('Are you sure that you want to create a billed date?')" />
|
81
|
+
|
82
|
+
</div>
|
83
|
+
</form>
|
84
|
+
<?cs include "footer.cs"?>
|
@@ -0,0 +1,104 @@
|
|
1
|
+
<!DOCTYPE html
|
2
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml"
|
5
|
+
xmlns:py="http://genshi.edgewall.org/"
|
6
|
+
xmlns:xi="http://www.w3.org/2001/XInclude">
|
7
|
+
<xi:include href="layout.html" />
|
8
|
+
<xi:include href="macros.html" />
|
9
|
+
|
10
|
+
<head>
|
11
|
+
<title>Time Tracking Management for Trac.11</title>
|
12
|
+
<script type="text/javascript" src="${chrome.htdocs_location}js/wikitoolbar.js"></script>
|
13
|
+
<script type="text/javascript" py:choose="">
|
14
|
+
$(document).ready(function() {
|
15
|
+
$("div.description").find("h1,h2,h3,h4,h5,h6").addAnchor("Link to this section");
|
16
|
+
});
|
17
|
+
</script>
|
18
|
+
</head>
|
19
|
+
|
20
|
+
<body>
|
21
|
+
<form method="post" action="${billing_info.href}" >
|
22
|
+
<div id="content" class="billing">
|
23
|
+
<a href="$billing_info.usermanual_href" >$billing_info.usermanual_title</a>
|
24
|
+
<div id="messages" >
|
25
|
+
<py:for each="item in billing_info.messages" >
|
26
|
+
<div class="message" >$item</div>
|
27
|
+
</py:for>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<table border="0" cellspacing="0" cellpadding="0" class="minorsection">
|
31
|
+
<tr>
|
32
|
+
<td colspan="2" >
|
33
|
+
<div class="minorsection">
|
34
|
+
<div class="label" >Billing Status:</div>
|
35
|
+
<label for="billable">Billable: </label>
|
36
|
+
<input id="billable" name="billable" type="checkbox" checked="true" />
|
37
|
+
|
38
|
+
|
|
39
|
+
<label for="unbillable">Not Billable: </label>
|
40
|
+
<input id="unbillable" name="unbillable" type="checkbox" />
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<div class="minorsection">
|
44
|
+
<div class="label" >Status:</div>
|
45
|
+
<py:for each="status in statuses" >
|
46
|
+
<label for="$status">${status.capitalize()}: </label>
|
47
|
+
<input id="$status" name="$status" type="checkbox" checked="true" />
|
48
|
+
</py:for>
|
49
|
+
<script>
|
50
|
+
<py:for each="status in statuses" >
|
51
|
+
addBillingField("$status", "checkbox", true);
|
52
|
+
</py:for>
|
53
|
+
</script>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
</td>
|
57
|
+
</tr><tr class="minorsection">
|
58
|
+
<td class="minorsectionleft" valign="top"><label for="startdate" >Start Date:</label></td>
|
59
|
+
<td class="minorsectionright"><input id="startdate" name="startdate" type="text" /> or:<br />
|
60
|
+
<label for="startbilling" >Choose an old billing date:</label><br />
|
61
|
+
<select id="startbilling" name="startbilling" >
|
62
|
+
<option value="" ></option>
|
63
|
+
<py:for each="item in billing_info.billdates" >
|
64
|
+
<option value="$item.value" >$item.text</option>
|
65
|
+
</py:for>
|
66
|
+
</select>
|
67
|
+
</td>
|
68
|
+
</tr><tr class="minorsection">
|
69
|
+
<td class="minorsectionleft" valign="top"><label for="enddate" >End Date:</label>
|
70
|
+
</td>
|
71
|
+
<td class="minorsectionright">
|
72
|
+
<input id="enddate" name="enddate" type="text" /> or:<br />
|
73
|
+
<label for="endbilling" >Choose an old billing date:</label><br />
|
74
|
+
<select id="endbilling" name="endbilling" >
|
75
|
+
<option value="" ></option>
|
76
|
+
<py:for each="item in billing_info.billdates" >
|
77
|
+
<option value="$item.value" >$item.text</option>
|
78
|
+
</py:for>
|
79
|
+
</select>
|
80
|
+
</td>
|
81
|
+
</tr>
|
82
|
+
</table>
|
83
|
+
<ul id="reportlinks">
|
84
|
+
<py:for each="key in reports" >
|
85
|
+
<li>${reports[key]["title"]}
|
86
|
+
<ul>
|
87
|
+
<py:for each="report in reports[key]['reports']">
|
88
|
+
<li><a href="" onmouseover="linkify(this, '$billing_info.report_base_href/$report.id')" >
|
89
|
+
$report.title
|
90
|
+
</a></li>
|
91
|
+
</py:for>
|
92
|
+
</ul>
|
93
|
+
</li>
|
94
|
+
</py:for>
|
95
|
+
|
96
|
+
</ul>
|
97
|
+
<input
|
98
|
+
py:if="is_time_admin"
|
99
|
+
type="submit" name="setbillingtime" value="Set Billing Time" onclick="return confirm('Are you sure that you want to create a billed date?')" />
|
100
|
+
|
101
|
+
</div>
|
102
|
+
</form>
|
103
|
+
</body>
|
104
|
+
</html>
|