Passed
Pull Request — master (#5853)
by
unknown
07:56
created

Version20230913162700::updateHtmlContent()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 24
nc 11
nop 4
dl 0
loc 39
rs 8.0555
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Migrations\Schema\V200;
8
9
use Chamilo\CoreBundle\Entity\Course;
10
use Chamilo\CoreBundle\Migrations\AbstractMigrationChamilo;
11
use Chamilo\CoreBundle\Repository\Node\CourseRepository;
12
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
13
use Chamilo\CourseBundle\Entity\CDocument;
14
use Chamilo\CourseBundle\Repository\CDocumentRepository;
15
use Doctrine\DBAL\Schema\Schema;
16
use Exception;
17
use RecursiveDirectoryIterator;
18
use RecursiveIteratorIterator;
19
20
use const PHP_URL_PATH;
21
22
final class Version20230913162700 extends AbstractMigrationChamilo
23
{
24
    public function getDescription(): string
25
    {
26
        return 'Replace old document path by resource file path';
27
    }
28
29
    public function up(Schema $schema): void
30
    {
31
        $documentRepo = $this->container->get(CDocumentRepository::class);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

31
        /** @scrutinizer ignore-call */ 
32
        $documentRepo = $this->container->get(CDocumentRepository::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
32
        $resourceNodeRepo = $this->container->get(ResourceNodeRepository::class);
33
34
        $q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c');
0 ignored issues
show
Bug introduced by
The method createQuery() does not exist on null. ( Ignorable by Annotation )

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

34
        /** @scrutinizer ignore-call */ 
35
        $q = $this->entityManager->createQuery('SELECT c FROM Chamilo\CoreBundle\Entity\Course c');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
35
        /*$updateConfigurations = [
36
            ['table' => 'c_tool_intro', 'field' => 'intro_text'],
37
            ['table' => 'c_course_description', 'field' => 'content'],
38
            ['table' => 'c_quiz', 'fields' => ['description', 'text_when_finished']],
39
            ['table' => 'c_quiz_question', 'fields' => ['description', 'question']],
40
            ['table' => 'c_quiz_answer', 'fields' => ['answer', 'comment']],
41
            ['table' => 'c_course_description', 'field' => 'content'],
42
            ['table' => 'c_student_publication', 'field' => 'description'],
43
            ['table' => 'c_student_publication_comment', 'field' => 'comment'],
44
            ['table' => 'c_forum_category', 'field' => 'cat_comment'],
45
            ['table' => 'c_forum_forum', 'field' => 'forum_comment'],
46
            ['table' => 'c_forum_post', 'field' => 'post_text'],
47
            ['table' => 'c_glossary', 'field' => 'description'],
48
            ['table' => 'c_survey', 'fields' => ['title', 'subtitle']],
49
            ['table' => 'c_survey_question', 'fields' => ['survey_question', 'survey_question_comment']],
50
            ['table' => 'c_survey_question_option', 'field' => 'option_text'],
51
        ];*/
52
53
        /** @var Course $course */
54
        foreach ($q->toIterable() as $course) {
55
            $courseId = $course->getId();
56
            $courseDirectory = $course->getDirectory();
57
58
            if (empty($courseDirectory)) {
59
                continue;
60
            }
61
62
           /* foreach ($updateConfigurations as $config) {
63
                $this->updateContent($config, $courseDirectory, $courseId, $documentRepo);
64
            }*/
65
66
            $this->updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo);
67
        }
68
    }
69
70
    private function updateContent($config, $courseDirectory, $courseId, $documentRepo): void
0 ignored issues
show
Unused Code introduced by
The method updateContent() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
71
    {
72
        if (isset($config['field'])) {
73
            $fields = [$config['field']];
74
        } elseif (isset($config['fields'])) {
75
            $fields = $config['fields'];
76
        } else {
77
            throw new Exception('No field or fields specified for updating.');
78
        }
79
80
        foreach ($fields as $field) {
81
            $sql = "SELECT iid, {$field} FROM {$config['table']} WHERE c_id = {$courseId}";
82
            $result = $this->connection->executeQuery($sql);
83
            $items = $result->fetchAllAssociative();
84
85
            foreach ($items as $item) {
86
                $originalText = $item[$field];
87
                if (!empty($originalText)) {
88
                    $updatedText = $this->replaceOldURLsWithNew($originalText, $courseDirectory, $courseId, $documentRepo);
89
                    if ($originalText !== $updatedText) {
90
                        $sql = "UPDATE {$config['table']} SET {$field} = :newText WHERE iid = :id";
91
                        $params = ['newText' => $updatedText, 'id' => $item['iid']];
92
                        $this->connection->executeQuery($sql, $params);
93
                    }
94
                }
95
            }
96
        }
97
    }
98
99
    private function updateHtmlContent($courseDirectory, $courseId, $documentRepo, $resourceNodeRepo): void
100
    {
101
        $sql = "SELECT iid, resource_node_id FROM c_document WHERE filetype = 'file'";
102
        $result = $this->connection->executeQuery($sql);
103
        $items = $result->fetchAllAssociative();
104
105
        foreach ($items as $item) {
106
            /** @var CDocument $document */
107
            $document = $documentRepo->find($item['iid']);
108
            if (!$document) {
109
                continue;
110
            }
111
112
            $resourceNode = $document->getResourceNode();
113
114
            if (!$resourceNode || !$resourceNode->hasResourceFile()) {
115
                continue;
116
            }
117
118
            $resourceFile = $resourceNode->getResourceFiles()->first();
119
120
            if (!$resourceFile) {
121
                continue;
122
            }
123
124
            $filePath = $resourceFile->getTitle();
125
            if ('text/html' === $resourceFile->getMimeType()) {
126
                error_log('Verifying HTML file: '.$filePath);
127
128
                try {
129
                    $content = $resourceNodeRepo->getResourceNodeFileContent($resourceNode);
130
                    $updatedContent = $this->replaceOldURLsWithNew($content, $courseDirectory, $courseId, $documentRepo);
131
132
                    if ($content !== $updatedContent) {
133
                        $documentRepo->updateResourceFileContent($document, $updatedContent);
134
                        $documentRepo->update($document);
135
                    }
136
                } catch (Exception $e) {
137
                    error_log("Error processing file $filePath: ".$e->getMessage());
138
                }
139
            }
140
        }
141
    }
142
143
    private function replaceOldURLsWithNew($itemDataText, $courseDirectory, $courseId, $documentRepo): array|string|null
144
    {
145
        $contentText = $itemDataText;
146
        $specificCoursePattern = '/(src|href)=["\']((https?:\/\/[^\/]+)?(\/courses\/([^\/]+)\/document\/[^"\']+\.\w+))["\']/i';
147
        preg_match_all($specificCoursePattern, $contentText, $matches);
148
149
        foreach ($matches[2] as $index => $fullUrl) {
150
            $videoPath = parse_url($fullUrl, PHP_URL_PATH) ?: $fullUrl;
151
            $actualCourseDirectory = $matches[5][$index];
152
            if ($actualCourseDirectory !== $courseDirectory) {
153
                $videoPath = preg_replace("/^\\/courses\\/$actualCourseDirectory\\//i", "/courses/$courseDirectory/", $videoPath);
154
            }
155
156
            $documentPath = str_replace('/courses/'.$courseDirectory.'/document/', '/', $videoPath);
157
158
            error_log('Debugging Replace URLs:');
159
            error_log('Full URL: ' . $fullUrl);
160
            error_log('Video Path: ' . $videoPath);
161
            error_log('Actual Course Directory: ' . $actualCourseDirectory);
162
            error_log('Processed Document Path: ' . $documentPath);
163
164
            /*
165
            $sql = "SELECT iid, path, resource_node_id FROM c_document WHERE c_id = $courseId AND path LIKE '$documentPath'";
166
            $result = $this->connection->executeQuery($sql);
167
            $documents = $result->fetchAllAssociative();
168
169
            if (!empty($documents)) {
170
                $this->replaceDocumentLinks($documents, $documentRepo, $matches, $index, $videoPath, $courseId, $contentText);
171
            } else {
172
                $document = $this->createNewDocument($videoPath, $courseId);
173
                if ($document) {
174
                    $newUrl = $documentRepo->getResourceFileUrl($document);
175
                    if ($newUrl) {
176
                        $replacement = $matches[1][$index].'="'.$newUrl.'"';
177
                        $contentText = str_replace($matches[0][$index], $replacement, $contentText);
178
                    }
179
                }
180
            }*/
181
        }
182
183
        return $contentText;
184
    }
185
186
    private function replaceDocumentLinks($documents, $documentRepo, $matches, $index, $videoPath, $courseId, &$contentText): void
0 ignored issues
show
Unused Code introduced by
The method replaceDocumentLinks() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
187
    {
188
        foreach ($documents as $documentData) {
189
            $resourceNodeId = (int) $documentData['resource_node_id'];
190
            $documentFile = $documentRepo->getResourceFromResourceNode($resourceNodeId);
191
            if ($documentFile) {
192
                $newUrl = $documentRepo->getResourceFileUrl($documentFile);
193
                if (!empty($newUrl)) {
194
                    $patternForReplacement = '/'.preg_quote($matches[0][$index], '/').'/';
195
                    $replacement = $matches[1][$index].'="'.$newUrl.'"';
196
                    $contentText = preg_replace($patternForReplacement, $replacement, $contentText, 1);
197
                }
198
            }
199
        }
200
    }
201
202
    private function createNewDocument($videoPath, $courseId)
0 ignored issues
show
Unused Code introduced by
The method createNewDocument() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
203
    {
204
        try {
205
            $documentRepo = $this->container->get(CDocumentRepository::class);
206
            $kernel = $this->container->get('kernel');
207
            $rootPath = $kernel->getProjectDir();
208
            $appCourseOldPath = $rootPath.'/app'.$videoPath;
209
            $title = basename($appCourseOldPath);
210
211
            $courseRepo = $this->container->get(CourseRepository::class);
212
            $course = $courseRepo->find($courseId);
213
            if (!$course) {
214
                throw new Exception("Course with ID $courseId not found.");
215
            }
216
217
            $document = $documentRepo->findCourseResourceByTitle($title, $course->getResourceNode(), $course);
218
            if (null !== $document) {
219
                return $document;
220
            }
221
222
            if (file_exists($appCourseOldPath) && !is_dir($appCourseOldPath)) {
223
                $document = new CDocument();
224
                $document->setFiletype('file')
225
                    ->setTitle($title)
226
                    ->setComment(null)
227
                    ->setReadonly(false)
228
                    ->setCreator($this->getAdmin())
229
                    ->setParent($course)
230
                    ->addCourseLink($course)
231
                ;
232
233
                $this->entityManager->persist($document);
234
                $this->entityManager->flush();
235
236
                $documentRepo->addFileFromPath($document, $title, $appCourseOldPath);
237
238
                return $document;
239
            }
240
            $generalCoursesPath = $this->getUpdateRootPath().'/app/courses/';
241
            $foundPath = $this->recursiveFileSearch($generalCoursesPath, $title);
242
            if ($foundPath) {
243
                $document = new CDocument();
244
                $document->setFiletype('file')
245
                    ->setTitle($title)
246
                    ->setComment(null)
247
                    ->setReadonly(false)
248
                    ->setCreator($this->getAdmin())
249
                    ->setParent($course)
250
                    ->addCourseLink($course)
251
                ;
252
253
                $this->entityManager->persist($document);
254
                $this->entityManager->flush();
255
256
                $documentRepo->addFileFromPath($document, $title, $foundPath);
257
                error_log('File found in new location: '.$foundPath);
258
259
                return $document;
260
            }
261
262
            throw new Exception('File not found in any location.');
263
        } catch (Exception $e) {
264
            error_log('Migration error: '.$e->getMessage());
265
266
            return null;
267
        }
268
    }
269
270
    private function recursiveFileSearch($directory, $title)
271
    {
272
        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
273
        foreach ($iterator as $file) {
274
            if ($file->isFile() && $file->getFilename() === $title) {
275
                return $file->getRealPath();
276
            }
277
        }
278
279
        return null;
280
    }
281
}
282