keithsalisbury-subtrac 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +7 -0
  3. data/Rakefile +56 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/subtrac +40 -0
  6. data/lib/subtrac.rb +245 -0
  7. data/lib/subtrac/common/clients/index.wsgi +89 -0
  8. data/lib/subtrac/common/favicon.ico +0 -0
  9. data/lib/subtrac/common/images/trac/banner_bg.jpg +0 -0
  10. data/lib/subtrac/common/images/trac/bar_bg.gif +0 -0
  11. data/lib/subtrac/common/images/trac/footer_back.png +0 -0
  12. data/lib/subtrac/common/images/trac/main_bg.gif +0 -0
  13. data/lib/subtrac/common/images/trac/saint_logo_small.png +0 -0
  14. data/lib/subtrac/common/static/404.html +14 -0
  15. data/lib/subtrac/common/styles/trac.css +222 -0
  16. data/lib/subtrac/common/trac.ini +178 -0
  17. data/lib/subtrac/config/config.yml +54 -0
  18. data/lib/subtrac/passwords +1 -0
  19. data/lib/subtrac/shared/trac.ini +178 -0
  20. data/lib/subtrac/templates/location.erb +16 -0
  21. data/lib/subtrac/templates/projects/blank/svn/branches/README +0 -0
  22. data/lib/subtrac/templates/projects/blank/svn/tags/README +0 -0
  23. data/lib/subtrac/templates/projects/blank/svn/trunk/README +0 -0
  24. data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +57 -0
  25. data/lib/subtrac/templates/projects/new/svn/trunk/trac/wiki/WikiStart +46 -0
  26. data/lib/subtrac/templates/projects/new/trac/wiki/WikiStart +23 -0
  27. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/index/index.html +22 -0
  28. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/layout.html +56 -0
  29. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/site.html +27 -0
  30. data/lib/subtrac/templates/projects/trac_theme/svn/trunk/templates/theme.html +86 -0
  31. data/lib/subtrac/templates/projects/trac_theme/trac/wiki/WikiStart +4 -0
  32. data/lib/subtrac/templates/trac.erb +25 -0
  33. data/lib/subtrac/templates/vhost.erb +35 -0
  34. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
  35. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +419 -0
  36. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +3 -0
  37. data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +20 -0
  38. data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
  39. data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +28 -0
  40. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +168 -0
  41. data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +137 -0
  42. data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +91 -0
  43. data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +199 -0
  44. data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +105 -0
  45. data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +287 -0
  46. data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +71 -0
  47. data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +4 -0
  48. data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +135 -0
  49. data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +70 -0
  50. data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +142 -0
  51. data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +231 -0
  52. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +27 -0
  53. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +152 -0
  54. data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +160 -0
  55. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +124 -0
  56. data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +134 -0
  57. data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +132 -0
  58. data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +97 -0
  59. data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +161 -0
  60. data/lib/subtrac/trac-plugins/clientsplugin/setup.py +43 -0
  61. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +4 -0
  62. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +273 -0
  63. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +44 -0
  64. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +36 -0
  65. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +32 -0
  66. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +409 -0
  67. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +30 -0
  68. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +53 -0
  69. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +181 -0
  70. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +66 -0
  71. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +47 -0
  72. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +93 -0
  73. data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +86 -0
  74. data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +20 -0
  75. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +23 -0
  76. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +128 -0
  77. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +40 -0
  78. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +285 -0
  79. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +173 -0
  80. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
  81. data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +164 -0
  82. data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +69 -0
  83. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +1 -0
  84. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +292 -0
  85. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +172 -0
  86. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +178 -0
  87. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +25 -0
  88. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +6 -0
  89. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +356 -0
  90. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +100 -0
  91. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +84 -0
  92. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +1149 -0
  93. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +119 -0
  94. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +73 -0
  95. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +165 -0
  96. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +28 -0
  97. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +221 -0
  98. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +675 -0
  99. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +150 -0
  100. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +25 -0
  101. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +131 -0
  102. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +84 -0
  103. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +104 -0
  104. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +194 -0
  105. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +62 -0
  106. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +28 -0
  107. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +127 -0
  108. data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +129 -0
  109. data/lib/subtrac/trac-plugins/worklogplugin/setup.py +29 -0
  110. data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +1 -0
  111. data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +187 -0
  112. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +40 -0
  113. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +67 -0
  114. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +12 -0
  115. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +7 -0
  116. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +40 -0
  117. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +208 -0
  118. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +1439 -0
  119. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.png +0 -0
  120. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/work.xcf +0 -0
  121. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/worklogplugin.css +80 -0
  122. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstart.png +0 -0
  123. data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/workstop.png +0 -0
  124. data/lib/subtrac/trac-plugins/worklogplugin/worklog/manager.py +336 -0
  125. data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +598 -0
  126. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +45 -0
  127. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +70 -0
  128. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +40 -0
  129. data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +59 -0
  130. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +33 -0
  131. data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +153 -0
  132. data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +96 -0
  133. data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +29 -0
  134. data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +31 -0
  135. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +47 -0
  136. data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +174 -0
  137. data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +73 -0
  138. data/lib/subtrac/version.rb +4 -0
  139. metadata +191 -0
@@ -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
@@ -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
+ &nbsp;&nbsp;|&nbsp;&nbsp;
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
+ &nbsp;&nbsp;|&nbsp;&nbsp;
30
+ <label for="assigned">Assigned: </label>
31
+ <input id="assigned" name="assigned" type="checkbox" checked="true" />
32
+ &nbsp;&nbsp;|&nbsp;&nbsp;
33
+ <label for="reopened">Reopened: </label>
34
+ <input id="reopened" name="reopened" type="checkbox" checked="true" />
35
+ &nbsp;&nbsp;|&nbsp;&nbsp;
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?')" />&nbsp;
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
+ &nbsp;&nbsp;|&nbsp;&nbsp;
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?')" />&nbsp;
100
+
101
+ </div>
102
+ </form>
103
+ </body>
104
+ </html>