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
@@ -1,168 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import locale
|
5
|
-
import time
|
6
|
-
import codecs
|
7
|
-
from datetime import datetime
|
8
|
-
from StringIO import StringIO
|
9
|
-
|
10
|
-
from trac import __version__
|
11
|
-
from trac.core import *
|
12
|
-
from trac.env import open_environment
|
13
|
-
from trac.util.datefmt import format_date, to_datetime
|
14
|
-
from trac.wiki import wiki_to_html
|
15
|
-
from genshi import escape
|
16
|
-
|
17
|
-
from lxml import etree
|
18
|
-
from clients.action import IClientActionProvider
|
19
|
-
|
20
|
-
|
21
|
-
class ClientActionEmail(Component):
|
22
|
-
implements(IClientActionProvider)
|
23
|
-
|
24
|
-
client = None
|
25
|
-
debug = False
|
26
|
-
|
27
|
-
def get_name(self):
|
28
|
-
return "Send Email"
|
29
|
-
|
30
|
-
def get_description(self):
|
31
|
-
return "Send an email to a certain list of addresses"
|
32
|
-
|
33
|
-
def options(self, client=None):
|
34
|
-
if client is None:
|
35
|
-
yield {'name': 'XSLT', 'description': 'Formatting XSLT to convert the summary to an email', 'type': 'large'}
|
36
|
-
yield {'name': 'Subject', 'description': 'Email subject (use %s to replace the active client name)', 'type': 'medium'}
|
37
|
-
else:
|
38
|
-
yield {'name': 'Email Addresses', 'description': 'Comma separated list of email addresses', 'type': 'medium'}
|
39
|
-
|
40
|
-
|
41
|
-
def init(self, event, client):
|
42
|
-
self.client = client
|
43
|
-
if not event.action_options.has_key('XSLT') or not event.action_options['XSLT']['value']:
|
44
|
-
return False
|
45
|
-
try:
|
46
|
-
self.transform = etree.XSLT(etree.fromstring(str(event.action_options['XSLT']['value'])))
|
47
|
-
except:
|
48
|
-
self.env.log.error("Error: Cannot load/parse stylesheet")
|
49
|
-
return False
|
50
|
-
|
51
|
-
if not event.action_client_options.has_key('Email Addresses') or not event.action_client_options['Email Addresses']['value']:
|
52
|
-
return False
|
53
|
-
|
54
|
-
self.emails = []
|
55
|
-
for email in event.action_client_options['Email Addresses']['value'].replace(',', ' ').split(' '):
|
56
|
-
if '' != email.strip():
|
57
|
-
self.emails.append(email.strip())
|
58
|
-
|
59
|
-
if not self.emails:
|
60
|
-
return False
|
61
|
-
|
62
|
-
if not event.action_options.has_key('Subject') or not event.action_options['Subject']['value']:
|
63
|
-
self.subject = 'Ticket Summary for %s'
|
64
|
-
else:
|
65
|
-
self.subject = event.action_options['Subject']['value']
|
66
|
-
|
67
|
-
if self.subject.find('%s') >= 0:
|
68
|
-
self.subject = self.subject % (client,)
|
69
|
-
|
70
|
-
return True
|
71
|
-
|
72
|
-
|
73
|
-
def perform(self, req, summary):
|
74
|
-
if summary is None:
|
75
|
-
return False
|
76
|
-
self.config = self.env.config
|
77
|
-
self.encoding = 'utf-8'
|
78
|
-
subject = self.subject
|
79
|
-
|
80
|
-
if not self.config.getbool('notification', 'smtp_enabled'):
|
81
|
-
return False
|
82
|
-
smtp_server = self.config['notification'].get('smtp_server')
|
83
|
-
smtp_port = self.config['notification'].getint('smtp_port')
|
84
|
-
from_email = self.config['notification'].get('smtp_from')
|
85
|
-
from_name = self.config['notification'].get('smtp_from_name')
|
86
|
-
replyto_email = self.config['notification'].get('smtp_replyto')
|
87
|
-
from_email = from_email or replyto_email
|
88
|
-
if not from_email:
|
89
|
-
return False
|
90
|
-
|
91
|
-
# Authentication info (optional)
|
92
|
-
user_name = self.config['notification'].get('smtp_user')
|
93
|
-
password = self.config['notification'].get('smtp_password')
|
94
|
-
|
95
|
-
# Thanks to the author of this recipe:
|
96
|
-
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473810
|
97
|
-
|
98
|
-
from email.MIMEMultipart import MIMEMultipart
|
99
|
-
from email.MIMEText import MIMEText
|
100
|
-
from email.MIMEImage import MIMEImage
|
101
|
-
from email.Charset import add_charset, SHORTEST
|
102
|
-
add_charset( 'utf-8', SHORTEST, None, None )
|
103
|
-
|
104
|
-
projname = self.config.get('project', 'name')
|
105
|
-
|
106
|
-
# Create the root message and fill in the from, to, and subject headers
|
107
|
-
msg_root = MIMEMultipart('alternative')
|
108
|
-
msg_root['To'] = str(', ').join(self.emails)
|
109
|
-
|
110
|
-
msg_root['X-Mailer'] = 'ClientsPlugin for Trac'
|
111
|
-
msg_root['X-Trac-Version'] = __version__
|
112
|
-
msg_root['X-Trac-Project'] = projname
|
113
|
-
msg_root['Precedence'] = 'bulk'
|
114
|
-
msg_root['Auto-Submitted'] = 'auto-generated'
|
115
|
-
msg_root['Subject'] = subject
|
116
|
-
msg_root['From'] = '%s <%s>' % (from_name or projname, from_email)
|
117
|
-
msg_root['Reply-To'] = replyto_email
|
118
|
-
msg_root.preamble = 'This is a multi-part message in MIME format.'
|
119
|
-
|
120
|
-
view = 'plain'
|
121
|
-
arg = "'%s'" % view
|
122
|
-
result = self.transform(summary, view=arg)
|
123
|
-
msg_text = MIMEText(str(result), view, self.encoding)
|
124
|
-
msg_root.attach(msg_text)
|
125
|
-
|
126
|
-
msg_related = MIMEMultipart('related')
|
127
|
-
msg_root.attach(msg_related)
|
128
|
-
|
129
|
-
view = 'html'
|
130
|
-
arg = "'%s'" % view
|
131
|
-
result = self.transform(summary, view=arg)
|
132
|
-
#file = open('/tmp/send-client-email.html', 'w')
|
133
|
-
#file.write(str(result))
|
134
|
-
#file.close()
|
135
|
-
|
136
|
-
msg_text = MIMEText(str(result), view, self.encoding)
|
137
|
-
msg_related.attach(msg_text)
|
138
|
-
|
139
|
-
# Handle image embedding...
|
140
|
-
view = 'images'
|
141
|
-
arg = "'%s'" % view
|
142
|
-
result = self.transform(summary, view=arg)
|
143
|
-
if result:
|
144
|
-
images = result.getroot()
|
145
|
-
if images:
|
146
|
-
for img in images:
|
147
|
-
if 'img' != img.tag:
|
148
|
-
continue
|
149
|
-
if not img.get('id') or not img.get('src'):
|
150
|
-
continue
|
151
|
-
|
152
|
-
fp = open(img.get('src'), 'rb')
|
153
|
-
if not fp:
|
154
|
-
continue
|
155
|
-
msg_img = MIMEImage(fp.read())
|
156
|
-
fp.close()
|
157
|
-
msg_img.add_header('Content-ID', '<%s>' % img.get('id'))
|
158
|
-
msg_related.attach(msg_img)
|
159
|
-
|
160
|
-
# Send the email
|
161
|
-
import smtplib
|
162
|
-
smtp = smtplib.SMTP() #smtp_server, smtp_port)
|
163
|
-
if False and user_name:
|
164
|
-
smtp.login(user_name, password)
|
165
|
-
smtp.connect()
|
166
|
-
smtp.sendmail(from_email, self.emails, msg_root.as_string())
|
167
|
-
smtp.quit()
|
168
|
-
return True
|
@@ -1,137 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import locale
|
5
|
-
import time
|
6
|
-
import codecs
|
7
|
-
import httplib
|
8
|
-
import urlparse
|
9
|
-
from datetime import datetime
|
10
|
-
from StringIO import StringIO
|
11
|
-
|
12
|
-
from trac.core import *
|
13
|
-
from trac.env import open_environment
|
14
|
-
from trac.util.datefmt import format_date, to_datetime
|
15
|
-
from trac.wiki import wiki_to_html
|
16
|
-
from genshi import escape
|
17
|
-
|
18
|
-
from lxml import etree
|
19
|
-
from clients.action import IClientActionProvider
|
20
|
-
|
21
|
-
|
22
|
-
class ClientActionZendesk(Component):
|
23
|
-
implements(IClientActionProvider)
|
24
|
-
|
25
|
-
client = None
|
26
|
-
debug = False
|
27
|
-
|
28
|
-
def get_name(self):
|
29
|
-
return "Post to Zendesk"
|
30
|
-
|
31
|
-
def get_description(self):
|
32
|
-
return "Post the summary to a Zendesk forum topic"
|
33
|
-
|
34
|
-
def options(self, client=None):
|
35
|
-
if client is None:
|
36
|
-
yield {'name': 'XSLT', 'description': 'Formatting XSLT to convert the summary to a Zendesk compatible post', 'type': 'large'}
|
37
|
-
yield {'name': 'Username', 'description': 'Zendesk username', 'type': 'medium'}
|
38
|
-
yield {'name': 'Password', 'description': 'Zendesk password', 'type': 'medium'}
|
39
|
-
yield {'name': 'Method', 'description': 'Interaction Method', 'type': 'list', 'vals': ['POST', 'PUT']}
|
40
|
-
else:
|
41
|
-
yield {'name': 'Zendesk URI', 'description': 'Zendesk Forum REST URI', 'type': 'medium'}
|
42
|
-
|
43
|
-
|
44
|
-
def init(self, event, client):
|
45
|
-
self.client = client
|
46
|
-
if not event.action_options.has_key('XSLT') or not event.action_options['XSLT']['value']:
|
47
|
-
return False
|
48
|
-
try:
|
49
|
-
self.transform = etree.XSLT(etree.fromstring(str(event.action_options['XSLT']['value'])))
|
50
|
-
except:
|
51
|
-
print "Error: Cannot load/parse stylesheet"
|
52
|
-
return False
|
53
|
-
|
54
|
-
if not event.action_options.has_key('Username') or not event.action_options['Username']['value']:
|
55
|
-
return False
|
56
|
-
self.username = event.action_options['Username']['value']
|
57
|
-
|
58
|
-
if not event.action_options.has_key('Password') or not event.action_options['Password']['value']:
|
59
|
-
return False
|
60
|
-
self.password = event.action_options['Password']['value']
|
61
|
-
|
62
|
-
if not event.action_options.has_key('Method') or not event.action_options['Method']['value']:
|
63
|
-
return False
|
64
|
-
self.method = event.action_options['Method']['value']
|
65
|
-
|
66
|
-
if not event.action_client_options.has_key('Zendesk URI') or not event.action_client_options['Zendesk URI']['value']:
|
67
|
-
return False
|
68
|
-
self.uri = event.action_client_options['Zendesk URI']['value']
|
69
|
-
|
70
|
-
return True
|
71
|
-
|
72
|
-
|
73
|
-
def perform(self, req, summary):
|
74
|
-
def parseuri(uri):
|
75
|
-
"""Parse URI, return (host, port, path) tuple.
|
76
|
-
|
77
|
-
>>> parseuri('http://example.org/testing?somequery#frag')
|
78
|
-
('example.org', 80, '/testing?somequery')
|
79
|
-
>>> parseuri('http://example.net:8080/test.html')
|
80
|
-
('example.net', 8080, '/test.html')
|
81
|
-
"""
|
82
|
-
|
83
|
-
scheme, netplace, path, query, fragid = urlparse.urlsplit(uri)
|
84
|
-
|
85
|
-
if ':' in netplace:
|
86
|
-
host, port = netplace.split(':', 2)
|
87
|
-
port = int(port)
|
88
|
-
else: host, port = netplace, 80
|
89
|
-
|
90
|
-
if query: path += '?' + query
|
91
|
-
|
92
|
-
return host, port, path
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if summary is None:
|
97
|
-
return False
|
98
|
-
|
99
|
-
result = self.transform(summary)
|
100
|
-
|
101
|
-
username = self.username
|
102
|
-
password = self.password
|
103
|
-
uri = self.uri
|
104
|
-
|
105
|
-
host, port, path = parseuri(uri)
|
106
|
-
|
107
|
-
okay = set([200, 201, 204])
|
108
|
-
|
109
|
-
import base64
|
110
|
-
userpass = username + ':' + password
|
111
|
-
userpass = base64.encodestring(userpass).strip()
|
112
|
-
authorization = 'Basic ' + userpass
|
113
|
-
|
114
|
-
# Attempt to HTTP PUT the data
|
115
|
-
h = httplib.HTTPConnection(host, port)
|
116
|
-
|
117
|
-
data = str(result)
|
118
|
-
h.putrequest('PUT', path)
|
119
|
-
|
120
|
-
h.putheader('User-Agent', 'Trac/1.0')
|
121
|
-
h.putheader('Accept', 'application/xml')
|
122
|
-
h.putheader('Content-Type', 'text/xml')
|
123
|
-
h.putheader('Authorization', authorization)
|
124
|
-
h.putheader('Content-Length', len(data))
|
125
|
-
h.endheaders()
|
126
|
-
|
127
|
-
h.send(str(data))
|
128
|
-
|
129
|
-
resp = h.getresponse()
|
130
|
-
status = resp.status # an int
|
131
|
-
|
132
|
-
# Got a response, now decide how to act upon it
|
133
|
-
if status not in okay:
|
134
|
-
print 'Got "%s %s"' % (status, resp.reason)
|
135
|
-
return False
|
136
|
-
|
137
|
-
return True
|
@@ -1,91 +0,0 @@
|
|
1
|
-
from trac.core import *
|
2
|
-
from trac.perm import PermissionSystem
|
3
|
-
#from trac.ticket.model import AbstractEnum
|
4
|
-
#from trac.ticket.admin import AbstractEnumAdminPage
|
5
|
-
from trac.ticket.admin import TicketAdminPanel
|
6
|
-
|
7
|
-
|
8
|
-
from clients.model import Client
|
9
|
-
from clients.events import ClientEvent
|
10
|
-
from trac.util.datefmt import utc, parse_date, get_date_format_hint, \
|
11
|
-
get_datetime_format_hint
|
12
|
-
from trac.web.chrome import add_link, add_script
|
13
|
-
|
14
|
-
class ClientAdminPanel(TicketAdminPanel):
|
15
|
-
|
16
|
-
_type = 'clients'
|
17
|
-
_label = ('Client', 'Clients')
|
18
|
-
|
19
|
-
# TicketAdminPanel methods
|
20
|
-
def _render_admin_panel(self, req, cat, page, client):
|
21
|
-
# Detail view?
|
22
|
-
if client:
|
23
|
-
clnt = Client(self.env, client)
|
24
|
-
events = ClientEvent.select(self.env, client)
|
25
|
-
if req.method == 'POST':
|
26
|
-
if req.args.get('save'):
|
27
|
-
clnt.name = req.args.get('name')
|
28
|
-
clnt.description = req.args.get('description')
|
29
|
-
clnt.changes_list = req.args.get('changes_list')
|
30
|
-
clnt.changes_period = req.args.get('changes_period')
|
31
|
-
clnt.summary_list = req.args.get('summary_list')
|
32
|
-
clnt.summary_period = req.args.get('summary_period')
|
33
|
-
clnt.default_rate = req.args.get('default_rate')
|
34
|
-
clnt.currency = req.args.get('currency')
|
35
|
-
clnt.update()
|
36
|
-
|
37
|
-
db = self.env.get_db_cnx()
|
38
|
-
for clev in events:
|
39
|
-
for option in clev.summary_client_options:
|
40
|
-
arg = 'summary-option-%s-%s' % (clev.md5, clev.summary_client_options[option]['md5'])
|
41
|
-
clev.summary_client_options[option]['value'] = req.args.get(arg)
|
42
|
-
for option in clev.action_client_options:
|
43
|
-
arg = 'action-option-%s-%s' % (clev.md5, clev.action_client_options[option]['md5'])
|
44
|
-
clev.action_client_options[option]['value'] = req.args.get(arg)
|
45
|
-
clev.update_options(client, db)
|
46
|
-
db.commit()
|
47
|
-
|
48
|
-
req.redirect(req.href.admin(cat, page))
|
49
|
-
elif req.args.get('cancel'):
|
50
|
-
req.redirect(req.href.admin(cat, page))
|
51
|
-
|
52
|
-
add_script(req, 'common/js/wikitoolbar.js')
|
53
|
-
data = {'view': 'detail', 'client': clnt, 'events': events}
|
54
|
-
|
55
|
-
else:
|
56
|
-
if req.method == 'POST':
|
57
|
-
# Add Client
|
58
|
-
if req.args.get('add') and req.args.get('name'):
|
59
|
-
clnt = Client(self.env)
|
60
|
-
clnt.name = req.args.get('name')
|
61
|
-
clnt.insert()
|
62
|
-
req.redirect(req.href.admin(cat, page))
|
63
|
-
|
64
|
-
# Remove clients
|
65
|
-
elif req.args.get('remove') and req.args.get('sel'):
|
66
|
-
sel = req.args.get('sel')
|
67
|
-
sel = isinstance(sel, list) and sel or [sel]
|
68
|
-
if not sel:
|
69
|
-
raise TracError('No client selected')
|
70
|
-
db = self.env.get_db_cnx()
|
71
|
-
for name in sel:
|
72
|
-
clnt = Client(self.env, name, db=db)
|
73
|
-
clnt.delete(db=db)
|
74
|
-
db.commit()
|
75
|
-
req.redirect(req.href.admin(cat, page))
|
76
|
-
|
77
|
-
# Set default client
|
78
|
-
elif req.args.get('apply'):
|
79
|
-
if req.args.get('default'):
|
80
|
-
name = req.args.get('default')
|
81
|
-
self.log.info('Setting default client to %s', name)
|
82
|
-
self.config.set('ticket', 'default_client', name)
|
83
|
-
self.config.save()
|
84
|
-
req.redirect(req.href.admin(cat, page))
|
85
|
-
|
86
|
-
default = self.config.get('ticket', 'default_client')
|
87
|
-
data = {'view': 'list',
|
88
|
-
'clients': Client.select(self.env),
|
89
|
-
'default': default}
|
90
|
-
|
91
|
-
return 'admin_clients.html', data
|
@@ -1,199 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
from trac.core import *
|
3
|
-
from trac.env import IEnvironmentSetupParticipant
|
4
|
-
from reportmanager import CustomReportManager
|
5
|
-
|
6
|
-
class ClientsSetupParticipant(Component):
|
7
|
-
implements(IEnvironmentSetupParticipant)
|
8
|
-
|
9
|
-
db_version_key = None
|
10
|
-
db_version = None
|
11
|
-
db_installed_version = None
|
12
|
-
|
13
|
-
def __init__(self):
|
14
|
-
self.db_version_key = 'clients_plugin_version'
|
15
|
-
self.db_version = 5
|
16
|
-
self.db_installed_version = None
|
17
|
-
|
18
|
-
# Initialise database schema version tracking.
|
19
|
-
db = self.env.get_db_cnx()
|
20
|
-
cursor = db.cursor()
|
21
|
-
cursor.execute("SELECT value FROM system WHERE name=%s", (self.db_version_key,))
|
22
|
-
try:
|
23
|
-
self.db_installed_version = int(cursor.fetchone()[0])
|
24
|
-
except:
|
25
|
-
self.db_installed_version = 0
|
26
|
-
cursor.execute("INSERT INTO system (name,value) VALUES(%s,%s)",
|
27
|
-
(self.db_version_key, self.db_installed_version))
|
28
|
-
db.commit()
|
29
|
-
db.close()
|
30
|
-
|
31
|
-
def system_needs_upgrade(self):
|
32
|
-
return self.db_installed_version < self.db_version
|
33
|
-
|
34
|
-
def do_db_upgrade(self):
|
35
|
-
db = self.env.get_db_cnx()
|
36
|
-
cursor = db.cursor()
|
37
|
-
|
38
|
-
# Do the staged updates
|
39
|
-
try:
|
40
|
-
if self.db_installed_version < 2:
|
41
|
-
print 'Creating client table'
|
42
|
-
cursor.execute('CREATE TABLE client ('
|
43
|
-
'name TEXT,'
|
44
|
-
'description TEXT,'
|
45
|
-
'changes_list TEXT,'
|
46
|
-
'changes_period TEXT,'
|
47
|
-
'changes_lastupdate INTEGER,'
|
48
|
-
'summary_list TEXT,'
|
49
|
-
'summary_period TEXT,'
|
50
|
-
'summary_lastupdate INTEGER'
|
51
|
-
')')
|
52
|
-
# Import old Enums
|
53
|
-
cursor.execute('INSERT INTO client (name) '
|
54
|
-
'SELECT name FROM enum WHERE type=%s', ('client',))
|
55
|
-
# Clean them out
|
56
|
-
cursor.execute('DELETE FROM enum WHERE type=%s', ('client',))
|
57
|
-
|
58
|
-
if self.db_installed_version < 3:
|
59
|
-
print 'Updating clients table (v3)'
|
60
|
-
cursor.execute('ALTER TABLE client '
|
61
|
-
'ADD COLUMN default_rate INTEGER')
|
62
|
-
cursor.execute('ALTER TABLE client '
|
63
|
-
'ADD COLUMN currency TEXT')
|
64
|
-
|
65
|
-
if self.db_installed_version < 4:
|
66
|
-
print 'Updating clients table (v4)'
|
67
|
-
cursor.execute('CREATE TABLE client_events ('
|
68
|
-
'name TEXT,'
|
69
|
-
'summary TEXT,'
|
70
|
-
'action TEXT,'
|
71
|
-
'lastrun INTEGER'
|
72
|
-
')')
|
73
|
-
cursor.execute('CREATE TABLE client_event_summary_options ('
|
74
|
-
'client_event TEXT,'
|
75
|
-
'client TEXT,'
|
76
|
-
'name TEXT,'
|
77
|
-
'value TEXT'
|
78
|
-
')')
|
79
|
-
cursor.execute('CREATE TABLE client_event_action_options ('
|
80
|
-
'client_event TEXT,'
|
81
|
-
'client TEXT,'
|
82
|
-
'name TEXT,'
|
83
|
-
'value TEXT'
|
84
|
-
')')
|
85
|
-
|
86
|
-
if self.db_installed_version < 5:
|
87
|
-
print 'Updating clients table (v5)'
|
88
|
-
cursor.execute('INSERT INTO client_events '
|
89
|
-
'SELECT "Weekly Summary", "Milestone Summary", "Send Email", MAX(summary_lastupdate) '
|
90
|
-
'FROM client')
|
91
|
-
cursor.execute('INSERT INTO client_event_action_options '
|
92
|
-
'SELECT "Weekly Summary", name, "Email Addresses", summary_list '
|
93
|
-
'FROM client '
|
94
|
-
'WHERE summary_list!=""')
|
95
|
-
cursor.execute('INSERT INTO client_events '
|
96
|
-
'SELECT "Ticket Changes", "Ticket Change Summary", "Send Email", MAX(changes_lastupdate) '
|
97
|
-
'FROM client')
|
98
|
-
cursor.execute('INSERT INTO client_event_action_options '
|
99
|
-
'SELECT "Ticket Changes", name, "Email Addresses", changes_list '
|
100
|
-
'FROM client '
|
101
|
-
'WHERE changes_list!=""')
|
102
|
-
cursor.execute('CREATE TEMPORARY TABLE client_tmp ('
|
103
|
-
'name TEXT,'
|
104
|
-
'description TEXT,'
|
105
|
-
'default_rate INTEGER,'
|
106
|
-
'currency TEXT'
|
107
|
-
')')
|
108
|
-
cursor.execute('INSERT INTO client_tmp SELECT name, description, default_rate, currency FROM client')
|
109
|
-
cursor.execute('DROP TABLE client')
|
110
|
-
cursor.execute('CREATE TABLE client ('
|
111
|
-
'name TEXT,'
|
112
|
-
'description TEXT,'
|
113
|
-
'default_rate INTEGER,'
|
114
|
-
'currency TEXT'
|
115
|
-
')')
|
116
|
-
cursor.execute('INSERT INTO client SELECT name, description, default_rate, currency FROM client_tmp')
|
117
|
-
cursor.execute('DROP TABLE client_tmp')
|
118
|
-
|
119
|
-
#if self.db_installed_version < 6:
|
120
|
-
# print 'Updating clients table (v6)'
|
121
|
-
# cursor.execute('...')
|
122
|
-
|
123
|
-
# Updates complete, set the version
|
124
|
-
cursor.execute("UPDATE system SET value=%s WHERE name=%s",
|
125
|
-
(self.db_version, self.db_version_key))
|
126
|
-
db.commit()
|
127
|
-
db.close()
|
128
|
-
except Exception, e:
|
129
|
-
self.log.error("WorklogPlugin Exception: %s" % (e,));
|
130
|
-
db.rollback()
|
131
|
-
|
132
|
-
def do_reports_upgrade(self):
|
133
|
-
mgr = CustomReportManager(self.env, self.log)
|
134
|
-
r = __import__('reports', globals(), locals(), ['reports'])
|
135
|
-
|
136
|
-
for report_group in r.reports:
|
137
|
-
rlist = report_group['reports']
|
138
|
-
group_title = report_group['title']
|
139
|
-
for report in rlist:
|
140
|
-
title = report['title']
|
141
|
-
new_version = report['version']
|
142
|
-
mgr.add_report(report["title"], 'Clients Plugin', \
|
143
|
-
report['description'], report['sql'], \
|
144
|
-
report['uuid'], report['version'],
|
145
|
-
'Timing and Estimation Plugin',
|
146
|
-
group_title)
|
147
|
-
|
148
|
-
def ticket_fields_need_upgrade(self):
|
149
|
-
section = 'ticket-custom'
|
150
|
-
return ('text' != self.config.get(section, 'client') \
|
151
|
-
or 'text' != self.config.get(section, 'clientrate'))
|
152
|
-
|
153
|
-
def do_ticket_field_upgrade(self):
|
154
|
-
section = 'ticket-custom'
|
155
|
-
|
156
|
-
self.config.set(section,'client', 'text')
|
157
|
-
self.config.set(section,'client.label', 'Client')
|
158
|
-
|
159
|
-
self.config.set(section,'clientrate', 'text')
|
160
|
-
self.config.set(section,'clientrate.label', 'Client Charge Rate')
|
161
|
-
|
162
|
-
self.config.save();
|
163
|
-
|
164
|
-
|
165
|
-
# IEnvironmentSetupParticipant methods
|
166
|
-
def environment_created(self):
|
167
|
-
"""Called when a new Trac environment is created."""
|
168
|
-
if self.environment_needs_upgrade(None):
|
169
|
-
self.upgrade_environment(None)
|
170
|
-
|
171
|
-
def environment_needs_upgrade(self, db):
|
172
|
-
"""Called when Trac checks whether the environment needs to be upgraded.
|
173
|
-
|
174
|
-
Should return `True` if this participant needs an upgrade to be
|
175
|
-
performed, `False` otherwise.
|
176
|
-
|
177
|
-
"""
|
178
|
-
return (self.system_needs_upgrade() \
|
179
|
-
or self.ticket_fields_need_upgrade())
|
180
|
-
|
181
|
-
def upgrade_environment(self, db):
|
182
|
-
"""Actually perform an environment upgrade.
|
183
|
-
|
184
|
-
Implementations of this method should not commit any database
|
185
|
-
transactions. This is done implicitly after all participants have
|
186
|
-
performed the upgrades they need without an error being raised.
|
187
|
-
"""
|
188
|
-
print 'ClientsPlugin needs an upgrade'
|
189
|
-
print ' * Upgrading db'
|
190
|
-
self.do_db_upgrade()
|
191
|
-
print ' * Upgrading reports'
|
192
|
-
self.do_reports_upgrade()
|
193
|
-
|
194
|
-
if self.ticket_fields_need_upgrade():
|
195
|
-
print ' * Upgrading fields'
|
196
|
-
self.do_ticket_field_upgrade()
|
197
|
-
|
198
|
-
print 'Done Upgrading'
|
199
|
-
|