Files
crm.clientright.ru/modules/ITS4YouEmailMarketing/models/Record.php
Fedor ac7467f0b4 Major CRM updates: AI Assistant, Court Status API, S3 integration improvements, and extensive file storage system
- Added comprehensive AI Assistant system (aiassist/ directory):
  * Vector search and embedding capabilities
  * Typebot proxy integration
  * Elastic search functionality
  * Message classification and chat history
  * MCP proxy for external integrations

- Implemented Court Status API (GetCourtStatus.php):
  * Real-time court document status checking
  * Integration with external court systems
  * Comprehensive error handling and logging

- Enhanced S3 integration:
  * Improved file backup system with metadata
  * Batch processing capabilities
  * Enhanced error logging and recovery
  * Copy operations with URL fixing

- Added Telegram contact creation API
- Improved error logging across all modules
- Enhanced callback system for AI responses
- Extensive backup file storage with timestamps
- Updated documentation and README files

- File storage improvements:
  * Thousands of backup files with proper metadata
  * Fix operations for broken file references
  * Project-specific backup and recovery systems
  * Comprehensive file integrity checking

Total: 26,461+ files added/modified including AWS SDK, vendor dependencies, and extensive backup system.
2025-10-16 11:17:21 +03:00

1433 lines
48 KiB
PHP

