Skip to content

Commit 3c70af5

Browse files
committed
#19 migrate menus items that can depend on all items on the system
1 parent 871d773 commit 3c70af5

File tree

4 files changed

+251
-5
lines changed

4 files changed

+251
-5
lines changed

src/administrator/components/com_ccm/src/Controller/MigrationController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ public function apply()
2828
['source' => 'tags', 'target' => 'tags'],
2929
['source' => 'media', 'target' => 'media'],
3030
['source' => 'posts', 'target' => 'articles'],
31-
['source' => 'menus', 'target' => 'menus']
31+
['source' => 'menus', 'target' => 'menus'],
32+
['source' => 'menu_items', 'target' => 'menu_items']
3233
];
3334

3435
$successfulMigrations = [];

src/administrator/components/com_ccm/src/Model/MigrationModel.php

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Joomla\CMS\Http\HttpFactory;
66
use \Joomla\CMS\Http\Http;
77
use Joomla\CMS\Factory;
8+
use Joomla\CMS\Filter\OutputFilter;
89
use Reem\Component\CCM\Administrator\Helper\MigrationHelper;
910

1011
/**
@@ -18,6 +19,7 @@ class MigrationModel extends FormModel
1819
// 'categories' => ['ids' => [oldId => newId, ...]]
1920
// 'users' => ['ids' => [oldId => newId, ...]]
2021
// 'media' => ['ids' => [oldId => newId, ...], 'urls' => [oldUrl => newUrl, ...]]
22+
// 'menus' => ['ids' => [oldId => [newId, menuType], ...]]
2123
];
2224
protected $migrationMapFile;
2325
protected $http;
@@ -126,17 +128,67 @@ private function getSourceItems($sourceCms, $sourceType) {
126128
$schemaPath = dirname(__DIR__, 1) . '/Schema/';
127129
$schema = json_decode(file_get_contents($schemaPath . $sourceSchemaFile), true);
128130

129-
// Find the endpoint for this source type
130-
$endpoint = $sourceType;
131+
$endpointConfig = null;
131132
if (isset($schema['ContentItem']) && is_array($schema['ContentItem'])) {
132133
foreach ($schema['ContentItem'] as $contentItem) {
133134
if (isset($contentItem['type']) && $contentItem['type'] === $sourceType) {
134-
$endpoint = $contentItem['config']['endpoint'] ?? $sourceType;
135+
$endpointConfig = $contentItem['config'] ?? ['endpoint' => $sourceType];
135136
break;
136137
}
137138
}
138139
}
139140

141+
if (!$endpointConfig) {
142+
throw new \RuntimeException("No endpoint configuration found for source type: $sourceType");
143+
}
144+
145+
$endpoint = $endpointConfig['endpoint'];
146+
$dependsOn = $endpointConfig['depends_on'] ?? null;
147+
148+
// If the endpoint depends on another migrated type (e.g., menu items depending on menus)
149+
if ($dependsOn && isset($dependsOn['type']) && isset($dependsOn['param'])) {
150+
if (file_exists($this->migrationMapFile)) {
151+
$this->migrationMap = json_decode(file_get_contents($this->migrationMapFile), true) ?: [];
152+
}
153+
154+
$dependencyType = $dependsOn['type'];
155+
$dependencyParam = $dependsOn['param'];
156+
$dependencyIds = $this->migrationMap[$dependencyType]['ids'] ?? [];
157+
158+
if (empty($dependencyIds)) {
159+
throw new \RuntimeException("No migrated items of type '{$dependencyType}' found, which is a dependency for '{$sourceType}'.");
160+
}
161+
162+
$allItems = [];
163+
foreach ($dependencyIds as $oldId => $newId) {
164+
$dependencyEndpoint = str_replace(':' . $dependencyParam, (string) $oldId, $endpoint);
165+
$sourceEndpoint = $sourceUrl . '/' . $dependencyEndpoint;
166+
error_log("[MigrationModel] Fetching source items from: $sourceEndpoint");
167+
168+
$headers = ['Accept' => 'application/json'];
169+
if ($sourceAuthentication) {
170+
$authHeaders = MigrationHelper::parseAuthentication($sourceAuthentication);
171+
$headers = array_merge($headers, $authHeaders);
172+
}
173+
174+
$sourceResponse = $this->http->get($sourceEndpoint, $headers);
175+
$sourceResponseBody = json_decode($sourceResponse->body, true);
176+
error_log("[MigrationModel] Menu Items - Source response body: " . $sourceResponse->body);
177+
178+
$items = $sourceResponseBody['items'] ?? ($sourceResponseBody[$sourceType] ?? $sourceResponseBody);
179+
180+
if (is_array($items)) {
181+
foreach ($items as &$item) {
182+
// Inject the original dependency ID for later mapping
183+
$item[$dependencyParam] = $oldId;
184+
}
185+
$allItems = array_merge($allItems, $items);
186+
}
187+
}
188+
return $allItems;
189+
}
190+
191+
// Default flow for endpoints without dependencies
140192
$sourceEndpoint = $sourceUrl . '/' . $endpoint;
141193

142194
$headers = [
@@ -292,9 +344,84 @@ private function convertCcmToTargetCms($ccmItems, $targetCms, $targetType) {
292344
$value = $ccmMap['default'];
293345
}
294346

347+
if (empty($value) && $format) {
348+
switch ($format) {
349+
case 'alias':
350+
error_log("[MigrationModel] Formatting alias for the title: " . $ccmItem['title']);
351+
$value = OutputFilter::stringURLSafe($ccmItem['title']);
352+
break;
353+
354+
355+
case 'name_map':
356+
// Look through menus mapping to find the matching menutype
357+
foreach ($this->migrationMap['menus']['ids'] as $mapping) {
358+
if (is_array($mapping) && isset($mapping[1])) {
359+
$value = $mapping[1]; // Get the menutype value
360+
error_log("[MigrationModel] Found menutype: $value");
361+
break; // Use the first menutype found
362+
}
363+
}
364+
if (empty($value)) {
365+
error_log("[MigrationModel] No menutype found in menus mapping");
366+
}
367+
break;
368+
}
369+
}
370+
295371
// Format handling (array, url_replace, id_map)
296372
if (!empty($value) && $format) {
297373
switch ($format) {
374+
case 'link_builder':
375+
$template = $ccmMap['template'] ?? '';
376+
$params = $ccmMap['params'] ?? [];
377+
$builtLink = $template;
378+
foreach ($params as $paramKey => $paramSource) {
379+
$replaceValue = '';
380+
if ($paramSource['source'] === 'id_map') {
381+
$sourceId = $ccmItem[$paramSource['ccm_key']] ?? null;
382+
if ($sourceId) {
383+
$mapType = $paramSource['map_type'];
384+
// Determine map_type based on content type if needed
385+
if ($mapType === 'articles' && isset($ccmItem['type']) && $ccmItem['type'] === 'category') {
386+
$mapType = 'categories';
387+
}
388+
$replaceValue = $this->migrationMap[$mapType]['ids'][$sourceId] ?? '';
389+
error_log("[MigrationModel] Mapping ID for $mapType: $sourceId -> $replaceValue");
390+
}
391+
} elseif ($paramSource['source'] === 'map') {
392+
$sourceValue = $ccmItem[$paramSource['ccm_key']] ?? null;
393+
if ($sourceValue && isset($paramSource['map'][$sourceValue])) {
394+
$replaceValue = $paramSource['map'][$sourceValue];
395+
}
396+
}
397+
$builtLink = str_replace(':' . $paramKey, $replaceValue, $builtLink);
398+
}
399+
$value = $builtLink;
400+
error_log("[MigrationModel] Built link for key '$targetKey': " . json_encode($value));
401+
break;
402+
case 'object_builder':
403+
$params = $ccmMap['params'] ?? [];
404+
$requestObject = [];
405+
foreach ($params as $paramKey => $paramSource) {
406+
if ($paramSource['source'] === 'id_map') {
407+
$sourceId = $ccmItem[$paramSource['ccm_key']] ?? null;
408+
if ($sourceId) {
409+
$mapType = $paramSource['map_type'];
410+
// Determine map_type based on content type if needed
411+
if ($mapType === 'articles' && isset($ccmItem['type']) && $ccmItem['type'] === 'category') {
412+
$mapType = 'categories';
413+
}
414+
$mappedId = $this->migrationMap[$mapType]['ids'][$sourceId] ?? null;
415+
if ($mappedId) {
416+
$requestObject[$paramKey] = $mappedId;
417+
error_log("[MigrationModel] Mapped ID for request object: $sourceId -> $mappedId ($mapType)");
418+
}
419+
}
420+
}
421+
}
422+
$value = $requestObject;
423+
error_log("[MigrationModel] Built object for key '$targetKey': " . json_encode($value));
424+
break;
298425
case 'array':
299426
if (is_array($value)) {
300427
$mappedValues = [];
@@ -462,7 +589,15 @@ private function migrateItemsToTargetCms($targetCms, $targetType, $ccmToTargetIt
462589
if (!isset($this->migrationMap[$targetType]['ids'])) {
463590
$this->migrationMap[$targetType]['ids'] = [];
464591
}
465-
$this->migrationMap[$targetType]['ids'][$oldId] = $newId;
592+
593+
// For menus, store both newId and menutype
594+
if ($targetType === 'menus') {
595+
error_log("[MigrationModel] Storing menu item with menutype for item: " . json_encode($item));
596+
$menutype = $item['menutype'] ?? $item['alias'] ?? $item['slug'] ?? '';
597+
$this->migrationMap[$targetType]['ids'][$oldId] = [$newId, $menutype];
598+
} else {
599+
$this->migrationMap[$targetType]['ids'][$oldId] = $newId;
600+
}
466601
error_log("[MigrationModel] Added to ID map: $targetType.ids[$oldId] = $newId");
467602
} else {
468603
error_log("[MigrationModel] Warning: Could not extract old/new IDs for mapping");

src/administrator/components/com_ccm/src/Schema/joomla-ccm.json

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,91 @@
234234
},
235235
"menutype": "alias"
236236
}
237+
},
238+
{
239+
"type": "menu_items",
240+
"config": {
241+
"endpoint": "menus/site/items"
242+
},
243+
"properties": {
244+
"id": "id",
245+
"menutype": {
246+
"ccm": "menutype",
247+
"format": "name_map",
248+
"type": "string"
249+
},
250+
"title": "title",
251+
"alias": {
252+
"ccm": "alias",
253+
"format": "alias"
254+
},
255+
"link": {
256+
"ccm": "object_id",
257+
"format": "link_builder",
258+
"template": "index.php?option=com_content&view=:view&id=:id",
259+
"params": {
260+
"id": {
261+
"source": "id_map",
262+
"ccm_key": "object_id",
263+
"map_type": "articles"
264+
},
265+
"view": {
266+
"source": "map",
267+
"ccm_key": "type",
268+
"map": {
269+
"post": "article",
270+
"category": "category&layout=blog"
271+
}
272+
}
273+
}
274+
},
275+
"published": {
276+
"ccm": "status",
277+
"map": {
278+
"publish": 1,
279+
"draft": 0,
280+
"pending": 0,
281+
"future": 0,
282+
"private": 0,
283+
"trash": -2
284+
},
285+
"default": 1
286+
},
287+
"type": {
288+
"ccm": "type",
289+
"map": {
290+
"category": "component",
291+
"post": "component",
292+
"page": "component",
293+
"custom": "url"
294+
},
295+
"default": "component"
296+
},
297+
"component_id": {
298+
"ccm": "type",
299+
"map": {
300+
"post": "19",
301+
"category": "19",
302+
"custom": "0"
303+
},
304+
"default": "0"
305+
},
306+
"request": {
307+
"ccm": "object_id",
308+
"format": "object_builder",
309+
"params": {
310+
"id": {
311+
"source": "id_map",
312+
"ccm_key": "object_id",
313+
"map_type": "articles"
314+
}
315+
}
316+
},
317+
"language": {
318+
"ccm": "language",
319+
"default": "*"
320+
}
321+
}
237322
}
238323
]
239324
}

src/administrator/components/com_ccm/src/Schema/wordpress-ccm.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,31 @@
124124
"auto_add": "auto_add",
125125
"meta": "meta"
126126
}
127+
},
128+
{
129+
"type": "menu_items",
130+
"config": {
131+
"endpoint": "menus/:menu_id/items",
132+
"depends_on": {
133+
"type": "menus",
134+
"param": "menu_id"
135+
}
136+
},
137+
"properties": {
138+
"ID": "id",
139+
"name": "title",
140+
"type_label": "type_label",
141+
"type": "type",
142+
"status": "status",
143+
"parent": "parent",
144+
"attr_title": "attr_title",
145+
"classes": "classes",
146+
"description": "description",
147+
"target": "target",
148+
"URL": "url",
149+
"content_ID": "object_id",
150+
"meta": "meta"
151+
}
127152
}
128153
]
129154
}

0 commit comments

Comments
 (0)