Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Helper;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Event\CampaignLeadChangeEvent;
|
||||
use Mautic\CampaignBundle\Helper\CampaignEventHelper;
|
||||
use Mautic\CampaignBundle\Tests\CampaignTestAbstract;
|
||||
|
||||
class CampaignEventHelperTest extends CampaignTestAbstract
|
||||
{
|
||||
public function testValidateLeadChangeTriggerWithEmptyCampaigns(): void
|
||||
{
|
||||
$eventDetails = new CampaignLeadChangeEvent(new Campaign(), [], 'badaction');
|
||||
$event = [
|
||||
'properties' => [
|
||||
'campaigns' => [],
|
||||
'action' => 'added',
|
||||
],
|
||||
'campaign' => [
|
||||
'id' => null,
|
||||
],
|
||||
];
|
||||
$result = CampaignEventHelper::validateLeadChangeTrigger($eventDetails, $event);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testValidateLeadChangeTriggerWithUnmatchingCampaignsAndInvalidAction(): void
|
||||
{
|
||||
$eventDetails = new CampaignLeadChangeEvent(new Campaign(), [], 'badaction');
|
||||
$event = [
|
||||
'properties' => [
|
||||
'campaigns' => [3],
|
||||
'action' => 'added',
|
||||
],
|
||||
'campaign' => [
|
||||
'id' => 4,
|
||||
],
|
||||
];
|
||||
$result = CampaignEventHelper::validateLeadChangeTrigger($eventDetails, $event);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testValidateLeadChangeTriggerWithMatchingCampaignsAndInvalidAction(): void
|
||||
{
|
||||
$eventDetails = new CampaignLeadChangeEvent(new Campaign(), [], 'removed');
|
||||
$event = [
|
||||
'properties' => [
|
||||
'campaigns' => [3],
|
||||
'action' => 'added',
|
||||
],
|
||||
'campaign' => [
|
||||
'id' => 3,
|
||||
],
|
||||
];
|
||||
$result = CampaignEventHelper::validateLeadChangeTrigger($eventDetails, $event);
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testValidateLeadChangeTriggerWithMatchingCampaignsAndVariousActions(): void
|
||||
{
|
||||
$actions = [
|
||||
'added' => true,
|
||||
'removed' => true,
|
||||
'invalid' => false,
|
||||
];
|
||||
|
||||
foreach ($actions as $action => $expectedResult) {
|
||||
$campaignId = 3;
|
||||
$eventDetails = new CampaignLeadChangeEvent(new Campaign(), [], $action);
|
||||
$event = [
|
||||
'properties' => [
|
||||
'campaigns' => [$campaignId, 8],
|
||||
'action' => $action,
|
||||
],
|
||||
'campaign' => [
|
||||
'id' => $campaignId,
|
||||
],
|
||||
];
|
||||
$result = CampaignEventHelper::validateLeadChangeTrigger($eventDetails, $event);
|
||||
$this->assertSame($expectedResult, $result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Helper;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Entity\LeadEventLog;
|
||||
use Mautic\CampaignBundle\EventCollector\Accessor\Event\AbstractEventAccessor;
|
||||
use Mautic\CampaignBundle\Helper\ChannelExtractor;
|
||||
|
||||
class ChannelExtractorTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testChannelIsSet(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$config = $this->createMock(AbstractEventAccessor::class);
|
||||
$config->expects($this->once())
|
||||
->method('getChannel')
|
||||
->willReturn('email');
|
||||
|
||||
$log = new LeadEventLog();
|
||||
ChannelExtractor::setChannel($log, $event, $config);
|
||||
|
||||
$this->assertEquals('email', $log->getChannel());
|
||||
}
|
||||
|
||||
public function testChannelIsIgnoredIfSet(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$config = $this->createMock(AbstractEventAccessor::class);
|
||||
$config->expects($this->never())
|
||||
->method('getChannel');
|
||||
|
||||
$log = new LeadEventLog();
|
||||
$log->setChannel('page');
|
||||
ChannelExtractor::setChannel($log, $event, $config);
|
||||
|
||||
$this->assertEquals('page', $log->getChannel());
|
||||
}
|
||||
|
||||
public function testChannelIdIsSet(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setProperties(['email' => 1]);
|
||||
$config = $this->createMock(AbstractEventAccessor::class);
|
||||
$config->expects($this->once())
|
||||
->method('getChannel')
|
||||
->willReturn('email');
|
||||
|
||||
$config->expects($this->once())
|
||||
->method('getChannelIdField')
|
||||
->willReturn('email');
|
||||
|
||||
$log = new LeadEventLog();
|
||||
ChannelExtractor::setChannel($log, $event, $config);
|
||||
|
||||
$this->assertEquals('email', $log->getChannel());
|
||||
$this->assertEquals(1, $log->getChannelId());
|
||||
}
|
||||
|
||||
public function testChannelIdIsIgnoredIfPropertiesAreEmpty(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setProperties(null);
|
||||
$config = $this->createMock(AbstractEventAccessor::class);
|
||||
$config->expects($this->once())
|
||||
->method('getChannel')
|
||||
->willReturn('email');
|
||||
|
||||
$config->expects($this->once())
|
||||
->method('getChannelIdField')
|
||||
->willReturn('email');
|
||||
|
||||
$log = new LeadEventLog();
|
||||
ChannelExtractor::setChannel($log, $event, $config);
|
||||
|
||||
$this->assertEquals('email', $log->getChannel());
|
||||
$this->assertEquals(null, $log->getChannelId());
|
||||
}
|
||||
|
||||
public function testChannelIdIsIgnoredIfChannelIdFieldIsNotSet(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$event->setProperties(['email' => 1]);
|
||||
$config = $this->createMock(AbstractEventAccessor::class);
|
||||
$config->expects($this->once())
|
||||
->method('getChannel')
|
||||
->willReturn('email');
|
||||
|
||||
$config->expects($this->once())
|
||||
->method('getChannelIdField')
|
||||
->willReturn(null);
|
||||
|
||||
$log = new LeadEventLog();
|
||||
ChannelExtractor::setChannel($log, $event, $config);
|
||||
|
||||
$this->assertEquals('email', $log->getChannel());
|
||||
$this->assertEquals(null, $log->getChannelId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Helper;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Entity\EventRepository;
|
||||
use Mautic\CampaignBundle\Entity\LeadEventLog;
|
||||
use Mautic\CampaignBundle\Entity\LeadEventLogRepository;
|
||||
use Mautic\CampaignBundle\Entity\LeadRepository;
|
||||
use Mautic\CampaignBundle\Executioner\ContactFinder\InactiveContactFinder;
|
||||
use Mautic\CampaignBundle\Executioner\Helper\DecisionHelper;
|
||||
use Mautic\CampaignBundle\Executioner\Helper\InactiveHelper;
|
||||
use Mautic\CampaignBundle\Executioner\Scheduler\EventScheduler;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class InactiveHelperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var EventScheduler|MockObject
|
||||
*/
|
||||
private MockObject $scheduler;
|
||||
|
||||
/**
|
||||
* @var InactiveContactFinder|MockObject
|
||||
*/
|
||||
private MockObject $inactiveContactFinder;
|
||||
|
||||
/**
|
||||
* @var LeadEventLogRepository|MockObject
|
||||
*/
|
||||
private MockObject $eventLogRepository;
|
||||
|
||||
/**
|
||||
* @var EventRepository|MockObject
|
||||
*/
|
||||
private MockObject $eventRepository;
|
||||
|
||||
/**
|
||||
* @var LeadRepository|MockObject
|
||||
*/
|
||||
private MockObject $leadRepository;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface|MockObject
|
||||
*/
|
||||
private MockObject $logger;
|
||||
|
||||
private InactiveHelper $inactiveHelper;
|
||||
|
||||
private DecisionHelper $decisionHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->scheduler = $this->createMock(EventScheduler::class);
|
||||
$this->inactiveContactFinder = $this->createMock(InactiveContactFinder::class);
|
||||
$this->eventLogRepository = $this->createMock(LeadEventLogRepository::class);
|
||||
$this->eventRepository = $this->createMock(EventRepository::class);
|
||||
$this->leadRepository = $this->createMock(LeadRepository::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->decisionHelper = new DecisionHelper($this->leadRepository);
|
||||
$this->inactiveHelper = new InactiveHelper(
|
||||
$this->scheduler,
|
||||
$this->inactiveContactFinder,
|
||||
$this->eventLogRepository,
|
||||
$this->eventRepository,
|
||||
$this->logger,
|
||||
$this->decisionHelper
|
||||
);
|
||||
}
|
||||
|
||||
public function testRemoveContactsThatAreNotApplicable(): void
|
||||
{
|
||||
$lastActiveEventId = 6;
|
||||
|
||||
// lead not applicable because of parent negative path taken
|
||||
$leadNegative = new Lead();
|
||||
$leadNegative->setId(9);
|
||||
|
||||
// lead not applicable because of parent positive path taken
|
||||
$leadNegative2 = new Lead();
|
||||
$leadNegative2->setId(10);
|
||||
|
||||
// applicable lead
|
||||
$leadPositive = new Lead();
|
||||
$leadPositive->setId(12);
|
||||
|
||||
// lead not applicable because of no parent event log
|
||||
$leadNegative3 = new Lead();
|
||||
$leadNegative3->setId(11);
|
||||
|
||||
$this->eventLogRepository->expects($this->once())
|
||||
->method('getDatesExecuted')
|
||||
->willReturn([
|
||||
$leadNegative->getId() => \DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-28 21:37:00'),
|
||||
$leadNegative2->getId() => \DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-28 21:37:00'),
|
||||
$leadPositive->getId() => \DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-28 21:37:00'),
|
||||
$leadNegative3->getId() => \DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-28 21:37:00'),
|
||||
]);
|
||||
|
||||
/** @var LeadEventLog&MockObject */
|
||||
$log = $this->createMock(LeadEventLog::class);
|
||||
$log->expects($this->exactly(3))
|
||||
->method('getNonActionPathTaken')
|
||||
->willReturnOnConsecutiveCalls(1, 0, 1);
|
||||
|
||||
/** @var Campaign&MockObject */
|
||||
$campaign = $this->createMock(Campaign::class);
|
||||
$campaign->expects($this->any())
|
||||
->method('getId')
|
||||
->willReturn(2);
|
||||
|
||||
/** @var Event&MockObject */
|
||||
$parentEvent = $this->createMock(Event::class);
|
||||
$parentEvent->expects($this->exactly(4))
|
||||
->method('getLogByContactAndRotation')
|
||||
->willReturnOnConsecutiveCalls($log, $log, $log, null);
|
||||
|
||||
$event = new Event();
|
||||
$event->setParent($parentEvent);
|
||||
$event->setDecisionPath('yes');
|
||||
$event->setCampaign($campaign);
|
||||
$event->setEventType(Event::TYPE_DECISION);
|
||||
|
||||
$parentEvent->expects($this->any())
|
||||
->method('getNegativeChildren')
|
||||
->willReturnOnConsecutiveCalls(new ArrayCollection(), new ArrayCollection([$event]));
|
||||
|
||||
$parentEvent->expects($this->any())
|
||||
->method('getPositiveChildren')
|
||||
->willReturnOnConsecutiveCalls(new ArrayCollection(), new ArrayCollection());
|
||||
|
||||
$this->leadRepository->expects($this->exactly(4))
|
||||
->method('getContactRotations')
|
||||
->willReturn([]);
|
||||
|
||||
$this->scheduler->expects($this->any())
|
||||
->method('getExecutionDateTime')
|
||||
->willReturn(\DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-30 12:00:00'));
|
||||
|
||||
$now = \DateTime::createFromFormat('Y-m-d H:i:s', '2022-05-31 12:00:00');
|
||||
$contacts = new ArrayCollection([
|
||||
$leadNegative->getId() => $leadNegative,
|
||||
$leadNegative2->getId() => $leadNegative2,
|
||||
$leadPositive->getId() => $leadPositive,
|
||||
$leadNegative3->getId() => $leadNegative3,
|
||||
]);
|
||||
|
||||
$this->inactiveHelper->removeContactsThatAreNotApplicable(
|
||||
$now,
|
||||
$contacts,
|
||||
$lastActiveEventId,
|
||||
new ArrayCollection([new Event()]),
|
||||
$event
|
||||
);
|
||||
|
||||
$this->assertEquals(1, $contacts->count());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\CampaignBundle\Tests\Helper;
|
||||
|
||||
use Mautic\CampaignBundle\Entity\Campaign;
|
||||
use Mautic\CampaignBundle\Entity\Event;
|
||||
use Mautic\CampaignBundle\Executioner\Helper\NotificationHelper;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Model\NotificationModel;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use Symfony\Component\Routing\Router;
|
||||
|
||||
class NotificationHelperTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|UserModel
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $userModel;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|NotificationModel
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $notificationModel;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|Router
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $router;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|Translator
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $translator;
|
||||
|
||||
/**
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|CoreParametersHelper
|
||||
*/
|
||||
private \PHPUnit\Framework\MockObject\MockObject $coreParametersHelper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->userModel = $this->createMock(UserModel::class);
|
||||
|
||||
$this->notificationModel = $this->createMock(NotificationModel::class);
|
||||
|
||||
$this->router = $this->createMock(Router::class);
|
||||
|
||||
$this->translator = $this->createMock(Translator::class);
|
||||
|
||||
$this->coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
}
|
||||
|
||||
public function testContactOwnerIsNotified(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$campaign = new Campaign();
|
||||
$event->setCampaign($campaign);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$user->method('getId')
|
||||
->willReturn('1');
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->expects($this->once())
|
||||
->method('getOwner')
|
||||
->willReturn($user);
|
||||
|
||||
$this->userModel->expects($this->never())
|
||||
->method('getEntity');
|
||||
|
||||
$this->userModel->expects($this->never())
|
||||
->method('getSystemAdministrator');
|
||||
|
||||
$this->notificationModel->expects($this->once())
|
||||
->method('addNotification')
|
||||
->with(
|
||||
' / ',
|
||||
'error',
|
||||
false,
|
||||
$this->anything(),
|
||||
null,
|
||||
null,
|
||||
$user
|
||||
);
|
||||
|
||||
$this->getNotificationHelper()->notifyOfFailure($lead, $event);
|
||||
}
|
||||
|
||||
public function testCampaignCreatorIsNotified(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$campaign = new Campaign();
|
||||
$event->setCampaign($campaign);
|
||||
$campaign->setCreatedBy(1);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$user->method('getId')
|
||||
->willReturn('1');
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->expects($this->once())
|
||||
->method('getOwner')
|
||||
->willReturn(null);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getEntity')
|
||||
->willReturn($user);
|
||||
|
||||
$this->userModel->expects($this->never())
|
||||
->method('getSystemAdministrator');
|
||||
|
||||
$this->notificationModel->expects($this->once())
|
||||
->method('addNotification')
|
||||
->with(
|
||||
' / ',
|
||||
'error',
|
||||
false,
|
||||
$this->anything(),
|
||||
null,
|
||||
null,
|
||||
$user
|
||||
);
|
||||
|
||||
$this->getNotificationHelper()->notifyOfFailure($lead, $event);
|
||||
}
|
||||
|
||||
public function testSystemAdminIsNotified(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$campaign = new Campaign();
|
||||
$event->setCampaign($campaign);
|
||||
$campaign->setCreatedBy(2);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
$user->method('getId')
|
||||
->willReturn('1');
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->expects($this->once())
|
||||
->method('getOwner')
|
||||
->willReturn(null);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getEntity')
|
||||
->willReturn(null);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getSystemAdministrator')
|
||||
->willReturn($user);
|
||||
|
||||
$this->notificationModel->expects($this->once())
|
||||
->method('addNotification')
|
||||
->with(
|
||||
' / ',
|
||||
'error',
|
||||
false,
|
||||
$this->anything(),
|
||||
null,
|
||||
null,
|
||||
$user
|
||||
);
|
||||
|
||||
$this->getNotificationHelper()->notifyOfFailure($lead, $event);
|
||||
}
|
||||
|
||||
public function testNotificationIgnoredIfUserNotFound(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$campaign = new Campaign();
|
||||
$event->setCampaign($campaign);
|
||||
$campaign->setCreatedBy(2);
|
||||
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->expects($this->once())
|
||||
->method('getOwner')
|
||||
->willReturn(null);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getEntity')
|
||||
->willReturn(null);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getSystemAdministrator')
|
||||
->willReturn(null);
|
||||
|
||||
$this->notificationModel->expects($this->never())
|
||||
->method('addNotification');
|
||||
|
||||
$this->getNotificationHelper()->notifyOfFailure($lead, $event);
|
||||
}
|
||||
|
||||
public function testNotificationOfUnpublishToAuthor(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$user = $this->createMock(User::class);
|
||||
$this->prepareCommonMocks($event, $user);
|
||||
|
||||
$this->coreParametersHelper
|
||||
->method('get')
|
||||
->with('campaign_send_notification_to_author')
|
||||
->willReturn(1);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('emailUser')
|
||||
->with($user, 'test', 'test');
|
||||
|
||||
$this->userModel->expects($this->never())
|
||||
->method('sendMailToEmailAddresses');
|
||||
|
||||
$this->getNotificationHelper()->notifyOfUnpublish($event);
|
||||
}
|
||||
|
||||
public function testNotificationOfUnpublishToEmailAddress(): void
|
||||
{
|
||||
$event = new Event();
|
||||
$user = $this->createMock(User::class);
|
||||
$this->prepareCommonMocks($event, $user);
|
||||
|
||||
$emails = 'a@test.co, b@test.co';
|
||||
$this->coreParametersHelper->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnMap([
|
||||
['campaign_send_notification_to_author', null, 0],
|
||||
['campaign_notification_email_addresses', null, $emails],
|
||||
]);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('sendMailToEmailAddresses')
|
||||
->with(array_map('trim', explode(',', $emails)), 'test', 'test');
|
||||
|
||||
$this->userModel->expects($this->never())
|
||||
->method('emailUser');
|
||||
|
||||
$this->getNotificationHelper()->notifyOfUnpublish($event);
|
||||
}
|
||||
|
||||
private function prepareCommonMocks(Event $event, User $user): void
|
||||
{
|
||||
$campaign = new Campaign();
|
||||
$event->setCampaign($campaign);
|
||||
$campaign->setCreatedBy(2);
|
||||
|
||||
$user = $this->createMock(User::class);
|
||||
|
||||
$lead = $this->createMock(Lead::class);
|
||||
$lead->expects($this->any())
|
||||
->method('getOwner')
|
||||
->willReturn(null);
|
||||
|
||||
$user->expects($this->once())
|
||||
->method('getId')
|
||||
->willReturn(1);
|
||||
|
||||
$this->userModel->expects($this->once())
|
||||
->method('getEntity')
|
||||
->willReturn($user);
|
||||
|
||||
$this->translator
|
||||
->expects($this->any())
|
||||
->method('trans')
|
||||
->willReturn('test');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NotificationHelper
|
||||
*/
|
||||
private function getNotificationHelper()
|
||||
{
|
||||
return new NotificationHelper(
|
||||
$this->userModel,
|
||||
$this->notificationModel,
|
||||
$this->translator,
|
||||
$this->router,
|
||||
$this->coreParametersHelper
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user