work in progress: 'send link by email' feature

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3154 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-01-17 16:51:51 +00:00
parent 6ff94ad004
commit 12061a6aa3
8 changed files with 184 additions and 40 deletions

View file

@ -101,8 +101,8 @@ class ConceptEditForm(EditForm, I18NView):
desc.height = 2 desc.height = 2
class ConceptRelationView(BaseView): class BaseRelationView(BaseView):
""" For displaying children and resources lists, used by ConceptView. """ For displaying children and resources lists.
""" """
def __init__(self, relation, request, contextIsSecond=False): def __init__(self, relation, request, contextIsSecond=False):
@ -123,6 +123,9 @@ class ConceptRelationView(BaseView):
@Lazy @Lazy
def data(self): def data(self):
return self.getData()
def getData(self):
return self.instance.applyTemplate() return self.instance.applyTemplate()
@Lazy @Lazy
@ -176,19 +179,13 @@ class ConceptRelationView(BaseView):
def order(self): def order(self):
return self.relation.order return self.relation.order
def getActions(self, category='object', page=None, target=None):
t = IType(self.context)
actInfo = t.optionsDict.get('action.' + category, '')
actNames = [n.strip() for n in actInfo.split(',')]
if actNames:
return actions.get(category, actNames, view=self, page=page, target=target)
return []
class ConceptView(BaseView): class ConceptView(BaseView):
template = concept_macros template = concept_macros
childViewFactory = ConceptRelationView
def childViewFactory(self, *args, **kw):
return ConceptRelationView(*args, **kw)
@Lazy @Lazy
def macro(self): def macro(self):
@ -354,12 +351,32 @@ class ConceptView(BaseView):
yield NodeView(node, self.request) yield NodeView(node, self.request)
def getActions(self, category='object', page=None, target=None): def getActions(self, category='object', page=None, target=None):
acts = []
t = IType(self.context) t = IType(self.context)
actInfo = t.optionsDict.get('action.' + category, '') actInfo = t.optionsDict.get('action.' + category, '')
actNames = [n.strip() for n in actInfo.split(',')] actNames = [n.strip() for n in actInfo.split(',')]
if actNames: if actNames:
return actions.get(category, actNames, view=self, page=page, target=target) acts = list(actions.get(category, actNames,
return [] view=self, page=page, target=target))
if category in self.actions:
acts.extend(self.actions[category](self, page, target))
return acts
def getObjectActions(self, page=None, target=None):
acts = ['info']
acts.extend('state.' + st.statesDefinition for st in self.states)
if self.globalOptions('organize.allowSendEmail'):
acts.append('send_email')
return actions.get('object', acts, view=self, page=page, target=target)
actions = dict(object=getObjectActions)
class ConceptRelationView(ConceptView, BaseRelationView):
__init__ = BaseRelationView.__init__
getData = BaseRelationView.getData
class ConceptConfigureView(ConceptView): class ConceptConfigureView(ConceptView):

View file

