Completed
Push — ezp-30696 ( 9bb3ad...3bd812 )
by
unknown
49:02 queued 18:35
created

PageConverter::generateXmlString()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 9
nop 1
dl 0
loc 38
rs 7.7564
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the Page converter.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
10
11
use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter;
12
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue;
13
use eZ\Publish\SPI\Persistence\Content\FieldValue;
14
use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition;
15
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition;
16
use eZ\Publish\Core\FieldType\FieldSettings;
17
use eZ\Publish\Core\FieldType\Page\Parts;
18
use DOMDocument;
19
use DOMElement;
20
21
class PageConverter implements Converter
22
{
23
    /**
24
     * Converts data from $value to $storageFieldValue.
25
     *
26
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value
27
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue
28
     */
29
    public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue)
30
    {
31
        $storageFieldValue->dataText = $value->data === null
32
            ? null
33
            : $this->generateXmlString($value->data);
34
    }
35
36
    /**
37
     * Converts data from $value to $fieldValue.
38
     *
39
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
40
     * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue
41
     */
42
    public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue)
43
    {
44
        $fieldValue->data = $value->dataText === null
0 ignored issues
show
Documentation Bug introduced by
It seems like $value->dataText === nul...tring($value->dataText) of type object<eZ\Publish\Core\FieldType\Page\Parts\Page> is incompatible with the declared type integer|double|boolean|string|null|array of property $data.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
45
            ? null
46
            : $this->restoreValueFromXmlString($value->dataText);
47
    }
48
49
    /**
50
     * Converts field definition data in $fieldDef into $storageFieldDef.
51
     *
52
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
53
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
54
     */
55
    public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef)
56
    {
57
        $storageDef->dataText1 = (isset($fieldDef->fieldTypeConstraints->fieldSettings['defaultLayout'])
58
            ? $fieldDef->fieldTypeConstraints->fieldSettings['defaultLayout']
59
            : '');
60
    }
61
62
    /**
63
     * Converts field definition data in $storageDef into $fieldDef.
64
     *
65
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef
66
     * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef
67
     */
68
    public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef)
69
    {
70
        $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings(
71
            [
72
                'defaultLayout' => $storageDef->dataText1,
73
            ]
74
        );
75
    }
76
77
    /**
78
     * Returns the name of the index column in the attribute table.
79
     *
80
     * Returns the name of the index column the datatype uses, which is either
81
     * "sort_key_int" or "sort_key_string". This column is then used for
82
     * filtering and sorting for this type.
83
     *
84
     * @return string
85
     */
86
    public function getIndexColumn()
87
    {
88
        return false;
89
    }
90
91
    /**
92
     * Generates XML string from $page object to be stored in storage engine.
93
     *
94
     * @param \eZ\Publish\Core\FieldType\Page\Parts\Page $page
95
     *
96
     * @return string
97
     */
98
    public function generateXmlString(Parts\Page $page)
99
    {
100
        $dom = new DOMDocument('1.0', 'utf-8');
101
        $dom->formatOutput = true;
102
        $dom->loadXML('<page />');
103
104
        $pageNode = $dom->documentElement;
105
106
        foreach ($page->getState() as $attrName => $attrValue) {
107
            switch ($attrName) {
108
                case 'id':
109
                    $pageNode->setAttribute('id', $attrValue);
110
                    break;
111
                case 'zones':
112
                    foreach ($page->{$attrName} as $zone) {
113
                        $pageNode->appendChild($this->generateZoneXmlString($zone, $dom));
114
                    }
115
                    break;
116
                case 'layout':
117
                    $node = $dom->createElement('zone_layout', $attrValue);
118
                    $pageNode->appendChild($node);
119
                    break;
120
                case 'attributes':
121
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
122
                        $this->addNewXmlElement($dom, $pageNode, $arrayItemKey, $arrayItemValue);
123
                    }
124
                    break;
125
                case 'zonesById':
126
                    // Do not store
127
                    break;
128
                default:
129
                    $this->addNewNotEmptyXmlElement($dom, $pageNode, $attrName, $attrValue);
130
                    break;
131
            }
132
        }
