Issues (3627)

bundles/LeadBundle/Controller/LeadDetailsTrait.php (1 issue)

1
<?php
2
3
/*
4
 * @copyright   2016 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\LeadBundle\Controller;
13
14
use Mautic\CoreBundle\Entity\AuditLogRepository;
15
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
16
use Mautic\CoreBundle\Helper\Chart\LineChart;
17
use Mautic\CoreBundle\Model\AuditLogModel;
18
use Mautic\LeadBundle\Entity\Lead;
19
use Mautic\LeadBundle\Model\LeadModel;
20
21
trait LeadDetailsTrait
22
{
23
    /**
24
     * @param int $page
25
     *
26
     * @return array
27
     */
28
    protected function getAllEngagements(array $leads, array $filters = null, array $orderBy = null, $page = 1, $limit = 25)
29
    {
30
        $session = $this->get('session');
0 ignored issues
show
It seems like get() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

30
        /** @scrutinizer ignore-call */ 
31
        $session = $this->get('session');
Loading history...
31
32
        if (null == $filters) {
33
            $filters = $session->get(
34
                'mautic.plugin.timeline.filters',
35
                [
36
                    'search'        => '',
37
                    'includeEvents' => [],
38
                    'excludeEvents' => [],
39
                ]
40
            );
41
        }
42
43
        if (null == $orderBy) {
44
            if (!$session->has('mautic.plugin.timeline.orderby')) {
45
                $session->set('mautic.plugin.timeline.orderby', 'timestamp');
46
                $session->set('mautic.plugin.timeline.orderbydir', 'DESC');
47
            }
48
49
            $orderBy = [
50
                $session->get('mautic.plugin.timeline.orderby'),
51
                $session->get('mautic.plugin.timeline.orderbydir'),
52
            ];
53
        }
54
55
        // prepare result object
56
        $result = [
57
            'events'   => [],
58
            'filters'  => $filters,
59
            'order'    => $orderBy,
60
            'types'    => [],
61
            'total'    => 0,
62
            'page'     => $page,
63
            'limit'    => $limit,
64
            'maxPages' => 0,
65
        ];
66
67
        // get events for each contact
68
        foreach ($leads as $lead) {
69
            //  if (!$lead->getEmail()) continue; // discard contacts without email
70
71
            /** @var LeadModel $model */
72
            $model       = $this->getModel('lead');
73
            $engagements = $model->getEngagements($lead, $filters, $orderBy, $page, $limit);
74
            $events      = $engagements['events'];
75
            $types       = $engagements['types'];
76
77
            // inject lead into events
78
            foreach ($events as &$event) {
79
                $event['leadId']    = $lead->getId();
80
                $event['leadEmail'] = $lead->getEmail();
81
                $event['leadName']  = $lead->getName() ? $lead->getName() : $lead->getEmail();
82
            }
83
84
            $result['events'] = array_merge($result['events'], $events);
85
            $result['types']  = array_merge($result['types'], $types);
86
            $result['total'] += $engagements['total'];
87
        }
88
89
        $result['maxPages'] = ($limit <= 0) ? 1 : round(ceil($result['total'] / $limit));
90
91
        usort($result['events'], [$this, 'cmp']); // sort events by
92
93
        // now all events are merged, let's limit to   $limit
94
        array_splice($result['events'], $limit);
95
96
        $result['total'] = count($result['events']);
97
98
        return $result;
99
    }
100
101
    /**
102
     * Makes sure that the event filter array is in the right format.
103
     *
104
     * @param mixed $filters
105
     *
106
     * @return array
107
     *
108
     * @throws InvalidArgumentException if not an array
109
     */
110
    public function sanitizeEventFilter($filters)
111
    {
112
        if (!is_array($filters)) {
113
            throw new \InvalidArgumentException('filters parameter must be an array');
114
        }
115
116
        if (!isset($filters['search'])) {
117
            $filters['search'] = '';
118
        }
119
120
        if (!isset($filters['includeEvents'])) {
121
            $filters['includeEvents'] = [];
122
        }
123
124
        if (!isset($filters['excludeEvents'])) {
125
            $filters['excludeEvents'] = [];
126
        }
127
128
        return $filters;
129
    }
130
131
    /**
132
     * @param $a
133
     * @param $b
134
     *
135
     * @return int
136
     */
137
    private function cmp($a, $b)
138
    {
139
        if ($a['timestamp'] === $b['timestamp']) {
140
            return 0;
141
        }
142
143
        return ($a['timestamp'] < $b['timestamp']) ? +1 : -1;
144
    }
145
146
    /**
147
     * Get a list of places for the lead based on IP location.
148
     *
149
     * @return array
150
     */
151
    protected function getPlaces(Lead $lead)
