Skip to content

Commit

Permalink
movements of features from project-base to packages (#3735)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-rostislav authored Jan 30, 2025
2 parents f1b6b83 + c7cf5cb commit 0a9614e
Show file tree
Hide file tree
Showing 116 changed files with 4,281 additions and 388 deletions.
23 changes: 23 additions & 0 deletions assets/js/admin/components/Product.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Register from '../../common/utils/Register';
export default class Product {
static init ($container) {
Product.initializeSideNavigation($container);
Product.initProductVideos($container);
}

static initializeSideNavigation ($container) {
Expand All @@ -23,6 +24,28 @@ export default class Product {
});
});
}

static initProductVideos ($container) {
$container.filterAllNodes('.js-videos-collection').on('click', '.js-remove-row', function () {
$(this).parent().parent().remove();
});

$container.filterAllNodes('.js-videos-collection-add-row').on('click', function (event) {
const $collection = $(this).closest('.js-form-group').find('.js-videos-collection');
let index = $collection.data('index');
index++;
let prototype = $collection.data('prototype');
let item = prototype
.replace(/__name__label__/g, index)
.replace(/__name__/g, index);

let $item = $($.parseHTML(item));

$item.data('index', index);
$collection.data('index', index);
$collection.append($item);
});
}
}

(new Register()).registerCallback(Product.init, 'Product.init');
22 changes: 22 additions & 0 deletions assets/js/admin/components/advert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import Register from '../../common/utils/Register';

(new Register()).registerCallback($container => {
const getCheckedPositionName = function () {
return $('#advert_form_settings_positionName').val();
};

const initAdvertForm = function () {
const positionNamesWithCategoryTree = [
'productListSecondRow'
];

if (positionNamesWithCategoryTree.includes(getCheckedPositionName())) {
$('#advert_form_settings').find('.js-category-tree-form').closest('.form-line').show();
} else {
$('#advert_form_settings').find('.js-category-tree-form').closest('.form-line').hide();
}
};

initAdvertForm();
$('#advert_form_settings_positionName').change(initAdvertForm);
});
1 change: 1 addition & 0 deletions assets/js/admin/components/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './advert';
import './AdministratorForm';
import './AdvancedSearch';
import './AjaxConfirm';
Expand Down
3 changes: 3 additions & 0 deletions assets/js/admin/validation/form/validationPromoCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export default function validationPromoCode () {
callbacks: {
validateUniquePromoCode: function () {

},
validateUniquePromoCodeByDomain: function () {
// JS validation is not necessary
}
}
});
Expand Down
19 changes: 8 additions & 11 deletions src/Component/Domain/DomainAwareSecurityHeadersSetter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

namespace Shopsys\FrameworkBundle\Component\Domain;

use Shopsys\FrameworkBundle\Component\Setting\Setting;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class DomainAwareSecurityHeadersSetter
{
/**
* @param \Shopsys\FrameworkBundle\Component\Domain\Domain $domain
* @param \Shopsys\FrameworkBundle\Component\Setting\Setting $setting
*/
public function __construct(protected readonly Domain $domain)
{
public function __construct(
protected readonly Domain $domain,
protected readonly Setting $setting,
) {
}

/**
Expand All @@ -28,14 +32,7 @@ public function onKernelResponse(ResponseEvent $event): void
return;
}

// Do not allow to external content from non-HTTPS URLs.
// Other security features stays as if CSP was not used:
// - allow inline JavaScript and CSS
// - allow eval() function in JavaScript
// - allow data URLs
$event->getResponse()->headers->set(
'Content-Security-Policy',
"default-src https: 'unsafe-inline' 'unsafe-eval' data:",
);
$cspHeaderValue = $this->setting->get(Setting::CSP_HEADER);
$event->getResponse()->headers->set('Content-Security-Policy', $cspHeaderValue);
}
}
1 change: 1 addition & 0 deletions src/Component/Setting/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Setting
public const string IMAGE_STRUCTURE_MIGRATED_FOR_PROXY = 'imageStructureMigratedForProxy';
public const string CUSTOMER_USER_DEFAULT_GROUP_ROLE_ID = 'customerUserDefaultGroupRoleId';
public const string FILE_STRUCTURE_MIGRATED_FOR_RELATIONS = 'fileStructureMigratedForRelations';
public const string CSP_HEADER = 'cspHeader';

/**
* @var \Shopsys\FrameworkBundle\Component\Setting\SettingValue[][]
Expand Down
43 changes: 43 additions & 0 deletions src/Controller/Admin/CspHeaderController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Controller\Admin;

use Shopsys\FrameworkBundle\Component\Setting\Setting;
use Shopsys\FrameworkBundle\Form\Admin\CspHeaderSetting\CspHeaderSettingFormType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class CspHeaderController extends AdminBaseController
{
/**
* @param \Shopsys\FrameworkBundle\Component\Setting\Setting $setting
*/
public function __construct(protected readonly Setting $setting)
{
}

/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: 'superadmin/csp-header-setting/')]
public function settingAction(Request $request): Response
{
$formData = ['cspHeader' => $this->setting->get(Setting::CSP_HEADER)];

$form = $this->createForm(CspHeaderSettingFormType::class, $formData);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->setting->set(Setting::CSP_HEADER, $form->getData()['cspHeader']);
$this->addSuccessFlashTwig(t('Content-Security-Policy header has been set.'));
}

