Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5658321
Add forward email endpoint (#164)
TatevikGr Feb 9, 2026
1419d83
After review 0
tatevikg1 Feb 9, 2026
cd8139f
Add tests
tatevikg1 Feb 9, 2026
8b1e424
Core from dev branch
tatevikg1 Feb 9, 2026
1be4006
Feat: attachement endpoint (#166)
TatevikGr Feb 11, 2026
aacaf11
Feat: track message view (#167)
TatevikGr Feb 13, 2026
65d3e3f
Add: get subscribers endpoint
tatevikg1 Feb 26, 2026
18d6b17
Fix test
tatevikg1 Feb 27, 2026
f49ce4e
Update guzzle version to ^7.2 add filter
tatevikg1 Feb 27, 2026
e5667e8
Subscriber filter in get subscribers endpoint
tatevikg1 Mar 4, 2026
fe79ae1
PaginatedResult
tatevikg1 Mar 10, 2026
9907f4e
Remove sorting
tatevikg1 Mar 10, 2026
953c350
Add: get auth user endpoint
tatevikg1 Mar 12, 2026
b879fc6
Add: history to subscriber object
tatevikg1 Mar 13, 2026
595fa6f
Allow origin frontend
tatevikg1 Mar 17, 2026
8801ad9
Remove additionalData property from UpdateSubscriberRequest
tatevikg1 Mar 17, 2026
97d5bbd
Add dashboard_statistics
tatevikg1 Mar 19, 2026
ddaf6bd
Add more data to dashboard_statistics
tatevikg1 Mar 19, 2026
462128a
Add: PaginatedFilter
tatevikg1 Mar 23, 2026
2943771
getLists with SubscriberListFilter
tatevikg1 Mar 23, 2026
646326b
Add: update list endpoint
tatevikg1 Mar 23, 2026
ff05834
Add: owner check to subscription endpoints
tatevikg1 Mar 23, 2026
7f54de3
Fix: listId validator
tatevikg1 Mar 25, 2026
86c72fa
Fix: tests
tatevikg1 Mar 25, 2026
5eb4ac1
Add: fields to export
tatevikg1 Mar 25, 2026
3ffdae8
Export: confirmed, blacklisted filters
tatevikg1 Mar 30, 2026
1c4d23d
Import: Add autoConfirm property
tatevikg1 Mar 30, 2026
ba61d06
Add: send_start to response campaign
tatevikg1 Mar 30, 2026
c675bca
Add: as_text as_html
tatevikg1 Mar 30, 2026
f26a1b7
Add: resend_campaign endpoint
tatevikg1 Apr 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@
},
"require": {
"php": "^8.1",
"phplist/core": "dev-main",
"phplist/core": "dev-ref/campaign-processing",
Comment thread
TatevikGr marked this conversation as resolved.
Outdated
"friendsofsymfony/rest-bundle": "*",
"symfony/test-pack": "^1.0",
"symfony/process": "^6.4",
"zircote/swagger-php": "^4.11",
"ext-dom": "*",
"tatevikgr/rss-feed": "dev-main as 0.1.0"
"tatevikgr/rss-feed": "dev-main as 0.1.0",
"psr/simple-cache": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
Expand Down
100 changes: 6 additions & 94 deletions config/services/normalizers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,104 +4,16 @@ services:
autoconfigure: true
public: false

_instanceof:
Symfony\Component\Serializer\Normalizer\NormalizerInterface:
tags: [ 'serializer.normalizer' ]

Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter: ~

Symfony\Component\Serializer\Normalizer\ObjectNormalizer:
arguments:
$classMetadataFactory: '@?serializer.mapping.class_metadata_factory'
$nameConverter: '@Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter'

PhpList\RestBundle\Subscription\Serializer\SubscriberNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscriberOnlyNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Identity\Serializer\AdministratorTokenNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscriberListNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscriberHistoryNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscriptionNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Messaging\Serializer\MessageNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Messaging\Serializer\TemplateImageNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Messaging\Serializer\TemplateNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Messaging\Serializer\ListMessageNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Identity\Serializer\AdministratorNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Identity\Serializer\AdminAttributeDefinitionNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Identity\Serializer\AdminAttributeValueNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\AttributeDefinitionNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscriberAttributeValueNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Common\Serializer\CursorPaginationNormalizer:
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscribersExportRequestNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Statistics\Serializer\CampaignStatisticsNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Statistics\Serializer\ViewOpensStatisticsNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Statistics\Serializer\TopDomainsNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Statistics\Serializer\TopLocalPartsNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\UserBlacklistNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Subscription\Serializer\SubscribePageNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true

PhpList\RestBundle\Messaging\Serializer\BounceRegexNormalizer:
tags: [ 'serializer.normalizer' ]
autowire: true
PhpList\RestBundle\:
resource: '../../src/*/Serializer/*'
15 changes: 13 additions & 2 deletions config/services/services.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
services:
PhpList\RestBundle\Subscription\Service\SubscriberService:
PhpList\RestBundle\Subscription\Service\SubscriberHistoryService:
autowire: true
autoconfigure: true

PhpList\RestBundle\Subscription\Service\SubscriberHistoryService:
PhpList\Core\Domain\Messaging\Service\ForwardingGuard:
autowire: true
autoconfigure: true
public: false

PhpList\Core\Domain\Messaging\Service\ForwardDeliveryService:
autowire: true
autoconfigure: true
public: false

PhpList\Core\Domain\Messaging\Service\ForwardContentService:
autowire: true
autoconfigure: true
public: false
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition;
use PhpList\Core\Domain\Identity\Service\AdminAttributeDefinitionManager;
use PhpList\Core\Domain\Identity\Service\Manager\AdminAttributeDefinitionManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;
Expand Down
2 changes: 1 addition & 1 deletion src/Identity/Controller/AdminAttributeValueController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use PhpList\Core\Domain\Identity\Model\Administrator;
use PhpList\Core\Domain\Identity\Model\AdminAttributeDefinition;
use PhpList\Core\Domain\Identity\Model\AdminAttributeValue;
use PhpList\Core\Domain\Identity\Service\AdminAttributeManager;
use PhpList\Core\Domain\Identity\Service\Manager\AdminAttributeManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;
Expand Down
2 changes: 1 addition & 1 deletion src/Identity/Controller/AdministratorController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Identity\Model\Administrator;
use PhpList\Core\Domain\Identity\Service\AdministratorManager;
use PhpList\Core\Domain\Identity\Service\Manager\AdministratorManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Service\Provider\PaginatedDataProvider;
Expand Down
2 changes: 1 addition & 1 deletion src/Identity/Controller/PasswordResetController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Identity\Service\PasswordManager;
use PhpList\Core\Domain\Identity\Service\Manager\PasswordManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Validator\RequestValidator;
Expand Down
2 changes: 1 addition & 1 deletion src/Identity/Controller/SessionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Identity\Model\AdministratorToken;
use PhpList\Core\Domain\Identity\Service\SessionManager;
use PhpList\Core\Domain\Identity\Service\Manager\SessionManager;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Validator\RequestValidator;
Expand Down
9 changes: 2 additions & 7 deletions src/Messaging/Controller/CampaignController.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,14 @@
#[Route('/campaigns', name: 'campaign_')]
class CampaignController extends BaseController
{
private CampaignService $campaignService;
private MessageBusInterface $messageBus;

public function __construct(
Authentication $authentication,
RequestValidator $validator,
CampaignService $campaignService,
MessageBusInterface $messageBus,
private readonly CampaignService $campaignService,
private readonly MessageBusInterface $messageBus,
private readonly EntityManagerInterface $entityManager,
) {
parent::__construct($authentication, $validator);
$this->campaignService = $campaignService;
$this->messageBus = $messageBus;
}

#[Route('', name: 'get_list', methods: ['GET'])]
Expand Down
121 changes: 121 additions & 0 deletions src/Messaging/Controller/EmailForwardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

declare(strict_types=1);

namespace PhpList\RestBundle\Messaging\Controller;

use Doctrine\ORM\EntityManagerInterface;
use OpenApi\Attributes as OA;
use PhpList\Core\Domain\Messaging\Model\Dto\MessageForwardDto;
use PhpList\Core\Domain\Messaging\Model\Message;
use PhpList\Core\Domain\Messaging\Service\MessageForwardService;
use PhpList\Core\Security\Authentication;
use PhpList\RestBundle\Common\Controller\BaseController;
use PhpList\RestBundle\Common\Validator\RequestValidator;
use PhpList\RestBundle\Messaging\Request\ForwardMessageRequest;
use PhpList\RestBundle\Messaging\Serializer\ForwardingResultNormalizer;
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

/**
* This controller provides REST API for email forwarding
*
* @author Tatevik Grigoryan <tatevik@phplist.com>
*/
#[Route('/email-forward', name: 'email_forward_')]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
class EmailForwardController extends BaseController
{
public function __construct(
Authentication $authentication,
RequestValidator $validator,
private readonly EntityManagerInterface $entityManager,
private readonly MessageForwardService $messageForwardService,
private readonly ForwardingResultNormalizer $forwardingResultNormalizer,
) {
parent::__construct($authentication, $validator);
}

#[Route('/{messageId}', name: 'forward', requirements: ['messageId' => '\\d+'], methods: ['POST'])]
#[OA\Post(
path: '/api/v2/campaigns/{messageId}/forward',
description: '🚧 **Status: Beta** – This method is under development. Avoid using in production. ' .
'Queues forwarding of a campaign/message to provided recipient emails.',
summary: 'Forward a message to recipients.',
requestBody: new OA\RequestBody(
description: 'Forwarding payload',
required: true,
content: new OA\JsonContent(ref: '#/components/schemas/ForwardMessageRequest')
),
tags: ['campaigns'],
parameters: [
new OA\Parameter(
name: 'php-auth-pw',
description: 'Session key obtained from login',
in: 'header',
required: true,
schema: new OA\Schema(type: 'string')
),
new OA\Parameter(
name: 'messageId',
description: 'message ID',
in: 'path',
required: true,
schema: new OA\Schema(type: 'string')
)
],
responses: [
new OA\Response(
response: 202,
description: 'Accepted',
content: new OA\JsonContent(ref: '#/components/schemas/ForwardResult')
),
new OA\Response(
response: 403,
description: 'Failure',
content: new OA\JsonContent(ref: '#/components/schemas/UnauthorizedResponse')
),
new OA\Response(
response: 404,
description: 'Failure',
content: new OA\JsonContent(ref: '#/components/schemas/NotFoundErrorResponse')
),
new OA\Response(
response: 422,
description: 'Failure',
content: new OA\JsonContent(ref: '#/components/schemas/ValidationErrorResponse')
)
]
)]
public function forwardMessage(
Request $request,
#[MapEntity(mapping: ['messageId' => 'id'])] ?Message $message = null
): JsonResponse {
if ($message === null) {
throw $this->createNotFoundException('Campaign not found.');
}

/** @var ForwardMessageRequest $forwardRequest */
$forwardRequest = $this->validator->validate($request, ForwardMessageRequest::class);

$result = $this->messageForwardService->forward(
messageForwardDto: new MessageForwardDto(
emails: $forwardRequest->recipients,
uid: $forwardRequest->uid,
fromName: $forwardRequest->fromName,
fromEmail: $forwardRequest->fromEmail,
note: $forwardRequest->note,
),
campaign: $message,
);

$this->entityManager->flush();

return $this->json(
$this->forwardingResultNormalizer->normalize($result),
Response::HTTP_ACCEPTED
);
}
}
Loading
Loading