Skip to content

Commit d5ad8b7

Browse files
committed
Merge remote-tracking branch 'origin/ACP2E-4132' into PR_2025_09_15_muntianu
2 parents bc68a68 + 8f535ac commit d5ad8b7

File tree

11 files changed

+652
-7
lines changed

11 files changed

+652
-7
lines changed

app/code/Magento/Sitemap/Model/Batch/Sitemap.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\Sitemap\Model\Batch;
99

1010
use Magento\Config\Model\Config\Reader\Source\Deployed\DocumentRoot;
11+
use Magento\Framework\App\Filesystem\DirectoryList;
1112
use Magento\Framework\App\ObjectManager;
1213
use Magento\Framework\App\RequestInterface;
1314
use Magento\Framework\Data\Collection\AbstractDb;
@@ -141,6 +142,7 @@ public function __construct(
141142
ObjectManager::getInstance()->get(ProductConfigReader::class);
142143
$this->sitemapItemFactory = $sitemapItemFactory ??
143144
ObjectManager::getInstance()->get(SitemapItemInterfaceFactory::class);
145+
$this->tmpDirectory = $filesystem->getDirectoryWrite(DirectoryList::SYS_TMP);
144146

145147
parent::__construct(
146148
$context,
@@ -310,4 +312,18 @@ private function processSitemapItem($item): void
310312
$this->_lineCount++;
311313
$this->_fileSize += strlen($xml);
312314
}
315+
316+
/**
317+
* Get path to sitemap file
318+
*
319+
* @param string $fileName
320+
* @return string
321+
*/
322+
private function getFilePath(string $fileName): string
323+
{
324+
$path = $this->getSitemapPath() !== null ? rtrim($this->getSitemapPath(), '/') : '';
325+
$path .= '/' . $fileName;
326+
327+
return $path;
328+
}
313329
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sitemap\Model\Config\Source;
9+
10+
use Magento\Framework\Data\OptionSourceInterface;
11+
12+
/**
13+
* Source model for sitemap generation method configuration
14+
*/
15+
class GenerationMethod implements OptionSourceInterface
16+
{
17+
/**
18+
* Standard generation method constant
19+
*/
20+
public const STANDARD = 'standard';
21+
22+
/**
23+
* Batch generation method constant
24+
*/
25+
public const BATCH = 'batch';
26+
27+
/**
28+
* Return array of options as value-label pairs
29+
*
30+
* @return array
31+
*/
32+
public function toOptionArray(): array
33+
{
34+
return [
35+
['value' => self::STANDARD, 'label' => __('Standard')],
36+
['value' => self::BATCH, 'label' => __('Batch (Memory Optimized)')],
37+
];
38+
}
39+
40+
/**
41+
* Get options in "key-value" format
42+
*
43+
* @return array
44+
*/
45+
public function toArray(): array
46+
{
47+
return [
48+
self::STANDARD => __('Standard'),
49+
self::BATCH => __('Batch (Memory Optimized)'),
50+
];
51+
}
52+
}
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Sitemap\Plugin\Cron\Model\Config\Backend;
9+
10+
use Magento\Cron\Model\Config\Backend\Sitemap;
11+
use Magento\Cron\Model\Config\Source\Frequency;
12+
use Magento\Framework\App\Config\ScopeConfigInterface;
13+
use Magento\Framework\App\Config\ValueFactory;
14+
use Magento\Framework\Exception\LocalizedException;
15+
use Magento\Sitemap\Model\Config\Source\GenerationMethod;
16+
17+
/**
18+
* Plugin for Cron Sitemap backend model to ensure extended cron job configuration
19+
*/
20+
class SitemapPlugin
21+
{
22+
/**
23+
* Cron string path for sitemap schedule
24+
*/
25+
private const CRON_STRING_PATH = 'crontab/default/jobs/sitemap_generate/schedule/cron_expr';
26+
27+
/**
28+
* Cron model path
29+
*/
30+
private const CRON_MODEL_PATH = 'crontab/default/jobs/sitemap_generate/run/model';
31+
32+
/**
33+
* @var ValueFactory
34+
*/
35+
private $configValueFactory;
36+
37+
/**
38+
* @param ValueFactory $configValueFactory
39+
*/
40+
public function __construct(
41+
ValueFactory $configValueFactory
42+
) {
43+
$this->configValueFactory = $configValueFactory;
44+
}
45+
46+
/**
47+
* Plugin to override the afterSave behavior to use unified cron job
48+
*
49+
* @param Sitemap $subject
50+
* @param mixed $result
51+
* @return mixed
52+
* @throws LocalizedException
53+
*/
54+
public function afterAfterSave(
55+
Sitemap $subject,
56+
mixed $result
57+
) {
58+
$config = $subject->getConfig();
59+
60+
$time = $this->getTimeConfiguration($subject, $config);
61+
$frequency = $subject->getValue();
62+
$generationMethod = $this->getGenerationMethod($subject, $config);
63+
64+
$cronExprString = $this->buildCronExpression($time, $frequency);
65+
$observerModel = $this->getObserverModel($generationMethod);
66+
67+
$this->updateCronConfiguration($cronExprString, $observerModel);
68+
69+
return $result;
70+
}
71+
72+
/**
73+
* Get time configuration from subject or config
74+
*
75+
* @param Sitemap $subject
76+
* @param ScopeConfigInterface|null $config
77+
* @return array
78+
* @throws LocalizedException
79+
*/
80+
private function getTimeConfiguration(Sitemap $subject, $config): array
81+
{
82+
$time = $subject->getData('groups/generate/fields/time/value');
83+
if (!$time && $config) {
84+
$timeConfig = $config->getValue(
85+
'sitemap/generate/time',
86+
$subject->getScope(),
87+
$subject->getScopeId()
88+
);
89+
$time = $timeConfig ? explode(',', $timeConfig) : null;
90+
}
91+
92+
if (!$time) {
93+
$recentTimeConfig = $this->getRecentlySavedTimeConfiguration();
94+
$time = $recentTimeConfig ? explode(',', $recentTimeConfig) : null;
95+
}
96+
97+
if (!is_array($time) || empty($time)) {
98+
$time = ['0', '0', '0'];
99+
}
100+
101+
while (count($time) < 3) {
102+
$time[] = '0';
103+
}
104+
105+
return $time;
106+
}
107+
108+
/**
109+
* Get recently saved time configuration from config value factory
110+
*
111+
* @return string|null
112+
* @throws LocalizedException
113+
*/
114+
private function getRecentlySavedTimeConfiguration(): ?string
115+
{
116+
$configValue = $this->configValueFactory->create()->load(
117+
'sitemap/generate/time',
118+
'path'
119+
);
120+
121+
return $configValue->getId() ? $configValue->getValue() : null;
122+
}
123+
124+
/**
125+
* Get generation method from various sources
126+
*
127+
* @param Sitemap $subject
128+
* @param ScopeConfigInterface|null $config
129+
* @return string
130+
* @throws LocalizedException
131+
*/
132+
private function getGenerationMethod(Sitemap $subject, ?ScopeConfigInterface $config): string
133+
{
134+
$generationMethod = $subject->getData('groups/generate/fields/generation_method/value');
135+
136+
if (!$generationMethod && $config) {
137+
$generationMethod = $config->getValue(
138+
'sitemap/generate/generation_method',
139+
$subject->getScope(),
140+
$subject->getScopeId()
141+
);
142+
}
143+
144+
if (!$generationMethod) {
145+
$generationMethod = $this->getRecentlySavedGenerationMethod();
146+
}
147+
148+
return $generationMethod ?: GenerationMethod::STANDARD;
149+
}
150+
151+
/**
152+
* Get recently saved generation method from config value factory
153+
*
154+
* @return string|null
155+
* @throws LocalizedException
156+
*/
157+
private function getRecentlySavedGenerationMethod(): ?string
158+
{
159+
$configValue = $this->configValueFactory->create()->load(
160+
'sitemap/generate/generation_method',
161+
'path'
162+
);
163+
164+
return $configValue->getId() ? $configValue->getValue() : null;
165+
}
166+
167+
/**
168+
* Build cron expression from time and frequency
169+
*
170+
* @param array $time
171+
* @param string $frequency
172+
* @return string
173+
*/
174+
private function buildCronExpression(array $time, string $frequency): string
175+
{
176+
$cronExprArray = [
177+
(int)($time[1] ?? 0), //Minute
178+
(int)($time[0] ?? 0), //Hour
179+
$frequency == Frequency::CRON_MONTHLY ? '1' : '*', //Day of the Month
180+
'*', //Month of the Year
181+
$frequency == Frequency::CRON_WEEKLY ? '1' : '*', //# Day of the Week
182+
];
183+
184+
return join(' ', $cronExprArray);
185+
}
186+
187+
/**
188+
* Get observer model based on generation method
189+
*
190+
* @param string $generationMethod
191+
* @return string
192+
*/
193+
private function getObserverModel(string $generationMethod): string
194+
{
195+
return $generationMethod === GenerationMethod::BATCH
196+
? 'Magento\Sitemap\Model\Batch\Observer::scheduledGenerateSitemaps'
197+
: 'Magento\Sitemap\Model\Observer::scheduledGenerateSitemaps';
198+
}
199+
200+
/**
201+
* Update cron configuration with new values
202+
*
203+
* @param string $cronExprString
204+
* @param string $observerModel
205+
* @return void
206+
* @throws LocalizedException
207+
*/
208+
private function updateCronConfiguration(string $cronExprString, string $observerModel): void
209+
{
210+
try {
211+
$this->clearCronConfiguration(self::CRON_STRING_PATH);
212+
$this->clearCronConfiguration(self::CRON_MODEL_PATH);
213+
214+
$this->setCronConfiguration(self::CRON_STRING_PATH, $cronExprString);
215+
$this->setCronConfiguration(self::CRON_MODEL_PATH, $observerModel);
216+
} catch (\Exception $e) {
217+
throw new LocalizedException(__('We can\'t save the cron expression.'));
218+
}
219+
}
220+
221+
/**
222+
* Set cron configuration value
223+
*
224+
* @param string $path
225+
* @param string $value
226+
* @return void
227+
* @throws LocalizedException
228+
*/
229+
private function setCronConfiguration(string $path, string $value): void
230+
{
231+
$this->configValueFactory->create()->load(
232+
$path,
233+
'path'
234+
)->setValue(
235+
$value
236+
)->setPath(
237+
$path
238+
)->save();
239+
}
240+
241+
/**
242+
* Clear cron configuration value
243+
*
244+
* @param string $path
245+
* @return void
246+
* @throws LocalizedException
247+
*/
248+
private function clearCronConfiguration(string $path): void
249+
{
250+
$configValue = $this->configValueFactory->create()->load($path, 'path');
251+
if ($configValue->getId()) {
252+
$configValue->delete();
253+
}
254+
}
255+
}

app/code/Magento/Sitemap/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"magento/module-catalog-url-rewrite": "*",
1313
"magento/module-cms": "*",
1414
"magento/module-config": "*",
15+
"magento/module-cron": "*",
1516
"magento/module-eav": "*",
1617
"magento/module-media-storage": "*",
1718
"magento/module-robots": "*",

app/code/Magento/Sitemap/etc/adminhtml/system.xml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0"?>
22
<!--
33
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
4+
* Copyright 2012 Adobe
5+
* All Rights Reserved.
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
@@ -69,6 +69,14 @@
6969
<label>Enabled</label>
7070
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
7171
</field>
72+
<field id="generation_method" translate="label comment" type="select" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
73+
<label>Generation Method</label>
74+
<comment>Standard method processes all data in memory. Batch method uses memory-optimized processing for large catalogs.</comment>
75+
<source_model>Magento\Sitemap\Model\Config\Source\GenerationMethod</source_model>
76+
<depends>
77+
<field id="enabled">1</field>
78+
</depends>
79+
</field>
7280
<field id="error_email" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
7381
<label>Error Email Recipient</label>
7482
<validate>validate-email</validate>

app/code/Magento/Sitemap/etc/config.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0"?>
22
<!--
33
/**
4-
* Copyright © Magento, Inc. All rights reserved.
5-
* See COPYING.txt for license details.
4+
* Copyright 2011 Adobe
5+
* All Rights Reserved.
66
*/
77
-->
88
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
@@ -31,6 +31,7 @@
3131
</store>
3232
<generate>
3333
<enabled>0</enabled>
34+
<generation_method>standard</generation_method>
3435
<error_email />
3536
<error_email_template>sitemap_generate_error_email_template</error_email_template>
3637
<error_email_identity>general</error_email_identity>

0 commit comments

Comments
 (0)