Commit 63ac2d77 authored by Jérome Perrin's avatar Jérome Perrin

ERP5ShortMessage: simplify gateway interface

Implementation dependant parameters, such as message type or quality,
must be defined by the gateway class, not in the common
ShortMessage_send script.

This should make it easier to switch implementations.

Also:
 * send method is expected to be called once per recipient. I guess we
   prefer to isolate messages than to batch send messages efficiently.
 * send method expect relative url of sender and reciever documents, so
   that it can decide which properties to use.
 * drop `sender_title` parameter, now the gateway implementation decide
   what to use.
 * drop `test` parameter. For simulation mode, set simulation mode on
   the gateway instance
 * sms.send() no longer accept direct from_url / to_url / body message.
   It only supports sending what's defined on document properties.
 * SMSTool_afterSend will be called with message_id parameter, not
   message_id_list
 * Split interfaces in two SMS Sending / SMS Receiving.
parent 5a53b421
"""Save the message id of the relative document"""
if document_relative_url:
document = context.getPortalObject().restrictedTraverse(document_relative_url)
document.edit(destination_reference=message_id_list[0],
gateway = gateway_relative_url)
document.edit(destination_reference=message_id,
gateway=gateway_relative_url)
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>message_id_list, document_relative_url=None, gateway_relative_url=None, **kw</string> </value>
<value> <string>message_id, document_relative_url=None, gateway_relative_url=None, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -4,42 +4,29 @@
"""
#Get recipients
if not to_url:
recipient_phone_list = [person.getDefaultMobileTelephoneValue() for person in context.getDestinationValueList()]
if None in recipient_phone_list:
raise ValueError("All recipients should have a default mobile phone")
recipient_phone_list = [
person.getDefaultMobileTelephoneValue() for person in context.getDestinationValueList()]
if None in recipient_phone_list:
raise ValueError("All recipients should have a default mobile phone")
to_url = [phone.asURL() for phone in recipient_phone_list]
if None in to_url:
raise ValueError("All recipients should have a valid default mobile phone number")
to_url = [phone.asURL() for phone in recipient_phone_list]
if None in to_url:
raise ValueError("All recipients should have a valid default mobile phone number")
#Get sender
if not from_url:
if context.getSourceValue():
sender_phone = context.getSourceValue().getDefaultMobileTelephoneValue()
if not sender_phone:
raise ValueError("The sender(%s) should have a default mobile phone" % context.getSourceValue())
#We use title of sender
from_title = sender_phone.getTitle()
from_url = sender_phone.asURL()
if not body:
body = context.getTextContent()
body = context.getTextContent()
if not context.getStartDate():
context.setStartDate(DateTime())
context.portal_sms.activate(
activity="SQLQueue",
# We do not retry these activities not to send SMS multiple times
max_retry=0,
conflict_retry=False,
).send(
text=body,
recipient=to_url,
sender=from_url,
sender_title=from_title,
message_type="MULTITEXT",
test=download,
document_relative_url=context.getRelativeUrl(),
**kw)
for recipient in context.getDestinationList():
context.portal_sms.activate(
activity="SQLQueue",
# We do not retry these activities not to send SMS multiple times
max_retry=0,
conflict_retry=False,
).send(
text=body,
sender=context.getSource(),
recipient=recipient,
document_relative_url=context.getRelativeUrl(),
)
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>from_url=None, from_title=None, to_url=None, reply_url=None, subject=None, body=None, attachment_format=None, attachment_list=None,download=False,**kw</string> </value>
<value> <string>from_url=None, from_title=None, to_url=None, reply_url=None, subject=None, body=None, attachment_format=None, attachment_list=None, download=False, **kw</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
......
......@@ -55,51 +55,29 @@ class DummyGateway(XMLObject):
add_permission = Permissions.AddPortalContent
zope.interface.implements(interfaces.ISmsGateway)
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properi ties
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
security.declarePublic('getAllowedMessageType')
def getAllowedMessageType(self):
"""List of all message type"""
return ['text',]
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text,recipient,sender=None, sender_title=None,
message_type="text",test=False,**kw):
"""Send a message.
Parameters:
text -- message
recipient -- phone url of destination_reference. Could be a list
sender -- phone url of source
sender_title -- Use it as source if the gateway has title mode enable
message_type -- Only 'text' is available today
test -- Force the test mode
Kw Parameters:
quality -- Quality of the SMS (default,n)
Return message id
"""
#Check messsage type
# XXX does it make sense to check message type in dummy gateway ? -jerome
#if message_type not in self.getAllowedMessageType():
# raise ValueError, "Type of message in not allowed"
def send(self, text, recipient, sender):
"""Send a short message.
"""
#Send message (or test)
if test or self.isSimulationMode():
if self.isSimulationMode():
return None
else:
return self._generateRandomMessageId()
return self._generateRandomMessageId()
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
......
......@@ -61,7 +61,9 @@ class EssendexGateway(XMLObject):
add_permission = Permissions.AddPortalContent
zope.interface.implements(interfaces.ISmsGateway)
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
......@@ -75,10 +77,6 @@ class EssendexGateway(XMLObject):
)
api_url = "https://www.esendex.com/secure/messenger/formpost"
security.declarePublic('getAllowedMessageType')
def getAllowedMessageType(self):
"""List of all message type"""
return ['text', 'binary', 'smartMessage', 'unicode']
security.declarePrivate("_fetchPageAsDict")
def _fetchPageAsDict(self,page):
......@@ -132,32 +130,17 @@ class EssendexGateway(XMLObject):
return timedelta.seconds + (timedelta.days * 24 * 60 * 60)
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text,recipient,sender=None, sender_title=None,
message_type="text",test=False,**kw):
def send(self, text, recipient, sender):
"""Send a message.
Parameters:
text -- message
recipient -- phone url of destination_reference. Could be a list
sender -- phone url of source
sender_title -- Use it as source if the gateway has title mode enable
message_type -- Only 'text' is available today
test -- Force the test mode
Kw Parameters:
validity_period -- Validity Period of SMS (default,0)
Return message id (or list if multiple recipient)
"""
if message_type not in self.getAllowedMessageType():
raise ValueError, "Type of message in not allowed"
"""
traverse = self.getPortalObject().restrictedTraverse
message_type = self.getProperty('essendex_message_type', 'text')
assert message_type in ('text', 'binary', 'smartMessage', 'unicode')
validity_period = kw.get('validity_period',0)
validity_period = self.getProperty('essendex_validity_period', 0)
if not isinstance(recipient, str):
recipient = ",".join([self._transformPhoneUrlToGatewayNumber(x) for x in recipient])
else:
recipient = self._transformPhoneUrlToGatewayNumber(recipient)
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
base_url = self.api_url + "/SendSMS.aspx"
params = {'Username': self.getGatewayUser(),
......@@ -170,16 +153,13 @@ class EssendexGateway(XMLObject):
'PlainText': 1,
}
if self.isTitleMode():
params['Originator'] = traverse(sender).getDefaultMobileTelephoneValue().getTitle()
else:
params['Originator'] = self._transformPhoneUrlToGatewayNumber(
traverse(sender).getDefaultMobileTelephoneValue().asURL()) or self.getDefaultSender()
if sender_title and self.isTitleMode():
params['Originator'] = sender_title
elif sender:
params['Originator'] = self._transformPhoneUrlToGatewayNumber(sender)
elif self.getDefaultSender():
params['Originator'] = self.getDefaultSender()
if test or self.isSimulationMode():
if self.isSimulationMode():
params['Test'] = 1
LOG("EssendexGateway", INFO, params)
......
......@@ -57,7 +57,9 @@ class MobytGateway(XMLObject):
add_permission = Permissions.AddPortalContent
zope.interface.implements(interfaces.ISmsGateway)
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
......@@ -74,12 +76,6 @@ class MobytGateway(XMLObject):
# for documentation of this old API
api_url = "http://multilevel.mobyt.fr/sms"
security.declarePublic('getAllowedMessageType')
def getAllowedMessageType(self):
"""List of all message type"""
# `text` is here for compatibility, but the API always expected uppercase
return ['text', 'TEXT', 'MULTITEXT', 'WAPPUSH', 'UCS2', 'MULTIUCS2']
security.declarePrivate("_fetchSendResponseAsDict")
def _fetchSendResponseAsDict(self,page):
"""Page result is like Key=value in text format.
......@@ -141,42 +137,24 @@ class MobytGateway(XMLObject):
return phone
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text,recipient,sender=None, sender_title=None,
message_type="text",test=False,**kw):
def send(self, text, recipient, sender):
"""Send a message.
Parameters:
text -- message
recipient -- phone url of destination_reference. Could be a list
sender -- phone url of source
sender_title -- Use it as source if the gateway has title mode enable
message_type -- see getAllowedMessageType
test -- Force the test mode
Kw Parameters:
quality -- Quality of the SMS (default,n)
Return message id
"""
"""
traverse = self.getPortalObject().restrictedTraverse
#Check messsage type
if message_type not in self.getAllowedMessageType():
raise ValueError, "Type of message in not allowed"
message_type = self.getProperty('mobyt_message_type', 'MULTITEXT')
if message_type not in ('TEXT', 'MULTITEXT', 'WAPPUSH', 'UCS2', 'MULTIUCS2'):
raise ValueError("Type of message in not allowed")
#Check message qualit
quality = kw.get('quality','n') #Allow sender personalization and status of SMS
assert quality in ['n','l','ll'], "Unknown quality : '%s'" % quality
#Check message quality
quality = self.getProperty('mobyt_quality', 'n') #Allow sender personalization and status of SMS
assert quality in ('n','l','ll'), "Unknown quality : '%s'" % quality
#Recipients
if not isinstance(recipient, str):
recipient_count = len(recipient)
recipient = ",".join([self._transformPhoneUrlToGatewayNumber(x) for x in recipient])
else:
recipient = self._transformPhoneUrlToGatewayNumber(recipient)
recipient_count = 1
#Recipient
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
if recipient_count > 1:
base_url = self.api_url + "/batch.php" #Multi recipient
else:
base_url = self.api_url + "/send.php"
base_url = self.api_url + "/send.php"
#Common params
params = { "user" : self.getGatewayUser(),
......@@ -186,22 +164,19 @@ class MobytGateway(XMLObject):
"qty" : quality,
"return_id": 1}
#Define sender
if sender_title and self.isTitleMode() and quality == 'n':
params['sender'] = sender_title
elif sender:
params['sender'] = self._transformPhoneUrlToGatewayNumber(sender)
elif self.getDefaultSender():
params['sender'] = self.getDefaultSender()
if self.isTitleMode():
params['sender'] = traverse(sender).getDefaultMobileTelephoneValue().getTitle()
else:
params['sender'] = self._transformPhoneUrlToGatewayNumber(
traverse(sender).getDefaultMobileTelephoneValue().asURL()) or self.getDefaultSender()
#Define type of message
if message_type != "text":
assert quality == 'n', "This type of message require top level messsage quality"
assert message_type in self.getAllowedMessageType(), "Unknown message type"
params['operation'] = message_type
#Send message (or test)
if test or self.isSimulationMode():
if self.isSimulationMode():
LOG("MobytGateway", INFO, params)
result = {'status': "Test"}
else:
......
......@@ -50,24 +50,22 @@ class SMSTool(BaseTool):
manage_overview = DTMLFile('explainSMSTool', _dtmldir )
security.declareProtected(ManagePortal, 'send')
def send(self, text,recipient, sender=None, sender_title=None,
message_type="text", test=False, gateway_reference='default',
document_relative_url=None, activate_kw=None, **kw):
"""
def send(self, text, recipient, sender, gateway_reference='default',
document_relative_url=None, activate_kw=None):
"""Send the message
gateway_reference: send message throught the gateway with this reference.
document_relative_url (optional) : allows to send back result to a document
activate_kw (optional) : Call SMSTool_afterSend if founded in activity with
message_id_list and document_relative_url
message_id and document_relative_url
"""
gateway = self.find(gateway_reference)
message_id_list = gateway.send(text=text,
recipient=recipient,
sender=sender,
sender_title=sender_title,
message_type=message_type,
test=test,
**kw)
message_id = gateway.send(
text=text,
recipient=recipient,
sender=sender)
if getattr(self, 'SMSTool_afterSend'):
# We need to use activities in order to avoid any conflict
......@@ -75,9 +73,9 @@ class SMSTool(BaseTool):
if activate_kw is not None:
send_activate_kw.update(**activate_kw)
self.activate(**send_activate_kw).SMSTool_afterSend(
message_id_list,
message_id,
document_relative_url=document_relative_url,
gateway_relative_url=gateway.getRelativeUrl(),**kw)
gateway_relative_url=gateway.getRelativeUrl())
security.declareProtected(ManagePortal, 'getMessageStatus')
def getMessageStatus(self,message_id, gateway_reference='default'):
......
......@@ -29,25 +29,33 @@
from zope.interface import Interface
class ISmsGateway(Interface):
class ISmsSendingGateway(Interface):
"""SMS Gateway allow sending Short Messages to phones.
"""
def send(text, recipient,
sender=None, sender_title=None,
message_type="text", test=False, **kw):
"""Send a message.
TODO: write
def send(text, recipient, sender):
"""Send a message.
* text: the message as an utf-8 encoded string
* recipient: relative URL of recipient person or organisation. Recipient must have a defaut mobile phone
* sender: relative URL of sender person or organisation.
TODO: is getAllowedMessageType part of this API ?
shouldn't we rely on content_type ? ( text/plain -> SMS, text/html -> MMS ? )
"""
On most implementations, returns a message-id that can be later passed to
getMessageStatus to check the status of the message.
"""
def receive(REQUEST):
"""Public handler to push notification from the gateway"""
def getMessageStatus(message_id):
"""Retrieve the status of a message
Should return x in ['sent', 'delivered', 'queued', 'failed']"""
def getAllowedMessageType():
"""List of all allowed message type when sending a message."""
def getMessageStatus(message_id):
"""Retrieve the status of a message
Should return x in ['sent', 'delivered', 'queued', 'failed']"""
class ISmsReceivingGateway(Interface):
"""Gateway to subscribe to events fired when Short Messages are send to SMS interface.
"""
def receive(REQUEST):
"""Public handler to push notifications from the gateway
REQUEST parameters are service provider dependent.
"""
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment