5 abstract class H5PEditorEndpoints
{
8 * Endpoint for retrieving library data necessary for displaying
9 * content types in the editor.
11 const LIBRARIES
= 'libraries';
14 * Endpoint for retrieving a singe library's data necessary for displaying
17 const SINGLE_LIBRARY
= 'single-library';
20 * Endpoint for retrieving the currently stored content type cache
22 const CONTENT_TYPE_CACHE
= 'content-type-cache';
25 * Endpoint for retrieving the currently stored content hub metadata cache
27 const CONTENT_HUB_METADATA_CACHE
= 'content-hub-metadata-cache';
30 * Endpoint for installing libraries from the Content Type Hub
32 const LIBRARY_INSTALL
= 'library-install';
35 * Endpoint for uploading libraries used by the editor through the Content
38 const LIBRARY_UPLOAD
= 'library-upload';
41 * Endpoint for uploading files used by the editor.
43 const FILES
= 'files';
46 * Endpoint for retrieveing translation files
48 const TRANSLATIONS
= 'translations';
51 * Endpoint for filtering parameters.
53 const FILTER
= 'filter';
56 * Endpoint for installing libraries from the Content Type Hub
58 const GET_HUB_CONTENT
= 'get-hub-content';
64 * @package modules\h5peditor\h5peditor
79 * @var H5peditorStorage
84 * H5PEditorAjax constructor requires core, editor and storage as building
87 * @param H5PCore $H5PCore
88 * @param H5peditor $H5PEditor
89 * @param H5peditorStorage $H5PEditorStorage
91 public function __construct(H5PCore
$H5PCore, H5peditor
$H5PEditor, H5peditorStorage
$H5PEditorStorage) {
92 $this->core
= $H5PCore;
93 $this->editor
= $H5PEditor;
94 $this->storage
= $H5PEditorStorage;
100 public function action($endpoint) {
102 case H5PEditorEndpoints
::LIBRARIES
:
103 H5PCore
::ajaxSuccess($this->editor
->getLibraries(), TRUE);
106 case H5PEditorEndpoints
::SINGLE_LIBRARY
:
108 $args = func_get_args();
110 $library = call_user_func_array(
111 array($this->editor
, 'getLibraryData'), $args
113 H5PCore
::ajaxSuccess($library, TRUE);
116 case H5PEditorEndpoints
::CONTENT_TYPE_CACHE
:
117 if (!$this->isHubOn()) return;
118 H5PCore
::ajaxSuccess($this->getContentTypeCache(!$this->isContentTypeCacheUpdated()), TRUE);
121 case H5PEditorEndpoints
::CONTENT_HUB_METADATA_CACHE
:
122 if (!$this->isHubOn()) return;
123 header('Cache-Control: no-cache');
124 header('Content-Type: application/json; charset=utf-8');
125 print '{"success":true,"data":' . $this->core
->getUpdatedContentHubMetadataCache(func_get_arg(1)) . '}';
128 case H5PEditorEndpoints
::LIBRARY_INSTALL
:
129 if (!$this->isPostRequest()) return;
131 $token = func_get_arg(1);
132 if (!$this->isValidEditorToken($token)) return;
134 $machineName = func_get_arg(2);
135 $this->libraryInstall($machineName);
138 case H5PEditorEndpoints
::LIBRARY_UPLOAD
:
139 if (!$this->isPostRequest()) return;
141 $token = func_get_arg(1);
142 if (!$this->isValidEditorToken($token)) return;
144 $uploadPath = func_get_arg(2);
145 $contentId = func_get_arg(3);
146 $this->libraryUpload($uploadPath, $contentId);
149 case H5PEditorEndpoints
::FILES
:
150 $token = func_get_arg(1);
151 $contentId = func_get_arg(2);
152 if (!$this->isValidEditorToken($token)) return;
153 $this->fileUpload($contentId);
156 case H5PEditorEndpoints
::TRANSLATIONS
:
157 $language = func_get_arg(1);
158 H5PCore
::ajaxSuccess($this->editor
->getTranslations($_POST['libraries'], $language));
161 case H5PEditorEndpoints
::FILTER
:
162 $token = func_get_arg(1);
163 if (!$this->isValidEditorToken($token)) return;
164 $this->filter(func_get_arg(2));
167 case H5PEditorEndpoints
::GET_HUB_CONTENT
:
168 if (!$this->isPostRequest() ||
!$this->isValidEditorToken(func_get_arg(1))) {
171 $this->getHubContent(func_get_arg(2), func_get_arg(3));
177 * Handles uploaded files from the editor, making sure they are validated
178 * and ready to be permanently stored if saved.
180 * Marks all uploaded files as
181 * temporary so they can be cleaned up when we have finished using them.
183 * @param int $contentId Id of content if already existing content
185 private function fileUpload($contentId = NULL) {
186 $file = new H5peditorFile($this->core
->h5pF
);
187 if (!$file->isLoaded()) {
188 H5PCore
::ajaxError($this->core
->h5pF
->t('File not found on server. Check file upload settings.'));
192 // Make sure file is valid and mark it for cleanup at a later time
193 if ($file->validate()) {
194 $file_id = $this->core
->fs
->saveFile($file, 0);
195 $this->storage
->markFileForCleanup($file_id, 0);
197 $file->printResult();
201 * Handles uploading libraries so they are ready to be modified or directly saved.
203 * Validates and saves any dependencies, then exposes content to the editor.
205 * @param {string} $uploadFilePath Path to the file that should be uploaded
206 * @param {int} $contentId Content id of library
208 private function libraryUpload($uploadFilePath, $contentId) {
210 if (!$uploadFilePath) {
211 H5PCore
::ajaxError($this->core
->h5pF
->t('Could not get posted H5P.'), 'NO_CONTENT_TYPE');
215 $file = $this->saveFileTemporarily($uploadFilePath, TRUE);
218 $this->processContent($contentId);
222 * Process H5P content from local H5P package.
224 * @param integer $contentId The Local Content ID / vid. TODO Remove when JI-366 is fixed
226 private function processContent($contentId) {
227 // Check if the downloaded package is valid
228 if (!$this->isValidPackage()) {
229 return; // Validation errors
232 // Install any required dependencies (libraries) from the package
233 // (if permission allows it, of course)
234 $storage = new H5PStorage($this->core
->h5pF
, $this->core
);
235 $storage->savePackage(NULL, NULL, TRUE);
237 // Make content available to editor
238 $files = $this->core
->fs
->moveContentDirectory($this->core
->h5pF
->getUploadedH5pFolderPath(), $contentId);
241 $this->storage
->removeTemporarilySavedFiles($this->core
->h5pF
->getUploadedH5pFolderPath());
243 // Mark all files as temporary
244 // TODO: Uncomment once moveContentDirectory() is fixed. JI-366
245 /*foreach ($files as $file) {
246 $this->storage->markFileForCleanup($file, 0);
249 H5PCore
::ajaxSuccess(array(
250 'h5p' => $this->core
->mainJsonData
,
251 'content' => $this->core
->contentJsonData
,
252 'contentTypes' => $this->getContentTypeCache()
257 * Validates security tokens used for the editor
259 * @param string $token
263 private function isValidEditorToken($token) {
264 $isValidToken = $this->editor
->ajaxInterface
->validateEditorToken($token);
265 if (!$isValidToken) {
267 $this->core
->h5pF
->t('Invalid security token.'),
276 * Handles installation of libraries from the Content Type Hub.
278 * Accepts a machine name and attempts to fetch and install it from the Hub if
279 * it is valid. Will also install any dependencies to the requested library.
281 * @param string $machineName Name of library that should be installed
283 private function libraryInstall($machineName) {
285 // Determine which content type to install from post data
287 H5PCore
::ajaxError($this->core
->h5pF
->t('No content type was specified.'), 'NO_CONTENT_TYPE');
291 // Look up content type to ensure it's valid(and to check permissions)
292 $contentType = $this->editor
->ajaxInterface
->getContentTypeCache($machineName);
294 H5PCore
::ajaxError($this->core
->h5pF
->t('The chosen content type is invalid.'), 'INVALID_CONTENT_TYPE');
298 // Check install permissions
299 if (!$this->editor
->canInstallContentType($contentType)) {
300 H5PCore
::ajaxError($this->core
->h5pF
->t('You do not have permission to install content types. Contact the administrator of your site.'), 'INSTALL_DENIED');
304 // Override core permission check
305 $this->core
->mayUpdateLibraries(TRUE);
308 // Retrieve content type from hub endpoint
309 $response = $this->callHubEndpoint(H5PHubEndpoints
::CONTENT_TYPES
. $machineName);
310 if (!$response) return;
312 // Session parameters has to be set for validation and saving of packages
313 if (!$this->isValidPackage(TRUE)) return;
316 $storage = new H5PStorage($this->core
->h5pF
, $this->core
);
317 $storage->savePackage(NULL, NULL, TRUE);
320 $this->storage
->removeTemporarilySavedFiles($this->core
->h5pF
->getUploadedH5pFolderPath());
322 // Successfully installed. Refresh content types
323 H5PCore
::ajaxSuccess($this->getContentTypeCache());
327 * End-point for filter parameter values according to semantics.
329 * @param {string} $libraryParameters
331 private function filter($libraryParameters) {
332 $libraryParameters = json_decode($libraryParameters);
333 if (!$libraryParameters) {
334 H5PCore
::ajaxError($this->core
->h5pF
->t('Could not parse post data.'), 'NO_LIBRARY_PARAMETERS');
338 // Filter parameters and send back to client
339 $validator = new H5PContentValidator($this->core
->h5pF
, $this->core
);
340 $validator->validateLibrary($libraryParameters, (object) array('options' => array($libraryParameters->library
)));
341 H5PCore
::ajaxSuccess($libraryParameters);
345 * Download and use content from the HUB
347 * @param integer $hubId The Hub Content ID
348 * @param integer $localContentId The Local Content ID
350 private function getHubContent($hubId, $localContentId) {
352 if (!$this->callHubEndpoint(H5PHubEndpoints
::CONTENT
. '/' . $hubId . '/export')) {
353 return; // Download failed
356 $this->processContent($localContentId);
360 * Validates the package. Sets error messages if validation fails.
362 * @param bool $skipContent Will not validate cotent if set to TRUE
366 private function isValidPackage($skipContent = FALSE) {
367 $validator = new H5PValidator($this->core
->h5pF
, $this->core
);
368 if (!$validator->isValidPackage($skipContent, FALSE)) {
369 $this->storage
->removeTemporarilySavedFiles($this->core
->h5pF
->getUploadedH5pPath());
372 $this->core
->h5pF
->t('Validating h5p package failed.'),
375 $this->core
->h5pF
->getMessages('error')
384 * Saves a file or moves it temporarily. This is often necessary in order to
385 * validate and store uploaded or fetched H5Ps.
387 * Sets error messages if saving fails.
389 * @param string $data Uri of data that should be saved as a temporary file
390 * @param boolean $move_file Can be set to TRUE to move the data instead of saving it
392 * @return bool|object Returns false if saving failed or the path to the file
393 * if saving succeeded
395 private function saveFileTemporarily($data, $move_file = FALSE) {
396 $file = $this->storage
->saveFileTemporarily($data, $move_file);
399 $this->core
->h5pF
->t('Failed to download the requested H5P.'),
409 * Calls provided hub endpoint and downloads the response to a .h5p file.
411 * @param string $endpoint Endpoint without protocol
415 private function callHubEndpoint($endpoint) {
416 $path = $this->core
->h5pF
->getUploadedH5pPath();
417 $response = $this->core
->h5pF
->fetchExternalData(H5PHubEndpoints
::createURL($endpoint), NULL, TRUE, empty($path) ?
TRUE : $path);
420 $this->core
->h5pF
->t('Failed to download the requested H5P.'),
423 $this->core
->h5pF
->getMessages('error')
432 * Checks if request is a POST. Sets error message on fail.
436 private function isPostRequest() {
437 if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
439 $this->core
->h5pF
->t('A post message is required to access the given endpoint'),
449 * Checks if H5P Hub is enabled. Sets error message on fail.
453 private function isHubOn() {
454 if (!$this->core
->h5pF
->getOption('hub_is_enabled', TRUE)) {
456 $this->core
->h5pF
->t('The hub is disabled. You can enable it in the H5P settings.'),
466 * Checks if Content Type Cache is up to date. Immediately tries to fetch
467 * a new Content Type Cache if it is outdated.
468 * Sets error message if fetching new Content Type Cache fails.
472 private function isContentTypeCacheUpdated() {
474 // Update content type cache if enabled and too old
475 $ct_cache_last_update = $this->core
->h5pF
->getOption('content_type_cache_updated_at', 0);
476 $outdated_cache = $ct_cache_last_update +
(60 * 60 * 24 * 7); // 1 week
477 if (time() > $outdated_cache) {
478 $success = $this->core
->updateContentTypeCache();
487 * Gets content type cache for globally available libraries and the order
488 * in which they have been used by the author
490 * @param bool $cacheOutdated The cache is outdated and not able to update
492 private function getContentTypeCache($cacheOutdated = FALSE) {
493 $canUpdateOrInstall = ($this->core
->h5pF
->hasPermission(H5PPermission
::INSTALL_RECOMMENDED
) ||
494 $this->core
->h5pF
->hasPermission(H5PPermission
::UPDATE_LIBRARIES
));
496 'outdated' => $cacheOutdated && $canUpdateOrInstall,
497 'libraries' => $this->editor
->getLatestGlobalLibrariesData(),
498 'recentlyUsed' => $this->editor
->ajaxInterface
->getAuthorsRecentlyUsedLibraries(),
499 'apiVersion' => array(
500 'major' => H5PCore
::$coreApi['majorVersion'],
501 'minor' => H5PCore
::$coreApi['minorVersion']
503 'details' => $this->core
->h5pF
->getMessages('info')