152
    {
153
        // Get Places from IP addresses
154
        $places = [];
155
        if ($lead->getIpAddresses()) {
156
            foreach ($lead->getIpAddresses() as $ip) {
157
                if ($details = $ip->getIpDetails()) {
158
                    if (!empty($details['latitude']) && !empty($details['longitude'])) {
159
                        $name = 'N/A';
160
                        if (!empty($details['city'])) {
161
                            $name = $details['city'];
162
                        } elseif (!empty($details['region'])) {
163
                            $name = $details['region'];
164
                        }
165
                        $place = [
166
                            'latLng' => [$details['latitude'], $details['longitude']],
167
                            'name'   => $name,
168
                        ];
169
                        $places[] = $place;
170
                    }
171
                }
172
            }
173
        }
174
175
        return $places;
176
    }
177
178
    /**
179
     * @return mixed
180
     */
181
    protected function getEngagementData(Lead $lead, \DateTime $fromDate = null, \DateTime $toDate = null)
182
    {
183
        $translator = $this->get('translator');
184
185
        if (null == $fromDate) {
186
            $fromDate = new \DateTime('first day of this month 00:00:00');
187
            $fromDate->modify('-6 months');
188
        }
189
        if (null == $toDate) {
190
            $toDate = new \DateTime();
191
        }
192
193
        $lineChart  = new LineChart(null, $fromDate, $toDate);
194
        $chartQuery = new ChartQuery($this->getDoctrine()->getConnection(), $fromDate, $toDate);
195
196
        /** @var LeadModel $model */
197
        $model       = $this->getModel('lead');
198
        $engagements = $model->getEngagementCount($lead, $fromDate, $toDate, 'm', $chartQuery);
199
        $lineChart->setDataset($translator->trans('mautic.lead.graph.line.all_engagements'), $engagements['byUnit']);
200
201
        $pointStats = $chartQuery->fetchSumTimeData('lead_points_change_log', 'date_added', ['lead_id' => $lead->getId()], 'delta');
202
        $lineChart->setDataset($translator->trans('mautic.lead.graph.line.points'), $pointStats);
203
204
        return $lineChart->render();
205
    }
206
207
    /**
208
     * @param int $page
209
     * @param int $limit
210
     *
211
     * @return array
212
     */
213
    protected function getAuditlogs(Lead $lead, array $filters = null, array $orderBy = null, $page = 1, $limit = 25)
214
    {
215
        $session = $this->get('session');
216
217
        if (null == $filters) {
218
            $filters = $session->get(
219
                'mautic.lead.'.$lead->getId().'.auditlog.filters',
220
                [
221
                    'search'        => '',
222
                    'includeEvents' => [],
223
                    'excludeEvents' => [],
224
                ]
225
            );
226
        }
227
228
        if (null == $orderBy) {
229
            if (!$session->has('mautic.lead.'.$lead->getId().'.auditlog.orderby')) {
230
                $session->set('mautic.lead.'.$lead->getId().'.auditlog.orderby', 'al.dateAdded');
231
                $session->set('mautic.lead.'.$lead->getId().'.auditlog.orderbydir', 'DESC');
232
            }
233
234
            $orderBy = [
235
                $session->get('mautic.lead.'.$lead->getId().'.auditlog.orderby'),
236
                $session->get('mautic.lead.'.$lead->getId().'.auditlog.orderbydir'),
237
            ];
238
        }
239
240
        // Audit Log
241
        /** @var AuditLogModel $auditlogModel */
242
        $auditlogModel = $this->getModel('core.auditlog');
243
        /** @var AuditLogRepository $repo */
244
        $repo     = $auditlogModel->getRepository();
245
        $logCount = $repo->getAuditLogsCount($lead, $filters);
246
        $logs     = $repo->getAuditLogs($lead, $filters, $orderBy, $page, $limit);
247
248
        $logEvents = array_map(function ($l) {
249
            return [
250
                'eventType'       => $l['action'],
251
                'eventLabel'      => $l['userName'],
252
                'timestamp'       => $l['dateAdded'],
253
                'details'         => $l['details'],
254
                'contentTemplate' => 'MauticLeadBundle:Auditlog:details.html.php',
255
            ];
256
        }, $logs);
257
258
        $types = [
259
            'delete'     => $this->translator->trans('mautic.lead.event.delete'),
260
            'create'     => $this->translator->trans('mautic.lead.event.create'),
261
            'identified' => $this->translator->trans('mautic.lead.event.identified'),
262
            'ipadded'    => $this->translator->trans('mautic.lead.event.ipadded'),
263
            'merge'      => $this->translator->trans('mautic.lead.event.merge'),
264
            'update'     => $this->translator->trans('mautic.lead.event.update'),
265
        ];
266
267
        return [
268
            'events'   => $logEvents,
269
            'filters'  => $filters,
270
            'order'    => $orderBy,
271
            'types'    => $types,
272
            'total'    => $logCount,
273
            'page'     => $page,
274
            'limit'    => $limit,
275
            'maxPages' => ceil($logCount / $limit),
276
        ];
277
    }
278
279
    /**
280
     * @param int $page
281
     * @param int $limit
282
     *
283
     * @return array
284
     */
285
    protected function getEngagements(Lead $lead, array $filters = null, array $orderBy = null, $page = 1, $limit = 25)
