[Zope3-checkins] CVS: Zope3/src/zope/app/wiki - INSTALL.txt:1.1 README.txt:1.1 TODO.txt:1.1 VERSION.txt:1.1 __init__.py:1.1 configure.zcml:1.1 diff.py:1.1 index.py:1.1 interfaces.py:1.1 traversal.py:1.1 wiki.py:1.1 wikipage.py:1.1

Philipp von Weitershausen philikon at philikon.de
Fri Feb 27 06:07:02 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/wiki
In directory cvs.zope.org:/tmp/cvs-serv21004/app/wiki

Added Files:
	INSTALL.txt README.txt TODO.txt VERSION.txt __init__.py 
	configure.zcml diff.py index.py interfaces.py traversal.py 
	wiki.py wikipage.py 
Log Message:
Moved the zwiki product to zope.app.wiki. It currently still needs to be
enabled in products.zcml, since it depends on securitypolicy enabled.
That's to change soon when we move securitypolicy back to zope.app.


=== Added File Zope3/src/zope/app/wiki/INSTALL.txt ===
Installation
============

  - Since Wiki is not enabled by default, you need to include the Wiki
    package in your products.zcml file::

    <include package="zope.app.wiki" />

  - You need to define the following role declarations to your user in order
    to use the wiki package effectively::

    <grant role="zwiki.Admin" principal="user" />
    <grant role="zwiki.Editor" principal="user" />
    <grant role="zwiki.User" principal="user" />

    <grant role="zwiki.User" principal="anybody" />

Usage
=====

  1. To see Wikis in action, go into the management interface and add a Wiki
     object named 'wiki'. Leave the two preselected options.

  2. To enter the end user interface, enter::

      http://localhost:8080/++skin++wiki/wiki


=== Added File Zope3/src/zope/app/wiki/README.txt ===
ZWiki for Zope 3
================

This product is a port/rewrite of the famous Zope 2 product Zwiki. At
the current stage only the most basic Wiki functionalities are
implemented and much more work needs to be done.

Features
--------

Rendering

  - Plain Text

  - reStructured Text (reST)


Wiki

  - Table of Contents

  - Mail Subscription for entire Wiki

  - Full-text Search


Wiki Page

  - Proper rendering of Wiki Links

  - Edit Wiki Page

  - Comment on a Wiki Page

  - Declare Wiki Hierarchy (Parents)

  - Local, WikiPage-based Mail Subscription

  - Jumping to other Wikis


Miscellaneous

  - Somewhat sophisticated rendering mechanism. New source types and
    their render methods can now be configured (added) via ZCML.

  - A fully independent skin called 'wiki'; Note that this skill will
    be only useful in the context of a Wiki Page.


=== Added File Zope3/src/zope/app/wiki/TODO.txt ===
TODO
====

Tests

  - Write tests for diff module


Rendering/Views

  - Create custom HTMLDocument class for rendering the STX and ReST in Wiki
    style.

  - Make sure WebDAV works.

  - Add File Extension recognition.

  - Create a Management screen (advancedform) that is accessible for users
    without management rights.


Other Features

  - Make use of Traverser features; i.e. create links that include
    parents...

  - Check in Traverser that found subobj has self.context as parent.

  - Activating diff support for edited Wiki Pages. The main issue right now
    is to get to the old version of the text. so that we can execute the
    Differ.


=== Added File Zope3/src/zope/app/wiki/VERSION.txt ===
Zope3 Wiki 0.0.1

