Passed
Push — main ( 17877b...431d09 )
by
unknown
01:35 queued 24s
created

ImportSitemapCommand::buildOptionParser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 12
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 16
rs 9.8666
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita Brevia plugin
6
 *
7
 * Copyright 2024 Atlas Srl
8
 */
9
namespace Brevia\BEdita\Command;
10
11
use BEdita\Core\Utility\LoggedUser;
12
use Brevia\BEdita\Client\BreviaClient;
13
use Brevia\BEdita\Utility\ReadCSVTrait;
14
use Cake\Command\Command;
15
use Cake\Console\Arguments;
16
use Cake\Console\ConsoleIo;
17
use Cake\Console\ConsoleOptionParser;
18
use Cake\Log\LogTrait;
19
use Cake\ORM\Table;
20
use Cake\Utility\Hash;
21
22
/**
23
 * Import links from sitemap and create links
24
 *
25
 * @property \BEdita\Core\Model\Table\ObjectsTable $Collections
26
 */
27
class ImportSitemapCommand extends Command
28
{
29
    use LogTrait;
30
    use ReadCSVTrait;
31
32
    /**
33
     * Brevia API client
34
     *
35
     * @var \Brevia\BEdita\Client\BreviaClient
36
     */
37
    protected BreviaClient $client;
38
39
    /**
40
     * Links Table
41
     *
42
     * @var \Cake\ORM\Table
43
     */
44
    protected Table $Links;
45
46
    /**
47
     * @inheritDoc
48
     */
49
    public $defaultTable = 'Collections';
50
51
    /**
52
     * @inheritDoc
53
     */
54
    protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
55
    {
56
        return $parser->addOption('sitemap', [
57
                'help' => 'File path or URL of sitemap to import',
58
                'short' => 's',
59
                'required' => true,
60
            ])
61
            ->addOption('prefix', [
62
                'help' => 'Optional path prefix of URLs to import',
63
                'short' => 'p',
64
                'required' => false,
65
            ])
66
            ->addOption('collection', [
67
                'help' => 'Collection used to index (use the unique collection name)',
68
                'short' => 'c',
69
                'required' => true,
70
            ]);
71
    }
72
73
    /**
74
     * @inheritDoc
75
     */
76
    public function initialize(): void
77
    {
78
        $this->client = new BreviaClient();
79
        $this->Links = $this->fetchTable('Links');
80
    }
81
82
    /**
83
     * @inheritDoc
84
     */
85
    public function execute(Arguments $args, ConsoleIo $io)
86
    {
87
        $sitemap = $args->getOption('sitemap');
88
        if (!file_exists($sitemap)) {
89
            $io->abort(sprintf('File not found: %s', $sitemap));
90
        }
91
        $content = file_get_contents($sitemap);
92
93
        $name = $args->getOption('collection');
94
        $response = $this->client->get('/collections', compact('name'));
95
        $collectionId = Hash::get($response->getJson(), '0.cmetadata.id');
96
        if (empty($collectionId)) {
97
            $io->abort(sprintf('Collection not found: %s', $name));
98
        }
99
        $collection = $this->Collections->get($collectionId, ['contain' => ['HasDocuments']]);
100
        $currentUrls = array_filter(array_map(function ($link) {
101
                $link = $link->getTable()->get($link->id);
102
103
                return $link->get('url');
104
        },
105
            (array)$collection->get('has_documents')));
106
        $prefix = $args->getOption('prefix');
107
108
        $xml = simplexml_load_string($content);
109
        $json = json_encode($xml);
110
        $data = (array)json_decode($json, true);
111
        $urls = Hash::extract($data, 'url.{n}.loc');
112
        if (empty($urls)) {
113
            $io->abort('No URLs found in sitemap');
114
        }
115
        $entities = [];
116
        LoggedUser::setUserAdmin();
117
        foreach ($urls as $url) {
118
            if (
119
                in_array($url, $currentUrls) ||
120
                in_array(urldecode($url), $currentUrls) ||
121
                ($prefix && strpos($url, $prefix) !== 0)
122
            ) {
123
                continue;
124
            }
125
            $io->info('Adding link: ' . $url);
126
            $data = [
127
                'status' => 'on',
128
                'title' => $url,
129
                'url' => $url,
130
                'extra' => [
131
                    'brevia' => [
132
                        'metadata' => [
133
                            'type' => 'links',
134
                            'url' => $url,
135
                        ],
136
                        'options' => $this->linkOptions($url, (array)$collection->get('link_load_options')),
137
                    ],
138
                ],
139
            ];
140
            $entity = $this->Links->newEntity($data);
141
            $entities[] = $this->Links->saveOrFail($entity);
142
        }
143
        // @phpstan-ignore-next-line
144
        $this->Collections->addRelated($collection, 'has_documents', $entities);
145
146
        $io->out('Done. Link added successfully: ' . count($entities));
147
148
        return null;
149
    }
150
151
    /**
152
     * Get link options
153
     *
154
     * @param string $url URL
155
     * @param array $linkLoadOptions Link load options
156
     * @return array
157
     */
158
    protected function linkOptions(string $url, array $linkLoadOptions): array
159
    {
160
        $options = array_filter($linkLoadOptions, function ($o) use ($url) {
161
            return $o['url'] === $url;
162
        });
163
        $selector = Hash::get($options, '0.selector');
164
        if (!empty($selector)) {
165
            return compact('selector');
166
        }
167
        $options = array_filter($linkLoadOptions, function ($o) use ($url) {
168
            return strpos($url, $o['url']) === 0;
169
        });
170
171
        return ['selector' => Hash::get($options, '0.selector')];
172
    }
173
}
174