286
    {
287
        $session = $this->get('session');
288
289
        if (null == $filters) {
290
            $filters = $session->get(
291
                'mautic.lead.'.$lead->getId().'.timeline.filters',
292
                [
293
                    'search'        => '',
294
                    'includeEvents' => [],
295
                    'excludeEvents' => [],
296
                ]
297
            );
298
        }
299
300
        if (null == $orderBy) {
301
            if (!$session->has('mautic.lead.'.$lead->getId().'.timeline.orderby')) {
302
                $session->set('mautic.lead.'.$lead->getId().'.timeline.orderby', 'timestamp');
303
                $session->set('mautic.lead.'.$lead->getId().'.timeline.orderbydir', 'DESC');
304
            }
305
306
            $orderBy = [
307
                $session->get('mautic.lead.'.$lead->getId().'.timeline.orderby'),
308
                $session->get('mautic.lead.'.$lead->getId().'.timeline.orderbydir'),
309
            ];
310
        }
311
        /** @var LeadModel $model */
312
        $model = $this->getModel('lead');
313
314
        return $model->getEngagements($lead, $filters, $orderBy, $page, $limit);
315
    }
316
317
    /**
318
     * Get an array with engagements and points of a contact.
319
     *
320
     * @return array
321
     */
322
    protected function getStatsCount(Lead $lead, \DateTime $fromDate = null, \DateTime $toDate = null)
323
    {
324
        if (null == $fromDate) {
325
            $fromDate = new \DateTime('first day of this month 00:00:00');
326
            $fromDate->modify('-6 months');
327
        }
328
        if (null == $toDate) {
329
            $toDate = new \DateTime();
330
        }
331
332
        /** @var LeadModel $model */
333
        $model       = $this->getModel('lead');
334
        $chartQuery  = new ChartQuery($this->getDoctrine()->getConnection(), $fromDate, $toDate);
335
336
        $engagements = $model->getEngagementCount($lead, $fromDate, $toDate, 'm', $chartQuery);
337
        $pointStats  = $chartQuery->fetchSumTimeData('lead_points_change_log', 'date_added', ['lead_id' => $lead->getId()], 'delta');
338
339
        return [
340
            'engagements' => $engagements,
341
            'points'      => $pointStats,
342
        ];
343
    }
344
345
    /**
346
     * Get an array to create company's engagements graph.
347
     *
348
     * @param array $contacts
349
     *
350
     * @return array
351
     */
352
    protected function getCompanyEngagementData($contacts)
353
    {
354
        $engagements = [0, 0, 0, 0, 0, 0];
355
        $points      = [0, 0, 0, 0, 0, 0];
356
        foreach ($contacts as $contact) {
357
            $model = $this->getModel('lead.lead');
358
            // When we change lead data these changes get cached
359
            // so we need to clear the entity manager
360
            $model->getRepository()->clear();
361
362
            /** @var \Mautic\LeadBundle\Entity\Lead $lead */
363
            if (!isset($contact['lead_id'])) {
364
                continue;
365
            }
366
            $lead            = $model->getEntity($contact['lead_id']);
367
            if (!$lead instanceof Lead) {
368
                continue;
369
            }
370
            $engagementsData = $this->getStatsCount($lead);
371
372
            $engagements = array_map(function ($a, $b) {
373
                return $a + $b;
374
            }, $engagementsData['engagements']['byUnit'], $engagements);
375
            $points = array_map(function ($points_first_user, $points_second_user) {
376
                return $points_first_user + $points_second_user;
377
            }, $engagementsData['points'], $points);
378
        }
379
380
        return [
381
            'engagements' => $engagements,
382
            'points'      => $points,
383
        ];
384
    }
385
386
    /**
387
     * Get company graph for points and engagements.
388
     *
389
     * @param $contacts
390
     *
391
     * @return mixed
392
     */
393
    protected function getCompanyEngagementsForGraph($contacts)
394
    {
395
        $graphData  = $this->getCompanyEngagementData($contacts);
396
        $translator = $this->get('translator');
397
398
        $fromDate = new \DateTime('first day of this month 00:00:00');
399
        $fromDate->modify('-6 months');
400
401
        $toDate = new \DateTime();
402
403
        $lineChart  = new LineChart(null, $fromDate, $toDate);
404
405
        $lineChart->setDataset($translator->trans('mautic.lead.graph.line.all_engagements'), $graphData['engagements']);
406
407
        $lineChart->setDataset($translator->trans('mautic.lead.graph.line.points'), $graphData['points']);
408
409
        return $lineChart->render();
410
    }
411
412
    /**
413
     * @return array
414
     */
415
    protected function getScheduledCampaignEvents(Lead $lead)
416
    {
417
        // Upcoming events from Campaign Bundle
418
        /** @var \Mautic\CampaignBundle\Entity\LeadEventLogRepository $leadEventLogRepository */
419
        $leadEventLogRepository = $this->getDoctrine()->getManager()->getRepository('MauticCampaignBundle:LeadEventLog');
420
421
        return $leadEventLogRepository->getUpcomingEvents(
422
            [
423
                'lead'      => $lead,
424
                'eventType' => ['action', 'condition'],
425
            ]
426
        );
427
    }
428
}
429