return $this->render('@ShopsysFramework/Admin/Content/CspHeader/setting.html.twig', [
'form' => $form->createView(),
]);
}
}
158 changes: 149 additions & 9 deletions src/Controller/Admin/FlagController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,185 @@

namespace Shopsys\FrameworkBundle\Controller\Admin;

use Shopsys\FrameworkBundle\Component\ConfirmDelete\ConfirmDeleteResponseFactory;
use Shopsys\FrameworkBundle\Component\Domain\Domain;
use Shopsys\FrameworkBundle\Component\Router\Security\Annotation\CsrfProtection;
use Shopsys\FrameworkBundle\Form\Admin\Product\Flag\FlagFormType;
use Shopsys\FrameworkBundle\Model\AdminNavigation\BreadcrumbOverrider;
use Shopsys\FrameworkBundle\Model\Product\Flag\Exception\FlagNotFoundException;
use Shopsys\FrameworkBundle\Model\Product\Flag\FlagDataFactory;
use Shopsys\FrameworkBundle\Model\Product\Flag\FlagFacade;
use Shopsys\FrameworkBundle\Model\Product\Flag\FlagInlineEdit;
use Shopsys\FrameworkBundle\Model\Product\Flag\FlagGridFactory;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class FlagController extends AdminBaseController
{
/**
* @param \Shopsys\FrameworkBundle\Model\Product\Flag\FlagFacade $flagFacade
* @param \Shopsys\FrameworkBundle\Model\Product\Flag\FlagInlineEdit $flagInlineEdit
* @param \Shopsys\FrameworkBundle\Model\Product\Flag\FlagGridFactory $flagGridFactory
* @param \Shopsys\FrameworkBundle\Component\ConfirmDelete\ConfirmDeleteResponseFactory $confirmDeleteResponseFactory
* @param \Shopsys\FrameworkBundle\Model\AdminNavigation\BreadcrumbOverrider $breadcrumbOverrider
* @param \Shopsys\FrameworkBundle\Model\Product\Flag\FlagDataFactory $flagDataFactory
* @param \Shopsys\FrameworkBundle\Component\Domain\Domain $domain
*/
public function __construct(
protected readonly FlagFacade $flagFacade,
protected readonly FlagInlineEdit $flagInlineEdit,
protected readonly FlagGridFactory $flagGridFactory,
protected readonly ConfirmDeleteResponseFactory $confirmDeleteResponseFactory,
protected readonly BreadcrumbOverrider $breadcrumbOverrider,
protected readonly FlagDataFactory $flagDataFactory,
protected readonly Domain $domain,
) {
}

#[Route(path: '/product/flag/list/')]
public function listAction()
{
$productInlineEdit = $this->flagInlineEdit;

$grid = $productInlineEdit->getGrid();
$grid = $this->flagGridFactory->create();

return $this->render('@ShopsysFramework/Admin/Content/Flag/list.html.twig', [
'gridView' => $grid->createView(),
]);
}

/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/product/flag/new/')]
public function newAction(Request $request): Response
{
$flagData = $this->flagDataFactory->create();

$form = $this->createForm(FlagFormType::class, $flagData, [
'flag' => null,
]);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
if (!$this->domain->hasAdminAllDomainsEnabled()) {
$this->addErrorFlash(t('Creating a record requires all domains to be enabled as domain-specific fields cannot be empty. If you want to proceed, select all domains in the Domain filter in the header first.'));

return $this->redirectToRoute('admin_flag_new');
}

$flag = $this->flagFacade->create($flagData);

$this
->addSuccessFlashTwig(
t('Flag <strong><a href="{{ url }}">{{ name }}</a></strong> created'),
[
'name' => $flag->getName(),
'url' => $this->generateUrl('admin_flag_edit', ['id' => $flag->getId()]),
],
);

return $this->redirectToRoute('admin_flag_list');
}

if ($form->isSubmitted() && !$form->isValid()) {
$this->addErrorFlashTwig(t('Please check the correctness of all data filled.'));
}

return $this->render('@ShopsysFramework/Admin/Content/Flag/new.html.twig', [
'form' => $form->createView(),
]);
}