=== Added File Zope3/src/zope/app/wiki/__init__.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.
#
##############################################################################
"""ZWiki for Zope 3

$Id: __init__.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""



=== Added File Zope3/src/zope/app/wiki/configure.zcml ===
<configure
   xmlns="http://namespaces.zope.org/zope"
   xmlns:event="http://namespaces.zope.org/event"
   i18n_domain="zope"
   >

  <!-- Security definitions -->

  <role
      id="zwiki.User"
      title="Wiki User"
      description="Wiki visitors, which can only view and comment on wikis." />

  <role
      id="zwiki.Editor"
      title="Wiki Editor"
      description="The Wiki Editor can create and edit wikis." />

  <role
      id="zwiki.Admin"
      title="Wiki Administrator"
      description="The Wiki Admin can fully manage wiki pages." />

  <permission
      id="zope.app.wiki.ViewWikiPage"
      title="View Wiki Page"
      description="View a Wiki Page." />

  <grant
      permission="zope.app.wiki.ViewWikiPage"
      role="zwiki.User" />

  <permission
      id="zope.app.wiki.CommentWikiPage"
      title="Comment on Wiki Page"
      description="Make a comment on Wiki Page." />

  <grant
      permission="zope.app.wiki.CommentWikiPage"
      role="zwiki.User" />

  <permission
      id="zope.app.wiki.AddWikiPage"
      title="Add Wiki Page"
      description="Add Wiki Page." />

  <grant
      permission="zope.app.wiki.AddWikiPage"
      role="zwiki.Editor" />

  <permission
      id="zope.app.wiki.EditWikiPage"
      title="Edit Wiki Page"
      description="Edit Wiki Page." />

  <grant
      permission="zope.app.wiki.EditWikiPage"
      role="zwiki.Editor" />

  <permission
      id="zope.app.wiki.DeleteWikiPage"
      title="Delete Wiki Page"
      description="Delete Wiki Page." />

  <grant
      permission="zope.app.wiki.DeleteWikiPage"
      role="zwiki.Admin" />

  <permission
      id="zope.app.wiki.ReparentWikiPage"
      title="Reparent Wiki Page"
      description="Reparent a Wiki Page." />

  <grant
      permission="zope.app.wiki.ReparentWikiPage"
      role="zwiki.Admin"/>


  <!-- Content declarations -->

 <content class=".wiki.Wiki">

    <implements interface="zope.app.interfaces.container.IContentContainer" />

    <implements
       interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />

    <factory
        id="Wiki"
        permission="zope.ManageContent"
        description="Minimal Wiki Page Container implementation " />

    <require
        permission="zope.View"
        interface="zope.app.interfaces.container.IReadContainer"/>

    <require
        permission="zope.app.wiki.AddWikiPage"
        interface="zope.app.interfaces.container.IWriteContainer"/>

  </content>

  <!-- Mail Subscriptions support -->
  <adapter
      factory=".wikipage.MailSubscriptions"
      provides=".interfaces.IMailSubscriptions"
      for=".interfaces.IWiki" />


  <content class=".wikipage.WikiPage">

    <implements
       interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />

    <factory
        id="WikiPage"
        permission="zope.app.wiki.AddWikiPage"
        title="Wiki Page"
        description="A Wiki Page" />

    <!-- XXX: I am not sure whether this is safe. We probably have to split
         the interface from the schema. -->
    <allow interface=".interfaces.IWikiPage" />

    <require
        permission="zope.app.wiki.AddWikiPage"
        set_schema=".interfaces.IWikiPage" />

  </content>

  <adapter
      factory=".wikipage.WikiPageHierarchyAdapter"
      provides=".interfaces.IWikiPageHierarchy"
      for=".interfaces.IWikiPage" />

  <!-- Mail Subscriptions support -->
  <adapter
      factory=".wikipage.MailSubscriptions"
      provides=".interfaces.IMailSubscriptions"
      for=".interfaces.IWikiPage" />

  <adapter 
      factory=".traversal.WikiPageTraversable"
      provides="zope.app.interfaces.traversing.ITraversable"
      for=".interfaces.IWikiPage" />


  <adapter 
      factory="zope.app.wiki.wikipage.SearchableText"
      provides="zope.app.interfaces.index.text.ISearchableText"
      for="zope.app.wiki.interfaces.IWikiPage" />


  <!-- WikiPage FTP configurations -->
  <adapter 
     for=".interfaces.IWikiPage"
     provides="zope.app.interfaces.file.IReadFile"
     factory=".wikipage.WikiPageReadFile"
     permission="zope.app.wiki.ViewWikiPage"
     />

  <adapter 
     for=".interfaces.IWikiPage"
     provides="zope.app.interfaces.file.IWriteFile"
     factory=".wikipage.WikiPageWriteFile"
     permission="zope.app.wiki.EditWikiPage"
     />

  <!-- Wiki Index registration -->

  <content class=".index.WikiTextIndex">

    <require
        permission="zope.ManageServices"
        interface="zope.app.interfaces.index.text.IUITextIndex"
        attributes="query"
        />

    <factory
        id="zope.app.wiki.index.WikiTextIndex"
        permission="zope.ManageServices"
        />
    <implements
        interface="zope.app.interfaces.services.query.IQueryProcessable"
        />

  </content>

  <!-- Register event listener for change mails -->
  <event:subscribe
      subscriber=".wikipage.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" />

</configure>


=== Added File Zope3/src/zope/app/wiki/diff.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.
#
##############################################################################
"""Browser View Components for WikiPages

