Passed
Pull Request — master (#5310)
by Angel Fernando Quiroz
07:41
created

AbstractMigrationChamilo   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 359
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 153
c 3
b 1
f 0
dl 0
loc 359
rs 8.48
wmc 49

18 Methods

Rating   Name   Duplication   Size   Complexity  
A addLegacyFileToResource() 0 25 5
A fileExists() 0 3 3
A getConfigurationValue() 0 13 3
F fixItemProperty() 0 112 17
A removeSettingCurrent() 0 2 1
A getAdmin() 0 8 1
A findCourse() 0 7 2
A findSession() 0 7 2
A adminExist() 0 14 2
A getEntityManager() 0 3 1
A setEntityManager() 0 3 1
A setContainer() 0 3 1
A getContainer() 0 3 1
A addSettingCurrent() 0 58 5
A writeFile() 0 6 1
A removeFile() 0 6 1
A generateFilePath() 0 5 1
A readFile() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like AbstractMigrationChamilo 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 AbstractMigrationChamilo, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Migrations;
8
9
use Chamilo\CoreBundle\Entity\AbstractResource;
10
use Chamilo\CoreBundle\Entity\AccessUrl;
11
use Chamilo\CoreBundle\Entity\Admin;
12
use Chamilo\CoreBundle\Entity\Course;
13
use Chamilo\CoreBundle\Entity\ResourceInterface;
14
use Chamilo\CoreBundle\Entity\ResourceLink;
15
use Chamilo\CoreBundle\Entity\Session;
16
use Chamilo\CoreBundle\Entity\SettingsCurrent;
17
use Chamilo\CoreBundle\Entity\SettingsOptions;
18
use Chamilo\CoreBundle\Entity\User;
19
use Chamilo\CoreBundle\Repository\Node\UserRepository;
20
use Chamilo\CoreBundle\Repository\ResourceRepository;
21
use Chamilo\CoreBundle\Repository\SessionRepository;
22
use Chamilo\CourseBundle\Repository\CGroupRepository;
23
use DateTime;
24
use DateTimeZone;
25
use Doctrine\Migrations\AbstractMigration;
26
use Doctrine\ORM\EntityManager;
27
use ReflectionClass;
28
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
29
use Symfony\Component\DependencyInjection\ContainerInterface;
30
use Symfony\Component\Filesystem\Filesystem;
31
use Symfony\Component\HttpFoundation\File\UploadedFile;
32
33
abstract class AbstractMigrationChamilo extends AbstractMigration implements ContainerAwareInterface
34
{
35
    public const BATCH_SIZE = 20;
36
37
    private ?EntityManager $manager = null;
38
    private ?ContainerInterface $container = null;
39
40
    public function setEntityManager(EntityManager $manager): void
41
    {
42
        $this->manager = $manager;
43
    }
44
45
    public function setContainer(?ContainerInterface $container = null): void
46
    {
47
        $this->container = $container;
48
    }
49
50
    /**
51
     * @return ContainerInterface
52
     */
53
    public function getContainer()
54
    {
55
        return $this->container;
56
    }
57
58
    public function adminExist(): bool
59
    {
60
        $em = $this->getEntityManager();
61
        $connection = $em->getConnection();
62
63
        $sql = 'SELECT user_id FROM admin WHERE user_id IN (SELECT id FROM user) ORDER BY id LIMIT 1';
64
        $result = $connection->executeQuery($sql);
65
        $adminRow = $result->fetchAssociative();
66
67
        if (empty($adminRow)) {
68
            return false;
69
        }
70
71
        return true;
72
    }
73
74
    public function getAdmin(): User
75
    {
76
        $admin = $this->getEntityManager()
77
            ->getRepository(Admin::class)
78
            ->findOneBy([], ['id' => 'ASC'])
79
        ;
80
81
        return $admin->getUser();
82
    }
83
84
    /**
85
     * @return EntityManager
86
     */
87
    public function getEntityManager()
88
    {
89
        return $this->getContainer()->get('doctrine')->getManager();
90
    }
91
92
    /**
93
     * Speeds up SettingsCurrent creation.
94
     *
95
     * @param string $variable            The variable itself
96
     * @param string $subKey              The subkey
97
     * @param string $type                The type of setting (text, radio, select, etc)
98
     * @param string $category            The category (Platform, User, etc)
99
     * @param string $selectedValue       The default value
100
     * @param string $title               The setting title string name
101
     * @param string $comment             The setting comment string name
102
     * @param string $scope               The scope
103
     * @param string $subKeyText          Text if there is a subKey
104
     * @param int    $accessUrl           What URL it is for
105
     * @param bool   $accessUrlChangeable Whether it can be changed on each url
106
     * @param bool   $accessUrlLocked     Whether the setting for the current URL is
107
     *                                    locked to the current value
108
     * @param array  $options             Optional array in case of a radio-type field,
109
     *                                    to insert options
110
     */
111
    public function addSettingCurrent(
112
        $variable,
113
        $subKey,
114
        $type,
115
        $category,
116
        $selectedValue,
117
        $title,
118
        $comment,
119
        $scope = '',
120
        $subKeyText = '',
121
        $accessUrl = 1,
122
        $accessUrlChangeable = false,
123
        $accessUrlLocked = true,
124
        $options = []
125
    ): void {
126
        $em = $this->getEntityManager();
127
128
        $accessUrl = $em->find(AccessUrl::class, $accessUrl);
129
130
        $setting = new SettingsCurrent();
131
        $setting
132
            ->setVariable($variable)
133
            ->setSubkey($subKey)
134
            ->setType($type)
135
            ->setCategory($category)
136
            ->setSelectedValue($selectedValue)
137
            ->setTitle($title)
138
            ->setComment($comment)
139
            ->setScope($scope)
140
            ->setSubkeytext($subKeyText)
141
            ->setUrl($accessUrl)
142
            ->setAccessUrlChangeable((int) $accessUrlChangeable)
143
            ->setAccessUrlLocked((int) $accessUrlLocked)
144
        ;
145
146
        $em->persist($setting);
147
148
        if (\count($options) > 0) {
149
            foreach ($options as $option) {
150
                if (empty($option['text'])) {
151
                    if ('true' === $option['value']) {
152
                        $option['text'] = 'Yes';
153
                    } else {
154
                        $option['text'] = 'No';
155
                    }
156
                }
157
158
                $settingOption = new SettingsOptions();
159
                $settingOption
160
                    ->setVariable($variable)
161
                    ->setValue($option['value'])
162
                    ->setDisplayText($option['text'])
163
                ;
164
165
                $em->persist($settingOption);
166
            }
167
        }
168
        $em->flush();
169
    }
170
171
    /**
172
     * @param string     $variable
173
     * @param null|mixed $configuration
174
     */
175
    public function getConfigurationValue($variable, $configuration = null)
176
    {
177
        global $_configuration;
178
179
        if (isset($configuration)) {
180
            $_configuration = $configuration;
181
        }
182
183
        if (isset($_configuration[$variable])) {
184
            return $_configuration[$variable];
185
        }
186
187
        return false;
188
    }
189
190
    /**
191
     * Remove a setting completely.
192
     *
193
     * @param string $variable The setting variable name
194
     */
195
    public function removeSettingCurrent($variable): void
196
    {
197
        // to be implemented
198
    }
199
200
    public function addLegacyFileToResource(
201
        string $filePath,
202
        ResourceRepository $repo,
203
        AbstractResource $resource,
204
        $id,
205
        $fileName = '',
206
        $description = ''
207
    ): bool {
208
        $class = $resource::class;
209
        $documentPath = basename($filePath);
210
211
        if (is_dir($filePath) || (!is_dir($filePath) && !file_exists($filePath))) {
212
            $this->warnIf(true, "Cannot migrate {$class} #'.{$id}.' file not found: {$documentPath}");
213
214
            return false;
215
        }
216
217
        $mimeType = mime_content_type($filePath);
218
        if (empty($fileName)) {
219
            $fileName = basename($documentPath);
220
        }
221
        $file = new UploadedFile($filePath, $fileName, $mimeType, null, true);
222
        $repo->addFile($resource, $file);
223
224
        return true;
225
    }
226
227
    public function fixItemProperty(
228
        $tool,
229
        ResourceRepository $repo,
230
        $course,
231
        $admin,
232
        ResourceInterface $resource,
233
        $parentResource,
234
        array $items = []
235
    ) {
236
        $container = $this->getContainer();
237
        $em = $this->getEntityManager();
238
        $connection = $em->getConnection();
239
240
        $courseId = $course->getId();
241
        $id = $resource->getResourceIdentifier();
242
243
        if (empty($items)) {
244
            $sql = "SELECT * FROM c_item_property
245
                    WHERE tool = '{$tool}' AND c_id = {$courseId} AND ref = {$id}";
246
            $result = $connection->executeQuery($sql);
247
            $items = $result->fetchAllAssociative();
248
        }
249
250
        // For some reason the resource doesn't have a c_item_property value.
251
        if (empty($items)) {
252
            return false;
253
        }
254
255
        $sessionRepo = $container->get(SessionRepository::class);
256
        $groupRepo = $container->get(CGroupRepository::class);
257
        $userRepo = $container->get(UserRepository::class);
258
259
        $resource->setParent($parentResource);
0 ignored issues
show
Bug introduced by
The method setParent() does not exist on Chamilo\CoreBundle\Entity\ResourceInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Chamilo\CoreBundle\Entity\ResourceInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

259
        $resource->/** @scrutinizer ignore-call */ 
260
                   setParent($parentResource);
Loading history...
260
        $resourceNode = null;
261
        $userList = [];
262
        $groupList = [];
263
        $sessionList = [];
264
        foreach ($items as $item) {
265
            $visibility = (int) $item['visibility'];
266
            $userId = (int) $item['insert_user_id'];
267
            $sessionId = $item['session_id'] ?? 0;
268
            $groupId = $item['to_group_id'] ?? 0;
269
            if (empty($item['lastedit_date'])) {
270
                $lastUpdatedAt = new DateTime('now', new DateTimeZone('UTC'));
271
            } else {
272
                $lastUpdatedAt = new DateTime($item['lastedit_date'], new DateTimeZone('UTC'));
273
            }
274
            $newVisibility = ResourceLink::VISIBILITY_DRAFT;
275
276
            // Old 1.11.x visibility (item property) is based in this switch:
277
            switch ($visibility) {
278
                case 0:
279
                    $newVisibility = ResourceLink::VISIBILITY_DRAFT;
280
281
                    break;
282
283
                case 1:
284
                    $newVisibility = ResourceLink::VISIBILITY_PUBLISHED;
285
286
                    break;
287
            }
288
289
            // If c_item_property.insert_user_id doesn't exist we use the first admin id.
290
            $user = null;
291
            if (isset($userList[$userId])) {
292
                $user = $userList[$userId];
293
            } else {
294
                if (!empty($userId)) {
295
                    $userFound = $userRepo->find($userId);
296
                    if ($userFound) {
297
                        $user = $userList[$userId] = $userRepo->find($userId);
298
                    }
299
                }
300
            }
301
302
            if (null === $user) {
303
                $user = $admin;
304
            }
305
306
            $session = null;
307
            if (!empty($sessionId)) {
308
                if (isset($sessionList[$sessionId])) {
309
                    $session = $sessionList[$sessionId];
310
                } else {
311
                    $session = $sessionList[$sessionId] = $sessionRepo->find($sessionId);
312
                }
313
            }
314
315
            $group = null;
316
            if (!empty($groupId)) {
317
                if (isset($groupList[$groupId])) {
318
                    $group = $groupList[$groupId];
319
                } else {
320
                    $group = $groupList[$groupId] = $groupRepo->find($groupId);
321
                }
322
            }
323
324
            if (null === $resourceNode) {
325
                $resourceNode = $repo->addResourceNode($resource, $user, $parentResource);
326
                $em->persist($resourceNode);
327
            }
328
            $resource->addCourseLink($course, $session, $group, $newVisibility);
0 ignored issues
show
Bug introduced by
The method addCourseLink() does not exist on Chamilo\CoreBundle\Entity\ResourceInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Chamilo\CoreBundle\Entity\User. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

328
            $resource->/** @scrutinizer ignore-call */ 
329
                       addCourseLink($course, $session, $group, $newVisibility);
Loading history...
329
330
            if (2 === $visibility) {
331
                $link = $resource->getResourceNode()->getResourceLinkByContext($course, $session, $group);
332
                $link->setDeletedAt($lastUpdatedAt);
333
            }
334
335
            $em->persist($resource);
336
        }
337
338
        return true;
339
    }
340
341
    public function fileExists($filePath): bool
342
    {
343
        return file_exists($filePath) && !is_dir($filePath) && is_readable($filePath);
344
    }
345
346
    public function findCourse(int $id): ?Course
347
    {
348
        if (0 === $id) {
349
            return null;
350
        }
351
352
        return $this->getEntityManager()->find(Course::class, $id);
353
    }
354
355
    public function findSession(int $id): ?Session
356
    {
357
        if (0 === $id) {
358
            return null;
359
        }
360
361
        return $this->getEntityManager()->find(Session::class, $id);
362
    }
363
364
    private function generateFilePath(string $filename): string
365
    {
366
        $cacheDir = $this->getContainer()->get('kernel')->getCacheDir();
367
368
        return $cacheDir.'/migration_'.$filename;
369
    }
370
371
    protected function writeFile(string $filename, string $content): void
372
    {
373
        $fullFilename = $this->generateFilePath($filename);
374
375
        $fs = new Filesystem();
376
        $fs->dumpFile($fullFilename, $content);
377
    }
378
379
    protected function readFile(string $filename): string
380
    {
381
        $fullFilename = $this->generateFilePath($filename);
382
383
        return file_get_contents($fullFilename);
384
    }
385
386
    protected function removeFile(string $filename): string
387
    {
388
        $fullFilename = $this->generateFilePath($filename);
389
390
        $fs = new Filesystem();
391
        $fs->remove($fullFilename);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
392
    }
393
}
394