Skip to content

Commit 1bf4ed6

Browse files
Merge branch '7.x' into UXUI-160-a-page-to-see-all-contacts-tagged-as-dnc-or-search-command
2 parents e271759 + f0be028 commit 1bf4ed6

File tree

132 files changed

+8551
-630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+8551
-630
lines changed

.all-contributorsrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,15 @@
15891589
"contributions": [
15901590
"userTesting"
15911591
]
1592+
},
1593+
{
1594+
"login": "stone-creator",
1595+
"name": "Stone-creator",
1596+
"avatar_url": "https://avatars.githubusercontent.com/u/1327019?v=4",
1597+
"profile": "https://github.com/stone-creator",
1598+
"contributions": [
1599+
"userTesting"
1600+
]
15921601
}
15931602
],
15941603
"contributorsPerLine": 7,

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
348348
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PedroLoureiro1"><img src="https://avatars.githubusercontent.com/u/116733776?v=4?s=100" width="100px;" alt="PedroLoureiro1"/><br /><sub><b>PedroLoureiro1</b></sub></a><br /><a href="https://github.com/mautic/mautic/commits?author=PedroLoureiro1" title="Code">💻</a></td>
349349
<td align="center" valign="top" width="14.28%"><a href="https://github.com/OlgaMarchuk"><img src="https://avatars.githubusercontent.com/u/219189660?v=4?s=100" width="100px;" alt="OlgaMarchuk"/><br /><sub><b>OlgaMarchuk</b></sub></a><br /><a href="#userTesting-OlgaMarchuk" title="User Testing">📓</a></td>
350350
</tr>
351+
<tr>
352+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/stone-creator"><img src="https://avatars.githubusercontent.com/u/1327019?v=4?s=100" width="100px;" alt="Stone-creator"/><br /><sub><b>Stone-creator</b></sub></a><br /><a href="#userTesting-stone-creator" title="User Testing">📓</a></td>
353+
</tr>
351354
</tbody>
352355
</table>
353356

UPGRADE-7.0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
### PHP
5959
- Removed `Mautic\DashboardBundle\Dashboard\Widget::FORMAT_MYSQL` constant. Use `DateTimeHelper::FORMAT_DB_DATE_ONLY` instead.
6060
- Removed `Mautic\ApiBundle\Security\OAuth2\Firewall::OAuthListener` class as it was empty. Use `FOS\OAuthServerBundle\Security\Firewall\OAuthListener` instead.
61+
- Removed `Mautic\LeadBundle\Segment\Query\Filter\SegmentReferenceFilterQueryBuilder` as unused.
6162

6263
### Javascript
6364
- Removed `Mautic.insertTextInEditor` function. Use `Mautic.insertHtmlInEditor` instead.

app/bundles/AssetBundle/Entity/Asset.php

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Mautic\AssetBundle\Entity;
44

5-
use ApiPlatform\Core\Annotation\ApiResource;
65
use Doctrine\DBAL\Types\Types;
76
use Doctrine\ORM\Mapping as ORM;
87
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
@@ -22,31 +21,14 @@
2221
use Symfony\Component\Validator\Context\ExecutionContextInterface;
2322
use Symfony\Component\Validator\Mapping\ClassMetadata;
2423

