PageRenderTime 42ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

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

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