Passed
Push — master ( dca2fc...188ee9 )
by
unknown
20:49 queued 08:39
created

OnlyofficeAppsettings   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 134
dl 0
loc 357
rs 2.96
c 3
b 0
f 0
wmc 68

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getLinkToDocs() 0 3 1
A __construct() 0 5 1
F getSetting() 0 129 50
B getAlternateKeys() 0 23 7
B setSetting() 0 47 7
A getServerUrl() 0 3 1
A isSettingUrl() 0 10 1

How to fix   Complexity   

Complex Class

Complex classes like OnlyofficeAppsettings often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OnlyofficeAppsettings, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * (c) Copyright Ascensio System SIA 2025.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
use Chamilo\CoreBundle\Entity\AccessUrlRelPlugin;
19
use Chamilo\CoreBundle\Entity\Plugin as PluginEntity;
20
use Chamilo\CoreBundle\Framework\Container;
21
use Doctrine\ORM\Exception\NotSupported;
22
use Doctrine\ORM\Exception\ORMException;
23
use Doctrine\ORM\OptimisticLockException;
24
use Onlyoffice\DocsIntegrationSdk\Manager\Settings\SettingsManager;
25
26
class OnlyofficeAppsettings extends SettingsManager
27
{
28
    /**
29
     * Link to Docs Cloud.
30
     *
31
     * @var string
32
     */
33
    public const LINK_TO_DOCS = 'https://www.onlyoffice.com/docs-registration.aspx?referer=chamilo';
34
    /**
35
     * The settings key for the document server address.
36
     *
37
     * @var string
38
     */
39
    public $documentServerUrl = 'document_server_url';
40
41
    /**
42
     * The config key for the jwt header.
43
     *
44
     * @var string
45
     */
46
    public $jwtHeader = 'onlyoffice_jwt_header';
47
48
    /**
49
     * The config key for the internal url.
50
     *
51
     * @var string
52
     */
53
    public $documentServerInternalUrl = 'onlyoffice_internal_url';
54
55
    /**
56
     * The config key for the storage url.
57
     *
58
     * @var string
59
     */
60
    public $storageUrl = 'onlyoffice_storage_url';
61
62
    /**
63
     * The config key for the demo data flag.
64
     *
65
     * @var string
66
     */
67
    public $useDemoName = 'onlyoffice_connect_demo_data';
68
69
    /**
70
     * Chamilo plugin instance (legacy plugin API).
71
     *
72
     * @var Plugin
73
     */
74
    public $plugin;
75
76
    /**
77
     * Runtime settings coming from the form submit (take precedence).
78
     *
79
     * @var array|null
80
     */
81
    public $newSettings;
82
83
    /**
84
     * The config key for JWT secret key.
85
     *
86
     * @var string
87
     */
88
    protected $jwtKey = 'jwt_secret';
89
90
    /**
91
     * Constructor.
92
     *
93
     * @param Plugin     $plugin
94
     * @param array|null $newSettings
95
     */
96
    public function __construct(Plugin $plugin, ?array $newSettings = null)
97
    {
98
        parent::__construct();
99
        $this->plugin = $plugin;
100
        $this->newSettings = $newSettings;
101
    }
102
103
    /**
104
     * Return a configuration value by name, checking (in order):
105
     * 1) Runtime overrides ($this->newSettings)
106
     * 2) Persisted configuration in access_url_rel_plugin.configuration
107
     * 3) Special cases / defaults
108
     * 4) Legacy plugin storage ($plugin->get())
109
     * 5) Global configuration (api_get_configuration_value)
110
     *
111
     * All access to DB-backed configuration is null-safe per access URL.
112
     *
113
     * @param string $settingName
114
     *
115
     * @return mixed|null
116
     */
117
    public function getSetting($settingName)
118
    {
119
        try {
120
            $em = Database::getManager();
121
            $repo = $em->getRepository(PluginEntity::class);
122
123
            // Find plugin entity (handle common casings)
124
            $pluginEntity = $repo->findOneBy(['title' => 'Onlyoffice'])
125
                ?: $repo->findOneBy(['title' => 'OnlyOffice']);
126
127
            // Load configuration array for current access URL (null-safe)
128
            $configuration = null;
129
            if ($pluginEntity) {
130
                $currentUrl = Container::getAccessUrlUtil()->getCurrent();
131
                $rel = $pluginEntity->getConfigurationsByAccessUrl($currentUrl); // might be null
132
                $configuration = $rel ? $rel->getConfiguration() : null; // might be null
133
            }
134
135
            // 1) Runtime overrides take precedence (posted form values)
136
            if (null !== $this->newSettings) {
137
                // Exact key
138
                if (array_key_exists($settingName, $this->newSettings)) {
139
                    $val = $this->newSettings[$settingName];
140
                    if ($this->isSettingUrl($settingName) && is_string($val)) {
141
                        $val = $this->processUrl($val);
142
                    }
143
                    if ($val !== '' && $val !== null) {
144
                        return $val;
145
                    }
146
                }
147
148
                // Allow prefix-less variant (e.g. 'document_server_url' when setting is 'Onlyoffice_document_server_url')
149
                $prefix = $this->plugin->get_name();
150
                if (0 === strpos((string) $settingName, $prefix.'_')) {
151
                    $stripped = substr($settingName, strlen($prefix) + 1);
152
                    if (array_key_exists($stripped, $this->newSettings)) {
153
                        $val = $this->newSettings[$stripped];
154
                        if ($this->isSettingUrl($stripped) && is_string($val)) {
155
                            $val = $this->processUrl($val);
156
                        }
157
                        if ($val !== '' && $val !== null) {
158
                            return $val;
159
                        }
160
                    }
161
                }
162
163
                // Try alternate key mappings (legacy ↔ short names)
164
                foreach ($this->getAlternateKeys($settingName) as $alt) {
165
                    if (array_key_exists($alt, $this->newSettings)) {
166
                        $val = $this->newSettings[$alt];
167
                        if ($this->isSettingUrl($alt) && is_string($val)) {
168
                            $val = $this->processUrl($val);
169
                        }
170
                        if ($val !== '' && $val !== null) {
171
                            return $val;
172
                        }
173
                    }
174
                }
175
            }
176
177
            // 2) Persisted configuration in access_url_rel_plugin
178
            if (is_array($configuration)) {
179
                // Exact key
180
                if (array_key_exists($settingName, $configuration)) {
181
                    $val = $configuration[$settingName];
182
                    if ($this->isSettingUrl($settingName) && is_string($val)) {
183
                        $val = $this->processUrl($val);
184
                    }
185
                    if ($val !== '' && $val !== null) {
186
                        return $val;
187
                    }
188
                }
189
                // Alternate keys
190
                foreach ($this->getAlternateKeys($settingName) as $alt) {
191
                    if (array_key_exists($alt, $configuration)) {
192
                        $val = $configuration[$alt];
193
                        if ($this->isSettingUrl($alt) && is_string($val)) {
194
                            $val = $this->processUrl($val);
195
                        }
196
                        if ($val !== '' && $val !== null) {
197
                            return $val;
198
                        }
199
                    }
200
                }
201
            }
202
203
            // 3) Special cases / defaults
204
            switch ($settingName) {
205
                case $this->jwtHeader:
206
                    // Legacy platform aggregated setting (array by plugin name)
207
                    $settings = api_get_setting($settingName);
208
                    $val = is_array($settings) && array_key_exists($this->plugin->get_name(), $settings)
209
                        ? $settings[$this->plugin->get_name()]
210
                        : null;
211
                    return $val ?: 'Authorization';
212
213
                case $this->documentServerInternalUrl:
214
                    // Legacy platform aggregated setting (array by plugin name)
215
                    $settings = api_get_setting($settingName);
216
                    $val = is_array($settings) ? ($settings[$this->plugin->get_name()] ?? null) : null;
217
                    return $val;
218
219
                case $this->useDemoName:
220
                    // Explicit fallback from per-URL configuration
221
                    return (is_array($configuration) && array_key_exists($settingName, $configuration))
222
                        ? $configuration[$settingName]
223
                        : null;
224
225
                // Note: $this->jwtPrefix may be declared by the parent SDK class.
226
                case $this->jwtPrefix:
227
                    return 'Bearer ';
228
            }
229
230
            // 4) Legacy plugin storage as last resort
231
            if (!empty($this->plugin) && method_exists($this->plugin, 'get')) {
232
                $val = $this->plugin->get($settingName);
233
                if ($val !== '' && $val !== null) {
234
                    return $val;
235
                }
236
            }
237
238
            // 5) Global configuration fallback
239
            $val = api_get_configuration_value($settingName);
240
            return $val !== false ? $val : null;
241
242
        } catch (\Throwable $e) {
243
            // Log and return null to avoid fatal errors on admin UI
244
            error_log('[OnlyOffice] getSetting error: '.$e->getMessage());
245
            return null;
246
        }
247
    }
248
249
    /**
250
     * Persist a configuration value into access_url_rel_plugin.configuration for the current URL.
251
     * URL-like values are normalized via processUrl().
252
     * Keeps alternate legacy/short keys in sync to maximize compatibility.
253
     *
254
     * @param string $settingName
255
     * @param mixed  $value
256
     * @param bool   $createSetting
257
     *
258
     * @return void
259
     *
260
     * @throws OptimisticLockException
261
     * @throws NotSupported
262
     * @throws ORMException
263
     */
264
    public function setSetting($settingName, $value, $createSetting = false): void
265
    {
266
        // Normalize URL values if applicable
267
        if ($this->isSettingUrl($settingName) && is_string($value)) {
268
            $value = $this->processUrl($value);
269
        }
270
271
        $em = Database::getManager();
272
        $repo = $em->getRepository(PluginEntity::class);
273
274
        // Find plugin entity (handle common casings)
275
        $pluginEntity = $repo->findOneBy(['title' => 'Onlyoffice'])
276
            ?: $repo->findOneBy(['title' => 'OnlyOffice']);
277
278
        if (!$pluginEntity) {
279
            // Safeguard: if plugin entity does not exist, avoid fatal and log
280
            error_log('[OnlyOffice] setSetting: plugin entity not found');
281
            return;
282
        }
283
284
        // Get or create relation for current Access URL
285
        $currentUrl = Container::getAccessUrlUtil()->getCurrent();
286
        $rel = $pluginEntity->getConfigurationsByAccessUrl($currentUrl);
287
288
        if (!$rel) {
289
            // Create relation with default inactive state until explicitly enabled
290
            $rel = (new AccessUrlRelPlugin())
291
                ->setUrl($currentUrl)
292
                ->setActive(false)
293
                ->setConfiguration([]);
294
            $pluginEntity->addConfigurationsInUrl($rel);
295
        }
296
297
        // Update configuration array
298
        $settings = $rel->getConfiguration() ?? [];
299
        $settings[$settingName] = $value;
300
301
        // Keep alternate keys in sync (legacy ↔ short)
302
        foreach ($this->getAlternateKeys($settingName) as $alt) {
303
            $settings[$alt] = $value;
304
        }
305
306
        $rel->setConfiguration($settings);
307
308
        // Persist changes
309
        $em->persist($pluginEntity);
310
        $em->flush();
311
    }
312
313
    /**
314
     * Returns the Chamilo server base URL.
315
     *
316
     * @return string
317
     */
318
    public function getServerUrl()
319
    {
320
        return api_get_path(WEB_PATH);
321
    }
322
323
    /**
324
     * Get link to Docs Cloud.
325
     *
326
     * @return string
327
     */
328
    public function getLinkToDocs()
329
    {
330
        return self::LINK_TO_DOCS;
331
    }
332
333
    /**
334
     * Determine if a given setting name refers to a URL value.
335
     * Accepts both legacy and short key variants.
336
     *
337
     * @param string $settingName
338
     *
339
     * @return bool
340
     */
341
    public function isSettingUrl($settingName)
342
    {
343
        // Note: we check by key name, not by value
344
        return in_array($settingName, [
345
            $this->documentServerUrl,       // 'document_server_url'
346
            $this->documentServerInternalUrl, // 'onlyoffice_internal_url' (legacy)
347
            'document_server_internal',     // short variant
348
            $this->storageUrl,              // 'onlyoffice_storage_url' (legacy)
349
            'storage_url',                  // short variant
350
        ], true);
351
    }
352
353
    /**
354
     * Map OnlyOffice setting keys between legacy and short names, both directions.
355
     *
356
     * @param string $key
357
     *
358
     * @return array<string>
359
     */
360
    private function getAlternateKeys(string $key): array
361
    {
362
        switch ($key) {
363
            // Header name
364
            case 'onlyoffice_jwt_header':
365
                return ['jwt_header'];
366
            case 'jwt_header':
367
                return ['onlyoffice_jwt_header'];
368
369
            // Internal URL
370
            case 'onlyoffice_internal_url':
371
                return ['document_server_internal'];
372
            case 'document_server_internal':
373
                return ['onlyoffice_internal_url'];
374
375
            // Storage URL
376
            case 'onlyoffice_storage_url':
377
                return ['storage_url'];
378
            case 'storage_url':
379
                return ['onlyoffice_storage_url'];
380
381
            default:
382
                return [];
383
        }
384
    }
385
}
386