133
134
        return $dom->saveXML();
135
    }
136
137
    /**
138
     * Generates XML string for a given $zone object.
139
     *
140
     * @param \eZ\Publish\Core\FieldType\Page\Parts\Zone $zone
141
     * @param \DOMDocument $dom
142
     *
143
     * @return \DOMElement
144
     */
145
    protected function generateZoneXmlString(Parts\Zone $zone, DOMDocument $dom)
146
    {
147
        $zoneNode = $dom->createElement('zone');
148
        foreach ($zone->getState() as $attrName => $attrValue) {
149
            switch ($attrName) {
150
                case 'id':
151
                    $zoneNode->setAttribute('id', 'id_' . $attrValue);
152
                    break;
153
                case 'action':
154
                    if ($attrValue !== null) {
155
                        $zoneNode->setAttribute('action', $attrValue);
156
                    }
157
                    break;
158
                case 'identifier':
159
                    $this->addNewXmlElement($dom, $zoneNode, 'zone_identifier', $attrValue);
160
                    break;
161
                case 'blocks':
162
                    foreach ($zone->{$attrName} as $block) {
163
                        $zoneNode->appendChild($this->generateBlockXmlString($block, $dom));
164
                    }
165
                    break;
166
                case 'attributes':
167
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
168
                        $this->addNewXmlElement($dom, $zoneNode, $arrayItemKey, $arrayItemValue);
169
                    }
170
                    break;
171
                case 'blocksById':
172
                    // Do not store
173
                    break;
174
                default:
175
                    $this->addNewNotEmptyXmlElement($dom, $zoneNode, $attrName, $attrValue);
176
                    break;
177
            }
178
        }
179
180
        return $zoneNode;
181
    }
182
183
    /**
184
     * Generates XML string for a given $block object.
185
     *
186
     * @param \eZ\Publish\Core\FieldType\Page\Parts\Block $block
187
     * @param \DOMDocument $dom
188
     *
189
     * @return \DOMElement
190
     */
191
    protected function generateBlockXmlString(Parts\Block $block, DOMDocument $dom)
192
    {
193
        $blockNode = $dom->createElement('block');
194
195
        foreach ($block->getState() as $attrName => $attrValue) {
196
            switch ($attrName) {
197
                case 'id':
198
                    $blockNode->setAttribute('id', 'id_' . $attrValue);
199
                    break;
200
                case 'zoneId':
201
                    $blockNode->appendChild($dom->createElement('zone_id', $attrValue));
202
                    break;
203
                case 'action':
204
                    if ($attrValue !== null) {
205
                        $blockNode->setAttribute('action', $attrValue);
206
                    }
207
                    break;
208
                case 'items':
209
                    foreach ($block->items as $item) {
210
                        $itemNode = $this->generateItemXmlString($item, $dom);
211
                        if ($itemNode) {
212
                            $blockNode->appendChild($itemNode);
213
                        }
214
                    }
215
                    break;
216
                case 'overflowId':
217
                    $this->addNewXmlElement($dom, $blockNode, 'overflow_id', $attrValue);
218
                    break;
219 View Code Duplication
                case 'rotation':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
220
                    if ($attrValue === null) {
221
                        continue 2;
222
                    }
223
224
                    $node = $dom->createElement($attrName);
225
                    $blockNode->appendChild($node);
226
227
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
228
                        $this->addNewXmlElement($dom, $node, $arrayItemKey, $arrayItemValue);
229
                    }
230
                    break;
231 View Code Duplication
                case 'customAttributes':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
232
                    if ($attrValue === null) {
233
                        continue 2;
234
                    }
235
236
                    $node = $dom->createElement('custom_attributes');
237
                    $blockNode->appendChild($node);
238
239
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
240
                        $this->addNewXmlElement($dom, $node, $arrayItemKey, $arrayItemValue);
241
                    }
242
                    break;
243
                case 'attributes':
244
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
245
                        $this->addNewXmlElement($dom, $blockNode, $arrayItemKey, $arrayItemValue);
246
                    }