25-
/**
26-
* @ApiResource(
27-
* attributes={
28-
* "security"="false",
29-
* "normalization_context"={
30-
* "groups"={
31-
* "asset:read"
32-
* },
33-
* "swagger_definition_name"="Read",
34-
* "api_included"={"category"}
35-
* },
36-
* "denormalization_context"={
37-
* "groups"={
38-
* "asset:write"
39-
* },
40-
* "swagger_definition_name"="Write"
41-
* }
42-
* }
43-
* )
44-
*/
4524
class Asset extends FormEntity implements UuidInterface
4625
{
4726
use UuidTrait;
27+
4828
use ProjectTrait;
4929

30+
public const ENTITY_NAME = 'asset';
31+
5032
/**
5133
* @var int|null
5234
*
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mautic\AssetBundle\Event;
6+
7+
use Mautic\CoreBundle\Event\CommonEvent;
8+
9+
final class AssetExportListEvent extends CommonEvent
10+
{
11+
/**
12+
* @var array<string>
13+
*/
14+
private array $list = [];
15+
16+
/**
17+
* @param list<array<string, array<string, mixed>>> $data
18+
*/
19+
public function __construct(private array $data)
20+
{
21+
}
22+
23+
/**
24+
* @return list<array<string, array<string, mixed>>>
25+
*/
26+
public function getEntityData(): array
27+
{
28+
return $this->data;
29+
}
30+
31+
public function setList(string $item): void
32+
{
33+
if (!in_array($item, $this->list)) {
34+
$this->list[] = $item;
35+
}
36+
}
37+
38+
/**
39+
* @return array<string>|null
40+
*/
41+
public function getList(): ?array
42+
{
43+
return $this->list ?? null;
44+
}
45+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mautic\AssetBundle\EventListener;
6+
7+
use Mautic\AssetBundle\Entity\Asset;
8+
use Mautic\AssetBundle\Event\AssetExportListEvent;
9+
use Mautic\CoreBundle\Helper\PathsHelper;
10+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
11+
12+
final class AssetExportListEventSubscriber implements EventSubscriberInterface
13+
{
14+
public function __construct(private PathsHelper $pathsHelper)
15+
{
16+
}
17+
18+
public static function getSubscribedEvents(): array
19+
{
20+
return [
21+
AssetExportListEvent::class => ['onExportList', 0],
22+
];
23+
}
24+
25+
public function onExportList(AssetExportListEvent $event): void
26+
{
27+
$data = $event->getEntityData();
28+
29+
if (empty($data)) {
30+
return;
31+
}
32+
33+
foreach ($event->getEntityData() as $section) {
34+
if (!is_array($section)) {
35+
continue;
36+
}
37+
38+
if (!isset($section[Asset::ENTITY_NAME]) || !is_array($section[Asset::ENTITY_NAME])) {
39+
continue;
40+
}
41+
42+
foreach ($section[Asset::ENTITY_NAME] as $asset) {
43+
$location = $asset['storage_location'] ?? null;
44+
$path = $asset['path'] ?? null;
45+
46+
if ('local' === $location && !empty($path)) {
47+
$assetPath = $this->pathsHelper->getSystemPath('media').'/files/'.$path;
48+
$event->setList($assetPath);
49+
}
50+
}
51+
}
52+
}
53+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mautic\AssetBundle\EventListener;
6+
7+
use Doctrine\ORM\EntityManagerInterface;
8+
use Mautic\AssetBundle\Entity\Asset;
9+
use Mautic\AssetBundle\Model\AssetModel;
10+
use Mautic\CoreBundle\Event\EntityExportEvent;
11+
use Mautic\CoreBundle\Event\EntityImportAnalyzeEvent;
12+
use Mautic\CoreBundle\Event\EntityImportEvent;
13+
use Mautic\CoreBundle\Event\EntityImportUndoEvent;
14+
use Mautic\CoreBundle\EventListener\ImportExportTrait;
15+
use Mautic\CoreBundle\Helper\IpLookupHelper;
16+
use Mautic\CoreBundle\Model\AuditLogModel;
17+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
18+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
19+
20+
final class AssetImportExportSubscriber implements EventSubscriberInterface
21+
{
22+
use ImportExportTrait;
23+
24+
public function __construct(
25+
private AssetModel $assetModel,
26+
private EntityManagerInterface $entityManager,
27+
private AuditLogModel $auditLogModel,
28+
private IpLookupHelper $ipLookupHelper,
29+
private DenormalizerInterface $serializer,
30+
) {
31+
}
32+
33+
public static function getSubscribedEvents(): array
34+
{
35+
return [
36+
EntityExportEvent::class => ['onAssetExport', 0],
37+
EntityImportEvent::class => ['onAssetImport', 0],
38+
EntityImportUndoEvent::class => ['onUndoImport', 0],
39+
EntityImportAnalyzeEvent::class => ['onDuplicationCheck', 0],
40+
];
41+
}
42+
43+
public function onAssetExport(EntityExportEvent $event): void
44+
{
45+
if (Asset::ENTITY_NAME !== $event->getEntityName()) {
46+
return;
47+
}
48+
49+
$assetId = $event->getEntityId();
50+
$asset = $this->assetModel->getEntity($assetId);
51+
if (!$asset) {
52+
return;
53+
}
54+
55+
$assetData = [
56+
'id' => $asset->getId(),
57+
'is_published' => $asset->isPublished(),
58+
'title' => $asset->getTitle(),
59+
'description' => $asset->getDescription(),
60+
'alias' => $asset->getAlias(),
61+
'storage_location' => $asset->getStorageLocation(),
62+
'path' => $asset->getPath(),
63+
'remote_path' => $asset->getRemotePath(),
64+
'original_file_name' => $asset->getOriginalFileName(),
65+
'lang' => $asset->getLanguage(),
66+
'publish_up' => $asset->getPublishUp() ? $asset->getPublishUp()->format(DATE_ATOM) : null,
67+
'publish_down' => $asset->getPublishDown() ? $asset->getPublishDown()->format(DATE_ATOM) : null,
68+
'extension' => $asset->getExtension(),
69+
'mime' => $asset->getMime(),
70+
'size' => (int) $asset->getSize(),
71+
'disallow' => $asset->getDisallow(),
72+
'uuid' => $asset->getUuid(),
73+
];
74+
75+
$event->addEntity(Asset::ENTITY_NAME, $assetData);
76+
$log = [
77+
'bundle' => 'asset',
78+
'object' => 'asset',
79+
'objectId' => $asset->getId(),
80+
'action' => 'export',
81+
'details' => $assetData,
82+
'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest(),
83+
];
84+
$this->auditLogModel->writeToLog($log);
85+
}
86+
87+
public function onAssetImport(EntityImportEvent $event): void
88+
{
89+
if (Asset::ENTITY_NAME !== $event->getEntityName() || !$event->getEntityData()) {
90+
return;
91+
}
92+
93+
$stats = [
94+
EntityImportEvent::NEW => ['names' => [], 'ids' => [], 'count' => 0],
95+
EntityImportEvent::UPDATE => ['names' => [], 'ids' => [], 'count' => 0],
96+
];
97+
98+
foreach ($event->getEntityData() as $element) {
99+
$object = $this->entityManager->getRepository(Asset::class)->findOneBy(['uuid' => $element['uuid']]);
100+
$isNew = !$object;
101+
102+
$object ??= new Asset();
103+
if (isset($element['size'])) {
104+
$element['size'] = (int) $element['size'];
105+
}
106+
$this->serializer->denormalize(
107+
$element,
108+
Asset::class,
109+
null,
110+
['object_to_populate' => $object]
111+
);
112+
$this->assetModel->saveEntity($object);
113+
114+
$event->addEntityIdMap((int) $element['id'], $object->getId());
115+
116+
$status = $isNew ? EntityImportEvent::NEW : EntityImportEvent::UPDATE;
117+
$stats[$status]['names'][] = $object->getTitle();
118+
$stats[$status]['ids'][] = $object->getId();
119+
++$stats[$status]['count'];
120+
121+
$this->logAction('import', $object->getId(), $element);
122+
}
123+
foreach ($stats as $status => $info) {
124+
if ($info['count'] > 0) {
125+
$event->setStatus($status, [Asset::ENTITY_NAME => $info]);
126+
}
127+
}
128+
}
129+
130+
public function onUndoImport(EntityImportUndoEvent $event): void
131+
{
132+
if (Asset::ENTITY_NAME !== $event->getEntityName()) {
133+
return;
134+
}
135+
136+
$summary = $event->getSummary();
137+
138+
if (!isset($summary['ids']) || empty($summary['ids'])) {
139+
return;
140+
}
141+
foreach ($summary['ids'] as $id) {
142+
$entity = $this->entityManager->getRepository(Asset::class)->find($id);
143+
144+
if ($entity) {
145+
$this->entityManager->remove($entity);
146+
$this->logAction('undo_import', $id, ['deletedEntity' => Asset::class]);
147+
}
148+
}
149+
150+
$this->entityManager->flush();
151+
}
152+
153+
public function onDuplicationCheck(EntityImportAnalyzeEvent $event): void
154+
{
155+
$this->performDuplicationCheck(
156+
$event,
157+
Asset::ENTITY_NAME,
158+
Asset::class,
159+
'title',
160+
$this->entityManager
161+
);
162+
}
163+
164+
/**
165+
* @param array<string, mixed> $details
166+
*/
167+
private function logAction(string $action, int $objectId, array $details): void
168+
{
169+
$this->auditLogModel->writeToLog([
170+
'bundle' => 'asset',
171+
'object' => 'asset',
172+
'objectId' => $objectId,
173+
'action' => $action,
174+
'details' => $details,
175+
'ipAddress' => $this->ipLookupHelper->getIpAddressFromRequest(),
176+
]);
177+
}
178+
}

app/bundles/AssetBundle/Resources/views/Asset/details.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
{{- include('@MauticCore/Helper/graph_dateselect.html.twig', {'dateRangeForm' : dateRangeForm, 'class' : 'pull-right'}) -}}
206206
</div>
207207
</div>
208-
<div class="pt-0 pl-15 pb-10 pr-15">
208+
<div class="d-flex fd-column pt-0 pl-15 pb-15 pr-15 min-h-256">
209209
{{- include('@MauticCore/Helper/chart.html.twig', {'chartData' : stats.downloads.timeStats, 'chartType' : 'line', 'chartHeight' : 300}) -}}
210210
</div>
211211
</div>

0 commit comments

Comments
 (0)