@ -3,7 +3,7 @@
<metal:info define-macro="object_info" <metal:info define-macro="object_info"
tal:define="item nocall:view/item"> tal:define="item nocall:view/item">
<table class="object_info"> <table class="object_info" width="400">
<tr> <tr>
<td colspan="2"><h2 i18n:translate="">Object Information</h2><br /></td> <td colspan="2"><h2 i18n:translate="">Object Information</h2><br /></td>
</tr> </tr>
@ -23,9 +23,10 @@
<td><span i18n:translate="">Type</span>:</td> <td><span i18n:translate="">Type</span>:</td>
<td tal:content="item/longTypeTitle"></td> <td tal:content="item/longTypeTitle"></td>
</tr> </tr>
<tr> <tr tal:define="size item/context/sizeForDisplay|nothing"
tal:condition="size">
<td><span i18n:translate="">Size</span>:</td> <td><span i18n:translate="">Size</span>:</td>
<td tal:content="item/context/sizeForDisplay"></td> <td tal:content="size"></td>
</tr> </tr>
<tr> <tr>
<td><span i18n:translate="">modified</span>:</td> <td><span i18n:translate="">modified</span>:</td>

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de # Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -38,7 +38,6 @@ from zope.event import notify
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.lifecycleevent import Attributes from zope.lifecycleevent import Attributes
from zope.formlib.form import Form, FormFields from zope.formlib.form import Form, FormFields
from zope.formlib.namedtemplate import NamedTemplate
from zope.proxy import removeAllProxies from zope.proxy import removeAllProxies
from zope.security import canAccess, canWrite, checkPermission from zope.security import canAccess, canWrite, checkPermission
from zope.security.proxy import removeSecurityProxy from zope.security.proxy import removeSecurityProxy
@ -50,7 +49,7 @@ from cybertools.browser.view import GenericView
from cybertools.stateful.interfaces import IStateful from cybertools.stateful.interfaces import IStateful
from cybertools.typology.interfaces import IType, ITypeManager from cybertools.typology.interfaces import IType, ITypeManager
from cybertools.xedit.browser import ExternalEditorView from cybertools.xedit.browser import ExternalEditorView
from loops.browser.action import DialogAction from loops.browser.action import actions, DialogAction
from loops.common import adapted, AdapterBase from loops.common import adapted, AdapterBase
from loops.i18n.browser import i18n_macros from loops.i18n.browser import i18n_macros
from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode from loops.interfaces import IConcept, IResource, IDocument, IMediaAsset, INode
@ -438,6 +437,12 @@ class NodeView(BaseView):
page=self, target=target)) page=self, target=target))
return actions return actions
def xx_getObjectActions(self, target=None):
acts = []
if self.globalOptions('organize.allowSendEmail'):
acts.append('send_email')
return actions.get('object', acts, view=self, page=self, target=target)
actions = dict(portlet=getPortletActions) actions = dict(portlet=getPortletActions)
@Lazy @Lazy
@ -704,7 +709,9 @@ class ConfigureView(NodeView):
class NodeAdding(Adding): class NodeAdding(Adding):
def addingInfo(self): pass
def xx_addingInfo(self):
info = super(NodeAdding, self).addingInfo() info = super(NodeAdding, self).addingInfo()
#info.append({'title': 'Document', #info.append({'title': 'Document',
# 'action': 'AddLoopsNodeDocument.html', # 'action': 'AddLoopsNodeDocument.html',

View file

@ -44,7 +44,8 @@ from cybertools.typology.interfaces import IType
from cybertools.xedit.browser import ExternalEditorView, fromUnicode from cybertools.xedit.browser import ExternalEditorView, fromUnicode
from loops.browser.action import DialogAction, TargetAction from loops.browser.action import DialogAction, TargetAction
from loops.browser.common import EditForm, BaseView from loops.browser.common import EditForm, BaseView
from loops.browser.concept import ConceptRelationView, ConceptConfigureView from loops.browser.concept import BaseRelationView, ConceptRelationView
from loops.browser.concept import ConceptConfigureView
from loops.browser.node import NodeView, node_macros from loops.browser.node import NodeView, node_macros
from loops.common import adapted, NameChooser from loops.common import adapted, NameChooser
from loops.interfaces import IBaseResource, IDocument, IMediaAsset, ITextDocument from loops.interfaces import IBaseResource, IDocument, IMediaAsset, ITextDocument
@ -190,8 +191,9 @@ class ResourceView(BaseView):
def getObjectActions(self, page=None, target=None): def getObjectActions(self, page=None, target=None):
acts = ['info'] acts = ['info']
#acts.extend('state.' + st for st in statefulActions)
acts.extend('state.' + st.statesDefinition for st in self.states) acts.extend('state.' + st.statesDefinition for st in self.states)
if self.globalOptions('organize.allowSendEmail'):
acts.append('send_email')
if self.xeditable: if self.xeditable:
acts.append('external_edit') acts.append('external_edit')
return actions.get('object', acts, view=self, page=page, target=target) return actions.get('object', acts, view=self, page=page, target=target)
@ -214,10 +216,9 @@ class ResourceView(BaseView):
yield NodeView(node, self.request) yield NodeView(node, self.request)
class ResourceRelationView(ResourceView, ConceptRelationView): class ResourceRelationView(ResourceView, BaseRelationView):
def __init__(self, relation, request, contextIsSecond=False): __init__ = BaseRelationView.__init__
ConceptRelationView.__init__(self, relation, request, contextIsSecond)
getActions = ResourceView.getActions getActions = ResourceView.getActions

