changeset 4165:38dac0b488de gsoc-2009

Removed old notify-roundup from beta branch
author Pygi <pygi@users.sourceforge.net>
date Thu, 02 Jul 2009 18:02:12 +0000
parents 6750b2dae53e
children fe9b0fdb1790
files scripts/notify-roundup/detectors/svnauditor.py scripts/notify-roundup/doc/README scripts/notify-roundup/extensions/revision_info.py scripts/notify-roundup/html/bug.item.html scripts/notify-roundup/html/svn_rev.item.html scripts/notify-roundup/html/user.item.html scripts/notify-roundup/notify-roundup.ini scripts/notify-roundup/notify-roundup.py
diffstat 8 files changed, 0 insertions(+), 1098 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/notify-roundup/detectors/svnauditor.py	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-# Subversion integration auditor
-# 
-# Watches for messages formatted by the notify-roundup.py Subversion hook
-# script, and parses the meta-data out of them, removing it from the
-# message body in the process.
-#
-# Place this file in your tracker's "detectors" directory.
-#
-# See end of file for change history
-
-import re, sets
-
-import roundup.date
-
-import ConfigParser
-
-svn_msg = re.compile('^(revision|repos|host|date|summary)=(.*)$')
-ini_path = '/path/to/notify-roundup.ini'
-
-def parse_message(db, cl, nodeid, newvalues):
-    '''Parse an incoming message for Subversion information.
-    '''
-
-    # collect up our meta-data from the message
-    info = {}
-    content = []
-    for line in newvalues.get('content', '').splitlines():
-        m = svn_msg.match(line)
-        if not m:
-            content.append(line)
-            continue
-        info[m.group(1)] = m.group(2).strip()
-
-    # only continue if all five pieces of information are present
-    if len(info) != 5:
-        return
-
-    # look up the repository id
-    try:
-        svn_repo_id = db.svn_repo.stringFind(path=info['repos'],
-            host=info['host'])[0]
-    except IndexError:
-        #logger.error('no repository %s in tracker'%repos.repos_dir)
-        return
-
-    # create the subversion revision item
-    svn_rev_id = db.svn_rev.create(repository=svn_repo_id,
-        revision=int(info['revision']))
-
-    # minor bit of content cleaning - remove the single leading blank line
-    if content and not content[0].strip():
-        del content[0]
-
-    # set the info on the message
-    newvalues['content'] = '\n'.join(content)
-    newvalues['date'] = roundup.date.Date(info['date'])
-    newvalues['summary'] = info['summary']
-    newvalues['revision'] = svn_rev_id
-
-def undo_title(db, cl, nodeid, newvalues):
-    '''Don't change the title of issues to "SVN commit message..."'''
-    if newvalues.get('title', '').lower().startswith('svn commit message'):
-        del newvalues['title']
-
-
-def init(db):
-    db.msg.audit('create', parse_message)
-    cfg = ConfigParser.ConfigParser()
-    cfg.read(ini_path)
-    fetch_klass = cfg.get('main', 'item-class')
-    klass = db.getclass(fetch_klass)
-    klass.audit('set', undo_title)
-
-#
-# 2005-05-16 - 1.2
-# 
-#   - Status wasn't being set by ID in local mode
-#   - Wasn't catching errors in local changes, hence not cleaning up db
-#     correctly
-#   - svnauditor.py wasn't handling the fifth argument from notify-roundup.py
-#   - viewcvs_url formatting wasn't quite right
-#
-# 2005-05-04 - 1.1
-#   - Several fixes from  Ron Alford
-#   - Don't change issue titles to "SVN commit message..."
-# 
-# 2005-04-26 - 1.0
-#   - Initial version released
-#
--- a/scripts/notify-roundup/doc/README	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-Roundup devel tracker in love with subversion
-=============================================
-
-This document serves a tutorial on setting up a Roundup instance
-with devel template featuring subversion integration. Several assumptions
-are made in this tutorial:
-
-1) You've checked out gsoc-2009 Roundup branch
-2) You know how to setup subversion repository
-3) You want to work with a local subversion repository
-
-Let's follow the steps:
-
-1) Setup subversion post-commit hook
-
-a) Rename post-commit.tmpl to post-commit
-b) Make sure its executable (chmod +x)
-c) Add the following to it:
-
-PYTHON=/usr/bin/python
-NOTIFY=/path/to/notify-roundup.py[1]
-CONFIG=/path/to/notify-roundup.ini[1]
-PYTHONPATH=/path/to/roundup/instance "$PYTHON" "$NOTIFY" "$CONFIG" "$REPOS" "$REV"
-
-[1] notify-roundup.py and notify-roundup.ini can be found in scripts/notify-roundup
-
-2) Modify notify-roundup.ini
-
-a) Set tracker home: tracker-home = /path/to/tracker-home
-b) Set address mappings
-
-3) Copy html templates from scripts/notify-roundup/html to share/roundup/templates/devel/html
-
-4) Copy revision_info.py extension from scripts/notify-roundup/extensions to share/roundup/templates/devel/extensions
-
-5) Now, now, this wasn't so hard :)
-
-How-to format subversion commit message
-=======================================
-
-By default, notify-roundup handles bugs.
-To change status of bug1, format your commit
-message like this:
-
-bug1 pending
-
-Current limitations
-===================
-
-1) Notify-roundup can work only with one item-class
-2) Notify-roundup can only modify Status
-
-We are aware of the those limitations, and have plans to alleviate them in the future.
--- a/scripts/notify-roundup/extensions/revision_info.py	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-# Subversion integration information fetcher
-# 
-# Extracts information about a specific revision from a local repository.
-#
-# Place this file in your tracker's "extensions" directory.
-
-import sys, os, time
-from svn import core, fs, delta, repos
-
-def inner(pool, path, rev):
-    repos_ptr = repos.svn_repos_open(path, pool)
-    fs_ptr = repos.svn_repos_fs(repos_ptr)
-
-    root = fs.revision_root(fs_ptr, rev, pool)
-    base_rev = rev - 1
-
-    # get all changes
-    editor = repos.RevisionChangeCollector(fs_ptr, rev, pool)
-    e_ptr, e_baton = delta.make_editor(editor, pool)
-    repos.svn_repos_replay(root, e_ptr, e_baton, pool)
-
-    changelist = editor.changes.items()
-    changelist.sort()
-
-    base_root = fs.revision_root(fs_ptr, base_rev, pool)
-
-    l = []
-    for filepath, change in changelist:
-        d = {'path': filepath, 'info': ''}
-        if change.path:
-            if change.added:
-                d['action'] = 'new'
-            else:
-                d['action'] = 'modify'
-                differ = fs.FileDiff(base_root, change.path, root, filepath,
-                    pool, '-L \t(original) -L \t(new) -u'.split(' '))
-                d['info'] = differ.get_pipe().read()
-        else:
-            d['action'] = 'delete'
-        l.append(d)
-    return l
-
-
-def getRevisionInfo(revision):
-    #path = '/Users/richard/tmp/test_repo'
-    #rev = 2
-    return core.run_app(inner, str(revision['repository']['path']),
-        int(revision['revision']))
-
-def init(instance):
-    instance.registerUtil('getRevisionInfo', getRevisionInfo)
-
-if __name__ == '__main__':
-    print getRevision(1)
-
--- a/scripts/notify-roundup/html/bug.item.html	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +0,0 @@
-<tal:block metal:use-macro="templates/page/macros/frame">
-<title metal:fill-slot="head_title">
-<tal:block condition="context/id" i18n:translate=""
- >Bug <span tal:replace="context/id" i18n:name="id"
- />: <span tal:replace="context/title" i18n:name="title"
- /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:block>
-<tal:block condition="not:context/id" i18n:translate=""
- >New Bug report - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:block>
-</title>
-<tal:block metal:fill-slot="body_title">
- <span tal:condition="python: not (context.id or context.is_edit_ok())"
-  tal:omit-tag="python:1" i18n:translate="">New Bug</span>
- <span tal:condition="python: not context.id and context.is_edit_ok()"
-  tal:omit-tag="python:1" i18n:translate="">New Bug Editing</span>
- <span tal:condition="python: context.id and not context.is_edit_ok()"
-  tal:omit-tag="python:1" i18n:translate="">Bug <tal:x
-  replace="context/id" i18n:name="id" /></span>
- <span tal:condition="python: context.id and context.is_edit_ok()"
-  tal:omit-tag="python:1" i18n:translate="">Bug<tal:x
-  replace="context/id" i18n:name="id" /> Editing</span>
-</tal:block>
-
-<td class="content" metal:fill-slot="content">
-
-<p tal:condition="python:not (context.is_view_ok()
- or request.user.hasRole('Anonymous'))" i18n:translate="">
- You are not allowed to view this page.</p>
-
-<p tal:condition="python:not context.is_view_ok()
- and request.user.hasRole('Anonymous')" i18n:translate="">
- Please login with your username and password.</p>
-
-<div tal:condition="context/is_view_ok">
-
-<form method="POST" name="itemSynopsis"
-      onSubmit="return submit_once()" enctype="multipart/form-data"
-      tal:attributes="action context/designator">
-
-<fieldset><legend>classification</legend>
-<table class="form">
-<tr>
- <th class="required" i18n:translate="">Title:</th>
- <td colspan="3" tal:condition="context/title/is_edit_ok"
-     tal:content="structure python:context.title.field(size=40)">title</td>
- <td colspan="3" tal:condition="not:context/title/is_edit_ok">
-  <span tal:content="structure context/title/plain"/>
-  <input type="hidden" name="title" tal:attributes="value context/title">
- </td>
-</tr>
-
-<tr>
- <th class="required" i18n:translate="">
-   <span tal:condition="context/type/is_edit_ok" 
-         tal:replace="structure python:db.bug_type.classhelp('id,name,description',property='type',label='Type')" />
-   <span tal:condition="not:context/type/is_edit_ok">Type</span>:
- </th>
- <td tal:content="structure context/type/menu">type</td>
- <th i18n:translate="">
-   <span tal:condition="context/severity/is_edit_ok"
-         tal:replace="structure python:db.severity.classhelp('id,name,description',property='severity',label='Severity')" />
-   <span tal:condition="not:context/severity/is_edit_ok">Severity</span>:
- </th>
- <td tal:content="structure context/severity/menu">severity</td>
-</tr>
-
-<tr>
- <th i18n:translate="">
-   <span tal:condition="context/components/is_edit_ok" 
-         tal:replace="structure python:db.component.classhelp('id,name,description',property='components',label='Components')" />
-   <span tal:condition="not:context/components/is_edit_ok">Components</span>:
- </th>
- <td tal:content="structure context/components/menu">components</td>
- <th i18n:translate="">
-   <span tal:condition="context/versions/is_edit_ok" 
-         tal:replace="structure python:db.version.classhelp('id,name,description',property='versions',label='Versions')" />
-   <span tal:condition="not:context/versions/is_edit_ok">Versions</span>:
- </th>
- <td tal:content="structure context/versions/menu">versions</td>
-</tr>
-</table>
-</fieldset>
-
-<fieldset><legend>process</legend>
-<table class="form">
-<tr tal:condition="context/id">
- <th i18n:translate="">
-   <span tal:condition="context/status/is_edit_ok" 
-         tal:replace="structure python:db.status.classhelp('id,name,description',property='status', label='Status')" />
-   <span tal:condition="not:context/status/is_edit_ok">Status</span>:
- </th>
- <td tal:content="structure context/status/menu">status</td>
- <th i18n:translate="">
-   <span tal:condition="context/resolution/is_edit_ok" 
-         tal:replace="structure python:db.resolution.classhelp('id,name,description',property='resolution', label='Resolution')" />
-   <span tal:condition="not:context/resolution/is_edit_ok">Resolution</span>:
- </th>
- <td tal:content="structure context/resolution/menu">resolution</td>
-</tr>
-
-<tr tal:condition="context/id">
- <th>
-  <tal:block i18n:translate="">Dependencies</tal:block>:
-  <span tal:condition="context/dependencies/is_edit_ok"
-        tal:replace="structure python:db.bug.classhelp('id,title', filter='status=0,1', property='dependencies')" />
- </th>
- <td>
-  <span tal:replace="structure python:context.dependencies.field(showid=1,size=20)" />
-  <span tal:condition="context/dependencies" tal:repeat="d python:context.dependencies.sorted('creation')">
-   <br/>View: <a tal:attributes="href string:bug${d/id}" tal:content="d/id"></a>
-  </span>
- </td>
- <th i18n:translate="">
-  <tal:block i18n:translate="">Superseder</tal:block>:
-  <span tal:condition="context/superseder/is_edit_ok"
-        tal:replace="structure python:db.bug.classhelp('id,title', filter='status=0,1', property='superseder')" />
- </th>
- <td>
-  <span tal:replace="structure python:context.superseder.field(showid=1, size=20)" />
- <span tal:condition="context/superseder">
-<!--   <br><span i18n:translate="">View</span>:
-     <a tal:repeat="sup context/superseder"
-        tal:content="python:sup['id'] + ', '*(not repeat['sup'].end)"
-        tal:attributes="href string:bug${sup/id}; title sup/title;"></a> -->
-   <br><span i18n:translate="">View</span>:
-     <a tal:content="context/superseder/id"
-        tal:attributes="href string:bug${context/superseder/id}; title context/superseder/title;"></a> 
-  </span> 
- </td>
- </tr>
- <tr>
- <th><tal:block i18n:translate="">Assigned To</tal:block>:</th>
- <td tal:content="structure context/assignee/menu">assignedto menu</td>
- <th><tal:block i18n:translate="">Nosy List</tal:block>:
-  <span tal:condition="context/nosy/is_edit_ok"
-        tal:replace="structure python:db.user.classhelp('username,realname,address', property='nosy')" />
- </th>
- <td>
-  <span tal:replace="structure context/nosy/field" />
- </td>
-</tr>
-<tr>
- <th>
-   <span tal:condition="context/priority/is_edit_ok"
-         tal:replace="structure python:db.priority.classhelp('id,name,description',property='priority',label='Priority')" />
-   <span tal:condition="not:context/priority/is_edit_ok">Priority</span>:
- </th>
- <td tal:content="structure context/priority/menu">priority</td>
- <th i18n:translate="">Keywords:</th>
- <td tal:content="structure python:context['keywords'].menu(height=5)">keywords</td>
-
-
-</tr>
-<tr tal:condition="context/is_edit_ok">
- <th><tal:block i18n:translate="">Comment</tal:block>:</th>
- <td colspan="3">
-  <textarea tal:content="request/form/@note/value | default"
-            name="@note" wrap="hard" rows="10" cols="60"></textarea>
- </td>
-</tr>
-
-<tr tal:condition="context/is_edit_ok">
- <th><tal:block i18n:translate="">File</tal:block>:</th>
- <td colspan="3">
-   <input type="hidden" name="@link@files" value="file-1">
-   <input type="file" name="file-1@content" size="35">
- </td>
-</tr>
-<tr tal:condition="context/is_edit_ok">
- <th><tal:block i18n:translate="">File Description</tal:block>:</th>
- <td colspan=3><input type="edit" name="file-1@description" size="40"></td>
-</tr>
-</table>
-</fieldset>
-<table class="form">
-<tr tal:condition="context/is_edit_ok">
- <td>
-  &nbsp;
-  <input type="hidden" name="@template" value="item">
-  <input type="hidden" name="@required" value="title">
- </td>
- <td colspan=3>
-  <span tal:replace="structure context/submit">submit button</span>
-  <a tal:condition="context/id" tal:attributes="href context/copy_url"
-   i18n:translate="">Make a copy</a>
- </td>
-</tr>
-</table>
-</form>
-
-<p tal:condition="context/id" i18n:translate="">
- Created on <b><tal:x replace="python:context.creation.pretty('%Y-%m-%d %H:%M')" i18n:name="creation" /></b>
- by <b><tal:x replace="context/creator" i18n:name="creator" /></b>,
- last changed <b><tal:x replace="python:context.activity.pretty('%Y-%m-%d %H:%M')" i18n:name="activity" /></b>
- by <b><tal:x replace="context/actor" i18n:name="actor" /></b>.
-</p>
-
-<table class="files" tal:condition="context/files">
- <tr><th colspan="5" class="header" i18n:translate="">Files</th></tr>
- <tr>
-  <th i18n:translate="">File name</th>
-  <th i18n:translate="">Uploaded</th>
-  <th i18n:translate="">Description</th>
-  <th i18n:translate="">Edit</th>
-  <th i18n:translate="">Remove</th>
- </tr>
- <tr tal:repeat="file python:context.files.sorted('creation')">
-  <td>
-   <a tal:attributes="href file/download_url"
-      tal:content="file/name">dld link</a>
-  </td>
-  <td>
-   <span tal:content="file/creator">creator's name</span>,
-   <span tal:content="python:file.creation.pretty('%Y-%m-%d %H:%M')">creation date</span>
-  </td>
-  <td tal:content="file/description" />
-  <td><a tal:condition="file/is_edit_ok"
-          tal:attributes="href string:file${file/id}">edit</a>
-  </td>
-  <td>
-   <form style="padding:0" tal:condition="file/is_edit_ok"
-         tal:attributes="action string:bug${context/id}">
-    <input type="hidden" name="@remove@files" tal:attributes="value file/id">
-    <input type="hidden" name="@action" value="edit">
-    <input type="submit" value="remove" i18n:attributes="value">
-   </form>
-  </td>
- </tr>
-</table>
-
-<table class="messages" tal:condition="context/messages">
- <tr><th colspan="4" class="header" i18n:translate="">Messages</th></tr>
- <tal:block tal:repeat="msg context/messages">
-
-  <tr>
-   <th><a tal:attributes="href string:msg${msg/id}"
-    i18n:translate="">msg<tal:x replace="msg/id" i18n:name="id" /></a></th>
-   <th i18n:translate="">Author: <tal:x replace="python:msg.author.realname.plain()"
-       i18n:name="author" /> (<tal:x replace="msg/author"/>)</th>
-   <th i18n:translate="">Date: <tal:x replace="python:msg.date.pretty('%Y-%m-%d %H:%M')"
-       i18n:name="date" /></th>
-    <form style="padding:0" tal:condition="msg/is_edit_ok"
-          tal:attributes="action string:bug${context/id}">
-     <input type="hidden" name="@remove@messages" tal:attributes="value msg/id">
-     <input type="hidden" name="@action" value="edit">
-     <input type="submit" value="remove" i18n:attributes="value">
-    </form>
-  </tr>
-
-
-   <tr tal:condition="msg/revision">
-   <th tal:define="r msg/revision" colspan="4">
-    <a tal:attributes="href string:svn_rev${r/id}"
-       tal:content="string:Subversion revision ${r/revision}" />
-   </th>
-  </tr>
-  
-  <tr>
-
-   <td colspan="4" class="content">
-    <pre tal:condition="python:msg.content.is_view_ok()"
-         tal:content="structure python:utils.localReplace(msg.content.hyperlinked())">content</pre>
-   </td>
-  </tr>
- </tal:block>
-</table>
-
-<tal:block tal:condition="context/id" tal:replace="structure context/history" />
-
-</div>
-
-</td>
-
-</tal:block>
--- a/scripts/notify-roundup/html/svn_rev.item.html	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-<tal:block metal:use-macro="templates/page/macros/frame">
-<title metal:fill-slot="head_title">
- SVN Revision <span tal:replace="context/revision" />
-</title>
-<tal:block metal:fill-slot="body_title">
- SVN Revision <span tal:replace="context/revision" />
-</tal:block>
-
-<td class="content" metal:fill-slot="content">
-
-<p tal:condition="not:context/is_view_ok" i18n:translate="">You are not
-    allowed to view this page.</p>
-
-<div tal:condition="context/is_view_ok">
-
-<table class="messages">
-<tal:block repeat="file python:utils.getRevisionInfo(context)">
- <tr>
-  <th tal:content="string: ${file/action} ${file/path}" />
- </tr>
- <tr tal:condition="file/info"><td><pre tal:content="file/info" /></td></tr>
-</tal:block>
-</table>
-
-<tal:block tal:condition="context/id" tal:replace="structure context/history" />
-
-</div>
-
-</td>
-
-</tal:block>
--- a/scripts/notify-roundup/html/user.item.html	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-<tal:doc metal:use-macro="templates/page/macros/frame"
-define="edit_ok context/is_edit_ok"
->
-<title metal:fill-slot="head_title">
-<tal:if condition="context/id" i18n:translate=""
- >User <span tal:replace="context/id" i18n:name="id"
- />: <span tal:replace="context/username" i18n:name="title"
- /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:if>
-<tal:if condition="not:context/id" i18n:translate=""
- >New User - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:if>
-</title>
-<metal:slot fill-slot="more-javascript">
-<script metal:use-macro="templates/page/macros/user_utils"></script>
-<script type="text/javascript" src="@@file/help_controls.js"></script>
-</metal:slot>
-<tal:block metal:fill-slot="body_title"
-  define="edit_ok context/is_edit_ok">
- <span tal:condition="python: not (context.id or edit_ok)"
-  tal:omit-tag="python:1" i18n:translate="">New User</span>
- <span tal:condition="python: not context.id and edit_ok"
-  tal:omit-tag="python:1" i18n:translate="">New User Editing</span>
- <span tal:condition="python: context.id and not edit_ok"
-  tal:omit-tag="python:1" i18n:translate="">User<tal:x
-  replace="context/id" i18n:name="id" /></span>
- <span tal:condition="python: context.id and edit_ok"
-  tal:omit-tag="python:1" i18n:translate="">User<tal:x
-  replace="context/id" i18n:name="id" /> Editing</span>
-</tal:block>
-
-<td class="content" metal:fill-slot="content">
-
-<p tal:condition="python:not (context.is_view_ok()
- or request.user.hasRole('Anonymous'))" i18n:translate="">
- You are not allowed to view this page.</p>
-
-<p tal:condition="python:not context.is_view_ok()
- and request.user.hasRole('Anonymous')" i18n:translate="">
- Please login with your username and password.</p>
-
-<div tal:condition="context/is_view_ok">
-
-<form method="POST"
-      tal:define="required python:'username address'.split()"
-      enctype="multipart/form-data"
-      tal:attributes="action context/designator;
-      onSubmit python:'return checkRequiredFields(\'%s\')'%'\', \''.join(required);
-      ">
-<table class="form" tal:define="
-  th_label templates/page/macros/th_label;
-  src_input templates/page/macros/user_src_input;
-  normal_input templates/page/macros/user_normal_input;
-  pw_input templates/page/macros/user_pw_input;
-  confirm_input templates/page/macros/user_confirm_input;
-  edit_ok context/is_edit_ok;
-  ">
- <tr tal:define="name string:realname; label string:Name; value context/realname; edit_ok edit_ok">
-  <th metal:use-macro="th_label">Name</th>
-  <td><input name="realname" metal:use-macro="src_input"></td>
- </tr>
- <tr tal:define="name string:username; label string:Login Name; value context/username">
-   <th metal:use-macro="th_label">Login Name</th>
-   <td><input metal:use-macro="src_input"></td>
- </tr>
-  <tal:if condition="edit_ok">
- <tr tal:define="name string:password; label string:Login Password">
-  <th metal:use-macro="th_label">Login Password</th>
-  <td><input metal:use-macro="pw_input" type="password"></td>
- </tr>
- <tr tal:define="name string:password; label string:Confirm Password">
-  <th metal:use-macro="th_label">Confirm Password</th>
-  <td><input metal:use-macro="confirm_input" type="password"></td>
- </tr>
-  </tal:if>
- <tr>
- <th i18n:translate="">Subversion login</th>
- <td tal:content="structure context/svn_name/field">svn_name</td>
- </tr>
-  <tal:if condition="python:request.user.hasPermission('Web Roles')">
- <tr tal:define="name string:roles; label string:Roles;">
-  <th><label for="roles" i18n:translate="">Roles</label></th>
-  <td tal:define="gips context/id">
-    <tal:subif condition=gips define="value context/roles">
-      <input metal:use-macro="normal_input">
-    </tal:subif>
-    <tal:subif condition="not:gips" define="value db/config/NEW_WEB_USER_ROLES">
-      <input metal:use-macro="normal_input">
-    </tal:subif>
-   <tal:block i18n:translate="">(to give the user more than one role,
-    enter a comma,separated,list)</tal:block>
-  </td>
- </tr>
- </tal:if>
-
- <tr tal:define="name string:phone; label string:Phone; value context/phone">
-  <th metal:use-macro="th_label">Phone</th>
-  <td><input name="phone" metal:use-macro="normal_input"></td>
- </tr>
-
- <tr tal:define="name string:organisation; label string:Organisation; value context/organisation">
-  <th metal:use-macro="th_label">Organisation</th>
-  <td><input name="organisation" metal:use-macro="normal_input"></td>
- </tr>
-
- <tr tal:condition="python:edit_ok or context.timezone"
-     tal:define="name string:timezone; label string:Timezone; value context/timezone">
-  <th metal:use-macro="th_label">Timezone</th>
-  <td><input tal:replace="structure python:
-       utils.tzfield(context.timezone, 'timezone', db.config.DEFAULT_TIMEZONE)"/>
-  </td>
- </tr>
-
- <tr tal:define="name string:address; label string:E-mail address; value context/address">
-  <th metal:use-macro="th_label">E-mail address</th>
-  <td tal:define="mailto python:context.address.field(id='address');
-	  mklink python:mailto and not edit_ok">
-      <a href="mailto:calvin@the-z.org"
-		  tal:attributes="href string:mailto:$value"
-		  tal:content="value"
-          tal:condition="python:mklink">calvin@the-z.org</a>
-      <tal:if condition=edit_ok>
-      <input metal:use-macro="src_input" value="calvin@the-z.org">
-      </tal:if>
-      &nbsp;
-  </td>
- </tr>
-
- <tr>
-  <th><label for="alternate_addresses" i18n:translate="">Alternate E-mail addresses<br>One address per line</label></th>
-  <td>
-    <textarea rows=5 cols=40 tal:replace="structure context/alternate_addresses/multiline">nobody@nowhere.org
-anybody@everywhere.net
-(alternate_addresses)
-    </textarea>
-  </td>
- </tr>
-
- <tr tal:condition="edit_ok">
-  <td>
-   &nbsp;
-   <input type="hidden" name="@template" value="item">
-   <input type="hidden" name="@required" value="username,address"
-          tal:attributes="value python:','.join(required)">
-  </td>
-  <td><input type="submit" value="save" tal:replace="structure context/submit"><!--submit button here-->
-    <input type="reset">
-  </td>
- </tr>
-</table>
-</form>
-
-<tal:block tal:condition="not:context/id" i18n:translate="">
-<table class="form">
-<tr>
- <td>Note:&nbsp;</td>
- <th class="required">highlighted</th>
- <td>&nbsp;fields are required.</td>
-</tr>
-</table>
-</tal:block>
-
-<tal:block tal:condition="context/id" tal:replace="structure context/history" />
-
-</div>
-
-</td>
-
-</tal:doc>
--- a/scripts/notify-roundup/notify-roundup.ini	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-; notify-roundup.py configuration file
-
-[main]
-; notify a local or emailed tracker -- 'email' or 'local'
-;mode = email
-mode = local
-
-; change this to detect other issue types
-; multiple issue classes are possible (use regular expression "either" syntax)
-item-class = bug
-; item-class = system
-; item-class = dev|system|network
-
-; only set this if socket.gethostname() doesn't return the host's name as
-; registered with your tracker
-; host = host.name.example
-
-[local]
-; if notifying a local tracker, configure this variable
-tracker-home = /path/to/your/tracker-home
-
-[email]
-; if notifying a tracker by email, configure these variables
-smtp-host = smtp-host.example
-tracker-address = issues@host.example
-; email-domain is used in conjuntion with the address mappings below
-default-domain = @host.example
-
-[address mappings]
-; map Subversion author names to email addresses that the tracker will
-; recognise. The "email :: default-domain" var will be appended if the
-; address doesn't specify a domain.
-richard = rjones
-; richard = ni@spam.example
-
-; If no mapping is defined for a particular author, we either:
-; 1. use the <Subversion author name>@<default-domain> address or,
-; 2. if a "*" entry is defined under address mappings, then we use
-;    that address as the from address.
-;* = unknown
-
--- a/scripts/notify-roundup/notify-roundup.py	Thu Jul 02 17:57:27 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,385 +0,0 @@
-#!/usr/bin/python
-#
-# notify-roundup.py: call into a roundup tracker to notify it of commits
-#
-# USAGE: notify-roundup.py TRACKER-HOME REPOS-DIR REVISION
-#        notify-roundup.py TRACKER-HOME REPOS-DIR REVISION AUTHOR PROPNAME
-#
-#   TRACKER-HOME is the tracker to notify
-#
-# See end of file for change history
-
-import sys, os, time, cStringIO, re, logging, smtplib, ConfigParser, socket
-
-
-# configure logging
-logger = logging.getLogger('notify-roundup')
-hdlr = logging.FileHandler('/tmp/log')
-formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
-hdlr.setFormatter(formatter)
-logger.addHandler(hdlr)
-logger.propogate = False
-logger.setLevel(logging.DEBUG)
-
-#print sys.argv
-# now try to import stuff that might not work
-try:
-    import roundup.instance, roundup.date
-
-    import svn.fs
-    import svn.delta
-    import svn.repos
-    import svn.core
-except:
-    logger.exception('Exception while importing Roundup and SVN')
-    sys.exit(1)
-
-class Failed(Exception):
-    pass
-class Unauthorised(Failed):
-    pass
-
-def main(pool):
-    '''Handle the commit revision.
-    '''
-    # command-line args
-    cfg = ConfigParser.ConfigParser()
-    cfg.read(sys.argv[1])
-    repos_dir = sys.argv[2]
-    revision = int(sys.argv[3])
-
-    # get a handle on the revision in the repository
-    repos = Repository(repos_dir, revision, pool)
-
-    repos.klass = cfg.get('main', 'item-class')
-    if not repos.extract_info():
-        return
-
-    if cfg.has_option('main', 'host'):
-        repos.host = cfg.get('main', 'host')
-    else:
-        repos.host = socket.gethostname()
-
-    mode = cfg.get('main', 'mode')
-    if mode == 'local':
-        notify_local(cfg.get('local', 'tracker-home'), repos)
-    elif mode == 'email':
-        tracker_address = cfg.get('email', 'tracker-address')
-        domain = cfg.get('email', 'default-domain')
-        smtp_host = cfg.get('email', 'smtp-host')
-        if cfg.has_option('address mappings', repos.author):
-            mapped_email = cfg.get('address mappings', repos.author)
-        elif cfg.has_option('address mappings', '*'):
-            mapped_email = cfg.get('address mappings', '*')
-        else:
-            mapped_email = repos.author
-        if '@' not in mapped_email:
-            mapped_email += domain
-        notify_email(tracker_address, mapped_email, smtp_host, repos)
-    else:
-        logging.error('invalid mode %s in config file'%mode)
-
-
-def notify_email(tracker_address, from_address, smtp_host, repos):
-    subject = '[%s%s] SVN commit message'%(repos.klass, repos.itemid)
-    if repos.status:
-        subject += ' [status=%s]'%repos.status
-    date = time.strftime('%Y-%m-%d %H:%M:%S', repos.date)
-    message = '''From: %s
-To: %s
-Subject: %s
-
-revision=%s
-host=%s
-repos=%s
-date=%s
-summary=%s
-
-%s'''%(from_address, tracker_address, subject, repos.rev, repos.host,
-    repos.repos_dir, date, repos.summary, repos.message)
-
-    logger.debug('MESSAGE TO SEND\n%s'%message)
-
-    smtp = smtplib.SMTP(smtp_host)
-    try:
-        smtp.sendmail(from_address, [tracker_address], message)
-    except:
-        logging.exception('mail to %r from %r via %r'%(tracker_address,
-            from_address, smtp_host))
-
-def notify_local(tracker_home, repos):
-    # get a handle on the tracker db
-    tracker = roundup.instance.open(tracker_home)
-    db = tracker.open('admin')
-    try:
-        notify_local_inner(db, tracker_home, repos)
-    except:
-        db.rollback()
-        db.close()
-        raise
-
-def notify_local_inner(db, tracker_home, repos):
-    # sanity check
-    try:
-        db.getclass(repos.klass)
-    except KeyError:
-        logger.error('no such tracker class %s'%repos.klass)
-        raise Failed
-    if not db.getclass(repos.klass).hasnode(repos.itemid):
-        logger.error('no such %s item %s'%(repos.klass, repos.itemid))
-        raise Failed
-    if repos.status:
-        try:
-            status_id = db.status.lookup(repos.status)
-        except KeyError:
-            logger.error('no such status %s'%repos.status)
-            raise Failed
-
-    print repos.host, repos.repos_dir
-    # get the svn repo information from the tracker
-    try:
-        svn_repo_id = db.svn_repo.stringFind(host=repos.host,
-            path=repos.repos_dir)[0]
-    except IndexError:
-        logger.error('no repository %s in tracker'%repos.repos_dir)
-        raise Failed
-
-    # log in as the appropriate user
-    try:
-        matches = db.user.stringFind(svn_name=repos.author)
-    except KeyError:
-        # the user class has no property "svn_name"
-        matches = []
-    if matches:
-        userid = matches[0]
-    else:
-        try:
-            userid = db.user.lookup(repos.author)
-        except KeyError:
-            raise Failed, 'no Roundup user matching %s'%repos.author
-    username = db.user.get(userid, 'username')
-    db.close()
-
-    # tell Roundup
-    tracker = roundup.instance.open(tracker_home)
-    db = tracker.open(username)
-
-    # check perms
-    if not db.security.hasPermission('Create', userid, 'svn_rev'):
-        raise Unauthorised, "Can't create items of class 'svn_rev'"
-    if not db.security.hasPermission('Create', userid, 'msg'):
-        raise Unauthorised, "Can't create items of class 'msg'"
-    if not db.security.hasPermission('Edit', userid, repos.klass,
-            'messages', repos.itemid):
-        raise Unauthorised, "Can't edit items of class '%s'"%repos.klass
-    if repos.status and not db.security.hasPermission('Edit', userid,
-            repos.klass, 'status', repos.itemid):
-        raise Unauthorised, "Can't edit items of class '%s'"%repos.klass
-
-    # create the revision
-    svn_rev_id = db.svn_rev.create(repository=svn_repo_id, revision=repos.rev)
-
-    # add the message to the spool
-    date = roundup.date.Date(repos.date)
-    msgid = db.msg.create(content=repos.message, summary=repos.summary,
-        author=userid, date=date, revision=svn_rev_id)
-    klass = db.getclass(repos.klass)
-    messages = klass.get(repos.itemid, 'messages')
-    messages.append(msgid)
-    klass.set(repos.itemid, messages=messages)
-    
-    # and set the status
-    if repos.status:
-        klass.set(repos.itemid, status=status_id)
-
-    db.commit()
-    logger.debug('Roundup modification complete')
-    db.close()
-
-
-def _select_adds(change):
-  return change.added
-def _select_deletes(change):
-  return change.path is None
-def _select_modifies(change):
-  return not change.added and change.path is not None
-
-
-def generate_list(output, header, changelist, selection):
-    items = [ ]
-    for path, change in changelist:
-      if selection(change):
-        items.append((path, change))
-    if not items:
-      return
-
-    output.write('%s:\n' % header)
-    for fname, change in items:
-      if change.item_kind == svn.core.svn_node_dir:
-        is_dir = '/'
-      else:
-        is_dir = ''
-      if change.prop_changes:
-        if change.text_changed:
-          props = '   (contents, props changed)'
-        else:
-          props = '   (props changed)'
-      else:
-        props = ''
-      output.write('   %s%s%s\n' % (fname, is_dir, props))
-      if change.added and change.base_path:
-        if is_dir:
-          text = ''
-        elif change.text_changed:
-          text = ', changed'
-        else:
-          text = ' unchanged'
-        output.write('      - copied%s from r%d, %s%s\n'
-                     % (text, change.base_rev, change.base_path[1:], is_dir))
-
-class Repository:
-    '''Hold roots and other information about the repository. From mailer.py
-    '''
-    def __init__(self, repos_dir, rev, pool):
-        self.repos_dir = repos_dir
-        self.rev = rev
-        self.pool = pool
-
-        self.repos_ptr = svn.repos.svn_repos_open(repos_dir, pool)
-        self.fs_ptr = svn.repos.svn_repos_fs(self.repos_ptr)
-
-        self.roots = {}
-
-        self.root_this = self.roots[rev] = svn.fs.revision_root(self.fs_ptr,
-            rev, self.pool)
-
-        self.author = self.get_rev_prop(svn.core.SVN_PROP_REVISION_AUTHOR)
-
-    def get_rev_prop(self, propname):
-        return svn.fs.revision_prop(self.fs_ptr, self.rev, propname, self.pool)
-
-    def extract_info(self):
-        issue_re = re.compile('^\s*(%s)\s*(\d+)(\s+(\S+))?\s*$'%self.klass,
-            re.I)
-
-        # parse for Roundup item information
-        log = self.get_rev_prop(svn.core.SVN_PROP_REVISION_LOG) or ''
-        for line in log.splitlines():
-            m = issue_re.match(line)
-            if m:
-                break
-        else:
-            # nothing to do
-            return
-
-        # parse out the issue information
-        klass = m.group(1)
-        self.itemid = m.group(2)
-
-        issue = klass + self.itemid
-        self.status = m.group(4)
-
-        logger.debug('Roundup info item=%r, status=%r'%(issue, self.status))
-
-        # get all the changes and sort by path
-        editor = svn.repos.RevisionChangeCollector(self.fs_ptr, self.rev,
-            self.pool)
-        e_ptr, e_baton = svn.delta.make_editor(editor, self.pool)
-        svn.repos.svn_repos_replay(self.root_this, e_ptr, e_baton, self.pool)
-
-        changelist = editor.changes.items()
-        changelist.sort()
-
-        # figure out the changed directories
-        dirs = { }
-        for path, change in changelist:
-            if change.item_kind == svn.core.svn_node_dir:
-                dirs[path] = None
-            else:
-                idx = path.rfind('/')
-                if idx == -1:
-                    dirs[''] = None
-                else:
-                    dirs[path[:idx]] = None
-
-        dirlist = dirs.keys()
-
-        # figure out the common portion of all the dirs. note that there is
-        # no "common" if only a single dir was changed, or the root was changed.
-        if len(dirs) == 1 or dirs.has_key(''):
-            commondir = ''
-        else:
-            common = dirlist.pop().split('/')
-            for d in dirlist:
-                parts = d.split('/')
-                for i in range(len(common)):
-                    if i == len(parts) or common[i] != parts[i]:
-                        del common[i:]
-                        break
-            commondir = '/'.join(common)
-            if commondir:
-                # strip the common portion from each directory
-                l = len(commondir) + 1
-                dirlist = [ ]
-                for d in dirs.keys():
-                    if d == commondir:
-                        dirlist.append('.')
-                    else:
-                        dirlist.append(d[l:])
-            else:
-                # nothing in common, so reset the list of directories
-                dirlist = dirs.keys()
-
-        # compose the basic subject line. later, we can prefix it.
-        dirlist.sort()
-        dirlist = ' '.join(dirlist)
-
-        if commondir:
-            self.summary = 'r%d - in %s: %s' % (self.rev, commondir, dirlist)
-        else:
-            self.summary = 'r%d - %s' % (self.rev, dirlist)
-
-        # Generate email for the various groups and option-params.
-        output = cStringIO.StringIO()
-
-        # print summary sections
-        generate_list(output, 'Added', changelist, _select_adds)
-        generate_list(output, 'Removed', changelist, _select_deletes)
-        generate_list(output, 'Modified', changelist, _select_modifies)
-
-        output.write('Log:\n%s\n'%log)
-
-        self.message = output.getvalue()
-
-        svndate = self.get_rev_prop(svn.core.SVN_PROP_REVISION_DATE)
-        self.date = time.localtime(svn.core.secs_from_timestr(svndate,
-            self.pool))
-
-        return True
-
-if __name__ == '__main__':
-    try:
-        svn.core.run_app(main)
-    except Failed, message:
-        logger.error(message)
-        sys.exit(1)
-    except:
-        logger.exception('top level')
-        sys.exit(1)
-
-#
-# 2005-05-16 - 1.2
-# 
-#   - Status wasn't being set by ID in local mode
-#   - Wasn't catching errors in local changes, hence not cleaning up db
-#     correctly
-#   - svnauditor.py wasn't handling the fifth argument from notify-roundup.py
-#   - viewcvs_url formatting wasn't quite right
-#
-# 2005-05-04 - 1.1
-#   - Several fixes from  Ron Alford
-#   - Don't change issue titles to "SVN commit message..."
-# 
-# 2005-04-26 - 1.0
-#   - Initial version released
-#

Roundup Issue Tracker: http://roundup-tracker.org/