247
                    break;
248
                default:
249
                    $this->addNewNotEmptyXmlElement($dom, $blockNode, $attrName, $attrValue);
250
                    break;
251
            }
252
        }
253
254
        return $blockNode;
255
    }
256
257
    /**
258
     * Generates XML string for a given $item object.
259
     *
260
     * @param \eZ\Publish\Core\FieldType\Page\Parts\Item $item
261
     * @param \DOMDocument $dom
262
     *
263
     * @return bool|\DOMElement
264
     */
265
    protected function generateItemXmlString(Parts\Item $item, DOMDocument $dom)
266
    {
267
        $itemNode = $dom->createElement('item');
268
269
        foreach ($item->getState() as $attrName => $attrValue) {
270
            switch ($attrName) {
271
                case 'action':
272
                    if ($attrValue !== null) {
273
                        $itemNode->setAttribute('action', $attrValue);
274
                    }
275
                    break;
276
                case 'contentId':
277
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'object_id', $attrValue);
278
                    break;
279
                case 'locationId':
280
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'node_id', $attrValue);
281
                    break;
282
                case 'priority':
283
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'priority', $attrValue);
284
                    break;
285
                case 'publicationDate':
286
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'ts_publication', $attrValue);
287
                    break;
288
                case 'visibilityDate':
289
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'ts_visible', $attrValue);
290
                    break;
291
                case 'hiddenDate':
292
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'ts_hidden', $attrValue);
293
                    break;
294
                case 'rotationUntilDate':
295
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'rotation_until', $attrValue);
296
                    break;
297
                case 'movedTo':
298
                    $this->addNewNotEmptyXmlElement($dom, $itemNode, 'moved_to', $attrValue);
299
                    break;
300
                case 'attributes':
301
                    foreach ($attrValue as $arrayItemKey => $arrayItemValue) {
302
                        $this->addNewNotEmptyXmlElement($dom, $itemNode, $arrayItemKey, $arrayItemValue);
303
                    }
304
                    break;
305
            }
306
        }
307
308
        return $itemNode;
309
    }
310
311
    /**
312
     * Utility method to add new elements to an xml node if their value is not empty.
313
     *
314
     * @param \DOMDocument $dom xml document
315
     * @param \DOMElement $node where to add the new element
316
     * @param string $name of the new element
317
     * @param string $value of the new element
318
     */
319
    private function addNewNotEmptyXmlElement(DOMDocument $dom, DOMElement $node, $name, $value)
320
    {
321
        if (!empty($value)) {
322
            $this->addNewXmlElement($dom, $node, $name, $value);
323
        }
324
    }
325
326
    /**
327
     * Utility method to add new elements to an xml node.
328
     *
329
     * @param \DOMDocument $dom xml document
330
     * @param \DOMElement $node where to add the new element
331
     * @param string $name of the new element
332
     * @param string $value of the new element
333
     */
334
    private function addNewXmlElement(DOMDocument $dom, DOMElement $node, $name, $value)
335
    {
336
        $new = $dom->createElement($name);
337
        $new->appendChild($dom->createTextNode($value));
338
        $node->appendChild($new);
339
    }
340
341
    /**
342
     * Restores value from XML string.
343
     *
344
     * @param string $xmlString
345
     *
346
     * @return \eZ\Publish\Core\FieldType\Page\Parts\Page
347
     */
348
    public function restoreValueFromXmlString($xmlString)
