|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
// For licensing terms, see /license.txt |
|
4
|
|
|
|
|
5
|
|
|
namespace Chamilo\PluginBundle\H5pImport\H5pImporter; |
|
6
|
|
|
|
|
7
|
|
|
use Chamilo\CoreBundle\Entity\Course; |
|
8
|
|
|
use Chamilo\CoreBundle\Entity\Session; |
|
9
|
|
|
use Chamilo\PluginBundle\Entity\H5pImport\H5pImport; |
|
10
|
|
|
use Chamilo\PluginBundle\Entity\H5pImport\H5pImportLibrary; |
|
11
|
|
|
use Database; |
|
12
|
|
|
use H5PCore; |
|
13
|
|
|
use Symfony\Component\Filesystem\Filesystem; |
|
14
|
|
|
|
|
15
|
|
|
class H5pPackageTools |
|
16
|
|
|
{ |
|
17
|
|
|
/** |
|
18
|
|
|
* Help read JSON from the archive. |
|
19
|
|
|
* |
|
20
|
|
|
* @return mixed JSON content if valid or FALSE for invalid |
|
21
|
|
|
*/ |
|
22
|
|
|
public static function getJson(string $file, bool $assoc = false) |
|
23
|
|
|
{ |
|
24
|
|
|
$fs = new Filesystem(); |
|
25
|
|
|
$json = false; |
|
26
|
|
|
|
|
27
|
|
|
if ($fs->exists($file)) { |
|
28
|
|
|
$contents = file_get_contents($file); |
|
29
|
|
|
|
|
30
|
|
|
// Decode the data |
|
31
|
|
|
$json = json_decode($contents, $assoc); |
|
32
|
|
|
if (null === $json) { |
|
33
|
|
|
// JSON cannot be decoded or the recursion limit has been reached. |
|
34
|
|
|
return false; |
|
35
|
|
|
} |
|
36
|
|
|
} |
|
37
|
|
|
|
|
38
|
|
|
return $json; |
|
39
|
|
|
} |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* Checks the integrity of an H5P package by verifying the existence of libraries |
|
43
|
|
|
* and moves them to the "libraries" directory. |
|
44
|
|
|
* |
|
45
|
|
|
* @param object $h5pJson the H5P JSON object |
|
46
|
|
|
* @param string $extractedDir the path to the extracted directory |
|
47
|
|
|
* |
|
48
|
|
|
* @return bool true if the package integrity is valid, false otherwise |
|
49
|
|
|
*/ |
|
50
|
|
|
public static function checkPackageIntegrity(object $h5pJson, string $extractedDir): bool |
|
51
|
|
|
{ |
|
52
|
|
|
$filesystem = new Filesystem(); |
|
53
|
|
|
$h5pDir = dirname($extractedDir, 2); |
|
54
|
|
|
$sharedLibrariesDir = $h5pDir.'/libraries'; |
|
55
|
|
|
|
|
56
|
|
|
// Move 'content' directory one level back (H5P specification) |
|
57
|
|
|
$filesystem->mirror($extractedDir.'/content', $extractedDir, null, ['override' => true]); |
|
58
|
|
|
$filesystem->remove($extractedDir.'/content'); |
|
59
|
|
|
// Get the list of preloaded dependencies |
|
60
|
|
|
$preloadedDependencies = $h5pJson->preloadedDependencies; |
|
61
|
|
|
|
|
62
|
|
|
// Check the existence of each library in the extracted directory |
|
63
|
|
|
foreach ($preloadedDependencies as $dependency) { |
|
64
|
|
|
$libraryName = $dependency->machineName; |
|
65
|
|
|
$majorVersion = $dependency->majorVersion; |
|
66
|
|
|
$minorVersion = $dependency->minorVersion; |
|
67
|
|
|
|
|
68
|
|
|
$libraryFolderName = api_replace_dangerous_char($libraryName.'-'.$majorVersion.'.'.$minorVersion); |
|
69
|
|
|
$libraryPath = $extractedDir.'/'.$libraryFolderName; |
|
70
|
|
|
|
|
71
|
|
|
// Check if the library folder exists |
|
72
|
|
|
if (!$filesystem->exists($libraryPath)) { |
|
73
|
|
|
return false; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
// Move the entire folder to the "libraries" directory |
|
77
|
|
|
$targetPath = $sharedLibrariesDir.'/'.$libraryFolderName; |
|
78
|
|
|
|
|
79
|
|
|
$filesystem->rename($libraryPath, $targetPath, true); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
return true; |
|
83
|
|
|
} |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Stores the H5P package information in the database. |
|
87
|
|
|
* |
|
88
|
|
|
* @param string $packagePath the path to the H5P package file |
|
89
|
|
|
* @param object $h5pJson the parsed H5P JSON object |
|
90
|
|
|
* @param Course $course the course entity related to the package |
|
91
|
|
|
* @param null|Session $session the session entity related to the package |
|
92
|
|
|
* @param null|array $values the advance options in upload form |
|
93
|
|
|
*/ |
|
94
|
|
|
public static function storeH5pPackage( |
|
95
|
|
|
string $packagePath, |
|
96
|
|
|
object $h5pJson, |
|
97
|
|
|
Course $course, |
|
98
|
|
|
Session $session = null, |
|
99
|
|
|
array $values = null |
|
100
|
|
|
) { |
|
101
|
|
|
$entityManager = \Database::getManager(); |
|
102
|
|
|
// Go back 2 directories |
|
103
|
|
|
$h5pDir = dirname($packagePath, 2); |
|
104
|
|
|
$sharedLibrariesDir = $h5pDir.'/libraries'; |
|
105
|
|
|
|
|
106
|
|
|
$mainLibraryName = $h5pJson->mainLibrary; |
|
107
|
|
|
$relativePath = api_get_path(REL_COURSE_PATH).$course->getDirectory().'/h5p/'; |
|
108
|
|
|
|
|
109
|
|
|
$h5pImport = new H5pImport(); |
|
110
|
|
|
$h5pImport->setName($h5pJson->title); |
|
111
|
|
|
$h5pImport->setPath($packagePath); |
|
112
|
|
|
if ($values) { |
|
113
|
|
|
$h5pImport->setDescription($values['description']); |
|
114
|
|
|
} |
|
115
|
|
|
$h5pImport->setRelativePath($relativePath); |
|
116
|
|
|
$h5pImport->setCourse($course); |
|
117
|
|
|
$h5pImport->setSession($session); |
|
118
|
|
|
$entityManager->persist($h5pImport); |
|
119
|
|
|
|
|
120
|
|
|
$libraries = $h5pJson->preloadedDependencies; |
|
121
|
|
|
|
|
122
|
|
|
foreach ($libraries as $libraryData) { |
|
123
|
|
|
$library = $entityManager |
|
124
|
|
|
->getRepository(H5pImportLibrary::class) |
|
125
|
|
|
->findOneBy( |
|
126
|
|
|
[ |
|
127
|
|
|
'machineName' => $libraryData->machineName, |
|
128
|
|
|
'majorVersion' => $libraryData->majorVersion, |
|
129
|
|
|
'minorVersion' => $libraryData->minorVersion, |
|
130
|
|
|
'course' => $course, |
|
131
|
|
|
] |
|
132
|
|
|
) |
|
133
|
|
|
; |
|
134
|
|
|
|
|
135
|
|
|
if (null === $library) { |
|
136
|
|
|
$auxFullName = $libraryData->machineName.'-'.$libraryData->majorVersion.'.'.$libraryData->minorVersion; |
|
137
|
|
|
$libraryOwnJson = self::getJson($sharedLibrariesDir.'/'.$auxFullName.'/library.json'); |
|
138
|
|
|
|
|
139
|
|
|
$library = new H5pImportLibrary(); |
|
140
|
|
|
$library->setMachineName($libraryData->machineName); |
|
141
|
|
|
$library->setTitle($libraryOwnJson->title); |
|
142
|
|
|
$library->setMajorVersion($libraryData->majorVersion); |
|
143
|
|
|
$library->setMinorVersion($libraryData->minorVersion); |
|
144
|
|
|
$library->setPatchVersion($libraryOwnJson->patchVersion); |
|
145
|
|
|
$library->setRunnable($libraryOwnJson->runnable); |
|
146
|
|
|
$library->setEmbedTypes($libraryOwnJson->embedTypes); |
|
147
|
|
|
$library->setPreloadedJs($libraryOwnJson->preloadedJs); |
|
148
|
|
|
$library->setPreloadedCss($libraryOwnJson->preloadedCss); |
|
149
|
|
|
$library->setLibraryPath($sharedLibrariesDir.'/'.$auxFullName); |
|
150
|
|
|
$library->setCourse($course); |
|
151
|
|
|
$entityManager->persist($library); |
|
152
|
|
|
$entityManager->flush(); |
|
153
|
|
|
} |
|
154
|
|
|
|
|
155
|
|
|
$h5pImport->addLibraries($library); |
|
156
|
|
|
if ($mainLibraryName === $libraryData->machineName) { |
|
157
|
|
|
$h5pImport->setMainLibrary($library); |
|
158
|
|
|
} |
|
159
|
|
|
$entityManager->persist($h5pImport); |
|
160
|
|
|
$entityManager->flush(); |
|
161
|
|
|
} |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
/** |
|
165
|
|
|
* Deletes an H5P package from the database and the disk. |
|
166
|
|
|
* |
|
167
|
|
|
* @param H5pImport $h5pImport the H5P import entity representing the package to delete |
|
168
|
|
|
* |
|
169
|
|
|
* @return bool true if the package was successfully deleted, false otherwise |
|
170
|
|
|
*/ |
|
171
|
|
|
public static function deleteH5pPackage(H5pImport $h5pImport): bool |
|
172
|
|
|
{ |
|
173
|
|
|
$packagePath = $h5pImport->getPath(); |
|
174
|
|
|
$entityManager = \Database::getManager(); |
|
175
|
|
|
$entityManager->remove($h5pImport); |
|
176
|
|
|
$entityManager->flush(); |
|
177
|
|
|
|
|
178
|
|
|
$filesystem = new Filesystem(); |
|
179
|
|
|
|
|
180
|
|
|
if ($filesystem->exists($packagePath)) { |
|
181
|
|
|
try { |
|
182
|
|
|
$filesystem->remove($packagePath); |
|
183
|
|
|
} catch (\Exception $e) { |
|
184
|
|
|
return false; |
|
185
|
|
|
} |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
return true; |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* Get core settings for H5P content. |
|
193
|
|
|
* |
|
194
|
|
|
* @param H5pImport $h5pImport the H5pImport object |
|
195
|
|
|
* @param \H5PCore $h5pCore the H5PCore object |
|
196
|
|
|
* |
|
197
|
|
|
* @return array the core settings for H5P content |
|
198
|
|
|
*/ |
|
199
|
|
|
public static function getCoreSettings(H5pImport $h5pImport, \H5PCore $h5pCore): array |
|
200
|
|
|
{ |
|
201
|
|
|
$originIsLearnpath = 'learnpath' === api_get_origin(); |
|
202
|
|
|
|
|
203
|
|
|
$settings = [ |
|
204
|
|
|
'baseUrl' => api_get_path(WEB_PATH), |
|
205
|
|
|
'url' => $h5pImport->getRelativePath(), |
|
206
|
|
|
'postUserStatistics' => true, |
|
207
|
|
|
'ajax' => [ |
|
208
|
|
|
'setFinished' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=set_finished&h5pId='.$h5pImport->getIid().'&learnpath='.$originIsLearnpath.'&token='.\H5PCore::createToken('result'), |
|
209
|
|
|
'contentUserData' => api_get_path(WEB_PLUGIN_PATH).'h5pimport/src/ajax.php?action=content_user_data&h5pId='.$h5pImport->getIid().'&token='.\H5PCore::createToken('content'), |
|
210
|
|
|
], |
|
211
|
|
|
'saveFreq' => false, |
|
212
|
|
|
'l10n' => [ |
|
213
|
|
|
'H5P' => $h5pCore->getLocalization(), |
|
214
|
|
|
], |
|
215
|
|
|
// 'hubIsEnabled' => variable_get('h5p_hub_is_enabled', TRUE) ? TRUE : FALSE, |
|
216
|
|
|
'crossorigin' => false, |
|
217
|
|
|
// 'crossoriginCacheBuster' => variable_get('h5p_crossorigin_cache_buster', NULL), |
|
218
|
|
|
// 'libraryConfig' => $core->h5pF->getLibraryConfig(), |
|
219
|
|
|
'pluginCacheBuster' => '?0', |
|
220
|
|
|
'libraryUrl' => $h5pImport->getMainLibrary()->getLibraryPath().'/js', |
|
221
|
|
|
]; |
|
222
|
|
|
|
|
223
|
|
|
$loggedUser = api_get_user_info(); |
|
224
|
|
|
if ($loggedUser) { |
|
225
|
|
|
$settings['user'] = [ |
|
226
|
|
|
'name' => $loggedUser['complete_name'], |
|
227
|
|
|
'mail' => $loggedUser['email'], |
|
228
|
|
|
]; |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
return $settings; |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* Get the core assets. |
|
236
|
|
|
* |
|
237
|
|
|
* @return array[]|bool an array containing CSS and JS assets or false if some core assets missing |
|
238
|
|
|
*/ |
|
239
|
|
|
public static function getCoreAssets() |
|
240
|
|
|
{ |
|
241
|
|
|
$assets = [ |
|
242
|
|
|
'css' => [], |
|
243
|
|
|
'js' => [], |
|
244
|
|
|
]; |
|
245
|
|
|
|
|
246
|
|
|
// Add CSS assets |
|
247
|
|
|
foreach (\H5PCore::$styles as $style) { |
|
248
|
|
|
$auxAssetPath = 'vendor/h5p/h5p-core/'.$style; |
|
249
|
|
|
$assets['css'][] = api_get_path(WEB_PATH).$auxAssetPath; |
|
250
|
|
|
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) { |
|
251
|
|
|
return false; |
|
252
|
|
|
} |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
// Add JS assets |
|
256
|
|
|
foreach (\H5PCore::$scripts as $script) { |
|
257
|
|
|
$auxAssetPath = 'vendor/h5p/h5p-core/'.$script; |
|
258
|
|
|
$auxUrl = api_get_path(WEB_PATH).$auxAssetPath; |
|
259
|
|
|
$assets['js'][] = $auxUrl; |
|
260
|
|
|
if (!file_exists(api_get_path(SYS_PATH).$auxAssetPath)) { |
|
261
|
|
|
return false; |
|
262
|
|
|
} |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
return $assets; |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
/** |
|
269
|
|
|
* Return the content body for the H5PIntegration javascript object. |
|
270
|
|
|
* |
|
271
|
|
|
* @param mixed $h5pNode |
|
272
|
|
|
*/ |
|
273
|
|
|
public static function getContentSettings($h5pNode, \H5PCore $h5pCore): array |
|
274
|
|
|
{ |
|
275
|
|
|
$filtered = $h5pCore->filterParameters($h5pNode); |
|
276
|
|
|
$contentUserData = [ |
|
277
|
|
|
0 => [ |
|
278
|
|
|
'state' => '{}', |
|
279
|
|
|
], |
|
280
|
|
|
]; |
|
281
|
|
|
|
|
282
|
|
|
// ToDo Use $h5pCore->getDisplayOptionsForView() function |
|
283
|
|
|
$displayOptions = [ |
|
284
|
|
|
'frame' => api_get_course_plugin_setting('h5pimport', 'frame'), |
|
285
|
|
|
'embed' => api_get_course_plugin_setting('h5pimport', 'embed'), |
|
286
|
|
|
'copyright' => api_get_course_plugin_setting('h5pimport', 'copyright'), |
|
287
|
|
|
'icon' => api_get_course_plugin_setting('h5pimport', 'icon'), |
|
288
|
|
|
]; |
|
289
|
|
|
|
|
290
|
|
|
return [ |
|
291
|
|
|
'library' => \H5PCore::libraryToString($h5pNode['library']), |
|
292
|
|
|
'jsonContent' => $h5pNode['params'], |
|
293
|
|
|
'fullScreen' => $h5pNode['library']['fullscreen'], |
|
294
|
|
|
'exportUrl' => '', |
|
295
|
|
|
'language' => 'en', |
|
296
|
|
|
'filtered' => $filtered, |
|
297
|
|
|
'embedCode' => '<iframe src="'.api_get_course_url().'h5p/embed/'.$h5pNode['mainId'].'" width=":w" height=":h" frameborder="0" allowfullscreen="allowfullscreen" allow="geolocation *; microphone *; camera *; midi *; encrypted-media *" title="'.$h5pNode['title'].'"></iframe>', |
|
298
|
|
|
'resizeCode' => '', |
|
299
|
|
|
'mainId' => $h5pNode['mainId'], |
|
300
|
|
|
'url' => $h5pNode['url'], |
|
301
|
|
|
'contentUserData' => $contentUserData, |
|
302
|
|
|
'displayOptions' => $displayOptions, |
|
303
|
|
|
'metadata' => $h5pNode['metadata'], |
|
304
|
|
|
]; |
|
305
|
|
|
} |
|
306
|
|
|
|
|
307
|
|
|
/** |
|
308
|
|
|
* Convert H5P dependencies to a library list. |
|
309
|
|
|
* |
|
310
|
|
|
* @param array $dependencies the H5P dependencies |
|
311
|
|
|
* |
|
312
|
|
|
* @return array the library list with machine names as keys and version information as values |
|
313
|
|
|
*/ |
|
314
|
|
|
public static function h5pDependenciesToLibraryList(array $dependencies): array |
|
315
|
|
|
{ |
|
316
|
|
|
$libraryList = []; |
|
317
|
|
|
|
|
318
|
|
|
foreach ($dependencies as $dependency) { |
|
319
|
|
|
$libraryList[$dependency['machineName']] = [ |
|
320
|
|
|
'majorVersion' => $dependency['majorVersion'], |
|
321
|
|
|
'minorVersion' => $dependency['minorVersion'], |
|
322
|
|
|
]; |
|
323
|
|
|
} |
|
324
|
|
|
|
|
325
|
|
|
return $libraryList; |
|
326
|
|
|
} |
|
327
|
|
|
} |
|
328
|
|
|
|