$Id: diff.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
from difflib import ndiff

MAX_OLD_LINES_DISPLAY = 40
MAX_NEW_LINES_DISPLAY = 40


def textdiff(old_text, new_text, verbose=1):
    """
    generate a plain text diff, optimized for human readability,
    between two revisions of this page, numbering back from the latest.
    Alternately, a and/or b texts can be specified.
    """

    old = split(old_text, '\n')
    new = split(new_text, '\n')
    cruncher=ndiff.SequenceMatcher(
        isjunk=lambda x: x in " \\t",
        a=old,
        b=new)

    r = []
    for tag, old_lo, old_hi, new_lo, new_hi in cruncher.get_opcodes():
        if tag == 'replace':
            if verbose: r.append('??changed:')
            r = r + _abbreviateDiffLines(old[old_lo:old_hi],'-',
                                         MAX_OLD_LINES_DISPLAY)
            r = r + _abbreviateDiffLines(new[new_lo:new_hi],'',
                                         MAX_NEW_LINES_DISPLAY)
            r.append('')
        elif tag == 'delete':
            if verbose: r.append('--removed:')
            r = r + _abbreviateDiffLines(old[old_lo:old_hi],'-',
                                         MAX_OLD_LINES_DISPLAY)
            r.append('')
        elif tag == 'insert':
            if verbose: r.append('++added:')
            r = r + _abbreviateDiffLines(new[new_lo:new_hi],'',
                                         MAX_NEW_LINES_DISPLAY)
            r.append('')
        elif tag == 'equal':
            pass
        else:
            raise ValueError, 'unknown tag ' + `tag`

    return '\n' + join(r, '\n')


def _abbreviateDiffLines(lines, prefix, maxlines=5):
    output = []
    if maxlines and len(lines) > maxlines:
        extra = len(lines) - maxlines
        for i in xrange(maxlines - 1):
            output.append(prefix + lines[i])
        output.append(prefix + "[%d more line%s...]" %
                      (extra, ((extra == 1) and '') or 's')) # not working
    else:
        for line in lines:
            output.append(prefix + line)
    return output


=== Added File Zope3/src/zope/app/wiki/index.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.
#
##############################################################################
"""A custom Text Index for Wikis

$Id: index.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
from zope.app.index.text.index import TextIndex
from zope.app.wiki.interfaces import IWikiPage

class WikiTextIndex(TextIndex):

    def notify(wrapped_self, event):
        """An event occurred.  Index or unindex the object in response."""
        if IWikiPage.isImplementedBy(event.object):
            super(WikiTextIndex, wrapped_self).notify(event)
            


=== Added File Zope3/src/zope/app/wiki/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.
#
##############################################################################
"""ZWiki Interface Declarations

This module defines the ZWiki relevant interfaces.

$Id: interfaces.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
from zope.interface import Interface
from zope.schema import TextLine, List, SourceText
from zope.schema.vocabulary import VocabularyField

#from zope.app.interfaces.container import IContentContainer
from zope.schema import Field
from zope.app.interfaces.container import IContained
from zope.app.container.constraints import ContainerTypesConstraint
from zope.app.container.constraints import ItemTypePrecondition
from zope.app.interfaces.container import IContainer
from zope.app.i18n import ZopeMessageIDFactory as _ 

class IWikiPage(Interface):
    """A single Wiki Page content object.

    The Wiki page is a simple content object that stores the content
    (source) and the source type of the wiki page."""

    source = SourceText(
        title=_(u"Source Text"),
        description=_(u"Renderable source text of the Wiki Page."),
        default=u"",
        required=True)

    type = VocabularyField(
        title=_(u"Source Type"),
        description=_(u"Type of the source text, e.g. structured text"),
        default=u"reStructured Text (reST)",
        required = True,
        vocabulary = "SourceTypes")

    def append(source):
        """Append some text to the existing source text."""

    def comment(source, user):
        """Comment on the current Wiki; add comment to source."""

    def getCommentCounter():
        """Returns the amount of written comments for this wiki page."""
        

class IWikiPageHierarchy(Interface):
    """This interface supports the virtual hierarchical structure of the Wiki
    Pages."""

    parents = List(
        title = _(u"Wiki Page Parents"),
        description = _(u"Parents of a a Wiki"),
        value_type = TextLine(title=_(u"Parent Name"),
                              description=_(u"Name of the parent wiki page.")),
        required=False)

    def reparent(parents):
        """Reset the parents the Wiki page belongs to.

           The parents attribute is a list of unicode strings that contain the
           names of the parent wiki pages.
        """

    def path():
        """Return the object path of the virtual Wiki Hierarchy.

        The return value for this method should be a list of wiki objects
        describing the path.

        XXX: Wiki Pages can have several parents, so that we should be able to
        have multiple paths; but let's not worry about that right now. At some
        point this needs to be done though.
        """

    def findChildren(recursive=True):
        """Returns a list of children for this wiki page.

        If the recursive is True, the method recurses into all children
        returning the entire sub-tree of this Wiki Page. Is the recursive
        argument set to False, only the first level of children will be
        returned.
        """
    
class IWiki(IContainer):
    def __setitem__(name, object):
        """Add a poll"""
    __setitem__.precondition = ItemTypePrecondition(IWikiPage)


class IWikiContained(IContained):
    __parent__ = Field(
        constraint = ContainerTypesConstraint(IWiki))



class IMailSubscriptions(Interface):
    """This interface allows you to retrieve a list of E-mails for
    mailings. In our context """

    def getSubscriptions():
        """Return a list of E-mails."""

    def addSubscriptions(emails):
        """Add a bunch of subscriptions, but one would be okay as well."""

    def removeSubscriptions(emails):
        """Remove a set of subscriptions."""
        



=== Added File Zope3/src/zope/app/wiki/traversal.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.
##############################################################################
"""Specific HTTP

$Id: traversal.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
from zope.interface import implements
from zope.proxy import removeAllProxies
from zope.component import getDefaultViewName, queryView
from zope.publisher.interfaces import IPublishTraverse
from zope.exceptions import NotFoundError

from zope.app.traversing import getParent
from zope.app.traversing.namespace import UnexpectedParameters
from zope.app.interfaces.traversing import ITraversable
from zope.app.wiki.interfaces import IWikiPage

class WikiPageTraverser:

    implements(IPublishTraverse)
    __used_for__ = IWikiPage

    def __init__(self, page, request):
        self.context = page
        self.wiki = getParent(page)
        self.request = request

    def publishTraverse(self, request, name):
        subob = self.wiki.get(name, None)

        # XXX: Check that subobj has self.context as parent!
        if subob is None:

            view = queryView(self.context, name, request)
            if view is not None:
                return view

            raise NotFoundError(self.context, name, request)

        return removeAllProxies(subob)

    def browserDefault(self, request):
        c = self.context
        view_name = getDefaultViewName(c, request)
        view_uri = "@@%s" % view_name
        return c, (view_uri,)


_marker = object()

class WikiPageTraversable:
    """Traverses wikipages via wiki itself and getattr.
    """

    implements(ITraversable)
    __used_for__ = IWikiPage

    def __init__(self, page):
        self._page = page
        self._wiki = getParent(page)


    def traverse(self, name, parameters, original_name, furtherPath):
        if parameters:
            raise UnexpectedParameters(parameters)

        subobj = self._wiki.get(name, _marker)
        if subobj is _marker:
            subobj = getattr(self._page, name, _marker)
            if subobj is _marker:
                raise NotFoundError, original_name

        return subobj


=== Added File Zope3/src/zope/app/wiki/wiki.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.
#
##############################################################################
"""Wiki implementation

