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