349
    {
350
        $zones = [];
351
        $attributes = [];
352
        $layout = null;
353
354
        if ($xmlString) {
355
            $dom = new DOMDocument('1.0', 'utf-8');
356
            $dom->loadXML($xmlString);
357
            $root = $dom->documentElement;
358
359 View Code Duplication
            foreach ($root->childNodes as $node) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
360
                if ($node->nodeType !== XML_ELEMENT_NODE) {
361
                    continue;
362
                }
363
364
                switch ($node->nodeName) {
365
                    case 'zone':
366
                        $zone = $this->restoreZoneFromXml($node);
367
                        $zones[] = $zone;
368
                        break;
369
                    case 'zone_layout':
370
                        $layout = $node->nodeValue;
371
                        break;
372
                    default:
373
                        $attributes[$node->nodeName] = $node->nodeValue;
374
                        break;
375
                }
376
            }
377
378
            if ($root->hasAttributes()) {
379
                foreach ($root->attributes as $attr) {
380
                    $attributes[$attr->name] = $attr->value;
381
                }
382
            }
383
        }
384
385
        return new Parts\Page(
386
            [
387
                'zones' => $zones,
388
                'layout' => $layout,
389
                'attributes' => $attributes,
390
            ]
391
        );
392
    }
393
394
    /**
395
     * Restores value for a given Zone $node.
396
     *
397
     * @param \DOMElement $node
398
     *
399
     * @return \eZ\Publish\Core\FieldType\Page\Parts\Zone
400
     */
401
    protected function restoreZoneFromXml(DOMElement $node)
402
    {
403
        $zoneId = null;
404
        $zoneIdentifier = null;
405
        $action = null;
406
        $blocks = [];
407
        $attributes = [];
408
409 View Code Duplication
        if ($node->hasAttributes()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
410
            foreach ($node->attributes as $attr) {
411
                switch ($attr->name) {
412
                    case 'id':
413
                        // Stored Id has following format : id_<zoneId>, so extract <zoneId>
414
                        $zoneId = substr(
415
                            $attr->value,
416
                            strpos($attr->value, '_') + 1
417
                        );
418
                        break;
419
                    case 'action':
420
                        $action = $attr->value;
421
                        break;
422
                    default:
423
                        $attributes[$attr->name] = $attr->value;
424
                }
425
            }
426
        }
427
428 View Code Duplication
        foreach ($node->childNodes as $node) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
            if ($node->nodeType !== XML_ELEMENT_NODE) {
430
                continue;
431
            }
432
433
            switch ($node->nodeName) {
434
                case 'block':
435
                    $block = $this->restoreBlockFromXml($node);
436
                    $blocks[] = $block;
437
                    break;
438
                case 'zone_identifier':
439
                    $zoneIdentifier = $node->nodeValue;
440
                    break;
441
                default:
442
                    $attributes[$node->nodeName] = $node->nodeValue;
443
            }
444
        }
445
446
        return new Parts\Zone(
447
            [
448
                'id' => $zoneId,
449
                'identifier' => $zoneIdentifier,
450
                'attributes' => $attributes,
451
                'action' => $action,
452
                'blocks' => $blocks,
453
            ]
454
        );
455
    }
456
457
    /**
458
     * Restores value for a given Block $node.
459
     *
460
     * @param \DOMElement $node
461
     *
462
     * @return \eZ\Publish\Core\FieldType\Page\Parts\Block
463
     */
464
    protected function restoreBlockFromXml(DOMElement $node)
