[Zope3-checkins] CVS: Products3/demo/messageboard/step8/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
Mon, 14 Jul 2003 20:01:20 -0400


Update of /cvs-repository/Products3/demo/messageboard/step8/browser
In directory cvs.zope.org:/tmp/cvs-serv12293/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 8 landed. The recipe is at 
http://dev.zope.org/Zope3//WorkflowForContent


=== Added File Products3/demo/messageboard/step8/browser/__init__.py ===


=== Added File Products3/demo/messageboard/step8/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/step8/browser/configure.zcml ===
<zopeConfigure
   xmlns="http://namespaces.zope.org/zope"
   xmlns:browser="http://namespaces.zope.org/browser">

  <!-- 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" />

  <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"/>

  <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:pages
      for="zopeproducts.messageboard.interfaces.IMessageBoard"
      class=".messageboard.ReviewMessages"
      permission="zopeproducts.messageboard.PublishContent">
      <browser:page name="review.html" template="review.pt"
          menu="zmi_views" title="Review Messages" />
  </browser:pages>

  <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" />

  <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"/>

  <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"/>

  <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>

  <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/step8/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/step8/browser/message.png ===
  <Binary-ish file>

=== Added File Products3/demo/messageboard/step8/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 00:01:06 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/step8/browser/messageboard.png ===
  <Binary-ish file>

=== Added File Products3/demo/messageboard/step8/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 00:01:06 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):
        """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/step8/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/step8/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/step8/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/step8/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/step8/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/step8/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 00:01:06 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/step8/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 00:01:06 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