1<?php
2
3namespace Moodle;
4
5/**
6 * This is a data layer which uses the file system so it isn't specific to any framework.
7 */
8class H5PDevelopment {
9
10  const MODE_NONE = 0;
11  const MODE_CONTENT = 1;
12  const MODE_LIBRARY = 2;
13
14  private $h5pF, $libraries, $language, $filesPath;
15
16  /**
17   * Constructor.
18   *
19   * @param H5PFrameworkInterface|object $H5PFramework
20   *  The frameworks implementation of the H5PFrameworkInterface
21   * @param string $filesPath
22   *  Path to where H5P should store its files
23   * @param $language
24   * @param array $libraries Optional cache input.
25   */
26  public function __construct(H5PFrameworkInterface $H5PFramework, $filesPath, $language, $libraries = NULL) {
27    $this->h5pF = $H5PFramework;
28    $this->language = $language;
29    $this->filesPath = $filesPath;
30    if ($libraries !== NULL) {
31      $this->libraries = $libraries;
32    }
33    else {
34      $this->findLibraries($filesPath . '/development');
35    }
36  }
37
38  /**
39   * Get contents of file.
40   *
41   * @param string $file File path.
42   * @return mixed String on success or NULL on failure.
43   */
44  private function getFileContents($file) {
45    if (file_exists($file) === FALSE) {
46      return NULL;
47    }
48
49    $contents = file_get_contents($file);
50    if ($contents === FALSE) {
51      return NULL;
52    }
53
54    return $contents;
55  }
56
57  /**
58   * Scans development directory and find all libraries.
59   *
60   * @param string $path Libraries development folder
61   */
62  private function findLibraries($path) {
63    $this->libraries = array();
64
65    if (is_dir($path) === FALSE) {
66      return;
67    }
68
69    $contents = scandir($path);
70
71    for ($i = 0, $s = count($contents); $i < $s; $i++) {
72      if ($contents[$i][0] === '.') {
73        continue; // Skip hidden stuff.
74      }
75
76      $libraryPath = $path . '/' . $contents[$i];
77      $libraryJSON = $this->getFileContents($libraryPath . '/library.json');
78      if ($libraryJSON === NULL) {
79        continue; // No JSON file, skip.
80      }
81
82      $library = json_decode($libraryJSON, TRUE);
83      if ($library === NULL) {
84        continue; // Invalid JSON.
85      }
86
87      // TODO: Validate props? Not really needed, is it? this is a dev site.
88
89      $library['libraryId'] = $this->h5pF->getLibraryId($library['machineName'], $library['majorVersion'], $library['minorVersion']);
90
91      // Convert metadataSettings values to boolean & json_encode it before saving
92      $library['metadataSettings'] = isset($library['metadataSettings']) ?
93        H5PMetadata::boolifyAndEncodeSettings($library['metadataSettings']) :
94        NULL;
95
96      // Save/update library.
97      $this->h5pF->saveLibraryData($library, $library['libraryId'] === FALSE);
98
99      // Need to decode it again, since it is served from here.
100      $library['metadataSettings'] = json_decode($library['metadataSettings']);
101
102      $library['path'] = 'development/' . $contents[$i];
103      $this->libraries[H5PDevelopment::libraryToString($library['machineName'], $library['majorVersion'], $library['minorVersion'])] = $library;
104    }
105
106    // TODO: Should we remove libraries without files? Not really needed, but must be cleaned up some time, right?
107
108    // Go trough libraries and insert dependencies. Missing deps. will just be ignored and not available. (I guess?!)
109    $this->h5pF->lockDependencyStorage();
110    foreach ($this->libraries as $library) {
111      $this->h5pF->deleteLibraryDependencies($library['libraryId']);
112      // This isn't optimal, but without it we would get duplicate warnings.
113      // TODO: You might get PDOExceptions if two or more requests does this at the same time!!
114      $types = array('preloaded', 'dynamic', 'editor');
115      foreach ($types as $type) {
116        if (isset($library[$type . 'Dependencies'])) {
117          $this->h5pF->saveLibraryDependencies($library['libraryId'], $library[$type . 'Dependencies'], $type);
118        }
119      }
120    }
121    $this->h5pF->unlockDependencyStorage();
122    // TODO: Deps must be inserted into h5p_nodes_libraries as well... ? But only if they are used?!
123  }
124
125  /**
126   * @return array Libraries in development folder.
127   */
128  public function getLibraries() {
129    return $this->libraries;
130  }
131
132  /**
133   * Get library
134   *
135   * @param string $name of the library.
136   * @param int $majorVersion of the library.
137   * @param int $minorVersion of the library.
138   * @return array library.
139   */
140  public function getLibrary($name, $majorVersion, $minorVersion) {
141    $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
142    return isset($this->libraries[$library]) === TRUE ? $this->libraries[$library] : NULL;
143  }
144
145  /**
146   * Get semantics for the given library.
147   *
148   * @param string $name of the library.
149   * @param int $majorVersion of the library.
150   * @param int $minorVersion of the library.
151   * @return string Semantics
152   */
153  public function getSemantics($name, $majorVersion, $minorVersion) {
154    $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
155    if (isset($this->libraries[$library]) === FALSE) {
156      return NULL;
157    }
158    return $this->getFileContents($this->filesPath . $this->libraries[$library]['path'] . '/semantics.json');
159  }
160
161  /**
162   * Get translations for the given library.
163   *
164   * @param string $name of the library.
165   * @param int $majorVersion of the library.
166   * @param int $minorVersion of the library.
167   * @param $language
168   * @return string Translation
169   */
170  public function getLanguage($name, $majorVersion, $minorVersion, $language) {
171    $library = H5PDevelopment::libraryToString($name, $majorVersion, $minorVersion);
172
173    if (isset($this->libraries[$library]) === FALSE) {
174      return NULL;
175    }
176
177    return $this->getFileContents($this->filesPath . $this->libraries[$library]['path'] . '/language/' . $language . '.json');
178  }
179
180  /**
181   * Writes library as string on the form "name majorVersion.minorVersion"
182   *
183   * @param string $name Machine readable library name
184   * @param integer $majorVersion
185   * @param $minorVersion
186   * @return string Library identifier.
187   */
188  public static function libraryToString($name, $majorVersion, $minorVersion) {
189    return $name . ' ' . $majorVersion . '.' . $minorVersion;
190  }
191}
192