1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
4
|
|
|
|
5
|
|
|
namespace moodleexport; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Class PageExport. |
9
|
|
|
* |
10
|
|
|
* Handles the export of pages within a course. |
11
|
|
|
*/ |
12
|
|
|
class PageExport extends ActivityExport |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* Export a page to the specified directory. |
16
|
|
|
* |
17
|
|
|
* @param int $activityId The ID of the page. |
18
|
|
|
* @param string $exportDir The directory where the page will be exported. |
19
|
|
|
* @param int $moduleId The ID of the module. |
20
|
|
|
* @param int $sectionId The ID of the section. |
21
|
|
|
*/ |
22
|
|
|
public function export($activityId, $exportDir, $moduleId, $sectionId): void |
23
|
|
|
{ |
24
|
|
|
// Prepare the directory where the page export will be saved |
25
|
|
|
$pageDir = $this->prepareActivityDirectory($exportDir, 'page', $moduleId); |
26
|
|
|
|
27
|
|
|
// Retrieve page data |
28
|
|
|
$pageData = $this->getData($activityId, $sectionId); |
29
|
|
|
|
30
|
|
|
// Generate XML files |
31
|
|
|
$this->createPageXml($pageData, $pageDir); |
32
|
|
|
$this->createModuleXml($pageData, $pageDir); |
33
|
|
|
$this->createGradesXml($pageData, $pageDir); |
34
|
|
|
$this->createFiltersXml($pageData, $pageDir); |
35
|
|
|
$this->createGradeHistoryXml($pageData, $pageDir); |
36
|
|
|
$this->createInforefXml($pageData, $pageDir); |
37
|
|
|
$this->createRolesXml($pageData, $pageDir); |
38
|
|
|
$this->createCommentsXml($pageData, $pageDir); |
39
|
|
|
$this->createCalendarXml($pageData, $pageDir); |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Get page data dynamically from the course. |
44
|
|
|
*/ |
45
|
|
|
public function getData(int $pageId, int $sectionId): ?array |
46
|
|
|
{ |
47
|
|
|
$contextid = $this->course->info['real_id']; |
48
|
|
|
if ($pageId === 0) { |
49
|
|
|
$introText = trim($this->course->resources[RESOURCE_TOOL_INTRO]['course_homepage']->intro_text ?? ''); |
50
|
|
|
|
51
|
|
|
if (!empty($introText)) { |
52
|
|
|
$files = []; |
53
|
|
|
$resources = \DocumentManager::get_resources_from_source_html($introText); |
54
|
|
|
$courseInfo = api_get_course_info($this->course->code); |
55
|
|
|
$adminId = MoodleExport::getAdminUserData()['id']; |
56
|
|
|
|
57
|
|
|
foreach ($resources as [$src]) { |
58
|
|
|
if (preg_match('#/document(/[^"\']+)#', $src, $matches)) { |
59
|
|
|
$path = $matches[1]; |
60
|
|
|
$docId = \DocumentManager::get_document_id($courseInfo, $path); |
61
|
|
|
if ($docId) { |
62
|
|
|
$this->course->used_page_doc_ids[] = $docId; |
63
|
|
|
$document = \DocumentManager::get_document_data_by_id($docId, $this->course->code); |
64
|
|
|
if ($document) { |
|
|
|
|
65
|
|
|
$contenthash = hash('sha1', basename($document['path'])); |
66
|
|
|
$mimetype = (new FileExport($this->course))->getMimeType($document['path']); |
67
|
|
|
|
68
|
|
|
$files[] = [ |
69
|
|
|
'id' => $document['id'], |
70
|
|
|
'contenthash' => $contenthash, |
71
|
|
|
'contextid' => $contextid, |
72
|
|
|
'component' => 'mod_page', |
73
|
|
|
'filearea' => 'content', |
74
|
|
|
'itemid' => 1, |
75
|
|
|
'filepath' => '/Documents/', |
76
|
|
|
'documentpath' => 'document' . $document['path'], |
77
|
|
|
'filename' => basename($document['path']), |
78
|
|
|
'userid' => $adminId, |
79
|
|
|
'filesize' => $document['size'], |
80
|
|
|
'mimetype' => $mimetype, |
81
|
|
|
'status' => 0, |
82
|
|
|
'timecreated' => time() - 3600, |
83
|
|
|
'timemodified' => time(), |
84
|
|
|
'source' => $document['title'], |
85
|
|
|
'author' => 'Unknown', |
86
|
|
|
'license' => 'allrightsreserved', |
87
|
|
|
]; |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
return [ |
94
|
|
|
'id' => 0, |
95
|
|
|
'moduleid' => 0, |
96
|
|
|
'modulename' => 'page', |
97
|
|
|
'contextid' => $contextid, |
98
|
|
|
'name' => get_lang('Introduction'), |
99
|
|
|
'intro' => '', |
100
|
|
|
'content' => $this->normalizeContent($introText), |
101
|
|
|
'sectionid' => $sectionId, |
102
|
|
|
'sectionnumber' => 1, |
103
|
|
|
'display' => 0, |
104
|
|
|
'timemodified' => time(), |
105
|
|
|
'users' => [], |
106
|
|
|
'files' => $files, |
107
|
|
|
]; |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$pageResources = $this->course->resources[RESOURCE_DOCUMENT] ?? []; |
112
|
|
|
foreach ($pageResources as $page) { |
113
|
|
|
if ($page->source_id == $pageId) { |
114
|
|
|
return [ |
115
|
|
|
'id' => $page->source_id, |
116
|
|
|
'moduleid' => $page->source_id, |
117
|
|
|
'modulename' => 'page', |
118
|
|
|
'contextid' => $contextid, |
119
|
|
|
'name' => $page->title, |
120
|
|
|
'intro' => $page->comment ?? '', |
121
|
|
|
'content' => $this->normalizeContent($this->getPageContent($page)), |
122
|
|
|
'sectionid' => $sectionId, |
123
|
|
|
'sectionnumber' => 1, |
124
|
|
|
'display' => 0, |
125
|
|
|
'timemodified' => time(), |
126
|
|
|
'users' => [], |
127
|
|
|
'files' => [], |
128
|
|
|
]; |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
return null; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Create the XML file for the page. |
137
|
|
|
*/ |
138
|
|
|
private function createPageXml(array $pageData, string $pageDir): void |
139
|
|
|
{ |
140
|
|
|
$xmlContent = '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL; |
141
|
|
|
$xmlContent .= '<activity id="'.$pageData['id'].'" moduleid="'.$pageData['moduleid'].'" modulename="page" contextid="'.$pageData['contextid'].'">'.PHP_EOL; |
142
|
|
|
$xmlContent .= ' <page id="'.$pageData['id'].'">'.PHP_EOL; |
143
|
|
|
$xmlContent .= ' <name>'.htmlspecialchars($pageData['name']).'</name>'.PHP_EOL; |
144
|
|
|
$xmlContent .= ' <intro>'.htmlspecialchars($pageData['intro']).'</intro>'.PHP_EOL; |
145
|
|
|
$xmlContent .= ' <introformat>1</introformat>'.PHP_EOL; |
146
|
|
|
$xmlContent .= ' <content>'.htmlspecialchars($pageData['content']).'</content>'.PHP_EOL; |
147
|
|
|
$xmlContent .= ' <contentformat>1</contentformat>'.PHP_EOL; |
148
|
|
|
$xmlContent .= ' <legacyfiles>0</legacyfiles>'.PHP_EOL; |
149
|
|
|
$xmlContent .= ' <display>5</display>'.PHP_EOL; |
150
|
|
|
$xmlContent .= ' <displayoptions>a:3:{s:12:"printheading";s:1:"1";s:10:"printintro";s:1:"0";s:17:"printlastmodified";s:1:"1";}</displayoptions>'.PHP_EOL; |
151
|
|
|
$xmlContent .= ' <revision>1</revision>'.PHP_EOL; |
152
|
|
|
$xmlContent .= ' <timemodified>'.$pageData['timemodified'].'</timemodified>'.PHP_EOL; |
153
|
|
|
$xmlContent .= ' </page>'.PHP_EOL; |
154
|
|
|
$xmlContent .= '</activity>'; |
155
|
|
|
|
156
|
|
|
$this->createXmlFile('page', $xmlContent, $pageDir); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
private function normalizeContent(string $html): string |
160
|
|
|
{ |
161
|
|
|
return preg_replace_callback( |
162
|
|
|
'#<img[^>]+src=["\'](?<url>[^"\']+)["\']#i', |
163
|
|
|
function ($match) { |
164
|
|
|
$src = $match['url']; |
165
|
|
|
|
166
|
|
|
if (preg_match('#/courses/[^/]+/document/(.+)$#', $src, $parts)) { |
167
|
|
|
$filename = basename($parts[1]); |
168
|
|
|
return str_replace($src, '@@PLUGINFILE@@/Documents/' . $filename, $match[0]); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return $match[0]; |
172
|
|
|
}, |
173
|
|
|
$html |
174
|
|
|
); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Retrieves the content of the page. |
179
|
|
|
*/ |
180
|
|
|
private function getPageContent(object $page): string |
181
|
|
|
{ |
182
|
|
|
if ($page->file_type === 'file') { |
183
|
|
|
return file_get_contents($this->course->path.$page->path); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
return ''; |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.