Commit f83f35e4 authored by Arnaud Fontaine's avatar Arnaud Fontaine

ZODB Components: Migrate ERP5ShortMessage Product and fix pylint warnings.

After upgrading bt5s, portal_sms may still ERP5BaseBroken until restarting Zope.
parent d8961c51
......@@ -40,85 +40,87 @@ from AccessControl.SecurityManagement import getSecurityManager, \
newSecurityManager
import zope.interface
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.XMLObject import XMLObject
from Products import ERP5Security
from erp5.component.interface.ISmsSendingGateway import ISmsSendingGateway
from erp5.component.interface.ISmsReceivingGateway import ISmsReceivingGateway
class DummyGateway(XMLObject):
"""Dummy SMS Gateway Implementation"""
meta_type='Dummy Gateway'
portal_type = 'Dummy Gateway'
security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a short message.
"""
#Send message (or test)
if self.isSimulationMode():
return None
return self._generateRandomMessageId()
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
return "delivered"
security.declarePublic('receive')
def receive(self,REQUEST):
"""Receive push notification from the gateway"""
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Dummy notify only new SMS
self.notifyReception(REQUEST.get("sender"),
REQUEST.get("text"),
self._generateRandomMessageId())
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, sender, text, message_id):
"""The gateway inform what we ha a new message.
"""
#Convert phone as erp5 compliant
def parsePhoneNumber(number):
#XXX: Should register well formatted number or brut number ?
#return number
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
#Create the new sms in activities
self.activate(activity='SQLQueue').SMSTool_pushNewSMS(
"""Dummy SMS Gateway Implementation"""
meta_type='Dummy Gateway'
portal_type = 'Dummy Gateway'
security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent
zope.interface.implements(
ISmsSendingGateway,
ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a short message.
"""
#Send message (or test)
if self.isSimulationMode():
return None
return self._generateRandomMessageId()
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
return "delivered"
security.declarePublic('receive')
def receive(self,REQUEST):
"""Receive push notification from the gateway"""
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Dummy notify only new SMS
self.notifyReception(REQUEST.get("sender"),
REQUEST.get("text"),
self._generateRandomMessageId())
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, sender, text, message_id):
"""The gateway inform what we ha a new message.
"""
#Convert phone as erp5 compliant
def parsePhoneNumber(number):
#XXX: Should register well formatted number or brut number ?
#return number
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
#Create the new sms in activities
self.activate(activity='SQLQueue').SMSTool_pushNewSMS(
message_id=message_id,
sender=parsePhoneNumber(sender),
recipient=None,
......@@ -126,9 +128,9 @@ class DummyGateway(XMLObject):
message_type='text/plain',
reception_date=DateTime())
def _generateRandomMessageId(self):
letters = random.sample(string.ascii_lowercase,20)
return "%s-%s-%s-%s" % (''.join(letters[0:5]),
''.join(letters[5:10]),
''.join(letters[10:15]),
''.join(letters[15:20]))
def _generateRandomMessageId(self):
letters = random.sample(string.ascii_lowercase,20)
return "%s-%s-%s-%s" % (''.join(letters[0:5]),
''.join(letters[5:10]),
''.join(letters[10:15]),
''.join(letters[15:20]))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>DummyGateway</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.Document.DummyGateway</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.DummyGateway</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -42,219 +42,218 @@ from AccessControl.SecurityManagement import getSecurityManager, \
import zope.interface
from zLOG import LOG, INFO
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.XMLObject import XMLObject
from Products import ERP5Security
#Product Module
from Products.ERP5ShortMessage.SMSGatewayError import SMSGatewayError
from erp5.component.module.SMSGatewayError import SMSGatewayError
from erp5.component.interface.ISmsSendingGateway import ISmsSendingGateway
from erp5.component.interface.ISmsReceivingGateway import ISmsReceivingGateway
class EssendexGateway(XMLObject):
"""Base of SMS an Gateway. You can use push notification for delivered and new message notification."""
meta_type='Essendex Gateway'
portal_type = 'Essendex Gateway'
security = ClassSecurityInfo()
class EssendexGateway(XMLObject):
"""Base of SMS an Gateway. You can use push notification for delivered and new message notification."""
meta_type='Essendex Gateway'
portal_type = 'Essendex Gateway'
security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
api_url = "https://www.esendex.com/secure/messenger/formpost"
security.declarePrivate("_fetchPageAsDict")
def _fetchPageAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
index = 0
#Read all lines
for line in page.readlines():
#Look is the line have multi key/value
parts = line.split('&')
if len(parts) == 1:
data = parts[0].split('=')
#Remove \n et \r from value
result[data[0]] = urllib.unquote(data[1].replace('\r','').replace('\n',''))
else:
#Mutil values
subresult = {}
for part in parts:
data = part.split('=')
subresult[data[0]] = urllib.unquote(data[1].replace('\r','').replace('\n',''))
result[index] = subresult
#Increment index for next
index += 1
return result
security.declarePrivate("_transformPhoneUrlToGatewayNumber")
def _transformPhoneUrlToGatewayNumber(self,phone):
"""Transform url of phone number to a valid phone number (gateway side)"""
phone = phone.replace('tel:', '').replace('+','').replace('(0)','').replace('-','')
# Check that phone number can not be something not existing
assert not(phone.startswith('99000'))
return phone
security.declarePrivate("_parsePhoneNumber")
def _parsePhoneNumber(self,number):
"""Convert phone number for erp5 compliance"""
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
security.declarePrivate("_parsePhoneNumber")
def _parseDate(self, string):
"""Convert a string (like 2011-05-03 10:23:16Z) to a DateTime"""
return DateTime(string.replace('Z', ' GTM+2'))
def _convertTimeDeltaToSeconds(self, timedelta):
""" Convert a timedelta to seconds """
return timedelta.seconds + (timedelta.days * 24 * 60 * 60)
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a message.
"""
traverse = self.getPortalObject().restrictedTraverse
message_type = self.getProperty('essendex_message_type', 'text')
assert message_type in ('text', 'binary', 'smartMessage', 'unicode')
validity_period = self.getProperty('essendex_validity_period', 0)
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
add_permission = Permissions.AddPortalContent
zope.interface.implements(
ISmsSendingGateway,
ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
api_url = "https://www.esendex.com/secure/messenger/formpost"
security.declarePrivate("_fetchPageAsDict")
def _fetchPageAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
index = 0
#Read all lines
for line in page.readlines():
#Look is the line have multi key/value
parts = line.split('&')
if len(parts) == 1:
data = parts[0].split('=')
#Remove \n et \r from value
result[data[0]] = urllib.unquote(data[1].replace('\r','').replace('\n',''))
base_url = self.api_url + "/SendSMS.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'Recipient': recipient,
'Body': text,
'Type': message_type.capitalize(),
'ValidityPeriod': validity_period,
'PlainText': 1,
}
if self.isTitleMode():
params['Originator'] = traverse(sender).getDefaultMobileTelephoneValue().getTitle()
else:
params['Originator'] = self._transformPhoneUrlToGatewayNumber(
#Mutil values
subresult = {}
for part in parts:
data = part.split('=')
subresult[data[0]] = urllib.unquote(data[1].replace('\r','').replace('\n',''))
result[index] = subresult
#Increment index for next
index += 1
return result
security.declarePrivate("_transformPhoneUrlToGatewayNumber")
def _transformPhoneUrlToGatewayNumber(self,phone):
"""Transform url of phone number to a valid phone number (gateway side)"""
phone = phone.replace('tel:', '').replace('+','').replace('(0)','').replace('-','')
# Check that phone number can not be something not existing
assert not(phone.startswith('99000'))
return phone
security.declarePrivate("_parsePhoneNumber")
def _parsePhoneNumber(self,number):
"""Convert phone number for erp5 compliance"""
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
security.declarePrivate("_parsePhoneNumber")
def _parseDate(self, string):
"""Convert a string (like 2011-05-03 10:23:16Z) to a DateTime"""
return DateTime(string.replace('Z', ' GTM+2'))
def _convertTimeDeltaToSeconds(self, timedelta):
""" Convert a timedelta to seconds """
return timedelta.seconds + (timedelta.days * 24 * 60 * 60)
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a message.
"""
traverse = self.getPortalObject().restrictedTraverse
message_type = self.getProperty('essendex_message_type', 'text')
assert message_type in ('text', 'binary', 'smartMessage', 'unicode')
validity_period = self.getProperty('essendex_validity_period', 0)
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
base_url = self.api_url + "/SendSMS.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'Recipient': recipient,
'Body': text,
'Type': message_type.capitalize(),
'ValidityPeriod': validity_period,
'PlainText': 1,
}
if self.isTitleMode():
params['Originator'] = traverse(sender).getDefaultMobileTelephoneValue().getTitle()
else:
params['Originator'] = self._transformPhoneUrlToGatewayNumber(
traverse(sender).getDefaultMobileTelephoneValue().asURL()) or self.getDefaultSender()
if self.isSimulationMode():
params['Test'] = 1
LOG("EssendexGateway", INFO, params)
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
message_ids = result.get('MessageIDs', "")
#If a message is sent to multiple recipients, multiple IDs are returned
#each seperated by a comma.
return message_ids.split(",")
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to send the SMS"))
elif result['Result'] == "Test":
#just a test, no message id
return None
if self.isSimulationMode():
params['Test'] = 1
LOG("EssendexGateway", INFO, params)
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
message_ids = result.get('MessageIDs', "")
#If a message is sent to multiple recipients, multiple IDs are returned
#each seperated by a comma.
return message_ids.split(",")
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to send the SMS"))
elif result['Result'] == "Test":
#just a test, no message id
return None
else:
raise ValueError("Unknown result", 0, result)
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
base_url = self.api_url + "/QueryStatus.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'PlainText': 1,
'MessageID': message_id,
}
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
return result.get('MessageStatus').lower()
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to get the message status"))
security.declarePublic('receive')
def receive(self, REQUEST, **kw):
"""Receive push notification"""
#XML is stored is BODY of request
datas = REQUEST['BODY']
if not datas:
raise SMSGatewayError, "Impossible to notify nothing"
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Parse XML
root = etree.fromstring(datas)
#Choice action corresponding to the notification type
notification_type = root.tag
#Parse text XML Element to dict
xml = {}
for child in root.getchildren():
xml[child.tag] = child.text
#Check Account id
if xml['AccountId'] != self.getGatewayAccountId():
raise Unauthorized, 'Bad accound id (%s)' % xml['AccountId']
if notification_type == 'InboundMessage':
self.notifyReception(xml)
elif notification_type == 'MessageDelivered':
self.notifyDelivery(xml)
elif notification_type == 'MessageError':
raise SMSGatewayError, "'MessageError' notification is not implemented (%s)" % str(kw)
elif notification_type == 'SubscriptionEvent':
raise SMSGatewayError, "'MessageError' notification is not implemented (%s)" % str(kw)
else:
raise ValueError("Unknown result", 0, result)
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
base_url = self.api_url + "/QueryStatus.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'PlainText': 1,
'MessageID': message_id,
}
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
return result.get('MessageStatus').lower()
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to get the message status"))
security.declarePublic('receive')
def receive(self, REQUEST, **kw):
"""Receive push notification"""
#XML is stored is BODY of request
datas = REQUEST['BODY']
if not datas:
raise SMSGatewayError, "Impossible to notify nothing"
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Parse XML
root = etree.fromstring(datas)
#Choice action corresponding to the notification type
notification_type = root.tag
#Parse text XML Element to dict
xml = {}
for child in root.getchildren():
xml[child.tag] = child.text
#Check Account id
if xml['AccountId'] != self.getGatewayAccountId():
raise Unauthorized, 'Bad accound id (%s)' % xml['AccountId']
if notification_type == 'InboundMessage':
self.notifyReception(xml)
elif notification_type == 'MessageDelivered':
self.notifyDelivery(xml)
elif notification_type == 'MessageError':
raise SMSGatewayError, "'MessageError' notification is not implemented (%s)" % str(kw)
elif notification_type == 'SubscriptionEvent':
raise SMSGatewayError, "'MessageError' notification is not implemented (%s)" % str(kw)
else:
raise SMSGatewayError, "Unknow '%s' notification (%s)" % (notification_type, str(kw))
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, xml):
"""The gateway inform what we ha a new message.
root: lxml Element"""
"""
raise SMSGatewayError, "Unknow '%s' notification (%s)" % (notification_type, str(kw))
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, xml):
"""The gateway inform what we ha a new message.
root: lxml Element"""
"""
<InboundMessage>
<Id>{guid-of-push-notification}</Id>
<MessageId>{guid-of-inbound-message}</MessageId>
......@@ -264,9 +263,9 @@ class EssendexGateway(XMLObject):
<To>{phone number of the recipient of the inbound message (the
virtual number of the esendex account in use)}</To>
</InboundMessage>
"""
#Create the new sms in activities
self.activate(activity='SQLQueue', priority=1).SMSTool_pushNewSMS(
"""
#Create the new sms in activities
self.activate(activity='SQLQueue', priority=1).SMSTool_pushNewSMS(
message_id=xml['MessageId'],
sender=self._parsePhoneNumber(xml['From']),
recipient=self._parsePhoneNumber(xml['To']),
......@@ -275,11 +274,11 @@ class EssendexGateway(XMLObject):
reception_date=DateTime(),
mode="push")
security.declareProtected(Permissions.ManagePortal, 'notifyDelivery')
def notifyDelivery(self, xml):
"""Handle delivery info
security.declareProtected(Permissions.ManagePortal, 'notifyDelivery')
def notifyDelivery(self, xml):
"""Handle delivery info
xml: lxml Element"""
"""
"""
<MessageDelivered>
<Id>{guid-of-push-notification}</Id>
<MessageId>{guid-of-inbound-message}</MessageId>
......@@ -288,53 +287,53 @@ class EssendexGateway(XMLObject):
message was delivered to the recipient}</OccurredAt>
</MessageDelivered>
"""
"""
#Convert date to DateTime
xml['OccurredAt'] = DateTime(xml['OccurredAt'][0:19])
#Convert date to DateTime
xml['OccurredAt'] = DateTime(xml['OccurredAt'][0:19])
self.activate(activity='SQLQueue').SMSTool_setMessageDelivery(
self.activate(activity='SQLQueue').SMSTool_setMessageDelivery(
portal_type="Short Message",
destination_reference=xml['MessageId'],
delivery_date=xml['OccurredAt'])
def pullLastMessageList(self, start_date=None, stop_date=None):
"""Get last messsages on the gateway"""
if start_date is not None or stop_date is not None:
base_url = self.api_url + "/GetInboxMessage.aspx"
else:
base_url = self.api_url + "/GetLatestInboxMessages.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'PlainText': 1,
}
if start_date is not None:
params['StartDate'] = start_date.strftime('%d/%m/%Y %H:%M:%S')
if stop_date is not None:
params['EndDate'] = stop_date.strftime('%d/%m/%Y %H:%M:%S')
if self.isSimulationMode():
params['Test'] = 1
LOG("EssendexGateway", INFO, params)
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
#Push all message
type_mapping = {'Text': 'text/plain'}
now = DateTime()
for key, value in result.items():
if type(key) == int:
reception_date = self._parseDate(value['ReceivedAt'])
#Take only message received more than 10s
if self._convertTimeDeltaToSeconds(now - reception_date) > 10:
self.activate(activity='SQLQueue',priority=2).SMSTool_pushNewSMS(
def pullLastMessageList(self, start_date=None, stop_date=None):
"""Get last messsages on the gateway"""
if start_date is not None or stop_date is not None:
base_url = self.api_url + "/GetInboxMessage.aspx"
else:
base_url = self.api_url + "/GetLatestInboxMessages.aspx"
params = {'Username': self.getGatewayUser(),
'Password': self.getGatewayPassword(),
'Account': self.getGatewayAccount(),
'PlainText': 1,
}
if start_date is not None:
params['StartDate'] = start_date.strftime('%d/%m/%Y %H:%M:%S')
if stop_date is not None:
params['EndDate'] = stop_date.strftime('%d/%m/%Y %H:%M:%S')
if self.isSimulationMode():
params['Test'] = 1
LOG("EssendexGateway", INFO, params)
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchPageAsDict(page)
if result['Result'] == "OK":
#Push all message
type_mapping = {'Text': 'text/plain'}
now = DateTime()
for key, value in result.items():
if isinstance(key, int):
reception_date = self._parseDate(value['ReceivedAt'])
#Take only message received more than 10s
if self._convertTimeDeltaToSeconds(now - reception_date) > 10:
self.activate(activity='SQLQueue',priority=2).SMSTool_pushNewSMS(
message_id=value['ID'],
sender=self._parsePhoneNumber(value['Originator']),
recipient=self._parsePhoneNumber(value['Recipient']),
......@@ -342,10 +341,10 @@ class EssendexGateway(XMLObject):
message_type=type_mapping[value['Type']],
reception_date=reception_date,
mode="pull")
elif result['Result'] == "Test":
LOG("EssendexGateway", INFO, result)
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to get last message list"))
elif result['Result'] == "Test":
LOG("EssendexGateway", INFO, result)
elif result['Result'] == "Error":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('Message', "Impossible to get last message list"))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>EssendexGateway</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.Document.EssendexGateway</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.EssendexGateway</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2011 Nexedi SA and Contributors. All Rights Reserved.
# Francois-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""Receive or send SMS"""
#Import python module
import urllib
from DateTime import DateTime
#Import Zope module
from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, \
newSecurityManager
import zope.interface
from zLOG import LOG, INFO
from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.XMLObject import XMLObject
from Products import ERP5Security
from erp5.component.module.SMSGatewayError import SMSGatewayError
from erp5.component.interface.ISmsSendingGateway import ISmsSendingGateway
from erp5.component.interface.ISmsReceivingGateway import ISmsReceivingGateway
class MobytGateway(XMLObject):
"""Mobyt SMS Gateway Implementation"""
meta_type='Mobyt Gateway'
portal_type = 'Mobyt Gateway'
security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent
zope.interface.implements(
ISmsSendingGateway,
ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properi ties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
# see https://web.archive.org/web/20111125005954/http://www.mobyt.fr/doc/mobyt_module_http.pdf
# for documentation of this old API
api_url = "https://multilevel.mobyt.fr/sms"
security.declarePrivate("_fetchSendResponseAsDict")
def _fetchSendResponseAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
lines = page.readlines()
assert len(lines) == 1, "Multi lines response is not managed %s" % lines
line = lines[0]
parts = line.split(' ')
#Format is 'Status Message'
result['status'] = parts[0]
result['status_info'] = ' '.join(parts[1:])
return result
security.declarePrivate("_fetchStatusResponseAsDict")
def _fetchStatusResponseAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
lines = page.readlines()
#First line is special : CSV column title or error inform
line = lines[0]
if line[0:1] == "KO":
result['status'] = "KO"
result['status_info'] = line[2:]
return result
def _cleanText(s):
return s.replace('\r','').replace('\n','')
column_name_list = line.split(',')
column_count = len(column_name_list)
#Clean last colum
column_name_list[-1] = _cleanText(column_name_list[-1])
result['status'] = "OK"
row_list = []
#Batch other line to get all status
for line in lines[1:]:
row = {}
column_value_list = line.split(',')
column_value_list[-1] = _cleanText(column_value_list[-1])
for i in range(0,column_count):
row[column_name_list[i]] = column_value_list[i]
row_list.append(row)
result['status_info'] = row_list
return result
security.declarePrivate("_transformPhoneUrlToGatewayNumber")
def _transformPhoneUrlToGatewayNumber(self,phone):
"""Transform url of phone number to a valid phone number (gateway side)"""
phone = phone.replace('tel:', '').replace('(0)','').replace('-','')
# Check that phone number can not be something not existing
assert not(phone.startswith('99000'))
return phone
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a message.
"""
traverse = self.getPortalObject().restrictedTraverse
#Check messsage type
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 quality
quality = self.getProperty('mobyt_quality', 'n') #Allow sender personalization and status of SMS
assert quality in ('n','l','ll'), "Unknown quality : '%s'" % quality
#Recipient
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
base_url = self.api_url + "/send.php"
#Common params
params = { "user" : self.getGatewayUser(),
"pass" : self.getGatewayPassword(),
"rcpt" : recipient,
"data" : text,
"qty" : quality,
"return_id": 1}
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"
params['operation'] = message_type
#Send message (or test)
if self.isSimulationMode():
LOG("MobytGateway", INFO, params)
result = {'status': "Test"}
else:
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchSendResponseAsDict(page)
#Check result and return
if result['status'] == "OK":
return [result.get('status_info', "")] #return message id (gateway side)
elif result['status'] == "KO":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to send the SMS"))
elif result['status'] == "Test":
#just a test, no message id
return None
else:
raise ValueError("Unknown result", 0, result)
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
base_url = self.api_url + "/batch-status.php"
params = { "user" : self.getGatewayUser(),
"pass" : self.getGatewayPassword(),
"id" : message_id,
"type" : 'notify',
"schema" : 1 }
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchStatusResponseAsDict(page)
if result['status'] == "OK":
row_list = result.get('status_info')
#return only status_text list
if len(row_list) == 1:
return row_list[0].get('status_text').lower()
else:
status_list = []
for row in row_list:
status_list.append(row.get('status_text').lower())
return status_list
elif result['status'] == "KO":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to get the message status"))
security.declarePublic('receive')
def receive(self,REQUEST):
"""Receive push notification from the gateway"""
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Mobyt notify only new SMS
self.notifyReception(REQUEST.get("orig"),
REQUEST.get("text"),
REQUEST.get("ticket"))
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, sender, text, message_id):
"""The gateway inform what we ha a new message.
"""
#Convert phone as erp5 compliant
def parsePhoneNumber(number):
#XXX: Should register well formatted number or brut number ?
#return number
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
#Create the new sms in activities
self.activate(activity='SQLQueue').SMSTool_pushNewSMS(
message_id=message_id,
sender=parsePhoneNumber(sender),
recipient=None,
text_content=text,
message_type='text/plain',
reception_date=DateTime())
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>MobytGateway</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.Document.MobytGateway</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.MobytGateway</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Interface Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ISmsReceivingGateway</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.interfaces.sms_receiving_gateway</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interface.erp5.ISmsReceivingGateway</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interface Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Interface Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ISmsSendingGateway</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.interfaces.sms_sending_gateway</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interface.erp5.ISmsSendingGateway</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Interface Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Module Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SMSGatewayError</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.SMSGatewayError</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>module.erp5.SMSGatewayError</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Module Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -27,33 +27,23 @@
##############################################################################
import unittest
import os
from zope.interface.verify import verifyObject
from DateTime import DateTime
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
class ShortMessageTestCase(ERP5TypeTestCase):
def getBusinessTemplateList(self):
return ('erp5_full_text_mroonga_catalog',
'erp5_core_proxy_field_legacy',
'erp5_base',
'erp5_crm',
'erp5_short_message'
)
pass
class TestShortMessageGateway(ShortMessageTestCase):
def _verifyGatewayPortalType(self, portal_type):
import Products.ERP5Type.interfaces
gateway = self.portal.portal_sms.newContent(portal_type=portal_type)
verifyObject(Products.ERP5Type.interfaces.ISmsSendingGateway, gateway)
verifyObject(Products.ERP5Type.interfaces.ISmsReceivingGateway, gateway)
from erp5.component.interface.ISmsSendingGateway import ISmsSendingGateway
verifyObject(ISmsSendingGateway, gateway)
from erp5.component.interface.ISmsReceivingGateway import ISmsReceivingGateway
verifyObject(ISmsReceivingGateway, gateway)
def test_EssendexGateway(self):
......@@ -74,7 +64,7 @@ class TestShortMessageSending(ShortMessageTestCase):
self.portal.portal_sms,
self.portal.person_module,
self.portal.event_module ):
module.manage_delObjects(list(module.objectIds()))
module.manage_delObjects(list(module.objectIds()))
self.tic()
def test_ShortMessage_start(self):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>testShortMessage</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.tests.testShortMessage</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>test.erp5.testShortMessage</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -31,8 +31,7 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type.Permissions import ManagePortal
from Products.ERP5Type.Globals import DTMLFile
from Products.ERP5ShortMessage import _dtmldir
#from Products.ERP5ShortMessage import _dtmldir
class SMSTool(BaseTool):
"""
......@@ -47,7 +46,7 @@ class SMSTool(BaseTool):
# Declarative Security
security = ClassSecurityInfo()
security.declareProtected(ManagePortal, 'manage_overview')
manage_overview = DTMLFile('explainSMSTool', _dtmldir )
#manage_overview = DTMLFile('explainSMSTool', _dtmldir )
security.declareProtected(ManagePortal, 'send')
def send(self, text, recipient, sender, gateway_reference='default',
......@@ -80,8 +79,8 @@ class SMSTool(BaseTool):
security.declareProtected(ManagePortal, 'getMessageStatus')
def getMessageStatus(self,message_id, gateway_reference='default'):
gateway = self._findGateway(gateway_reference)
return gateway.getMessageStatus(message_id)
gateway = self._findGateway(gateway_reference)
return gateway.getMessageStatus(message_id)
security.declarePublic('isSendByTitleAllowed')
def isSendByTitleAllowed(self, gateway_reference='default'):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Tool Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>SMSTool</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5ShortMessage.Tool.SMSTool</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>tool.erp5.SMSTool</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Tool Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
document.erp5.DummyGateway
document.erp5.EssendexGateway
document.erp5.MobytGateway
\ No newline at end of file
interface.erp5.ISmsReceivingGateway
interface.erp5.ISmsSendingGateway
\ No newline at end of file
module.erp5.SMSGatewayError
\ No newline at end of file
test.erp5.testShortMessage
\ No newline at end of file
tool.erp5.SMSTool
\ No newline at end of file
erp5_full_text_mroonga_catalog
\ No newline at end of file
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2011 Nexedi SA and Contributors. All Rights Reserved.
# Francois-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""Receive or send SMS"""
#Import python module
import urllib
from DateTime import DateTime
#Import Zope module
from AccessControl import ClassSecurityInfo
from AccessControl.SecurityManagement import getSecurityManager, \
setSecurityManager, \
newSecurityManager
import zope.interface
from zLOG import LOG, INFO
from Products.ERP5Type import Permissions, PropertySheet, interfaces
from Products.ERP5Type.XMLObject import XMLObject
from Products import ERP5Security
#Product Module
from Products.ERP5ShortMessage.SMSGatewayError import SMSGatewayError
class MobytGateway(XMLObject):
"""Mobyt SMS Gateway Implementation"""
meta_type='Mobyt Gateway'
portal_type = 'Mobyt Gateway'
security = ClassSecurityInfo()
add_permission = Permissions.AddPortalContent
zope.interface.implements(
interfaces.ISmsSendingGateway,
interfaces.ISmsReceivingGateway)
# Declarative security
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
# Declarative properi ties
property_sheets = ( PropertySheet.Base
, PropertySheet.XMLObject
, PropertySheet.Reference
, PropertySheet.SMSGateway
)
# see https://web.archive.org/web/20111125005954/http://www.mobyt.fr/doc/mobyt_module_http.pdf
# for documentation of this old API
api_url = "https://multilevel.mobyt.fr/sms"
security.declarePrivate("_fetchSendResponseAsDict")
def _fetchSendResponseAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
lines = page.readlines()
assert len(lines) == 1, "Multi lines response is not managed %s" % lines
line = lines[0]
parts = line.split(' ')
#Format is 'Status Message'
result['status'] = parts[0]
result['status_info'] = ' '.join(parts[1:])
return result
security.declarePrivate("_fetchStatusResponseAsDict")
def _fetchStatusResponseAsDict(self,page):
"""Page result is like Key=value in text format.
We transform it to a more powerfull dictionnary"""
result = {}
lines = page.readlines()
#First line is special : CSV column title or error inform
line = lines[0]
if line[0:1] == "KO":
result['status'] = "KO"
result['status_info'] = line[2:]
return result
def _cleanText(s):
return s.replace('\r','').replace('\n','')
column_name_list = line.split(',')
column_count = len(column_name_list)
#Clean last colum
column_name_list[-1] = _cleanText(column_name_list[-1])
result['status'] = "OK"
row_list = []
#Batch other line to get all status
for line in lines[1:]:
row = {}
column_value_list = line.split(',')
column_value_list[-1] = _cleanText(column_value_list[-1])
for i in range(0,column_count):
row[column_name_list[i]] = column_value_list[i]
row_list.append(row)
result['status_info'] = row_list
return result
security.declarePrivate("_transformPhoneUrlToGatewayNumber")
def _transformPhoneUrlToGatewayNumber(self,phone):
"""Transform url of phone number to a valid phone number (gateway side)"""
phone = phone.replace('tel:', '').replace('(0)','').replace('-','')
# Check that phone number can not be something not existing
assert not(phone.startswith('99000'))
return phone
security.declareProtected(Permissions.ManagePortal, 'send')
def send(self, text, recipient, sender):
"""Send a message.
"""
traverse = self.getPortalObject().restrictedTraverse
#Check messsage type
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 quality
quality = self.getProperty('mobyt_quality', 'n') #Allow sender personalization and status of SMS
assert quality in ('n','l','ll'), "Unknown quality : '%s'" % quality
#Recipient
recipient = self._transformPhoneUrlToGatewayNumber(
traverse(recipient).getDefaultMobileTelephoneValue().asURL())
base_url = self.api_url + "/send.php"
#Common params
params = { "user" : self.getGatewayUser(),
"pass" : self.getGatewayPassword(),
"rcpt" : recipient,
"data" : text,
"qty" : quality,
"return_id": 1}
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"
params['operation'] = message_type
#Send message (or test)
if self.isSimulationMode():
LOG("MobytGateway", INFO, params)
result = {'status': "Test"}
else:
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchSendResponseAsDict(page)
#Check result and return
if result['status'] == "OK":
return [result.get('status_info', "")] #return message id (gateway side)
elif result['status'] == "KO":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to send the SMS"))
elif result['status'] == "Test":
#just a test, no message id
return None
else:
raise ValueError("Unknown result", 0, result)
security.declareProtected(Permissions.ManagePortal, 'getMessageStatus')
def getMessageStatus(self, message_id):
"""Retrive the status of a message"""
base_url = self.api_url + "/batch-status.php"
params = { "user" : self.getGatewayUser(),
"pass" : self.getGatewayPassword(),
"id" : message_id,
"type" : 'notify',
"schema" : 1 }
params = urllib.urlencode(params)
page = urllib.urlopen(base_url, params)
result = self._fetchStatusResponseAsDict(page)
if result['status'] == "OK":
row_list = result.get('status_info')
#return only status_text list
if len(row_list) == 1:
return row_list[0].get('status_text').lower()
else:
status_list = []
for row in row_list:
status_list.append(row.get('status_text').lower())
return status_list
elif result['status'] == "KO":
#we get an error when call the gateway
raise SMSGatewayError, urllib.unquote(result.get('status_info', "Impossible to get the message status"))
security.declarePublic('receive')
def receive(self,REQUEST):
"""Receive push notification from the gateway"""
#Get current user
sm = getSecurityManager()
try:
#Use SUPER_USER
portal_membership = self.getPortalObject().portal_membership
newSecurityManager(None, portal_membership.getMemberById(ERP5Security.SUPER_USER))
#Mobyt notify only new SMS
self.notifyReception(REQUEST.get("orig"),
REQUEST.get("text"),
REQUEST.get("ticket"))
finally:
#Restore orinal user
setSecurityManager(sm)
security.declareProtected(Permissions.ManagePortal, 'notifyReception')
def notifyReception(self, sender, text, message_id):
"""The gateway inform what we ha a new message.
"""
#Convert phone as erp5 compliant
def parsePhoneNumber(number):
#XXX: Should register well formatted number or brut number ?
#return number
return "+%s(%s)-%s" % (number[0:2],0,number[2:])
#Create the new sms in activities
self.activate(activity='SQLQueue').SMSTool_pushNewSMS(
message_id=message_id,
sender=parsePhoneNumber(sender),
recipient=None,
text_content=text,
message_type='text/plain',
reception_date=DateTime())
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Francois-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
# This file is kept for backward compatibility only
from Products.ERP5ShortMessage.SMSGatewayError import SMSGatewayError
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Francois-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
\ No newline at end of file
ERP5ShortMessage
The ERP5ShortMessage product provides support for sending or receiving SMS from
within the Zope environment.
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# Francois-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""
ERP5ShortMessage is a product containing everything needed to implement
Short Message management in ERP5.
"""# Update ERP5 Globals
from Products.ERP5Type.Utils import initializeProduct, updateGlobals
import sys, Permissions
this_module = sys.modules[ __name__ ]
document_classes = updateGlobals(this_module, globals(),
permissions_module=Permissions)
from Tool import SMSTool
# Define object classes and tools
object_classes = ()
portal_tools = (SMSTool.SMSTool,
)
content_classes = ()
content_constructors = ()
# Finish installation
def initialize(context):
import Document
initializeProduct(context, this_module, globals(),
document_module=Document,
document_classes=document_classes,
object_classes=object_classes,
portal_tools=portal_tools,
content_constructors=content_constructors,
content_classes=content_classes)
from AccessControl.SecurityInfo import allow_module
allow_module('Products.ERP5ShortMessage.Errors')
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<h3>Explain SMS Tool</h3>
<p>
SMS Tool provides all to send and receive sms with one or more sms gateway.
</p>
<p>
You need to have a SMS Gateway with reference egal to "default" to send sms.
Other reference can be used for specific send.
</p>
<dtml-var manage_page_footer>
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
# François-Xavier Algrain <fxalgrain@tiolive.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
# This file has been kept for backward compatiblity only
from Products.ERP5ShortMessage.interfaces.sms_receiving_gateway import ISmsReceivingGateway
from Products.ERP5ShortMessage.interfaces.sms_sending_gateway import ISmsSendingGateway
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