[Zope3-checkins] CVS: Products3/demo/messageboard/step12 - __init__.py:1.1 configure.zcml:1.1 fields.py:1.1 fs.py:1.1 interfaces.py:1.1 message.py:1.1 messageboard.py:1.1 workflow.xml:1.1 xmlrpc.py:1.1 xmlrpc_client.py:1.1
Stephan Richter
srichter@cosmos.phy.tufts.edu
Mon, 21 Jul 2003 17:33:54 -0400
Update of /cvs-repository/Products3/demo/messageboard/step12
In directory cvs.zope.org:/tmp/cvs-serv1069
Added Files:
__init__.py configure.zcml fields.py fs.py interfaces.py
message.py messageboard.py workflow.xml xmlrpc.py
xmlrpc_client.py
Log Message:
Final step of the Message Board Demo product. This step corresponds to the
recipe: http://dev.zope.org/Zope3/DevelopingSkins
=== Added File Products3/demo/messageboard/step12/__init__.py ===
=== Added File Products3/demo/messageboard/step12/configure.zcml ===
<zopeConfigure
xmlns="http://namespaces.zope.org/zope"
xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
xmlns:event="http://namespaces.zope.org/event"
xmlns:mail="http://namespaces.zope.org/mail"
xmlns:translate="http://namespaces.zope.org/gts">
<!-- Security definitions -->
<role
id="zopeproducts.messageboard.User"
title="Message Board User"
description="Users that actually use the Message Board."/>
<role
id="zopeproducts.messageboard.Editor"
title="Message Board Editor"
description="The Editor can edit and delete Messages."/>
<permission
id="zopeproducts.messageboard.View"
title="View Message Board and Messages"
description="View the Message Board and all its content."/>
<grant
permission="zopeproducts.messageboard.View"
role="zopeproducts.messageboard.User"/>
<permission
id="zopeproducts.messageboard.Add"
title="Add Message"
description="Add Message."/>
<grant
permission="zopeproducts.messageboard.Add"
role="zopeproducts.messageboard.User"/>
<permission
id="zopeproducts.messageboard.Edit"
title="Edit Messages"
description="Edit Messages."/>
<grant
permission="zopeproducts.messageboard.Edit"
role="zopeproducts.messageboard.Editor"/>
<permission
id="zopeproducts.messageboard.Delete"
title="Delete Message"
description="Delete Message."/>
<grant
permission="zopeproducts.messageboard.Delete"
role="zopeproducts.messageboard.Editor"/>
<permission
id="zopeproducts.messageboard.PublishContent"
title="Publish Message"
description="Publish Message."/>
<grant
permission="zopeproducts.messageboard.PublishContent"
role="zopeproducts.messageboard.Editor"/>
<!-- Content declarations -->
<content class=".messageboard.MessageBoard">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
<implements
interface="zope.app.interfaces.container.IContentContainer" />
<factory
id="MessageBoard"
permission="zope.ManageContent"
description="Message Board" />
<require
permission="zopeproducts.messageboard.View"
interface=".interfaces.IMessageBoard"/>
<require
permission="zopeproducts.messageboard.Edit"
set_schema=".interfaces.IMessageBoard"/>
</content>
<adapter
factory=".messageboard.PlainText"
provides=".interfaces.IPlainText"
for=".interfaces.IMessageBoard" />
<content class=".message.Message">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
<implements
interface="zope.app.interfaces.container.IContentContainer" />
<implements interface=
"zope.app.interfaces.workflow.IProcessInstanceContainerAdaptable" />
<factory
id="Message"
permission="zopeproducts.messageboard.Add"
description="Message" />
<require
permission="zopeproducts.messageboard.View"
interface=".interfaces.IMessage"/>
<require
permission="zopeproducts.messageboard.Edit"
set_schema=".interfaces.IMessage"/>
</content>
<adapter
factory=".message.MessageSized"
provides="zope.app.interfaces.size.ISized"
for=".interfaces.IMessage" />
<adapter
factory=".message.PlainText"
provides=".interfaces.IPlainText"
for=".interfaces.IMessage" />
<!-- File System Representation -->
<adapter
for=".interfaces.IMessageBoard"
provides="zope.app.interfaces.file.IReadDirectory"
factory=".fs.ReadDirectory"
permission="zope.View"/>
<adapter
for=".interfaces.IMessageBoard"
provides="zope.app.interfaces.file.IDirectoryFactory"
factory=".fs.MessageFactory"
permission="zope.View"/>
<adapter
for=".interfaces.IMessage"
provides="zope.app.interfaces.file.IReadDirectory"
factory=".fs.ReadDirectory"
permission="zope.View"/>
<adapter
for=".interfaces.IMessage"
provides="zope.app.interfaces.file.IDirectoryFactory"
factory=".fs.MessageFactory"
permission="zope.View"/>
<content class=".fs.VirtualContentsFile">
<implements
interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
<require
permission="zope.View"
interface="zope.app.interfaces.content.file.IReadFile" />
<require
permission="zope.ManageContent"
interface="zope.app.interfaces.content.file.IWriteFile"
set_schema="zope.app.interfaces.content.file.IReadFile" />
</content>
<!-- XML-RPC presentation -->
<xmlrpc:view
name="methods"
for=".interfaces.IMessageBoard"
permission="zopeproducts.messageboard.Edit"
allowed_methods="getMessageNames addMessage deleteMessage
getDescription setDescription"
factory=".xmlrpc.MessageBoardMethods" />
<xmlrpc:view
name="methods"
for=".interfaces.IMessage"
permission="zopeproducts.messageboard.Edit"
allowed_methods="getMessageNames addMessage deleteMessage
getTitle setTitle getBody setBody"
factory=".xmlrpc.MessageMethods" />
<!-- Mail Subscriptions support -->
<adapter
factory=".message.MailSubscriptions"
provides=".interfaces.IMailSubscriptions"
for=".interfaces.IMessage" />
<!-- Register Mailer and Mail Service -->
<mail:smtpMailer id="msgboard-smtp" hostname="localhost" port="25" />
<mail:queuedService permission="zope.SendMail"
queuePath="./src/zopeproducts/messageboard/mail-queue"
mailer="msgboard-smtp" />
<!-- Register event listener for change mails -->
<event:subscribe
subscriber=".message.mailer"
event_types="zope.app.interfaces.event.IObjectAddedEvent
zope.app.interfaces.event.IObjectModifiedEvent
zope.app.interfaces.event.IObjectRemovedEvent
zope.app.interfaces.event.IObjectMovedEvent" />
<include package=".browser" />
<translate:registerTranslations directory="locales" />
</zopeConfigure>
=== Added File Products3/demo/messageboard/step12/fields.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.
#
##############################################################################
"""Module containing custom field definitions.
$Id: fields.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
import re
from zope.schema.interfaces import ValidationError
from zope.schema import Text
from zope.i18n import MessageIDFactory
_ = MessageIDFactory('messageboard')
ForbiddenTags = _('Forbidden HTML Tags used.')
forbidden_regex = r'</?(?:%s).*?/?>'
allowed_regex = r'</??(?!%s)[a-zA-Z0-9]*? ?(?:[a-z0-9]*?=?".*?")*/??>'
class HTML(Text):
allowed_tags = ()
forbidden_tags = ()
def __init__(self, allowed_tags=(), forbidden_tags=(), **kw):
self.allowed_tags = allowed_tags
self.forbidden_tags = forbidden_tags
super(HTML, self).__init__(**kw)
def _validate(self, value):
super(HTML, self)._validate(value)
if self.forbidden_tags:
regex = forbidden_regex %'|'.join(self.forbidden_tags)
if re.findall(regex, value):
raise ValidationError(
ForbiddenTags, value, self.forbidden_tags)
if self.allowed_tags:
regex = allowed_regex %'|'.join(self.allowed_tags)
if re.findall(regex, value):
raise ValidationError(
ForbiddenTags, value, self.allowed_tags)
=== Added File Products3/demo/messageboard/step12/fs.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.
#
##############################################################################
"""FTP Views for the MessageBoard and Message component
$Id: fs.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
from zope.component import getAdapter
from zope.interface import implements
from zope.app.interfaces.file import IReadDirectory, IDirectoryFactory
from zope.app.interfaces.index.text import ISearchableText
from zope.app.content.folder import ReadDirectory as ReadDirectoryBase
from zope.app.content.file import File
from zope.app.context import ContextWrapper
from interfaces import IVirtualContentsFile, IPlainText
from message import Message
class VirtualContentsFile(object):
implements(IVirtualContentsFile)
def __init__(self, context):
self.context = context
def setContentType(self, contentType):
'''See interface IFile'''
pass
def getContentType(self):
'''See interface IFile'''
return 'text/plain'
contentType = property(getContentType, setContentType)
def edit(self, data, contentType=None):
'''See interface IFile'''
self.setData(data)
def getData(self):
'''See interface IFile'''
adapter = getAdapter(self.context, IPlainText)
return adapter.getText()
def setData(self, data):
'''See interface IFile'''
adapter = getAdapter(self.context, IPlainText)
return adapter.setText(data)
data = property(getData, setData)
def getSize(self):
'''See interface IFile'''
return len(self.getData())
size = property(getSize)
class ReadDirectory(ReadDirectoryBase):
"""An special implementation of the directory."""
implements(IReadDirectory)
def keys(self):
keys = self.context.keys()
return list(keys) + ['contents']
def get(self, key, default=None):
if key == 'contents':
return VirtualContentsFile(self.context)
return self.context.get(key, default)
def __len__(self):
l = len(self.context)
return l+1
class MessageFactory(object):
"""A simple message factory for file system representations."""
implements(IDirectoryFactory)
def __init__(self, context):
self.context = context
def __call__(self, name):
"""See IDirectoryFactory interface."""
return Message()
=== Added File Products3/demo/messageboard/step12/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.
#
##############################################################################
"""Message Board Interfaces
Interfaces for the Zope 3 based Message Board Product
$Id: interfaces.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
from zope.app.interfaces.container import IContainer
from zope.app.interfaces.content.file import IFile, IFileContent
from zope.schema.interfaces import IText
from zope.interface import Interface
from zope.interface import classImplements
from zope.schema import Text, TextLine, Container
from fields import HTML
from zope.i18n import MessageIDFactory
_ = MessageIDFactory('messageboard')
class IMessageBoard(IContainer):
"""The message board is the base object for our product. It can only
contain IMessage objects."""
description = Text(
title=_("Description"),
description=_("A detailed description of the content of the board."),
default=u"",
required=False)
class IMessage(IContainer):
"""A message object. It can contain its own responses."""
title = TextLine(
title=_("Title/Subject"),
description=_("Title and/or subject of the message."),
default=u"",
required=True)
body = HTML(
title=_("Message Body"),
description=_("This is the actual message. Type whatever!"),
default=u"",
allowed_tags=('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img', 'a', 'br',
'b', 'i', 'u', 'em', 'sub', 'sup', 'table', 'tr', 'td',
'th', 'code', 'pre', 'center', 'div', 'span', 'p',
'font', 'ol', 'ul', 'li', 'q', 's', 'strong'),
required=False)
class IVirtualContentsFile(IFile, IFileContent):
"""Marker Interface to mark special Message and Message Board Contents
files in FS representations.
This interface is most likely to be implemented as an adapter to the
'contents' file's container, so that the data can be directly stored where
it belongs.
"""
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; one would be okay too."""
def removeSubscriptions(emails):
"""Remove a set of subscriptions."""
class IPlainText(Interface):
"""This interface allows you to represent an object's content in plain
text.
It is used to create the contents for the IVirtualContentsFile."""
def getText():
"""Get a pure text representation of the object's content."""
def setText(text):
"""Write the text to the object.
This method might have some logic to read various attributes from the
text. For example, if a text starts with 'Title: ', then the method
could use the following text to extract the title for the object and
so on.
"""
class IHTML(IText):
"""A text field that is geared towards handeling HTML input."""
allowed_tags = Container(
title=_("Allowed HTML Tags"),
description=_("""\
Listed tags can be used in the value of the field.
"""),
required=False)
forbidden_tags = Container(
title=_("Forbidden HTML Tags"),
description=_("""\
Listed tags cannot be used in the value of the field.
"""),
required=False)
# To avoid recursive imports:
classImplements(HTML, IHTML)
=== Added File Products3/demo/messageboard/step12/message.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.
#
##############################################################################
"""Message Implementation
An implementation of the Message using Folders as base.
$Id: message.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
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.interfaces.size import ISized
from zopeproducts.messageboard.interfaces import IMessage, IPlainText
from zopeproducts.messageboard.interfaces import IMailSubscriptions
from zope.interface import implements
from zope.component import getAdapter, getService
from zope.app.container.btree import BTreeContainer
from zope.app.traversing import getParent, getName
from zope.i18n import MessageIDFactory
_ = MessageIDFactory('messageboard')
SubscriberKey = 'http://www.zope.org/messageboard#1.0/MailSubscriptions/emails'
class Message(BTreeContainer):
__doc__ = IMessage.__doc__
implements(IMessage)
_title = u''
_body = u''
def getTitle(self):
"""Get the title of the board."""
return self._title
def setTitle(self, title):
"""Set the title of the board."""
self._title = title
# See zopeproducts.messageboard.interfaces.IMessage
title = property(getTitle, setTitle)
def getBody(self):
"""Get the body of the board."""
return self._body
def setBody(self, body):
"""Set the body of the board."""
self._body = body
# See zopeproducts.messageboard.interfaces.IMessage
body = property(getBody, setBody)
class MessageSized:
implements(ISized)
def __init__(self, message):
self._message = message
def sizeForSorting(self):
'See ISized'
return ('item', len(self._message))
def sizeForDisplay(self):
'See ISized'
messages = 0
for obj in self._message.values():
if IMessage.isImplementedBy(obj):
messages += 1
attach = len(self._message)-messages
if messages == 1: size = '1 reply'
else: size = '${messages} replies'
if attach == 1: size += ', 1 attachment'
else: size += ', ${attach} attachments'
size = _(size)
size.mapping = {'messages': `messages`, 'attach': `attach`}
size.default = unicode(size)
return size
class PlainText:
implements(IPlainText)
def __init__(self, context):
self.context = context
def getText(self):
return 'Title: %s\n\n%s' %(self.context.title, self.context.body)
def setText(self, text):
if text.startswith('Title: '):
title, text = text.split('\n', 1)
self.context.title = title[7:]
self.context.body = text.strip()
class MailSubscriptions:
"""An adapter for IMessage to provide an interface for collecting E-mails
for sending out change notices."""
implements(IMailSubscriptions)
__used_for__ = IMessage
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 MessageMailer:
"""Class to handle all outgoing mail."""
implements(ISubscriber)
def notify(self, event):
"""See zope.app.interfaces.event.ISubscriber"""
if IMessage.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.body
self.mail(emails, subject, body)
def handleModified(self, object):
subject = 'Modified: '+getName(object)
emails = self.getAllSubscribers(object)
body = object.body
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 for this message and all above."""
emails = ()
msg = object
while IMessage.isImplementedBy(msg):
emails += tuple(getAdapter(
msg, IMailSubscriptions).getSubscriptions())
msg = getParent(msg)
return emails
def mail(self, toaddrs, subject, body):
"""Mail out the Wiki change message."""
if not toaddrs:
return
msg = 'Subject: %s\n\n\n%s' %(subject, body)
mail_service = getService(None, 'Mail')
mail_service.send('mailer@messageboard.org' , toaddrs, msg)
# Create a global mailer object.
mailer = MessageMailer()
=== Added File Products3/demo/messageboard/step12/messageboard.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.
#
##############################################################################
"""Message Board Implementation
An implementation of the Message Board using Folders as base.
$Id: messageboard.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
from zope.interface import implements
from zope.app.container.btree import BTreeContainer
from zopeproducts.messageboard.interfaces import IMessageBoard, IPlainText
class MessageBoard(BTreeContainer):
__doc__ = IMessageBoard.__doc__
implements(IMessageBoard)
_desc = u''
def getDescription(self):
"""Get the description of the board."""
return self._desc
def setDescription(self, desc):
"""Set the description of the board."""
self._desc = desc
# See zopeproducts.messageboard.interfaces.IMessageBoard
description = property(getDescription, setDescription)
class PlainText:
implements(IPlainText)
def __init__(self, context):
self.context = context
def getText(self):
return self.context.description
def setText(self, text):
self.context.description = unicode(text)
=== Added File Products3/demo/messageboard/step12/workflow.xml ===
<?xml version="1.0"?>
<workflow type="StatefulWorkflow" title="Message Publication Review">
<schema name=""/>
<states>
<state name="INITIAL" title="initial" />
<state name="private" title="Private" />
<state name="pending" title="Pending Publication" />
<state name="published" title="Public" />
</states>
<transitions>
<transition
sourceState="published"
destinationState="private"
name="published_private"
title="Unpublish Message"
permission="zopeproducts.messageboard.PublishContent"
triggerMode="Manual" />
<transition
sourceState="private"
destinationState="pending"
name="private_pending"
title="Submit Message"
permission="zopeproducts.messageboard.Edit"
triggerMode="Manual" />
<transition
sourceState="INITIAL"
destinationState="private"
name="initial_private"
title="Make Private"
triggerMode="Automatic" />
<transition
sourceState="pending"
destinationState="published"
name="pending_published"
title="Publish Message"
permission="zopeproducts.messageboard.PublishContent"
triggerMode="Manual" />
<transition
sourceState="pending"
destinationState="private"
name="pending_private"
title="Retract Message"
permission="zopeproducts.messageboard.Edit"
triggerMode="Manual" />
<transition
sourceState="pending"
destinationState="private"
name="pending_private_reject"
title="Reject Message"
permission="zopeproducts.messageboard.PublishContent"
triggerMode="Manual" />
</transitions>
</workflow>
=== Added File Products3/demo/messageboard/step12/xmlrpc.py ===
##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
"""XML-RPC Representations
$Id: xmlrpc.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
from zope.publisher.xmlrpc import XMLRPCView
from zope.app.event import publish
from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent
from zopeproducts.messageboard.message import Message
class MessageContainerMethods(XMLRPCView):
def getMessageNames(self):
"""Get a list of all messages."""
return list(self.context.keys())
def addMessage(self, name, title, body):
"""Add a message."""
msg = Message()
msg.title = title
msg.body = body
publish(self.context, ObjectCreatedEvent(msg))
return self.context.setObject(name, msg)
def deleteMessage(self, name):
"""Delete a message. Return True, if successful."""
self.context.__delitem__(name)
return True
class MessageMethods(MessageContainerMethods):
def getTitle(self):
return self.context.title
def setTitle(self, title):
self.context.title = title
publish(self.context, ObjectModifiedEvent(self.context))
return True
def getBody(self):
return self.context.body
def setBody(self, body):
self.context.body = body
publish(self.context, ObjectModifiedEvent(self.context))
return True
class MessageBoardMethods(MessageContainerMethods):
def getDescription(self):
return self.context.description
def setDescription(self, description):
self.context.description = description
publish(self.context, ObjectModifiedEvent(self.context))
return True
=== Added File Products3/demo/messageboard/step12/xmlrpc_client.py ===
#!/usr/bin/env python2.3
##############################################################################
#
# Copyright (c) 2001, 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.
#
##############################################################################
"""XML-RPC Client
$Id: xmlrpc_client.py,v 1.1 2003/07/21 21:33:45 srichter Exp $
"""
import httplib
import xmlrpclib
from code import InteractiveConsole
from base64 import encodestring
class BasicAuthTransport(xmlrpclib.Transport):
def __init__(self, username=None, password=None, verbose=0):
self.username=username
self.password=password
self.verbose=verbose
def request(self, host, handler, request_body, verbose=0):
# issue XML-RPC request
self.verbose = verbose
h = httplib.HTTP(host)
h.putrequest("POST", handler)
# required by HTTP/1.1
h.putheader("Host", host)
# required by XML-RPC
h.putheader("User-Agent", self.user_agent)
h.putheader("Content-Type", "text/xml")
h.putheader("Content-Length", str(len(request_body)))
# basic auth
if self.username is not None and self.password is not None:
h.putheader("AUTHORIZATION", "Basic %s" %
encodestring("%s:%s" % (self.username, self.password)
).replace("\012", ""))
h.endheaders()
if request_body:
h.send(request_body)
errcode, errmsg, headers = h.getreply()
if errcode != 200:
raise xmlrpclib.ProtocolError(host + handler,
errcode, errmsg, headers)
return self.parse_response(h.getfile())
if __name__ == '__main__':
url = raw_input('Message Board URL [http://localhost:8080/board/]: ')
if url == '':
url = 'http://localhost:8080/board/'
username = raw_input('Username: ')
password = raw_input('Password: ')
board = xmlrpclib.Server(
url, transport = BasicAuthTransport(username, password))
print "The message board is available as 'board'"
console = InteractiveConsole(locals={'board': board})
console.interact()