Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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