View file

@ -132,6 +132,7 @@ concept assigned we should get an error:
>>> johnC.conceptType = person >>> johnC.conceptType = person
>>> john = IPerson(johnC) >>> john = IPerson(johnC)
>>> john.userId = 'users.john' >>> john.userId = 'users.john'
>>> john.email = 'john@loopz.org'
>>> marthaC = concepts['martha'] = Concept(u'Martha') >>> marthaC = concepts['martha'] = Concept(u'Martha')
>>> marthaC.conceptType = person >>> marthaC.conceptType = person
@ -377,6 +378,20 @@ Allocation of persons to tasks
... conceptType=predicate, predicateInterface=IAllocated) ... conceptType=predicate, predicateInterface=IAllocated)
Send Email to Members
=====================
>>> menu.target = event01
>>> from loops.organize.browser.party import SendEmailForm
>>> form = SendEmailForm(menu, TestRequest())
>>> form.members
[{'email': 'john@loopz.org', 'title': u'John'}]
>>> form.subject
u"loops Notification from '$site'"
>>> form.mailBody
u'\n\nEvent #1\nhttp://127.0.0.1/loops/views/menu/.target95\n\n'
Fin de partie Fin de partie
============= =============

View file

@ -11,8 +11,7 @@
zope.publisher.interfaces.browser.IBrowserRequest" zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface" provides="zope.interface.Interface"
factory="loops.organize.browser.member.PersonalInfo" factory="loops.organize.browser.member.PersonalInfo"
permission="zope.View" permission="zope.View" />
/>
<zope:view <zope:view
type="zope.publisher.interfaces.browser.IBrowserRequest" type="zope.publisher.interfaces.browser.IBrowserRequest"
@ -20,22 +19,19 @@
zope.schema.interfaces.ITextLine" zope.schema.interfaces.ITextLine"
provides="zope.app.form.interfaces.IDisplayWidget" provides="zope.app.form.interfaces.IDisplayWidget"
factory="cybertools.browser.widget.SimpleListDisplayWidget" factory="cybertools.browser.widget.SimpleListDisplayWidget"
permission="zope.Public" permission="zope.Public" />
/>
<browser:page <browser:page
for="loops.interfaces.INode" for="loops.interfaces.INode"
name="register_user.html" name="register_user.html"
class="loops.organize.browser.member.MemberRegistration" class="loops.organize.browser.member.MemberRegistration"
permission="zope.Public" permission="zope.Public" />
/>
<browser:page <browser:page
for="loops.interfaces.INode" for="loops.interfaces.INode"
name="change_password.html" name="change_password.html"
class="loops.organize.browser.member.PasswordChange" class="loops.organize.browser.member.PasswordChange"
permission="zope.Public" permission="zope.Public" />
/>
<zope:adapter <zope:adapter
name="task.html" name="task.html"
@ -43,8 +39,7 @@
zope.publisher.interfaces.browser.IBrowserRequest" zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface" provides="zope.interface.Interface"
factory="loops.organize.browser.task.TaskView" factory="loops.organize.browser.task.TaskView"
permission="zope.View" permission="zope.View" />
/>
<zope:adapter <zope:adapter
name="list_events.html" name="list_events.html"
@ -52,15 +47,19 @@
zope.publisher.interfaces.browser.IBrowserRequest" zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface" provides="zope.interface.Interface"
factory="loops.organize.browser.event.Events" factory="loops.organize.browser.event.Events"
permission="zope.View" permission="zope.View" />
/>
<zope:adapter <zope:adapter
for="cybertools.tracking.interfaces.ITrack for="cybertools.tracking.interfaces.ITrack
zope.publisher.interfaces.browser.IBrowserRequest" zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface" provides="zope.interface.Interface"
factory="cybertools.tracking.browser.TrackView" factory="cybertools.tracking.browser.TrackView"
permission="zope.View" permission="zope.View" />
/>
<browser:page
name="object_send_email.html"
for="loops.interfaces.INode"
class="loops.organize.browser.party.SendEmailForm"
permission="zope.View" />
</configure> </configure>

