Issues (3627)

app/bundles/AssetBundle/Model/AssetModel.php (6 issues)

1
<?php
2
3
/*
4
 * @copyright   2014 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\AssetBundle\Model;
13
14
use Doctrine\ORM\PersistentCollection;
15
use Mautic\AssetBundle\AssetEvents;
16
use Mautic\AssetBundle\Entity\Asset;
17
use Mautic\AssetBundle\Entity\Download;
18
use Mautic\AssetBundle\Event\AssetEvent;
19
use Mautic\AssetBundle\Event\AssetLoadEvent;
20
use Mautic\AssetBundle\Form\Type\AssetType;
21
use Mautic\CategoryBundle\Model\CategoryModel;
22
use Mautic\CoreBundle\Helper\Chart\ChartQuery;
23
use Mautic\CoreBundle\Helper\Chart\LineChart;
24
use Mautic\CoreBundle\Helper\Chart\PieChart;
25
use Mautic\CoreBundle\Helper\CoreParametersHelper;
26
use Mautic\CoreBundle\Helper\FileHelper;
27
use Mautic\CoreBundle\Helper\IpLookupHelper;
28
use Mautic\CoreBundle\Model\FormModel;
29
use Mautic\EmailBundle\Entity\Email;
30
use Mautic\LeadBundle\Entity\Lead;
31
use Mautic\LeadBundle\Model\LeadModel;
32
use Mautic\LeadBundle\Tracker\ContactTracker;
33
use Mautic\LeadBundle\Tracker\Factory\DeviceDetectorFactory\DeviceDetectorFactoryInterface;
34
use Mautic\LeadBundle\Tracker\Service\DeviceCreatorService\DeviceCreatorServiceInterface;
35
use Mautic\LeadBundle\Tracker\Service\DeviceTrackingService\DeviceTrackingServiceInterface;
36
use Symfony\Component\EventDispatcher\Event;
37
use Symfony\Component\HttpFoundation\RequestStack;
38
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
39
40
class AssetModel extends FormModel
41
{
42
    /**
43
     * @var CategoryModel
44
     */
45
    protected $categoryModel;
46
47
    /**
48
     * @var LeadModel
49
     */
50
    protected $leadModel;
51
52
    /**
53
     * @var \Symfony\Component\HttpFoundation\Request|null
54
     */
55
    protected $request;
56
57
    /**
58
     * @var IpLookupHelper
59
     */
60
    protected $ipLookupHelper;
61
62
    /**
63
     * @var int
64
     */
65
    protected $maxAssetSize;
66
67
    /**
68
     * @var DeviceCreatorServiceInterface
69
     */
70
    private $deviceCreatorService;
71
72
    /**
73
     * @var DeviceDetectorFactoryInterface
74
     */
75
    private $deviceDetectorFactory;
76
77
    /**
78
     * @var DeviceTrackingServiceInterface
79
     */
80
    private $deviceTrackingService;
81
82
    /**
83
     * @var ContactTracker
84
     */
85
    private $contactTracker;
86
87
    /**
88
     * AssetModel constructor.
89
     */