<?php
/* * *******************************************************************************
* The content of this file is subject to the ITS4YouEmailMarketing license.
* ("License"); You may not use this file except in compliance with the License
* The Initial Developer of the Original Code is IT-Solutions4You s.r.o.
* Portions created by IT-Solutions4You s.r.o. are Copyright(C) IT-Solutions4You s.r.o.
* All Rights Reserved.
* ****************************************************************************** */
class ITS4YouEmailMarketing_Record_Model extends Vtiger_Record_Model
{
public static $EMAILS_MODULE = 'ITS4YouEmails';
public static $OLD_EMAILS_MODULE = 'Emails';
public static $statusField = 'emailmarketingstatus';
public static $statusError = 'Error';
public static $VALID_EMAIL_STATUS = 'ValidEmail';
public static $EMPTY_EMAIL_STATUS = 'EmptyEmail';
public static $INVALID_EMAIL_STATUS = 'InvalidEmail';
public static $USED_EMAIL_STATUS = 'UsedEmail';
public static $UNSUBSCRIBED_EMAIL_STATUS = 'UnsubscribedUser';
public static $DUPLICATE_EMAIL_STATUS = 'DuplicateUser';
public static $SELECTED_EMAIL_STATUS = 'SelectedEmail';
protected $usedEmails = array();
protected $usedSubscribers = array();
protected $unSubscribedEmails;
protected $relatedRecords;
protected $emailTrack;
protected $previewEmail = false;
/**
* @var array
*/
protected $campaignTables = array(
'Contacts' => array(
'table' => 'vtiger_campaigncontrel',
'name' => 'contactid',
),
'Leads' => array(
'table' => 'vtiger_campaignleadrel',
'name' => 'leadid',
),
'Accounts' => array(
'table' => 'vtiger_campaignaccountrel',
'name' => 'accountid',
),
);
protected $templateSourceModules = array(
'Newsletter' => 'ITS4YouNewsletter',
'EMAILMaker' => 'EMAILMaker',
);
protected $sendToEmail = array();
protected $moduleModels = array();
protected $recordModels = array();
protected $content;
public static function getProcessedContent($content)
{
// remove script tags from whole html content
return preg_replace('#<script(.*?)>(.*?)</script>#is', '', $content);
}
public static function copyConfig($fromRecord, $toRecord)
{
/**
* @var ITS4YouEmailMarketing_Record_Model $duplicateRecord
* @var ITS4YouEmailMarketing_Record_Model $record
*/
$duplicateRecord = Vtiger_Record_Model::getInstanceById($fromRecord);
$modules = $duplicateRecord->getConfig();
$record = Vtiger_Record_Model::getInstanceById($toRecord);
foreach ($modules as $module => $fields) {
foreach ($fields as $field => $config) {
$record->setConfig($module, $field);
}
}
}
public function setConfig($module, $field)
{
$recordId = $this->getId();
$adb = PearDatabase::getInstance();
$adb->pquery('INSERT INTO its4you_emailmarketing_config (crmid, module, field) VALUES (?,?,?)', array($recordId, $module, $field));
}
/**
* @param string $mode
* @param int $fromRecordId
* @param int $toRecordId
* @return void
*/
public static function copyRecipients($mode, $fromRecordId, $toRecordId)
{
/** @var ITS4YouEmailMarketing_Record_Model $toRecord */
$toRecord = Vtiger_Record_Model::getInstanceById($toRecordId);
$toModule = $toRecord->getModule();
$subModules = $toRecord->getSubscribersModules();
$sql = 'SELECT * FROM vtiger_crmentityrel WHERE module=? AND crmid=? AND relmodule IN (' . generateQuestionMarks($subModules) . ') ';
$params = ['ITS4YouEmailMarketing', $fromRecordId, $subModules];
if ('NotOpened' === $mode) {
$sql .= ' AND relcrmid NOT IN (' . self::getAccessedRecipientIdsQuery($fromRecordId, false) . ') ';
}
if ('OpenRecipients' === $mode) {
$sql .= ' AND relcrmid IN (' . self::getAccessedRecipientIdsQuery($fromRecordId, false) . ') ';
}
$adb = PearDatabase::getInstance();
$result = $adb->pquery($sql, $params);
while ($row = $adb->fetchByAssoc($result)) {
$recipientId = intval($row['relcrmid']);
if (!$recipientId || !isRecordExists($recipientId)) {
continue;
}
$recipientRecord = Vtiger_Record_Model::getInstanceById($recipientId, $row['relmodule']);
/** @var Vtiger_Relation_Model $recipientRelation */
$recipientRelation = Vtiger_Relation_Model::getInstance($toModule, $recipientRecord->getModule());
if ($recipientRelation) {
$recipientRelation->addRelation($toRecord->getId(), $recipientRecord->getId());
}
}
}
/**
* @param string $marketingId
* @return string
*/
public static function getAccessedRecipientIdsQuery($marketingId, $accessCount = true)
{
$marketingId = intval($marketingId);
$accessCountQuery = $accessCount ? ', COUNT(recipient_id) as access_count' : '';
return 'SELECT recipient_id' . $accessCountQuery . '
FROM its4you_emails
INNER JOIN its4you_emails_access ON its4you_emails_access.mail_id=its4you_emails.its4you_emails_id
INNER JOIN vtiger_shorturls ON vtiger_shorturls.uid=its4you_emails_access.access_id
WHERE its4you_emails.related_to=' . $marketingId . ' AND vtiger_shorturls.handler_data LIKE "%open%" GROUP BY recipient_id';
}
public function setRelationsFromList($customViewId, $sourceModule, $entity)
{
$return = false;
$moduleModel = $this->getModule();
$recordId = $this->getId();
$recordIds = array();
if (vtlib_isModuleActive($sourceModule)) {
if ('Campaigns' == $sourceModule) {
if (isRecordExists($customViewId)) {
$recordIds = $this->getCampaignRelatedRecords($customViewId, $entity);
$sourceModule = $entity;
}
} else {
$customView = CustomView_Record_Model::getInstanceById($customViewId);
$recordIds = $customView->getRecordIds();
}
$sourceModel = Vtiger_Module_Model::getInstance($sourceModule);
$relationModel = Vtiger_Relation_Model::getInstance($moduleModel, $sourceModel);
foreach ($recordIds as $contactId) {
$relationModel->addRelation($recordId, $contactId);
}
$return = true;
}
return $return;
}
public function getCampaignRelatedRecords($record, $module)
{
$related = array();
if (vtlib_isModuleActive($module)) {
$adb = PearDatabase::getInstance();
$sql = $this->getCampaignQuery($module);
$result = $adb->pquery($sql, array($record));
$name = $this->campaignTables[$module]['name'];
while ($row = $adb->fetchByAssoc($result)) {
$related[] = $row[$name];
}
}
return $related;
}
/**
* @param string $module
* @return string
*/
public function getCampaignQuery($module)
{
return sprintf('SELECT * FROM %s WHERE campaignid=?', $this->campaignTables[$module]['table']);
}
/**
* @param Vtiger_Record_Model $recordModel
* @return void
*/
public function addRelation($recordModel)
{
$moduleModel = $this->getModule();
$relationModel = Vtiger_Relation_Model::getInstance($moduleModel, $recordModel->getModule());
if ($relationModel) {
$relationModel->addRelation($this->getId(), $recordModel->getId());
}
}
/**
* @param string $relatedName
* @param int|false $campaignId
* @return array
* @throws Exception
*/
public function getCustomViews($relatedName, $campaignId = false)
{
$views = array();
$customViewsPageCount = 0;
$customViewsPage = $this->getCustomViewPage();
if ('Campaigns' == $relatedName) {
if (!!$campaignId && isRecordExists($campaignId)) {
$emailRelatedModules = getEmailRelatedModules();
$campaignModel = Vtiger_Module_Model::getInstance($relatedName);
$campaignRelations = $campaignModel->getRelations();
foreach ($campaignRelations as $relation) {
$relTabid = $relation->get('related_tabid');
$parentModule = $relation->get('modulename');
$parentLabel = vtranslate($parentModule, $parentModule);
if (in_array($parentModule, $emailRelatedModules)) {
$countIds = $this->getCampaignCount($campaignId, $parentModule);
if ($countIds > 0) {
$views[$relTabid] = array(
'cvid' => $campaignId,
'viewname' => $parentLabel,
'entitytype' => $parentModule,
'countids' => $countIds,
'loaded' => $this->getCampaignLoadedCount($campaignId, $parentModule),
'url' => 'index.php?module=' . $relatedName . '&relatedModule=' . $parentModule . '&view=Detail&record=' . $campaignId . '&mode=showRelatedList',
);
}
}
}
} else {
$views = 'NOT_RECORD';
}
} else {
ITS4YouEmailMarketing_CustomView_Model::$custom_views_page = $customViewsPage;
$customViewsPageCount = ITS4YouEmailMarketing_CustomView_Model::getAllCount($relatedName);
$customViews = ITS4YouEmailMarketing_CustomView_Model::getAll($relatedName);
foreach ($customViews as $customView) {
$data = $customView->getData();
$cvId = $data['cvid'];
$cvCount = $this->getCustomViewCount($customView);
$views[$cvId] = array(
'cvid' => $cvId,
'viewname' => $data['viewname'],
'entitytype' => $data['entitytype'],
'countids' => $cvCount,
'loaded' => $this->getLoadedCount($customView),
'url' => 'index.php?module=' . $relatedName . '&view=List&viewname=' . $cvId,
);
}
}
if (empty($views)) {
$views = 'NOT_VIEWS';
}
return array(
'total_records' => $this->getSubscribersCount(),
'custom_views_pages' => ceil($customViewsPageCount / 10),
'custom_views_page' => $customViewsPage,
'views' => $views,
);
}
public function getCustomViewPage()
{
$value = $this->get('custom_views_page');
return !empty($value) ? $value : 1;
}
/**
* @parma int $record
* @param string $module
* @return int
* @throws Exception
*/
public function getCampaignCount($record, $module)
{
$adb = PearDatabase::getInstance();
$result = $adb->pquery($this->getCampaignCountQuery($record, $module), array($record));
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @param int $record
* @param string $module
* @return string
*/
public function getCampaignCountQuery($record, $module)
{
$tableId = implode('.', array($this->campaignTables[$module]['table'], $this->campaignTables[$module]['name']));
return sprintf('SELECT count(%s) as count FROM %s WHERE campaignid=?', $tableId, $this->campaignTables[$module]['table']);
}
/**
* @param int $record
* @param string $module
* @return int
* @throws Exception
*/
public function getCampaignLoadedCount($record, $module)
{
$adb = PearDatabase::getInstance();
$tableId = implode('.', array($this->campaignTables[$module]['table'], $this->campaignTables[$module]['name']));
$query = $this->getCampaignCountQuery($record, $module);
$query .= ' AND ' . $tableId . ' IN (SELECT relcrmid FROM vtiger_crmentityrel WHERE module=? AND crmid=? AND relcrmid=' . $tableId . ')';
$result = $adb->pquery($query, array($record, $this->getModuleName(), $this->getId()));
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @param object $view
* @return int
* @throws Exception
*/
public function getCustomViewCount($view)
{
$adb = PearDatabase::getInstance();
$result = $adb->pquery($this->getCustomViewCountQuery($view), array());
return intval($adb->query_result($result, 0, 'count'));
}
public function getCustomViewCountQuery($view)
{
$query = $this->getCustomViewQuery($view);
$split = preg_split('/\ FROM\ /', $query);
$split[0] = sprintf('SELECT count(%s.%s) as count', $view->getModule()->basetable, $view->getModule()->basetableid);
return implode(' FROM ', $split);
}
/**
* @param CustomView_Record_Model $view
* @return string
*/
public function getCustomViewQuery($view)
{
$cvId = $view->getId();
$moduleModel = $view->getModule();
$listViewModel = Vtiger_ListView_Model::getInstance($moduleModel->getName(), $cvId);
/** @var EnhancedQueryGenerator|QueryGenerator $queryGenerator */
$queryGenerator = $listViewModel->get('query_generator');
return $queryGenerator->getQuery();
}
/**
* @param object $view
* @return int
* @throws Exception
*/
public function getLoadedCount($view)
{
$adb = PearDatabase::getInstance();
$query = $this->getCustomViewCountQuery($view);
$tableId = implode('.', array($view->getModule()->basetable, $view->getModule()->basetableid));
$query .= sprintf(' AND %s IN (SELECT relcrmid FROM vtiger_crmentityrel WHERE module=? AND crmid=? AND relcrmid=%s)', $tableId, $tableId);
$result = $adb->pquery($query, array($this->getModuleName(), $this->getId()));
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @return int
* @throws Exception
*/
public function getSubscribersCount()
{
$adb = PearDatabase::getInstance();
$subscriberModules = $this->getSubscribersModules();
$query = 'SELECT count(vtiger_crmentityrel.relcrmid) AS count FROM vtiger_crmentityrel WHERE crmid=? AND module=? AND relmodule IN (' . generateQuestionMarks($subscriberModules) . ') ';
$params = [$this->getId(), $this->getModule()->getName(), $subscriberModules];
$result = $adb->pquery($query, $params);
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @return array
*/
public function getSubscribersModules()
{
return [$this->getEmailMarketingModule()];
}
/**
* @param bool $emails
* @return true
*/
public function deleteRelations($emails = false)
{
if ($emails) {
$relatedModules = ['Emails', 'ITS4YouEmails'];
} else {
$relatedModules = $this->getEmailMarketingModules();
}
$this->deleteRelationsForModules($relatedModules);
return true;
}
/**
* @return array
*/
public function getEmailMarketingModules()
{
$field = new ITS4YouEmailMarketing_Field_Model();
return $field->getEmailMarketingModules();
}
/**
* @param array $relatedModules
* @return void
*/
public function deleteRelationsForModules($relatedModules)
{
$module = $this->getModule()->getName();
$record = $this->getId();
$adb = PearDatabase::getInstance();
$adb->pquery('DELETE FROM vtiger_crmentityrel WHERE module=? AND crmid=? AND relmodule IN (' . generateQuestionMarks($relatedModules) . ') ', array($module, $record, $relatedModules));
}
public function getEmailFields()
{
$fields = array();
$emailRelated = getEmailRelatedModules();
foreach ($emailRelated as $moduleName) {
$emailFields = array();
$moduleModel = Vtiger_Module_Model::getInstance($moduleName);
$fieldModels = $moduleModel->getFields();
foreach ($fieldModels as $fieldName => $field) {
if ((strcasecmp($field->get('typeofdata'), 'e') === 0) || $field->get('uitype') === '13') {
$emailFields[$fieldName] = array(
'label' => vtranslate($field->get('label'), $moduleName),
'module' => $field->getModule()->getName(),
);
}
}
$fields[$moduleName] = $emailFields;
}
return $fields;
}
public function deleteConfig($module, $field)
{
$recordId = $this->getId();
$adb = PearDatabase::getInstance();
$adb->pquery('DELETE FROM its4you_emailmarketing_config WHERE crmid=? AND module=? AND field=?', array($recordId, $module, $field));
}
public function getEmailPreview($email)
{
$record = $this->getId();
$adb = PearDatabase::getInstance();
$result = $adb->pquery('SELECT * FROM its4you_emailmarketing_emails WHERE emailmarketingid=? AND emailsid=?', array($record, $email));
$row = $adb->fetchByAssoc($result, 0);
return $row['description'];
}
/**
* @return array
* @var int $limitStart
*/
public function getSendEmailRecords($limitStart = 0)
{
$limitEnd = $limitStart + $this->getCronLimit();
$related = array();
$adb = PearDatabase::getInstance();
$record = $this->getId();
$module = $this->getModule()->getName();
$query = 'SELECT relcrmid,relmodule
FROM vtiger_crmentityrel,vtiger_crmentity
WHERE vtiger_crmentityrel.relcrmid=vtiger_crmentity.crmid AND
vtiger_crmentity.deleted=? AND
vtiger_crmentityrel.module=? AND
vtiger_crmentityrel.crmid=? AND
vtiger_crmentityrel.relmodule != ? AND
vtiger_crmentityrel.relcrmid NOT IN (
SELECT DISTINCT REPLACE(idlists, "@1|", "") as idlists
FROM vtiger_crmentityrel,vtiger_emaildetails
WHERE vtiger_crmentityrel.relcrmid=vtiger_emaildetails.emailid AND
vtiger_crmentityrel.crmid = ? AND
vtiger_crmentityrel.module = ? AND
vtiger_crmentityrel.relmodule = ?
)
LIMIT ' . $limitStart . ',' . $limitEnd;
$params = ['0', $module, $record, 'Emails', $record, $module, 'Emails'];
$result = $adb->pquery($query, $params);
while ($row = $adb->fetchByAssoc($result)) {
$relId = $row['relcrmid'];
$related[$relId] = $row;
}
return $related;
}
/**
* @return int
*/
public function getCronLimit()
{
require_once 'vtlib/Vtiger/Cron.php';
$cron = Vtiger_Cron::getInstance($this->getModuleName());
if ($cron) {
$cronFrequency = $cron->getFrequency();
if ($cronFrequency) {
return ceil($cronFrequency / 3600 * $this->getSendHourly());
}
}
return $this->getSendHourly();
}
/**
* @return int
*/
public function getSendHourly()
{
return !$this->isEmpty('sendhourly') ? intval($this->get('sendhourly')) : 1000;
}
/**
* @return int
* @throws Exception
*/
public function getSendEmailCount()
{
$adb = PearDatabase::getInstance();
$query = str_replace('SELECT * ', 'SELECT count(relcrmid) as count ', $this->getEmailRelSql());
$result = $adb->pquery($query, [$this->getId(), $this->getEmailsModule()]);
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @return string
*/
public function getEmailRelSql()
{
return 'SELECT * FROM vtiger_crmentityrel,its4you_emails,vtiger_crmentity
WHERE vtiger_crmentityrel.relcrmid=vtiger_crmentity.crmid
AND vtiger_crmentityrel.relcrmid=its4you_emails.its4you_emails_id
AND vtiger_crmentity.deleted=0
AND vtiger_crmentityrel.crmid=?
AND vtiger_crmentityrel.relmodule=? ';
}
public function getEmailsModule()
{
if ($this->isEmpty('emails_module')) {
$this->set('emails_module', self::$OLD_EMAILS_MODULE);
}
return $this->get('emails_module');
}
/**
* @return array
* @throws Exception
*/
public function getToEmailInfo()
{
$this->sendToEmail = array();
$relatedRecords = $this->getRecipientRecords();
foreach ($relatedRecords as $relatedRecord) {
$relCrmId = $relatedRecord['relcrmid'];
$relModule = $relatedRecord['relmodule'];
$recipientRecord = $this->getRecordModel($relCrmId, $relModule);
$this->sendToEmail[$relCrmId] = [
'status' => self::$SELECTED_EMAIL_STATUS,
'module' => $relModule,
'id' => $relCrmId,
'email' => '',
];
if (!$this->validateSubscriber($relCrmId)) {
$this->sendToEmail[$relCrmId]['status'] = self::$DUPLICATE_EMAIL_STATUS;
$this->usedSubscribers[] = $relCrmId;
} elseif (!$recipientRecord->get('emailoptout')) {
foreach ($this->getConfig($relModule) as $fieldName => $singleConfig) {
$email = $recipientRecord->get($fieldName);
if (empty($email)) {
$this->sendToEmail[$relCrmId]['status'] = self::$EMPTY_EMAIL_STATUS;
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->sendToEmail[$relCrmId]['status'] = self::$INVALID_EMAIL_STATUS;
} elseif (!$this->validateEmail($email)) {
$this->sendToEmail[$relCrmId]['status'] = self::$USED_EMAIL_STATUS;
} else {
$this->sendToEmail[$relCrmId]['status'] = self::$VALID_EMAIL_STATUS;
$this->sendToEmail[$relCrmId]['email'] = $email;
$this->usedEmails[] = $email;
$this->usedSubscribers[] = $relCrmId;
break;
}
}
} else {
$this->sendToEmail[$relCrmId]['status'] = self::$UNSUBSCRIBED_EMAIL_STATUS;
}
}
return $this->sendToEmail;
}
public function deleteAllRelations()
{
$adb = PearDatabase::getInstance();
$adb->pquery('DELETE FROM vtiger_crmentityrel WHERE crmid=?', [$this->getId()]);
}
public function delete()
{
$this->deleteAllRelations();
$this->getModule()->deleteRecord($this);
}
/**
* @throws Exception
*/
public function getRecipientRecordsLimit()
{
$cronLimit = $this->getCronLimit();
$sentEmailsThisHour = $this->getSentEmailsThisHour();
$sendHourly = $this->getSendHourly();
$limit = $cronLimit;
if ($cronLimit + $sentEmailsThisHour > $sendHourly) {
$limit = $sendHourly - $sentEmailsThisHour;
if (1 > $limit) {
$limit = 1;
}
}
return $limit;
}
/**
* @return array
* @throws Exception
* @var int $limitStart
*/
public function getRecipientRecords()
{
$limit = $this->getRecipientRecordsLimit();
$related = array();
$adb = PearDatabase::getInstance();
$record = $this->getId();
$module = $this->getModule()->getName();
$subscribersModule = $this->getEmailMarketingModule();
$query = 'SELECT relcrmid,relmodule
FROM vtiger_crmentityrel as recipientRel
LEFT JOIN vtiger_crmentity as recipientDetail ON recipientRel.relcrmid=recipientDetail.crmid
WHERE recipientDetail.deleted=?
AND recipientRel.module=?
AND recipientRel.crmid=?
AND recipientRel.relmodule=?
AND recipientRel.relcrmid NOT IN (SELECT recipient_id FROM its4you_emails WHERE related_to=?)
LIMIT ' . $limit;
$params = ['0', $module, $record, $subscribersModule, $record];
$result = $adb->pquery($query, $params);
while ($row = $adb->fetchByAssoc($result)) {
$relId = $row['relcrmid'];
$related[$relId] = $row;
}
return $related;
}
public function getEmailMarketingModule()
{
return $this->get('emailmarketingmodule');
}
public function validateSubscriber($record)
{
if (in_array($record, $this->usedSubscribers) || $this->isUsedSubscriber($record)) {
return false;
}
return true;
}
/**
* @param int $record
* @return bool
*/
public function isUsedSubscriber($record)
{
$adb = PearDatabase::getInstance();
$query = 'SELECT recipient_id FROM its4you_emails WHERE related_to=? AND recipient_id = ?';
$params = [$this->getId(), $record];
$result = $adb->pquery($query, $params);
return (bool)$adb->num_rows($result);
}
public function getRecordModel($record, $module)
{
if (!isset($this->recordModels[$record])) {
$this->recordModels[$record] = Vtiger_Record_Model::getInstanceById($record, $this->getModuleModel($module));
}
return $this->recordModels[$record];
}
public function getModuleModel($module)
{
if (!isset($this->moduleModels[$module])) {
$this->moduleModels[$module] = Vtiger_Module_Model::getInstance($module);
}
return $this->moduleModels[$module];
}
/**
* @param string|bool $module
* @return array
*/
public function getConfig($module = false)
{
$adb = PearDatabase::getInstance();
$query = 'SELECT * FROM its4you_emailmarketing_config WHERE crmid=? ';
$params = [$this->getId()];
if ($module) {
$query .= ' AND module=? ';
array_push($params, $module);
}
$result = $adb->pquery($query, $params);
$config = [];
while ($row = $adb->fetchByAssoc($result)) {
$config[$row['module']][$row['field']] = $row;
}
return $module ? $config[$module] : $config;
}
public function validateEmail($email)
{
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL) || in_array($email, $this->usedEmails) || $this->isUsedEmail($email)) {
return false;
}
return true;
}
/**
* @param string $email
* @return bool
*/
public function isUsedEmail($email)
{
$adb = PearDatabase::getInstance();
$query = 'SELECT to_email FROM its4you_emails WHERE related_to=? AND to_email LIKE ? AND recipient_id > 0';
$params = [$this->getId(), '%' . $email . '%'];
$result = $adb->pquery($query, $params);
return (bool)$adb->num_rows($result);
}
/**
* @return bool
* @throws Exception
*/
public function isAllowedLimit()
{
return $this->getSendHourly() > $this->getSentEmailsThisHour();
}
/**
* @throws Exception
*/
public function getSentEmailsThisHour()
{
$recordId = $this->getId();
$adb = PearDatabase::getInstance();
$dateTo = date('Y-m-d H:i:s');
$dateFrom = date('Y-m-d H:i:s', strtotime('-1 hour'));
$sql = 'SELECT count(vtiger_crmentityrel.crmid) as count
FROM vtiger_crmentity,vtiger_crmentityrel
WHERE vtiger_crmentity.crmid=vtiger_crmentityrel.relcrmid
AND vtiger_crmentity.createdtime >= ?
AND vtiger_crmentity.createdtime <= ?
AND vtiger_crmentityrel.crmid=?
AND vtiger_crmentity.setype=?
GROUP BY vtiger_crmentityrel.crmid';
$result = $adb->pquery($sql, [$dateFrom, $dateTo, $recordId, $this->getEmailsModule()]);
return intval($adb->query_result($result, 0, 'count'));
}
public function isVtigerEmails()
{
return self::$EMAILS_MODULE !== $this->getEmailsModule();
}
/**
* @return array
* @throws Exception
*/
public function getEmailTrack()
{
$adb = PearDatabase::getInstance();
$sql = 'SELECT access_count,click_count FROM its4you_emails WHERE related_to = ? AND recipient_id > 0';
$result = $adb->pquery($sql, [$this->getId()]);
$access = $unique = $click = 0;
$opened = array();
$subscribers = array();
while ($row = $adb->fetchByAssoc($result)) {
if ($row['access_count'] >= 1) {
$unique += 1;
array_push($opened, $row['mailid']);
}
array_push($subscribers, $row['crmid']);
$access += $row['access_count'];
$click += $row['click_count'];
}
return array(
'unique' => $unique,
'access' => $access,
'click' => $click,
'open' => $opened,
'subscribers' => $subscribers,
'notopen' => $this->getSendEmailsCount() - $unique,
);
}
/**
* @return int
* @throws Exception
*/
public function getSendEmailsCount()
{
$adb = PearDatabase::getInstance();
$result = $adb->pquery(
'SELECT count(recipient_id) AS count FROM its4you_emails WHERE related_to = ? AND recipient_id > 0',
[$this->getId()]
);
return intval($adb->query_result($result, 0, 'count'));
}
/**
* @param string $email
* @param int $recipientId
* @param array $emailInfo
* @return array
* @throws Exception
*/
public function sendEmail($email, $recipientId, $emailInfo = array())
{
$recipientModule = getSalesEntityType($recipientId);
if ($this->isPreviewEmail()) {
$assignedUserId = Users_Record_Model::getCurrentUserModel()->getId();
} else {
$assignedUserId = $this->get('assigned_user_id');
}
$assignedUserId = $this->get('assigned_user_id');
$smtp = $this->get('emailmarketingsmtp');
$moduleModel = $this->getModule();
$moduleName = $moduleModel->getName();
$replyTo = $this->get('replyto');
$fromEmail = $this->get('fromemail');
$subject = $this->get('subject');
$images = array();
$content = '';
$success = true;
$message = vtranslate('LBL_SEND_EMAIL_ERROR', $moduleName);
/** Required include before EMAILMaker on multiple simple_html_dom*/
include_once 'modules/Emails/models/Mailer.php';
$recipientRecord = $this->getRecordModel($recipientId, $recipientModule);
$contentTemplate = ITS4YouEmailMarketing_Content_Template::getInstanceFromMarketing($this);
if ($contentTemplate) {
$content = $contentTemplate->getContent($recipientRecord);
$images = $contentTemplate->getImages();
} else {
$message = vtranslate('LBL_MISSING_CONTENT', $this->getModuleName());
$this->set(self::$statusField, self::$statusError);
$this->setErrorMessage(vtranslate('LBL_MISSING_CONTENT', $this->getModuleName()));
return array(
'success' => false,
'message' => $message,
);
}
$flag = self::$UNSUBSCRIBED_EMAIL_STATUS === $emailInfo['status'] ? 'UNSUBSCRIBED' : 'SAVED';
/** @var ITS4YouEmails_Record_Model $emailRecord */
$emailRecord = ITS4YouEmails_Record_Model::getCleanInstance(self::$EMAILS_MODULE);
$emailRecord->set('assigned_user_id', $assignedUserId);
$emailRecord->set('subject', $subject);
$emailRecord->set('body', $content);
$emailRecord->set('email_flag', $flag);
$emailRecord->set('related_to', $this->getId());
$emailRecord->set('smtp', $smtp);
if (!$this->isPreviewEmail()) {
$emailRecord->set('recipient_id', $recipientId);
}
$emailRecord->set('from_email', $fromEmail);
$emailRecord->set('from_email_ids', $assignedUserId . '|' . $fromEmail . '|Users');
if (!empty($replyTo)) {
$emailRecord->set('reply_email', $replyTo);
$emailRecord->set('reply_email_ids', $assignedUserId . '|' . $replyTo . '|Users');
}
$emailRecord->set('to_email', $email);
$emailRecord->set('to_email_ids', implode('|', [$recipientId, $email, $recipientModule]));
$emailRecord->set('result', vtranslate($emailInfo['status'], $this->getModuleName()));
$emailRecord->save();
$this->setDocumentsToEmails($emailRecord);
if (self::$VALID_EMAIL_STATUS === $emailInfo['status']) {
/** @var ITS4YouEmails_Record_Model $emailRecord */
$emailRecord = ITS4YouEmails_Record_Model::getInstanceById($emailRecord->getId(), self::$EMAILS_MODULE);
$emailRecord->set('images', $images);
$emailRecord->set('from_name', $this->getFromName());
$emailRecord->set('reply_name', $this->getFromName());
$emailRecord->set('skip_save_email_to_sent_folder', true);
$this->setUnsubscribeToEmails($emailRecord);
$emailRecord->send();
}
if ($this->isPreviewEmail()) {
$this->deleteRelation($emailRecord);
} else {
$this->addRelation($emailRecord);
}
$success = 'SENT' === (string)$emailRecord->get('email_flag');
$message = $emailRecord->get('result');
if (!$success) {
$this->setErrorMessage($message);
}
return array(
'success' => $success,
'message' => $message,
);
}
/**
* @param ITS4YouEmails_Record_Model $emailRecord
* @return void
* @throws Exception
*/
public function setUnsubscribeToEmails($emailRecord)
{
if ($emailRecord->isEmpty('recipient_id')) {
return;
}
$emailRecord->getMailer()->addCustomHeader(
'List-Unsubscribe',
'<' . (new ITS4YouEmailMarketing_Module_Model())->getUnsubscribeLink($emailRecord->get('recipient_id')) . '>,<mailto:' . $this->get('from_email') . '?subject=Unsubscribe>'
);
$emailRecord->getMailer()->addCustomHeader(
'List-Unsubscribe-Post',
'List-Unsubscribe=One-Click'
);
}
public function isPreviewEmail()
{
return $this->previewEmail;
}
public function setPreviewEmail()
{
$this->previewEmail = true;
}
/**
* @return object|ITS4YouEmailMarketing_Content_Template
*/
public function getContent()
{
if (empty($this->content)) {
$this->retrieveContent();
}
return $this->content;
}
/**
* @param object|ITS4YouEmailMarketing_Content_Template $value
*/
public function setContent($value)
{
$this->content = $value;
}
public function retrieveContent()
{
$this->setContent(ITS4YouEmailMarketing_Content_Template::getInstanceFromMarketing($this));
}
public function setErrorMessage($message)
{
$oldMessage = !$this->isEmpty('errormessage') ? "\n" . $this->get('errormessage') : '';
$this->set('id', $this->getId());
$this->set('mode', 'edit');
$this->set('errormessage', print_r($message, true) . $oldMessage);
$this->save();
}
public function save()
{
$send = $this->get('emailmarketingsend');
$status = $this->get('emailmarketingstatus');
if ('Scheduled' === $send && 'Created' === $status) {
$this->set('emailmarketingstatus', 'Ready');
}
$this->getModule()->saveRecord($this);
}
/**
* @param ITS4YouEmails_Record_Model $emailRecord
* @return void
*/
public function setDocumentsToEmails($emailRecord)
{
foreach ($this->getDocumentIds() as $documentId) {
$emailRecord->saveDocumentRelation($documentId);
}
}
public function getDocumentIds()
{
$adb = PearDatabase::getInstance();
$result = $adb->pquery(
'SELECT vtiger_crmentity.crmid AS document_id FROM vtiger_senotesrel
INNER JOIN vtiger_crmentity ON vtiger_senotesrel.notesid = vtiger_crmentity.crmid AND vtiger_senotesrel.crmid = ?
INNER JOIN vtiger_notes ON vtiger_notes.notesid = vtiger_senotesrel.notesid
INNER JOIN vtiger_seattachmentsrel ON vtiger_seattachmentsrel.crmid = vtiger_notes.notesid
INNER JOIN vtiger_attachments ON vtiger_attachments.attachmentsid = vtiger_seattachmentsrel.attachmentsid
WHERE vtiger_crmentity.deleted = 0',
array($this->getId())
);
$documentIds = [];
while ($row = $adb->fetchByAssoc($result)) {
$documentIds[] = $row['document_id'];
}
return $documentIds;
}
public function getFromName()
{
return decode_html($this->get('fromname'));
}
/**
* @param Vtiger_Record_Model $recordModel
* @return void
*/
public function deleteRelation($recordModel)
{
$moduleModel = $this->getModule();
/** @var Vtiger_Relation_Model $relationModel */
$relationModel = Vtiger_Relation_Model::getInstance($moduleModel, $recordModel->getModule());
if ($relationModel) {
$relationModel->deleteRelation($this->getId(), $recordModel->getId());
}
}
public function retrieveSourceTemplateDocuments()
{
$contentTemplate = ITS4YouEmailMarketing_Content_Template::getInstanceFromMarketing($this);
if ($contentTemplate) {
$documentsIds = $contentTemplate->getDocumentsIds();
foreach ($documentsIds as $documentId) {
$documentRecord = Vtiger_Record_Model::getInstanceById($documentId);
if ($documentRecord) {
$this->addRelation($documentRecord);
}
}
}
}
public function deleteRelationByModule()
{
$relatedModules = array_diff($this->getEmailMarketingModules(), [$this->get('emailmarketingmodule')]);
$this->deleteRelationsForModules($relatedModules);
}
public function updateEmailFlag($emailRecordId, $flag = 'Error')
{
$db = PearDatabase::getInstance();
$query = 'UPDATE vtiger_emaildetails SET email_flag=? WHERE emailid=?';
$db->pquery($query, array($flag, $emailRecordId));
}
/**
* @param string $value
* @return bool
*/
public function isStatus($value)
{
return $value === $this->get('emailmarketingstatus');
}
public function updateStatus($status = false)
{
$this->set('id', $this->getId());
$this->set('mode', 'edit');
if ($status !== false) {
$newStatus = $status;
} elseif ($this->isReadyToSendEmail()) {
$newStatus = 'Stop';
} else {
$newStatus = 'Ready';
}
$deliveryDate = $this->get('deliverydate');
$deliveryTime = $this->get('deliverytime');
if (!$deliveryDate || !$deliveryTime) {
$dateNow = $this->getDateNow();
$date = date('Y-m-d', $dateNow);
$time = date('H:i:s', $dateNow);
$this->set('deliverydate', $date);
$this->set('deliverytime', $time);
}
$this->set('emailmarketingstatus', $newStatus);
$this->save();
}
public function isReadyToSendEmail()
{
$ready = false;
$time = $this->get('deliverytime');
$date = $this->get('deliverydate');
if (!empty($time) && !empty($date)) {
$status = $this->get('emailmarketingstatus');
$dateNow = $this->getDateNow();
$start = $date . ' ' . $time;
$dateStart = strtotime($start);
if (in_array($status, ['Sending', 'Ready'])) {
if ($dateNow >= $dateStart) {
$ready = true;
}
}
}
return $ready;
}
/**
* @return int
*/
public function getDateNow()
{
$dateNow = date('Y-m-d H:i:s');
$DateTimeField = new DateTimeField($dateNow);
$dateNow = $DateTimeField->getDisplayDateTimeValue();
return strtotime($dateNow);
}
public function getErrorMessage()
{
return $this->get('errormessage');
}
/**
* @throws Exception
*/
public function getKeyMetrics()
{
$track = $this->getEmailTrack();
$emailsCount = $this->getSendEmailsCount();
$progressOpen = $emailsCount > 0 ? round($track['unique'] / $emailsCount * 100) : 0;
$keyMetric = array(
'subscribers' => array(
'LBL_STATUS' => vtranslate($this->get('emailmarketingstatus'), $this->getModuleName()),
'LBL_SELECT_SUBSCRIBERS' => $this->getSubscribersCount(),
'LBL_SENT_EMAILS' => $emailsCount,
'LBL_UNSUBSCRIBED' => $this->getUnsubscribed(),
),
'emailsinfo' => array(
'LBL_UNIQUE_OPEN' => $track['unique'],
'LBL_CLICK_COUNT' => $track['click'],
'LBL_ACCESS_COUNT' => $track['access'],
'LBL_NOT_OPEN' => $track['notopen'],
),
);
if (6 === intval(Vtiger_Version::current())) {
$keyMetric['emailsinfo']['LBL_EMAIL_PER_OPEN'] = $progressOpen . ' %';
unset($keyMetric['emailsinfo']['LBL_CLICK_COUNT']);
}
return $keyMetric;
}
/**
* @param bool $module
* @return int
* @throws Exception
*/
public function getUnsubscribed($module = false)
{
$unsubscribed = 0;
if ($module) {
$moduleModel = Vtiger_Module_Model::getInstance($module);
$field = Vtiger_Field_Model::getInstance('emailoptout', $moduleModel);
if ($field) {
$adb = PearDatabase::getInstance();
$query = 'SELECT count(recipient_id) as count FROM its4you_emails WHERE email_flag = ? AND related_to = ? AND recipient_id > 0';
$result = $adb->pquery($query, ['UNSUBSCRIBED', $this->getId()]);
$unsubscribed = $adb->query_result($result, 0, 'count');
}
} else {
$modules = $this->getRelatedRecordsModules();
foreach ($modules as $module) {
$unsubscribed += $this->getUnsubscribed($module);
}
}
return intval($unsubscribed);
}
public function getRelatedRecordsModules()
{
$related = array();
$adb = PearDatabase::getInstance();
$record = $this->getId();
$query = 'SELECT DISTINCT relmodule FROM vtiger_crmentityrel WHERE crmid=? AND relmodule NOT IN (?,?)';
$result = $adb->pquery($query, array($record, 'Emails', 'ITS4YouEmails'));
while ($row = $adb->fetchByAssoc($result)) {
array_push($related, $row['relmodule']);
}
return $related;
}
/**
* @return array
* @throws Exception
*/
public function getProgress()
{
$progress = $openEmails = false;
$emails = $this->getSendEmailsCount();
$status = [1 => 'Ready', 2 => 'Sending', 3 => 'Finish'];
$status_num = 1;
if ($emails) {
$subscribers = $this->getSubscribersCount();
$emailTrack = $this->getEmailTrack();
if ($subscribers) {
$emStatus = $this->get('emailmarketingstatus');
$status_num = array_search($emStatus, $status) ?: 1;
$progress = 'Finish' === $emStatus ? 100 : ceil($emails / $subscribers * 100);
}
if ($emailTrack['unique']) {
$openEmails = 100 / $emails * intval($emailTrack['unique']);
}
}
return [
'already_sent' => $progress,
'open_emails' => ceil($openEmails),
'status' => $status,
'status_num' => $status_num,
];
}
public function isFinished()
{
return 'Finish' === $this->get('emailmarketingstatus');
}
/**
* @return array
*/
public function getBreadCrumbLabels()
{
$breadCrumbLabels = [
'CampaignDetails' => 'LBL_CAMPAIGN_DETAILS',
'SelectSubscribers' => 'LBL_SELECT_SUBSCRIBERS',
'SelectTemplate' => 'LBL_SELECT_TEMPLATE',
'TemplateRecords' => 'LBL_TEMPLATE_RECORDS',
'Schedule' => 'LBL_SEND_CAMPAIGN',
];
if (!$this->isRecordBlockTemplate()) {
unset($breadCrumbLabels['TemplateRecords']);
}
return $breadCrumbLabels;
}
/**
* @return bool
*/
public function isRecordBlockTemplate()
{
return $this->getContent() ? $this->getContent()->isAllowedRecordsBlock() : false;
}
/**
* @return array|false
*/
public function getRecordsBlockModules()
{
if ($this->getContent()) {
$blocks = $this->getContent()->getBlockData('recordsblock');
$modules = array();
foreach ($blocks as $block) {
if (vtlib_isModuleActive($block['data-module'])) {
array_push($modules, $block['data-module']);
}
}
return array_unique($modules);
}
return false;
}
public function retrieveTemplateSource()
{
if (!$this->isEmpty('templatesource')) {
return;
}
foreach ($this->templateSourceModules as $source => $module) {
if (vtlib_isModuleActive($module)) {
$this->set('templatesource', $source);
}
}
}
public function retrieveTemplateRelation()
{
if (!$this->isEmpty('templatesource') && !$this->isEmpty('template')) {
$this->set('templaterelation', $this->get('templatesource') . ':' . $this->get('template'));
}
}
}