PageRenderTime 48ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/app/bundles/PointBundle/Model/TriggerModel.php

https://gitlab.com/mautic-master/mautic
PHP | 439 lines | 269 code | 55 blank | 115 comment | 35 complexity | 89ccc900b646b7eac28cbed18d8b70e3 MD5 | raw file
  1. <?php
  2. /**
  3. * @package Mautic
  4. * @copyright 2014 Mautic Contributors. All rights reserved.
  5. * @author Mautic
  6. * @link http://mautic.org
  7. * @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
  8. */
  9. namespace Mautic\PointBundle\Model;
  10. use Mautic\CoreBundle\Helper\DateTimeHelper;
  11. use Mautic\CoreBundle\Helper\IpLookupHelper;
  12. use Mautic\CoreBundle\Model\FormModel as CommonFormModel;
  13. use Mautic\LeadBundle\Entity\Lead;
  14. use Mautic\LeadBundle\Model\LeadModel;
  15. use Mautic\PointBundle\Entity\LeadTriggerLog;
  16. use Mautic\PointBundle\Entity\Trigger;
  17. use Mautic\PointBundle\Entity\TriggerEvent;
  18. use Mautic\PointBundle\Event as Events;
  19. use Mautic\PointBundle\PointEvents;
  20. use Symfony\Component\EventDispatcher\Event;
  21. use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
  22. /**
  23. * Class TriggerModel
  24. */
  25. class TriggerModel extends CommonFormModel
  26. {
  27. /**
  28. * @deprecated Remove in 2.0
  29. *
  30. * @var MauticFactory
  31. */
  32. protected $factory;
  33. /**
  34. * @var IpLookupHelper
  35. */
  36. protected $ipLookupHelper;
  37. /**
  38. * @var LeadModel
  39. */
  40. protected $leadModel;
  41. /**
  42. * @var TriggerEventModel
  43. */
  44. protected $pointTriggerEventModel;
  45. /**
  46. * EventModel constructor.
  47. *
  48. * @param IpLookupHelper $ipLookupHelper
  49. * @param LeadModel $leadModel
  50. * @param TriggerEventModel $pointTriggerEventModel
  51. */
  52. public function __construct(IpLookupHelper $ipLookupHelper, LeadModel $leadModel, TriggerEventModel $pointTriggerEventModel)
  53. {
  54. $this->ipLookupHelper = $ipLookupHelper;
  55. $this->leadModel = $leadModel;
  56. $this->pointTriggerEventModel = $pointTriggerEventModel;
  57. }
  58. /**
  59. * {@inheritdoc}
  60. *
  61. * @return \Mautic\PointBundle\Entity\TriggerRepository
  62. */
  63. public function getRepository()
  64. {
  65. return $this->em->getRepository('MauticPointBundle:Trigger');
  66. }
  67. /**
  68. * Retrieves an instance of the TriggerEventRepository
  69. *
  70. * @return \Mautic\PointBundle\Entity\TriggerEventRepository
  71. */
  72. public function getEventRepository()
  73. {
  74. return $this->em->getRepository('MauticPointBundle:TriggerEvent');
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public function getPermissionBase()
  80. {
  81. return 'point:triggers';
  82. }
  83. /**
  84. * {@inheritdoc}
  85. *
  86. * @throws MethodNotAllowedHttpException
  87. */
  88. public function createForm($entity, $formFactory, $action = null, $options = array())
  89. {
  90. if (!$entity instanceof Trigger) {
  91. throw new MethodNotAllowedHttpException(array('Trigger'));
  92. }
  93. $params = (!empty($action)) ? array('action' => $action) : array();
  94. return $formFactory->create('pointtrigger', $entity, $params);
  95. }
  96. /**
  97. * {@inheritdoc}
  98. *
  99. * @param \Mautic\PointBundle\Entity\Trigger $entity
  100. * @param bool $unlock
  101. */
  102. public function saveEntity($entity, $unlock = true)
  103. {
  104. $isNew = ($entity->getId()) ? false : true;
  105. parent::saveEntity($entity, $unlock);
  106. //should we trigger for existing leads?
  107. if ($entity->getTriggerExistingLeads() && $entity->isPublished()) {
  108. $events = $entity->getEvents();
  109. $repo = $this->getEventRepository();
  110. $persist = array();
  111. $ipAddress = $this->ipLookupHelper->getIpAddress();
  112. foreach ($events as $event) {
  113. $filter = array('force' => array(
  114. array(
  115. 'column' => 'l.date_added',
  116. 'expr' => 'lte',
  117. 'value' => (new DateTimeHelper($entity->getDateAdded()))->toUtcString()
  118. ),
  119. array(
  120. 'column' => 'l.points',
  121. 'expr' => 'gte',
  122. 'value' => $entity->getPoints()
  123. )
  124. ));
  125. if (!$isNew) {
  126. //get a list of leads that has already had this event applied
  127. $leadIds = $repo->getLeadsForEvent($event->getId());
  128. if (!empty($leadIds)) {
  129. $filter['force'][] = array(
  130. 'column' => 'l.id',
  131. 'expr' => 'notIn',
  132. 'value' => $leadIds
  133. );
  134. }
  135. }
  136. //get a list of leads that are before the trigger's date_added and trigger if not already done so
  137. $leads = $this->leadModel->getEntities(array(
  138. 'filter' => $filter
  139. ));
  140. foreach ($leads as $l) {
  141. if ($this->triggerEvent($event->convertToArray(), $l, true)) {
  142. $log = new LeadTriggerLog();
  143. $log->setIpAddress($ipAddress);
  144. $log->setEvent($event);
  145. $log->setLead($l);
  146. $log->setDateFired(new \DateTime());
  147. $event->addLog($log);
  148. $persist[] = $event;
  149. }
  150. }
  151. }
  152. if (!empty($persist)) {
  153. $repo->saveEntities($persist);
  154. }
  155. }
  156. }
  157. /**
  158. * {@inheritdoc}
  159. *
  160. * @return Trigger|null
  161. */
  162. public function getEntity($id = null)
  163. {
  164. if ($id === null) {
  165. return new Trigger();
  166. }
  167. return parent::getEntity($id);
  168. }
  169. /**
  170. * {@inheritdoc}
  171. *
  172. * @throws MethodNotAllowedHttpException
  173. */
  174. protected function dispatchEvent($action, &$entity, $isNew = false, Event $event = null)
  175. {
  176. if (!$entity instanceof Trigger) {
  177. throw new MethodNotAllowedHttpException(array('Trigger'));
  178. }
  179. switch ($action) {
  180. case "pre_save":
  181. $name = PointEvents::TRIGGER_PRE_SAVE;
  182. break;
  183. case "post_save":
  184. $name = PointEvents::TRIGGER_POST_SAVE;
  185. break;
  186. case "pre_delete":
  187. $name = PointEvents::TRIGGER_PRE_DELETE;
  188. break;
  189. case "post_delete":
  190. $name = PointEvents::TRIGGER_POST_DELETE;
  191. break;
  192. default:
  193. return null;
  194. }
  195. if ($this->dispatcher->hasListeners($name)) {
  196. if (empty($event)) {
  197. $event = new Events\TriggerEvent($entity, $isNew);
  198. }
  199. $this->dispatcher->dispatch($name, $event);
  200. return $event;
  201. }
  202. return null;
  203. }
  204. /**
  205. * @param Trigger $entity
  206. * @param array $sessionEvents
  207. *
  208. * @return void
  209. */
  210. public function setEvents(Trigger $entity, $sessionEvents)
  211. {
  212. $order = 1;
  213. $existingActions = $entity->getEvents();
  214. foreach ($sessionEvents as $properties) {
  215. $isNew = (!empty($properties['id']) && isset($existingActions[$properties['id']])) ? false : true;
  216. $event = !$isNew ? $existingActions[$properties['id']] : new TriggerEvent();
  217. foreach ($properties as $f => $v) {
  218. if (in_array($f, array('id', 'order')))
  219. continue;
  220. $func = "set" . ucfirst($f);
  221. if (method_exists($event, $func)) {
  222. $event->$func($v);
  223. }
  224. }
  225. $event->setTrigger($entity);
  226. $event->setOrder($order);
  227. $order++;
  228. $entity->addTriggerEvent($properties['id'], $event);
  229. }
  230. // Persist if editing the trigger
  231. if ($entity->getId()) {
  232. $this->pointTriggerEventModel->saveEntities($entity->getEvents());
  233. }
  234. }
  235. /**
  236. * Gets array of custom events from bundles subscribed PointEvents::TRIGGER_ON_BUILD
  237. *
  238. * @return mixed
  239. */
  240. public function getEvents()
  241. {
  242. static $events;
  243. if (empty($events)) {
  244. //build them
  245. $events = array();
  246. $event = new Events\TriggerBuilderEvent($this->translator);
  247. $this->dispatcher->dispatch(PointEvents::TRIGGER_ON_BUILD, $event);
  248. $events = $event->getEvents();
  249. }
  250. return $events;
  251. }
  252. /**
  253. * Gets array of custom events from bundles inside groups
  254. *
  255. * @return mixed
  256. */
  257. public function getEventGroups()
  258. {
  259. $events = $this->getEvents();
  260. $groups = array();
  261. foreach ($events as $key => $event) {
  262. $groups[$event['group']][$key] = $event;
  263. }
  264. return $groups;
  265. }
  266. /**
  267. * Triggers a specific event
  268. *
  269. * @param array $event
  270. * @param Lead $lead
  271. * @param bool $force
  272. *
  273. * @return bool Was event triggered
  274. */
  275. public function triggerEvent($event, Lead $lead = null, $force = false)
  276. {
  277. //only trigger events for anonymous users
  278. if (!$force && !$this->security->isAnonymous()) {
  279. return false;
  280. }
  281. if ($lead == null) {
  282. $lead = $this->leadModel->getCurrentLead();
  283. }
  284. if (!$force) {
  285. //get a list of events that has already been performed on this lead
  286. $appliedEvents = $this->getEventRepository()->getLeadTriggeredEvents($lead->getId());
  287. //if it's already been done, then skip it
  288. if (isset($appliedEvents[$event['id']])) {
  289. return false;
  290. }
  291. }
  292. $availableEvents = $this->getEvents();
  293. $eventType = $event['type'];
  294. //make sure the event still exists
  295. if (!isset($availableEvents[$eventType])) {
  296. return false;
  297. }
  298. $settings = $availableEvents[$eventType];
  299. $args = array(
  300. 'event' => $event,
  301. 'lead' => $lead,
  302. 'factory' => $this->factory, // WHAT??
  303. 'config' => $event['properties']
  304. );
  305. if (is_callable($settings['callback'])) {
  306. if (is_array($settings['callback'])) {
  307. $reflection = new \ReflectionMethod($settings['callback'][0], $settings['callback'][1]);
  308. } elseif (strpos($settings['callback'], '::') !== false) {
  309. $parts = explode('::', $settings['callback']);
  310. $reflection = new \ReflectionMethod($parts[0], $parts[1]);
  311. } else {
  312. $reflection = new \ReflectionMethod(null, $settings['callback']);
  313. }
  314. $pass = array();
  315. foreach ($reflection->getParameters() as $param) {
  316. if (isset($args[$param->getName()])) {
  317. $pass[] = $args[$param->getName()];
  318. } else {
  319. $pass[] = null;
  320. }
  321. }
  322. return $reflection->invokeArgs($this, $pass);
  323. }
  324. return false;
  325. }
  326. /**
  327. * Trigger events for the current lead
  328. *
  329. * @param Lead $lead
  330. */
  331. public function triggerEvents(Lead $lead)
  332. {
  333. $points = $lead->getPoints();
  334. //find all published triggers that is applicable to this points
  335. /** @var \Mautic\PointBundle\Entity\TriggerEventRepository $repo */
  336. $repo = $this->getEventRepository();
  337. $events = $repo->getPublishedByPointTotal($points);
  338. if (!empty($events)) {
  339. //get a list of actions that has already been applied to this lead
  340. $appliedEvents = $repo->getLeadTriggeredEvents($lead->getId());
  341. $ipAddress = $this->ipLookupHelper->getIpAddress();
  342. $persist = array();
  343. foreach ($events as $event) {
  344. if (isset($appliedEvents[$event['id']])) {
  345. //don't apply the event to the lead if it's already been done
  346. continue;
  347. }
  348. if ($this->triggerEvent($event, $lead, true)) {
  349. $log = new LeadTriggerLog();
  350. $log->setIpAddress($ipAddress);
  351. $log->setEvent($this->em->getReference('MauticPointBundle:TriggerEvent', $event['id']));
  352. $log->setLead($lead);
  353. $log->setDateFired(new \DateTime());
  354. $persist[] = $log;
  355. }
  356. }
  357. if (!empty($persist)) {
  358. $this->getEventRepository()->saveEntities($persist);
  359. $this->em->clear('Mautic\PointBundle\Entity\LeadTriggerLog');
  360. $this->em->clear('Mautic\PointBundle\Entity\TriggerEvent');
  361. }
  362. }
  363. }
  364. /**
  365. * Returns configured color based on passed in $points
  366. *
  367. * @param $points
  368. *
  369. * @return string
  370. */
  371. public function getColorForLeadPoints($points)
  372. {
  373. static $triggers;
  374. if (!is_array($triggers)) {
  375. $triggers = $this->getRepository()->getTriggerColors();
  376. }
  377. foreach ($triggers as $trigger) {
  378. if ($points >= $trigger['points']) {
  379. return $trigger['color'];
  380. }
  381. }
  382. return '';
  383. }
  384. }