keithsalisbury-subtrac 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION.yml +1 -1
- data/bin/subtrac +2 -0
- data/lib/subtrac.rb +85 -50
- data/lib/subtrac/config/config.yml +22 -19
- data/lib/subtrac/templates/location.erb +2 -2
- data/lib/subtrac/templates/projects/blank/trac/wiki/WikiStart +1 -1
- data/lib/subtrac/templates/trac.erb +1 -1
- data/lib/subtrac/templates/vhost.erb +6 -6
- metadata +1 -105
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/advancedworkflow/controller.py +0 -419
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.cfg +0 -3
- data/lib/subtrac/trac-plugins/advancedticketworkflowplugin/setup.py +0 -20
- data/lib/subtrac/trac-plugins/clientsplugin/clients/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action.py +0 -28
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_email.py +0 -168
- data/lib/subtrac/trac-plugins/clientsplugin/clients/action_zendesk_forum.py +0 -137
- data/lib/subtrac/trac-plugins/clientsplugin/clients/admin.py +0 -91
- data/lib/subtrac/trac-plugins/clientsplugin/clients/api.py +0 -199
- data/lib/subtrac/trac-plugins/clientsplugin/clients/client.py +0 -105
- data/lib/subtrac/trac-plugins/clientsplugin/clients/events.py +0 -287
- data/lib/subtrac/trac-plugins/clientsplugin/clients/eventsadmin.py +0 -71
- data/lib/subtrac/trac-plugins/clientsplugin/clients/htdocs/clients.css +0 -4
- data/lib/subtrac/trac-plugins/clientsplugin/clients/model.py +0 -135
- data/lib/subtrac/trac-plugins/clientsplugin/clients/processor.py +0 -70
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reportmanager.py +0 -142
- data/lib/subtrac/trac-plugins/clientsplugin/clients/reports.py +0 -231
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary.py +0 -27
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_milestone.py +0 -152
- data/lib/subtrac/trac-plugins/clientsplugin/clients/summary_ticketchanges.py +0 -160
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_client_events.html +0 -124
- data/lib/subtrac/trac-plugins/clientsplugin/clients/templates/admin_clients.html +0 -134
- data/lib/subtrac/trac-plugins/clientsplugin/cron/changes.xslt +0 -132
- data/lib/subtrac/trac-plugins/clientsplugin/cron/run-client-event +0 -97
- data/lib/subtrac/trac-plugins/clientsplugin/cron/summary.xslt +0 -161
- data/lib/subtrac/trac-plugins/clientsplugin/setup.py +0 -43
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/__init__.py +0 -4
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/burndownchart.py +0 -273
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursinplaceeditor.py +0 -44
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/hoursremaining.py +0 -36
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery-1.2.3.min.js +0 -32
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.js +0 -409
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/htdocs/jquery.jeditable.mini.js +0 -30
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/templates/edithours.html +0 -53
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/burndownchart.py +0 -181
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/hoursremaining.py +0 -66
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/tests/workloadchart.py +0 -47
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/utils.py +0 -93
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/estimationtools/workloadchart.py +0 -86
- data/lib/subtrac/trac-plugins/estimationtoolsplugin/setup.py +0 -20
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/SumRollups.js +0 -23
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/adw_tracdb.py +0 -128
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/git-post-receive +0 -40
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac-post-commit.py +0 -285
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/trac_billing.py +0 -173
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/__init__.py +0 -0
- data/lib/subtrac/trac-plugins/timingandestimationplugin/scripts/utils/mail.py +0 -164
- data/lib/subtrac/trac-plugins/timingandestimationplugin/setup.py +0 -69
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/__init__.py +0 -1
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/api.py +0 -292
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/blackmagic.py +0 -172
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/dbhelper.py +0 -178
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/billingplugin.css +0 -25
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/field_disabler.js +0 -6
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/formatDate.js +0 -356
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_centerwindow.js +0 -100
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/tip_followscroll.js +0 -84
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/js/wz_tooltip.js +0 -1149
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/linkifyer.js +0 -119
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/query.js +0 -73
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/htdocs/ticket.js +0 -165
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/query_webui.py +0 -28
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reportmanager.py +0 -221
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports.py +0 -675
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/reports_filter.py +0 -150
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/statuses.py +0 -25
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/tande_filters.py +0 -131
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.cs +0 -84
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/templates/billing.html +0 -104
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py +0 -194
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py +0 -62
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py +0 -28
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py +0 -127
- data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/webui.py +0 -129
- data/lib/subtrac/trac-plugins/worklogplugin/setup.py +0 -29
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/__init__.py +0 -1
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/api.py +0 -187
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.css +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jqModal.js +0 -67
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.mousewheel.pack.js +0 -12
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/jquery.timeentry.pack.js +0 -7
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/tracWorklog.js +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.css +0 -208
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/htdocs/ui.datepicker.js +0 -1439
- 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 +0 -80
- 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 +0 -336
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/reports.py +0 -598
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog.html +0 -45
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_stop.html +0 -70
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_user.html +0 -40
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/templates/worklog_webadminui.html +0 -59
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_daemon.py +0 -33
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/ticket_filter.py +0 -153
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/timeline_hook.py +0 -96
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/usermanual.py +0 -29
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/util.py +0 -31
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webadminui.py +0 -47
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/webui.py +0 -174
- data/lib/subtrac/trac-plugins/worklogplugin/worklog/xmlrpc.py +0 -73
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_daemon.py
DELETED
@@ -1,194 +0,0 @@
|
|
1
|
-
from trac.ticket import ITicketChangeListener, Ticket, ITicketManipulator
|
2
|
-
from trac.perm import PermissionCache
|
3
|
-
from trac.core import *
|
4
|
-
import datetime
|
5
|
-
|
6
|
-
def identity(x):
|
7
|
-
return x;
|
8
|
-
|
9
|
-
try:
|
10
|
-
import trac.util.datefmt
|
11
|
-
to_timestamp = trac.util.datefmt.to_timestamp
|
12
|
-
except Exception:
|
13
|
-
to_timestamp = identity
|
14
|
-
|
15
|
-
|
16
|
-
def save_custom_field_value( db, ticket_id, field, value ):
|
17
|
-
cursor = db.cursor();
|
18
|
-
cursor.execute("SELECT * FROM ticket_custom "
|
19
|
-
"WHERE ticket=%s and name=%s", (ticket_id, field))
|
20
|
-
if cursor.fetchone():
|
21
|
-
cursor.execute("UPDATE ticket_custom SET value=%s "
|
22
|
-
"WHERE ticket=%s AND name=%s",
|
23
|
-
(value, ticket_id, field))
|
24
|
-
else:
|
25
|
-
cursor.execute("INSERT INTO ticket_custom (ticket,name, "
|
26
|
-
"value) VALUES(%s,%s,%s)",
|
27
|
-
(ticket_id, field, value))
|
28
|
-
db.commit()
|
29
|
-
|
30
|
-
DONTUPDATE = "DONTUPDATE"
|
31
|
-
|
32
|
-
def save_ticket_change( db, ticket_id, author, change_time, field, oldvalue, newvalue, log, dontinsert=False):
|
33
|
-
"""tries to save a ticket change,
|
34
|
-
|
35
|
-
dontinsert means do not add the change if it didnt already exist
|
36
|
-
"""
|
37
|
-
if type(change_time) == datetime.datetime:
|
38
|
-
change_time = to_timestamp(change_time)
|
39
|
-
cursor = db.cursor();
|
40
|
-
sql = """SELECT * FROM ticket_change
|
41
|
-
WHERE ticket=%s and author=%s and time=%s and field=%s"""
|
42
|
-
|
43
|
-
cursor.execute(sql, (ticket_id, author, change_time, field))
|
44
|
-
if cursor.fetchone():
|
45
|
-
if oldvalue == DONTUPDATE:
|
46
|
-
cursor.execute("""UPDATE ticket_change SET newvalue=%s
|
47
|
-
WHERE ticket=%s and author=%s and time=%s and field=%s""",
|
48
|
-
( newvalue, ticket_id, author, change_time, field))
|
49
|
-
|
50
|
-
else:
|
51
|
-
cursor.execute("""UPDATE ticket_change SET oldvalue=%s, newvalue=%s
|
52
|
-
WHERE ticket=%s and author=%s and time=%s and field=%s""",
|
53
|
-
(oldvalue, newvalue, ticket_id, author, change_time, field))
|
54
|
-
else:
|
55
|
-
if oldvalue == DONTUPDATE:
|
56
|
-
oldvalue = '0'
|
57
|
-
if not dontinsert:
|
58
|
-
cursor.execute("""INSERT INTO ticket_change (ticket,time,author,field, oldvalue, newvalue)
|
59
|
-
VALUES(%s, %s, %s, %s, %s, %s)""",
|
60
|
-
(ticket_id, change_time, author, field, oldvalue, newvalue))
|
61
|
-
db.commit()
|
62
|
-
|
63
|
-
def delete_ticket_change( comp, ticket_id, author, change_time, field):
|
64
|
-
""" removes a ticket change from the database """
|
65
|
-
db=comp.env.get_db_cnx()
|
66
|
-
cursor = db.cursor();
|
67
|
-
sql = """DELETE FROM ticket_change
|
68
|
-
WHERE ticket=%s and author=%s and time=%s and field=%s"""
|
69
|
-
cursor.execute(sql, (ticket_id, author, change_time, field))
|
70
|
-
db.commit()
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
class TimeTrackingTicketObserver(Component):
|
76
|
-
implements(ITicketChangeListener)
|
77
|
-
def __init__(self):
|
78
|
-
pass
|
79
|
-
|
80
|
-
def watch_hours(self, ticket):
|
81
|
-
def readTicketValue(name, tipe, default=0):
|
82
|
-
if ticket.values.has_key(name):
|
83
|
-
return tipe(ticket.values[name] or default)
|
84
|
-
else:
|
85
|
-
cursor = self.env.get_db_cnx().cursor()
|
86
|
-
cursor.execute("SELECT * FROM ticket_custom where ticket=%s and name=%s" , (ticket.id, name))
|
87
|
-
val = cursor.fetchone()
|
88
|
-
if val:
|
89
|
-
return tipe(val[2] or default)
|
90
|
-
return default
|
91
|
-
|
92
|
-
#some european countries use , as the decimal separator
|
93
|
-
convertfloat = lambda x: float(str(x).replace(',','.'))
|
94
|
-
hours = readTicketValue("hours", convertfloat)
|
95
|
-
totalHours = readTicketValue("totalhours", convertfloat)
|
96
|
-
|
97
|
-
db = self.env.get_db_cnx()
|
98
|
-
ticket_id = ticket.id
|
99
|
-
cl = ticket.get_changelog()
|
100
|
-
|
101
|
-
self.log.debug("found hours: "+str(hours ));
|
102
|
-
#self.log.debug("Dir_ticket:"+str(dir(ticket)))
|
103
|
-
#self.log.debug("ticket.values:"+str(ticket.values))
|
104
|
-
#self.log.debug("changelog:"+str(cl))
|
105
|
-
|
106
|
-
most_recent_change = None
|
107
|
-
if cl:
|
108
|
-
most_recent_change = cl[-1];
|
109
|
-
change_time = most_recent_change[0]
|
110
|
-
author = most_recent_change[1]
|
111
|
-
else:
|
112
|
-
change_time = ticket.time_created
|
113
|
-
author = ticket.values["reporter"]
|
114
|
-
|
115
|
-
self.log.debug("Checking permissions")
|
116
|
-
perm = PermissionCache(self.env, author)
|
117
|
-
if not perm or not perm.has_permission("TIME_RECORD"):
|
118
|
-
self.log.debug("Skipping recording because no permission to affect time")
|
119
|
-
if hours != 0:
|
120
|
-
|
121
|
-
tup = (ticket_id, author, change_time, "hours")
|
122
|
-
self.log.debug("deleting ticket change %s %s %s %s" % tup)
|
123
|
-
try:
|
124
|
-
delete_ticket_change(self, ticket_id, author, change_time, "hours")
|
125
|
-
except Exception, e:
|
126
|
-
self.log.debug("FAIL: %s" % e)
|
127
|
-
self.log.debug("hours change deleted")
|
128
|
-
return
|
129
|
-
self.log.debug("passed permissions check")
|
130
|
-
|
131
|
-
## SAVE estimated hour
|
132
|
-
estimatedhours = readTicketValue("estimatedhours", convertfloat)
|
133
|
-
self.log.debug("found Estimated hours:"+str(estimatedhours))
|
134
|
-
db = self.env.get_db_cnx()
|
135
|
-
save_ticket_change( db, ticket_id, author, change_time, "estimatedhours", DONTUPDATE, str(estimatedhours), self.log, True)
|
136
|
-
save_custom_field_value( db, ticket.id, "estimatedhours", str(estimatedhours))
|
137
|
-
db.commit();
|
138
|
-
#######################
|
139
|
-
|
140
|
-
|
141
|
-
## If our hours changed
|
142
|
-
if not hours == 0:
|
143
|
-
newtotal = str(totalHours+hours)
|
144
|
-
save_ticket_change( db, ticket_id, author, change_time, "hours", '0.0', str(hours), self.log)
|
145
|
-
save_ticket_change( db, ticket_id, author, change_time, "totalhours", str(totalHours), str(newtotal), self.log)
|
146
|
-
save_custom_field_value( db, ticket_id, "hours", '0')
|
147
|
-
save_custom_field_value( db, ticket_id, "totalhours", str(newtotal) )
|
148
|
-
########################
|
149
|
-
|
150
|
-
# END of watch_hours
|
151
|
-
|
152
|
-
def ticket_created(self, ticket):
|
153
|
-
"""Called when a ticket is created."""
|
154
|
-
self.watch_hours(ticket)
|
155
|
-
|
156
|
-
|
157
|
-
def ticket_changed(self, ticket, comment, author, old_values):
|
158
|
-
"""Called when a ticket is modified.
|
159
|
-
|
160
|
-
`old_values` is a dictionary containing the previous values of the
|
161
|
-
fields that have changed.
|
162
|
-
"""
|
163
|
-
self.watch_hours(ticket)
|
164
|
-
|
165
|
-
def ticket_deleted(self, ticket):
|
166
|
-
"""Called when a ticket is deleted."""
|
167
|
-
|
168
|
-
class TimeTrackingTicketValidator(Component):
|
169
|
-
implements(ITicketManipulator)
|
170
|
-
|
171
|
-
def __init__(self):
|
172
|
-
pass
|
173
|
-
|
174
|
-
def prepare_ticket(req, ticket, fields, actions):
|
175
|
-
"""not currently called"""
|
176
|
-
|
177
|
-
def validate_ticket(self, req, ticket):
|
178
|
-
"""Validate a ticket after it's been populated from user input.
|
179
|
-
|
180
|
-
Must return a list of `(field, message)` tuples, one for each problem
|
181
|
-
detected. `field` can be `None` to indicate an overall problem with the
|
182
|
-
ticket. Therefore, a return value of `[]` means everything is OK."""
|
183
|
-
errors = []
|
184
|
-
#some european countries use , as the decimal separator
|
185
|
-
convertfloat = lambda x: float(str(x).replace(',','.'))
|
186
|
-
try:
|
187
|
-
convertfloat(ticket.values['hours'])
|
188
|
-
except ValueError:
|
189
|
-
errors.append(('Add Hours to Ticket', 'Value must be a number'))
|
190
|
-
try:
|
191
|
-
convertfloat(ticket.values['estimatedhours'])
|
192
|
-
except ValueError:
|
193
|
-
errors.append(('Estimated Number of Hours', 'Value must be a number'))
|
194
|
-
return errors
|
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_policy.py
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
from trac.core import *
|
2
|
-
from trac.perm import PermissionCache, IPermissionRequestor, IPermissionGroupProvider, IPermissionPolicy, PermissionSystem
|
3
|
-
from trac.ticket.model import Ticket
|
4
|
-
from trac.config import IntOption, ListOption
|
5
|
-
from trac.util.compat import set
|
6
|
-
|
7
|
-
class InternalTicketsPolicy(Component):
|
8
|
-
"""Hide internal tickets."""
|
9
|
-
implements(IPermissionPolicy)
|
10
|
-
group_providers = ExtensionPoint(IPermissionGroupProvider)
|
11
|
-
|
12
|
-
# IPermissionPolicy(Interface)
|
13
|
-
def check_permission(self, action, username, resource, perm):
|
14
|
-
self.log.debug("Internal: action:%s, user:%s, resource:%s, perm: %s" %
|
15
|
-
( action, username, resource, perm))
|
16
|
-
self.username = username
|
17
|
-
# Look up the resource parentage for a ticket.
|
18
|
-
while resource:
|
19
|
-
if resource.realm == 'ticket':
|
20
|
-
break
|
21
|
-
resource = resource.parent
|
22
|
-
if resource and resource.realm == 'ticket' and resource.id is not None:
|
23
|
-
rtn = self.check_ticket_access(perm, resource, username)
|
24
|
-
self.log.debug("Internal: RESULTS for %s: %s" % (action,rtn))
|
25
|
-
return rtn
|
26
|
-
return None
|
27
|
-
|
28
|
-
# Internal methods
|
29
|
-
def _get_groups(self, user):
|
30
|
-
# Get initial subjects
|
31
|
-
groups = set([user])
|
32
|
-
for provider in self.group_providers:
|
33
|
-
for group in provider.get_permission_groups(user):
|
34
|
-
groups.add(group)
|
35
|
-
|
36
|
-
perms = PermissionSystem(self.env).get_all_permissions()
|
37
|
-
repeat = True
|
38
|
-
while repeat:
|
39
|
-
repeat = False
|
40
|
-
for subject, action in perms:
|
41
|
-
if subject in groups and action.islower() and action not in groups:
|
42
|
-
groups.add(action)
|
43
|
-
repeat = True
|
44
|
-
|
45
|
-
return groups
|
46
|
-
|
47
|
-
# Public methods
|
48
|
-
def check_ticket_access(self, perm, res, user):
|
49
|
-
"""Return if this req is permitted access to the given ticket ID."""
|
50
|
-
try:
|
51
|
-
tkt = Ticket(self.env, res.id)
|
52
|
-
except TracError:
|
53
|
-
return None # Ticket doesn't exist
|
54
|
-
private_tkt = tkt['internal'] == '1'
|
55
|
-
|
56
|
-
if private_tkt:
|
57
|
-
# cant just check or we get in an infinite call loop
|
58
|
-
perm = PermissionCache(self.env, self.username, None, perm._cache)
|
59
|
-
groups = self._get_groups(user)
|
60
|
-
perm_or_group = self.config.get('ticket', 'internalgroup', 'TIME_ADMIN' )
|
61
|
-
return perm_or_group in groups or perm.has_permission(perm_or_group)
|
62
|
-
return None
|
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/ticket_webui.py
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
from trac.web.api import ITemplateStreamFilter
|
3
|
-
from trac.log import logger_factory
|
4
|
-
from trac.core import *
|
5
|
-
from genshi.builder import tag
|
6
|
-
from trac.web import IRequestHandler
|
7
|
-
from trac.util import Markup
|
8
|
-
from trac.web.href import Href
|
9
|
-
from genshi.filters.transform import Transformer
|
10
|
-
|
11
|
-
|
12
|
-
class TicketWebUiAddon(Component):
|
13
|
-
implements(ITemplateStreamFilter)
|
14
|
-
|
15
|
-
def __init__(self):
|
16
|
-
pass
|
17
|
-
|
18
|
-
# ITemplateStreamFilter
|
19
|
-
def filter_stream(self, req, method, filename, stream, data):
|
20
|
-
self.log.debug("TicketWebUiAddon executing")
|
21
|
-
if not filename == 'ticket.html':
|
22
|
-
self.log.debug("TicketWebUiAddon not the correct template")
|
23
|
-
return stream
|
24
|
-
stream = stream | Transformer('//div[@id="banner"]').before(
|
25
|
-
tag.script(type="text/javascript",
|
26
|
-
src=req.href.chrome("Billing", "ticket.js"))()
|
27
|
-
)
|
28
|
-
return stream
|
data/lib/subtrac/trac-plugins/timingandestimationplugin/timingandestimationplugin/usermanual.py
DELETED
@@ -1,127 +0,0 @@
|
|
1
|
-
user_manual_title = "Timing and Estimation Plugin User Manual"
|
2
|
-
user_manual_version = 13
|
3
|
-
user_manual_wiki_title = "TimingAndEstimationPluginUserManual"
|
4
|
-
user_manual_content = """
|
5
|
-
|
6
|
-
[[PageOutline]]
|
7
|
-
= Timing and Estimation Plugin User Manual =
|
8
|
-
[http://trac-hacks.org/wiki/TimingAndEstimationPlugin TimingAndEstimationPlugin on TracHacks] | [http://trac-hacks.org/report/9?COMPONENT=TimingAndEstimationPlugin Open Tickets] | [http://trac-hacks.org/newticket?component=TimingAndEstimationPlugin&owner=bobbysmith007 New Ticket] |
|
9
|
-
[http://trac-hacks.org/browser/timingandestimationplugin/trunk Web Browsable Source Code]
|
10
|
-
|
11
|
-
== Abstract Design Goal ==
|
12
|
-
My goal in writing this plugin was to use as much of the existing structure as possible (therefore not needing to add extra structure that might make maintainability difficult). The largest downside I have found to this is that there is no way to attach more permissions to anything.
|
13
|
-
|
14
|
-
== Custom Ticket Fields ==
|
15
|
-
In adhering to our design goal, rather than creating a new ticket interface, I create some custom fields and a small daemon to watch over them.
|
16
|
-
|
17
|
-
=== Fields: ===
|
18
|
-
* '''Hours to Add''' This field functions as a time tracker. When you add hours to it , those hours get added to the total hours field. The person who made the change is there fore credited with the hours spent on it.
|
19
|
-
* '''Total Hours''' This field is the total number of hours that have been added to the project. This has been made uneditable by including javascript which replaces the input box with a span containing its value
|
20
|
-
* Reports might not agree with each other if this is manually edited (which is possible if you disable javascript).
|
21
|
-
* '''Is this billable?''' An extra flag on tickets so that they can be marked as billable / not billable.
|
22
|
-
* '''Estimated Hours''' a field that contains the estimated amount of work
|
23
|
-
=== Future Fields ===
|
24
|
-
* '''Ticket Rate''' The ability to attach a cost per hour or total amount to an individual ticket
|
25
|
-
|
26
|
-
== Billing / Management Page / Time Reports ==
|
27
|
-
This page provide a small interface for querying the tickets and adding a bill date at the current time.
|
28
|
-
This interface mostly just gives you links that match the interface to open any of the give reports,
|
29
|
-
providing it the correct set of input parameters
|
30
|
-
|
31
|
-
The direct url is '/Billing'.
|
32
|
-
|
33
|
-
=== No Permissions Branch ===
|
34
|
-
The 'Management' button should be in the main title bar. It is possible that if you are viewing at a low resolution, it was pushed off the edge of the screen. Also if you are not logged in with report_view permissions, it will not show that button.
|
35
|
-
|
36
|
-
=== Permissions Branch ===
|
37
|
-
The 'Time Reports' button should be in the main title bar. Whether or not you see this will be based on whether your user has TIME_VIEW permissions.
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
=== Set Bill Date ===
|
43
|
-
|
44
|
-
This button will add now as a bill date. This is mostly to make it
|
45
|
-
easier to select the last time you billed. This would allow you to
|
46
|
-
set a ticket as having been billed at a given time while others have
|
47
|
-
not, and accurately get the correct billing information for all
|
48
|
-
tickets.
|
49
|
-
|
50
|
-
== Reports ==
|
51
|
-
=== Report Types ===
|
52
|
-
We provide a few different reports for querying different types of data:
|
53
|
-
* '''Billing Reports''' Currently the billing reports are the only time based reports, and are therefore useful for getting an estimate what tickets had times (and totals), and which developers spent their time where.
|
54
|
-
* Ticket Work Summary
|
55
|
-
* Milestone Work Summary
|
56
|
-
* Developer Work Summary
|
57
|
-
* '''Ticket/Hour Reports''' These reports are useful for reviewing estimates on a large scale or getting an idea of the project at large. These reports currently ignore the time.
|
58
|
-
* Ticket Hours
|
59
|
-
* Ticket Hours with Description
|
60
|
-
* Ticket Hours Grouped By Component
|
61
|
-
* Ticket Hours Grouped By Component with Description
|
62
|
-
* Ticket Hours Grouped By Milestone
|
63
|
-
* Ticket Hours Grouped By Milestone with Description
|
64
|
-
=== Adding More Reports ===
|
65
|
-
To add reports to the Management screen sections, you must run the following sql against your trac database
|
66
|
-
Remember to fill in the @reportID of the report you want to insert, and to select the insert statement for the section of your choice.
|
67
|
-
* {{{INSERT INTO custom_report (id, uuid, maingroup, subgroup, version, ordering) VALUES (@reportID , @uuid, 'Timing and Estimation Plugin', 'Billing Reports', 1, 0);}}}
|
68
|
-
* {{{INSERT INTO custom_report (id, uuid, maingroup, subgroup, version, ordering) VALUES (@reportID , @uuid, 'Timing and Estimation Plugin', 'Ticket/Hour Reports', 1, 0);}}}
|
69
|
-
|
70
|
-
''NB: @uuid is a globally uninque identifier created via a tool such as {{{uuidgen}}} on Linux or various [http://www.famkruithof.net/uuid/uuidgen online tools]. It is used in this plugin to provide programatic reference to specific reports such that they can be upgraded successfully on future revisions of the plugin''
|
71
|
-
|
72
|
-
=== Removing a Report ===
|
73
|
-
To remove reports from the Management page, run the following query.
|
74
|
-
Remember to fill in the @reportID of the report you want to modify.
|
75
|
-
* To remove for this version of the plugin (will be over written in future plugin upgrades)
|
76
|
-
* {{{UPDATE custom_report SET maingroup='x'||maingroup WHERE report = @reportID;}}}
|
77
|
-
* To remove permanently (wont be over written in future plugin upgrades)
|
78
|
-
* {{{UPDATE custom_report SET version=9999, maingroup='x'||maingroup WHERE report = @reportID;}}}
|
79
|
-
''NB: The 'x' part is not important - you just need to make the column read something other than 'Timing and Estimation Plugin'.''
|
80
|
-
|
81
|
-
=== TAKE NOTE ===
|
82
|
-
'''The reports can only be called from the Management Page. They will not work from the Trac View Tickets page. (Due to the custom variables that need values).'''
|
83
|
-
|
84
|
-
== Permissions Branch ==
|
85
|
-
Recently a branch of this plugin was sponsored by [http://www.obsidiansoft.com/ Obsidian Software] so that it would support per field permissions.
|
86
|
-
|
87
|
-
This is accomplished with Genshi 5 stream filters in trac 11. This code draws from the [http://trac-hacks.org/wiki/BlackMagicTicketTweaksPlugin BlackMagicTicketTweaksPlugin]
|
88
|
-
|
89
|
-
=== Configuration ===
|
90
|
-
There is a new trac.ini configuration section which is filled in by default as follows.
|
91
|
-
{{{
|
92
|
-
#!ini
|
93
|
-
[field settings] # per field permissions
|
94
|
-
|
95
|
-
# a list of all the fields to apply permissions to
|
96
|
-
fields = billable, totalhours, hours, estimatedhours
|
97
|
-
|
98
|
-
# a bunch of:
|
99
|
-
# field.permission = PERMISSION:consequence
|
100
|
-
# where consequence is one of: hide, remove, disable
|
101
|
-
# hide - replaces with hidden input
|
102
|
-
# remove - removes element
|
103
|
-
# disable - removes input in favor of text
|
104
|
-
billable.permission = TIME_VIEW:hide, TIME_RECORD:disable
|
105
|
-
totalhours.permission = TIME_VIEW:remove, TIME_RECORD:disable
|
106
|
-
hours.permission = TIME_RECORD:remove
|
107
|
-
estimatedhours.permission = TIME_RECORD:disable
|
108
|
-
}}}
|
109
|
-
|
110
|
-
|
111
|
-
It also adds an "Internal" checkbox which allows you to set a ticket as internal. For this policy to work correctly you need to add a line to the trac section of the config telling it which permission policies to use. (The setup will attempt to put this line of configuration in place. ) The permission that looks at currently is 'TIME_ADMIN'. To change that group set the internalgroup of the ticket section in the trac.ini as follows:
|
112
|
-
|
113
|
-
{{{
|
114
|
-
#!ini
|
115
|
-
[ticket]
|
116
|
-
internalgroup = TRAC_ADMIN
|
117
|
-
|
118
|
-
[trac]
|
119
|
-
permission_policies = InternalTicketsPolicy, DefaultPermissionPolicy, LegacyAttachmentPolicy
|
120
|
-
}}}
|
121
|
-
|
122
|
-
|
123
|
-
== Future Improvements ==
|
124
|
-
* [http://trac-hacks.org/wiki/TimingAndEstimationPlugin See tickets] at the [http://trac-hacks.org/wiki/TimingAndEstimationPlugin project trac]
|
125
|
-
|
126
|
-
|
127
|
-
"""
|
@@ -1,129 +0,0 @@
|
|
1
|
-
from pkg_resources import resource_filename
|
2
|
-
import re
|
3
|
-
import time
|
4
|
-
import datetime
|
5
|
-
import dbhelper
|
6
|
-
from sets import Set
|
7
|
-
from usermanual import *
|
8
|
-
from trac.log import logger_factory
|
9
|
-
from trac.core import *
|
10
|
-
from trac.web import IRequestHandler
|
11
|
-
from trac.perm import IPermissionRequestor
|
12
|
-
from trac.util import Markup
|
13
|
-
from trac.web.chrome import add_stylesheet, add_script, \
|
14
|
-
INavigationContributor, ITemplateProvider
|
15
|
-
from trac.web.href import Href
|
16
|
-
from reportmanager import CustomReportManager
|
17
|
-
from statuses import get_statuses
|
18
|
-
|
19
|
-
#get_statuses = api.get_statuses
|
20
|
-
|
21
|
-
class TimingEstimationAndBillingPage(Component):
|
22
|
-
implements(IPermissionRequestor, INavigationContributor, IRequestHandler, ITemplateProvider)
|
23
|
-
|
24
|
-
def __init__(self):
|
25
|
-
pass
|
26
|
-
|
27
|
-
# IPermissionRequestor methods
|
28
|
-
def get_permission_actions(self):
|
29
|
-
return ["TIME_VIEW", "TIME_RECORD", ("TIME_ADMIN", ["TIME_RECORD", "TIME_VIEW"])]
|
30
|
-
|
31
|
-
def set_bill_date(self, username="Timing and Estimation Plugin", when=0):
|
32
|
-
now = time.time()
|
33
|
-
if not when:
|
34
|
-
when = now
|
35
|
-
when = int(when)
|
36
|
-
now = int(now)
|
37
|
-
dtwhen = datetime.datetime.fromtimestamp(when);
|
38
|
-
strwhen = "%s-%s-%s %#02d:%#02d:%#02d" % \
|
39
|
-
(dtwhen.year, dtwhen.month, dtwhen.day, dtwhen.hour,dtwhen.minute, dtwhen.second)
|
40
|
-
sql = """
|
41
|
-
INSERT INTO bill_date (time, set_when, str_value)
|
42
|
-
VALUES (%s, %s, %s)
|
43
|
-
"""
|
44
|
-
dbhelper.execute_non_query(self, sql, when, now, strwhen)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
# INavigationContributor methods
|
52
|
-
def get_active_navigation_item(self, req):
|
53
|
-
val = re.search('/Billing$', req.path_info)
|
54
|
-
if val and val.start() == 0:
|
55
|
-
return "Billing"
|
56
|
-
else:
|
57
|
-
return ""
|
58
|
-
|
59
|
-
def get_navigation_items(self, req):
|
60
|
-
url = req.href.Billing()
|
61
|
-
if req.perm.has_permission("TIME_VIEW"):
|
62
|
-
yield 'mainnav', "Billing", \
|
63
|
-
Markup('<a href="%s">%s</a>' % \
|
64
|
-
(url , "Time Reports"))
|
65
|
-
|
66
|
-
# IRequestHandler methods
|
67
|
-
def set_request_billing_dates(self, data):
|
68
|
-
billing_dates = []
|
69
|
-
billing_time_sql = """
|
70
|
-
SELECT DISTINCT time as value, str_value as text
|
71
|
-
FROM bill_date
|
72
|
-
"""
|
73
|
-
rs = dbhelper.get_result_set(self, billing_time_sql)
|
74
|
-
if rs:
|
75
|
-
for (value, text) in rs.rows:
|
76
|
-
billing_info = {'text':text , 'value':value}
|
77
|
-
billing_dates.extend([billing_info])
|
78
|
-
#self.log.debug("bill-dates: %s"%billing_dates)
|
79
|
-
data['billing_info']["billdates"] = billing_dates
|
80
|
-
|
81
|
-
def match_request(self, req):
|
82
|
-
val = re.search('/Billing$', req.path_info)
|
83
|
-
return val and val.start() == 0
|
84
|
-
|
85
|
-
def process_request(self, req):
|
86
|
-
messages = []
|
87
|
-
req.perm.require("TIME_VIEW")
|
88
|
-
def addMessage(s):
|
89
|
-
messages.extend([s]);
|
90
|
-
|
91
|
-
if req.method == 'POST':
|
92
|
-
req.perm.require("TIME_VIEW")
|
93
|
-
if req.args.has_key('setbillingtime'):
|
94
|
-
self.set_bill_date(req.authname)
|
95
|
-
addMessage("All tickets last bill date updated")
|
96
|
-
|
97
|
-
mgr = CustomReportManager(self.env, self.log)
|
98
|
-
data = {};
|
99
|
-
data["is_time_admin"] = req.perm.has_permission("TIME_ADMIN")
|
100
|
-
data["statuses"] = get_statuses(self)
|
101
|
-
data["reports"] = mgr.get_reports_by_group(CustomReportManager.TimingAndEstimationKey);
|
102
|
-
#self.log.debug("DEBUG got %s, %s" % (data["reports"], type(data["reports"])));
|
103
|
-
data["billing_info"] = {"messages": messages,
|
104
|
-
"href": req.href.Billing(),
|
105
|
-
"report_base_href": req.href.report(),
|
106
|
-
"usermanual_href": req.href.wiki(user_manual_wiki_title),
|
107
|
-
"usermanual_title": user_manual_title }
|
108
|
-
|
109
|
-
self.set_request_billing_dates(data)
|
110
|
-
|
111
|
-
add_stylesheet(req, "Billing/billingplugin.css")
|
112
|
-
add_script(req, "Billing/linkifyer.js")
|
113
|
-
return 'billing.html', data, None
|
114
|
-
|
115
|
-
|
116
|
-
# ITemplateProvider
|
117
|
-
def get_htdocs_dirs(self):
|
118
|
-
"""Return the absolute path of a directory containing additional
|
119
|
-
static resources (such as images, style sheets, etc).
|
120
|
-
"""
|
121
|
-
return [('Billing', resource_filename(__name__, 'htdocs'))]
|
122
|
-
|
123
|
-
def get_templates_dirs(self):
|
124
|
-
"""Return the absolute path of the directory containing the provided
|
125
|
-
genshi templates.
|
126
|
-
"""
|
127
|
-
rtn = [resource_filename(__name__, 'templates')]
|
128
|
-
return rtn
|
129
|
-
|