See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 3 namespace Moodle; 4 5 use stdClass; 6 7 class H5peditor { 8 9 private static $hasWYSIWYGEditor = array( 10 'H5P.CoursePresentation', 11 'H5P.InteractiveVideo', 12 'H5P.DragQuestion' 13 ); 14 15 public static $styles = array( 16 'libs/darkroom.css', 17 'styles/css/h5p-hub-client.css', 18 'styles/css/fonts.css', 19 'styles/css/application.css', 20 'styles/css/libs/zebra_datepicker.min.css' 21 ); 22 public static $scripts = array( 23 'scripts/h5p-hub-client.js', 24 'scripts/h5peditor.js', 25 'scripts/h5peditor-semantic-structure.js', 26 'scripts/h5peditor-editor.js', 27 'scripts/h5peditor-library-selector.js', 28 'scripts/h5peditor-fullscreen-bar.js', 29 'scripts/h5peditor-form.js', 30 'scripts/h5peditor-text.js', 31 'scripts/h5peditor-html.js', 32 'scripts/h5peditor-number.js', 33 'scripts/h5peditor-textarea.js', 34 'scripts/h5peditor-file-uploader.js', 35 'scripts/h5peditor-file.js', 36 'scripts/h5peditor-image.js', 37 'scripts/h5peditor-image-popup.js', 38 'scripts/h5peditor-av.js', 39 'scripts/h5peditor-group.js', 40 'scripts/h5peditor-boolean.js', 41 'scripts/h5peditor-list.js', 42 'scripts/h5peditor-list-editor.js', 43 'scripts/h5peditor-library.js', 44 'scripts/h5peditor-library-list-cache.js', 45 'scripts/h5peditor-select.js', 46 'scripts/h5peditor-selector-hub.js', 47 'scripts/h5peditor-selector-legacy.js', 48 'scripts/h5peditor-dimensions.js', 49 'scripts/h5peditor-coordinates.js', 50 'scripts/h5peditor-none.js', 51 'scripts/h5peditor-metadata.js', 52 'scripts/h5peditor-metadata-author-widget.js', 53 'scripts/h5peditor-metadata-changelog-widget.js', 54 'scripts/h5peditor-pre-save.js', 55 'ckeditor/ckeditor.js', 56 ); 57 private $h5p, $storage; 58 public $ajax, $ajaxInterface; 59 60 /** 61 * Constructor for the core editor library. 62 * 63 * @param H5PCore $h5p Instance of core 64 * @param H5peditorStorage $storage Instance of h5peditor storage interface 65 * @param H5PEditorAjaxInterface $ajaxInterface Instance of h5peditor ajax 66 * interface 67 */ 68 function __construct($h5p, $storage, $ajaxInterface) { 69 $this->h5p = $h5p; 70 $this->storage = $storage; 71 $this->ajaxInterface = $ajaxInterface; 72 $this->ajax = new H5PEditorAjax($h5p, $this, $storage); 73 } 74 75 /** 76 * Get list of libraries. 77 * 78 * @return array 79 */ 80 public function getLibraries() { 81 if (isset($_POST['libraries'])) { 82 // Get details for the specified libraries. 83 $libraries = array(); 84 foreach ($_POST['libraries'] as $libraryName) { 85 $matches = array(); 86 preg_match_all('/(.+)\s(\d+)\.(\d+)$/', $libraryName, $matches); 87 if ($matches && $matches[1] && $matches[2] && $matches[3]) { 88 $libraries[] = (object) array( 89 'uberName' => $libraryName, 90 'name' => $matches[1][0], 91 'majorVersion' => $matches[2][0], 92 'minorVersion' => $matches[3][0] 93 ); 94 } 95 } 96 } 97 98 $libraries = $this->storage->getLibraries(!isset($libraries) ? NULL : $libraries); 99 100 if ($this->h5p->development_mode & H5PDevelopment::MODE_LIBRARY) { 101 $devLibs = $this->h5p->h5pD->getLibraries(); 102 } 103 104 for ($i = 0, $s = count($libraries); $i < $s; $i++) { 105 if (!empty($devLibs)) { 106 $lid = $libraries[$i]->name . ' ' . $libraries[$i]->majorVersion . '.' . $libraries[$i]->minorVersion; 107 if (isset($devLibs[$lid])) { 108 // Replace library with devlib 109 $isOld = !empty($libraries[$i]->isOld) && $libraries[$i]->isOld === TRUE; 110 $libraries[$i] = (object) array( 111 'uberName' => $lid, 112 'name' => $devLibs[$lid]['machineName'], 113 'title' => $devLibs[$lid]['title'], 114 'majorVersion' => $devLibs[$lid]['majorVersion'], 115 'minorVersion' => $devLibs[$lid]['minorVersion'], 116 'runnable' => $devLibs[$lid]['runnable'], 117 'restricted' => $libraries[$i]->restricted, 118 'tutorialUrl' => $libraries[$i]->tutorialUrl, 119 'metadataSettings' => $devLibs[$lid]['metadataSettings'], 120 ); 121 if ($isOld) { 122 $libraries[$i]->isOld = TRUE; 123 } 124 } 125 } 126 127 // Some libraries rely on an LRS to work and must be enabled manually 128 if (in_array($libraries[$i]->name, array('H5P.Questionnaire', 'H5P.FreeTextQuestion')) && 129 !$this->h5p->h5pF->getOption('enable_lrs_content_types')) { 130 $libraries[$i]->restricted = TRUE; 131 } 132 } 133 134 return $libraries; 135 } 136 137 /** 138 * Get translations for a language for a list of libraries 139 * 140 * @param array $libraries An array of libraries, in the form "<machineName> <majorVersion>.<minorVersion> 141 * @param string $language_code 142 * @return array 143 */ 144 public function getTranslations($libraries, $language_code) { 145 return $this->ajaxInterface->getTranslations($libraries, $language_code); 146 } 147 148 /** 149 * Move uploaded files, remove old files and update library usage. 150 * 151 * @param stdClass $content 152 * @param array $newLibrary 153 * @param array $newParameters 154 * @param array $oldLibrary 155 * @param array $oldParameters 156 */ 157 public function processParameters($content, $newLibrary, $newParameters, $oldLibrary = NULL, $oldParameters = NULL) { 158 $newFiles = array(); 159 $oldFiles = array(); 160 161 // Keep track of current content ID (used when processing files) 162 $this->content = $content; 163 164 // Find new libraries/content dependencies and files. 165 // Start by creating a fake library field to process. This way we get all the dependencies of the main library as well. 166 $field = (object) array( 167 'type' => 'library' 168 ); 169 $libraryParams = (object) array( 170 'library' => H5PCore::libraryToString($newLibrary), 171 'params' => $newParameters 172 ); 173 $this->processField($field, $libraryParams, $newFiles); 174 175 if ($oldLibrary !== NULL) { 176 // Find old files and libraries. 177 $this->processSemantics($oldFiles, $this->h5p->loadLibrarySemantics($oldLibrary['name'], $oldLibrary['majorVersion'], $oldLibrary['minorVersion']), $oldParameters); 178 179 // Remove old files. 180 for ($i = 0, $s = count($oldFiles); $i < $s; $i++) { 181 if (!in_array($oldFiles[$i], $newFiles) && 182 preg_match('/^(\w+:\/\/|\.\.\/)/i', $oldFiles[$i]) === 0) { 183 $this->h5p->fs->removeContentFile($oldFiles[$i], $content); 184 // (optionally we could just have marked them as tmp files) 185 } 186 } 187 } 188 } 189 190 /** 191 * Recursive function that moves the new files in to the h5p content folder and generates a list over the old files. 192 * Also locates all the librares. 193 * 194 * @param array $files 195 * @param array $libraries 196 * @param array $semantics 197 * @param array $params 198 */ 199 private function processSemantics(&$files, $semantics, &$params) { 200 for ($i = 0, $s = count($semantics); $i < $s; $i++) { 201 $field = $semantics[$i]; 202 if (!isset($params->{$field->name})) { 203 continue; 204 } 205 $this->processField($field, $params->{$field->name}, $files); 206 } 207 } 208 209 /** 210 * Process a single field. 211 * 212 * @staticvar string $h5peditor_path 213 * @param object $field 214 * @param mixed $params 215 * @param array $files 216 */ 217 private function processField(&$field, &$params, &$files) { 218 switch ($field->type) { 219 case 'file': 220 case 'image': 221 if (isset($params->path)) { 222 $this->processFile($params, $files); 223 224 // Process original image 225 if (isset($params->originalImage) && isset($params->originalImage->path)) { 226 $this->processFile($params->originalImage, $files); 227 } 228 } 229 break; 230 231 case 'video': 232 case 'audio': 233 if (is_array($params)) { 234 for ($i = 0, $s = count($params); $i < $s; $i++) { 235 $this->processFile($params[$i], $files); 236 } 237 } 238 break; 239 240 case 'library': 241 if (isset($params->library) && isset($params->params)) { 242 $library = H5PCore::libraryFromString($params->library); 243 $semantics = $this->h5p->loadLibrarySemantics($library['machineName'], $library['majorVersion'], $library['minorVersion']); 244 245 // Process parameters for the library. 246 $this->processSemantics($files, $semantics, $params->params); 247 } 248 break; 249 250 case 'group': 251 if (isset($params)) { 252 $isSubContent = isset($field->isSubContent) && $field->isSubContent == TRUE; 253 254 if (count($field->fields) == 1 && !$isSubContent) { 255 $params = (object) array($field->fields[0]->name => $params); 256 } 257 $this->processSemantics($files, $field->fields, $params); 258 } 259 break; 260 261 case 'list': 262 if (is_array($params)) { 263 for ($j = 0, $t = count($params); $j < $t; $j++) { 264 $this->processField($field->field, $params[$j], $files); 265 } 266 } 267 break; 268 } 269 } 270 271 /** 272 * @param mixed $params 273 * @param array $files 274 */ 275 private function processFile(&$params, &$files) { 276 if (preg_match('/^https?:\/\//', $params->path)) { 277 return; // Skip external files 278 } 279 280 // Remove temporary files suffix 281 if (substr($params->path, -4, 4) === '#tmp') { 282 $params->path = substr($params->path, 0, strlen($params->path) - 4); 283 } 284 285 // File could be copied from another content folder. 286 $matches = array(); 287 if (preg_match($this->h5p->relativePathRegExp, $params->path, $matches)) { 288 289 // Create a copy of the file 290 $this->h5p->fs->cloneContentFile($matches[5], $matches[4], $this->content); 291 292 // Update Params with correct filename 293 $params->path = $matches[5]; 294 } 295 else { 296 // Check if file exists in content folder 297 $fileId = $this->h5p->fs->getContentFile($params->path, $this->content); 298 if ($fileId) { 299 // Mark the file as a keeper 300 $this->storage->keepFile($fileId); 301 } 302 else { 303 // File is not in content folder, try to copy it from the editor tmp dir 304 // to content folder. 305 $this->h5p->fs->cloneContentFile($params->path, 'editor', $this->content); 306 // (not removed in case someone has copied it) 307 // (will automatically be removed after 24 hours) 308 } 309 } 310 311 $files[] = $params->path; 312 } 313 314 /** 315 * TODO: Consider moving to core. 316 */ 317 public function getLibraryLanguage($machineName, $majorVersion, $minorVersion, $languageCode) { 318 if ($this->h5p->development_mode & H5PDevelopment::MODE_LIBRARY) { 319 // Try to get language development library first. 320 $language = $this->h5p->h5pD->getLanguage($machineName, $majorVersion, $minorVersion, $languageCode); 321 } 322 323 if (isset($language) === FALSE) { 324 $language = $this->storage->getLanguage($machineName, $majorVersion, $minorVersion, $languageCode); 325 } 326 327 return ($language === FALSE ? NULL : $language); 328 } 329 330 /** 331 * Return all libraries used by the given editor library. 332 * 333 * @param string $machineName Library identfier part 1 334 * @param int $majorVersion Library identfier part 2 335 * @param int $minorVersion Library identfier part 3 336 */ 337 public function findEditorLibraries($machineName, $majorVersion, $minorVersion) { 338 $library = $this->h5p->loadLibrary($machineName, $majorVersion, $minorVersion); 339 $dependencies = array(); 340 $this->h5p->findLibraryDependencies($dependencies, $library); 341 342 // Load addons for wysiwyg editors 343 if (in_array($machineName, self::$hasWYSIWYGEditor)) { 344 $addons = $this->h5p->h5pF->loadAddons(); 345 foreach ($addons as $addon) { 346 $key = 'editor-' . $addon['machineName']; 347 $dependencies[$key]['weight'] = sizeof($dependencies)+1; 348 $dependencies[$key]['type'] = 'editor'; 349 $dependencies[$key]['library'] = $addon; 350 } 351 } 352 353 // Order dependencies by weight 354 $orderedDependencies = array(); 355 for ($i = 1, $s = count($dependencies); $i <= $s; $i++) { 356 foreach ($dependencies as $dependency) { 357 if ($dependency['weight'] === $i && $dependency['type'] === 'editor') { 358 // Only load editor libraries. 359 $dependency['library']['id'] = $dependency['library']['libraryId']; 360 $orderedDependencies[$dependency['library']['libraryId']] = $dependency['library']; 361 break; 362 } 363 } 364 } 365 366 return $orderedDependencies; 367 } 368 369 /** 370 * Get all scripts, css and semantics data for a library 371 * 372 * @param string $machineName Library name 373 * @param int $majorVersion 374 * @param int $minorVersion 375 * @param string $prefix Optional part to add between URL and asset path 376 * @param string $fileDir Optional file dir to read files from 377 * 378 * @return array Libraries that was requested 379 */ 380 public function getLibraryData($machineName, $majorVersion, $minorVersion, $languageCode, $prefix = '', $fileDir = '', $defaultLanguage = '') { 381 $libraryData = new stdClass(); 382 383 $library = $this->h5p->loadLibrary($machineName, $majorVersion, $minorVersion); 384 385 // Include name and version in data object for convenience 386 $libraryData->name = $library['machineName']; 387 $libraryData->version = (object) array('major' => $library['majorVersion'], 'minor' => $library['minorVersion']); 388 $libraryData->title = $library['title']; 389 390 $libraryData->upgradesScript = $this->h5p->fs->getUpgradeScript($library['machineName'], $library['majorVersion'], $library['minorVersion']); 391 if ($libraryData->upgradesScript !== NULL) { 392 // If valid add URL prefix 393 $libraryData->upgradesScript = $this->h5p->url . $prefix . $libraryData->upgradesScript; 394 } 395 396 $libraries = $this->findEditorLibraries($library['machineName'], $library['majorVersion'], $library['minorVersion']); 397 $libraryData->semantics = $this->h5p->loadLibrarySemantics($library['machineName'], $library['majorVersion'], $library['minorVersion']); 398 $libraryData->language = $this->getLibraryLanguage($library['machineName'], $library['majorVersion'], $library['minorVersion'], $languageCode); 399 $libraryData->defaultLanguage = empty($defaultLanguage) ? NULL : $this->getLibraryLanguage($library['machineName'], $library['majorVersion'], $library['minorVersion'], $defaultLanguage); 400 $libraryData->languages = $this->storage->getAvailableLanguages($library['machineName'], $library['majorVersion'], $library['minorVersion']); 401 402 // Temporarily disable asset aggregation 403 $aggregateAssets = $this->h5p->aggregateAssets; 404 $this->h5p->aggregateAssets = FALSE; 405 // This is done to prevent files being loaded multiple times due to how 406 // the editor works. 407 408 // Get list of JS and CSS files that belongs to the dependencies 409 $files = $this->h5p->getDependenciesFiles($libraries, $prefix); 410 $libraryName = H5PCore::libraryToString(compact('machineName', 'majorVersion', 'minorVersion'), true); 411 if ($this->hasPresave($libraryName) === true) { 412 $this->addPresaveFile($files, $library, $prefix); 413 } 414 $this->storage->alterLibraryFiles($files, $libraries); 415 416 // Restore asset aggregation setting 417 $this->h5p->aggregateAssets = $aggregateAssets; 418 419 // Create base URL 420 $url = $this->h5p->url; 421 422 // Javascripts 423 if (!empty($files['scripts'])) { 424 foreach ($files['scripts'] as $script) { 425 if (preg_match('/:\/\//', $script->path) === 1) { 426 // External file 427 $libraryData->javascript[] = $script->path . $script->version; 428 } 429 else { 430 // Local file 431 $path = $url . $script->path; 432 if (!isset($this->h5p->h5pD)) { 433 $path .= $script->version; 434 } 435 $libraryData->javascript[] = $path; 436 } 437 } 438 } 439 440 // Stylesheets 441 if (!empty($files['styles'])) { 442 foreach ($files['styles'] as $css) { 443 if (preg_match('/:\/\//', $css->path) === 1) { 444 // External file 445 $libraryData->css[] = $css->path . $css->version; 446 } 447 else { 448 // Local file 449 $path = $url . $css->path; 450 if (!isset($this->h5p->h5pD)) { 451 $path .= $css->version; 452 } 453 $libraryData->css[] = $path; 454 } 455 } 456 } 457 458 $translations = array(); 459 // Add translations for libraries. 460 foreach ($libraries as $library) { 461 if (empty($library['semantics'])) { 462 $translation = $this->getLibraryLanguage($library['machineName'], $library['majorVersion'], $library['minorVersion'], $languageCode); 463 464 // If translation was not found, and this is not the English one, try to load 465 // the English translation 466 if ($translation === NULL && $languageCode !== 'en') { 467 $translation = $this->getLibraryLanguage($library['machineName'], $library['majorVersion'], $library['minorVersion'], 'en'); 468 } 469 470 if ($translation !== NULL) { 471 $translations[$library['machineName']] = json_decode($translation); 472 } 473 } 474 } 475 476 $libraryData->translations = $translations; 477 478 return $libraryData; 479 } 480 481 /** 482 * This function will prefix all paths within a CSS file. 483 * Copied from Drupal 6. 484 * 485 * @staticvar type $_base 486 * @param type $matches 487 * @param type $base 488 * @return type 489 */ 490 public static function buildCssPath($matches, $base = NULL) { 491 static $_base; 492 // Store base path for preg_replace_callback. 493 if (isset($base)) { 494 $_base = $base; 495 } 496 497 // Prefix with base and remove '../' segments where possible. 498 $path = $_base . $matches[1]; 499 $last = ''; 500 while ($path != $last) { 501 $last = $path; 502 $path = preg_replace('`(^|/)(?!\.\./)([^/]+)/\.\./`', '$1', $path); 503 } 504 return 'url('. $path .')'; 505 } 506 507 /** 508 * Gets content type cache, applies user specific properties and formats 509 * as camelCase. 510 * 511 * @return array $libraries Cached libraries from the H5P Hub with user specific 512 * permission properties 513 */ 514 public function getUserSpecificContentTypeCache() { 515 $cached_libraries = $this->ajaxInterface->getContentTypeCache(); 516 517 // Check if user has access to install libraries 518 $libraries = array(); 519 foreach ($cached_libraries as &$result) { 520 // Check if user can install content type 521 $result->restricted = !$this->canInstallContentType($result); 522 523 // Formats json 524 $libraries[] = $this->getCachedLibsMap($result); 525 } 526 527 return $libraries; 528 } 529 530 public function canInstallContentType($contentType) { 531 $canInstallAll = $this->h5p->h5pF->hasPermission(H5PPermission::UPDATE_LIBRARIES); 532 $canInstallRecommended = $this->h5p->h5pF->hasPermission(H5PPermission::INSTALL_RECOMMENDED); 533 534 return $canInstallAll || $contentType->is_recommended && $canInstallRecommended; 535 } 536 537 /** 538 * Gets local and external libraries data with metadata to display 539 * all libraries that are currently available for the user. 540 * 541 * @return array $libraries Latest local and external libraries data with 542 * user specific permissions 543 */ 544 public function getLatestGlobalLibrariesData() { 545 $latest_local_libraries = $this->ajaxInterface->getLatestLibraryVersions(); 546 $cached_libraries = $this->getUserSpecificContentTypeCache(); 547 $this->mergeLocalLibsIntoCachedLibs($latest_local_libraries, $cached_libraries); 548 return $cached_libraries; 549 } 550 551 552 /** 553 * Extract library properties from cached library so they are ready to be 554 * returned as JSON 555 * 556 * @param object $cached_library A single library from the content type cache 557 * 558 * @return array A map containing the necessary properties for a cached 559 * library to send to the front-end 560 */ 561 public function getCachedLibsMap($cached_library) { 562 $restricted = isset($cached_library->restricted) ? $cached_library->restricted : FALSE; 563 564 // Add mandatory fields 565 $lib = array( 566 'id' => intval($cached_library->id), 567 'machineName' => $cached_library->machine_name, 568 'majorVersion' => intval( $cached_library->major_version), 569 'minorVersion' => intval($cached_library->minor_version), 570 'patchVersion' => intval($cached_library->patch_version), 571 'h5pMajorVersion' => intval($cached_library->h5p_major_version), 572 'h5pMinorVersion' => intval($cached_library->h5p_minor_version), 573 'title' => $cached_library->title, 574 'summary' => $cached_library->summary, 575 'description' => $cached_library->description, 576 'icon' => $cached_library->icon, 577 'createdAt' => intval($cached_library->created_at), 578 'updatedAt' => intval($cached_library->updated_at), 579 'isRecommended' => $cached_library->is_recommended != 0, 580 'popularity' => intval($cached_library->popularity), 581 'screenshots' => json_decode($cached_library->screenshots), 582 'license' => json_decode($cached_library->license), 583 'owner' => $cached_library->owner, 584 'installed' => FALSE, 585 'isUpToDate' => FALSE, 586 'restricted' => $restricted, 587 'canInstall' => !$restricted 588 ); 589 590 // Add optional fields 591 if (!empty($cached_library->categories)) { 592 $lib['categories'] = json_decode($cached_library->categories); 593 } 594 if (!empty($cached_library->keywords)) { 595 $lib['keywords'] = json_decode($cached_library->keywords); 596 } 597 if (!empty($cached_library->tutorial)) { 598 $lib['tutorial'] = $cached_library->tutorial; 599 } 600 if (!empty($cached_library->example)) { 601 $lib['example'] = $cached_library->example; 602 } 603 if (!empty($cached_library->icons)) { 604 $lib['icons'] = json_decode($cached_library->icons); 605 } 606 607 return $lib; 608 } 609 610 611 /** 612 * Merge local libraries into cached libraries so that local libraries will 613 * get supplemented with the additional info from externally cached libraries. 614 * 615 * Also sets whether a given cached library is installed and up to date with 616 * the locally installed libraries 617 * 618 * @param array $local_libraries Locally installed libraries 619 * @param array $cached_libraries Cached libraries from the H5P hub 620 */ 621 public function mergeLocalLibsIntoCachedLibs($local_libraries, &$cached_libraries) { 622 $can_create_restricted = $this->h5p->h5pF->hasPermission(H5PPermission::CREATE_RESTRICTED); 623 624 // Add local libraries to supplement content type cache 625 foreach ($local_libraries as $local_lib) { 626 $is_local_only = TRUE; 627 $icon_path = NULL; 628 629 // Check if icon is available locally: 630 if ($local_lib->has_icon) { 631 // Create path to icon: 632 $library_folder = H5PCore::libraryToString(array( 633 'machineName' => $local_lib->machine_name, 634 'majorVersion' => $local_lib->major_version, 635 'minorVersion' => $local_lib->minor_version 636 ), TRUE); 637 $icon_path = $this->h5p->h5pF->getLibraryFileUrl($library_folder, 'icon.svg'); 638 } 639 640 foreach ($cached_libraries as &$cached_lib) { 641 // Determine if library is local 642 $is_matching_library = $cached_lib['machineName'] === $local_lib->machine_name; 643 if ($is_matching_library) { 644 $is_local_only = FALSE; 645 646 // Set icon if it exists locally 647 if (isset($icon_path)) { 648 $cached_lib['icon'] = $icon_path; 649 } 650 651 // Set local properties 652 $cached_lib['installed'] = TRUE; 653 $cached_lib['restricted'] = $can_create_restricted ? FALSE 654 : ($local_lib->restricted ? TRUE : FALSE); 655 656 // Set local version 657 $cached_lib['localMajorVersion'] = (int) $local_lib->major_version; 658 $cached_lib['localMinorVersion'] = (int) $local_lib->minor_version; 659 $cached_lib['localPatchVersion'] = (int) $local_lib->patch_version; 660 661 // Determine if library is newer or same as cache 662 $major_is_updated = 663 $cached_lib['majorVersion'] < $cached_lib['localMajorVersion']; 664 665 $minor_is_updated = 666 $cached_lib['majorVersion'] === $cached_lib['localMajorVersion'] && 667 $cached_lib['minorVersion'] < $cached_lib['localMinorVersion']; 668 669 $patch_is_updated = 670 $cached_lib['majorVersion'] === $cached_lib['localMajorVersion'] && 671 $cached_lib['minorVersion'] === $cached_lib['localMinorVersion'] && 672 $cached_lib['patchVersion'] <= $cached_lib['localPatchVersion']; 673 674 $is_updated_library = 675 $major_is_updated || 676 $minor_is_updated || 677 $patch_is_updated; 678 679 if ($is_updated_library) { 680 $cached_lib['isUpToDate'] = TRUE; 681 } 682 } 683 } 684 685 // Add minimal data to display local only libraries 686 if ($is_local_only) { 687 $local_only_lib = array( 688 'id' => (int) $local_lib->id, 689 'machineName' => $local_lib->machine_name, 690 'title' => $local_lib->title, 691 'description' => '', 692 'majorVersion' => (int) $local_lib->major_version, 693 'minorVersion' => (int) $local_lib->minor_version, 694 'patchVersion' => (int) $local_lib->patch_version, 695 'localMajorVersion' => (int) $local_lib->major_version, 696 'localMinorVersion' => (int) $local_lib->minor_version, 697 'localPatchVersion' => (int) $local_lib->patch_version, 698 'canInstall' => FALSE, 699 'installed' => TRUE, 700 'isUpToDate' => TRUE, 701 'owner' => '', 702 'restricted' => $can_create_restricted ? FALSE : 703 ($local_lib->restricted ? TRUE : FALSE) 704 ); 705 706 if (isset($icon_path)) { 707 $local_only_lib['icon'] = $icon_path; 708 } 709 710 $cached_libraries[] = $local_only_lib; 711 } 712 } 713 714 // Restrict LRS dependent content 715 if (!$this->h5p->h5pF->getOption('enable_lrs_content_types')) { 716 foreach ($cached_libraries as &$lib) { 717 if (in_array($lib['machineName'], array('H5P.Questionnaire', 'H5P.FreeTextQuestion'))) { 718 $lib['restricted'] = TRUE; 719 } 720 } 721 } 722 } 723 724 /** 725 * Determine if a library has a presave.js file in the root folder 726 * 727 * @param string $libraryName 728 * @return bool 729 */ 730 public function hasPresave($libraryName){ 731 if( isset($this->h5p->h5pD) ){ 732 $parsedLibrary = H5PCore::libraryFromString($libraryName); 733 if($parsedLibrary !== false){ 734 $machineName = $parsedLibrary['machineName']; 735 $majorVersion = $parsedLibrary['majorVersion']; 736 $minorVersion = $parsedLibrary['minorVersion']; 737 $library = $this->h5p->h5pD->getLibrary($machineName, $majorVersion, $minorVersion); 738 if( !is_null($library)){ 739 return $this->h5p->fs->hasPresave($libraryName, $library['path']); 740 } 741 } 742 } 743 return $this->h5p->fs->hasPresave($libraryName); 744 } 745 746 /** 747 * Adds the path to the presave.js file to the list of dependency assets for the library 748 * 749 * @param array $assets 750 * @param array $library 751 * @param string $prefix 752 */ 753 public function addPresaveFile(&$assets, $library, $prefix = ''){ 754 $path = 'libraries' . '/' . H5PCore::libraryToString($library, true); 755 if( array_key_exists('path', $library)){ 756 $path = $library['path']; 757 } 758 $version = "?ver={$library['majorVersion']}.{$library['minorVersion']}.{$library['patchVersion']}"; 759 if( array_key_exists('version', $library) ){ 760 $version = $library['version']; 761 } 762 763 $assets['scripts'][] = (object) array( 764 'path' => $prefix . '/' . $path . '/' . 'presave.js', 765 'version' => $version, 766 ); 767 } 768 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body