Skip to content

Commit f66d45c

Browse files
committed
initial
0 parents  commit f66d45c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+3940
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
db
2+
config.ini
3+
config.bak

TEMPLATE-INFO.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Name: classic-demo
2+
Description: This is a generic issue tracker that may be used to track
3+
bugs, feature requests, project issues or any number of other types of
4+
issues. Most users of Roundup will find that this template suits them,
5+
with perhaps a few customisations.
6+
Intended-for: All first-time Roundup users
7+
Path: /home/malav/Documents/roundup/demo

detectors/messagesummary.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from roundup.mailgw import parseContent
2+
3+
def summarygenerator(db, cl, nodeid, newvalues):
4+
''' If the message doesn't have a summary, make one for it.
5+
'''
6+
if 'summary' in newvalues or 'content' not in newvalues:
7+
return
8+
9+
summary, content = parseContent(newvalues['content'], config=db.config)
10+
newvalues['summary'] = summary
11+
12+
13+
def init(db):
14+
# fire before changes are made
15+
db.msg.audit('create', summarygenerator)
16+
17+
# vim: set filetype=python ts=4 sw=4 et si
18+
#SHA: 9d9fa12402532ea9ffbff213f912c3f03231f06f

detectors/statusauditor.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Copyright (c) 2002 ekit.com Inc (http://www.ekit-inc.com/)
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
# SOFTWARE.
20+
#
21+
22+
from roundup.configuration import BooleanOption, InvalidOptionError
23+
24+
def chatty(db, cl, nodeid, newvalues):
25+
''' If the issue is currently 'resolved', 'done-cbb' or None,
26+
then set it to 'chatting'. If issue is 'unread' and
27+
chatting_requires_two_users is true, set state
28+
to 'chatting' if the person adding the new message is not
29+
the same as the person who created the issue. This allows
30+
somebody to submit multiple emails describing the problem
31+
without changing it to 'chatting'. 'chatting' should
32+
indicate at least two people are 'chatting'.
33+
'''
34+
# If set to true, change state from 'unread' to 'chatting' only
35+
# if the author of the update is not the person who created the
36+
# first message (and thus the issue). If false (default ini file
37+
# setting) set 'chatting' when the second message is received.
38+
try:
39+
chatting_requires_two_users = BooleanOption(None,
40+
"detector::Statusauditor",
41+
"CHATTING_REQUIRES_TWO_USERS").str2value(
42+
db.config.detectors[
43+
'STATUSAUDITOR_CHATTING_REQUIRES_TWO_USERS' ]
44+
)
45+
except InvalidOptionError:
46+
raise InvalidOptionError("Option STATUSAUDITOR_CHATTING_REQUIRES_TWO_USERS not found in detectors/config.ini. Contact tracker admin to fix.")
47+
48+
# don't fire if there's no new message (ie. chat)
49+
if 'messages' not in newvalues:
50+
return
51+
if newvalues['messages'] == cl.get(nodeid, 'messages'):
52+
return
53+
54+
# get the chatting state ID
55+
try:
56+
chatting_id = db.status.lookup('chatting')
57+
except KeyError:
58+
# no chatting state, ignore all this stuff
59+
return
60+
61+
# get the current value
62+
current_status = cl.get(nodeid, 'status')
63+
64+
# see if there's an explicit change in this transaction
65+
if 'status' in newvalues:
66+
# yep, skip
67+
return
68+
69+
# determine the id of 'unread', 'resolved' and 'chatting'
70+
fromstates = []
71+
for state in 'unread resolved done-cbb'.split():
72+
try:
73+
fromstates.append(db.status.lookup(state))
74+
except KeyError:
75+
pass
76+
77+
unread = fromstates[0] # grab the 'unread' state which is first
78+
79+
# ok, there's no explicit change, so check if we are in a state that
80+
# should be changed. First see if we should set 'chatting' based on
81+
# who opened the issue.
82+
if current_status == unread and chatting_requires_two_users:
83+
# find creator of issue and compare to currentuser making
84+
# update. If the creator is same as initial author don't
85+
# change to 'chatting'.
86+
issue_creator = cl.get(nodeid, 'creator')
87+
if issue_creator == db.getuid():
88+
# person is chatting with themselves, don't set 'chatting'
89+
return
90+
91+
# Current author is not the initiator of the issue so
92+
# we are 'chatting'.
93+
if current_status in fromstates + [None]:
94+
# yep, we're now chatting
95+
newvalues['status'] = chatting_id
96+
97+
98+
def presetunread(db, cl, nodeid, newvalues):
99+
''' Make sure the status is set on new issues
100+
'''
101+
if 'status' in newvalues and newvalues['status']:
102+
return
103+
104+
# get the unread state ID
105+
try:
106+
unread_id = db.status.lookup('unread')
107+
except KeyError:
108+
# no unread state, ignore all this stuff
109+
return
110+
111+
# ok, do it
112+
newvalues['status'] = unread_id
113+
114+
115+
def init(db):
116+
# fire before changes are made
117+
db.issue.audit('set', chatty)
118+
db.issue.audit('create', presetunread)
119+
120+
# vim: set filetype=python ts=4 sw=4 et si
121+
#SHA: 4de6a95a26b76d520d6ef541f7ab6d25d075edbc

detectors/userauditor.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Copyright (c) 2003 Richard Jones ([email protected])
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
# SOFTWARE.
20+
#
21+
22+
import re
23+
24+
# regular expression thanks to: http://www.regular-expressions.info/email.html
25+
# this is the "99.99% solution for syntax only".
26+
email_regexp = (r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*", r"(localhost|(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9]))")
27+
email_rfc = re.compile('^' + email_regexp[0] + '@' + email_regexp[1] + '$', re.IGNORECASE)
28+
email_local = re.compile('^' + email_regexp[0] + '$', re.IGNORECASE)
29+
30+
valid_username = re.compile('^[a-z0-9_@!%.+-]+$', re.IGNORECASE)
31+
32+
def valid_address(address):
33+
''' If we see an @-symbol in the address then check against the full
34+
RFC syntax. Otherwise it is a local-only address so only check
35+
the local part of the RFC syntax.
36+
'''
37+
if '@' in address:
38+
return email_rfc.match(address)
39+
else:
40+
return email_local.match(address)
41+
42+
def get_addresses(user):
43+
''' iterate over all known addresses in a newvalues dict
44+
this takes of the address/alterate_addresses handling
45+
'''
46+
if 'address' in user:
47+
yield user['address']
48+
if user.get('alternate_addresses', None):
49+
for address in user['alternate_addresses'].split('\n'):
50+
yield address
51+
52+
def audit_user_fields(db, cl, nodeid, newvalues):
53+
''' Make sure user properties are valid.
54+
55+
- email address is syntactically valid
56+
- email address is unique
57+
- roles specified exist
58+
- timezone is valid
59+
- username matches A-z0-9_-.@!+% (email symbols)
60+
'''
61+
62+
if 'username' in newvalues:
63+
if not valid_username.match(newvalues['username']):
64+
raise ValueError("Username/Login Name must consist only of the letters a-z (any case), digits 0-9 and the symbols: @._-!+%")
65+
66+
for address in get_addresses(newvalues):
67+
if not valid_address(address):
68+
raise ValueError('Email address syntax is invalid "%s"'%address)
69+
70+
check_main = db.user.stringFind(address=address)
71+
# make sure none of the alts are owned by anyone other than us (x!=nodeid)
72+
check_alts = [x for x in db.user.filter(None, {'alternate_addresses' : address}) if x != nodeid]
73+
if check_main or check_alts:
74+
raise ValueError('Email address %s already in use' % address)
75+
76+
newroles = newvalues.get('roles')
77+
if newroles:
78+
for rolename in [r.lower().strip() for r in newroles.split(',')]:
79+
if rolename and rolename not in db.security.role:
80+
raise ValueError('Role "%s" does not exist'%rolename)
81+
82+
tz = newvalues.get('timezone', None)
83+
if tz:
84+
# if they set a new timezone validate the timezone by attempting to
85+
# use it before we store it to the db.
86+
import roundup.date
87+
import datetime
88+
try:
89+
TZ = roundup.date.get_timezone(tz)
90+
dt = datetime.datetime.now()
91+
local = TZ.localize(dt).utctimetuple()
92+
except IOError:
93+
raise ValueError('Timezone "%s" does not exist' % tz)
94+
except ValueError:
95+
raise ValueError('Timezone "%s" exceeds valid range [-23...23]' % tz)
96+
97+
def init(db):
98+
# fire before changes are made
99+
db.user.audit('set', audit_user_fields)
100+
db.user.audit('create', audit_user_fields)
101+
102+
# vim: sts=4 sw=4 et si
103+
#SHA: 3c82fbbb589cec96d60013c792d2486d427506a2

extensions/README.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
This directory is for tracker extensions:
2+
3+
- CGI Actions
4+
- Templating functions
5+
6+
See the customisation doc for more information.

html/_generic.404.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- dollarId: issue.item,v 1.4 2001/08/03 01:19:43 richard Exp dollar-->
2+
<tal:block metal:use-macro="templates/page/macros/icing">
3+
<title metal:fill-slot="head_title">
4+
Item Not Found
5+
</title>
6+
<tal:block metal:fill-slot="body_title">
7+
Item Not Found
8+
</tal:block>
9+
10+
<td class="content" metal:fill-slot="content">
11+
There is no <span tal:content="context/_classname" /> with id
12+
<span tal:content="context/id"/>
13+
</td>
14+
</tal:block>
15+
<!-- SHA: 34f0c9649a79a91b1a5d5f96820951baf969da43 -->

html/_generic.calendar.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2+
<html>
3+
<head>
4+
<link rel="stylesheet" type="text/css" href="@@file/style.css" />
5+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8;" />
6+
<meta name="robots" content="noindex, nofollow" />
7+
<title tal:content="string:Roundup Calendar"></title>
8+
<script language="Javascript"
9+
type="text/javascript"
10+
tal:content="structure string:
11+
// this is the name of the field in the original form that we're working on
12+
form = window.opener.document.${request/form/form/value};
13+
field = '${request/form/property/value}';" >
14+
</script>
15+
</head>
16+
<body class="body"
17+
tal:content="structure python:utils.html_calendar(request)">
18+
</body>
19+
</html>
20+
<!-- SHA: 2a6bacec3be1972209b39a274bce7c735edeb815 -->

html/_generic.collision.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<tal:block metal:use-macro="templates/page/macros/icing">
2+
<title metal:fill-slot="head_title" i18n:translate=""
3+
><span tal:replace="python:context._classname.capitalize()"
4+
i18n:name="class" /> Edit Collision - <span i18n:name="tracker"
5+
tal:replace="config/TRACKER_NAME" /></title>
6+
<tal:block metal:fill-slot="body_title" i18n:translate=""
7+
><span tal:replace="python:context._classname.capitalize()"
8+
i18n:name="class" /> Edit Collision</tal:block>
9+
10+
<td class="content" metal:fill-slot="content" i18n:translate="
11+
There has been a collision. Another user updated this node
12+
while you were editing. Please <a href='${context}'>reload</a>
13+
the node and review your edits.
14+
"><span tal:replace="context/designator" i18n:name="context" />
15+
</td>
16+
</tal:block>
17+
<!-- SHA: db15fb6c88215d4baf223910a6c9cd81c63dc994 -->

html/_generic.help-empty.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<html>
2+
<head>
3+
<title>Empty page (no search performed yet)</title>
4+
</head>
5+
<body>
6+
<p i18n:translate="">Please specify your search parameters!</p>
7+
</body>
8+
</html>
9+
<!-- SHA: 9a118377b03172347d95097ff75fca26a6dd3738 -->

0 commit comments

Comments
 (0)