diff --git a/merling+.zip b/merling+.zip deleted file mode 100644 index 420c297..0000000 Binary files a/merling+.zip and /dev/null differ diff --git a/merlingPlus.zip b/merlingPlus.zip new file mode 100644 index 0000000..2584658 Binary files /dev/null and b/merlingPlus.zip differ diff --git a/updates/files/layouts/vlayout/modules/Vtiger/SendSMSForm.tpl b/updates/files/layouts/vlayout/modules/Vtiger/SendSMSForm.tpl new file mode 100644 index 0000000..ed27933 --- /dev/null +++ b/updates/files/layouts/vlayout/modules/Vtiger/SendSMSForm.tpl @@ -0,0 +1,76 @@ +{**} +{strip} + +{/strip} \ No newline at end of file diff --git a/updates/files/libraries/bootstrap3/css/bootstrap.min.css b/updates/files/libraries/bootstrap3/css/bootstrap.min.css index 78d7318..9f52ea3 100644 --- a/updates/files/libraries/bootstrap3/css/bootstrap.min.css +++ b/updates/files/libraries/bootstrap3/css/bootstrap.min.css @@ -4843,7 +4843,7 @@ select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.i .navbar-right { float: left!important; - margin-left: -15px; + margin-left: 15px; } .navbar-left~.navbar-right { diff --git a/updates/files/modules/SMSNotifier/SMSNotifier.php b/updates/files/modules/SMSNotifier/SMSNotifier.php new file mode 100644 index 0000000..5f728ab --- /dev/null +++ b/updates/files/modules/SMSNotifier/SMSNotifier.php @@ -0,0 +1,315 @@ +id; + } else { + $ownerid = 1; + } + } + + $moduleName = 'SMSNotifier'; + $focus = CRMEntity::getInstance($moduleName); + + $focus->column_fields['message'] = $message; + $focus->column_fields['assigned_user_id'] = $ownerid; + $focus->save($moduleName); + + if($linktoids !== false) { + + if($linktoModule !== false) { + relateEntities($focus, $moduleName, $focus->id, $linktoModule, $linktoids); + } else { + // Link modulename not provided (linktoids can belong to mix of module so determine proper modulename) + $linkidsetypes = $adb->pquery( "SELECT setype,crmid FROM vtiger_crmentity WHERE crmid IN (".generateQuestionMarks($linktoids) . ")", array($linktoids) ); + if($linkidsetypes && $adb->num_rows($linkidsetypes)) { + while($linkidsetypesrow = $adb->fetch_array($linkidsetypes)) { + relateEntities($focus, $moduleName, $focus->id, $linkidsetypesrow['setype'], $linkidsetypesrow['crmid']); + } + } + } + } + $responses = self::fireSendSMS($message, $sourceNumber, $tonumbers); + $focus->processFireSendSMSResponse($responses); + } + + /** + * Detect the related modules based on the entity relation information for this instance. + */ + function detectRelatedModules() { + + $adb = PearDatabase::getInstance(); $current_user = vglobal('current_user'); + + // Pick the distinct modulenames based on related records. + $result = $adb->pquery("SELECT distinct setype FROM vtiger_crmentity WHERE crmid in ( + SELECT relcrmid FROM vtiger_crmentityrel INNER JOIN vtiger_crmentity ON vtiger_crmentity.crmid=vtiger_crmentityrel.crmid + WHERE vtiger_crmentity.crmid = ? AND vtiger_crmentity.deleted=0)", array($this->id)); + + $relatedModules = array(); + + // Calculate the related module access (similar to getRelatedList API in DetailViewUtils.php) + if($result && $adb->num_rows($result)) { + require('user_privileges/user_privileges_'.$current_user->id.'.php'); + while($resultrow = $adb->fetch_array($result)) { + $accessCheck = false; + $relatedTabId = getTabid($resultrow['setype']); + if($relatedTabId == 0) { + $accessCheck = true; + } else { + if($profileTabsPermission[$relatedTabId] == 0) { + if($profileActionPermission[$relatedTabId][3] == 0) { + $accessCheck = true; + } + } + } + + if($accessCheck) { + $relatedModules[$relatedTabId] = $resultrow['setype']; + } + } + } + + return $relatedModules; + + } + + protected function isUserOrGroup($id) { + $adb = PearDatabase::getInstance(); + $result = $adb->pquery("SELECT 1 FROM vtiger_users WHERE id=?", array($id)); + if($result && $adb->num_rows($result)) { + return 'U'; + } else { + return 'T'; + } + } + + protected function smsAssignedTo() { + $adb = PearDatabase::getInstance(); + + // Determine the number based on Assign To + $assignedtoid = $this->column_fields['assigned_user_id']; + $type = $this->isUserOrGroup($assignedtoid); + + if($type == 'U'){ + $userIds = array($assignedtoid); + }else { + require_once('include/utils/GetGroupUsers.php'); + $getGroupObj=new GetGroupUsers(); + $getGroupObj->getAllUsersInGroup($assignedtoid); + $userIds = $getGroupObj->group_users; + } + + $tonumbers = array(); + + if(count($userIds) > 0) { + $phoneSqlQuery = "select phone_mobile, id from vtiger_users WHERE status='Active' AND id in(". generateQuestionMarks($userIds) .")"; + $phoneSqlResult = $adb->pquery($phoneSqlQuery, array($userIds)); + while($phoneSqlResultRow = $adb->fetch_array($phoneSqlResult)) { + $number = $phoneSqlResultRow['phone_mobile']; + if(!empty($number)) { + $tonumbers[] = $number; + } + } + } + + if(!empty($tonumbers)) { + $responses = self::fireSendSMS($this->column_fields['message'], $tonumbers); + $this->processFireSendSMSResponse($responses); + } + } + + private function processFireSendSMSResponse($responses) { + + if(empty($responses)) return; + + $adb = PearDatabase::getInstance(); + + foreach($responses as $response) { + $responseID = ''; + $responseStatus = ''; + $responseStatusMessage = ''; + + $needlookup = 1; + if($response['error']) { + $responseStatus = ISMSProvider::MSG_STATUS_FAILED; + $needlookup = 0; + } else { + $responseID = $response['id']; + $responseStatus = $response['status']; + } + + if(isset($response['statusmessage'])) { + $responseStatusMessage = $response['statusmessage']; + } + $adb->pquery("INSERT INTO vtiger_smsnotifier_status(smsnotifierid,tonumber,status,statusmessage,smsmessageid,needlookup) VALUES(?,?,?,?,?,?)", + array($this->id,$response['to'],$responseStatus,$responseStatusMessage,$responseID,$needlookup) + ); + } + } + + static function smsquery($record) { + $adb = PearDatabase::getInstance(); + $result = $adb->pquery("SELECT * FROM vtiger_smsnotifier_status WHERE smsnotifierid = ? AND needlookup = 1", array($record)); + if($result && $adb->num_rows($result)) { + $provider = SMSNotifierManager::getActiveProviderInstance(); + + while($resultrow = $adb->fetch_array($result)) { + $messageid = $resultrow['smsmessageid']; + + $response = $provider->query($messageid); + + if($response['error']) { + $responseStatus = ISMSProvider::MSG_STATUS_FAILED; + $needlookup = $response['needlookup']; + } else { + $responseStatus = $response['status']; + $needlookup = $response['needlookup']; + } + + $responseStatusMessage = ''; + if(isset($response['statusmessage'])) { + $responseStatusMessage = $response['statusmessage']; + } + + $adb->pquery("UPDATE vtiger_smsnotifier_status SET status=?, statusmessage=?, needlookup=? WHERE smsmessageid = ?", + array($responseStatus, $responseStatusMessage, $needlookup, $messageid)); + } + } + } + + + + static function fireSendSMS($message, $sourceNumber, $tonumbers) { + $log = vglobal('log'); + $provider = SMSNotifierManager::getActiveProviderInstance(); + if($provider) { + return $provider->send($message, $sourceNumber, $tonumbers); + } + } + + static function getSMSStatusInfo($record) { + $adb = PearDatabase::getInstance(); + $results = array(); + $qresult = $adb->pquery("SELECT * FROM vtiger_smsnotifier_status WHERE smsnotifierid=?", array($record)); + if($qresult && $adb->num_rows($qresult)) { + while($resultrow = $adb->fetch_array($qresult)) { + $results[] = $resultrow; + } + } + return $results; + } +} + +class SMSNotifierManager { + + /** Server configuration management */ + static function listAvailableProviders() { + return SMSNotifier_Provider_Model::listAll(); + } + + static function getActiveProviderInstance() { + $adb = PearDatabase::getInstance(); + $result = $adb->pquery("SELECT * FROM vtiger_smsnotifier_servers WHERE isactive = 1 LIMIT 1", array()); + if($result && $adb->num_rows($result)) { + $resultrow = $adb->fetch_array($result); + $provider = SMSNotifier_Provider_Model::getInstance($resultrow['providertype']); + $parameters = array(); + if(!empty($resultrow['parameters'])) $parameters = Zend_Json::decode(decode_html($resultrow['parameters'])); + foreach($parameters as $k=>$v) { + $provider->setParameter($k, $v); + } + $provider->setAuthParameters($resultrow['username'], $resultrow['password']); + + return $provider; + } + return false; + } + + static function listConfiguredServer($id) { + $adb = PearDatabase::getInstance(); + $result = $adb->pquery("SELECT * FROM vtiger_smsnotifier_servers WHERE id=?", array($id)); + if($result) { + return $adb->fetchByAssoc($result); + } + return false; + } + static function listConfiguredServers() { + $adb = PearDatabase::getInstance(); + $result = $adb->pquery("SELECT * FROM vtiger_smsnotifier_servers", array()); + $servers = array(); + if($result) { + while($row = $adb->fetchByAssoc($result)){ + $servers[] = $row; + } + } + return $servers; + } + static function updateConfiguredServer($id, $frmvalues) { + $adb = PearDatabase::getInstance(); + $providertype = vtlib_purify($frmvalues['smsserver_provider']); + $username = vtlib_purify($frmvalues['smsserver_username']); + $password = vtlib_purify($frmvalues['smsserver_password']); + $isactive = vtlib_purify($frmvalues['smsserver_isactive']); + + $provider = SMSNotifier_Provider_Model::getInstance($providertype); + + $parameters = ''; + if($provider) { + $providerParameters = $provider->getRequiredParams(); + $inputServerParams = array(); + foreach($providerParameters as $k=>$v) { + $lookupkey = "smsserverparam_{$providertype}_{$v}"; + if(isset($frmvalues[$lookupkey])) { + $inputServerParams[$v] = vtlib_purify($frmvalues[$lookupkey]); + } + } + $parameters = Zend_Json::encode($inputServerParams); + } + + if(empty($id)) { + $adb->pquery("INSERT INTO vtiger_smsnotifier_servers (providertype,username,password,isactive,parameters) VALUES(?,?,?,?,?)", + array($providertype, $username, $password, $isactive, $parameters)); + } else { + $adb->pquery("UPDATE vtiger_smsnotifier_servers SET username=?, password=?, isactive=?, providertype=?, parameters=? WHERE id=?", + array($username, $password, $isactive, $providertype, $parameters, $id)); + } + } + static function deleteConfiguredServer($id) { + $adb = PearDatabase::getInstance(); + $adb->pquery("DELETE FROM vtiger_smsnotifier_servers WHERE id=?", array($id)); + } +} diff --git a/updates/files/modules/SMSNotifier/actions/MassSaveAjax.php b/updates/files/modules/SMSNotifier/actions/MassSaveAjax.php new file mode 100644 index 0000000..6672733 --- /dev/null +++ b/updates/files/modules/SMSNotifier/actions/MassSaveAjax.php @@ -0,0 +1,61 @@ +getModule(); + $moduleModel = Vtiger_Module_Model::getInstance($moduleName); + + $currentUserPriviligesModel = Users_Privileges_Model::getCurrentUserPrivilegesModel(); + if(!$currentUserPriviligesModel->hasModuleActionPermission($moduleModel->getId(), 'Save')) { + throw new AppException(vtranslate($moduleName).' '.vtranslate('LBL_NOT_ACCESSIBLE')); + } + } + + /** + * Function that saves SMS records + * @param Vtiger_Request $request + */ + public function process(Vtiger_Request $request) { + $moduleName = $request->getModule(); + + $currentUserModel = Users_Record_Model::getCurrentUserModel(); + $recordIds = $this->getRecordsListFromRequest($request); + $phoneFieldList = $request->get('fields'); + $message = $request->get('message'); + $sourceNumber = $request->get('sourceNumber'); + + foreach($recordIds as $recordId) { + $recordModel = Vtiger_Record_Model::getInstanceById($recordId); + $numberSelected = false; + foreach($phoneFieldList as $fieldname) { + $fieldValue = $recordModel->get($fieldname); + if(!empty($fieldValue)) { + $toNumbers[] = $fieldValue; + $numberSelected = true; + } + } + if($numberSelected) { + $recordIds[] = $recordId; + } + } + + $response = new Vtiger_Response(); + + if(!empty($toNumbers)) { + SMSNotifier_Record_Model::SendSMS($message,$sourceNumber, $toNumbers, $currentUserModel->getId(), $recordIds, $moduleName); + $response->setResult(true); + } else { + $response->setResult(false); + } + return $response; + } +} diff --git a/updates/files/modules/SMSNotifier/models/ISMSProvider.php b/updates/files/modules/SMSNotifier/models/ISMSProvider.php new file mode 100644 index 0000000..3aed218 --- /dev/null +++ b/updates/files/modules/SMSNotifier/models/ISMSProvider.php @@ -0,0 +1,65 @@ + $type like SEND, PING, QUERY + */ + public function getServiceURL($type = false); + + /** + * Function to set authentication parameters + * @param $userName + * @param $password + */ + public function setAuthParameters($userName, $password); + + /** + * Function to set non-auth parameter. + * @param $key + * @param $value + */ + public function setParameter($key, $value); + + /** + * Function to handle SMS Send operation + * @param $message + * @param $toNumbers One or Array of numbers + */ + public function send($message, $sourceNumber, $toNumbers); + + /** + * Function to get query for status using messgae id + * @param $messageId + */ + public function query($messageId); + +} +?> diff --git a/updates/files/modules/SMSNotifier/models/Record.php b/updates/files/modules/SMSNotifier/models/Record.php new file mode 100644 index 0000000..70db0d9 --- /dev/null +++ b/updates/files/modules/SMSNotifier/models/Record.php @@ -0,0 +1,44 @@ +get('id')); + + $statusColor = $this->getColorForStatus($statusDetails[0]['status']); + + $this->setData($statusDetails[0]); + + return $this; + } + + public function getCheckStatusUrl() { + return "index.php?module=".$this->getModuleName()."&view=CheckStatus&record=".$this->getId(); + } + + public function getColorForStatus($smsStatus) { + if ($smsStatus == 'Processing') { + $statusColor = '#FFFCDF'; + } elseif ($smsStatus == 'Dispatched') { + $statusColor = '#E8FFCF'; + } elseif ($smsStatus == 'Failed') { + $statusColor = '#FFE2AF'; + } else { + $statusColor = '#FFFFFF'; + } + return $statusColor; + } +} diff --git a/updates/files/modules/SMSNotifier/providers/slng.php b/updates/files/modules/SMSNotifier/providers/slng.php new file mode 100644 index 0000000..0c2a50e --- /dev/null +++ b/updates/files/modules/SMSNotifier/providers/slng.php @@ -0,0 +1,198 @@ + provider name + */ + public function getName() { + return 'slng'; + } + + /** + * Function to get required parameters other than (userName, password) + * @return required parameters list + */ + public function getRequiredParams() { + return self::$REQUIRED_PARAMETERS; + } + + /** + * Function to get service URL to use for a given type + * @param $type like SEND, PING, QUERY + */ + public function getServiceURL($type = false) { + if($type) { + switch(strtoupper($type)) { + //case self::SERVICE_AUTH: return self::SERVICE_URI . '/http/auth'; + case self::SERVICE_SEND: return self::SERVICE_URI . '/CreateAndSendSmsToMultiContactsByMobileNo'; + //case self::SERVICE_QUERY: return self::SERVICE_URI . '/http/querymsg'; + } + } + return false; + } + + /** + * Function to set authentication parameters + * @param $userName + * @param $password + */ + public function setAuthParameters($userName, $password) { + $this->userName = $userName; + $this->password = $password; + } + + /** + * Function to set non-auth parameter. + * @param $key + * @param $value + */ + public function setParameter($key, $value) { + $this->parameters[$key] = $value; + } + + /** + * Function to get parameter value + * @param $key + * @param $defaultValue + * @return value/$default value + */ + public function getParameter($key, $defaultValue = false) { + if(isset($this->parameters[$key])) { + return $this->parameters[$key]; + } + return $defaultValue; + } + + /** + * Function to prepare parameters + * @return parameters + */ + protected function prepareParameters() { + $params = array('Username' => $this->userName, 'Password' => $this->password); + foreach (self::$REQUIRED_PARAMETERS as $key) { + $params[$key] = $this->getParameter($key); + } + return $params; + } + + /** + * Function to handle SMS Send operation + * @param $message + * @param $toNumbers One or Array of numbers + */ + public function send($message, $sourceNumber, $toNumbers) { + if(!is_array($toNumbers)) { + $toNumbers = array($toNumbers); + } + + $params = $this->prepareParameters(); + $params['Msg'] = $message; + if ($sourceNumber == null || $sourceNumber == "") + $params['FromMobile'] = $this->getParameter('SourceNumber'); + else + $params['FromMobile'] = $sourceNumber; + $params['GlobalID'] = '-1'; + $params['MsgName'] = 'FromVtiger'; + $params['Mobiles'] = implode(',', $toNumbers); + + $serviceURL = $this->getServiceURL(self::SERVICE_SEND); + $httpClient = new Vtiger_Net_Client($serviceURL); + $response = $httpClient->doPost($params); + $responseLines = split("\n", $response); + + $results = array(); + $i=0; + foreach($responseLines as $responseLine) { + $responseLine = trim($responseLine); + if(empty($responseLine)) continue; + + $result = array( 'error' => false, 'statusmessage' => '' ); + if(preg_match("/ERR:(.*)/", trim($responseLine), $matches)) { + $result['error'] = true; + $result['to'] = $toNumbers[$i++]; + $result['statusmessage'] = $matches[0]; // Complete error message + } else if(preg_match("/ID: ([^ ]+)TO:(.*)/", $responseLine, $matches)) { + $result['id'] = trim($matches[1]); + $result['to'] = trim($matches[2]); + $result['status'] = self::MSG_STATUS_PROCESSING; + } else if(preg_match("/ID: (.*)/", $responseLine, $matches)) { + $result['id'] = trim($matches[1]); + $result['to'] = $toNumbers[0]; + $result['status'] = self::MSG_STATUS_PROCESSING; + } + $results[] = $result; + } + return $results; + } + + /** + * Function to get query for status using messgae id + * @param $messageId + */ + public function query($messageId) { + $params = $this->prepareParameters(); + $params['apimsgid'] = $messageId; + + $serviceURL = $this->getServiceURL(self::SERVICE_QUERY); + $httpClient = new Vtiger_Net_Client($serviceURL); + $response = $httpClient->doPost($params); + $response = trim($response); + + $result = array( 'error' => false, 'needlookup' => 1, 'statusmessage' => '' ); + + if(preg_match("/ERR: (.*)/", $response, $matches)) { + $result['error'] = true; + $result['needlookup'] = 0; + $result['statusmessage'] = $matches[0]; + } else if(preg_match("/ID: ([^ ]+) Status: ([^ ]+)/", $response, $matches)) { + $result['id'] = trim($matches[1]); + $status = trim($matches[2]); + + // Capture the status code as message by default. + $result['statusmessage'] = "CODE: $status"; + + if($status == '002' || $status == '008' || $status == '011' ) { + $result['status'] = self::MSG_STATUS_PROCESSING; + } else if($status == '003' || $status == '004') { + $result['status'] = self::MSG_STATUS_DISPATCHED; + $result['needlookup'] = 0; + } else { + $statusMessage = ''; + switch($status) { + case '001': $statusMessage = 'Message unknown'; break; + case '005': $statusMessage = 'Error with message'; break; + case '006': $statusMessage = 'User cancelled message delivery'; break; + case '007': $statusMessage = 'Error delivering message'; break; + case '009': $statusMessage = 'Routing error'; break; + case '010': $statusMessage = 'Message expired'; break; + case '012': $statusMessage = 'Out of credit'; break; + } + if(!empty($statusMessage)) { + $result['error'] = true; + $result['needlookup'] = 0; + $result['statusmessage'] = $statusMessage; + } + } + } + return $result; + } +} +?>