90
    public function __construct(
91
        LeadModel $leadModel,
92
        CategoryModel $categoryModel,
93
        RequestStack $requestStack,
94
        IpLookupHelper $ipLookupHelper,
95
        CoreParametersHelper $coreParametersHelper,
96
        DeviceCreatorServiceInterface $deviceCreatorService,
97
        DeviceDetectorFactoryInterface $deviceDetectorFactory,
98
        DeviceTrackingServiceInterface $deviceTrackingService,
99
        ContactTracker $contactTracker
100
    ) {
101
        $this->leadModel              = $leadModel;
102
        $this->categoryModel          = $categoryModel;
103
        $this->request                = $requestStack->getCurrentRequest();
104
        $this->ipLookupHelper         = $ipLookupHelper;
105
        $this->deviceCreatorService   = $deviceCreatorService;
106
        $this->deviceDetectorFactory  = $deviceDetectorFactory;
107
        $this->deviceTrackingService  = $deviceTrackingService;
108
        $this->contactTracker         = $contactTracker;
109
        $this->maxAssetSize           = $coreParametersHelper->get('max_size');
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function saveEntity($entity, $unlock = true)
116
    {
117
        if (empty($this->inConversion)) {
118
            $alias = $entity->getAlias();
119
            if (empty($alias)) {
120
                $alias = $entity->getTitle();
121
            }
122
            $alias = $this->cleanAlias($alias, '', false, '-');
123
124
            //make sure alias is not already taken
125
            $repo      = $this->getRepository();
126
            $testAlias = $alias;
127
            $count     = $repo->checkUniqueAlias($testAlias, $entity);
128
            $aliasTag  = $count;
129
130
            while ($count) {
131
                $testAlias = $alias.$aliasTag;
132
                $count     = $repo->checkUniqueAlias($testAlias, $entity);
133
                ++$aliasTag;
134
            }
135
            if ($testAlias != $alias) {
136
                $alias = $testAlias;
137
            }
138
            $entity->setAlias($alias);
139
        }
140
141
        if (!$entity->isNew()) {
142
            //increase the revision
143
            $revision = $entity->getRevision();
144
            ++$revision;
145
            $entity->setRevision($revision);
146
        }
147
148
        parent::saveEntity($entity, $unlock);
149
    }
150
151
    /**
152
     * @param $asset
153
     * @param null   $request
154
     * @param string $code
155
     * @param array  $systemEntry
156
     *
157
     * @throws \Doctrine\ORM\ORMException
158
     * @throws \Exception
159
     */
160
    public function trackDownload($asset, $request = null, $code = '200', $systemEntry = [])
161
    {
162
        // Don't skew results with in-house downloads
163
        if (empty($systemEntry) && !$this->security->isAnonymous()) {
164
            return;
165
        }
166
167
        if (null == $request) {
168
            $request = $this->request;
169
        }
170
171
        $download = new Download();
172
        $download->setDateDownload(new \Datetime());
173
174
        // Download triggered by lead
175
        if (empty($systemEntry)) {
176
            //check for any clickthrough info
177
            $clickthrough = $request->get('ct', false);
0 ignored issues
show
The method get() does not exist on null. ( Ignorable by Annotation )

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

177
            /** @scrutinizer ignore-call */ 
178
            $clickthrough = $request->get('ct', false);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
178
            if (!empty($clickthrough)) {
179
                $clickthrough = $this->decodeArrayFromUrl($clickthrough);
180
181
                if (!empty($clickthrough['lead'])) {
182
                    $lead = $this->leadModel->getEntity($clickthrough['lead']);
183
                    if (null !== $lead) {
184
                        $wasTrackedAlready                    = $this->deviceTrackingService->isTracked();
185
                        $deviceDetector                       = $this->deviceDetectorFactory->create($request->server->get('HTTP_USER_AGENT'));
186
                        $deviceDetector->parse();
187
                        $currentDevice                             = $this->deviceCreatorService->getCurrentFromDetector($deviceDetector, $lead);
188
                        $trackedDevice                             = $this->deviceTrackingService->trackCurrentDevice($currentDevice, false);
0 ignored issues
show
It seems like $currentDevice can also be of type null; however, parameter $device of Mautic\LeadBundle\Tracke...e::trackCurrentDevice() does only seem to accept Mautic\LeadBundle\Entity\LeadDevice, maybe add an additional type check? ( Ignorable by Annotation )

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

188
                        $trackedDevice                             = $this->deviceTrackingService->trackCurrentDevice(/** @scrutinizer ignore-type */ $currentDevice, false);
Loading history...
189
                        $trackingId                                = $trackedDevice->getTrackingId();
190
                        $trackingNewlyGenerated                    = !$wasTrackedAlready;
191
                        $leadClickthrough                          = true;
192
193
                        $this->contactTracker->setTrackedContact($lead);
194
                    }
195
                }
196
                if (!empty($clickthrough['channel'])) {
197
                    if (1 === count($clickthrough['channel'])) {
198
                        $channelId = reset($clickthrough['channel']);
199
                        $channel   = key($clickthrough['channel']);
200
                    } else {
201
                        $channel   = $clickthrough['channel'][0];
202
                        $channelId = (int) $clickthrough['channel'][1];
203
                    }
204
                    $download->setSource($channel);
205
                    $download->setSourceId($channelId);
206
                } elseif (!empty($clickthrough['source'])) {
207
                    $download->setSource($clickthrough['source'][0]);
208
                    $download->setSourceId($clickthrough['source'][1]);
209
                }
210
211
                if (!empty($clickthrough['email'])) {
212
                    $emailRepo = $this->em->getRepository('MauticEmailBundle:Email');
213
                    if ($emailEntity = $emailRepo->getEntity($clickthrough['email'])) {
214
                        $download->setEmail($emailEntity);
215
                    }
216
                }
217
            }
218
219
            if (empty($leadClickthrough)) {
220
                $wasTrackedAlready         = $this->deviceTrackingService->isTracked();
221
                $lead                      = $this->contactTracker->getContact();
222
                $trackedDevice             = $this->deviceTrackingService->getTrackedDevice();
223
                $trackingId                = null;
224
                $trackingNewlyGenerated    = false;
225
                if (null !== $trackedDevice) {
226
                    $trackingId             = $trackedDevice->getTrackingId();
227
                    $trackingNewlyGenerated = !$wasTrackedAlready;
228
                }
229
            }
230
231
            $download->setLead($lead);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lead does not seem to be defined for all execution paths leading up to this point.
Loading history...
232
        } else {
233
            $trackingId = '';
234
235
            if (isset($systemEntry['lead'])) {
236
                $lead = $systemEntry['lead'];
237
                if (!$lead instanceof Lead) {
238
                    $leadId = is_array($lead) ? $lead['id'] : $lead;
239
                    $lead   = $this->em->getReference('MauticLeadBundle:Lead', $leadId);
240
                }
241
242
                $download->setLead($lead);
243
            }
244
245
            if (!empty($systemEntry['source'])) {
246
                $download->setSource($systemEntry['source'][0]);
247
                $download->setSourceId($systemEntry['source'][1]);
248
            }
249
250
            if (isset($systemEntry['email'])) {
251
                $email = $systemEntry['email'];
252
                if (!$email instanceof Email) {
253
                    $emailId = is_array($email) ? $email['id'] : $email;
254
                    $email   = $this->em->getReference('MauticEmailBundle:Email', $emailId);
255
                }
256
257
                $download->setEmail($email);
0 ignored issues
show
It seems like $email can also be of type null; however, parameter $email of Mautic\AssetBundle\Entity\Download::setEmail() does only seem to accept Mautic\EmailBundle\Entity\Email, maybe add an additional type check? ( Ignorable by Annotation )

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

257
                $download->setEmail(/** @scrutinizer ignore-type */ $email);
Loading history...
258
            }
259
260
            if (isset($systemEntry['tracking_id'])) {
261
                $trackingId             = $systemEntry['tracking_id'];
262
                $trackingNewlyGenerated = false;
263
            } elseif ($this->security->isAnonymous() && !defined('IN_MAUTIC_CONSOLE')) {
264
                // If the session is anonymous and not triggered via CLI, assume the lead did something to trigger the
265
                // system forced download such as an email
266
                $deviceWasTracked       = $this->deviceTrackingService->isTracked();
267
                $deviceDetector         = $this->deviceDetectorFactory->create($request->server->get('HTTP_USER_AGENT'));
268
                $deviceDetector->parse();
269
                $currentDevice          = $this->deviceCreatorService->getCurrentFromDetector($deviceDetector, $lead);
270
                $trackedDevice          = $this->deviceTrackingService->trackCurrentDevice($currentDevice, false);
271
                $trackingId             = $trackedDevice->getTrackingId();
272
                $trackingNewlyGenerated = !$deviceWasTracked;
273
            }
274
        }
275
276
        $isUnique = true;
277
        if (!empty($trackingNewlyGenerated)) {
278
            // Cookie was just generated so this is definitely a unique download
279
            $isUnique = $trackingNewlyGenerated;
280
        } elseif (!empty($trackingId)) {
281
            // Determine if this is a unique download
282
            $isUnique = $this->getDownloadRepository()->isUniqueDownload($asset->getId(), $trackingId);
283
        }
284
285
        $download->setTrackingId($trackingId);
286
287
        if (!empty($asset) && empty($systemEntry)) {
288
            $download->setAsset($asset);
289
290
            $this->getRepository()->upDownloadCount($asset->getId(), 1, $isUnique);
291
        }
292
293
        //check for existing IP
294
        $ipAddress = $this->ipLookupHelper->getIpAddress();
295
296
        $download->setCode($code);
297
        $download->setIpAddress($ipAddress);
298
299
        if (null !== $request) {
300
            $download->setReferer($request->server->get('HTTP_REFERER'));
301
        }
302
303
        // Dispatch event
304
        if ($this->dispatcher->hasListeners(AssetEvents::ASSET_ON_LOAD)) {
305
            $event = new AssetLoadEvent($download, $isUnique);
306
            $this->dispatcher->dispatch(AssetEvents::ASSET_ON_LOAD, $event);
307
        }
308
309
        // Wrap in a try/catch to prevent deadlock errors on busy servers
310
        try {
311
            $this->em->persist($download);
312
            $this->em->flush();
313
        } catch (\Exception $e) {
314
            if (MAUTIC_ENV === 'dev') {
315
                throw $e;
316
            } else {
317
                error_log($e);
318
            }
319
        }
320
321
        $this->em->detach($download);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\EntityManager::detach() has been deprecated: 2.7 This method is being removed from the ORM and won't have any replacement ( Ignorable by Annotation )

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

321
        /** @scrutinizer ignore-deprecated */ $this->em->detach($download);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
322
    }
323
324
    /**
325
     * Increase the download count.
326
     *
327
     * @param            $asset
328
     * @param int        $increaseBy
329
     * @param bool|false $unique
330
     */
331
    public function upDownloadCount($asset, $increaseBy = 1, $unique = false)
332
    {
333
        $id = ($asset instanceof Asset) ? $asset->getId() : (int) $asset;
334
335
        $this->getRepository()->upDownloadCount($id, $increaseBy, $unique);
336
    }
337
338
    /**
339
     * @return \Mautic\AssetBundle\Entity\AssetRepository
340
     */
341
    public function getRepository()
342
    {
343
        return $this->em->getRepository('MauticAssetBundle:Asset');
344
    }
345
346
    /**
347
     * @return \Mautic\AssetBundle\Entity\DownloadRepository
348
     */
349
    public function getDownloadRepository()
350
    {
351
        return $this->em->getRepository('MauticAssetBundle:Download');
352
    }
353
354
    /**
355
     * @return string
356
     */
357
    public function getPermissionBase()
358
    {
359
        return 'asset:assets';
360
    }
361
362
    /**
363
     * @return string
364
     */
365
    public function getNameGetter()
366
    {
367
        return 'getTitle';
368
    }
369
370
    /**
371
     * {@inheritdoc}
372
     *
373
     * @throws NotFoundHttpException
374
     */
375
    public function createForm($entity, $formFactory, $action = null, $options = [])
376
    {
377
        if (!$entity instanceof Asset) {
378
            throw new MethodNotAllowedHttpException(['Asset']);
379
        }
380
381
        if (!empty($action)) {
382
            $options['action'] = $action;
383
        }
384
385
        return $formFactory->create(AssetType::class, $entity, $options);
386
    }
387
388
    /**
389
     * Get a specific entity or generate a new one if id is empty.
390
     *
391
     * @param $id
392
     *
393
     * @return Asset|null
394
     */
395
    public function getEntity($id = null)
396
    {
397
        if (null === $id) {
398
            $entity = new Asset();
399
        } else {
400
            $entity = parent::getEntity($id);
401
        }
402
403
        return $entity;
404
    }
405
406
    /**
407
     * {@inheritdoc}
408
     *
409
     * @param $action
410
     * @param $event
411
     * @param $entity
412
     * @param $isNew
413
     *
414
     * @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
415
     */
416
    protected function dispatchEvent($action, &$entity, $isNew = false, Event $event = null)
417
    {
418
        if (!$entity instanceof Asset) {
419
            throw new MethodNotAllowedHttpException(['Asset']);
420
        }
421
422
        switch ($action) {
423
            case 'pre_save':
424
                $name = AssetEvents::ASSET_PRE_SAVE;
425
                break;
426
            case 'post_save':
427
                $name = AssetEvents::ASSET_POST_SAVE;
428
                break;
429
            case 'pre_delete':
430
                $name = AssetEvents::ASSET_PRE_DELETE;
431
                break;
432
            case 'post_delete':
433
                $name = AssetEvents::ASSET_POST_DELETE;
434
                break;
435
            default:
436
                return null;
437
        }
438
439
        if ($this->dispatcher->hasListeners($name)) {
440
            if (empty($event)) {
441
                $event = new AssetEvent($entity, $isNew);
442
                $event->setEntityManager($this->em);
443
            }
444
445
            $this->dispatcher->dispatch($name, $event);
446
447
            return $event;
448
        } else {
449
            return null;
450
        }
451
    }
452
453
    /**
454
     * Get list of entities for autopopulate fields.
455
     *
456
     * @param $type
457
     * @param $filter
458
     * @param $limit
459
     *
460
     * @return array
461
     */
462
    public function getLookupResults($type, $filter = '', $limit = 10)
463
    {
464
        $results = [];
465
        switch ($type) {
466
            case 'asset':
467
                $viewOther = $this->security->isGranted('asset:assets:viewother');
468
                $repo      = $this->getRepository();
469
                $repo->setCurrentUser($this->userHelper->getUser());
470
                $results = $repo->getAssetList($filter, $limit, 0, $viewOther);
471
                break;
472
            case 'category':
473
                $results = $this->categoryModel->getRepository()->getCategoryList($filter, $limit, 0);
474
                break;
475
        }
476
477
        return $results;
478
    }
479
480
    /**
481
     * Generate url for an asset.
482
     *
483
     * @param Asset $entity
484
     * @param bool  $absolute
485
     * @param array $clickthrough
486
     *
487
     * @return string
488
     */
489
    public function generateUrl($entity, $absolute = true, $clickthrough = [])
490
    {
491
        $assetSlug = $entity->getId().':'.$entity->getAlias();
492
493
        $slugs = [
494
            'slug' => $assetSlug,
495
        ];
496
497
        return $this->buildUrl('mautic_asset_download', $slugs, $absolute, $clickthrough);
498
    }
499
500
    /**
501
     * Determine the max upload size based on PHP restrictions and config.
502
     *
503
     * @param string     $unit          If '', determine the best unit based on the number
504
     * @param bool|false $humanReadable Return as a human readable filesize
505
     *
506
     * @return float
507
     */
508
    public function getMaxUploadSize($unit = 'M', $humanReadable = false)
509
    {
510
        $maxAssetSize  = $this->maxAssetSize;
511
        $maxAssetSize  = (-1 == $maxAssetSize || 0 === $maxAssetSize) ? PHP_INT_MAX : FileHelper::convertMegabytesToBytes($maxAssetSize);
512
        $maxPostSize   = Asset::getIniValue('post_max_size');
513
        $maxUploadSize = Asset::getIniValue('upload_max_filesize');
514
        $memoryLimit   = Asset::getIniValue('memory_limit');
515
        $maxAllowed    = min(array_filter([$maxAssetSize, $maxPostSize, $maxUploadSize, $memoryLimit]));
516
517
        if ($humanReadable) {
518
            $number = Asset::convertBytesToHumanReadable($maxAllowed);
519
        } else {
520
            list($number, $unit) = Asset::convertBytesToUnit($maxAllowed, $unit);
521
        }
522
523
        return $number;
524
    }
525
526
    /**
527
     * @param $assets
528
     *
529
     * @return int|string
530
     */
531
    public function getTotalFilesize($assets)
532
    {
533
        $firstAsset = is_array($assets) ? reset($assets) : false;
534
        if ($assets instanceof PersistentCollection || is_object($firstAsset)) {
535
            $assetIds = [];
536
            foreach ($assets as $asset) {
537
                $assetIds[] = $asset->getId();
538
            }
539
            $assets = $assetIds;
540
        }
541
542
        if (!is_array($assets)) {
543
            $assets = [$assets];
544
        }
545
546
        if (empty($assets)) {
547
            return 0;
548
        }
549
550
        $repo = $this->getRepository();
551
        $size = $repo->getAssetSize($assets);
552
553
        if ($size) {
554
            $size = Asset::convertBytesToHumanReadable($size);
555
        }
556
557
        return $size;
558
    }
559
560
    /**
561
     * Get line chart data of downloads.
562
     *
563
     * @param char   $unit          {@link php.net/manual/en/function.date.php#refsect1-function.date-parameters}
0 ignored issues
show
The type Mautic\AssetBundle\Model\char was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
564
     * @param string $dateFormat
565
     * @param array  $filter
566
     * @param bool   $canViewOthers
567
     *
568
     * @return array
569
     */
570
    public function getDownloadsLineChartData($unit, \DateTime $dateFrom, \DateTime $dateTo, $dateFormat = null, $filter = [], $canViewOthers = true)
571
    {
572
        $chart = new LineChart($unit, $dateFrom, $dateTo, $dateFormat);
573
        $query = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
574
        $q     = $query->prepareTimeDataQuery('asset_downloads', 'date_download', $filter);
575
576
        if (!$canViewOthers) {
577
            $q->join('t', MAUTIC_TABLE_PREFIX.'assets', 'a', 'a.id = t.asset_id')
578
                ->andWhere('a.created_by = :userId')
579
                ->setParameter('userId', $this->userHelper->getUser()->getId());
580
        }
581
582
        $data = $query->loadAndBuildTimeData($q);
583
584
        $chart->setDataset($this->translator->trans('mautic.asset.downloadcount'), $data);
585
586
        return $chart->render();
587
    }
588
589
    /**
590
     * Get pie chart data of unique vs repetitive downloads.
591
     * Repetitive in this case mean if a lead downloaded any of the assets more than once.
592
     *
593
     * @param string $dateFrom
594
     * @param string $dateTo
595
     * @param array  $filters
596
     * @param bool   $canViewOthers
597
     *
598
     * @return array
599
     */
600
    public function getUniqueVsRepetitivePieChartData($dateFrom, $dateTo, $filters = [], $canViewOthers = true)
601
    {
602
        $chart   = new PieChart();
603
        $query   = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
604
        $allQ    = $query->getCountQuery('asset_downloads', 'id', 'date_download', $filters);
605
        $uniqueQ = $query->getCountQuery('asset_downloads', 'lead_id', 'date_download', $filters, ['getUnique' => true]);
606
607
        if (!$canViewOthers) {
608
            $allQ->join('t', MAUTIC_TABLE_PREFIX.'assets', 'a', 'a.id = t.asset_id')
609
                ->andWhere('a.created_by = :userId')
610
                ->setParameter('userId', $this->userHelper->getUser()->getId());
611
            $uniqueQ->join('t', MAUTIC_TABLE_PREFIX.'assets', 'a', 'a.id = t.asset_id')
612
                ->andWhere('a.created_by = :userId')
613
                ->setParameter('userId', $this->userHelper->getUser()->getId());
614
        }
615
616
        $all    = $query->fetchCount($allQ);
617
        $unique = $query->fetchCount($uniqueQ);
618
619
        $repetitive = $all - $unique;
620
        $chart->setDataset($this->translator->trans('mautic.asset.unique'), $unique);
621
        $chart->setDataset($this->translator->trans('mautic.asset.repetitive'), $repetitive);
622
623
        return $chart->render();
624
    }
625
626
    /**
627
     * Get a list of popular (by downloads) assets.
628
     *
629
     * @param int    $limit
630
     * @param string $dateFrom
631
     * @param string $dateTo
632
     * @param array  $filters
633
     * @param bool   $canViewOthers
634
     *
635
     * @return array
636
     */
637
    public function getPopularAssets($limit = 10, $dateFrom = null, $dateTo = null, $filters = [], $canViewOthers = true)
638
    {
639
        $q = $this->em->getConnection()->createQueryBuilder();
640
        $q->select('COUNT(DISTINCT t.id) AS download_count, a.id, a.title')
641
            ->from(MAUTIC_TABLE_PREFIX.'asset_downloads', 't')
642
            ->join('t', MAUTIC_TABLE_PREFIX.'assets', 'a', 'a.id = t.asset_id')
643
            ->orderBy('download_count', 'DESC')
644
            ->groupBy('a.id')
645
            ->setMaxResults($limit);
646
647
        if (!$canViewOthers) {
648
            $q->andWhere('a.created_by = :userId')
649
                ->setParameter('userId', $this->userHelper->getUser()->getId());
650
        }
651
652
        $chartQuery = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
653
        $chartQuery->applyFilters($q, $filters);
654
        $chartQuery->applyDateFilters($q, 'date_download');
655
656
        return $q->execute()->fetchAll();
657
    }
658
659
    /**
660
     * Get a list of assets in a date range.
661
     *
662
     * @param int       $limit
663
     * @param \DateTime $dateFrom
664
     * @param \DateTime $dateTo
665
     * @param array     $filters
666
     * @param array     $options
667
     *
668
     * @return array
669
     */
670
    public function getAssetList($limit = 10, \DateTime $dateFrom = null, \DateTime $dateTo = null, $filters = [], $options = [])
671
    {
672
        $q = $this->em->getConnection()->createQueryBuilder();
673
        $q->select('t.id, t.title as name, t.date_added, t.date_modified')
674
            ->from(MAUTIC_TABLE_PREFIX.'assets', 't')
675
            ->setMaxResults($limit);
676
677
        if (!empty($options['canViewOthers'])) {
678
            $q->andWhere('t.created_by = :userId')
679
                ->setParameter('userId', $this->userHelper->getUser()->getId());
680
        }
681
682
        $chartQuery = new ChartQuery($this->em->getConnection(), $dateFrom, $dateTo);
683
        $chartQuery->applyFilters($q, $filters);
684
        $chartQuery->applyDateFilters($q, 'date_added');
685
686
        return $q->execute()->fetchAll();
687
    }
688
}
689