Skip to content

Commit 67aa8d5

Browse files
[shopsys] added hreflang feature (#2970)
2 parents e7d0438 + 9598147 commit 67aa8d5

54 files changed

Lines changed: 2603 additions & 67 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import Register from '../../common/utils/Register';
2+
import {
3+
addNewItemToCollection,
4+
removeItemFromCollection
5+
} from '../validation/customization/customizeCollectionBundle';
6+
7+
export default class HreflangSetting {
8+
static init () {
9+
const $collectionItemAddButton = $('.js-hreflang-setting-item-add');
10+
const $collection = $('.js-hreflang-setting');
11+
12+
$collectionItemAddButton.off('click');
13+
$collection.off('click', '.js-hreflang-setting-item-remove');
14+
15+
$collection.on('click', '.js-hreflang-setting-item-remove', function (event) {
16+
const $collection = $(this).closest('.js-hreflang-setting');
17+
18+
const $item = $(this).closest('.js-hreflang-setting-item');
19+
const index = $item.data('index');
20+
removeItemFromCollection('.js-hreflang-setting', index);
21+
$item.remove();
22+
23+
HreflangSetting.refreshCount($collection);
24+
event.preventDefault();
25+
});
26+
27+
$collectionItemAddButton.on('click', function () {
28+
const $collection = $('.js-hreflang-setting');
29+
const index = $collection.data('index');
30+
31+
const prototype = $collection.data('prototype');
32+
const item = prototype
33+
.replace(/__name__label__/g, index)
34+
.replace(/__name__/g, index);
35+
const $item = $($.parseHTML(item));
36+
37+
$item.data('index', index);
38+
$collection.data('index', index + 1);
39+
40+
$collection.append($item);
41+
(new Register()).registerNewContent($item);
42+
43+
addNewItemToCollection('.js-hreflang-setting', index);
44+
HreflangSetting.refreshCount($collection);
45+
46+
return false;
47+
});
48+
49+
HreflangSetting.refreshCount($collection);
50+
}
51+
52+
static refreshCount ($collection) {
53+
if ($collection.find('.js-hreflang-setting-item').length === 0) {
54+
$collection.find('.js-hreflang-setting-empty-item').show();
55+
} else {
56+
$collection.find('.js-hreflang-setting-empty-item').hide();
57+
}
58+
}
59+
}
60+
61+
(new Register()).registerCallback(HreflangSetting.init, 'HreflangSetting.init');

assets/js/admin/components/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import './GridInlineEdit';
2626
import './GridMassAction';
2727
import './GridDragAndDrop';
2828
import './GridMultipleDragAndDrop';
29+
import './HreflangSetting';
2930
import './MailTemplate';
3031
import './MailWhitelist';
3132
import './MassAction';

assets/js/admin/validation/form/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ import './validationAdvert';
66
import './validationOrder';
77
import './validationProduct';
88
import './validationPromoCode';
9+
import './validationHreflangSetting';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Register from '../../../common/utils/Register';
2+
3+
export default function validationHreflangSetting () {
4+
const hreflangForm = $('#hreflang_setting_form_hreflang_collection');
5+
hreflangForm.jsFormValidator({
6+
callbacks: {
7+
validateDomainUniqueness: function () {
8+
9+
}
10+
}
11+
});
12+
}
13+
14+
(new Register()).registerCallback(validationHreflangSetting, 'validationHreflangSetting');

src/Component/Router/FriendlyUrl/FriendlyUrlFacade.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,14 @@ public function getAllSlugsByRouteNameAndEntityId(int $domainId, string $routeNa
314314
{
315315
return $this->friendlyUrlRepository->getAllSlugsByRouteNameAndDomainId($domainId, $routeName, $entityId);
316316
}
317+
318+
/**
319+
* @param int $domainId
320+
* @param string $slug
321+
* @return \Shopsys\FrameworkBundle\Component\Router\FriendlyUrl\FriendlyUrl|null
322+
*/
323+
public function findByDomainIdAndSlug(int $domainId, string $slug): ?FriendlyUrl
324+
{
325+
return $this->friendlyUrlRepository->findByDomainIdAndSlug($domainId, $slug);
326+
}
317327
}

src/Controller/Admin/SeoController.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Shopsys\FrameworkBundle\Controller\Admin;
66

77
use Shopsys\FrameworkBundle\Component\Domain\AdminDomainTabsFacade;
8+
use Shopsys\FrameworkBundle\Form\Admin\Seo\HreflangSettingFormType;
89
use Shopsys\FrameworkBundle\Form\Admin\Seo\SeoRobotsSettingFormType;
910
use Shopsys\FrameworkBundle\Form\Admin\Seo\SeoSettingFormType;
1011
use Shopsys\FrameworkBundle\Model\Seo\SeoSettingFacade;
@@ -91,4 +92,38 @@ public function robotsAction(Request $request): Response
9192
'form' => $form->createView(),
9293
]);
9394
}
95+
96+
/**
97+
* @Route("/seo/hreflang/")
98+
* @param \Symfony\Component\HttpFoundation\Request $request
99+
* @return \Symfony\Component\HttpFoundation\Response
100+
*/
101+
public function hreflangAction(Request $request): Response
102+
{
103+
$hreflangData = [
104+
HreflangSettingFormType::FIELD_HREFLANG_COLLECTION => $this->seoSettingFacade->getAllAlternativeDomains(),
105+
];
106+
107+
$form = $this->createForm(HreflangSettingFormType::class, $hreflangData)->handleRequest($request);
108+
109+
if ($form->isSubmitted() && $form->isValid()) {
110+
$hreflangData = $form->getData();
111+
112+
$this->seoSettingFacade->setAllAlternativeDomains(
113+
$hreflangData[HreflangSettingFormType::FIELD_HREFLANG_COLLECTION],
114+
);
115+
116+
$this->addSuccessFlash(t('Alternate language settings modified'));
117+
118+
return $this->redirectToRoute('admin_seo_hreflang');
119+
}
120+
121+
if ($form->isSubmitted() && !$form->isValid()) {
122+
$this->addErrorFlashTwig(t('Please check the correctness of all data filled.'));
123+
}
124+
125+
return $this->render('@ShopsysFramework/Admin/Content/Seo/hreflangSetting.html.twig', [
126+
'form' => $form->createView(),
127+
]);
128+
}
94129
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Controller\Admin;
6+
7+
use Shopsys\FrameworkBundle\Component\ConfirmDelete\ConfirmDeleteResponseFactory;
8+
use Shopsys\FrameworkBundle\Component\Domain\Domain;
9+
use Shopsys\FrameworkBundle\Component\Router\Security\Annotation\CsrfProtection;
10+
use Shopsys\FrameworkBundle\Form\Admin\Seo\SeoPageFormType;
11+
use Shopsys\FrameworkBundle\Model\Seo\Page\Exception\DefaultSeoPageCannotBeDeletedException;
12+
use Shopsys\FrameworkBundle\Model\Seo\Page\Exception\SeoPageNotFoundException;
13+
use Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageDataFactory;
14+
use Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageFacade;
15+
use Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageGridFactory;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\Response;
18+
use Symfony\Component\Routing\Annotation\Route;
19+
20+
class SeoPageController extends AdminBaseController
21+
{
22+
/**
23+
* @param \Shopsys\FrameworkBundle\Component\Domain\Domain $domain
24+
* @param \Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageGridFactory $seoPageGridFactory
25+
* @param \Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageDataFactory $seoPageDataFactory
26+
* @param \Shopsys\FrameworkBundle\Model\Seo\Page\SeoPageFacade $seoPageFacade
27+
* @param \Shopsys\FrameworkBundle\Component\ConfirmDelete\ConfirmDeleteResponseFactory $confirmDeleteResponseFactory
28+
*/
29+
public function __construct(
30+
protected readonly Domain $domain,
31+
protected readonly SeoPageGridFactory $seoPageGridFactory,
32+
protected readonly SeoPageDataFactory $seoPageDataFactory,
33+
protected readonly SeoPageFacade $seoPageFacade,
34+
protected readonly ConfirmDeleteResponseFactory $confirmDeleteResponseFactory,
35+
) {
36+
}
37+
38+
/**
39+
* @Route("/seo/page/list")
40+
* @return \Symfony\Component\HttpFoundation\Response
41+
*/
42+
public function listAction(): Response
43+
{
44+
$grid = $this->seoPageGridFactory->create($this->domain->getId());
45+
46+
return $this->render('@ShopsysFramework/Admin/Content/Seo/Page/list.html.twig', [
47+
'gridView' => $grid->createView(),
48+
]);
49+
}
50+
51+
/**
52+
* @Route("/seo/page/new")
53+
* @param \Symfony\Component\HttpFoundation\Request $request
54+
* @return \Symfony\Component\HttpFoundation\Response
55+
*/
56+
public function newAction(Request $request): Response
57+
{
58+
$seoPageData = $this->seoPageDataFactory->create();
59+
60+
$form = $this->createForm(SeoPageFormType::class, $seoPageData, [
61+
'seoPage' => null,
62+
]);
63+
$form->handleRequest($request);
64+
65+
if ($form->isSubmitted() && $form->isValid()) {
66+
$seoPage = $this->seoPageFacade->create($seoPageData);
67+
68+
$this
69+
->addSuccessFlashTwig(
70+
t('SEO Page <strong><a href="{{ url }}">{{ name }}</a></strong> created'),
71+
[
72+
'name' => $seoPage->getPageName(),
73+
'url' => $this->generateUrl('admin_seopage_edit', ['id' => $seoPage->getId()]),
74+
],
75+
);
76+
77+
return $this->redirectToRoute('admin_seopage_list');
78+
}
79+
80+
if ($form->isSubmitted() && !$form->isValid()) {
81+
$this->addErrorFlashTwig(t('Please check the correctness of all data filled.'));
82+
}
83+
84+
return $this->render('@ShopsysFramework/Admin/Content/Seo/Page/new.html.twig', [
85+
'form' => $form->createView(),
86+
]);
87+
}
88+
89+
/**
90+
* @Route("/seo/page/edit/{id}", requirements={"id" = "\d+"})
91+
* @param int $id
92+
* @param \Symfony\Component\HttpFoundation\Request $request
93+
* @return \Symfony\Component\HttpFoundation\Response
94+
*/
95+
public function editAction(int $id, Request $request): Response
96+
{
97+
$seoPage = $this->seoPageFacade->getById($id);
98+
$seoPageData = $this->seoPageDataFactory->createFromSeoPage($seoPage);
99+
100+
$form = $this->createForm(SeoPageFormType::class, $seoPageData, [
101+
'seoPage' => $seoPage,
102+
]);
103+
$form->handleRequest($request);
104+
105+
if ($form->isSubmitted() && $form->isValid()) {
106+
$this->seoPageFacade->edit($id, $seoPageData);
107+
108+
$this
109+
->addSuccessFlashTwig(
110+
t('SEO Page <strong><a href="{{ url }}">{{ name }}</a></strong> modified'),
111+
[
112+
'name' => $seoPage->getPageName(),
113+
'url' => $this->generateUrl('admin_seopage_edit', ['id' => $seoPage->getId()]),
114+
],
115+
);
116+
117+
return $this->redirectToRoute('admin_seopage_list');
118+
}
119+
120+
if ($form->isSubmitted() && !$form->isValid()) {
121+
$this->addErrorFlashTwig(t('Please check the correctness of all data filled.'));
122+
}
123+
124+
return $this->render('@ShopsysFramework/Admin/Content/Seo/Page/edit.html.twig', [
125+
'form' => $form->createView(),
126+
'seoPage' => $seoPage,
127+
]);
128+
}
129+
130+
/**
131+
* @Route("/seo/page/delete/{id}", requirements={"id" = "\d+"})
132+
* @CsrfProtection
133+
* @param int $id
134+
* @return \Symfony\Component\HttpFoundation\Response
135+
*/
136+
public function deleteAction(int $id): Response
137+
{
138+
try {
139+
$seoPage = $this->seoPageFacade->getById($id);
140+
$this->seoPageFacade->delete($id);
141+
142+
$this->addSuccessFlashTwig(
143+
t('SEO Page <strong>{{ name }}</strong> removed'),
144+
[
145+
'name' => $seoPage->getPageName(),
146+
],
147+
);
148+
} catch (SeoPageNotFoundException) {
149+
$this->addErrorFlash(t('Selected SEO page does not exist'));
150+
} catch (DefaultSeoPageCannotBeDeletedException) {
151+
$this->addErrorFlash(t('Selected SEO page cannot be deleted'));
152+
}
153+
154+
return $this->redirectToRoute('admin_seopage_list');
155+
}
156+
157+
/**
158+
* @Route("/seo/page/delete-confirm/{id}", requirements={"id" = "\d+"})
159+
* @param int $id
160+
* @return \Symfony\Component\HttpFoundation\Response
161+
*/
162+
public function deleteConfirmAction(int $id): Response
163+
{
164+
$message = t('Do you really want to remove this SEO page?');
165+
166+
return $this->confirmDeleteResponseFactory->createDeleteResponse($message, 'admin_seopage_delete', $id);
167+
}
168+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Shopsys\FrameworkBundle\Form\Admin\Seo;
6+
7+
use Shopsys\FrameworkBundle\Component\Domain\Config\DomainConfig;
8+
use Shopsys\FrameworkBundle\Component\Domain\Domain;
9+
use Symfony\Component\Form\DataTransformerInterface;
10+
11+
class DomainConfigCollectionToDomainIdsTransformer implements DataTransformerInterface
12+
{
13+
/**
14+
* @param \Shopsys\FrameworkBundle\Component\Domain\Domain $domain
15+
*/
16+
public function __construct(
17+
protected readonly Domain $domain,
18+
) {
19+
}
20+
21+
/**
22+
* @param int[][] $value
23+
* @return \Shopsys\FrameworkBundle\Component\Domain\Config\DomainConfig[][]
24+
*/
25+
public function transform($value): array
26+
{
27+
return array_map(
28+
fn (array $domainIds) => array_map(
29+
fn (int $domainId) => $this->domain->getDomainConfigById($domainId),
30+
$domainIds,
31+
),
32+
$value,
33+
);
34+
}
35+
36+
/**
37+
* @param \Shopsys\FrameworkBundle\Component\Domain\Config\DomainConfig[][] $value
38+
* @return int[][]
39+
*/
40+
public function reverseTransform($value): array
41+
{
42+
return array_map(
43+
static fn (array $domainConfigs) => array_map(
44+
static fn (DomainConfig $domainConfig) => $domainConfig->getId(),
45+
$domainConfigs,
46+
),
47+
$value,
48+
);
49+
}
50+
}

0 commit comments

Comments
 (0)