View file

@ -1,5 +1,5 @@
# #
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de # Copyright (c) 2009 Helmut Merz helmutm@cy55.de
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -24,12 +24,19 @@ $Id$
""" """
from zope import interface, component from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from zope.dublincore.interfaces import IZopeDublinCore
from cybertools.ajax import innerHtml
from cybertools.browser.action import actions from cybertools.browser.action import actions
from loops.browser.action import DialogAction from loops.browser.action import DialogAction
from loops.browser.node import NodeView
from loops.common import adapted
from loops.util import _ from loops.util import _
organize_macros = ViewPageTemplateFile('view_macros.pt')
actions.register('createPerson', 'portlet', DialogAction, actions.register('createPerson', 'portlet', DialogAction,
title=_(u'Create Person...'), title=_(u'Create Person...'),
@ -66,3 +73,53 @@ actions.register('editAddress', 'portlet', DialogAction,
viewName='edit_concept.html', viewName='edit_concept.html',
dialogName='editAddress', dialogName='editAddress',
) )
actions.register('send_email', 'object', DialogAction,
description=_(u'Send a link to this object by email.'),
viewName='object_send_email.html',
dialogName='',
icon='cybertools.icons/email.png',
cssClass='icon-action',
prerequisites=['registerDojoTextWidget', 'registerDojoTextarea'],
addParams=dict(version='this'),
)
class SendEmailForm(NodeView):
__call__ = innerHtml
@property
def macro(self):
return organize_macros.macros['send_email']
@Lazy
def dialog_name(self):
return self.request.get('dialog', 'object_send_email')
@Lazy
def title(self):
return self.target.title
@Lazy
def targetUrl(self):
return self.getUrlForTarget(self.virtualTargetObject)
@Lazy
def members(self):
persons = self.conceptManager['person'].getChildren([self.typePredicate])
persons = [adapted(p) for p in persons]
return [dict(title=p.title, email=p.email) for p in persons if p.email]
@Lazy
def mailBody(self):
return '\n\n%s\n%s\n\n' % (self.title, self.targetUrl)
@Lazy
def subject(self):
menu = self.context.getMenu()
zdc = IZopeDublinCore(menu)
zdc.languageInfo = self.languageInfo
site = zdc.title or menu.title
return _(u"loops Notification from '$site'",
mapping=dict(site=site))

View file

@ -43,3 +43,50 @@
</tal:child> </tal:child>
</div> </div>
</metal:block> </metal:block>
<metal:block define-macro="send_email">
<form method="post" id="sendEmail_form" class="dialog"
dojoType="dijit.form.Form">
<input type="hidden" name="form.action" value="create_workitem" />
<div class="heading">
<span i18n:translate="">Send Link by Email</span> -
<span tal:content="view/target/title"></span></div>
<div>
<label i18n:translate="" for="subject">Subject</label>
<div>
<input name="subject" id="subject" style="width: 60em"
dojoType="dijit.form.ValidationTextBox" required
tal:attributes="value view/subject" /></div>
</div>
<div>
<label i18n:translate="" for="mailbody">Mail Body</label>
<div>
<textarea name="mailbody" cols="80" rows="4" id="mailbody"
dojoType="dijit.form.SimpleTextarea" style="width: 60em"
tal:content="view/mailBody"></textarea></div>
</div>
<div>
<label i18n:translate="">Recipients</label>
<div tal:repeat="member view/members">
<input type="checkbox" checked name="recipients:list"
tal:attributes="value member/email" />
<span tal:content="member/title">Johnny</span>
(<span tal:content="member/email">Johnny</span>)
</div>
<div>
<label i18n:translate="" for="addrecipients">Additional recipients</label>
<div>
<textarea name="addrecipients" cols="80" rows="4" id="addrecipients"
dojoType="dijit.form.SimpleTextarea"
style="width: 60em"></textarea></div>
</div>
<div class="buttons">
<input value="Send email" type="submit"
onClick="return closeDialog(true)"
i18n:attributes="value">
<input type="button" value="Cancel"
onClick="return closeDialog(false)"
i18n:attributes="value"></div>
</form>
</metal:block>