[Zope3-checkins] CVS: Products3/demo/messageboard/step9/browser - __init__.py:1.1 add.pt:1.1 configure.zcml:1.1 details.pt:1.1 message.png:1.1 message.py:1.1 messageboard.png:1.1 messageboard.py:1.1 messageboard_add.pt:1.1 review.pt:1.1 subscriptions.pt:1.1 subthread.pt:1.1 thread.pt:1.1 thread.py:1.1 widgets.py:1.1
Stephan Richter
srichter@cosmos.phy.tufts.edu
Tue, 15 Jul 2003 17:43:50 -0400
Update of /cvs-repository/Products3/demo/messageboard/step9/browser
In directory cvs.zope.org:/tmp/cvs-serv2533/browser
Added Files:
__init__.py add.pt configure.zcml details.pt message.png
message.py messageboard.png messageboard.py
messageboard_add.pt review.pt subscriptions.pt subthread.pt
thread.pt thread.py widgets.py
Log Message:
Step 9: Online Help completed.
=== Added File Products3/demo/messageboard/step9/browser/__init__.py ===
=== Added File Products3/demo/messageboard/step9/browser/add.pt ===
<html metal:use-macro="views/standard_macros/dialog">
<body>
<div metal:fill-slot="body">
<form action="action.html" method="POST" i18n:domain="messageboard">
<table class="TypeListing" cellpadding="3">
<caption i18n:translate="">Add Content</caption>
<tbody tal:repeat="info view/addingInfo">
<tr>
<td class="Selector">
<input type="radio" name="type_name"
tal:attributes="value info/action; id info/action" />
</td>
<td class="TypeName">
<label style="font-weight: bold;"
tal:attributes="for info/action">
<span tal:replace="info/title" >Folder</span>
</label>
<div class="TypeDescription" tal:content="info/description">
Folders are generic containers for content, including other
folders.
</div>
</td>
</tr>
</tbody>
<tbody tal:condition="nothing">
<tr>
<td class="Selector">
<input type="radio" name="type_name" value="" />
</td>
<td class="TypeName">
<img alt="Folder" src="../../ZMI/www/document_icon.gif" />
Document
</td>
</tr>
<tr>
<td class="Selector"><br /></td>
<td class="TypeDescription">
Documents are simple textual content.
</td>
</tr>
</tbody>
<tr>
<td><br/></td>
<td>
<input type="text" name="id"
tal:condition="view/namesAccepted"
tal:attributes="value request/id | nothing"
/>
<input type="submit" value=" Add "
i18n:attributes="value add-button"/>
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/configure.zcml ===
<zopeConfigure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:help="http://namespaces.zope.org/help">
<help:register
id="messageboard"
title="Message Board Help"
parent="ui"
doc_path="./help/product_intro.txt"/>
<!-- IMessageBoard Views -->
<browser:menu
id="add_messageboard"
title="Menu of objects to be added to Message Boards."/>
<browser:view
name="+"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
class=".messageboard.MessageBoardAdding"
permission="zopeproducts.messageboard.Add"
allowed_attributes="addingInfo"
menu="zmi_actions"
title="Add">
<browser:page name="index.html" template="add.pt" />
<browser:page name="action.html" attribute="action" />
</browser:view>
<browser:addform
label="Add Message Board"
name="AddMessageBoard"
schema="zopeproducts.messageboard.interfaces.IMessageBoard"
content_factory="zopeproducts.messageboard.messageboard.MessageBoard"
permission="zope.ManageContent"
class=".messageboard.AddMessageBoard"
template="messageboard_add.pt"
menu="add_content" title="Message Board"/>
<browser:editform
schema="zopeproducts.messageboard.interfaces.IMessageBoard"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
label="Change Message Board"
name="edit.html"
permission="zopeproducts.messageboard.Edit"
menu="zmi_views" title="Edit" />
<help:register
id="board.edit"
title="Change Message Board"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
view="edit.html"
doc_path="./help/board_edit.txt"/>
<browser:page
name="thread.html"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
class=".thread.Thread"
template="thread.pt"
permission="zopeproducts.messageboard.View"
menu="zmi_views" title="Thread"/>
<help:register
id="board.thread"
title="Discussion Thread"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
view="thread.html"
doc_path="./help/board_thread.txt"/>
<browser:page
name="contents.html"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
permission="zope.ManageContent"
class="zope.app.browser.container.contents.Contents"
attribute="contents"
menu="zmi_views" title="Contents"/>
<browser:page
name="review.html"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
class=".messageboard.ReviewMessages"
permission="zopeproducts.messageboard.PublishContent"
template="review.pt"
menu="zmi_views" title="Review Messages"/>
<help:register
id="board.review"
title="Publication Review"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
view="review.html"
doc_path="./help/board_review.txt"/>
<browser:defaultView
for="zopeproducts.messageboard.interfaces.IMessageBoard"
name="thread.html"/>
<browser:icon
name="zmi_icon"
for="zopeproducts.messageboard.interfaces.IMessageBoard"
file="messageboard.png" />
<!-- IMessage Views -->
<browser:menu
id="add_message"
title="Menu of objects to be added to Messages."/>
<browser:view
name="+"
for="zopeproducts.messageboard.interfaces.IMessage"
class=".message.MessageAdding"
permission="zopeproducts.messageboard.Add"
allowed_attributes="addingInfo"
menu="zmi_actions" title="Add">
<browser:page name="index.html" template="add.pt" />
<browser:page name="action.html" attribute="action" />
</browser:view>
<browser:addform
label="Add Message"
name="AddMessage"
schema="zopeproducts.messageboard.interfaces.IMessage"
content_factory="zopeproducts.messageboard.message.Message"
permission="zopeproducts.messageboard.Add"
menu="add_messageboard" title="Message"/>
<browser:editform
schema="zopeproducts.messageboard.interfaces.IMessage"
for="zopeproducts.messageboard.interfaces.IMessage"
label="Change Message"
name="edit.html"
permission="zopeproducts.messageboard.Edit"
menu="zmi_views" title="Edit" />
<help:register
id="message.edit"
title="Change Message"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessage"
view="edit.html"
doc_path="./help/msg_edit.txt"/>
<browser:page
name="details.html"
for="zopeproducts.messageboard.interfaces.IMessage"
class=".message.MessageDetails"
template="details.pt"
permission="zopeproducts.messageboard.View"
menu="zmi_views" title="Preview"/>
<help:register
id="message.details"
title="Message Details"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessage"
view="details.html"
doc_path="./help/msg_preview.txt"/>
<browser:page
name="thread.html"
for="zopeproducts.messageboard.interfaces.IMessage"
class=".thread.Thread"
template="thread.pt"
permission="zopeproducts.messageboard.View"
menu="zmi_views" title="Thread"/>
<help:register
id="message.thread"
title="Discussion Thread"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessage"
view="thread.html"
doc_path="./help/msg_thread.txt"/>
<browser:pages
for="zopeproducts.messageboard.interfaces.IMessage"
class=".message.MailSubscriptions"
permission="zopeproducts.messageboard.Edit">
<browser:page name="subscriptions.html" template="subscriptions.pt"
menu="zmi_views" title="Subscriptions" />
<browser:page name="changeSubscriptions.html" attribute="change" />
</browser:pages>
<help:register
id="message.subscriptions"
title="Message Mail Subscriptions"
parent="ui/messageboard"
for="zopeproducts.messageboard.interfaces.IMessage"
view="subscriptions.html"
doc_path="./help/msg_subscriptions.txt"/>
<browser:menuItem
menu="add_message"
for="zope.app.interfaces.container.IAdding"
action="AddMessage"
title="Message"/>
<browser:menuItem
menu="add_message"
for="zope.app.interfaces.container.IAdding"
action="Image"
title="Image" />
<browser:menuItem
menu="add_message"
for="zope.app.interfaces.container.IAdding"
action="File"
title="File" />
<browser:page
name="contents.html"
for="zopeproducts.messageboard.interfaces.IMessage"
permission="zope.ManageContent"
class="zope.app.browser.container.contents.Contents"
attribute="contents"
menu="zmi_views" title="Contents"/>
<browser:defaultView
for="zopeproducts.messageboard.interfaces.IMessage"
name="details.html"/>
<browser:icon
name="zmi_icon"
for="zopeproducts.messageboard.interfaces.IMessage"
file="message.png" />
<!-- Custom Widgets -->
<browser:page
permission="zope.Public"
allowed_interface="zope.app.interfaces.browser.form.IBrowserWidget"
for="zopeproducts.messageboard.interfaces.IHTML"
name="edit"
class=".widgets.HTMLSourceWidget" />
</zopeConfigure>
=== Added File Products3/demo/messageboard/step9/browser/details.pt ===
<html metal:use-macro="views/standard_macros/page">
<body>
<div metal:fill-slot="body" i18n:domain="messageboard">
<h1 i18n:translate="">Message Details</h1>
<div class="row">
<div class="label" i18n:translate="">Title</div>
<div class="field" tal:content="context/title" />
</div>
<div class="row">
<div class="label" i18n:translate="">Author</div>
<div class="field" tal:content="view/author"/>
</div>
<div class="row">
<div class="label" i18n:translate="">Date/Time</div>
<div class="field" tal:content="view/modified"/>
</div>
<div class="row">
<div class="label" i18n:translate="">Parent</div>
<div class="field" tal:define="info view/parent_info">
<a href="../"
tal:condition="info"
tal:content="info/title" />
</div>
</div>
<div class="row">
<div class="label" i18n:translate="">Body</div>
<div class="field" tal:content="structure context/body"/>
</div>
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/message.png ===
<Binary-ish file>
=== Added File Products3/demo/messageboard/step9/browser/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.
#
##############################################################################
"""Browser Views for IMessage
$Id: message.py,v 1.1 2003/07/15 21:43:36 srichter Exp $
"""
from zope.app.interfaces.dublincore import ICMFDublinCore
from zopeproducts.messageboard.interfaces import IMessage, IMailSubscriptions
from zope.component import getAdapter
from zope.app.traversing import getParent, getName
from zope.app.browser.container.adding import Adding
class MessageDetails:
def author(self):
"""Get user who last modified the Wiki Page."""
creators = getAdapter(self.context, ICMFDublinCore).creators
if not creators:
return 'unknown'
return creators[0]
def modified(self):
"""Get last modification date."""
date = getAdapter(self.context, ICMFDublinCore).modified
if date is None:
date = getAdapter(self.context, ICMFDublinCore).created
if date is None:
return ''
formatter = self.request.locale.getDateTimeFormatter('medium')
return formatter.format(date)
def parent_info(self):
"""Get the parent of the message"""
parent = getParent(self.context)
if not IMessage.isImplementedBy(parent):
return None
return {'name': getName(parent), 'title': parent.title}
class MailSubscriptions:
def subscriptions(self):
return getAdapter(self.context, IMailSubscriptions).getSubscriptions()
def change(self):
if 'ADD' in self.request:
emails = self.request['emails'].split('\n')
getAdapter(self.context,
IMailSubscriptions).addSubscriptions(emails)
elif 'REMOVE' in self.request:
emails = self.request['remails']
print emails
if isinstance(emails, (str, unicode)):
emails = [emails]
getAdapter(self.context,
IMailSubscriptions).removeSubscriptions(emails)
self.request.response.redirect('.')
class MessageAdding(Adding):
"""Custom adding view for Message objects."""
menu_id = "add_message"
=== Added File Products3/demo/messageboard/step9/browser/messageboard.png ===
<Binary-ish file>
=== Added File Products3/demo/messageboard/step9/browser/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.
#
##############################################################################
"""Browser Views for IMessageBoard
$Id: messageboard.py,v 1.1 2003/07/15 21:43:36 srichter Exp $
"""
import os
from zope.app.interfaces.services.registration import ActiveStatus
from zope.app.interfaces.workflow import IProcessInstanceContainer
from zope.app.interfaces.workflow import IProcessDefinitionImportExport
from zopeproducts import messageboard as mb_package
from zopeproducts.messageboard.interfaces import IMessage
from zope.app.browser.container.adding import Adding
from zope.app.context import ContextWrapper
from zope.app.services.service import ServiceManager, ServiceRegistration
from zope.app.services.servicenames import Workflows
from zope.app.traversing import getPath, getParent, traverse
from zope.app.workflow.service import WorkflowService
from zope.app.workflow.stateful.contentworkflow import \
ContentWorkflowsUtility
from zope.app.workflow.service import ProcessDefinitionRegistration
from zope.app.workflow.stateful.definition import \
StatefulProcessDefinition
from zope.component import getAdapter, queryAdapter, getView, getUtility
class MessageBoardAdding(Adding):
"""Custom adding view for MessageBoard objects."""
menu_id = "add_messageboard"
class AddMessageBoard(object):
"""Add a message board."""
def createAndAdd(self, data):
content = super(AddMessageBoard, self).createAndAdd(data)
if self.request.get('workflow'):
site = getParent(content)
if not site.hasServiceManager():
site.setServiceManager(ServiceManager())
sm = traverse(site, '++etc++site')
default = traverse(sm, 'default')
rm = default.getRegistrationManager()
# Create Workflow Serivce
default.setObject('MB-Workflows', WorkflowService())
workflowservice = traverse(default, 'MB-Workflows')
wf_path = "%s/%s" % (getPath(default), 'MB-Workflows')
registration = ServiceRegistration(Workflows, wf_path, site)
wf_id = rm.setObject('', registration)
rm[wf_id].status = ActiveStatus
# Create Content Workflows Utility
default.setObject('MB-WFContent', ContentWorkflowsUtility())
default['MB-WFContent'].subscribe()
# Create Default Processdefinition
default.setObject('MB-WFProcesses',
StatefulProcessDefinition())
pd_path = getPath(traverse(default, 'MB-WFProcesses'))
registration = ProcessDefinitionRegistration(
'default', pd_path)
pd_id = rm.setObject('', registration)
traverse(rm, pd_id).status = ActiveStatus
# Import Default ProcessReview Workflow
import_util = getUtility(
default, IProcessDefinitionImportExport)
xml = os.path.join(
os.path.dirname(mb_package.__file__), 'workflow.xml')
import_util.importProcessDefinition(
default['MB-WFProcesses'], open(xml, mode='r'))
return content
class ReviewMessages:
"""Workflow: Review all pending messages"""
def getPendingMessages(self, wrapped_pmsg):
"""Get all pending messages recursively."""
msgs = []
for name, msg in wrapped_pmsg.items():
if IMessage.isImplementedBy(msg):
wrapped_msg = ContextWrapper(msg, wrapped_pmsg, name=name)
if hasMessageStatus(msg, 'pending'):
msgs.append(ContextWrapper(msg, wrapped_msg, name=name))
msgs += self.getPendingMessages(wrapped_msg)
return msgs
def getPendingMessagesInfo(self):
"""Get all the display info for pending messages"""
msg_infos = []
for msg in self.getPendingMessages(self.context):
info = {}
info['title'] = msg.title
info['url'] = getView(
msg, 'absolute_url', self.request)() + '/@@workflows.html'
msg_infos.append(info)
return msg_infos
def hasMessageStatus(msg, status, workflow='default'):
"""Check whether a particular message matches a given status"""
adapter = queryAdapter(msg, IProcessInstanceContainer)
if adapter:
# No workflow is defined; so all messages are shown.
if not adapter.keys():
return True
for item in adapter.values():
if item.processDefinitionName != workflow:
continue
if item.status == status:
return True
return False
=== Added File Products3/demo/messageboard/step9/browser/messageboard_add.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<style metal:fill-slot="style_slot">
</style>
</head>
<body>
<div metal:fill-slot="body" i18n:domain="messageboard">
<p tal:define="status view/update"
tal:condition="status"
tal:content="status" />
<form action="." tal:attributes="action request/URL" method="post">
<div class="row">
<div class="field">
<h1><input type="checkbox" name="workflow:int" value="1"
checked=""/>
<span i18n:translate="">Create Workflow</span>
</h1>
<span i18n:translate=''>Without the workflow you will not be
able to review messages before they are published. Note that
you can always modify the messageboard workflow later to make
all transitions automatically.</span>
</div>
</div>
<div class="row">
<div class="controls">
<input type="submit" value="Refresh"
i18n:attributes="value refresh-button" />
<input type="submit" name="UPDATE_SUBMIT" value="Submit"
i18n:attributes="value submit-button" />
</div>
</div>
</form>
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/review.pt ===
<html metal:use-macro="views/standard_macros/page">
<body>
<div metal:fill-slot="body" i18n:domain="messageboard">
<h1 i18n:translate="">Pending Messages</h1>
<div class="row" tal:repeat="msg view/getPendingMessagesInfo">
<div class="field">
<a href="" tal:attributes="href msg/url"
tal:content="msg/title" />
</div>
</div>
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/subscriptions.pt ===
<html metal:use-macro="views/standard_macros/page">
<head>
<style metal:fill-slot="style_slot">
</style>
</head>
<body>
<div metal:fill-slot="body" i18n:domain="messageboard">
<form action="changeSubscriptions.html" method="post">
<div class="row">
<div class="label" i18n:translate="">Current Subscriptions</div>
<div class="field">
<div tal:repeat="email view/subscriptions">
<input type="checkbox" name="remails:list"
value="" tal:attributes="value email">
<div tal:replace="email">zope3@zope3.org</div>
</div>
<input type="submit" name="REMOVE" value="Remove"
i18n:attributes="value remove-button">
</div>
</div>
<div class="row">
<div class="label" i18n:translate="">
Enter new Users (separate by 'Return')
</div>
<div class="field">
<textarea name="emails" cols="40" rows="10"></textarea>
</div>
</div>
<div class="row">
<div class="controls">
<input type="submit" value="Refresh"
i18n:attributes="value refresh-button" />
<input type="submit" name="ADD" value="Add"
i18n:attributes="value add-button" />
</div>
</div>
</form>
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/subthread.pt ===
<ul>
<li tal:repeat="item view/listContentInfo">
<a href=""
tal:attributes="href item/url"
tal:content="item/title">Message 1</a>
<div tal:replace="structure item/thread"/>
</li>
</ul>
=== Added File Products3/demo/messageboard/step9/browser/thread.pt ===
<html metal:use-macro="views/standard_macros/page">
<body>
<div metal:fill-slot="body" i18n:domain="messageboard">
<h1 i18n:translate="">Discussion Thread</h1>
<div tal:replace="structure view/subthread" />
</div>
</body>
</html>
=== Added File Products3/demo/messageboard/step9/browser/thread.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 for the sub-thread of a Message or MessageBoard
$Id: thread.py,v 1.1 2003/07/15 21:43:36 srichter Exp $
"""
from messageboard import hasMessageStatus
from zopeproducts.messageboard.interfaces import IMessage
from zope.app.context import ContextWrapper
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
class Thread:
def __init__(self, context, request, base_url=''):
self.context = context
self.request = request
self.base_url = base_url
def listContentInfo(self):
children = []
for name, child in self.context.items():
if IMessage.isImplementedBy(child) and \
hasMessageStatus(child, 'published'):
wrapped = ContextWrapper(child, self.context, name=name)
info = {}
info['title'] = child.title
url = self.base_url + name + '/'
info['url'] = url + '@@thread.html'
thread = Thread(wrapped, self.request, url)
info['thread'] = thread.subthread()
children.append(info)
return children
subthread = ViewPageTemplateFile('subthread.pt')
=== Added File Products3/demo/messageboard/step9/browser/widgets.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 widget definitions.
$Id: widgets.py,v 1.1 2003/07/15 21:43:36 srichter Exp $
"""
import re
from zope.app.browser.form.widget import TextAreaWidget
from zopeproducts.messageboard.fields import \
forbidden_regex, allowed_regex
class HTMLSourceWidget(TextAreaWidget):
def _convert(self, value):
if self.context.forbidden_tags:
regex = forbidden_regex %'|'.join(
self.context.forbidden_tags)
value = re.sub(regex, '', value)
if self.context.allowed_tags:
regex = allowed_regex %'|'.join(
self.context.allowed_tags)
value = re.sub(regex, '', value)
return value