[Zope3-checkins] CVS: zopeproducts/bugtracker - I18N.txt:1.1 INSTALL.txt:1.1 LICENSE.txt:1.1 README.txt:1.1 TODO.txt:1.1 VERSION.txt:1.1 __init__.py:1.1 bug.py:1.1 comment.py:1.1 configure.zcml:1.1 interfaces.py:1.1 mail.py:1.1 tracker.py:1.1 vocabulary.py:1.1
Stephan Richter
srichter@cosmos.phy.tufts.edu
Thu, 24 Jul 2003 14:08:15 -0400
Update of /cvs-repository/zopeproducts/bugtracker
In directory cvs.zope.org:/tmp/cvs-serv302
Added Files:
I18N.txt INSTALL.txt LICENSE.txt README.txt TODO.txt
VERSION.txt __init__.py bug.py comment.py configure.zcml
interfaces.py mail.py tracker.py vocabulary.py
Log Message:
First Checkin of the Bug Tracker. A list of features is the README.txt file
and a to-do list is in TODO.txt.
The code features the use of vocabularies and vocabulary fields.
There is still a bit of work to do, but I am pretty close to make it usable
for us.
=== Added File zopeproducts/bugtracker/I18N.txt ===
Internationalization (I18n) and Localalization (L10n)
=====================================================
Crating/Updating Message Catalog Template (POT) Files
-----------------------------------------------------
0. Install 'bugtracker' in '<zope3>/src/zopeproducts'.
1. Set the the Python path::
export PYTHONPATH=<zope3>/src
2. Go into the 'locales' directory and execute extract.py::
python2.2 extract.py
Updating Message Catalog (PO) Files
-----------------------------------
1. For each language do simply::
msgmerge -U de/LC_MESSAGES/bugtracker.po bugtracker.pot
2. Translate the updated PO file.
Note: KBabel is a great tool for this task!
Compiling Message Catalogs (PO) to binary (MO) Files
----------------------------------------------------
1. Go to the right directory, such as '<bugtracker>/locales/de/LC_MESSAGES'.
2. Run the following command::
msgfmt -o bugtracker.mo bugtracker.po
=== Added File zopeproducts/bugtracker/INSTALL.txt ===
Installation
============
- create 'zopeproducts' inside your Zope 3 installation src directory
- add an empty '__init__.py' to zopeproducts
- copy the 'bugtracker' folder to 'zopeproducts'
- add the following line to the 'products.zcml' file::
<include package='zopeproducts.bugtracker' />
- XXX: (Not yet) You need to define the following role declarations to your
user in order to use the zwiki product effectively.
<grant role="BugTrackerAdmin" principal="user" />
<grant role="BugTrackerEditor" principal="user" />
<grant role="BugTrackerUser" principal="user" />
<grant role="BugTrackerUser" principal="anybody" />
Usage
=====
1. To see a Bug Tracker in action, go into the management interface and add
a "Bug Tracker" object named 'tracker'. Leave the preselected option and
enter a title.
2. To get to the end user interface, enter::
http://localhost:8080/++skin++tracker/tracker
=== Added File zopeproducts/bugtracker/LICENSE.txt ===
Zope Public License (ZPL) Version 2.0
-----------------------------------------------
This software is Copyright (c) Zope Corporation (tm) and
Contributors. All rights reserved.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the above
copyright notice, this list of conditions, and the following
disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions, and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
3. The name Zope Corporation (tm) must not be used to
endorse or promote products derived from this software
without prior written permission from Zope Corporation.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use Servicemarks
(sm) or Trademarks (tm) of Zope Corporation. Use of them is
covered in a separate agreement (see
http://www.zope.com/Marks).
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY ZOPE CORPORATION ``AS IS''
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL ZOPE CORPORATION OR ITS CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
This software consists of contributions made by Zope
Corporation and many individuals on behalf of Zope
Corporation. Specific attributions are listed in the
accompanying credits file.
=== Added File zopeproducts/bugtracker/README.txt ===
Bug Tracker Product for Zope 3
==============================
This product is an implementation of a bug tracker in Zope 3.
Features
--------
Bug Tracker
- View list of bugs
o Filtering by status, type, release, priority and text
o Batching, when list of bugs becomes too long.
o Bug status and priority values are marked up based on value.
- Settings
o When creating a Bug Tracker, one can select the option to
automatically create a set of status, type, release and priority
choices.
o The choices for the status, type, release and priority are flexible
and can be changed.
- Mail Subscriptions
o These are Bug Tracker wide mail subscriptions that send the
recipients an E-mail about additions, changes and deletions of bugs.
Bug
- Overview
o This screen provides a comprehensive overview of all the available
information about the bug.
o The status and priority are marked up based on their value.
o The description and the comments are rendered using STX.
o Upload Files and Images
o Add new comments
- Edit
o To provide a familiar interface, the edit form is layed out in the
same way as the overview
- Dependencies
o One in my opinion major improvement over the current collector is
the availability of a dependency feature, where I can say that this
bug depends on that one.
o Based on this information a dependency tree is generated using the
markup rules for status and priority, so that a user can quickly
recognize critical spots in the tree.
o There is also a Statistics section that tells you how many bugs are
completed, have not been viewed and are being fixed.
- Mail Subscriptions
o These are specific bug mail subscriptions that send the recipients
an E-mail about additions, changes and deletions of the bug.
=== Added File zopeproducts/bugtracker/TODO.txt ===
To Do
=====
Tests
- Write tests for View code
- Write tests for the Batcher
I18n and L10n
- Internationalize all screens and code.
- Create message catalogs.
- Provide a sample translation (probably German).
Security
- Declare permissions and roles
- Assign correct permissions to objects and views
UI
- Display Term title instead of value in the drop-down elements.
Other Features
- Improve Mailings (use some sort of diff library)
- XML Import and Export
=== Added File zopeproducts/bugtracker/VERSION.txt ===
0.1
=== Added File zopeproducts/bugtracker/__init__.py ===
=== Added File zopeproducts/bugtracker/bug.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A Container based Bug
$Id: bug.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import implements
from zope.component import getAdapter, queryAdapter
from zope.app.interfaces.dublincore import IZopeDublinCore
from zope.app.interfaces.annotation import IAnnotations
from zope.app.interfaces.index.text import ISearchableText
from zope.app.traversing import getParent, getName
from zope.app.container.btree import BTreeContainer
from zope.app.context import ContextWrapper
from zope.proxy import removeAllProxies
from zopeproducts.bugtracker.interfaces import IBug, IComment
from zopeproducts.bugtracker.interfaces import IBugDependencies
DependencyKey = 'http://www.zope.org/bugtracker#1.0/Dependencies'
class Bug(BTreeContainer):
implements(IBug)
# See zopeproducts.bugtracker.interfaces.IBug
status = u'new'
# See zopeproducts.bugtracker.interfaces.IBug
priority = u'normal'
# See zopeproducts.bugtracker.interfaces.IBug
type = u'bug'
# See zopeproducts.bugtracker.interfaces.IBug
release = u'None'
def getOwners(self):
return getattr(self, '_owners', [])
def setOwners(self, owners):
self._owners = removeAllProxies(owners)
# See zopeproducts.bugtracker.interfaces.IBug
owners = property(getOwners, setOwners)
def setTitle(self, title):
"""Set bug title."""
dc = queryAdapter(self, IZopeDublinCore)
dc.title = title
def getTitle(self):
"""Get bug title."""
dc = queryAdapter(self, IZopeDublinCore)
return dc.title
# See zopeproducts.bugtracker.interfaces.IBug
title = property(getTitle, setTitle)
def setDescription(self, description):
"""Set bug description."""
dc = queryAdapter(self, IZopeDublinCore)
dc.description = description
def getDescription(self):
"""Get bug description."""
dc = queryAdapter(self, IZopeDublinCore)
return dc.description
# See zopeproducts.bugtracker.interfaces.IBug
description = property(getDescription, setDescription)
def getSubmitter(self):
"""Get bug submitter."""
dc = queryAdapter(self, IZopeDublinCore)
if not dc.creators:
return None
return dc.creators[0]
# See zopeproducts.bugtracker.interfaces.IBug
submitter = property(getSubmitter)
class BugDependencyAdapter(object):
implements(IBugDependencies)
__used_for__ = IBug
def __init__(self, context):
self.context = context
self._annotations = getAdapter(context, IAnnotations)
if not self._annotations.get(DependencyKey):
self._annotations[DependencyKey] = ()
def setDependencies(self, dependencies):
self._annotations[DependencyKey] = tuple(dependencies)
def getDependencies(self):
return self._annotations[DependencyKey]
dependencies = property(getDependencies, setDependencies)
def findChildren(self, recursive=True, all=None):
"See zopeproducts.bugtracker.interfaces.IBugDependencies"
if all is None:
all = []
tracker = getParent(self.context)
contextName = getName(self.context)
deps = getAdapter(self.context, IBugDependencies)
children = []
for bugName in deps.dependencies:
# Circle detection; if the bugName was processed before, skip it
if bugName in all:
continue
else:
all.append(bugName)
wrapped = ContextWrapper(tracker[bugName], tracker, name=bugName)
if recursive is True:
deps = getAdapter(wrapped, IBugDependencies)
subs = deps.findChildren(all=all)
else:
subs = ()
children.append((wrapped, subs))
return tuple(children)
class SearchableText:
"""This adapter allows us to get all searchable text at once."""
implements(ISearchableText)
__used_for__ = IBug
def __init__(self, context):
self.context = context
def getSearchableText(self):
return [self.context.title, self.context.description]
=== Added File zopeproducts/bugtracker/comment.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A simple Comment
$Id: comment.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import implements
from persistence import Persistent
from zopeproducts.bugtracker.interfaces import IComment
class Comment(Persistent):
implements(IComment)
# See zopeproducts.bugtracker.interfaces.IComment
body = u""
=== Added File zopeproducts/bugtracker/configure.zcml ===
<zopeConfigure
xmlns="http://namespaces.zope.org/zope"
xmlns:event="http://namespaces.zope.org/event"
xmlns:mail="http://namespaces.zope.org/mail"
xmlns:translate="http://namespaces.zope.org/gts">
<!-- Setting up the vocabularies for the bug tracker -->
<vocabulary
name="Stati"
factory=".vocabulary.StatusVocabulary" />
<vocabulary
name="Releases"
factory=".vocabulary.ReleaseVocabulary" />
<vocabulary
name="Priorities"
factory=".vocabulary.PriorityVocabulary" />
<vocabulary
name="BugTypes"
factory=".vocabulary.BugTypeVocabulary" />
<vocabulary
name="Users"
factory=".vocabulary.UserVocabulary" />
<content class=".vocabulary.ManagableVocabulary">
<allow interface=".interfaces.IManagableVocabulary"/>
<allow attributes="__contains__"/>
</content>
<content class=".vocabulary.StatusVocabulary">
<require like_class=".vocabulary.ManagableVocabulary"/>
</content>
<content class=".vocabulary.PriorityVocabulary">
<require like_class=".vocabulary.ManagableVocabulary"/>
</content>
<content class=".vocabulary.ReleaseVocabulary">
<require like_class=".vocabulary.ManagableVocabulary"/>
</content>
<content class=".vocabulary.BugTypeVocabulary">
<require like_class=".vocabulary.ManagableVocabulary"/>
</content>
<content class=".vocabulary.SimpleTerm">
<allow interface="zope.schema.interfaces.ITokenizedTerm"/>
<allow attributes="title"/>
</content>
<content class=".vocabulary.UserVocabulary">
<allow interface="zope.schema.interfaces.IVocabulary"/>
<allow interface="zope.schema.interfaces.IVocabularyTokenized"/>
<allow attributes="__contains__"/>
</content>
<content class=".vocabulary.UserTerm">
<allow
interface="zope.schema.interfaces.ITokenizedTerm"/>
<allow attributes="principal"/>
</content>
<!-- Bug Tracker related configuration -->
<content class=".tracker.BugTracker">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
<factory
id="BugTracker"
permission="zope.View"
title="Bug Tracker"
description="A Bug Tracker" />
<allow interface="zope.app.interfaces.services.service.Read" />
<require
permission="zope.ManageServices"
interface="zope.app.interfaces.services.service.Write" />
<require
permission="zope.View"
interface="zope.app.interfaces.container.IReadContainer"/>
<require
permission="zope.View"
interface="zope.app.interfaces.container.IWriteContainer"/>
<require
permission="zope.View"
set_schema=".interfaces.IBugTracker" />
</content>
<adapter
factory=".vocabulary.StatusVocabulary"
provides=".interfaces.IStatusVocabulary"
for=".interfaces.IBugTracker" />
<adapter
factory=".vocabulary.ReleaseVocabulary"
provides=".interfaces.IReleaseVocabulary"
for=".interfaces.IBugTracker" />
<adapter
factory=".vocabulary.PriorityVocabulary"
provides=".interfaces.IPriorityVocabulary"
for=".interfaces.IBugTracker" />
<adapter
factory=".vocabulary.BugTypeVocabulary"
provides=".interfaces.IBugTypeVocabulary"
for=".interfaces.IBugTracker" />
<adapter
factory=".mail.MailSubscriptions"
provides=".interfaces.IMailSubscriptions"
for=".interfaces.IBugTracker" />
<!-- Bug related configuration -->
<content class=".bug.Bug">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"/>
<implements
interface="zope.app.interfaces.container.IContentContainer" />
<factory
id="Bug"
permission="zope.View"
title="Bug"
description="A Bug" />
<require
permission="zope.View"
interface="zope.app.interfaces.container.IReadContainer"/>
<require
permission="zope.View"
interface="zope.app.interfaces.container.IWriteContainer"/>
<require
permission="zope.View"
interface=".interfaces.IBug"
set_schema=".interfaces.IBug" />
</content>
<adapter
factory=".bug.SearchableText"
provides="zope.app.interfaces.index.text.ISearchableText"
for=".interfaces.IBug" />
<adapter
factory=".bug.BugDependencyAdapter"
provides=".interfaces.IBugDependencies"
for=".interfaces.IBug" />
<adapter
factory=".mail.MailSubscriptions"
provides=".interfaces.IMailSubscriptions"
for=".interfaces.IBug" />
<!-- Comment related configuration -->
<content class=".comment.Comment">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable"/>
<factory
id="BugComment"
permission="zope.View"
title="Comment"
description="A comment about the bug." />
<require
permission="zope.View"
interface=".interfaces.IComment"
set_schema=".interfaces.IComment" />
</content>
<!-- Register Mailer and Mail Service -->
<mail:smtpMailer id="bugs-smtp" hostname="localhost" port="25" />
<mail:queuedService permission="zope.SendMail"
queuePath="./src/zopeproducts/bugtracker/mail-queue"
mailer="bugs-smtp" />
<!-- Register event listener for change mails -->
<event:subscribe
subscriber=".mail.mailer"
event_types="zope.app.interfaces.event.IObjectAddedEvent
zope.app.interfaces.event.IObjectModifiedEvent
zope.app.interfaces.event.IObjectRemovedEvent
zope.app.interfaces.event.IObjectMovedEvent" />
<!-- Register various browser related components, including all views -->
<include package=".browser" />
</zopeConfigure>
=== Added File zopeproducts/bugtracker/interfaces.py ===
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bug Tracker Interfaces
Bag Tracker related interfaces.
$Id: interfaces.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import Interface
from zope.schema import Text, TextLine, List, Dict
from zope.schema.vocabulary import VocabularyField, VocabularyListField
from zope.schema.interfaces import IVocabulary, IVocabularyTokenized
from zope.app.interfaces.container import IContainer
from zope.i18n import MessageIDFactory
_ = MessageIDFactory('bugtracker')
class IBugTracker(IContainer):
"""A Bug Tracker object represents a collection of bugs for a particular
software or subject.
Note that there is no specific interface for managing bugs, since the
generic IContainer interface is sufficient.
The status messages are a collection of possible stati a bug can be
in. The reason we define the list here is that we can change and adjust it
later for the particular instance.
"""
title = TextLine(
title = _(u"Title"),
description = _(u"Title of the bug tracker."),
required=True)
class IBug(Interface):
"""A bug is the content object containing all necessary information that
are relevant to a bug report.
Note: I also included title in the interface, since I think it is part of
the data and not the meta data of the bug content object.
"""
title = TextLine(
title = _(u"Title"),
description = _(u"Title/Summary of the bug."),
required=True)
description = Text(
title = _(u"Description"),
description = _(u"Detailed Description of the bug."),
required=True)
submitter = TextLine(
title = _(u"Submitter"),
description = _(u"Name of the person that submitted the bug."),
required=False)
status = VocabularyField(
title = _(u"Status"),
description = _(u"The current status of the bug."),
default= 'new',
required = True,
vocabulary = "Stati")
priority = VocabularyField(
title = _(u"Priority"),
description = _(u"Specifies how urgent this bug is."),
default= 'normal',
required = True,
vocabulary = "Priorities")
type = VocabularyField(
title = _(u"Type"),
description = _(u"Specifies of what nature the bug is."),
default= 'bug',
required = True,
vocabulary = "BugTypes")
release = VocabularyField(
title = _(u"Release"),
description = _(u"Defines the release for which the bug is scheduled."),
default = 'None',
required = True,
vocabulary = "Releases")
owners = VocabularyListField(
title = _(u"Owners"),
description = _(u"List of people assigned as owners of the bug."),
required=False,
vocabulary = "Users")
class IBugDependencies(Interface):
"""This object handles the dependencies of a bug."""
dependencies = List(
title = _(u"Dependencies"),
description = _(u"Other bugs this bug depends on."),
value_type = TextLine(title=_(u"Bug Id"),
description=_(u"Bug Id.")),
required=False)
def findChildren(recursive=True):
"""Returns a list of children for this bug.
If the recursive is True, the method recurses into all children
returning the entire sub-graph of this Bug. Is the recursive
argument set to False, only the first level of children will be
returned.
While circular references are okay (since dependencies are general
graphs, not trees), this method must be aware of this fact and cannot
just implement a plain old recursive algorithm. When a circle is
detected, then simply cut off the search at this point.
"""
class IComment(Interface):
"""Simple comment for Bug.
For now we assume the body to be structured text.
"""
body = Text(
title=_(u"Body"),
description=_(u"Renderable body of the Comment."),
default=u"",
required=True)
class IManagableVocabulary(IVocabulary, IVocabularyTokenized):
"""Vocabulary that can be modified by addign and deleting terms.
Note that this is a simple interface, where vocabularies are simple
value-title mappings. The values should be preferibly in ASCII, so that
there is no problem with encoding them in HTML.
"""
def add(value, title):
"""Add a new vocabulary entry."""
def delete(value):
"""Delete an entry from the vocabulary."""
class IStatusVocabulary(IManagableVocabulary):
"""Manageable vocabulary that stores stati."""
class IReleaseVocabulary(IManagableVocabulary):
"""Manageable vocabulary that stores all releases."""
class IPriorityVocabulary(IManagableVocabulary):
"""Manageable vocabulary that stores all priority values."""
class IBugTypeVocabulary(IManagableVocabulary):
"""Manageable vocabulary that stores all type values."""
class IMailSubscriptions(Interface):
"""This interface allows you to retrieve a list of E-mails for
mailings."""
def getSubscriptions():
"""Return a list of E-mails."""
def addSubscriptions(emails):
"""Add a bunch of subscriptions; one would be okay too."""
def removeSubscriptions(emails):
"""Remove a set of subscriptions."""
=== Added File zopeproducts/bugtracker/mail.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Mail Support
$Id: mail.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import implements
from zope.component import getAdapter, getService
from zope.app.interfaces.annotation import IAnnotations
from zope.app.interfaces.event import ISubscriber
from zope.app.interfaces.event import IObjectAddedEvent, IObjectModifiedEvent
from zope.app.interfaces.event import IObjectRemovedEvent, IObjectMovedEvent
from zope.app.traversing import getParent, getName
from interfaces import IBug, IBugTracker, IMailSubscriptions
SubscriberKey = 'http://www.zope.org/bugtracker#1.0/MailSubs/emails'
class MailSubscriptions:
"""An adapter for IBugTracker and IBug to provide an
interface for collecting E-mails for sending out change notices."""
implements(IMailSubscriptions)
def __init__(self, context):
self.context = context
self._annotations = getAdapter(context, IAnnotations)
if not self._annotations.get(SubscriberKey):
self._annotations[SubscriberKey] = ()
def getSubscriptions(self):
"See zopeproducts.messageboard.interfaces.IMailSubscriptions"
return self._annotations[SubscriberKey]
def addSubscriptions(self, emails):
"See zopeproducts.messageboard.interfaces.IMailSubscriptions"
subscribers = list(self._annotations[SubscriberKey])
for email in emails:
if email not in subscribers:
subscribers.append(email.strip())
self._annotations[SubscriberKey] = tuple(subscribers)
def removeSubscriptions(self, emails):
"See zopeproducts.messageboard.interfaces.IMailSubscriptions"
subscribers = list(self._annotations[SubscriberKey])
for email in emails:
if email in subscribers:
subscribers.remove(email)
self._annotations[SubscriberKey] = tuple(subscribers)
class Mailer:
"""Class to handle all outgoing mail."""
implements(ISubscriber)
def notify(self, event):
"""See zope.app.interfaces.event.ISubscriber"""
if IBug.isImplementedBy(event.object):
if IObjectAddedEvent.isImplementedBy(event):
self.handleAdded(event.object)
elif IObjectModifiedEvent.isImplementedBy(event):
self.handleModified(event.object)
elif IObjectRemovedEvent.isImplementedBy(event):
self.handleRemoved(event.object)
def handleAdded(self, object):
subject = 'Added: %s (%s)' %(object.title, getName(object))
emails = self.getAllSubscribers(object)
body = object.description
self.mail(emails, subject, body)
def handleModified(self, object):
subject = 'Modified: %s (%s)' %(object.title, getName(object))
emails = self.getAllSubscribers(object)
body = object.description
self.mail(emails, subject, body)
def handleRemoved(self, object):
subject = 'Removed: %s (%s)' %(object.title, getName(object))
emails = self.getAllSubscribers(object)
body = self.description
self.mail(emails, subject, body)
def getAllSubscribers(self, object):
"""Retrieves all email subscribers for this message and all above."""
emails = ()
obj = object
while IBug.isImplementedBy(obj) or IBugTracker.isImplementedBy(obj):
emails += tuple(getAdapter(
obj, IMailSubscriptions).getSubscriptions())
obj = getParent(obj)
return emails
def mail(self, toaddrs, subject, body):
"""Mail out the change message."""
if not toaddrs:
return
msg = 'Subject: %s\n\n\n%s' %(subject, body)
mail_service = getService(None, 'Mail')
mail_service.send('bugtracker@zope3.org' , toaddrs, msg)
# Create a global mailer object.
mailer = Mailer()
=== Added File zopeproducts/bugtracker/tracker.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""A Bug Tracker implementation
$Id: tracker.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import implements
from zope.app.content.folder import Folder
from zopeproducts.bugtracker.interfaces import IBugTracker
class BugTracker(Folder):
implements(IBugTracker)
# See zopeproducts.bugtracker.interfaces.IBugTracker
title = u''
=== Added File zopeproducts/bugtracker/vocabulary.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Vocabularies for the Bug Tracker
$Id: vocabulary.py,v 1.1 2003/07/24 18:08:03 srichter Exp $
"""
from zope.interface import implements
from zope.component import getAdapter, getService
from zope.app.traversing import getParents
from persistence import Persistent
from persistence.dict import PersistentDict
from zope.schema.interfaces import \
ITokenizedTerm, IVocabulary, IVocabularyTokenized
from zope.app.interfaces.annotation import IAnnotatable, IAnnotations
from zope.app.interfaces.security import IAuthenticationService
from zope.app.services.servicenames import Authentication
from zopeproducts.bugtracker.interfaces import IManagableVocabulary, IBugTracker
from zopeproducts.bugtracker.interfaces import \
IStatusVocabulary, IReleaseVocabulary, IPriorityVocabulary
from zopeproducts.bugtracker.interfaces import \
IBugTypeVocabulary
class SimpleTerm(Persistent):
implements(ITokenizedTerm)
def __init__(self, value, title):
self.value = value
self.title = title
def getToken(self):
return self.value
token = property(getToken)
class ManagableVocabulary(object):
implements(IManagableVocabulary)
__used_for__ = IAnnotatable
key = None
interface = None
def __init__(self, context):
self.context = self._getRealContext(context)
self.annotations = getAdapter(self.context, IAnnotations)
if not self.annotations.get(self.key):
self.annotations[self.key] = PersistentDict()
def __contains__(self, value):
return value in self.annotations[self.key].keys()
def __iter__(self):
return iter(self.annotations[self.key].values())
def __len__(self):
return len(self.annotations[self.key])
def getQuery(self):
return None
def getTerm(self, value):
return self.annotations[self.key][value]
def getTermByToken(self, token):
return self.getTerm(token)
def add(self, value, title):
self.annotations[self.key][value] = SimpleTerm(value, title)
def delete(self, value):
del self.annotations[self.key][value]
def _getRealContext(self, context):
for obj in getParents(context):
if self.interface.isImplementedBy(obj):
return obj
return context
class StatusVocabulary(ManagableVocabulary):
implements(IStatusVocabulary)
key = 'http://www.zope.org/bugtracker#1.0/status/values'
interface = IBugTracker
title = 'Status Definitions'
class ReleaseVocabulary(ManagableVocabulary):
implements(IReleaseVocabulary)
key = 'http://www.zope.org/bugtracker#1.0/release/values'
interface = IBugTracker
title = 'Release Definitions'
class PriorityVocabulary(ManagableVocabulary):
implements(IPriorityVocabulary)
key = 'http://www.zope.org/bugtracker#1.0/priority/values'
interface = IBugTracker
title = 'Priority Definitions'
class BugTypeVocabulary(ManagableVocabulary):
implements(IBugTypeVocabulary)
key = 'http://www.zope.org/bugtracker#1.0/bugtype/values'
interface = IBugTracker
title = 'Bug Type Definitions'
class UserTerm(Persistent):
implements(ITokenizedTerm)
def __init__(self, principal):
self.principal = principal
self.value = principal.getId()
self.token = principal.getId()
class UserVocabulary(object):
implements(IVocabulary, IVocabularyTokenized)
def __init__(self, context):
self.auth = getService(context, 'Authentication')
def __contains__(self, value):
ids = map(lambda p: p.getId(), self.auth.getPrincipals(''))
return value in ids
def __iter__(self):
terms = map(lambda p: UserTerm(p), self.auth.getPrincipals(''))
return iter(terms)
def __len__(self):
return len(self.auth.getPrincipals(''))
def getQuery(self):
return None
def getTerm(self, value):
return UserTerm(self.auth.getPrincipal(value))
def getTermByToken(self, token):
return self.getTerm(token)