/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param int $id
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/product/flag/edit/{id}', requirements: ['id' => '\d+'])]
public function editAction(Request $request, int $id): Response
{
$flag = $this->flagFacade->getById($id);
$flagData = $this->flagDataFactory->createFromFlag($flag);

$form = $this->createForm(FlagFormType::class, $flagData, [
'flag' => $flag,
]);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->flagFacade->edit($id, $flagData);

$this
->addSuccessFlashTwig(
t('Flag <strong><a href="{{ url }}">{{ name }}</a></strong> modified'),
[
'name' => $flag->getName(),
'url' => $this->generateUrl('admin_flag_edit', ['id' => $flag->getId()]),
],
);

return $this->redirectToRoute('admin_flag_list');
}

if ($form->isSubmitted() && !$form->isValid()) {
$this->addErrorFlashTwig(t('Please check the correctness of all data filled.'));
}

$this->breadcrumbOverrider->overrideLastItem(t('Editing flag - {{ name }}', ['{{ name }}' => $flag->getName()]));

return $this->render('@ShopsysFramework/Admin/Content/Flag/edit.html.twig', [
'form' => $form->createView(),
'flag' => $flag,
]);
}

/**
* @param int $id
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/product/flag/delete-confirm/{id}', requirements: ['id' => '\d+'])]
public function deleteConfirmAction(int $id): Response
{
try {
$flag = $this->flagFacade->getById($id);
$flagDependencies = $this->flagFacade->getFlagDependencies($flag->getId());
$hasDependency = $flagDependencies->hasPromoCodeDependency || $flagDependencies->hasSeoMixDependency;

if ($hasDependency) {
return $this->render('@ShopsysFramework/Admin/Content/Flag/deleteForbidden.html.twig', [
'hasPromoCodeDependency' => $flagDependencies->hasPromoCodeDependency,
'hasSeoMixDependency' => $flagDependencies->hasSeoMixDependency,
]);
}
$message = t('Do you really want to remove this flag?');

return $this->confirmDeleteResponseFactory->createDeleteResponse(
$message,
'admin_flag_delete',
$id,
);
} catch (FlagNotFoundException $ex) {
return new Response(t('Selected flag doesn\'t exist.'));
}
}

/**
* @CsrfProtection
* @param int $id
* @return \Symfony\Component\HttpFoundation\Response
*/
#[Route(path: '/product/flag/delete/{id}', requirements: ['id' => '\d+'])]
public function deleteAction($id)
public function deleteAction($id): Response
{
try {
$fullName = $this->flagFacade->getById($id)->getName();
$flag = $this->flagFacade->getById($id);
$fullName = $flag->getName();

$flagDependencies = $this->flagFacade->getFlagDependencies($flag->getId());

if ($flagDependencies->hasSeoMixDependency || $flagDependencies->hasPromoCodeDependency) {
$this->addErrorFlash(t('The selected flag cannot be deleted.'));

return $this->redirectToRoute('admin_flag_list');
}

$this->flagFacade->deleteById($id);

Expand All @@ -52,7 +192,7 @@ public function deleteAction($id)
'name' => $fullName,
],
);
} catch (FlagNotFoundException $ex) {
} catch (FlagNotFoundException) {
$this->addErrorFlash(t('Selected flag doesn\'t exist.'));
}

Expand Down
Loading

0 comments on commit 0a9614e

Please sign in to comment.