$Id: wiki.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
from zope.interface import implements
from zope.app.folder import Folder
from zope.app.wiki.interfaces import IWiki

class Wiki(Folder):
    __doc__ = IWiki.__doc__

    implements(IWiki)


=== Added File Zope3/src/zope/app/wiki/wikipage.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.
#
##############################################################################
"""Wiki implementation

$Id: wikipage.py,v 1.1 2004/02/27 11:06:58 philikon Exp $
"""
import smtplib
from persistent import Persistent

from zope.interface import implements
from zope.component import getAdapter
from zope.app.traversing import getParent, getName

from zope.app.interfaces.index.text import ISearchableText
from zope.app.interfaces.file import IReadFile, IWriteFile
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.wiki.interfaces import IWiki, IWikiPage
from zope.app.wiki.interfaces import IWikiPageHierarchy, IMailSubscriptions

__metaclass__ = type

HierarchyKey = 'http://www.zope.org/zwiki#1.0/PageHierarchy/parents'
SubscriberKey = 'http://www.zope.org/zwiki#1.0/MailSubscriptions/emails'


class WikiPage(Persistent):
    __doc__ = IWikiPage.__doc__

    implements(IWikiPage)

    # See zope.app.wiki.interfaces.IWikiPage
    source = u''
    
    # See zope.app.wiki.interfaces.IWikiPage
    type = u'reStructured Text (reST)'

    def __init__(self):
        self.__comments = 1

    def append(self, source):
        "See zope.app.wiki.interfaces.IWikiPage"
        self.source += source

    def comment(self, comment):
        "See zope.app.wiki.interfaces.IWikiPage"
        self.__comments += 1
        self.append(comment)

    def getCommentCounter(self):
        "See zope.app.wiki.interfaces.IWikiPage"
        return self.__comments
        

class WikiPageHierarchyAdapter:
    __doc__ = IWikiPageHierarchy.__doc__

    implements(IWikiPageHierarchy)
    __used_for__ = IWikiPage

    def __init__(self, context):
        self.context = context
        self._annotations = getAdapter(context, IAnnotations)
        if not self._annotations.get(HierarchyKey):
            self._annotations[HierarchyKey] = ()

    def reparent(self, parents):
        "See zope.app.wiki.interfaces.IWikiPageHierarchy"
        self.setParents(parents)

    def setParents(self, parents):
        self._annotations[HierarchyKey] = tuple(parents)

    def getParents(self):
        return self._annotations[HierarchyKey]

    parents = property(getParents, setParents)

    def path(self):
        "See zope.app.wiki.interfaces.IWikiPageHierarchy"
        # XXX: Allow for multpile parents
        if not self.getParents():
            return [self.context]
        wiki = getParent(self.context)
        name = self.getParents()[0]
        hier = getAdapter(wiki[name], IWikiPageHierarchy)
        return hier.path() + [self.context]

    def findChildren(self, recursive=True):
        "See zope.app.wiki.interfaces.IWikiPageHierarchy"
        wiki = getParent(self.context)
        contextName = getName(self.context)
        children = []
        for pageName in wiki:
            hier = getAdapter(wiki[pageName], IWikiPageHierarchy)
            if contextName in hier.getParents():
                if recursive:
                    subs = hier.findChildren()
                else:
                    subs = ()
                children.append((wiki[pageName], subs))
        return tuple(children)