465
    {
466
        $blockId = null;
467
        $items = [];
468
        $rotation = null;
469
        $customAttributes = null;
470
        $attributes = [];
471
        $name = null;
472
        $type = null;
473
        $view = null;
474
        $overflowId = null;
475
        $action = null;
476
        $zoneId = null;
477
478 View Code Duplication
        if ($node->hasAttributes()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
479
            foreach ($node->attributes as $attr) {
480
                switch ($attr->name) {
481
                    case 'id':
482
                        // Stored Id has following format : id_<blockId>, so extract <blockId>
483
                        $blockId = substr(
484
                            $attr->value,
485
                            strpos($attr->value, '_') + 1
486
                        );
487
                        break;
488
                    case 'action':
489
                        $action = $attr->value;
490
                        break;
491
                    default:
492
                        $attributes[$attr->name] = $attr->value;
493
                }
494
            }
495
        }
496
497
        foreach ($node->childNodes as $node) {
498
            if ($node->nodeType !== XML_ELEMENT_NODE) {
499
                continue;
500
            }
501
502
            switch ($node->nodeName) {
503
                case 'item':
504
                    $items[] = $this->restoreItemFromXml($node);
505
                    break;
506 View Code Duplication
                case 'rotation':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
507
                    if ($rotation === null) {
508
                        $rotation = [];
509
                    }
510
511
                    foreach ($node->childNodes as $subNode) {
512
                        if ($subNode->nodeType !== XML_ELEMENT_NODE) {
513
                            continue;
514
                        }
515
516
                        $rotation[$subNode->nodeName] = $subNode->nodeValue;
517
                    }
518
                    break;
519 View Code Duplication
                case 'custom_attributes':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
520
                    if ($customAttributes === null) {
521
                        $customAttributes = [];
522
                    }
523
524
                    foreach ($node->childNodes as $subNode) {
525
                        if ($subNode->nodeType !== XML_ELEMENT_NODE) {
526
                            continue;
527
                        }
528
529
                        $customAttributes[$subNode->nodeName] = $subNode->nodeValue;
530
                    }
531
                    break;
532
                case 'name':
533
                case 'type':
534
                case 'view':
535
                    ${$node->nodeName} = $node->nodeValue;
536
                    break;
537
                case 'overflow_id':
538
                    $overflowId = $node->nodeValue;
539
                    break;
540
                case 'zone_id':
541
                    $zoneId = $node->nodeValue;
542
                    break;
543
                default:
544
                    $attributes[$node->nodeName] = $node->nodeValue;
545
            }
546
        }
547
548
        return new Parts\Block(
549
            [
550
                'id' => $blockId,
551
                'action' => $action,
552
                'items' => $items,
553
                'rotation' => $rotation,
554
                'customAttributes' => $customAttributes,
555
                'attributes' => $attributes,
556
                'name' => $name,
557
                'type' => $type,
558
                'view' => $view,
559
                'overflowId' => $overflowId,
560
                'zoneId' => $zoneId,
561
            ]
562
        );
563
    }
564
565
    /**
566
     * Restores value for a given Item $node.
567
     *
568
     * @param \DOMElement $node
569
     *
570
     * @return \eZ\Publish\Core\FieldType\Page\Parts\Item
571
     */
572
    protected function restoreItemFromXml(DOMElement $node)
573
    {
574
        $item = ['attributes' => []];
575
576
        if ($node->hasAttributes()) {
577
            foreach ($node->attributes as $attr) {
578
                switch ($attr->name) {
579
                    case 'action':
580
                        $item['action'] = $attr->value;
581
                        break;
582
                    default:
583
                        $item['attributes'][$attr->name] = $attr->value;
584
                }
585
            }
586
        }
587
588
        foreach ($node->childNodes as $node) {
589
            if ($node->nodeType !== XML_ELEMENT_NODE) {
590
                continue;
591
            }
592
593
            switch ($node->nodeName) {
594
                case 'object_id':
595
                    $item['contentId'] = $node->nodeValue;
596
                    break;
597
                case 'node_id':
598
                    $item['locationId'] = $node->nodeValue;
599
                    break;
600
                case 'priority':
601
                    $item[$node->nodeName] = $node->nodeValue;
602
                    break;
603
                case 'ts_publication':
604
                    $item['publicationDate'] = $node->nodeValue;
605
                    break;
606
                case 'ts_visible':
607
                    $item['visibilityDate'] = $node->nodeValue;
608
                    break;
609
                case 'ts_hidden':
610
                    $item['hiddenDate'] = $node->nodeValue;
611
                    break;
612
                case 'rotation_until':
613
                    $item['rotationUntilDate'] = $node->nodeValue;
614
                    break;
615
                case 'moved_to':
616
                    $item['movedTo'] = $node->nodeValue;
617
                    break;
618
            }
619
        }
620
621
        return new Parts\Item($item);
622
    }
623
}
624