# Adapters for file-system style access

class WikiPageReadFile:
    """Adapter for letting a Wiki Page look like a regular readable file."""

    implements(IReadFile)
    __used_for__ = IWikiPage

    def __init__(self, context):
        self.context = context

    def read(self):
        """See zope.app.interfaces.file.IReadFile"""
        return self.context.source

    def size(self):
        """See zope.app.interfaces.file.IReadFile"""
        return len(self.context.source)


class WikiPageWriteFile:
    """Adapter for letting a Wiki Page look like a regular writable file."""

    implements(IWriteFile)
    __used_for__ = IWikiPage
    
    def __init__(self, context):
        self.context = context

    def write(self, data):
        """See zope.app.interfaces.file.IWriteFile"""
        self.context.source = unicode(data)


# Adapter for ISearchableText

class SearchableText:
    """This adapter provides an API that allows the Wiki Pages to be indexed
    by the Text Index.""" 

    implements(ISearchableText)
    __used_for__ = IWikiPage

    def __init__(self, page):
        self.page = page

    def getSearchableText(self):
        return [unicode(self.page.source)]


# Component to fullfill mail subscriptions

class MailSubscriptions:
    """An adapter for WikiPages to provide an interface for collecting E-mails
    for sending out change notices."""

    implements(IMailSubscriptions)
    __used_for__ = IWikiPage, IWiki

    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 zope.app.wiki.interfaces.IMailSubscriptions"
        return self._annotations[SubscriberKey]
        
    def addSubscriptions(self, emails):
        "See zope.app.wiki.interfaces.IMailSubscriptions"
        subscribers = list(self._annotations[SubscriberKey])
        for email in emails:
            # XXX: Make sure these are actually E-mail addresses.
            if email not in subscribers:
                subscribers.append(email.strip())
        self._annotations[SubscriberKey] = tuple(subscribers)
                
    def removeSubscriptions(self, emails):
        "See zope.app.wiki.interfaces.IMailSubscriptions"
        subscribers = list(self._annotations[SubscriberKey])
        for email in emails:
            if email in subscribers:
                subscribers.remove(email)
        self._annotations[SubscriberKey] = tuple(subscribers)
                


class WikiMailer:
    """Class to handle all outgoing mail."""

    implements(ISubscriber)

    def __init__(self, host="localhost", port="25"):
        """Initialize the the object.""" 
        self.host = host
        self.port = port

    def notify(self, event):
        """See zope.app.interfaces.event.ISubscriber"""
        if IWikiPage.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: '+getName(object)
        emails = self.getAllSubscribers(object)
        body = object.source
        self.mail(emails, subject, body)        

    def handleModified(self, object):
        # XXX: Should have some nice diff code here.
        # from diff import textdiff
        subject = 'Modified: '+getName(object)
        emails = self.getAllSubscribers(object)
        body = object.source
        self.mail(emails, subject, body)

    def handleRemoved(self, object):
        subject = 'Removed: '+getName(object)
        emails = self.getAllSubscribers(object)
        body = subject
        self.mail(emails, subject, body)

    def getAllSubscribers(self, object):
        """Retrieves all email subscribers by looking into the local Wiki Page
           and into the Wiki for the global subscriptions."""
        emails = tuple(getAdapter(object,
                                  IMailSubscriptions).getSubscriptions())
        emails += tuple(getAdapter(getParent(object),
                                   IMailSubscriptions).getSubscriptions())
        return emails

    def mail(self, emails, subject, body):
        """Mail out the Wiki change message."""
        if not emails:
            return
        msg = 'Subject: %s\n\n\n%s' %(subject, body)
        server = smtplib.SMTP(self.host, self.port)
        server.set_debuglevel(0)
        server.sendmail('wiki at zope3.org', emails, msg)
        server.quit()

# Create a global mailer object.
mailer = WikiMailer()




More information about the Zope3-Checkins mailing list