2012-11-21 14:14:07 +01:00
< ? php
2013-01-14 23:47:50 +01:00
/**
* Interface defining functions the h5p library needs the framework to implement
*/
2013-02-17 15:39:26 +01:00
interface H5PFrameworkInterface {
2014-03-26 08:43:29 +01:00
2014-09-05 09:59:16 +02:00
/**
* Returns info for the current platform
2014-10-10 13:56:48 +02:00
*
* @ return array
2014-10-08 10:16:16 +02:00
* An associative array containing :
2016-04-13 06:01:15 +02:00
* - name : The name of the platform , for instance " Wordpress "
* - version : The version of the platform , for instance " 4.0 "
2014-11-02 13:46:09 +01:00
* - h5pVersion : The version of the H5P plugin / module
2014-09-05 09:59:16 +02:00
*/
public function getPlatformInfo ();
2014-10-10 13:56:48 +02:00
2015-04-13 18:41:53 +02:00
2014-11-02 14:29:13 +01:00
/**
* Fetches a file from a remote server using HTTP GET
*
2017-04-04 12:44:22 +02:00
* @ param string $url Where you want to get or send data .
* @ param array $data Data to post to the URL .
* @ param bool $blocking Set to 'FALSE' to instantly time out ( fire and forget ) .
* @ param string $stream Path to where the file should be saved .
2014-11-02 14:29:13 +01:00
* @ return string The content ( response body ) . NULL if something went wrong
*/
2017-04-03 14:51:54 +02:00
public function fetchExternalData ( $url , $data = NULL , $blocking = TRUE , $stream = NULL );
2014-11-02 14:29:13 +01:00
2014-11-01 17:05:15 +01:00
/**
* Set the tutorial URL for a library . All versions of the library is set
*
* @ param string $machineName
* @ param string $tutorialUrl
*/
public function setLibraryTutorialUrl ( $machineName , $tutorialUrl );
2013-01-14 23:47:50 +01:00
/**
* Show the user an error message
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* @ param string $message
2014-10-08 10:16:16 +02:00
* The error message
2013-01-14 23:47:50 +01:00
*/
2012-11-22 14:00:06 +01:00
public function setErrorMessage ( $message );
2013-07-09 10:14:42 +02:00
2013-01-14 23:47:50 +01:00
/**
* Show the user an information message
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* @ param string $message
* The error message
*/
2012-11-22 14:00:06 +01:00
public function setInfoMessage ( $message );
2013-01-14 23:47:50 +01:00
/**
* Translation function
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* @ param string $message
2013-07-09 10:14:42 +02:00
* The english string to be translated .
2016-04-13 06:01:15 +02:00
* @ param array $replacements
2013-01-14 23:47:50 +01:00
* An associative array of replacements to make after translation . Incidences
* of any key in this array are replaced with the corresponding value . Based
* on the first character of the key , the value is escaped and / or themed :
* - ! variable : inserted as is
* - @ variable : escape plain text to HTML
* - % variable : escape text and theme as a placeholder for user - submitted
* content
2016-04-13 06:01:15 +02:00
* @ return string Translated string
* Translated string
2013-01-14 23:47:50 +01:00
*/
2012-12-03 00:19:25 +01:00
public function t ( $message , $replacements = array ());
2013-07-09 10:14:42 +02:00
2017-03-07 10:25:52 +01:00
/**
* Get URL to file in the specific library
* @ param string $libraryFolderName
* @ param string $fileName
* @ return string URL to file
*/
public function getLibraryFileUrl ( $libraryFolderName , $fileName );
2017-03-07 09:46:16 +01:00
2013-01-14 23:47:50 +01:00
/**
* Get the Path to the last uploaded h5p
2013-07-09 10:14:42 +02:00
*
2014-10-08 10:16:16 +02:00
* @ return string
* Path to the folder where the last uploaded h5p for this session is located .
2013-01-14 23:47:50 +01:00
*/
2012-11-30 15:51:39 +01:00
public function getUploadedH5pFolderPath ();
2013-07-09 10:14:42 +02:00
2013-01-14 23:47:50 +01:00
/**
* Get the path to the last uploaded h5p file
2013-07-09 10:14:42 +02:00
*
2014-10-08 10:16:16 +02:00
* @ return string
* Path to the last uploaded h5p
2013-01-14 23:47:50 +01:00
*/
2012-11-22 14:00:06 +01:00
public function getUploadedH5pPath ();
2014-10-10 13:56:48 +02:00
2014-08-19 10:14:55 +02:00
/**
2014-10-08 10:16:16 +02:00
* Get a list of the current installed libraries
2014-10-10 13:56:48 +02:00
*
* @ return array
2016-04-13 06:01:15 +02:00
* Associative array containing one entry per machine name .
2014-10-08 10:16:16 +02:00
* For each machineName there is a list of libraries ( with different versions )
2014-08-19 10:14:55 +02:00
*/
public function loadLibraries ();
2014-10-10 13:56:48 +02:00
2014-08-22 14:03:00 +02:00
/**
* Returns the URL to the library admin page
2014-10-10 13:56:48 +02:00
*
2014-10-08 10:16:16 +02:00
* @ return string
* URL to admin page
2014-08-22 14:03:00 +02:00
*/
public function getAdminUrl ();
2013-07-09 10:14:42 +02:00
2013-01-14 23:47:50 +01:00
/**
2016-01-20 13:21:51 +01:00
* Get id to an existing library .
* If version number is not specified , the newest version will be returned .
2013-07-09 10:14:42 +02:00
*
2013-03-09 01:20:55 +01:00
* @ param string $machineName
2014-10-08 10:16:16 +02:00
* The librarys machine name
2013-01-14 23:47:50 +01:00
* @ param int $majorVersion
2016-01-20 13:21:51 +01:00
* Optional major version number for library
2013-01-14 23:47:50 +01:00
* @ param int $minorVersion
2016-01-20 13:21:51 +01:00
* Optional minor version number for library
2013-01-14 23:47:50 +01:00
* @ return int
2014-10-08 10:16:16 +02:00
* The id of the specified library or FALSE
2013-01-14 23:47:50 +01:00
*/
2016-01-20 13:21:51 +01:00
public function getLibraryId ( $machineName , $majorVersion = NULL , $minorVersion = NULL );
2013-07-15 16:25:10 +02:00
2013-07-13 22:25:18 +02:00
/**
* Get file extension whitelist
2013-07-15 16:25:10 +02:00
*
2013-07-13 22:25:18 +02:00
* The default extension list is part of h5p , but admins should be allowed to modify it
2013-07-15 16:25:10 +02:00
*
2013-07-13 22:25:18 +02:00
* @ param boolean $isLibrary
2014-10-08 10:16:16 +02:00
* TRUE if this is the whitelist for a library . FALSE if it is the whitelist
* for the content folder we are getting
2013-07-13 22:25:18 +02:00
* @ param string $defaultContentWhitelist
2014-10-08 10:16:16 +02:00
* A string of file extensions separated by whitespace
2013-07-13 22:25:18 +02:00
* @ param string $defaultLibraryWhitelist
2014-10-08 10:16:16 +02:00
* A string of file extensions separated by whitespace
2013-07-13 22:25:18 +02:00
*/
public function getWhitelist ( $isLibrary , $defaultContentWhitelist , $defaultLibraryWhitelist );
2014-10-10 13:56:48 +02:00
2013-01-14 23:47:50 +01:00
/**
2013-06-30 16:39:17 +02:00
* Is the library a patched version of an existing library ?
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* @ param object $library
2016-04-13 06:01:15 +02:00
* An associative array containing :
2014-10-08 10:16:16 +02:00
* - machineName : The library machineName
* - majorVersion : The librarys majorVersion
* - minorVersion : The librarys minorVersion
* - patchVersion : The librarys patchVersion
2013-01-14 23:47:50 +01:00
* @ return boolean
2014-10-10 13:57:43 +02:00
* TRUE if the library is a patched version of an existing library
2014-10-08 10:16:16 +02:00
* FALSE otherwise
2013-01-14 23:47:50 +01:00
*/
2012-12-22 06:35:16 +01:00
public function isPatchedLibrary ( $library );
2013-10-21 13:22:13 +02:00
2013-08-12 08:11:11 +02:00
/**
* Is H5P in development mode ?
2013-10-21 13:22:13 +02:00
*
2013-08-12 08:11:11 +02:00
* @ return boolean
* TRUE if H5P development mode is active
* FALSE otherwise
*/
public function isInDevMode ();
2013-07-09 10:14:42 +02:00
2013-06-30 16:39:17 +02:00
/**
2013-07-15 17:36:56 +02:00
* Is the current user allowed to update libraries ?
2013-06-30 16:39:17 +02:00
*
* @ return boolean
2013-07-15 17:36:56 +02:00
* TRUE if the user is allowed to update libraries
* FALSE if the user is not allowed to update libraries
2013-06-30 16:39:17 +02:00
*/
2013-07-15 17:36:56 +02:00
public function mayUpdateLibraries ();
2013-06-30 16:39:17 +02:00
2013-01-14 23:47:50 +01:00
/**
* Store data about a library
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* Also fills in the libraryId in the libraryData object if the object is new
2013-07-09 10:14:42 +02:00
*
2013-01-14 23:47:50 +01:00
* @ param object $libraryData
2014-10-08 10:16:16 +02:00
* Associative array containing :
* - libraryId : The id of the library if it is an existing l ibrary .
* - title : The library ' s name
* - machineName : The library machineName
* - majorVersion : The library ' s majorVersion
* - minorVersion : The library ' s minorVersion
* - patchVersion : The library ' s patchVersion
* - runnable : 1 if the library is a content type , 0 otherwise
* - fullscreen ( optional ) : 1 if the library supports fullscreen , 0 otherwise
* - embedTypes ( optional ) : list of supported embed types
* - preloadedJs ( optional ) : list of associative arrays containing :
* - path : path to a js file relative to the library root folder
* - preloadedCss ( optional ) : list of associative arrays containing :
* - path : path to css file relative to the library root folder
* - dropLibraryCss ( optional ) : list of associative arrays containing :
* - machineName : machine name for the librarys that are to drop their css
* - semantics ( optional ) : Json describing the content structure for the library
* - language ( optional ) : associative array containing :
2014-10-10 13:56:48 +02:00
* - languageCode : Translation in json format
2016-04-13 06:01:15 +02:00
* @ param bool $new
* @ return
2013-01-14 23:47:50 +01:00
*/
2013-09-10 22:43:31 +02:00
public function saveLibraryData ( & $libraryData , $new = TRUE );
2014-10-10 13:56:48 +02:00
2013-01-14 23:47:50 +01:00
/**
2014-05-27 10:55:46 +02:00
* Insert new content .
2014-10-10 13:56:48 +02:00
*
2014-10-08 10:16:16 +02:00
* @ param array $content
* An associative array containing :
* - id : The content id
* - params : The content in json format
* - library : An associative array containing :
* - libraryId : The id of the main library for this content
2013-01-14 23:47:50 +01:00
* @ param int $contentMainId
2016-04-13 06:01:15 +02:00
* Main id for the content if this is a system that supports versions
2013-01-14 23:47:50 +01:00
*/
2014-05-27 10:55:46 +02:00
public function insertContent ( $content , $contentMainId = NULL );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
* Update old content .
2014-10-10 13:56:48 +02:00
*
2014-10-08 10:16:16 +02:00
* @ param array $content
* An associative array containing :
* - id : The content id
* - params : The content in json format
* - library : An associative array containing :
* - libraryId : The id of the main library for this content
2014-05-27 10:55:46 +02:00
* @ param int $contentMainId
2016-04-13 06:01:15 +02:00
* Main id for the content if this is a system that supports versions
2014-05-27 10:55:46 +02:00
*/
public function updateContent ( $content , $contentMainId = NULL );
2014-10-10 13:56:48 +02:00
2015-04-07 19:32:44 +02:00
/**
* Resets marked user data for the given content .
*
* @ param int $contentId
*/
public function resetContentUserData ( $contentId );
2013-03-29 16:35:54 +01:00
/**
2016-04-13 06:01:15 +02:00
* Save what libraries a library is depending on
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:35:54 +01:00
* @ param int $libraryId
2014-10-08 10:16:16 +02:00
* Library Id for the library we ' re saving dependencies for
2013-03-29 16:35:54 +01:00
* @ param array $dependencies
2014-10-08 10:16:16 +02:00
* List of dependencies as associative arrays containing :
* - machineName : The library machineName
* - majorVersion : The library ' s majorVersion
* - minorVersion : The library ' s minorVersion
2013-03-29 16:35:54 +01:00
* @ param string $dependency_type
2014-10-08 10:16:16 +02:00
* What type of dependency this is , the following values are allowed :
* - editor
* - preloaded
* - dynamic
2013-03-29 16:35:54 +01:00
*/
public function saveLibraryDependencies ( $libraryId , $dependencies , $dependency_type );
2013-02-15 11:38:49 +01:00
/**
2014-10-08 10:16:16 +02:00
* Give an H5P the same library dependencies as a given H5P
2013-02-15 11:38:49 +01:00
*
* @ param int $contentId
2014-10-08 10:16:16 +02:00
* Id identifying the content
2013-02-15 11:38:49 +01:00
* @ param int $copyFromId
2014-10-08 10:16:16 +02:00
* Id identifying the content to be copied
2013-02-15 11:38:49 +01:00
* @ param int $contentMainId
2014-10-08 10:16:16 +02:00
* Main id for the content , typically used in frameworks
2016-04-13 06:01:15 +02:00
* That supports versions . ( In this case the content id will typically be
2014-10-08 10:16:16 +02:00
* the version id , and the contentMainId will be the frameworks content id
2013-02-15 11:38:49 +01:00
*/
2013-03-07 05:31:30 +01:00
public function copyLibraryUsage ( $contentId , $copyFromId , $contentMainId = NULL );
2013-02-15 11:38:49 +01:00
/**
* Deletes content data
*
* @ param int $contentId
2014-10-08 10:16:16 +02:00
* Id identifying the content
2013-02-15 11:38:49 +01:00
*/
2012-12-22 07:09:17 +01:00
public function deleteContentData ( $contentId );
2013-02-15 11:38:49 +01:00
2013-03-29 16:35:54 +01:00
/**
* Delete what libraries a content item is using
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:35:54 +01:00
* @ param int $contentId
2014-10-08 10:16:16 +02:00
* Content Id of the content we ' ll be deleting library usage for
2013-03-29 16:35:54 +01:00
*/
public function deleteLibraryUsage ( $contentId );
2013-07-09 10:14:42 +02:00
2013-02-15 11:38:49 +01:00
/**
* Saves what libraries the content uses
*
* @ param int $contentId
2014-10-08 10:16:16 +02:00
* Id identifying the content
2013-02-15 11:38:49 +01:00
* @ param array $librariesInUse
2014-10-08 10:16:16 +02:00
* List of libraries the content uses . Libraries consist of associative arrays with :
* - library : Associative array containing :
2016-04-13 06:01:15 +02:00
* - dropLibraryCss ( optional ) : comma separated list of machineNames
2014-10-08 10:16:16 +02:00
* - machineName : Machine name for the library
* - libraryId : Id of the library
2014-10-09 19:29:20 +02:00
* - type : The dependency type . Allowed values :
* - editor
* - dynamic
* - preloaded
2013-02-15 11:38:49 +01:00
*/
2012-11-30 15:51:39 +01:00
public function saveLibraryUsage ( $contentId , $librariesInUse );
2013-02-15 11:38:49 +01:00
2014-03-26 08:43:29 +01:00
/**
2014-10-10 13:56:48 +02:00
* Get number of content / nodes using a library , and the number of
2014-03-26 08:43:29 +01:00
* dependencies to other libraries
2014-10-10 13:56:48 +02:00
*
2014-10-10 13:57:43 +02:00
* @ param int $libraryId
2014-10-09 19:29:20 +02:00
* Library identifier
* @ return array
* Associative array containing :
* - content : Number of content using the library
* - libraries : Number of libraries depending on the library
2014-03-26 08:43:29 +01:00
*/
public function getLibraryUsage ( $libraryId );
2013-02-15 11:38:49 +01:00
/**
* Loads a library
*
2013-03-09 01:20:55 +01:00
* @ param string $machineName
2014-10-09 19:29:20 +02:00
* The library ' s machine name
2013-02-15 11:38:49 +01:00
* @ param int $majorVersion
2014-10-09 19:29:20 +02:00
* The library ' s major version
2013-02-15 11:38:49 +01:00
* @ param int $minorVersion
2014-10-09 19:29:20 +02:00
* The library ' s minor version
2013-02-15 11:38:49 +01:00
* @ return array | FALSE
2016-04-13 06:01:15 +02:00
* FALSE if the library does not exist .
2014-10-09 19:29:20 +02:00
* Otherwise an associative array containing :
* - libraryId : The id of the library if it is an existing library .
* - title : The library ' s name
* - machineName : The library machineName
* - majorVersion : The library ' s majorVersion
* - minorVersion : The library ' s minorVersion
* - patchVersion : The library ' s patchVersion
* - runnable : 1 if the library is a content type , 0 otherwise
* - fullscreen ( optional ) : 1 if the library supports fullscreen , 0 otherwise
* - embedTypes ( optional ) : list of supported embed types
* - preloadedJs ( optional ) : comma separated string with js file paths
* - preloadedCss ( optional ) : comma separated sting with css file paths
* - dropLibraryCss ( optional ) : list of associative arrays containing :
* - machineName : machine name for the librarys that are to drop their css
* - semantics ( optional ) : Json describing the content structure for the library
* - preloadedDependencies ( optional ) : list of associative arrays containing :
* - machineName : Machine name for a library this library is depending on
* - majorVersion : Major version for a library this library is depending on
* - minorVersion : Minor for a library this library is depending on
* - dynamicDependencies ( optional ) : list of associative arrays containing :
* - machineName : Machine name for a library this library is depending on
* - majorVersion : Major version for a library this library is depending on
* - minorVersion : Minor for a library this library is depending on
* - editorDependencies ( optional ) : list of associative arrays containing :
* - machineName : Machine name for a library this library is depending on
* - majorVersion : Major version for a library this library is depending on
* - minorVersion : Minor for a library this library is depending on
2013-02-15 11:38:49 +01:00
*/
2013-03-09 01:20:55 +01:00
public function loadLibrary ( $machineName , $majorVersion , $minorVersion );
2013-04-13 14:11:25 +02:00
2013-07-09 15:13:09 +02:00
/**
2014-03-26 08:43:29 +01:00
* Loads library semantics .
2013-07-09 15:13:09 +02:00
*
2014-10-09 19:29:20 +02:00
* @ param string $machineName
* Machine name for the library
* @ param int $majorVersion
* The library ' s major version
* @ param int $minorVersion
* The library ' s minor version
* @ return string
* The library ' s semantics as json
2013-07-09 15:13:09 +02:00
*/
2014-10-09 19:29:20 +02:00
public function loadLibrarySemantics ( $machineName , $majorVersion , $minorVersion );
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Makes it possible to alter the semantics , adding custom fields , etc .
*
* @ param array $semantics
2014-10-09 19:29:20 +02:00
* Associative array representing the semantics
* @ param string $machineName
* The library ' s machine name
* @ param int $majorVersion
* The library ' s major version
2014-10-10 13:56:48 +02:00
* @ param int $minorVersion
2014-10-09 19:29:20 +02:00
* The library ' s minor version
2014-03-26 08:43:29 +01:00
*/
2014-10-09 19:29:20 +02:00
public function alterLibrarySemantics ( & $semantics , $machineName , $majorVersion , $minorVersion );
2013-07-09 15:13:09 +02:00
2013-04-13 14:11:25 +02:00
/**
* Delete all dependencies belonging to given library
*
* @ param int $libraryId
2014-10-09 19:29:20 +02:00
* Library identifier
2013-04-13 14:11:25 +02:00
*/
public function deleteLibraryDependencies ( $libraryId );
2014-10-10 13:56:48 +02:00
2014-11-27 23:14:15 +01:00
/**
* Start an atomic operation against the dependency storage
*/
public function lockDependencyStorage ();
2015-01-09 10:00:40 +01:00
2014-11-27 23:14:15 +01:00
/**
* Stops an atomic operation against the dependency storage
*/
public function unlockDependencyStorage ();
2015-01-09 10:00:40 +01:00
2014-03-26 08:43:29 +01:00
/**
* Delete a library from database and file system
2014-10-10 13:56:48 +02:00
*
2014-10-23 15:18:03 +02:00
* @ param stdClass $library
* Library object with id , name , major version and minor version .
2014-03-26 08:43:29 +01:00
*/
2014-08-22 11:33:47 +02:00
public function deleteLibrary ( $library );
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Load content .
*
2014-10-09 19:29:20 +02:00
* @ param int $id
* Content identifier
* @ return array
* Associative array containing :
* - contentId : Identifier for the content
* - params : json content as string
* - embedType : csv of embed types
* - title : The contents title
* - language : Language code for the content
* - libraryId : Id for the main library
* - libraryName : The library machine name
* - libraryMajorVersion : The library ' s majorVersion
* - libraryMinorVersion : The library ' s minorVersion
* - libraryEmbedTypes : CSV of the main library ' s embed types
* - libraryFullscreen : 1 if fullscreen is supported . 0 otherwise .
2014-03-26 08:43:29 +01:00
*/
public function loadContent ( $id );
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Load dependencies for the given content of the given type .
*
2014-10-09 19:29:20 +02:00
* @ param int $id
* Content identifier
* @ param int $type
* Dependency types . Allowed values :
* - editor
* - preloaded
* - dynamic
2014-03-26 08:43:29 +01:00
* @ return array
2014-10-09 19:29:20 +02:00
* List of associative arrays containing :
* - libraryId : The id of the library if it is an existing library .
* - machineName : The library machineName
* - majorVersion : The library ' s majorVersion
* - minorVersion : The library ' s minorVersion
* - patchVersion : The library ' s patchVersion
* - preloadedJs ( optional ) : comma separated string with js file paths
* - preloadedCss ( optional ) : comma separated sting with css file paths
* - dropCss ( optional ) : csv of machine names
2014-03-26 08:43:29 +01:00
*/
public function loadContentDependencies ( $id , $type = NULL );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2014-09-26 15:56:42 +02:00
* Get stored setting .
2014-10-10 13:56:48 +02:00
*
2014-10-23 15:18:03 +02:00
* @ param string $name
* Identifier for the setting
* @ param string $default
* Optional default value if settings is not set
2014-10-09 19:29:20 +02:00
* @ return mixed
2014-10-23 15:18:03 +02:00
* Whatever has been stored as the setting
2014-05-27 10:55:46 +02:00
*/
2014-09-26 15:56:42 +02:00
public function getOption ( $name , $default = NULL );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2014-09-26 15:56:42 +02:00
* Stores the given setting .
* For example when did we last check h5p . org for updates to our libraries .
2014-10-10 13:56:48 +02:00
*
2014-10-23 15:18:03 +02:00
* @ param string $name
* Identifier for the setting
2014-09-26 15:56:42 +02:00
* @ param mixed $value Data
2014-10-23 15:18:03 +02:00
* Whatever we want to store as the setting
2014-05-27 10:55:46 +02:00
*/
2014-09-26 15:56:42 +02:00
public function setOption ( $name , $value );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2015-03-13 15:17:19 +01:00
* This will update selected fields on the given content .
2014-10-10 13:56:48 +02:00
*
2015-03-13 15:17:19 +01:00
* @ param int $id Content identifier
* @ param array $fields Content fields , e . g . filtered or slug .
2014-05-27 10:55:46 +02:00
*/
2015-03-13 15:17:19 +01:00
public function updateContentFields ( $id , $fields );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2014-09-26 15:56:42 +02:00
* Will clear filtered params for all the content that uses the specified
* library . This means that the content dependencies will have to be rebuilt ,
2016-04-13 06:01:15 +02:00
* and the parameters re - filtered .
2014-10-09 19:29:20 +02:00
*
2014-05-27 10:55:46 +02:00
* @ param int $library_id
*/
2014-09-26 15:56:42 +02:00
public function clearFilteredParameters ( $library_id );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2014-09-26 15:56:42 +02:00
* Get number of contents that has to get their content dependencies rebuilt
2016-04-13 06:01:15 +02:00
* and parameters re - filtered .
2014-09-26 15:56:42 +02:00
*
* @ return int
2014-05-27 10:55:46 +02:00
*/
2014-09-26 15:56:42 +02:00
public function getNumNotFiltered ();
2014-10-10 13:56:48 +02:00
2014-06-03 16:07:50 +02:00
/**
* Get number of contents using library as main library .
2014-10-10 13:56:48 +02:00
*
2014-10-10 13:57:43 +02:00
* @ param int $libraryId
2014-09-26 15:56:42 +02:00
* @ return int
2014-06-03 16:07:50 +02:00
*/
2014-10-10 13:57:43 +02:00
public function getNumContent ( $libraryId );
2015-03-12 14:38:10 +01:00
/**
2015-03-13 15:17:19 +01:00
* Determines if content slug is used .
2015-03-12 14:38:10 +01:00
*
2015-03-13 15:17:19 +01:00
* @ param string $slug
* @ return boolean
2015-03-12 14:38:10 +01:00
*/
2015-03-13 15:17:19 +01:00
public function isContentSlugAvailable ( $slug );
2016-02-04 16:34:32 +01:00
/**
* Generates statistics from the event log per library
*
* @ param string $type Type of event to generate stats for
* @ return array Number values indexed by library name and version
*/
2016-02-11 09:53:16 +01:00
public function getLibraryStats ( $type );
2016-02-09 10:55:44 +01:00
/**
* Aggregate the current number of H5P authors
* @ return int
*/
public function getNumAuthors ();
2016-03-21 11:51:45 +01:00
2016-01-13 14:23:34 +01:00
/**
* Stores hash keys for cached assets , aggregated JavaScripts and
* stylesheets , and connects it to libraries so that we know which cache file
* to delete when a library is updated .
*
* @ param string $key
* Hash key for the given libraries
* @ param array $libraries
* List of dependencies ( libraries ) used to create the key
*/
public function saveCachedAssets ( $key , $libraries );
/**
* Locate hash keys for given library and delete them .
* Used when cache file are deleted .
*
* @ param int $library_id
* Library identifier
* @ return array
* List of hash keys removed
*/
public function deleteCachedAssets ( $library_id );
2016-04-13 06:01:15 +02:00
/**
* Get the amount of content items associated to a library
* return int
*/
public function getLibraryContentCount ();
2016-09-30 13:41:50 +02:00
/**
* Will trigger after the export file is created .
*/
2017-04-25 16:19:53 +02:00
public function afterExportCreated ( $content , $filename );
2016-12-13 10:29:33 +01:00
2016-12-13 10:33:59 +01:00
/**
* Check if user has permissions to an action
*
* @ method hasPermission
* @ param [ H5PPermission ] $permission Permission type , ref H5PPermission
* @ param [ int ] $id Id need by platform to determine permission
* @ return boolean
*/
public function hasPermission ( $permission , $id = NULL );
2017-02-22 14:50:45 +01:00
/**
* Replaces existing content type cache with the one passed in
*
* @ param object $contentTypeCache Json with an array called 'libraries'
* containing the new content type cache that should replace the old one .
*/
public function replaceContentTypeCache ( $contentTypeCache );
2012-11-21 14:14:07 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* This class is used for validating H5P files
*/
2013-02-17 15:04:30 +01:00
class H5PValidator {
2012-11-22 14:00:06 +01:00
public $h5pF ;
2012-11-29 22:52:41 +01:00
public $h5pC ;
2013-01-16 19:39:49 +01:00
2012-11-24 01:09:25 +01:00
// Schemas used to validate the h5p files
private $h5pRequired = array (
2012-11-28 14:06:55 +01:00
'title' => '/^.{1,255}$/' ,
2017-01-30 14:49:09 +01:00
'language' => '/^[-a-zA-Z]{1,10}$/' ,
2012-11-24 01:09:25 +01:00
'preloadedDependencies' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2012-12-04 22:26:20 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
'minorVersion' => '/^[0-9]{1,5}$/' ,
2012-11-24 01:09:25 +01:00
),
2013-03-09 01:20:55 +01:00
'mainLibrary' => '/^[$a-z_][0-9a-z_\.$]{1,254}$/i' ,
2012-11-24 01:09:25 +01:00
'embedTypes' => array ( 'iframe' , 'div' ),
);
2013-01-16 19:39:49 +01:00
2012-11-24 01:09:25 +01:00
private $h5pOptional = array (
2012-11-28 14:06:55 +01:00
'contentType' => '/^.{1,255}$/' ,
'author' => '/^.{1,255}$/' ,
2013-11-08 18:16:41 +01:00
'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/' ,
2012-11-24 01:09:25 +01:00
'dynamicDependencies' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2012-12-04 22:26:20 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
'minorVersion' => '/^[0-9]{1,5}$/' ,
2012-11-24 01:09:25 +01:00
),
2013-03-09 01:20:55 +01:00
'w' => '/^[0-9]{1,4}$/' ,
'h' => '/^[0-9]{1,4}$/' ,
2012-12-02 21:55:51 +01:00
'metaKeywords' => '/^.{1,}$/' ,
2013-12-18 11:32:18 +01:00
'metaDescription' => '/^.{1,}$/' ,
2012-12-02 21:55:51 +01:00
);
2013-01-14 23:47:50 +01:00
// Schemas used to validate the library files
2012-12-02 21:55:51 +01:00
private $libraryRequired = array (
'title' => '/^.{1,255}$/' ,
2012-12-04 20:56:16 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
2012-12-04 22:26:20 +01:00
'minorVersion' => '/^[0-9]{1,5}$/' ,
'patchVersion' => '/^[0-9]{1,5}$/' ,
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2013-01-20 16:13:11 +01:00
'runnable' => '/^(0|1)$/' ,
2012-12-02 21:55:51 +01:00
);
2013-01-16 19:39:49 +01:00
2012-12-02 21:55:51 +01:00
private $libraryOptional = array (
'author' => '/^.{1,255}$/' ,
2013-11-08 18:16:41 +01:00
'license' => '/^(cc-by|cc-by-sa|cc-by-nd|cc-by-nc|cc-by-nc-sa|cc-by-nc-nd|pd|cr|MIT|GPL1|GPL2|GPL3|MPL|MPL2)$/' ,
2012-12-02 21:55:51 +01:00
'description' => '/^.{1,}$/' ,
'dynamicDependencies' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2012-12-04 22:26:20 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
'minorVersion' => '/^[0-9]{1,5}$/' ,
2013-01-16 19:39:49 +01:00
),
2012-12-02 21:55:51 +01:00
'preloadedDependencies' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2012-12-04 22:26:20 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
2012-12-22 06:35:16 +01:00
'minorVersion' => '/^[0-9]{1,5}$/' ,
2012-12-02 21:55:51 +01:00
),
2013-02-06 17:39:45 +01:00
'editorDependencies' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2013-02-06 17:39:45 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
'minorVersion' => '/^[0-9]{1,5}$/' ,
),
2012-11-30 15:51:39 +01:00
'preloadedJs' => array (
2013-04-14 15:31:29 +02:00
'path' => '/^((\\\|\/)?[a-z_\-\s0-9\.]+)+\.js$/i' ,
2012-11-30 15:51:39 +01:00
),
'preloadedCss' => array (
2013-04-14 15:31:29 +02:00
'path' => '/^((\\\|\/)?[a-z_\-\s0-9\.]+)+\.css$/i' ,
2012-11-30 15:51:39 +01:00
),
2013-02-28 20:06:18 +01:00
'dropLibraryCss' => array (
2013-03-09 01:20:55 +01:00
'machineName' => '/^[\w0-9\-\.]{1,255}$/i' ,
2013-02-28 20:06:18 +01:00
),
2013-03-09 01:20:55 +01:00
'w' => '/^[0-9]{1,4}$/' ,
'h' => '/^[0-9]{1,4}$/' ,
2012-12-02 21:55:51 +01:00
'embedTypes' => array ( 'iframe' , 'div' ),
2013-04-11 14:59:33 +02:00
'fullscreen' => '/^(0|1)$/' ,
2013-12-05 12:15:14 +01:00
'coreApi' => array (
2013-11-27 10:09:57 +01:00
'majorVersion' => '/^[0-9]{1,5}$/' ,
'minorVersion' => '/^[0-9]{1,5}$/' ,
),
2012-11-24 01:09:25 +01:00
);
2012-11-22 14:00:06 +01:00
2012-11-29 15:41:06 +01:00
/**
2013-02-17 15:04:30 +01:00
* Constructor for the H5PValidator
2012-11-29 15:41:06 +01:00
*
2016-04-13 06:01:15 +02:00
* @ param H5PFrameworkInterface $H5PFramework
2013-02-17 15:39:26 +01:00
* The frameworks implementation of the H5PFrameworkInterface
2016-04-13 06:01:15 +02:00
* @ param H5PCore $H5PCore
2012-11-29 15:41:06 +01:00
*/
2013-02-17 15:04:30 +01:00
public function __construct ( $H5PFramework , $H5PCore ) {
$this -> h5pF = $H5PFramework ;
$this -> h5pC = $H5PCore ;
2013-07-13 22:25:18 +02:00
$this -> h5pCV = new H5PContentValidator ( $this -> h5pF , $this -> h5pC );
2012-11-22 14:00:06 +01:00
}
2012-11-29 15:41:06 +01:00
/**
* Validates a . h5p file
*
2016-04-13 06:01:15 +02:00
* @ param bool $skipContent
* @ param bool $upgradeOnly
* @ return bool TRUE if the . h5p file is valid
* TRUE if the . h5p file is valid
2012-11-29 15:41:06 +01:00
*/
2014-08-26 12:00:14 +02:00
public function isValidPackage ( $skipContent = FALSE , $upgradeOnly = FALSE ) {
2016-01-11 18:31:04 +01:00
// Check dependencies, make sure Zip is present
2015-06-15 14:57:51 +02:00
if ( ! class_exists ( 'ZipArchive' )) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Your PHP version does not support ZipArchive.' ));
return FALSE ;
}
2012-11-22 14:00:06 +01:00
// Create a temporary dir to extract package in.
2013-05-01 23:24:58 +02:00
$tmpDir = $this -> h5pF -> getUploadedH5pFolderPath ();
2014-03-26 08:43:29 +01:00
$tmpPath = $this -> h5pF -> getUploadedH5pPath ();
2012-11-22 14:00:06 +01:00
// Extract and then remove the package file.
2012-12-03 00:19:25 +01:00
$zip = new ZipArchive ;
2014-10-10 13:56:48 +02:00
2014-04-13 15:52:12 +02:00
// Only allow files with the .h5p extension:
if ( strtolower ( substr ( $tmpPath , - 3 )) !== 'h5p' ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The file you uploaded is not a valid HTML5 Package (It does not have the .h5p file extension)' ));
H5PCore :: deleteFileTree ( $tmpDir );
2016-04-13 06:01:15 +02:00
return FALSE ;
2014-04-13 15:52:12 +02:00
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $zip -> open ( $tmpPath ) === true ) {
2013-05-01 23:24:58 +02:00
$zip -> extractTo ( $tmpDir );
2012-12-03 00:19:25 +01:00
$zip -> close ();
}
else {
2014-04-13 15:52:12 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The file you uploaded is not a valid HTML5 Package (We are unable to unzip it)' ));
2014-03-26 08:43:29 +01:00
H5PCore :: deleteFileTree ( $tmpDir );
2016-04-13 06:01:15 +02:00
return FALSE ;
2012-11-22 14:00:06 +01:00
}
2014-03-26 08:43:29 +01:00
unlink ( $tmpPath );
2012-11-22 14:00:06 +01:00
// Process content and libraries
2016-01-11 18:31:04 +01:00
$valid = TRUE ;
2012-11-28 15:31:55 +01:00
$libraries = array ();
2013-05-01 23:24:58 +02:00
$files = scandir ( $tmpDir );
2016-04-13 06:01:15 +02:00
$mainH5pData = null ;
$libraryJsonData = null ;
$contentJsonData = null ;
2017-03-02 11:43:45 +01:00
$mainH5pExists = $contentExists = FALSE ;
2012-11-23 17:06:03 +01:00
foreach ( $files as $file ) {
2013-01-16 22:00:12 +01:00
if ( in_array ( substr ( $file , 0 , 1 ), array ( '.' , '_' ))) {
2012-11-23 17:06:03 +01:00
continue ;
}
2013-05-01 23:24:58 +02:00
$filePath = $tmpDir . DIRECTORY_SEPARATOR . $file ;
2013-01-16 19:39:49 +01:00
// Check for h5p.json file.
2012-11-23 17:06:03 +01:00
if ( strtolower ( $file ) == 'h5p.json' ) {
2014-03-26 08:43:29 +01:00
if ( $skipContent === TRUE ) {
continue ;
}
2014-10-10 13:56:48 +02:00
2013-05-01 17:22:09 +02:00
$mainH5pData = $this -> getJsonData ( $filePath );
2012-11-29 15:41:06 +01:00
if ( $mainH5pData === FALSE ) {
2012-11-29 08:51:36 +01:00
$valid = FALSE ;
2014-04-13 15:52:12 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Could not parse the main h5p.json file' ));
2012-11-29 08:51:36 +01:00
}
else {
2012-11-29 15:41:06 +01:00
$validH5p = $this -> isValidH5pData ( $mainH5pData , $file , $this -> h5pRequired , $this -> h5pOptional );
2012-11-29 08:51:36 +01:00
if ( $validH5p ) {
$mainH5pExists = TRUE ;
}
else {
$valid = FALSE ;
2014-04-13 15:52:12 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The main h5p.json file is not valid' ));
2012-11-29 08:51:36 +01:00
}
}
2012-11-23 17:06:03 +01:00
}
2013-01-16 19:39:49 +01:00
// Content directory holds content.
2012-11-23 17:06:03 +01:00
elseif ( $file == 'content' ) {
2014-03-26 08:43:29 +01:00
// We do a separate skipContent check to avoid having the content folder being treated as a library
if ( $skipContent ) {
continue ;
}
2013-05-01 17:22:09 +02:00
if ( ! is_dir ( $filePath )) {
2012-12-22 06:35:16 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid content folder' ));
$valid = FALSE ;
continue ;
}
2013-05-01 17:22:09 +02:00
$contentJsonData = $this -> getJsonData ( $filePath . DIRECTORY_SEPARATOR . 'content.json' );
2012-11-30 15:51:39 +01:00
if ( $contentJsonData === FALSE ) {
2012-11-29 22:52:41 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Could not find or parse the content.json file' ));
2012-11-28 15:21:34 +01:00
$valid = FALSE ;
continue ;
}
2012-11-29 08:51:36 +01:00
else {
2012-11-29 08:57:01 +01:00
$contentExists = TRUE ;
2013-06-30 22:14:16 +02:00
// In the future we might let the libraries provide validation functions for content.json
}
2013-07-15 16:25:10 +02:00
2013-07-13 22:25:18 +02:00
if ( ! $this -> h5pCV -> validateContentFiles ( $filePath )) {
2016-04-13 06:01:15 +02:00
// validateContentFiles adds potential errors to the queue
2013-06-30 22:14:16 +02:00
$valid = FALSE ;
continue ;
2012-11-28 15:21:34 +01:00
}
2012-11-23 17:06:03 +01:00
}
2012-11-22 14:00:06 +01:00
2013-01-16 19:39:49 +01:00
// The rest should be library folders
2017-02-28 13:05:11 +01:00
elseif ( $this -> h5pC -> mayUpdateLibraries ()) {
2013-05-01 17:22:09 +02:00
if ( ! is_dir ( $filePath )) {
2013-01-16 19:39:49 +01:00
// Ignore this. Probably a file that shouldn't have been included.
2012-12-22 06:35:16 +01:00
continue ;
}
2013-06-30 15:32:12 +02:00
2013-05-01 23:24:58 +02:00
$libraryH5PData = $this -> getLibraryData ( $file , $filePath , $tmpDir );
2014-10-10 13:56:48 +02:00
2014-04-14 10:50:37 +02:00
if ( $libraryH5PData !== FALSE ) {
2014-08-26 12:00:14 +02:00
// Library's directory name must be:
// - <machineName>
// - or -
// - <machineName>-<majorVersion>.<minorVersion>
2016-04-13 06:01:15 +02:00
// where machineName, majorVersion and minorVersion is read from library.json
2014-08-26 12:00:14 +02:00
if ( $libraryH5PData [ 'machineName' ] !== $file && H5PCore :: libraryToString ( $libraryH5PData , TRUE ) !== $file ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Library directory name must match machineName or machineName-majorVersion.minorVersion (from library.json). (Directory: %directoryName , machineName: %machineName, majorVersion: %majorVersion, minorVersion: %minorVersion)' , array (
'%directoryName' => $file ,
'%machineName' => $libraryH5PData [ 'machineName' ],
'%majorVersion' => $libraryH5PData [ 'majorVersion' ],
'%minorVersion' => $libraryH5PData [ 'minorVersion' ])));
$valid = FALSE ;
continue ;
}
$libraryH5PData [ 'uploadDirectory' ] = $filePath ;
$libraries [ H5PCore :: libraryToString ( $libraryH5PData )] = $libraryH5PData ;
2012-11-28 15:31:55 +01:00
}
2013-05-01 17:22:09 +02:00
else {
$valid = FALSE ;
}
2012-11-22 14:00:06 +01:00
}
}
2014-03-26 08:43:29 +01:00
if ( $skipContent === FALSE ) {
if ( ! $contentExists ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'A valid content folder is missing' ));
$valid = FALSE ;
}
if ( ! $mainH5pExists ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'A valid main h5p.json file is missing' ));
$valid = FALSE ;
}
2012-11-29 15:41:06 +01:00
}
2012-11-28 15:31:55 +01:00
if ( $valid ) {
2014-08-26 12:00:14 +02:00
if ( $upgradeOnly ) {
2016-01-20 13:21:51 +01:00
// When upgrading, we only add the already installed libraries, and
// the new dependent libraries
2014-08-26 12:00:14 +02:00
$upgrades = array ();
2015-02-12 15:56:31 +01:00
foreach ( $libraries as $libString => & $library ) {
2014-08-26 12:00:14 +02:00
// Is this library already installed?
2016-01-20 13:21:51 +01:00
if ( $this -> h5pF -> getLibraryId ( $library [ 'machineName' ]) !== FALSE ) {
2015-02-12 15:56:31 +01:00
$upgrades [ $libString ] = $library ;
2014-08-26 12:00:14 +02:00
}
}
while ( $missingLibraries = $this -> getMissingLibraries ( $upgrades )) {
2015-02-12 15:56:31 +01:00
foreach ( $missingLibraries as $libString => $missing ) {
2014-08-26 12:00:14 +02:00
$library = $libraries [ $libString ];
if ( $library ) {
$upgrades [ $libString ] = $library ;
}
}
}
2014-10-10 13:56:48 +02:00
2014-08-26 12:00:14 +02:00
$libraries = $upgrades ;
}
2014-10-10 13:56:48 +02:00
2012-11-30 15:51:39 +01:00
$this -> h5pC -> librariesJsonData = $libraries ;
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $skipContent === FALSE ) {
$this -> h5pC -> mainJsonData = $mainH5pData ;
$this -> h5pC -> contentJsonData = $contentJsonData ;
$libraries [ 'mainH5pData' ] = $mainH5pData ; // Check for the dependencies in h5p.json as well as in the libraries
}
2014-10-10 13:56:48 +02:00
2012-11-28 23:29:22 +01:00
$missingLibraries = $this -> getMissingLibraries ( $libraries );
2015-02-12 15:56:31 +01:00
foreach ( $missingLibraries as $libString => $missing ) {
if ( $this -> h5pC -> getLibraryId ( $missing , $libString )) {
unset ( $missingLibraries [ $libString ]);
2012-11-28 23:29:22 +01:00
}
}
2014-10-10 13:56:48 +02:00
2013-04-13 13:28:48 +02:00
if ( ! empty ( $missingLibraries )) {
2015-02-12 15:56:31 +01:00
foreach ( $missingLibraries as $libString => $library ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Missing required library @library' , array ( '@library' => $libString )));
2013-04-13 13:28:48 +02:00
}
2017-02-28 13:05:11 +01:00
if ( ! $this -> h5pC -> mayUpdateLibraries ()) {
2014-08-26 11:26:41 +02:00
$this -> h5pF -> setInfoMessage ( $this -> h5pF -> t ( " Note that the libraries may exist in the file you uploaded, but you're not allowed to upload new libraries. Contact the site administrator about this. " ));
2013-07-15 17:36:56 +02:00
}
2013-04-13 13:28:48 +02:00
}
2012-11-28 23:29:22 +01:00
$valid = empty ( $missingLibraries ) && $valid ;
}
if ( ! $valid ) {
2014-03-26 08:43:29 +01:00
H5PCore :: deleteFileTree ( $tmpDir );
2012-11-28 23:29:22 +01:00
}
return $valid ;
}
2012-11-29 15:41:06 +01:00
2013-05-01 17:22:09 +02:00
/**
* Validates a H5P library
*
* @ param string $file
* Name of the library folder
* @ param string $filePath
* Path to the library folder
2013-05-01 23:24:58 +02:00
* @ param string $tmpDir
* Path to the temporary upload directory
2016-04-13 06:01:15 +02:00
* @ return boolean | array
2013-05-01 17:22:09 +02:00
* H5P data from library . json and semantics if the library is valid
* FALSE if the library isn ' t valid
*/
2013-05-01 23:24:58 +02:00
public function getLibraryData ( $file , $filePath , $tmpDir ) {
2013-05-01 17:22:09 +02:00
if ( preg_match ( '/^[\w0-9\-\.]{1,255}$/i' , $file ) === 0 ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid library name: %name' , array ( '%name' => $file )));
2013-05-01 23:24:58 +02:00
return FALSE ;
2013-05-01 17:22:09 +02:00
}
$h5pData = $this -> getJsonData ( $filePath . DIRECTORY_SEPARATOR . 'library.json' );
if ( $h5pData === FALSE ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Could not find library.json file with valid json format for library %name' , array ( '%name' => $file )));
2013-05-01 23:24:58 +02:00
return FALSE ;
2013-05-01 17:22:09 +02:00
}
// validate json if a semantics file is provided
$semanticsPath = $filePath . DIRECTORY_SEPARATOR . 'semantics.json' ;
if ( file_exists ( $semanticsPath )) {
$semantics = $this -> getJsonData ( $semanticsPath , TRUE );
if ( $semantics === FALSE ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid semantics.json file has been included in the library %name' , array ( '%name' => $file )));
2013-05-01 23:24:58 +02:00
return FALSE ;
2013-05-01 17:22:09 +02:00
}
else {
$h5pData [ 'semantics' ] = $semantics ;
}
}
2013-05-02 17:09:48 +02:00
// validate language folder if it exists
$languagePath = $filePath . DIRECTORY_SEPARATOR . 'language' ;
if ( is_dir ( $languagePath )) {
$languageFiles = scandir ( $languagePath );
foreach ( $languageFiles as $languageFile ) {
2013-05-03 11:21:08 +02:00
if ( in_array ( $languageFile , array ( '.' , '..' ))) {
continue ;
}
if ( preg_match ( '/^(-?[a-z]+){1,7}\.json$/i' , $languageFile ) === 0 ) {
2013-05-02 17:09:48 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid language file %file in library %library' , array ( '%file' => $languageFile , '%library' => $file )));
return FALSE ;
}
2013-05-03 11:21:08 +02:00
$languageJson = $this -> getJsonData ( $languagePath . DIRECTORY_SEPARATOR . $languageFile , TRUE );
2013-05-02 17:09:48 +02:00
if ( $languageJson === FALSE ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid language file %languageFile has been included in the library %name' , array ( '%languageFile' => $languageFile , '%name' => $file )));
return FALSE ;
}
$parts = explode ( '.' , $languageFile ); // $parts[0] is the language code
$h5pData [ 'language' ][ $parts [ 0 ]] = $languageJson ;
}
}
2017-03-02 11:43:45 +01:00
// Check for icon:
$h5pData [ 'hasIcon' ] = file_exists ( $filePath . DIRECTORY_SEPARATOR . 'icon.svg' );
2013-05-01 17:22:09 +02:00
$validLibrary = $this -> isValidH5pData ( $h5pData , $file , $this -> libraryRequired , $this -> libraryOptional );
2013-07-13 22:25:18 +02:00
$validLibrary = $this -> h5pCV -> validateContentFiles ( $filePath , TRUE ) && $validLibrary ;
2013-07-08 18:22:38 +02:00
2013-05-01 17:22:09 +02:00
if ( isset ( $h5pData [ 'preloadedJs' ])) {
2013-05-01 23:24:58 +02:00
$validLibrary = $this -> isExistingFiles ( $h5pData [ 'preloadedJs' ], $tmpDir , $file ) && $validLibrary ;
2013-05-01 17:22:09 +02:00
}
if ( isset ( $h5pData [ 'preloadedCss' ])) {
2013-05-01 23:24:58 +02:00
$validLibrary = $this -> isExistingFiles ( $h5pData [ 'preloadedCss' ], $tmpDir , $file ) && $validLibrary ;
2013-05-01 17:22:09 +02:00
}
if ( $validLibrary ) {
return $h5pData ;
}
else {
return FALSE ;
}
}
2012-11-29 15:41:06 +01:00
/**
* Use the dependency declarations to find any missing libraries
*
* @ param array $libraries
2013-03-09 01:20:55 +01:00
* A multidimensional array of libraries keyed with machineName first and majorVersion second
2012-11-29 15:41:06 +01:00
* @ return array
2013-03-09 01:20:55 +01:00
* A list of libraries that are missing keyed with machineName and holds objects with
* machineName , majorVersion and minorVersion properties
2012-11-29 15:41:06 +01:00
*/
2012-11-28 23:29:22 +01:00
private function getMissingLibraries ( $libraries ) {
$missing = array ();
foreach ( $libraries as $library ) {
if ( isset ( $library [ 'preloadedDependencies' ])) {
2013-04-13 13:28:48 +02:00
$missing = array_merge ( $missing , $this -> getMissingDependencies ( $library [ 'preloadedDependencies' ], $libraries ));
2012-11-28 23:29:22 +01:00
}
if ( isset ( $library [ 'dynamicDependencies' ])) {
2013-04-13 13:28:48 +02:00
$missing = array_merge ( $missing , $this -> getMissingDependencies ( $library [ 'dynamicDependencies' ], $libraries ));
2012-11-28 23:29:22 +01:00
}
2013-02-08 04:38:27 +01:00
if ( isset ( $library [ 'editorDependencies' ])) {
2013-04-13 13:28:48 +02:00
$missing = array_merge ( $missing , $this -> getMissingDependencies ( $library [ 'editorDependencies' ], $libraries ));
2013-02-08 04:38:27 +01:00
}
2012-11-28 15:31:55 +01:00
}
2012-11-28 23:29:22 +01:00
return $missing ;
}
2012-11-29 15:41:06 +01:00
/**
* Helper function for getMissingLibraries , searches for dependency required libraries in
* the provided list of libraries
*
* @ param array $dependencies
2013-03-09 01:20:55 +01:00
* A list of objects with machineName , majorVersion and minorVersion properties
2012-11-29 15:41:06 +01:00
* @ param array $libraries
2013-03-09 01:20:55 +01:00
* An array of libraries keyed with machineName
2012-11-29 15:41:06 +01:00
* @ return
2013-03-09 01:20:55 +01:00
* A list of libraries that are missing keyed with machineName and holds objects with
* machineName , majorVersion and minorVersion properties
2012-11-29 15:41:06 +01:00
*/
2012-11-28 23:29:22 +01:00
private function getMissingDependencies ( $dependencies , $libraries ) {
$missing = array ();
2012-12-04 15:52:03 +01:00
foreach ( $dependencies as $dependency ) {
2015-02-12 15:56:31 +01:00
$libString = H5PCore :: libraryToString ( $dependency );
if ( ! isset ( $libraries [ $libString ])) {
$missing [ $libString ] = $dependency ;
2012-11-28 23:29:22 +01:00
}
}
return $missing ;
2012-11-26 22:48:51 +01:00
}
2012-11-29 15:41:06 +01:00
/**
* Figure out if the provided file paths exists
*
* Triggers error messages if files doesn ' t exist
*
* @ param array $files
2013-05-01 23:24:58 +02:00
* List of file paths relative to $tmpDir
* @ param string $tmpDir
2012-11-29 15:41:06 +01:00
* Path to the directory where the $files are stored .
* @ param string $library
* Name of the library we are processing
* @ return boolean
* TRUE if all the files excists
*/
2013-05-01 23:24:58 +02:00
private function isExistingFiles ( $files , $tmpDir , $library ) {
2012-12-04 15:52:03 +01:00
foreach ( $files as $file ) {
$path = str_replace ( array ( '/' , '\\' ), DIRECTORY_SEPARATOR , $file [ 'path' ]);
2013-05-01 23:24:58 +02:00
if ( ! file_exists ( $tmpDir . DIRECTORY_SEPARATOR . $library . DIRECTORY_SEPARATOR . $path )) {
2013-01-16 19:39:49 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The file "%file" is missing from library: "%name"' , array ( '%file' => $path , '%name' => $library )));
2012-11-26 22:48:51 +01:00
return FALSE ;
2012-11-22 14:00:06 +01:00
}
}
2012-11-26 22:48:51 +01:00
return TRUE ;
}
2012-11-29 15:41:06 +01:00
/**
2012-12-02 21:55:51 +01:00
* Validates h5p . json and library . json data
2012-11-29 15:41:06 +01:00
*
* Error messages are triggered if the data isn ' t valid
*
* @ param array $h5pData
* h5p data
* @ param string $library_name
* Name of the library we are processing
* @ param array $required
* Validation pattern for required properties
* @ param array $optional
* Validation pattern for optional properties
* @ return boolean
* TRUE if the $h5pData is valid
*/
2012-11-29 08:51:36 +01:00
private function isValidH5pData ( $h5pData , $library_name , $required , $optional ) {
$valid = $this -> isValidRequiredH5pData ( $h5pData , $required , $library_name );
$valid = $this -> isValidOptionalH5pData ( $h5pData , $optional , $library_name ) && $valid ;
2013-11-27 10:09:57 +01:00
2016-05-31 10:51:59 +02:00
// Check the library's required API version of Core.
// If no requirement is set this implicitly means 1.0.
2013-12-05 12:15:14 +01:00
if ( isset ( $h5pData [ 'coreApi' ]) && ! empty ( $h5pData [ 'coreApi' ])) {
if (( $h5pData [ 'coreApi' ][ 'majorVersion' ] > H5PCore :: $coreApi [ 'majorVersion' ]) ||
2016-05-31 10:51:59 +02:00
( ( $h5pData [ 'coreApi' ][ 'majorVersion' ] == H5PCore :: $coreApi [ 'majorVersion' ]) &&
( $h5pData [ 'coreApi' ][ 'minorVersion' ] > H5PCore :: $coreApi [ 'minorVersion' ]) )) {
2013-11-27 10:09:57 +01:00
$this -> h5pF -> setErrorMessage (
2016-05-31 10:51:59 +02:00
$this -> h5pF -> t ( 'The system was unable to install the <em>%component</em> component from the package, it requires a newer version of the H5P plugin. This site is currently running version %current, whereas the required version is %required or higher. You should consider upgrading and then try again.' ,
array (
'%component' => ( isset ( $h5pData [ 'title' ]) ? $h5pData [ 'title' ] : $library_name ),
'%current' => H5PCore :: $coreApi [ 'majorVersion' ] . '.' . H5PCore :: $coreApi [ 'minorVersion' ],
'%required' => $h5pData [ 'coreApi' ][ 'majorVersion' ] . '.' . $h5pData [ 'coreApi' ][ 'minorVersion' ]
)
)
);
2013-11-27 10:09:57 +01:00
$valid = false ;
}
}
2016-05-31 10:51:59 +02:00
2012-11-28 15:21:34 +01:00
return $valid ;
2012-11-28 13:56:42 +01:00
}
2012-11-29 15:41:06 +01:00
/**
* Helper function for isValidH5pData
*
* Validates the optional part of the h5pData
*
* Triggers error messages
*
* @ param array $h5pData
* h5p data
* @ param array $requirements
* Validation pattern
* @ param string $library_name
* Name of the library we are processing
* @ return boolean
* TRUE if the optional part of the $h5pData is valid
*/
2012-11-28 15:21:34 +01:00
private function isValidOptionalH5pData ( $h5pData , $requirements , $library_name ) {
$valid = TRUE ;
2012-11-28 13:56:42 +01:00
foreach ( $h5pData as $key => $value ) {
if ( isset ( $requirements [ $key ])) {
2012-12-04 15:52:03 +01:00
$valid = $this -> isValidRequirement ( $value , $requirements [ $key ], $library_name , $key ) && $valid ;
2012-11-28 13:56:42 +01:00
}
// Else: ignore, a package can have parameters that this library doesn't care about, but that library
// specific implementations does care about...
}
2012-11-28 15:21:34 +01:00
return $valid ;
2012-11-28 13:56:42 +01:00
}
2012-11-29 15:41:06 +01:00
/**
2013-11-27 10:09:57 +01:00
* Validate a requirement given as regexp or an array of requirements
2012-11-29 15:41:06 +01:00
*
2013-03-29 16:10:29 +01:00
* @ param mixed $h5pData
* The data to be validated
* @ param mixed $requirement
* The requirement the data is to be validated against , regexp or array of requirements
* @ param string $library_name
* Name of the library we are validating ( used in error messages )
* @ param string $property_name
* Name of the property we are validating ( used in error messages )
* @ return boolean
* TRUE if valid , FALSE if invalid
2012-11-29 15:41:06 +01:00
*/
2012-11-28 15:21:34 +01:00
private function isValidRequirement ( $h5pData , $requirement , $library_name , $property_name ) {
$valid = TRUE ;
2012-11-28 13:56:42 +01:00
if ( is_string ( $requirement )) {
2013-02-23 23:29:41 +01:00
if ( $requirement == 'boolean' ) {
if ( ! is_bool ( $h5pData )) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( " Invalid data provided for %property in %library. Boolean expected. " , array ( '%property' => $property_name , '%library' => $library_name )));
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
}
else {
2013-02-23 23:29:41 +01:00
// The requirement is a regexp, match it against the data
if ( is_string ( $h5pData ) || is_int ( $h5pData )) {
if ( preg_match ( $requirement , $h5pData ) === 0 ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( " Invalid data provided for %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
$valid = FALSE ;
}
}
else {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( " Invalid data provided for %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
$valid = FALSE ;
}
2012-11-28 13:56:42 +01:00
}
}
elseif ( is_array ( $requirement )) {
// We have sub requirements
if ( is_array ( $h5pData )) {
2012-12-04 15:52:03 +01:00
if ( is_array ( current ( $h5pData ))) {
foreach ( $h5pData as $sub_h5pData ) {
$valid = $this -> isValidRequiredH5pData ( $sub_h5pData , $requirement , $library_name ) && $valid ;
}
}
else {
$valid = $this -> isValidRequiredH5pData ( $h5pData , $requirement , $library_name ) && $valid ;
}
2012-11-28 13:56:42 +01:00
}
else {
2012-12-04 09:19:42 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( " Invalid data provided for %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
2012-11-28 15:21:34 +01:00
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
}
else {
2012-11-29 22:52:41 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( " Can't read the property %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
2012-11-28 15:21:34 +01:00
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
2012-11-28 15:21:34 +01:00
return $valid ;
2012-11-28 13:56:42 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* Validates the required h5p data in libraray . json and h5p . json
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:10:29 +01:00
* @ param mixed $h5pData
* Data to be validated
* @ param array $requirements
* Array with regexp to validate the data against
* @ param string $library_name
* Name of the library we are validating ( used in error messages )
* @ return boolean
* TRUE if all the required data exists and is valid , FALSE otherwise
*/
2012-11-28 15:21:34 +01:00
private function isValidRequiredH5pData ( $h5pData , $requirements , $library_name ) {
$valid = TRUE ;
2012-11-28 13:56:42 +01:00
foreach ( $requirements as $required => $requirement ) {
2012-11-28 14:45:54 +01:00
if ( is_int ( $required )) {
// We have an array of allowed options
2012-12-03 00:19:25 +01:00
return $this -> isValidH5pDataOptions ( $h5pData , $requirements , $library_name );
2012-11-28 14:45:54 +01:00
}
2012-11-28 13:56:42 +01:00
if ( isset ( $h5pData [ $required ])) {
2012-12-03 00:19:25 +01:00
$valid = $this -> isValidRequirement ( $h5pData [ $required ], $requirement , $library_name , $required ) && $valid ;
2012-11-28 13:56:42 +01:00
}
else {
2012-11-29 22:52:41 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The required property %property is missing from %library' , array ( '%property' => $required , '%library' => $library_name )));
2012-11-28 15:21:34 +01:00
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
}
2012-11-28 15:21:34 +01:00
return $valid ;
2012-11-22 14:00:06 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* Validates h5p data against a set of allowed values ( options )
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:10:29 +01:00
* @ param array $selected
* The option ( s ) that has been specified
* @ param array $allowed
* The allowed options
* @ param string $library_name
* Name of the library we are validating ( used in error messages )
* @ return boolean
* TRUE if the specified data is valid , FALSE otherwise
*/
2012-11-28 15:21:34 +01:00
private function isValidH5pDataOptions ( $selected , $allowed , $library_name ) {
$valid = TRUE ;
2012-11-28 14:45:54 +01:00
foreach ( $selected as $value ) {
if ( ! in_array ( $value , $allowed )) {
2012-11-29 22:52:41 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Illegal option %option in %library' , array ( '%option' => $value , '%library' => $library_name )));
2012-11-28 15:21:34 +01:00
$valid = FALSE ;
2012-11-28 14:45:54 +01:00
}
}
2012-11-28 15:21:34 +01:00
return $valid ;
2012-11-28 14:45:54 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* Fetch json data from file
2013-07-09 10:14:42 +02:00
*
2013-05-01 17:22:09 +02:00
* @ param string $filePath
2013-03-29 16:10:29 +01:00
* Path to the file holding the json string
* @ param boolean $return_as_string
* If true the json data will be decoded in order to validate it , but will be
* returned as string
* @ return mixed
* FALSE if the file can 't be read or the contents can' t be decoded
* string if the $return as string parameter is set
* array otherwise
*/
2013-05-01 17:22:09 +02:00
private function getJsonData ( $filePath , $return_as_string = FALSE ) {
$json = file_get_contents ( $filePath );
2013-10-21 13:22:13 +02:00
if ( $json === FALSE ) {
return FALSE ; // Cannot read from file.
2012-11-29 08:51:36 +01:00
}
2012-12-03 00:19:25 +01:00
$jsonData = json_decode ( $json , TRUE );
2013-10-21 13:22:13 +02:00
if ( $jsonData === NULL ) {
return FALSE ; // JSON cannot be decoded or the recursion limit has been reached.
2012-11-29 08:51:36 +01:00
}
2013-01-27 23:08:48 +01:00
return $return_as_string ? $json : $jsonData ;
2012-11-29 08:51:36 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* Helper function that copies an array
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:10:29 +01:00
* @ param array $array
* The array to be copied
* @ return array
* Copy of $array . All objects are cloned
*/
2012-11-29 08:51:36 +01:00
private function arrayCopy ( array $array ) {
$result = array ();
foreach ( $array as $key => $val ) {
if ( is_array ( $val )) {
2016-04-13 06:01:15 +02:00
$result [ $key ] = self :: arrayCopy ( $val );
2012-11-29 08:51:36 +01:00
}
elseif ( is_object ( $val )) {
$result [ $key ] = clone $val ;
}
else {
$result [ $key ] = $val ;
}
}
return $result ;
}
2012-11-21 14:14:07 +01:00
}
2012-11-29 22:52:41 +01:00
2013-03-29 16:10:29 +01:00
/**
* This class is used for saving H5P files
*/
2013-02-17 15:04:30 +01:00
class H5PStorage {
2013-07-09 10:14:42 +02:00
2012-11-29 22:52:41 +01:00
public $h5pF ;
public $h5pC ;
2014-10-10 13:56:48 +02:00
2014-04-28 15:09:07 +02:00
public $contentId = NULL ; // Quick fix so WP can get ID of new content.
2012-11-29 22:52:41 +01:00
/**
2013-02-17 15:04:30 +01:00
* Constructor for the H5PStorage
2012-11-29 22:52:41 +01:00
*
2016-04-13 06:01:15 +02:00
* @ param H5PFrameworkInterface | object $H5PFramework
2013-02-17 15:39:26 +01:00
* The frameworks implementation of the H5PFrameworkInterface
2016-04-13 06:01:15 +02:00
* @ param H5PCore $H5PCore
2012-11-29 22:52:41 +01:00
*/
2016-04-13 06:01:15 +02:00
public function __construct ( H5PFrameworkInterface $H5PFramework , H5PCore $H5PCore ) {
2013-02-17 15:04:30 +01:00
$this -> h5pF = $H5PFramework ;
$this -> h5pC = $H5PCore ;
2012-11-29 22:52:41 +01:00
}
2013-07-09 10:14:42 +02:00
2013-03-29 16:10:29 +01:00
/**
* Saves a H5P file
2013-07-09 10:14:42 +02:00
*
2016-04-13 06:01:15 +02:00
* @ param null $content
2013-03-29 16:10:29 +01:00
* @ param int $contentMainId
* The main id for the content we are saving . This is used if the framework
* we 're integrating with uses content id' s and version id ' s
2016-04-13 06:01:15 +02:00
* @ param bool $skipContent
* @ param array $options
* @ return bool TRUE if one or more libraries were updated
* TRUE if one or more libraries were updated
* FALSE otherwise
2013-03-29 16:10:29 +01:00
*/
2015-04-15 13:54:46 +02:00
public function savePackage ( $content = NULL , $contentMainId = NULL , $skipContent = FALSE , $options = array ()) {
2017-02-28 13:05:11 +01:00
if ( $this -> h5pC -> mayUpdateLibraries ()) {
2015-02-12 15:56:31 +01:00
// Save the libraries we processed during validation
$this -> saveLibraries ();
2013-02-24 00:12:57 +01:00
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( ! $skipContent ) {
2015-02-12 15:56:31 +01:00
$basePath = $this -> h5pF -> getUploadedH5pFolderPath ();
$current_path = $basePath . DIRECTORY_SEPARATOR . 'content' ;
2015-02-10 16:29:16 +01:00
2014-05-06 16:52:43 +02:00
// Save content
if ( $content === NULL ) {
$content = array ();
}
if ( ! is_array ( $content )) {
$content = array ( 'id' => $content );
}
2015-02-12 15:56:31 +01:00
// Find main library version
foreach ( $this -> h5pC -> mainJsonData [ 'preloadedDependencies' ] as $dep ) {
if ( $dep [ 'machineName' ] === $this -> h5pC -> mainJsonData [ 'mainLibrary' ]) {
$dep [ 'libraryId' ] = $this -> h5pC -> getLibraryId ( $dep );
$content [ 'library' ] = $dep ;
break ;
}
}
2014-05-06 16:52:43 +02:00
$content [ 'params' ] = file_get_contents ( $current_path . DIRECTORY_SEPARATOR . 'content.json' );
2015-04-22 10:44:16 +02:00
2015-04-15 13:54:46 +02:00
if ( isset ( $options [ 'disable' ])) {
$content [ 'disable' ] = $options [ 'disable' ];
}
2016-09-14 10:44:10 +02:00
$content [ 'id' ] = $this -> h5pC -> saveContent ( $content , $contentMainId );
$this -> contentId = $content [ 'id' ];
2014-04-22 16:33:01 +02:00
2016-01-11 18:31:04 +01:00
try {
// Save content folder contents
2016-09-14 10:44:10 +02:00
$this -> h5pC -> fs -> saveContent ( $current_path , $content );
2016-01-11 18:31:04 +01:00
}
catch ( Exception $e ) {
2016-09-14 10:44:10 +02:00
$this -> h5pF -> setErrorMessage ( $e -> getMessage ());
2016-01-11 18:31:04 +01:00
}
2014-10-10 13:56:48 +02:00
2015-02-12 15:56:31 +01:00
// Remove temp content folder
H5PCore :: deleteFileTree ( $basePath );
2014-03-26 08:43:29 +01:00
}
2015-02-12 15:56:31 +01:00
}
/**
* Helps savePackage .
*
* @ return int Number of libraries saved
*/
private function saveLibraries () {
// Keep track of the number of libraries that have been saved
$newOnes = 0 ;
$oldOnes = 0 ;
2014-10-10 13:56:48 +02:00
2015-02-12 15:56:31 +01:00
// Go through libraries that came with this package
foreach ( $this -> h5pC -> librariesJsonData as $libString => & $library ) {
// Find local library identifier
$libraryId = $this -> h5pC -> getLibraryId ( $library , $libString );
// Assume new library
$new = TRUE ;
if ( $libraryId ) {
// Found old library
$library [ 'libraryId' ] = $libraryId ;
if ( $this -> h5pF -> isPatchedLibrary ( $library )) {
// This is a newer version than ours. Upgrade!
$new = FALSE ;
}
else {
$library [ 'saveDependencies' ] = FALSE ;
// This is an older version, no need to save.
continue ;
}
}
2014-10-10 13:56:48 +02:00
2015-02-12 15:56:31 +01:00
// Indicate that the dependencies of this library should be saved.
$library [ 'saveDependencies' ] = TRUE ;
// Save library meta data
$this -> h5pF -> saveLibraryData ( $library , $new );
2016-01-11 18:31:04 +01:00
// Save library folder
$this -> h5pC -> fs -> saveLibrary ( $library );
2015-02-12 15:56:31 +01:00
2016-04-13 06:01:15 +02:00
// Remove cached assets that uses this library
2016-01-13 14:23:34 +01:00
if ( $this -> h5pC -> aggregateAssets && isset ( $library [ 'libraryId' ])) {
$removedKeys = $this -> h5pF -> deleteCachedAssets ( $library [ 'libraryId' ]);
$this -> h5pC -> fs -> deleteCachedAssets ( $removedKeys );
}
2016-01-11 18:31:04 +01:00
// Remove tmp folder
2015-02-12 15:56:31 +01:00
H5PCore :: deleteFileTree ( $library [ 'uploadDirectory' ]);
if ( $new ) {
$newOnes ++ ;
}
else {
$oldOnes ++ ;
}
2014-08-22 14:03:00 +02:00
}
2013-07-09 10:14:42 +02:00
2015-02-12 15:56:31 +01:00
// Go through the libraries again to save dependencies.
foreach ( $this -> h5pC -> librariesJsonData as & $library ) {
if ( ! $library [ 'saveDependencies' ]) {
continue ;
}
// TODO: Should the table be locked for this operation?
// Remove any old dependencies
$this -> h5pF -> deleteLibraryDependencies ( $library [ 'libraryId' ]);
// Insert the different new ones
if ( isset ( $library [ 'preloadedDependencies' ])) {
$this -> h5pF -> saveLibraryDependencies ( $library [ 'libraryId' ], $library [ 'preloadedDependencies' ], 'preloaded' );
}
if ( isset ( $library [ 'dynamicDependencies' ])) {
$this -> h5pF -> saveLibraryDependencies ( $library [ 'libraryId' ], $library [ 'dynamicDependencies' ], 'dynamic' );
}
if ( isset ( $library [ 'editorDependencies' ])) {
$this -> h5pF -> saveLibraryDependencies ( $library [ 'libraryId' ], $library [ 'editorDependencies' ], 'editor' );
}
// Make sure libraries dependencies, parameter filtering and export files gets regenerated for all content who uses this library.
$this -> h5pF -> clearFilteredParameters ( $library [ 'libraryId' ]);
}
// Tell the user what we've done.
if ( $newOnes && $oldOnes ) {
$message = $this -> h5pF -> t ( 'Added %new new H5P libraries and updated %old old.' , array ( '%new' => $newOnes , '%old' => $oldOnes ));
}
elseif ( $newOnes ) {
$message = $this -> h5pF -> t ( 'Added %new new H5P libraries.' , array ( '%new' => $newOnes ));
}
elseif ( $oldOnes ) {
$message = $this -> h5pF -> t ( 'Updated %old H5P libraries.' , array ( '%old' => $oldOnes ));
}
if ( isset ( $message )) {
$this -> h5pF -> setInfoMessage ( $message );
}
2012-11-30 15:51:39 +01:00
}
2014-10-10 13:56:48 +02:00
2013-03-29 16:10:29 +01:00
/**
* Delete an H5P package
2013-07-09 10:14:42 +02:00
*
2016-04-13 06:01:15 +02:00
* @ param $content
2013-03-29 16:10:29 +01:00
*/
2016-01-11 18:31:04 +01:00
public function deletePackage ( $content ) {
2016-09-14 10:44:10 +02:00
$this -> h5pC -> fs -> deleteContent ( $content );
2016-09-08 14:48:08 +02:00
$this -> h5pC -> fs -> deleteExport (( $content [ 'slug' ] ? $content [ 'slug' ] . '-' : '' ) . $content [ 'id' ] . '.h5p' );
2016-01-11 18:31:04 +01:00
$this -> h5pF -> deleteContentData ( $content [ 'id' ]);
2012-12-22 08:04:09 +01:00
}
2013-03-29 16:10:29 +01:00
/**
* Copy / clone an H5P package
2013-07-09 10:14:42 +02:00
*
2016-04-13 06:01:15 +02:00
* May for instance be used if the content is being revisioned without
2013-03-29 16:10:29 +01:00
* uploading a new H5P package
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:10:29 +01:00
* @ param int $contentId
* The new content id
* @ param int $copyFromId
* The content id of the content that should be cloned
* @ param int $contentMainId
* The main id of the new content ( used in frameworks that support revisioning )
*/
2012-12-22 08:04:09 +01:00
public function copyPackage ( $contentId , $copyFromId , $contentMainId = NULL ) {
2016-04-05 16:06:09 +02:00
$this -> h5pC -> fs -> cloneContent ( $copyFromId , $contentId );
2013-03-07 05:31:30 +01:00
$this -> h5pF -> copyLibraryUsage ( $contentId , $copyFromId , $contentMainId );
2012-12-22 08:04:09 +01:00
}
2012-11-29 22:52:41 +01:00
}
2013-11-14 23:42:10 +01:00
/**
* This class is used for exporting zips
*/
Class H5PExport {
public $h5pF ;
public $h5pC ;
/**
2013-11-15 01:13:00 +01:00
* Constructor for the H5PExport
2013-11-14 23:42:10 +01:00
*
2016-04-13 06:01:15 +02:00
* @ param H5PFrameworkInterface | object $H5PFramework
2013-11-14 23:42:10 +01:00
* The frameworks implementation of the H5PFrameworkInterface
2016-04-13 06:01:15 +02:00
* @ param H5PCore $H5PCore
* Reference to an instance of H5PCore
2013-11-14 23:42:10 +01:00
*/
2016-04-13 06:01:15 +02:00
public function __construct ( H5PFrameworkInterface $H5PFramework , H5PCore $H5PCore ) {
2013-11-14 23:42:10 +01:00
$this -> h5pF = $H5PFramework ;
$this -> h5pC = $H5PCore ;
}
2014-10-10 13:56:48 +02:00
2013-11-14 23:42:10 +01:00
/**
2013-11-18 18:03:01 +01:00
* Return path to h5p package .
*
* Creates package if not already created
2013-11-14 23:42:10 +01:00
*
2014-05-27 14:05:20 +02:00
* @ param array $content
2013-11-18 18:03:01 +01:00
* @ return string
2013-11-14 23:42:10 +01:00
*/
2014-05-27 14:05:20 +02:00
public function createExportFile ( $content ) {
2014-10-10 13:56:48 +02:00
2016-01-11 18:31:04 +01:00
// Get path to temporary folder, where export will be contained
$tmpPath = $this -> h5pC -> fs -> getTmpPath ();
mkdir ( $tmpPath , 0777 , true );
2014-05-27 14:05:20 +02:00
2016-01-11 18:31:04 +01:00
try {
// Create content folder and populate with files
$this -> h5pC -> fs -> exportContent ( $content [ 'id' ], " { $tmpPath } /content " );
}
catch ( Exception $e ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( $e -> getMessage ()));
H5PCore :: deleteFileTree ( $tmpPath );
2014-05-27 14:05:20 +02:00
return FALSE ;
}
2016-01-11 18:31:04 +01:00
// Update content.json with content from database
file_put_contents ( " { $tmpPath } /content/content.json " , $content [ 'params' ]);
2015-06-22 11:22:49 +02:00
2016-01-11 18:31:04 +01:00
// Make embedType into an array
$embedTypes = explode ( ', ' , $content [ 'embedType' ]);
2014-05-27 14:05:20 +02:00
// Build h5p.json
$h5pJson = array (
'title' => $content [ 'title' ],
2014-11-06 11:13:24 +01:00
'language' => ( isset ( $content [ 'language' ]) && strlen ( trim ( $content [ 'language' ])) !== 0 ) ? $content [ 'language' ] : 'und' ,
2014-05-27 14:05:20 +02:00
'mainLibrary' => $content [ 'library' ][ 'name' ],
'embedTypes' => $embedTypes ,
);
// Add dependencies to h5p
foreach ( $content [ 'dependencies' ] as $dependency ) {
$library = $dependency [ 'library' ];
2014-10-10 13:56:48 +02:00
2016-01-11 18:31:04 +01:00
try {
2016-04-21 11:38:14 +02:00
$exportFolder = NULL ;
// Determine path of export library
if ( isset ( $this -> h5pC ) && isset ( $this -> h5pC -> h5pD )) {
// Tries to find library in development folder
$isDevLibrary = $this -> h5pC -> h5pD -> getLibrary (
$library [ 'machineName' ],
$library [ 'majorVersion' ],
$library [ 'minorVersion' ]
);
if ( $isDevLibrary !== NULL ) {
$exportFolder = " / " . $library [ 'path' ];
}
}
2016-01-11 18:31:04 +01:00
// Export required libraries
2016-04-21 11:38:14 +02:00
$this -> h5pC -> fs -> exportLibrary ( $library , $tmpPath , $exportFolder );
2016-01-11 18:31:04 +01:00
}
catch ( Exception $e ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( $e -> getMessage ()));
H5PCore :: deleteFileTree ( $tmpPath );
return FALSE ;
}
2014-05-27 14:05:20 +02:00
// Do not add editor dependencies to h5p json.
if ( $dependency [ 'type' ] === 'editor' ) {
2014-10-10 13:56:48 +02:00
continue ;
2013-12-20 13:31:08 +01:00
}
2014-05-27 14:05:20 +02:00
2016-01-11 18:31:04 +01:00
// Add to h5p.json dependencies
2014-05-27 14:05:20 +02:00
$h5pJson [ $dependency [ 'type' ] . 'Dependencies' ][] = array (
'machineName' => $library [ 'machineName' ],
'majorVersion' => $library [ 'majorVersion' ],
'minorVersion' => $library [ 'minorVersion' ]
2013-11-17 18:04:39 +01:00
);
2014-05-27 14:05:20 +02:00
}
2013-11-14 23:42:10 +01:00
2014-05-27 14:05:20 +02:00
// Save h5p.json
$results = print_r ( json_encode ( $h5pJson ), true );
2016-01-11 18:31:04 +01:00
file_put_contents ( " { $tmpPath } /h5p.json " , $results );
2014-05-27 14:05:20 +02:00
2015-03-19 16:58:48 +01:00
// Get a complete file list from our tmp dir
$files = array ();
2016-01-11 18:31:04 +01:00
self :: populateFileList ( $tmpPath , $files );
// Get path to temporary export target file
$tmpFile = $this -> h5pC -> fs -> getTmpPath ();
2015-03-19 16:58:48 +01:00
2014-05-27 14:05:20 +02:00
// Create new zip instance.
$zip = new ZipArchive ();
2016-04-13 06:01:15 +02:00
$zip -> open ( $tmpFile , ZipArchive :: CREATE | ZipArchive :: OVERWRITE );
2014-05-27 14:05:20 +02:00
2015-03-19 16:58:48 +01:00
// Add all the files from the tmp dir.
foreach ( $files as $file ) {
// Please note that the zip format has no concept of folders, we must
// use forward slashes to separate our directories.
2017-04-25 16:19:53 +02:00
if ( file_exists ( realpath ( $file -> absolutePath ))) {
$zip -> addFile ( realpath ( $file -> absolutePath ), $file -> relativePath );
}
2013-11-14 23:42:10 +01:00
}
2015-03-19 16:58:48 +01:00
2016-01-11 18:31:04 +01:00
// Close zip and remove tmp dir
2014-05-27 14:05:20 +02:00
$zip -> close ();
2016-01-11 18:31:04 +01:00
H5PCore :: deleteFileTree ( $tmpPath );
2017-04-25 16:19:53 +02:00
$filename = $content [ 'slug' ] . '-' . $content [ 'id' ] . '.h5p' ;
2016-01-11 18:31:04 +01:00
try {
// Save export
2017-04-25 16:19:53 +02:00
$this -> h5pC -> fs -> saveExport ( $tmpFile , $filename );
2016-01-11 18:31:04 +01:00
}
catch ( Exception $e ) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( $e -> getMessage ()));
2016-09-26 12:05:02 +02:00
return false ;
2016-01-11 18:31:04 +01:00
}
unlink ( $tmpFile );
2017-04-25 16:19:53 +02:00
$this -> h5pF -> afterExportCreated ( $content , $filename );
2016-04-13 06:01:15 +02:00
return true ;
2013-11-14 23:42:10 +01:00
}
2013-11-18 17:57:32 +01:00
2015-03-19 16:58:48 +01:00
/**
* Recursive function the will add the files of the given directory to the
* given files list . All files are objects with an absolute path and
* a relative path . The relative path is forward slashes only ! Great for
* use in zip files and URLs .
*
* @ param string $dir path
* @ param array $files list
* @ param string $relative prefix . Optional
*/
private static function populateFileList ( $dir , & $files , $relative = '' ) {
$strip = strlen ( $dir ) + 1 ;
2015-10-14 14:43:03 +02:00
$contents = glob ( $dir . DIRECTORY_SEPARATOR . '*' );
if ( ! empty ( $contents )) {
foreach ( $contents as $file ) {
$rel = $relative . substr ( $file , $strip );
if ( is_dir ( $file )) {
self :: populateFileList ( $file , $files , $rel . '/' );
}
else {
$files [] = ( object ) array (
'absolutePath' => $file ,
'relativePath' => $rel
);
}
2015-03-19 16:58:48 +01:00
}
}
}
2013-11-18 18:03:01 +01:00
/**
* Delete . h5p file
*
2015-04-27 15:20:40 +02:00
* @ param array $content object
2013-11-18 18:03:01 +01:00
*/
2015-04-27 15:20:40 +02:00
public function deleteExport ( $content ) {
2016-09-08 14:48:08 +02:00
$this -> h5pC -> fs -> deleteExport (( $content [ 'slug' ] ? $content [ 'slug' ] . '-' : '' ) . $content [ 'id' ] . '.h5p' );
2013-11-18 17:57:32 +01:00
}
2013-11-18 18:03:01 +01:00
/**
* Add editor libraries to the list of libraries
*
2016-04-13 06:01:15 +02:00
* These are not supposed to go into h5p . json , but must be included with the rest
2013-11-18 18:03:01 +01:00
* of the libraries
*
2016-04-13 06:01:15 +02:00
* TODO This is a private function that is not currently being used
*
2013-11-18 18:03:01 +01:00
* @ param array $libraries
* List of libraries keyed by machineName
* @ param array $editorLibraries
* List of libraries keyed by machineName
2016-04-13 06:01:15 +02:00
* @ return array List of libraries keyed by machineName
2013-11-18 18:03:01 +01:00
*/
2013-11-18 17:45:31 +01:00
private function addEditorLibraries ( $libraries , $editorLibraries ) {
foreach ( $editorLibraries as $editorLibrary ) {
2014-10-10 13:56:48 +02:00
$libraries [ $editorLibrary [ 'machineName' ]] = $editorLibrary ;
2013-11-17 18:04:39 +01:00
}
return $libraries ;
}
2013-11-14 23:42:10 +01:00
}
2016-12-13 10:29:33 +01:00
abstract class H5PPermission {
const DOWNLOAD_H5P = 0 ;
const EMBED_H5P = 1 ;
2017-03-03 10:33:21 +01:00
const CREATE_RESTRICTED = 2 ;
2017-03-11 18:43:08 +01:00
const UPDATE_LIBRARIES = 3 ;
const INSTALL_RECOMMENDED = 4 ;
2016-12-13 10:29:33 +01:00
}
abstract class H5PDisplayOptionBehaviour {
const NEVER_SHOW = 0 ;
const CONTROLLED_BY_AUTHOR_DEFAULT_ON = 1 ;
const CONTROLLED_BY_AUTHOR_DEFAULT_OFF = 2 ;
const ALWAYS_SHOW = 3 ;
const CONTROLLED_BY_PERMISSIONS = 4 ;
}
2017-03-12 17:56:36 +01:00
abstract class H5PHubEndpoints {
const CONTENT_TYPES = 'api.h5p.org/v1/content-types/' ;
}
2016-12-13 10:29:33 +01:00
2013-03-29 16:10:29 +01:00
/**
* Functions and storage shared by the other H5P classes
*/
2013-02-17 15:04:30 +01:00
class H5PCore {
2014-03-26 08:43:29 +01:00
2013-12-05 12:15:14 +01:00
public static $coreApi = array (
2013-11-27 10:09:57 +01:00
'majorVersion' => 1 ,
2017-05-02 10:30:27 +02:00
'minorVersion' => 13
2013-11-27 10:09:57 +01:00
);
2013-04-10 17:08:57 +02:00
public static $styles = array (
'styles/h5p.css' ,
2016-04-05 18:35:02 +02:00
'styles/h5p-confirmation-dialog.css' ,
'styles/h5p-core-button.css'
2013-04-10 17:08:57 +02:00
);
2013-04-03 15:39:59 +02:00
public static $scripts = array (
'js/jquery.js' ,
'js/h5p.js' ,
2014-11-10 11:31:23 +01:00
'js/h5p-event-dispatcher.js' ,
2015-02-16 16:47:04 +01:00
'js/h5p-x-api-event.js' ,
'js/h5p-x-api.js' ,
2015-07-17 13:32:35 +02:00
'js/h5p-content-type.js' ,
2016-12-13 10:29:33 +01:00
'js/h5p-confirmation-dialog.js' ,
'js/h5p-action-bar.js'
2013-04-03 15:39:59 +02:00
);
2014-03-26 08:43:29 +01:00
public static $adminScripts = array (
'js/jquery.js' ,
'js/h5p-utils.js' ,
);
2013-07-15 16:25:10 +02:00
2017-05-03 10:02:27 +02:00
public static $defaultContentWhitelist = 'json png jpg jpeg gif bmp tif tiff svg eot ttf woff woff2 otf webm mp4 ogg mp3 wav txt pdf rtf doc docx xls xlsx ppt pptx odt ods odp xml csv diff patch swf md textile' ;
2013-07-13 22:25:18 +02:00
public static $defaultLibraryWhitelistExtras = 'js css' ;
2014-10-10 13:56:48 +02:00
2017-01-30 11:38:34 +01:00
public $librariesJsonData , $contentJsonData , $mainJsonData , $h5pF , $fs , $h5pD , $disableFileCheck ;
2014-10-10 13:56:48 +02:00
const SECONDS_IN_WEEK = 604800 ;
2014-05-27 14:05:20 +02:00
private $exportEnabled ;
2012-11-29 22:52:41 +01:00
2015-02-10 16:29:16 +01:00
// Disable flags
const DISABLE_NONE = 0 ;
const DISABLE_FRAME = 1 ;
const DISABLE_DOWNLOAD = 2 ;
const DISABLE_EMBED = 4 ;
const DISABLE_COPYRIGHT = 8 ;
const DISABLE_ABOUT = 16 ;
2016-12-16 14:22:03 +01:00
const DISPLAY_OPTION_FRAME = 'frame' ;
const DISPLAY_OPTION_DOWNLOAD = 'export' ;
const DISPLAY_OPTION_EMBED = 'embed' ;
const DISPLAY_OPTION_COPYRIGHT = 'copyright' ;
const DISPLAY_OPTION_ABOUT = 'icon' ;
2015-02-10 16:29:16 +01:00
// Map flags to string
public static $disable = array (
2016-12-16 14:22:03 +01:00
self :: DISABLE_FRAME => self :: DISPLAY_OPTION_FRAME ,
self :: DISABLE_DOWNLOAD => self :: DISPLAY_OPTION_DOWNLOAD ,
self :: DISABLE_EMBED => self :: DISPLAY_OPTION_EMBED ,
self :: DISABLE_COPYRIGHT => self :: DISPLAY_OPTION_COPYRIGHT
2015-02-10 16:29:16 +01:00
);
2012-11-29 22:52:41 +01:00
/**
2013-02-17 15:04:30 +01:00
* Constructor for the H5PCore
2012-11-29 22:52:41 +01:00
*
2016-04-13 06:01:15 +02:00
* @ param H5PFrameworkInterface $H5PFramework
2013-02-17 15:39:26 +01:00
* The frameworks implementation of the H5PFrameworkInterface
2016-01-14 09:46:00 +01:00
* @ param string | \H5PFileStorage $path H5P file storage directory or class .
2016-01-11 18:31:04 +01:00
* @ param string $url To file storage directory .
2014-03-26 08:43:29 +01:00
* @ param string $language code . Defaults to english .
2014-05-27 14:05:20 +02:00
* @ param boolean $export enabled ?
2012-11-29 22:52:41 +01:00
*/
2017-01-30 11:38:34 +01:00
public function __construct ( H5PFrameworkInterface $H5PFramework , $path , $url , $language = 'en' , $export = FALSE ) {
2013-02-17 15:04:30 +01:00
$this -> h5pF = $H5PFramework ;
2014-10-10 13:56:48 +02:00
2016-01-14 09:46:00 +01:00
$this -> fs = ( $path instanceof \H5PFileStorage ? $path : new \H5PDefaultStorage ( $path ));
2016-01-11 18:31:04 +01:00
2015-03-18 14:25:55 +01:00
$this -> url = $url ;
2014-05-27 14:05:20 +02:00
$this -> exportEnabled = $export ;
2017-02-07 12:55:53 +01:00
$this -> development_mode = H5PDevelopment :: MODE_NONE ;
2014-10-10 13:56:48 +02:00
2016-01-13 14:23:34 +01:00
$this -> aggregateAssets = FALSE ; // Off by default.. for now
2016-02-02 16:29:48 +01:00
$this -> detectSiteType ();
2016-02-08 10:38:53 +01:00
$this -> fullPluginPath = preg_replace ( '/\/[^\/]+[\/]?$/' , '' , dirname ( __FILE__ ));
2016-07-29 14:15:00 +02:00
// Standard regex for converting copied files paths
$this -> relativePathRegExp = '/^((\.\.\/){1,2})(.*content\/)?(\d+|editor)\/(.+)$/' ;
2014-03-26 08:43:29 +01:00
}
2017-01-30 11:38:34 +01:00
2014-08-22 11:34:38 +02:00
/**
* Save content and clear cache .
2014-10-10 13:56:48 +02:00
*
2014-08-22 11:34:38 +02:00
* @ param array $content
2016-04-13 06:01:15 +02:00
* @ param null | int $contentMainId
2014-08-22 11:34:38 +02:00
* @ return int Content ID
*/
public function saveContent ( $content , $contentMainId = NULL ) {
if ( isset ( $content [ 'id' ])) {
$this -> h5pF -> updateContent ( $content , $contentMainId );
}
else {
2014-10-10 13:56:48 +02:00
$content [ 'id' ] = $this -> h5pF -> insertContent ( $content , $contentMainId );
2014-08-22 11:34:38 +02:00
}
2014-10-10 13:56:48 +02:00
2015-04-07 19:32:44 +02:00
// Some user data for content has to be reset when the content changes.
$this -> h5pF -> resetContentUserData ( $contentMainId ? $contentMainId : $content [ 'id' ]);
2014-08-22 11:34:38 +02:00
return $content [ 'id' ];
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Load content .
*
* @ param int $id for content .
* @ return object
*/
public function loadContent ( $id ) {
$content = $this -> h5pF -> loadContent ( $id );
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $content !== NULL ) {
$content [ 'library' ] = array (
'id' => $content [ 'libraryId' ],
'name' => $content [ 'libraryName' ],
'majorVersion' => $content [ 'libraryMajorVersion' ],
'minorVersion' => $content [ 'libraryMinorVersion' ],
'embedTypes' => $content [ 'libraryEmbedTypes' ],
'fullscreen' => $content [ 'libraryFullscreen' ],
);
unset ( $content [ 'libraryId' ], $content [ 'libraryName' ], $content [ 'libraryEmbedTypes' ], $content [ 'libraryFullscreen' ]);
2014-10-10 13:56:48 +02:00
2014-05-27 13:07:53 +02:00
// // TODO: Move to filterParameters?
2017-01-30 11:38:34 +01:00
// if (isset($this->h5pD)) {
2014-05-27 13:07:53 +02:00
// // TODO: Remove Drupal specific stuff
// $json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json');
// if (file_exists($json_content_path) === TRUE) {
// $json_content = file_get_contents($json_content_path);
// if (json_decode($json_content, TRUE) !== FALSE) {
// drupal_set_message(t('Invalid json in json content'), 'warning');
// }
// $content['params'] = $json_content;
// }
// }
2014-03-26 08:43:29 +01:00
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
return $content ;
}
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
/**
2016-04-13 06:01:15 +02:00
* Filter content run parameters , rebuild content dependency cache and export file .
2014-10-10 13:56:48 +02:00
*
2016-04-13 06:01:15 +02:00
* @ param Object | array $content
2014-05-27 10:55:46 +02:00
* @ return Object NULL on failure .
*/
2015-03-12 14:38:10 +01:00
public function filterParameters ( & $content ) {
2016-09-23 10:52:40 +02:00
if ( ! empty ( $content [ 'filtered' ]) &&
( ! $this -> exportEnabled ||
( $content [ 'slug' ] &&
$this -> fs -> hasExport ( $content [ 'slug' ] . '-' . $content [ 'id' ] . '.h5p' )))) {
2014-09-26 15:56:42 +02:00
return $content [ 'filtered' ];
2014-05-27 10:55:46 +02:00
}
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
// Validate and filter against main library semantics.
$validator = new H5PContentValidator ( $this -> h5pF , $this );
$params = ( object ) array (
'library' => H5PCore :: libraryToString ( $content [ 'library' ]),
'params' => json_decode ( $content [ 'params' ])
);
2015-06-16 14:52:22 +02:00
if ( ! $params -> params ) {
return NULL ;
}
2014-05-27 10:55:46 +02:00
$validator -> validateLibrary ( $params , ( object ) array ( 'options' => array ( $params -> library )));
$params = json_encode ( $params -> params );
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
// Update content dependencies.
2014-05-27 14:05:20 +02:00
$content [ 'dependencies' ] = $validator -> getDependencies ();
2014-10-10 13:56:48 +02:00
2014-11-02 19:13:14 +01:00
// Sometimes the parameters are filtered before content has been created
if ( $content [ 'id' ]) {
$this -> h5pF -> deleteLibraryUsage ( $content [ 'id' ]);
$this -> h5pF -> saveLibraryUsage ( $content [ 'id' ], $content [ 'dependencies' ]);
2014-10-10 13:56:48 +02:00
2015-03-13 15:17:19 +01:00
if ( ! $content [ 'slug' ]) {
$content [ 'slug' ] = $this -> generateContentSlug ( $content );
// Remove old export file
2016-09-08 14:48:08 +02:00
$this -> fs -> deleteExport ( $content [ 'id' ] . '.h5p' );
2015-03-12 14:38:10 +01:00
}
2014-11-02 19:13:14 +01:00
if ( $this -> exportEnabled ) {
// Recreate export file
$exporter = new H5PExport ( $this -> h5pF , $this );
$exporter -> createExportFile ( $content );
}
2014-05-27 10:55:46 +02:00
2014-11-02 19:13:14 +01:00
// Cache.
2015-03-13 15:17:19 +01:00
$this -> h5pF -> updateContentFields ( $content [ 'id' ], array (
'filtered' => $params ,
'slug' => $content [ 'slug' ]
));
2014-11-02 19:13:14 +01:00
}
2014-05-27 10:55:46 +02:00
return $params ;
}
2014-10-10 13:56:48 +02:00
2015-03-13 15:17:19 +01:00
/**
* Generate content slug
*
* @ param array $content object
* @ return string unique content slug
*/
private function generateContentSlug ( $content ) {
$slug = H5PCore :: slugify ( $content [ 'title' ]);
$available = NULL ;
while ( ! $available ) {
if ( $available === FALSE ) {
// If not available, add number suffix.
$matches = array ();
if ( preg_match ( '/(.+-)([0-9]+)$/' , $slug , $matches )) {
$slug = $matches [ 1 ] . ( intval ( $matches [ 2 ]) + 1 );
}
else {
$slug .= '-2' ;
}
}
$available = $this -> h5pF -> isContentSlugAvailable ( $slug );
}
return $slug ;
}
2014-03-26 08:43:29 +01:00
/**
* Find the files required for this content to work .
*
* @ param int $id for content .
2016-04-13 06:01:15 +02:00
* @ param null $type
2014-03-26 08:43:29 +01:00
* @ return array
*/
public function loadContentDependencies ( $id , $type = NULL ) {
$dependencies = $this -> h5pF -> loadContentDependencies ( $id , $type );
2014-10-10 13:56:48 +02:00
2017-01-30 11:38:34 +01:00
if ( isset ( $this -> h5pD )) {
2014-03-26 08:43:29 +01:00
$developmentLibraries = $this -> h5pD -> getLibraries ();
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
foreach ( $dependencies as $key => $dependency ) {
$libraryString = H5PCore :: libraryToString ( $dependency );
if ( isset ( $developmentLibraries [ $libraryString ])) {
2014-05-07 16:43:48 +02:00
$developmentLibraries [ $libraryString ][ 'dependencyType' ] = $dependencies [ $key ][ 'dependencyType' ];
2014-03-26 08:43:29 +01:00
$dependencies [ $key ] = $developmentLibraries [ $libraryString ];
}
}
}
2014-10-10 13:56:48 +02:00
2014-05-07 16:43:48 +02:00
return $dependencies ;
2014-03-26 08:43:29 +01:00
}
2014-10-10 13:56:48 +02:00
2014-05-09 10:48:05 +02:00
/**
* Get all dependency assets of the given type
2014-10-10 13:56:48 +02:00
*
2014-05-09 10:48:05 +02:00
* @ param array $dependency
* @ param string $type
* @ param array $assets
2015-03-20 11:28:21 +01:00
* @ param string $prefix Optional . Make paths relative to another dir .
2014-05-09 10:48:05 +02:00
*/
2015-03-20 11:28:21 +01:00
private function getDependencyAssets ( $dependency , $type , & $assets , $prefix = '' ) {
2014-05-09 10:48:05 +02:00
// Check if dependency has any files of this type
if ( empty ( $dependency [ $type ]) || $dependency [ $type ][ 0 ] === '' ) {
return ;
}
2014-10-10 13:56:48 +02:00
2014-05-09 10:48:05 +02:00
// Check if we should skip CSS.
if ( $type === 'preloadedCss' && ( isset ( $dependency [ 'dropCss' ]) && $dependency [ 'dropCss' ] === '1' )) {
return ;
}
foreach ( $dependency [ $type ] as $file ) {
$assets [] = ( object ) array (
2015-04-07 19:30:46 +02:00
'path' => $prefix . '/' . $dependency [ 'path' ] . '/' . trim ( is_array ( $file ) ? $file [ 'path' ] : $file ),
2014-05-09 10:48:05 +02:00
'version' => $dependency [ 'version' ]
);
}
}
2014-10-10 13:56:48 +02:00
2014-05-09 10:48:05 +02:00
/**
* Combines path with cache buster / version .
2014-10-10 13:56:48 +02:00
*
2014-05-09 10:48:05 +02:00
* @ param array $assets
* @ return array
*/
public function getAssetsUrls ( $assets ) {
$urls = array ();
2014-10-10 13:56:48 +02:00
2014-05-09 10:48:05 +02:00
foreach ( $assets as $asset ) {
2015-03-20 11:28:21 +01:00
$url = $asset -> path ;
// Add URL prefix if not external
if ( strpos ( $asset -> path , '://' ) === FALSE ) {
2015-03-24 18:55:13 +01:00
$url = $this -> url . $url ;
2015-03-20 11:28:21 +01:00
}
// Add version/cache buster if set
if ( isset ( $asset -> version )) {
$url .= $asset -> version ;
}
$urls [] = $url ;
2014-05-09 10:48:05 +02:00
}
2014-10-10 13:56:48 +02:00
2014-05-09 10:48:05 +02:00
return $urls ;
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
2016-04-13 06:01:15 +02:00
* Return file paths for all dependencies files .
2014-03-26 08:43:29 +01:00
*
* @ param array $dependencies
2015-03-20 11:28:21 +01:00
* @ param string $prefix Optional . Make paths relative to another dir .
2014-03-26 08:43:29 +01:00
* @ return array files .
*/
2015-03-20 11:28:21 +01:00
public function getDependenciesFiles ( $dependencies , $prefix = '' ) {
2016-01-25 14:44:10 +01:00
// Build files list for assets
2014-03-26 08:43:29 +01:00
$files = array (
'scripts' => array (),
2014-05-06 11:46:19 +02:00
'styles' => array ()
2014-03-26 08:43:29 +01:00
);
2016-01-25 14:44:10 +01:00
2016-04-13 06:01:15 +02:00
$key = null ;
2016-01-25 14:44:10 +01:00
// Avoid caching empty files
if ( empty ( $dependencies )) {
return $files ;
}
2016-01-13 14:23:34 +01:00
if ( $this -> aggregateAssets ) {
2016-01-12 18:09:31 +01:00
// Get aggregated files for assets
$key = self :: getDependenciesHash ( $dependencies );
2016-04-12 11:37:55 +02:00
$cachedAssets = $this -> fs -> getCachedAssets ( $key );
if ( $cachedAssets !== NULL ) {
return array_merge ( $files , $cachedAssets ); // Using cached assets
2016-01-12 18:09:31 +01:00
}
}
// Using content dependencies
2014-03-26 08:43:29 +01:00
foreach ( $dependencies as $dependency ) {
if ( isset ( $dependency [ 'path' ]) === FALSE ) {
2015-04-07 19:30:46 +02:00
$dependency [ 'path' ] = 'libraries/' . H5PCore :: libraryToString ( $dependency , TRUE );
2014-03-26 08:43:29 +01:00
$dependency [ 'preloadedJs' ] = explode ( ',' , $dependency [ 'preloadedJs' ]);
$dependency [ 'preloadedCss' ] = explode ( ',' , $dependency [ 'preloadedCss' ]);
}
2014-05-09 10:48:05 +02:00
$dependency [ 'version' ] = " ?ver= { $dependency [ 'majorVersion' ] } . { $dependency [ 'minorVersion' ] } . { $dependency [ 'patchVersion' ] } " ;
2015-03-20 11:28:21 +01:00
$this -> getDependencyAssets ( $dependency , 'preloadedJs' , $files [ 'scripts' ], $prefix );
$this -> getDependencyAssets ( $dependency , 'preloadedCss' , $files [ 'styles' ], $prefix );
2014-03-26 08:43:29 +01:00
}
2016-01-12 18:09:31 +01:00
2016-01-13 14:23:34 +01:00
if ( $this -> aggregateAssets ) {
2016-01-12 18:09:31 +01:00
// Aggregate and store assets
$this -> fs -> cacheAssets ( $files , $key );
2016-01-13 14:23:34 +01:00
// Keep track of which libraries have been cached in case they are updated
$this -> h5pF -> saveCachedAssets ( $key , $dependencies );
2016-01-12 18:09:31 +01:00
}
2014-03-26 08:43:29 +01:00
return $files ;
}
2014-10-10 13:56:48 +02:00
2016-01-12 18:09:31 +01:00
private static function getDependenciesHash ( & $dependencies ) {
// Build hash of dependencies
$toHash = array ();
// Use unique identifier for each library version
2016-01-25 14:44:40 +01:00
foreach ( $dependencies as $dep ) {
2016-01-12 18:09:31 +01:00
$toHash [] = " { $dep [ 'machineName' ] } - { $dep [ 'majorVersion' ] } . { $dep [ 'minorVersion' ] } . { $dep [ 'patchVersion' ] } " ;
}
// Sort in case the same dependencies comes in a different order
sort ( $toHash );
// Calculate hash sum
return hash ( 'sha1' , implode ( '' , $toHash ));
}
2014-03-26 08:43:29 +01:00
/**
* Load library semantics .
*
2016-04-13 06:01:15 +02:00
* @ param $name
* @ param $majorVersion
* @ param $minorVersion
2014-05-27 10:55:46 +02:00
* @ return string
2014-03-26 08:43:29 +01:00
*/
public function loadLibrarySemantics ( $name , $majorVersion , $minorVersion ) {
2014-03-26 08:47:44 +01:00
$semantics = NULL ;
2017-01-30 11:38:34 +01:00
if ( isset ( $this -> h5pD )) {
2014-03-26 08:43:29 +01:00
// Try to load from dev lib
$semantics = $this -> h5pD -> getSemantics ( $name , $majorVersion , $minorVersion );
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $semantics === NULL ) {
// Try to load from DB.
$semantics = $this -> h5pF -> loadLibrarySemantics ( $name , $majorVersion , $minorVersion );
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $semantics !== NULL ) {
$semantics = json_decode ( $semantics );
$this -> h5pF -> alterLibrarySemantics ( $semantics , $name , $majorVersion , $minorVersion );
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
return $semantics ;
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Load library .
*
2016-04-13 06:01:15 +02:00
* @ param $name
* @ param $majorVersion
* @ param $minorVersion
2014-03-26 08:43:29 +01:00
* @ return array or null .
*/
public function loadLibrary ( $name , $majorVersion , $minorVersion ) {
2014-03-26 08:47:44 +01:00
$library = NULL ;
2017-01-30 11:38:34 +01:00
if ( isset ( $this -> h5pD )) {
2014-03-26 08:43:29 +01:00
// Try to load from dev
$library = $this -> h5pD -> getLibrary ( $name , $majorVersion , $minorVersion );
2014-05-08 16:28:36 +02:00
if ( $library !== NULL ) {
$library [ 'semantics' ] = $this -> h5pD -> getSemantics ( $name , $majorVersion , $minorVersion );
}
2014-03-26 08:43:29 +01:00
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $library === NULL ) {
// Try to load from DB.
$library = $this -> h5pF -> loadLibrary ( $name , $majorVersion , $minorVersion );
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
return $library ;
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
2014-08-22 14:03:00 +02:00
* Deletes a library
2014-10-10 13:56:48 +02:00
*
2016-04-13 06:01:15 +02:00
* @ param stdClass $libraryId
2014-08-22 14:03:00 +02:00
*/
public function deleteLibrary ( $libraryId ) {
$this -> h5pF -> deleteLibrary ( $libraryId );
}
2014-10-10 13:56:48 +02:00
2014-08-22 14:03:00 +02:00
/**
2014-10-10 13:56:48 +02:00
* Recursive . Goes through the dependency tree for the given library and
2014-03-26 08:43:29 +01:00
* adds all the dependencies to the given array in a flat format .
2014-10-10 13:56:48 +02:00
*
2016-04-13 06:01:15 +02:00
* @ param $dependencies
2014-03-26 08:43:29 +01:00
* @ param array $library To find all dependencies for .
2015-01-30 10:59:40 +01:00
* @ param int $nextWeight An integer determining the order of the libraries
* when they are loaded
2016-04-13 06:01:15 +02:00
* @ param bool $editor Used internally to force all preloaded sub dependencies
* of an editor dependency to be editor dependencies .
* @ return int
2014-03-26 08:43:29 +01:00
*/
2015-01-30 10:59:40 +01:00
public function findLibraryDependencies ( & $dependencies , $library , $nextWeight = 1 , $editor = FALSE ) {
2014-03-26 08:43:29 +01:00
foreach ( array ( 'dynamic' , 'preloaded' , 'editor' ) as $type ) {
$property = $type . 'Dependencies' ;
2014-03-26 08:47:44 +01:00
if ( ! isset ( $library [ $property ])) {
2014-03-26 08:43:29 +01:00
continue ; // Skip, no such dependencies.
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $type === 'preloaded' && $editor === TRUE ) {
// All preloaded dependencies of an editor library is set to editor.
$type = 'editor' ;
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
foreach ( $library [ $property ] as $dependency ) {
$dependencyKey = $type . '-' . $dependency [ 'machineName' ];
if ( isset ( $dependencies [ $dependencyKey ]) === TRUE ) {
continue ; // Skip, already have this.
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
$dependencyLibrary = $this -> loadLibrary ( $dependency [ 'machineName' ], $dependency [ 'majorVersion' ], $dependency [ 'minorVersion' ]);
2014-04-14 10:50:37 +02:00
if ( $dependencyLibrary ) {
$dependencies [ $dependencyKey ] = array (
'library' => $dependencyLibrary ,
'type' => $type
);
2015-01-30 10:59:40 +01:00
$nextWeight = $this -> findLibraryDependencies ( $dependencies , $dependencyLibrary , $nextWeight , $type === 'editor' );
$dependencies [ $dependencyKey ][ 'weight' ] = $nextWeight ++ ;
2014-04-14 10:50:37 +02:00
}
else {
// This site is missing a dependency!
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Missing dependency @dep required by @lib.' , array ( '@dep' => H5PCore :: libraryToString ( $dependency ), '@lib' => H5PCore :: libraryToString ( $library ))));
}
2014-03-26 08:43:29 +01:00
}
}
2015-01-30 10:59:40 +01:00
return $nextWeight ;
2012-11-29 22:52:41 +01:00
}
2013-07-09 10:14:42 +02:00
2013-03-29 16:10:29 +01:00
/**
* Check if a library is of the version we ' re looking for
2013-07-09 10:14:42 +02:00
*
2016-04-13 06:01:15 +02:00
* Same version means that the majorVersion and minorVersion is the same
2013-07-09 10:14:42 +02:00
*
2013-03-29 16:10:29 +01:00
* @ param array $library
* Data from library . json
* @ param array $dependency
* Definition of what library we ' re looking for
* @ return boolean
* TRUE if the library is the same version as the dependency
* FALSE otherwise
*/
2012-12-04 22:26:20 +01:00
public function isSameVersion ( $library , $dependency ) {
2014-08-26 12:00:14 +02:00
if ( $library [ 'machineName' ] != $dependency [ 'machineName' ]) {
return FALSE ;
}
2012-12-04 22:26:20 +01:00
if ( $library [ 'majorVersion' ] != $dependency [ 'majorVersion' ]) {
return FALSE ;
}
if ( $library [ 'minorVersion' ] != $dependency [ 'minorVersion' ]) {
return FALSE ;
}
return TRUE ;
}
2012-12-22 06:35:16 +01:00
/**
* Recursive function for removing directories .
*
2013-03-29 16:10:29 +01:00
* @ param string $dir
* Path to the directory we ' ll be deleting
* @ return boolean
* Indicates if the directory existed .
2012-12-22 06:35:16 +01:00
*/
2014-03-26 08:43:29 +01:00
public static function deleteFileTree ( $dir ) {
2012-12-22 06:35:16 +01:00
if ( ! is_dir ( $dir )) {
2016-04-13 06:01:15 +02:00
return false ;
2012-12-22 06:35:16 +01:00
}
$files = array_diff ( scandir ( $dir ), array ( '.' , '..' ));
foreach ( $files as $file ) {
2014-03-26 08:43:29 +01:00
( is_dir ( " $dir / $file " )) ? self :: deleteFileTree ( " $dir / $file " ) : unlink ( " $dir / $file " );
2012-12-22 06:35:16 +01:00
}
return rmdir ( $dir );
}
2012-12-22 08:04:09 +01:00
2013-04-13 13:28:48 +02:00
/**
* Writes library data as string on the form { machineName } { majorVersion } . { minorVersion }
*
* @ param array $library
* With keys machineName , majorVersion and minorVersion
2013-07-05 17:35:59 +02:00
* @ param boolean $folderName
* Use hyphen instead of space in returned string .
2013-04-13 13:28:48 +02:00
* @ return string
* On the form { machineName } { majorVersion } . { minorVersion }
*/
2014-03-26 08:43:29 +01:00
public static function libraryToString ( $library , $folderName = FALSE ) {
return ( isset ( $library [ 'machineName' ]) ? $library [ 'machineName' ] : $library [ 'name' ]) . ( $folderName ? '-' : ' ' ) . $library [ 'majorVersion' ] . '.' . $library [ 'minorVersion' ];
2013-04-13 13:28:48 +02:00
}
2013-07-05 17:35:59 +02:00
/**
2013-07-09 11:01:29 +02:00
* Parses library data from a string on the form { machineName } { majorVersion } . { minorVersion }
2013-07-05 17:35:59 +02:00
*
* @ param string $libraryString
* On the form { machineName } { majorVersion } . { minorVersion }
* @ return array | FALSE
* With keys machineName , majorVersion and minorVersion .
* Returns FALSE only if string is not parsable in the normal library
* string formats " Lib.Name-x.y " or " Lib.Name x.y "
*/
2014-08-18 14:09:38 +02:00
public static function libraryFromString ( $libraryString ) {
2013-07-05 17:35:59 +02:00
$re = '/^([\w0-9\-\.]{1,255})[\-\ ]([0-9]{1,5})\.([0-9]{1,5})$/i' ;
$matches = array ();
$res = preg_match ( $re , $libraryString , $matches );
if ( $res ) {
return array (
'machineName' => $matches [ 1 ],
'majorVersion' => $matches [ 2 ],
'minorVersion' => $matches [ 3 ]
);
}
return FALSE ;
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
/**
* Determine the correct embed type to use .
*
2016-04-13 06:01:15 +02:00
* @ param $contentEmbedType
* @ param $libraryEmbedTypes
2014-03-26 08:43:29 +01:00
* @ return string 'div' or 'iframe' .
*/
public static function determineEmbedType ( $contentEmbedType , $libraryEmbedTypes ) {
// Detect content embed type
$embedType = strpos ( strtolower ( $contentEmbedType ), 'div' ) !== FALSE ? 'div' : 'iframe' ;
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $libraryEmbedTypes !== NULL && $libraryEmbedTypes !== '' ) {
// Check that embed type is available for library
$embedTypes = strtolower ( $libraryEmbedTypes );
if ( strpos ( $embedTypes , $embedType ) === FALSE ) {
// Not available, pick default.
$embedType = strpos ( $embedTypes , 'div' ) !== FALSE ? 'div' : 'iframe' ;
2014-10-10 13:56:48 +02:00
}
2014-03-26 08:43:29 +01:00
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
return $embedType ;
}
2014-10-10 13:56:48 +02:00
2014-06-03 16:07:50 +02:00
/**
* Get the absolute version for the library as a human readable string .
2014-10-10 13:56:48 +02:00
*
2014-06-03 16:07:50 +02:00
* @ param object $library
* @ return string
*/
public static function libraryVersion ( $library ) {
return $library -> major_version . '.' . $library -> minor_version . '.' . $library -> patch_version ;
}
2014-10-10 13:56:48 +02:00
2014-06-03 16:07:50 +02:00
/**
2016-04-13 06:01:15 +02:00
* Determine which versions content with the given library can be upgraded to .
2014-10-10 13:56:48 +02:00
*
2014-06-03 16:07:50 +02:00
* @ param object $library
* @ param array $versions
* @ return array
*/
2014-06-13 15:02:26 +02:00
public function getUpgrades ( $library , $versions ) {
2014-06-03 16:07:50 +02:00
$upgrades = array ();
foreach ( $versions as $upgrade ) {
if ( $upgrade -> major_version > $library -> major_version || $upgrade -> major_version === $library -> major_version && $upgrade -> minor_version > $library -> minor_version ) {
$upgrades [ $upgrade -> id ] = H5PCore :: libraryVersion ( $upgrade );
}
}
return $upgrades ;
}
2014-10-10 13:56:48 +02:00
2014-07-18 11:26:46 +02:00
/**
* Converts all the properties of the given object or array from
* snake_case to camelCase . Useful after fetching data from the database .
2014-10-10 13:56:48 +02:00
*
2014-07-18 11:26:46 +02:00
* Note that some databases does not support camelCase .
2014-10-10 13:56:48 +02:00
*
2014-07-18 11:26:46 +02:00
* @ param mixed $arr input
* @ param boolean $obj return object
* @ return mixed object or array
*/
public static function snakeToCamel ( $arr , $obj = false ) {
$newArr = array ();
foreach ( $arr as $key => $val ) {
$next = - 1 ;
while (( $next = strpos ( $key , '_' , $next + 1 )) !== FALSE ) {
$key = substr_replace ( $key , strtoupper ( $key { $next + 1 }), $next , 2 );
}
$newArr [ $key ] = $val ;
}
return $obj ? ( object ) $newArr : $newArr ;
}
2014-10-10 13:56:48 +02:00
2016-02-02 16:29:48 +01:00
/**
* Detects if the site was accessed from localhost ,
* through a local network or from the internet .
*/
public function detectSiteType () {
$type = $this -> h5pF -> getOption ( 'site_type' , 'local' );
// Determine remote/visitor origin
2016-02-04 15:15:35 +01:00
if ( $type === 'network' ||
2016-05-12 15:52:37 +02:00
( $type === 'local' &&
isset ( $_SERVER [ 'REMOTE_ADDR' ]) &&
! preg_match ( '/^localhost$|^127(?:\.[0-9]+){0,2}\.[0-9]+$|^(?:0*\:)*?:?0*1$/i' , $_SERVER [ 'REMOTE_ADDR' ]))) {
2016-04-24 05:40:46 +02:00
if ( isset ( $_SERVER [ 'REMOTE_ADDR' ]) && filter_var ( $_SERVER [ 'REMOTE_ADDR' ], FILTER_VALIDATE_IP , FILTER_FLAG_NO_PRIV_RANGE )) {
2016-02-02 16:29:48 +01:00
// Internet
$this -> h5pF -> setOption ( 'site_type' , 'internet' );
}
elseif ( $type === 'local' ) {
// Local network
$this -> h5pF -> setOption ( 'site_type' , 'network' );
}
}
}
2016-02-03 15:58:49 +01:00
/**
* Get a list of installed libraries , different minor versions will
* return separate entries .
*
* @ return array
* A distinct array of installed libraries
*/
public function getLibrariesInstalled () {
2016-02-04 16:34:32 +01:00
$librariesInstalled = array ();
2016-02-03 15:58:49 +01:00
$libs = $this -> h5pF -> loadLibraries ();
2016-03-07 10:58:39 +01:00
foreach ( $libs as $libName => $library ) {
2016-02-03 15:58:49 +01:00
foreach ( $library as $libVersion ) {
2016-03-21 12:35:44 +01:00
$librariesInstalled [ $libName . ' ' . $libVersion -> major_version . '.' . $libVersion -> minor_version ] = $libVersion -> patch_version ;
2016-02-03 15:58:49 +01:00
}
}
return $librariesInstalled ;
}
2016-02-04 16:34:32 +01:00
/**
2016-04-13 06:01:15 +02:00
* Easy way to combine similar data sets .
2016-02-04 16:34:32 +01:00
*
* @ param array $inputs Multiple arrays with data
* @ return array
*/
public function combineArrayValues ( $inputs ) {
$results = array ();
foreach ( $inputs as $index => $values ) {
foreach ( $values as $key => $value ) {
$results [ $key ][ $index ] = $value ;
}
}
return $results ;
}
2014-09-05 09:59:16 +02:00
/**
2017-02-23 11:33:39 +01:00
* Communicate with H5P . org and get content type cache . Each platform
* implementation is responsible for invoking this , eg using cron
*
2016-04-13 06:01:15 +02:00
* @ param bool $fetchingDisabled
2017-03-16 09:51:46 +01:00
*
* @ return bool | object Returns endpoint data if found , otherwise FALSE
2014-09-05 09:59:16 +02:00
*/
2014-11-01 17:05:15 +01:00
public function fetchLibrariesMetadata ( $fetchingDisabled = FALSE ) {
2016-02-08 11:39:21 +01:00
// Gather data
$uuid = $this -> h5pF -> getOption ( 'site_uuid' , '' );
2016-02-09 10:54:04 +01:00
$platform = $this -> h5pF -> getPlatformInfo ();
2017-03-18 15:39:27 +01:00
$registrationData = array (
2016-02-08 11:39:21 +01:00
'uuid' => $uuid ,
2016-02-09 13:52:35 +01:00
'platform_name' => $platform [ 'name' ],
'platform_version' => $platform [ 'version' ],
'h5p_version' => $platform [ 'h5pVersion' ],
'disabled' => $fetchingDisabled ? 1 : 0 ,
2016-02-08 11:39:21 +01:00
'local_id' => hash ( 'crc32' , $this -> fullPluginPath ),
2017-03-18 15:39:27 +01:00
'type' => $this -> h5pF -> getOption ( 'site_type' , 'local' )
2016-02-08 11:39:21 +01:00
);
2017-03-16 09:51:46 +01:00
// Register site if it is not registered
if ( empty ( $uuid )) {
$protocol = ( extension_loaded ( 'openssl' ) ? 'https' : 'http' );
$endpoint = 'api.h5p.org/v1/sites' ;
2017-03-18 15:39:27 +01:00
$registration = $this -> h5pF -> fetchExternalData ( " { $protocol } :// { $endpoint } " , $registrationData );
2017-03-16 09:51:46 +01:00
// Failed retrieving uuid
if ( ! $registration ) {
2017-03-16 10:01:41 +01:00
$errorMessage = $this -> h5pF -> t ( 'Site could not be registered with the hub. Please contact your site administrator.' );
$this -> h5pF -> setErrorMessage ( $errorMessage );
$this -> h5pF -> setErrorMessage (
$this -> h5pF -> t ( 'The H5P Hub has been disabled until this problem can be resolved. You may still upload libraries through the "H5P Libraries" page.' )
);
2017-03-16 10:09:52 +01:00
return FALSE ;
2017-03-16 09:51:46 +01:00
}
// Successfully retrieved new uuid
$json = json_decode ( $registration );
2017-03-18 15:39:27 +01:00
$registrationData [ 'uuid' ] = $json -> uuid ;
2017-03-16 09:51:46 +01:00
$this -> h5pF -> setOption ( 'site_uuid' , $json -> uuid );
2017-03-16 10:01:41 +01:00
$this -> h5pF -> setInfoMessage (
$this -> h5pF -> t ( 'Your site was successfully registered with the H5P Hub.' )
);
2017-04-25 16:19:53 +02:00
// TODO: Uncomment when key is once again available in H5P Settings
// $this->h5pF->setInfoMessage(
// $this->h5pF->t('You have been provided a unique key that identifies you with the Hub when receiving new updates. The key is available for viewing in the "H5P Settings" page.')
// );
2017-03-16 09:51:46 +01:00
}
2017-04-25 16:19:53 +02:00
if ( $this -> h5pF -> getOption ( 'send_usage_statistics' , TRUE )) {
2017-04-10 14:09:02 +02:00
$siteData = array_merge (
$registrationData ,
array (
'num_authors' => $this -> h5pF -> getNumAuthors (),
'libraries' => json_encode ( $this -> combineArrayValues ( array (
'patch' => $this -> getLibrariesInstalled (),
'content' => $this -> h5pF -> getLibraryContentCount (),
'loaded' => $this -> h5pF -> getLibraryStats ( 'library' ),
'created' => $this -> h5pF -> getLibraryStats ( 'content create' ),
'createdUpload' => $this -> h5pF -> getLibraryStats ( 'content create upload' ),
'deleted' => $this -> h5pF -> getLibraryStats ( 'content delete' ),
'resultViews' => $this -> h5pF -> getLibraryStats ( 'results content' ),
'shortcodeInserts' => $this -> h5pF -> getLibraryStats ( 'content shortcode insert' )
)))
)
);
}
else {
$siteData = $registrationData ;
}
2017-03-18 15:39:27 +01:00
$result = $this -> updateContentTypeCache ( $siteData );
2016-02-08 11:39:21 +01:00
2017-02-23 11:33:39 +01:00
// No data received
if ( ! $result || empty ( $result )) {
2017-05-22 12:57:43 +02:00
return FALSE ;
2016-02-08 11:39:21 +01:00
}
2017-02-24 15:38:49 +01:00
// Handle libraries metadata
if ( isset ( $result -> libraries )) {
foreach ( $result -> libraries as $library ) {
if ( isset ( $library -> tutorialUrl ) && isset ( $library -> machineName )) {
$this -> h5pF -> setLibraryTutorialUrl ( $library -> machineNamee , $library -> tutorialUrl );
}
}
}
2017-03-18 15:39:27 +01:00
return $result ;
2014-11-01 17:05:15 +01:00
}
2015-02-10 16:29:16 +01:00
2016-02-08 11:39:21 +01:00
/**
2016-12-16 13:29:18 +01:00
* Create representation of display options as int
2016-02-08 11:39:21 +01:00
*
2015-02-10 16:29:16 +01:00
* @ param array $sources
2015-05-22 15:31:28 +02:00
* @ param int $current
2015-02-10 16:29:16 +01:00
* @ return int
2016-02-08 11:39:21 +01:00
*/
2016-12-16 13:29:18 +01:00
public function getStorableDisplayOptions ( & $sources , $current ) {
// Download - force setting it if always on or always off
2016-12-16 14:22:03 +01:00
$download = $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_DOWNLOAD , H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
if ( $download == H5PDisplayOptionBehaviour :: ALWAYS_SHOW ||
$download == H5PDisplayOptionBehaviour :: NEVER_SHOW ) {
$sources [ self :: DISPLAY_OPTION_DOWNLOAD ] = ( $download == H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
2016-12-16 13:29:18 +01:00
}
2015-02-10 16:29:16 +01:00
2016-12-16 13:29:18 +01:00
// Embed - force setting it if always on or always off
2016-12-16 14:22:03 +01:00
$embed = $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_EMBED , H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
2016-12-16 13:29:18 +01:00
if ( $embed == H5PDisplayOptionBehaviour :: ALWAYS_SHOW ||
$embed == H5PDisplayOptionBehaviour :: NEVER_SHOW ) {
2016-12-16 14:22:03 +01:00
$sources [ self :: DISPLAY_OPTION_EMBED ] = ( $embed == H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
2015-02-10 16:29:16 +01:00
}
2016-12-16 13:29:18 +01:00
2015-05-22 15:31:28 +02:00
foreach ( H5PCore :: $disable as $bit => $option ) {
2016-12-16 13:29:18 +01:00
if ( ! isset ( $sources [ $option ]) || ! $sources [ $option ]) {
$current |= $bit ; // Disable
2015-02-10 16:29:16 +01:00
}
2016-12-16 13:29:18 +01:00
else {
$current &= ~ $bit ; // Enable
2015-02-10 16:29:16 +01:00
}
}
2015-05-22 15:31:28 +02:00
return $current ;
2015-02-10 16:29:16 +01:00
}
2015-02-12 15:56:31 +01:00
2016-12-13 10:29:33 +01:00
/**
* Determine display options visibility and value on edit
*
2016-12-16 11:38:47 +01:00
* @ param int $disable
* @ return array
2016-12-13 10:29:33 +01:00
*/
public function getDisplayOptionsForEdit ( $disable = NULL ) {
2017-01-26 10:37:26 +01:00
$display_options = array ();
2016-12-13 10:29:33 +01:00
2017-01-26 10:37:26 +01:00
$current_display_options = $disable === NULL ? array () : $this -> getDisplayOptionsAsArray ( $disable );
2016-12-13 10:29:33 +01:00
2016-12-16 14:22:03 +01:00
if ( $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_FRAME , TRUE )) {
$display_options [ self :: DISPLAY_OPTION_FRAME ] =
isset ( $current_display_options [ self :: DISPLAY_OPTION_FRAME ]) ?
$current_display_options [ self :: DISPLAY_OPTION_FRAME ] :
TRUE ;
2016-12-13 10:29:33 +01:00
2016-12-16 13:27:40 +01:00
// Download
2016-12-16 14:22:03 +01:00
$export = $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_DOWNLOAD , H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
2016-12-16 13:27:40 +01:00
if ( $export == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_ON ||
$export == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_OFF ) {
2016-12-16 14:22:03 +01:00
$display_options [ self :: DISPLAY_OPTION_DOWNLOAD ] =
isset ( $current_display_options [ self :: DISPLAY_OPTION_DOWNLOAD ]) ?
$current_display_options [ self :: DISPLAY_OPTION_DOWNLOAD ] :
2016-12-16 13:27:40 +01:00
( $export == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_ON );
2015-02-10 16:29:16 +01:00
}
2016-12-16 13:27:40 +01:00
// Embed
2016-12-16 14:22:03 +01:00
$embed = $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_EMBED , H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
2016-12-16 13:27:40 +01:00
if ( $embed == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_ON ||
$embed == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_OFF ) {
2016-12-16 14:22:03 +01:00
$display_options [ self :: DISPLAY_OPTION_EMBED ] =
isset ( $current_display_options [ self :: DISPLAY_OPTION_EMBED ]) ?
$current_display_options [ self :: DISPLAY_OPTION_EMBED ] :
2016-12-16 13:27:40 +01:00
( $embed == H5PDisplayOptionBehaviour :: CONTROLLED_BY_AUTHOR_DEFAULT_ON );
2016-12-13 10:29:33 +01:00
}
2016-12-16 13:27:40 +01:00
// Copyright
2016-12-16 14:22:03 +01:00
if ( $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_COPYRIGHT , TRUE )) {
$display_options [ self :: DISPLAY_OPTION_COPYRIGHT ] =
isset ( $current_display_options [ self :: DISPLAY_OPTION_COPYRIGHT ]) ?
$current_display_options [ self :: DISPLAY_OPTION_COPYRIGHT ] :
2016-12-16 13:27:40 +01:00
TRUE ;
2015-02-10 16:29:16 +01:00
}
}
2016-12-13 10:29:33 +01:00
return $display_options ;
2015-02-10 16:29:16 +01:00
}
/**
2016-12-13 15:57:05 +01:00
* Helper function used to figure out embed & download behaviour
2015-02-10 16:29:16 +01:00
*
2016-12-16 11:38:47 +01:00
* @ param string $option_name
* @ param H5PPermission $permission
* @ param int $id
* @ param bool & $value
2015-02-10 16:29:16 +01:00
*/
2016-12-13 15:57:05 +01:00
private function setDisplayOptionOverrides ( $option_name , $permission , $id , & $value ) {
$behaviour = $this -> h5pF -> getOption ( $option_name , H5PDisplayOptionBehaviour :: ALWAYS_SHOW );
// If never show globally, force hide
if ( $behaviour == H5PDisplayOptionBehaviour :: NEVER_SHOW ) {
$value = false ;
}
elseif ( $behaviour == H5PDisplayOptionBehaviour :: ALWAYS_SHOW ) {
// If always show or permissions say so, force show
$value = true ;
}
elseif ( $behaviour == H5PDisplayOptionBehaviour :: CONTROLLED_BY_PERMISSIONS ) {
$value = $this -> h5pF -> hasPermission ( $permission , $id );
}
}
2016-12-13 10:29:33 +01:00
/**
* Determine display option visibility when viewing H5P
*
2016-12-16 11:38:47 +01:00
* @ param int $display_options
* @ param int $id Might be content id or user id .
* Depends on what the platform needs to be able to determine permissions .
* @ return array
2016-12-13 10:29:33 +01:00
*/
public function getDisplayOptionsForView ( $disable , $id ) {
$display_options = $this -> getDisplayOptionsAsArray ( $disable );
2016-12-16 14:22:03 +01:00
if ( $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_FRAME , TRUE ) == FALSE ) {
$display_options [ self :: DISPLAY_OPTION_FRAME ] = false ;
2016-12-13 10:29:33 +01:00
}
else {
2016-12-16 14:22:03 +01:00
$this -> setDisplayOptionOverrides ( self :: DISPLAY_OPTION_DOWNLOAD , H5PPermission :: DOWNLOAD_H5P , $id , $display_options [ self :: DISPLAY_OPTION_DOWNLOAD ]);
$this -> setDisplayOptionOverrides ( self :: DISPLAY_OPTION_EMBED , H5PPermission :: EMBED_H5P , $id , $display_options [ self :: DISPLAY_OPTION_EMBED ]);
2016-12-13 10:29:33 +01:00
2016-12-16 14:22:03 +01:00
if ( $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_COPYRIGHT , TRUE ) == FALSE ) {
$display_options [ self :: DISPLAY_OPTION_COPYRIGHT ] = false ;
2015-05-22 15:31:28 +02:00
}
2015-02-10 16:29:16 +01:00
}
2016-12-13 10:29:33 +01:00
return $display_options ;
}
/**
* Convert display options as single byte to array
*
2016-12-16 11:38:47 +01:00
* @ param int $disable
* @ return array
2016-12-13 10:29:33 +01:00
*/
private function getDisplayOptionsAsArray ( $disable ) {
return array (
2016-12-16 14:22:03 +01:00
self :: DISPLAY_OPTION_FRAME => ! ( $disable & H5PCore :: DISABLE_FRAME ),
self :: DISPLAY_OPTION_DOWNLOAD => ! ( $disable & H5PCore :: DISABLE_DOWNLOAD ),
self :: DISPLAY_OPTION_EMBED => ! ( $disable & H5PCore :: DISABLE_EMBED ),
self :: DISPLAY_OPTION_COPYRIGHT => ! ( $disable & H5PCore :: DISABLE_COPYRIGHT ),
2016-12-21 15:17:36 +01:00
self :: DISPLAY_OPTION_ABOUT => !! $this -> h5pF -> getOption ( self :: DISPLAY_OPTION_ABOUT , TRUE ),
2016-12-13 10:29:33 +01:00
);
2015-02-10 16:29:16 +01:00
}
2015-02-12 15:56:31 +01:00
/**
* Small helper for getting the library ' s ID .
*
* @ param array $library
* @ param string [ $libString ]
* @ return int Identifier , or FALSE if non - existent
*/
public function getLibraryId ( $library , $libString = NULL ) {
if ( ! $libString ) {
$libString = self :: libraryToString ( $library );
}
if ( ! isset ( $libraryIdMap [ $libString ])) {
$libraryIdMap [ $libString ] = $this -> h5pF -> getLibraryId ( $library [ 'machineName' ], $library [ 'majorVersion' ], $library [ 'minorVersion' ]);
}
return $libraryIdMap [ $libString ];
}
2015-03-12 14:38:10 +01:00
/**
2015-03-13 15:17:19 +01:00
* Convert strings of text into simple kebab case slugs .
2015-03-12 14:38:10 +01:00
* Very useful for readable urls etc .
*
* @ param string $input
* @ return string
*/
2015-03-13 15:17:19 +01:00
public static function slugify ( $input ) {
2015-03-12 14:38:10 +01:00
// Down low
$input = strtolower ( $input );
// Replace common chars
$input = str_replace (
2015-03-13 15:17:19 +01:00
array ( 'æ' , 'ø' , 'ö' , 'ó' , 'ô' , 'Ò' , 'Õ' , 'Ý' , 'ý' , 'ÿ' , 'ā' , 'ă' , 'ą' , 'œ' , 'å' , 'ä' , 'á' , 'à' , 'â' , 'ã' , 'ç' , 'ć' , 'ĉ' , 'ċ' , 'č' , 'é' , 'è' , 'ê' , 'ë' , 'í' , 'ì' , 'î' , 'ï' , 'ú' , 'ñ' , 'ü' , 'ù' , 'û' , 'ß' , 'ď' , 'đ' , 'ē' , 'ĕ' , 'ė' , 'ę' , 'ě' , 'ĝ' , 'ğ' , 'ġ' , 'ģ' , 'ĥ' , 'ħ' , 'ĩ' , 'ī' , 'ĭ' , 'į' , 'ı ' , 'ij' , 'ĵ' , 'ķ' , 'ĺ' , 'ļ' , 'ľ' , 'ŀ' , 'ł' , 'ń' , 'ņ' , 'ň' , 'ʼn' , 'ō' , 'ŏ' , 'ő' , 'ŕ' , 'ŗ' , 'ř' , 'ś' , 'ŝ' , 'ş' , 'š' , 'ţ' , 'ť' , 'ŧ' , 'ũ' , 'ū' , 'ŭ' , 'ů' , 'ű' , 'ų' , 'ŵ' , 'ŷ' , 'ź' , 'ż' , 'ž' , 'ſ ' , 'ƒ' , 'ơ' , 'ư' , 'ǎ' , 'ǐ' , 'ǒ' , 'ǔ' , 'ǖ' , 'ǘ' , 'ǚ' , 'ǜ' , 'ǻ' , 'ǽ' , 'ǿ' ),
array ( 'ae' , 'oe' , 'o' , 'o' , 'o' , 'oe' , 'o' , 'o' , 'y' , 'y' , 'y' , 'a' , 'a' , 'a' , 'a' , 'a' , 'a' , 'a' , 'a' , 'a' , 'c' , 'c' , 'c' , 'c' , 'c' , 'e' , 'e' , 'e' , 'e' , 'i' , 'i' , 'i' , 'i' , 'u' , 'n' , 'u' , 'u' , 'u' , 'es' , 'd' , 'd' , 'e' , 'e' , 'e' , 'e' , 'e' , 'g' , 'g' , 'g' , 'g' , 'h' , 'h' , 'i' , 'i' , 'i' , 'i' , 'i' , 'ij' , 'j' , 'k' , 'l' , 'l' , 'l' , 'l' , 'l' , 'n' , 'n' , 'n' , 'n' , 'o' , 'o' , 'o' , 'r' , 'r' , 'r' , 's' , 's' , 's' , 's' , 't' , 't' , 't' , 'u' , 'u' , 'u' , 'u' , 'u' , 'u' , 'w' , 'y' , 'z' , 'z' , 'z' , 's' , 'f' , 'o' , 'u' , 'a' , 'i' , 'o' , 'u' , 'u' , 'u' , 'u' , 'u' , 'a' , 'ae' , 'oe' ),
2015-03-12 14:38:10 +01:00
$input );
// Replace everything else
$input = preg_replace ( '/[^a-z0-9]/' , '-' , $input );
// Prevent double hyphen
$input = preg_replace ( '/-{2,}/' , '-' , $input );
// Prevent hyphen in beginning or end
2015-03-13 15:17:19 +01:00
$input = trim ( $input , '-' );
// Prevent to long slug
if ( strlen ( $input ) > 91 ) {
2015-09-15 09:26:58 +02:00
$input = substr ( $input , 0 , 92 );
2015-03-13 15:17:19 +01:00
}
2015-03-12 14:38:10 +01:00
2015-03-13 15:17:19 +01:00
// Prevent empty slug
2015-03-12 14:38:10 +01:00
if ( $input === '' ) {
$input = 'interactive' ;
}
return $input ;
}
2015-06-15 14:57:51 +02:00
/**
2016-02-25 13:46:05 +01:00
* Makes it easier to print response when AJAX request succeeds .
2015-06-15 14:57:51 +02:00
*
2016-02-25 13:46:05 +01:00
* @ param mixed $data
* @ since 1.6 . 0
2015-06-15 14:57:51 +02:00
*/
2017-03-11 18:43:08 +01:00
public static function ajaxSuccess ( $data = NULL , $only_data = FALSE ) {
2016-02-25 13:46:05 +01:00
$response = array (
'success' => TRUE
);
2016-03-10 16:33:53 +01:00
if ( $data !== NULL ) {
2016-02-25 13:46:05 +01:00
$response [ 'data' ] = $data ;
2017-03-11 18:43:08 +01:00
// Pass data flatly to support old methods
if ( $only_data ) {
$response = $data ;
}
2015-06-15 14:57:51 +02:00
}
2016-02-25 13:46:05 +01:00
self :: printJson ( $response );
}
2015-06-22 11:22:49 +02:00
2016-02-25 13:46:05 +01:00
/**
* Makes it easier to print response when AJAX request fails .
* Will exit after printing error .
*
2017-03-11 18:43:08 +01:00
* @ param string $message A human readable error message
* @ param string $error_code An machine readable error code that a client
* should be able to interpret
* @ param null | int $status_code Http response code
2016-02-25 13:46:05 +01:00
* @ since 1.6 . 0
*/
2017-03-11 18:43:08 +01:00
public static function ajaxError ( $message = NULL , $error_code = NULL , $status_code = NULL ) {
2016-02-25 13:46:05 +01:00
$response = array (
'success' => FALSE
);
if ( $message !== NULL ) {
$response [ 'message' ] = $message ;
2015-06-22 11:22:49 +02:00
}
2017-02-24 16:34:16 +01:00
if ( $error_code !== NULL ) {
2017-03-02 14:39:36 +01:00
$response [ 'errorCode' ] = $error_code ;
2017-02-24 16:34:16 +01:00
}
2017-03-11 18:43:08 +01:00
self :: printJson ( $response , $status_code );
2016-02-25 13:46:05 +01:00
}
2015-06-22 11:22:49 +02:00
2016-02-25 13:46:05 +01:00
/**
* Print JSON headers with UTF - 8 charset and json encode response data .
* Makes it easier to respond using JSON .
*
* @ param mixed $data
2017-03-11 18:43:08 +01:00
* @ param null | int $status_code Http response code
2016-02-25 13:46:05 +01:00
*/
2017-03-11 18:43:08 +01:00
private static function printJson ( $data , $status_code = NULL ) {
2016-02-25 13:46:05 +01:00
header ( 'Cache-Control: no-cache' );
header ( 'Content-type: application/json; charset=utf-8' );
print json_encode ( $data );
}
/**
* Get a new H5P security token for the given action .
*
* @ param string $action
* @ return string token
*/
public static function createToken ( $action ) {
if ( ! isset ( $_SESSION [ 'h5p_token' ])) {
// Create an unique key which is used to create action tokens for this session.
$_SESSION [ 'h5p_token' ] = uniqid ();
2015-06-15 14:57:51 +02:00
}
2016-02-25 13:46:05 +01:00
// Timefactor
$time_factor = self :: getTimeFactor ();
// Create and return token
return substr ( hash ( 'md5' , $action . $time_factor . $_SESSION [ 'h5p_token' ]), - 16 , 13 );
}
/**
* Create a time based number which is unique for each 12 hour .
* @ return int
*/
private static function getTimeFactor () {
return ceil ( time () / ( 86400 / 2 ));
}
/**
* Verify if the given token is valid for the given action .
*
* @ param string $action
* @ param string $token
* @ return boolean valid token
*/
public static function validToken ( $action , $token ) {
$time_factor = self :: getTimeFactor ();
return $token === substr ( hash ( 'md5' , $action . $time_factor . $_SESSION [ 'h5p_token' ]), - 16 , 13 ) || // Under 12 hours
$token === substr ( hash ( 'md5' , $action . ( $time_factor - 1 ) . $_SESSION [ 'h5p_token' ]), - 16 , 13 ); // Between 12-24 hours
2015-06-15 14:57:51 +02:00
}
2017-02-22 14:50:45 +01:00
2017-02-27 14:15:17 +01:00
/**
* Update content type cache
*
* @ param object $postData Data sent to the hub
*
* @ return bool | object Returns endpoint data if found , otherwise FALSE
*/
2017-02-24 17:00:43 +01:00
public function updateContentTypeCache ( $postData = NULL ) {
2017-02-22 14:50:45 +01:00
$interface = $this -> h5pF ;
2017-02-23 11:33:39 +01:00
2017-04-25 16:19:53 +02:00
// Make sure data is sent!
if ( ! isset ( $postData ) || ! isset ( $postData [ 'uuid' ])) {
return $this -> fetchLibrariesMetadata ();
2017-02-23 11:33:39 +01:00
}
2017-02-24 16:06:55 +01:00
$postData [ 'current_cache' ] = $this -> h5pF -> getOption ( 'content_type_cache_updated_at' , 0 );
2017-03-02 14:21:11 +01:00
$protocol = ( extension_loaded ( 'openssl' ) ? 'https' : 'http' );
2017-03-12 17:56:36 +01:00
$endpoint = H5PHubEndpoints :: CONTENT_TYPES ;
2017-03-02 14:25:30 +01:00
$data = $interface -> fetchExternalData ( " { $protocol } :// { $endpoint } " , $postData );
2017-02-22 14:50:45 +01:00
2017-04-25 16:19:53 +02:00
if ( ! $this -> h5pF -> getOption ( 'hub_is_enabled' , TRUE )) {
2017-04-10 14:09:02 +02:00
return TRUE ;
}
2017-02-22 14:50:45 +01:00
// No data received
if ( ! $data ) {
$interface -> setErrorMessage (
2017-04-10 14:09:02 +02:00
$interface -> t ( " Couldn't communicate with the H5P Hub. Please try again later. " )
2017-02-22 14:50:45 +01:00
);
return FALSE ;
}
$json = json_decode ( $data );
// No libraries received
2017-02-27 16:01:08 +01:00
if ( ! isset ( $json -> contentTypes ) || empty ( $json -> contentTypes )) {
2017-02-22 14:50:45 +01:00
$interface -> setErrorMessage (
2017-04-10 14:09:02 +02:00
$interface -> t ( 'No content types were received from the H5P Hub. Please try again later.' )
2017-02-22 14:50:45 +01:00
);
return FALSE ;
}
// Replace content type cache
$interface -> replaceContentTypeCache ( $json );
// Inform of the changes and update timestamp
$interface -> setInfoMessage ( $interface -> t ( 'Library cache was successfully updated!' ));
2017-02-24 15:38:49 +01:00
$interface -> setOption ( 'content_type_cache_updated_at' , time ());
2017-02-23 11:33:39 +01:00
return $data ;
2017-02-22 14:50:45 +01:00
}
2017-02-27 14:15:17 +01:00
2017-02-21 17:37:01 +01:00
/**
* Check if the current server setup is valid and set error messages
*
2017-03-03 11:29:09 +01:00
* @ return object Setup object with errors and disable hub properties
2017-02-21 17:37:01 +01:00
*/
public function checkSetupErrorMessage () {
2017-03-03 11:29:09 +01:00
$setup = ( object ) array (
'errors' => array (),
'disable_hub' => FALSE
);
2017-02-21 14:01:49 +01:00
2017-02-21 17:37:01 +01:00
if ( ! class_exists ( 'ZipArchive' )) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] = $this -> h5pF -> t ( 'Your PHP version does not support ZipArchive.' );
$setup -> disable_hub = TRUE ;
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-21 17:37:01 +01:00
if ( ! extension_loaded ( 'mbstring' )) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] = $this -> h5pF -> t (
2017-03-28 13:32:30 +02:00
'The mbstring PHP extension is not loaded. H5P needs this to function properly'
2017-03-03 11:29:09 +01:00
);
$setup -> disable_hub = TRUE ;
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-21 17:37:01 +01:00
// Check php version >= 5.2
$php_version = explode ( '.' , phpversion ());
if ( $php_version [ 0 ] < 5 || ( $php_version [ 0 ] === 5 && $php_version [ 1 ] < 2 )) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] = $this -> h5pF -> t ( 'Your PHP version is outdated. H5P requires version 5.2 to function properly. Version 5.6 or later is recommended.' );
$setup -> disable_hub = TRUE ;
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-23 14:20:56 +01:00
// Check write access
if ( ! $this -> fs -> hasWriteAccess ()) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] = $this -> h5pF -> t ( 'A problem with the server write access was detected. Please make sure that your server can write to your data folder.' );
$setup -> disable_hub = TRUE ;
2017-02-23 14:20:56 +01:00
}
$max_upload_size = self :: returnBytes ( ini_get ( 'upload_max_filesize' ));
$max_post_size = self :: returnBytes ( ini_get ( 'post_max_size' ));
2017-02-21 17:37:01 +01:00
$byte_threshold = 5000000 ; // 5MB
2017-02-23 14:20:56 +01:00
if ( $max_upload_size < $byte_threshold ) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] =
2017-03-03 14:47:15 +01:00
$this -> h5pF -> t ( 'Your PHP max upload size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB.' , array ( '%number' => number_format ( $max_upload_size / 1024 / 1024 , 2 , '.' , ' ' )));
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-23 14:20:56 +01:00
if ( $max_post_size < $byte_threshold ) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] =
2017-03-03 14:47:15 +01:00
$this -> h5pF -> t ( 'Your PHP max post size is quite small. With your current setup, you may not upload files larger than %number MB. This might be a problem when trying to upload H5Ps, images and videos. Please consider to increase it to more than 5MB' , array ( '%number' => number_format ( $max_upload_size / 1024 / 1024 , 2 , '.' , ' ' )));
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-23 14:20:56 +01:00
if ( $max_upload_size > $max_post_size ) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] =
2017-02-23 14:20:56 +01:00
$this -> h5pF -> t ( 'Your PHP max upload size is bigger than your max post size. This is known to cause issues in some installations.' );
}
2017-02-21 17:37:01 +01:00
// Check SSL
2017-02-22 18:29:21 +01:00
if ( ! extension_loaded ( 'openssl' )) {
2017-03-03 11:29:09 +01:00
$setup -> errors [] =
2017-02-22 11:14:35 +01:00
$this -> h5pF -> t ( 'Your server does not have SSL enabled. SSL should be enabled to ensure a secure connection with the H5P hub.' );
2017-03-03 11:29:09 +01:00
$setup -> disable_hub = TRUE ;
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-03-03 11:29:09 +01:00
return $setup ;
2017-02-21 17:37:01 +01:00
}
2017-02-21 14:01:49 +01:00
2017-02-21 17:37:01 +01:00
/**
* Check that all H5P requirements for the server setup is met .
*/
public function checkSetupForRequirements () {
2017-03-03 11:29:09 +01:00
$setup = $this -> checkSetupErrorMessage ();
2017-02-22 11:14:35 +01:00
2017-03-03 11:29:09 +01:00
$this -> h5pF -> setOption ( 'hub_is_enabled' , ! $setup -> disable_hub );
if ( ! empty ( $setup -> errors )) {
foreach ( $setup -> errors as $err ) {
2017-02-22 11:14:35 +01:00
$this -> h5pF -> setErrorMessage ( $err );
}
2017-03-03 11:29:09 +01:00
}
2017-02-21 17:37:01 +01:00
2017-03-03 11:29:09 +01:00
if ( $setup -> disable_hub ) {
2017-02-22 11:14:35 +01:00
// Inform how to re-enable hub
2017-02-21 17:37:01 +01:00
$this -> h5pF -> setErrorMessage (
$this -> h5pF -> t ( 'H5P hub communication has been disabled because one or more H5P requirements failed.' )
);
$this -> h5pF -> setErrorMessage (
$this -> h5pF -> t ( 'When you have revised your server setup you may re-enable H5P hub communication in H5P Settings.' )
);
}
}
/**
* Return bytes from php_ini string value
*
* @ param string $val
*
* @ return int | string
*/
public static function returnBytes ( $val ) {
2017-05-29 11:18:12 +02:00
$val = trim ( $val );
2017-02-21 17:37:01 +01:00
$last = strtolower ( $val [ strlen ( $val ) - 1 ]);
2017-05-29 11:18:12 +02:00
$bytes = ( int ) $val ;
2017-02-21 17:37:01 +01:00
switch ( $last ) {
case 'g' :
2017-05-29 11:18:12 +02:00
$bytes *= 1024 ;
2017-02-21 17:37:01 +01:00
case 'm' :
2017-05-29 11:18:12 +02:00
$bytes *= 1024 ;
2017-02-21 17:37:01 +01:00
case 'k' :
2017-05-29 11:18:12 +02:00
$bytes *= 1024 ;
2017-02-21 17:37:01 +01:00
}
2017-05-29 11:18:12 +02:00
return $bytes ;
2017-02-21 14:01:49 +01:00
}
2017-02-28 13:05:11 +01:00
/**
* Check if the current user has permission to update and install new
* libraries .
*
* @ param bool [ $set ] Optional , sets the permission
* @ return bool
*/
public function mayUpdateLibraries ( $set = null ) {
static $can ;
if ( $set !== null ) {
// Use value set
$can = $set ;
}
if ( $can === null ) {
// Ask our framework
$can = $this -> h5pF -> mayUpdateLibraries ();
}
return $can ;
}
2014-11-01 17:05:15 +01:00
}
2014-10-10 13:56:48 +02:00
2013-07-05 17:35:59 +02:00
/**
* Functions for validating basic types from H5P library semantics .
2016-04-13 06:01:15 +02:00
* @ property bool allowedStyles
2013-07-05 17:35:59 +02:00
*/
class H5PContentValidator {
public $h5pF ;
public $h5pC ;
2015-01-30 10:59:40 +01:00
private $typeMap , $libraries , $dependencies , $nextWeight ;
2017-04-25 16:19:53 +02:00
private static $allowed_styleable_tags = array ( 'span' , 'p' , 'div' , 'h1' , 'h2' , 'h3' , 'td' );
2013-07-05 17:35:59 +02:00
/**
* Constructor for the H5PContentValidator
*
* @ param object $H5PFramework
* The frameworks implementation of the H5PFrameworkInterface
* @ param object $H5PCore
* The main H5PCore instance
*/
public function __construct ( $H5PFramework , $H5PCore ) {
$this -> h5pF = $H5PFramework ;
$this -> h5pC = $H5PCore ;
$this -> typeMap = array (
'text' => 'validateText' ,
'number' => 'validateNumber' ,
'boolean' => 'validateBoolean' ,
'list' => 'validateList' ,
'group' => 'validateGroup' ,
2013-07-10 09:59:35 +02:00
'file' => 'validateFile' ,
2013-07-05 17:35:59 +02:00
'image' => 'validateImage' ,
'video' => 'validateVideo' ,
'audio' => 'validateAudio' ,
'select' => 'validateSelect' ,
'library' => 'validateLibrary' ,
);
2015-01-30 10:59:40 +01:00
$this -> nextWeight = 1 ;
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
// Keep track of the libraries we load to avoid loading it multiple times.
$this -> libraries = array ();
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
// Keep track of all dependencies for the given content.
$this -> dependencies = array ();
2013-07-05 17:35:59 +02:00
}
/**
2016-04-13 06:01:15 +02:00
* Get the flat dependency tree .
2014-10-10 13:56:48 +02:00
*
2014-05-27 10:55:46 +02:00
* @ return array
2013-07-05 17:35:59 +02:00
*/
2014-05-27 10:55:46 +02:00
public function getDependencies () {
return $this -> dependencies ;
2013-07-05 17:35:59 +02:00
}
2014-10-10 13:56:48 +02:00
2013-07-05 17:35:59 +02:00
/**
* Validate given text value against text semantics .
2016-04-13 06:01:15 +02:00
* @ param $text
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateText ( & $text , $semantics ) {
2013-07-24 18:01:07 +02:00
if ( ! is_string ( $text )) {
$text = '' ;
}
2013-07-11 14:36:31 +02:00
if ( isset ( $semantics -> tags )) {
// Not testing for empty array allows us to use the 4 defaults without
// specifying them in semantics.
$tags = array_merge ( array ( 'div' , 'span' , 'p' , 'br' ), $semantics -> tags );
2013-07-10 09:59:35 +02:00
// Add related tags for table etc.
2013-07-11 13:12:17 +02:00
if ( in_array ( 'table' , $tags )) {
2013-07-10 09:59:35 +02:00
$tags = array_merge ( $tags , array ( 'tr' , 'td' , 'th' , 'colgroup' , 'thead' , 'tbody' , 'tfoot' ));
}
2013-07-11 14:36:31 +02:00
if ( in_array ( 'b' , $tags ) && ! in_array ( 'strong' , $tags )) {
2013-07-10 09:59:35 +02:00
$tags [] = 'strong' ;
}
2013-07-11 14:36:31 +02:00
if ( in_array ( 'i' , $tags ) && ! in_array ( 'em' , $tags )) {
2013-07-10 09:59:35 +02:00
$tags [] = 'em' ;
}
2013-07-11 14:36:31 +02:00
if ( in_array ( 'ul' , $tags ) || in_array ( 'ol' , $tags ) && ! in_array ( 'li' , $tags )) {
2013-07-10 09:59:35 +02:00
$tags [] = 'li' ;
}
2013-11-08 18:46:47 +01:00
if ( in_array ( 'del' , $tags ) || in_array ( 'strike' , $tags ) && ! in_array ( 's' , $tags )) {
$tags [] = 's' ;
}
2015-05-08 16:06:04 +02:00
2015-05-11 14:15:16 +02:00
// Determine allowed style tags
$stylePatterns = array ();
2016-07-15 15:33:11 +02:00
// All styles must be start to end patterns (^...$)
2015-05-11 14:15:16 +02:00
if ( isset ( $semantics -> font )) {
if ( isset ( $semantics -> font -> size ) && $semantics -> font -> size ) {
$stylePatterns [] = '/^font-size: *[0-9.]+(em|px|%) *;?$/i' ;
}
if ( isset ( $semantics -> font -> family ) && $semantics -> font -> family ) {
$stylePatterns [] = '/^font-family: *[a-z0-9," ]+;?$/i' ;
}
if ( isset ( $semantics -> font -> color ) && $semantics -> font -> color ) {
$stylePatterns [] = '/^color: *(#[a-f0-9]{3}[a-f0-9]{3}?|rgba?\([0-9, ]+\)) *;?$/i' ;
}
if ( isset ( $semantics -> font -> background ) && $semantics -> font -> background ) {
$stylePatterns [] = '/^background-color: *(#[a-f0-9]{3}[a-f0-9]{3}?|rgba?\([0-9, ]+\)) *;?$/i' ;
}
2016-03-09 11:18:21 +01:00
if ( isset ( $semantics -> font -> spacing ) && $semantics -> font -> spacing ) {
$stylePatterns [] = '/^letter-spacing: *[0-9.]+(em|px|%) *;?$/i' ;
}
if ( isset ( $semantics -> font -> height ) && $semantics -> font -> height ) {
$stylePatterns [] = '/^line-height: *[0-9.]+(em|px|%|) *;?$/i' ;
}
2015-05-11 14:15:16 +02:00
}
2016-04-13 06:01:15 +02:00
// Alignment is allowed for all wysiwyg texts
2015-09-09 13:28:16 +02:00
$stylePatterns [] = '/^text-align: *(center|left|right);?$/i' ;
2013-07-05 17:35:59 +02:00
// Strip invalid HTML tags.
2015-05-11 14:15:16 +02:00
$text = $this -> filter_xss ( $text , $tags , $stylePatterns );
2013-07-05 17:35:59 +02:00
}
else {
// Filter text to plain text.
2013-07-17 15:11:37 +02:00
$text = htmlspecialchars ( $text , ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-05 17:35:59 +02:00
}
2013-07-11 14:36:31 +02:00
2013-07-08 15:28:45 +02:00
// Check if string is within allowed length
if ( isset ( $semantics -> maxLength )) {
2016-06-08 11:28:07 +02:00
if ( ! extension_loaded ( 'mbstring' )) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The mbstring PHP extension is not loaded. H5P need this to function properly' ), 'error' );
}
else {
$text = mb_substr ( $text , 0 , $semantics -> maxLength );
}
2013-07-08 15:28:45 +02:00
}
2013-07-11 14:36:31 +02:00
2013-07-08 15:28:45 +02:00
// Check if string is according to optional regexp in semantics
2015-05-20 10:55:02 +02:00
if ( ! ( $text === '' && isset ( $semantics -> optional ) && $semantics -> optional ) && isset ( $semantics -> regexp )) {
2013-11-26 14:40:38 +01:00
// Escaping '/' found in patterns, so that it does not break regexp fencing.
2014-02-11 13:47:24 +01:00
$pattern = '/' . str_replace ( '/' , '\\/' , $semantics -> regexp -> pattern ) . '/' ;
2013-07-08 15:28:45 +02:00
$pattern .= isset ( $semantics -> regexp -> modifiers ) ? $semantics -> regexp -> modifiers : '' ;
if ( preg_match ( $pattern , $text ) === 0 ) {
// Note: explicitly ignore return value FALSE, to avoid removing text
// if regexp is invalid...
2014-03-26 08:43:29 +01:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Provided string is not valid according to regexp in semantics. (value: "%value", regexp: "%regexp")' , array ( '%value' => $text , '%regexp' => $pattern )));
2013-07-08 15:28:45 +02:00
$text = '' ;
}
}
2013-07-05 17:35:59 +02:00
}
2013-07-15 16:25:10 +02:00
2013-07-13 22:25:18 +02:00
/**
* Validates content files
*
* @ param string $contentPath
2016-04-13 06:01:15 +02:00
* The path containing content files to validate .
* @ param bool $isLibrary
* @ return bool TRUE if all files are valid
* TRUE if all files are valid
* FALSE if one or more files fail validation . Error message should be set accordingly by validator .
2013-07-13 22:25:18 +02:00
*/
public function validateContentFiles ( $contentPath , $isLibrary = FALSE ) {
2014-10-21 15:42:47 +02:00
if ( $this -> h5pC -> disableFileCheck === TRUE ) {
2014-11-03 07:02:45 +01:00
return TRUE ;
2014-10-21 15:42:47 +02:00
}
2013-07-13 22:25:18 +02:00
// Scan content directory for files, recurse into sub directories.
$files = array_diff ( scandir ( $contentPath ), array ( '.' , '..' ));
$valid = TRUE ;
$whitelist = $this -> h5pF -> getWhitelist ( $isLibrary , H5PCore :: $defaultContentWhitelist , H5PCore :: $defaultLibraryWhitelistExtras );
$wl_regex = '/\.(' . preg_replace ( '/ +/i' , '|' , preg_quote ( $whitelist )) . ')$/i' ;
foreach ( $files as $file ) {
$filePath = $contentPath . DIRECTORY_SEPARATOR . $file ;
if ( is_dir ( $filePath )) {
$valid = $this -> validateContentFiles ( $filePath , $isLibrary ) && $valid ;
}
else {
// Snipped from drupal 6 "file_validate_extensions". Using own code
// to avoid 1. creating a file-like object just to test for the known
// file name, 2. testing against a returned error array that could
// never be more than 1 element long anyway, 3. recreating the regex
// for every file.
2016-06-08 11:28:07 +02:00
if ( ! extension_loaded ( 'mbstring' )) {
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'The mbstring PHP extension is not loaded. H5P need this to function properly' ), 'error' );
$valid = FALSE ;
}
else if ( ! preg_match ( $wl_regex , mb_strtolower ( $file ))) {
2013-07-13 22:25:18 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'File "%filename" not allowed. Only files with the following extensions are allowed: %files-allowed.' , array ( '%filename' => $file , '%files-allowed' => $whitelist )), 'error' );
$valid = FALSE ;
}
}
}
return $valid ;
}
2013-07-15 16:25:10 +02:00
2013-07-05 17:35:59 +02:00
/**
* Validate given value against number semantics
2016-04-13 06:01:15 +02:00
* @ param $number
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateNumber ( & $number , $semantics ) {
// Validate that $number is indeed a number
if ( ! is_numeric ( $number )) {
$number = 0 ;
}
2013-07-08 15:28:45 +02:00
// Check if number is within valid bounds. Move within bounds if not.
if ( isset ( $semantics -> min ) && $number < $semantics -> min ) {
$number = $semantics -> min ;
}
if ( isset ( $semantics -> max ) && $number > $semantics -> max ) {
$number = $semantics -> max ;
}
// Check if number is within allowed bounds even if step value is set.
if ( isset ( $semantics -> step )) {
2016-04-13 06:01:15 +02:00
$testNumber = $number - ( isset ( $semantics -> min ) ? $semantics -> min : 0 );
$rest = $testNumber % $semantics -> step ;
2013-07-08 15:28:45 +02:00
if ( $rest !== 0 ) {
$number -= $rest ;
}
}
// Check if number has proper number of decimals.
if ( isset ( $semantics -> decimals )) {
$number = round ( $number , $semantics -> decimals );
}
2013-07-05 17:35:59 +02:00
}
/**
* Validate given value against boolean semantics
2016-04-13 06:01:15 +02:00
* @ param $bool
* @ return bool
2013-07-05 17:35:59 +02:00
*/
2016-04-13 06:01:15 +02:00
public function validateBoolean ( & $bool ) {
return is_bool ( $bool );
2013-07-05 17:35:59 +02:00
}
2016-04-13 06:01:15 +02:00
/**
2013-07-05 17:35:59 +02:00
* Validate select values
2016-04-13 06:01:15 +02:00
* @ param $select
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateSelect ( & $select , $semantics ) {
2016-08-29 15:54:48 +02:00
$optional = isset ( $semantics -> optional ) && $semantics -> optional ;
2013-07-15 15:07:53 +02:00
$strict = FALSE ;
if ( isset ( $semantics -> options ) && ! empty ( $semantics -> options )) {
// We have a strict set of options to choose from.
$strict = TRUE ;
$options = array ();
foreach ( $semantics -> options as $option ) {
$options [ $option -> value ] = TRUE ;
}
}
2013-07-15 16:25:10 +02:00
if ( isset ( $semantics -> multiple ) && $semantics -> multiple ) {
2016-04-13 06:01:15 +02:00
// Multi-choice generates array of values. Test each one against valid
2013-07-15 16:25:10 +02:00
// options, if we are strict. First make sure we are working on an
// array.
if ( ! is_array ( $select )) {
$select = array ( $select );
}
2013-07-17 12:55:51 +02:00
foreach ( $select as $key => & $value ) {
2016-08-29 15:54:48 +02:00
if ( $strict && ! $optional && ! isset ( $options [ $value ])) {
2016-04-13 06:01:15 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid selected option in multi-select.' ));
2013-07-15 15:07:53 +02:00
unset ( $select [ $key ]);
}
else {
2013-07-17 15:11:37 +02:00
$select [ $key ] = htmlspecialchars ( $value , ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-15 15:07:53 +02:00
}
2013-07-08 14:56:48 +02:00
}
}
2013-07-15 15:07:53 +02:00
else {
2013-07-15 16:25:10 +02:00
// Single mode. If we get an array in here, we chop off the first
// element and use that instead.
if ( is_array ( $select )) {
$select = $select [ 0 ];
}
2016-08-29 15:54:48 +02:00
if ( $strict && ! $optional && ! isset ( $options [ $select ])) {
2013-07-15 15:07:53 +02:00
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'Invalid selected option in select.' ));
$select = $semantics -> options [ 0 ] -> value ;
}
2013-07-17 15:11:37 +02:00
$select = htmlspecialchars ( $select , ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-05 17:35:59 +02:00
}
}
/**
2016-04-13 06:01:15 +02:00
* Validate given list value against list semantics .
2013-07-05 17:35:59 +02:00
* Will recurse into validating each item in the list according to the type .
2016-04-13 06:01:15 +02:00
* @ param $list
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateList ( & $list , $semantics ) {
$field = $semantics -> field ;
$function = $this -> typeMap [ $field -> type ];
2013-07-08 15:28:45 +02:00
// Check that list is not longer than allowed length. We do this before
2016-04-13 06:01:15 +02:00
// iterating to avoid unnecessary work.
2013-07-08 15:28:45 +02:00
if ( isset ( $semantics -> max )) {
array_splice ( $list , $semantics -> max );
}
2013-07-17 11:41:23 +02:00
if ( ! is_array ( $list )) {
$list = array ();
}
2013-07-08 15:28:45 +02:00
// Validate each element in list.
2013-07-17 11:41:23 +02:00
foreach ( $list as $key => & $value ) {
if ( ! is_int ( $key )) {
2015-04-22 10:44:16 +02:00
array_splice ( $list , $key , 1 );
2013-07-17 11:41:23 +02:00
continue ;
}
2013-07-05 17:35:59 +02:00
$this -> $function ( $value , $field );
2015-04-22 10:44:16 +02:00
if ( $value === NULL ) {
array_splice ( $list , $key , 1 );
}
}
if ( count ( $list ) === 0 ) {
$list = NULL ;
2013-07-05 17:35:59 +02:00
}
}
2016-04-13 06:01:15 +02:00
/**
* Validate a file like object , such as video , image , audio and file .
* @ param $file
* @ param $semantics
* @ param array $typeValidKeys
*/
private function _validateFilelike ( & $file , $semantics , $typeValidKeys = array ()) {
2015-09-18 12:55:58 +02:00
// Do not allow to use files from other content folders.
$matches = array ();
2016-07-29 14:15:00 +02:00
if ( preg_match ( $this -> h5pC -> relativePathRegExp , $file -> path , $matches )) {
$file -> path = $matches [ 5 ];
2015-09-18 12:55:58 +02:00
}
2013-07-11 15:17:26 +02:00
// Make sure path and mime does not have any special chars
2013-07-17 15:11:37 +02:00
$file -> path = htmlspecialchars ( $file -> path , ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-11 15:17:26 +02:00
if ( isset ( $file -> mime )) {
2013-07-17 15:11:37 +02:00
$file -> mime = htmlspecialchars ( $file -> mime , ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-11 15:17:26 +02:00
}
2013-07-10 09:59:35 +02:00
// Remove attributes that should not exist, they may contain JSON escape
// code.
2016-04-13 06:01:15 +02:00
$validKeys = array_merge ( array ( 'path' , 'mime' , 'copyright' ), $typeValidKeys );
2013-07-10 09:59:35 +02:00
if ( isset ( $semantics -> extraAttributes )) {
2016-04-13 06:01:15 +02:00
$validKeys = array_merge ( $validKeys , $semantics -> extraAttributes ); // TODO: Validate extraAttributes
2013-07-10 09:59:35 +02:00
}
2016-04-13 06:01:15 +02:00
$this -> filterParams ( $file , $validKeys );
2014-10-10 13:56:48 +02:00
2013-12-03 11:36:31 +01:00
if ( isset ( $file -> width )) {
$file -> width = intval ( $file -> width );
}
2014-10-10 13:56:48 +02:00
2013-12-03 11:36:31 +01:00
if ( isset ( $file -> height )) {
$file -> height = intval ( $file -> height );
}
2014-10-10 13:56:48 +02:00
2013-12-03 11:36:31 +01:00
if ( isset ( $file -> codecs )) {
$file -> codecs = htmlspecialchars ( $file -> codecs , ENT_QUOTES , 'UTF-8' , FALSE );
}
2014-10-10 13:56:48 +02:00
2013-12-03 11:36:31 +01:00
if ( isset ( $file -> quality )) {
if ( ! is_object ( $file -> quality ) || ! isset ( $file -> quality -> level ) || ! isset ( $file -> quality -> label )) {
unset ( $file -> quality );
}
else {
$this -> filterParams ( $file -> quality , array ( 'level' , 'label' ));
$file -> quality -> level = intval ( $file -> quality -> level );
$file -> quality -> label = htmlspecialchars ( $file -> quality -> label , ENT_QUOTES , 'UTF-8' , FALSE );
}
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( isset ( $file -> copyright )) {
2015-11-23 14:35:51 +01:00
$this -> validateGroup ( $file -> copyright , $this -> getCopyrightSemantics ());
2014-03-26 08:43:29 +01:00
}
2013-07-10 09:59:35 +02:00
}
2013-07-11 15:17:26 +02:00
/**
* Validate given file data
2016-04-13 06:01:15 +02:00
* @ param $file
* @ param $semantics
2013-07-11 15:17:26 +02:00
*/
public function validateFile ( & $file , $semantics ) {
$this -> _validateFilelike ( $file , $semantics );
}
2013-07-05 17:35:59 +02:00
/**
* Validate given image data
2016-04-13 06:01:15 +02:00
* @ param $image
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateImage ( & $image , $semantics ) {
2016-03-06 13:40:50 +01:00
$this -> _validateFilelike ( $image , $semantics , array ( 'width' , 'height' , 'originalImage' ));
2013-07-05 17:35:59 +02:00
}
/**
* Validate given video data
2016-04-13 06:01:15 +02:00
* @ param $video
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateVideo ( & $video , $semantics ) {
2013-07-17 12:55:51 +02:00
foreach ( $video as & $variant ) {
2013-12-03 11:36:31 +01:00
$this -> _validateFilelike ( $variant , $semantics , array ( 'width' , 'height' , 'codecs' , 'quality' ));
2013-07-05 17:35:59 +02:00
}
}
/**
* Validate given audio data
2016-04-13 06:01:15 +02:00
* @ param $audio
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateAudio ( & $audio , $semantics ) {
2013-07-17 12:55:51 +02:00
foreach ( $audio as & $variant ) {
2013-07-11 15:17:26 +02:00
$this -> _validateFilelike ( $variant , $semantics );
2013-07-05 17:35:59 +02:00
}
}
/**
* Validate given group value against group semantics .
* Will recurse into validating each group member .
2016-04-13 06:01:15 +02:00
* @ param $group
* @ param $semantics
* @ param bool $flatten
2013-07-05 17:35:59 +02:00
*/
2013-07-08 14:56:48 +02:00
public function validateGroup ( & $group , $semantics , $flatten = TRUE ) {
// Groups with just one field are compressed in the editor to only output
// the child content. (Exemption for fake groups created by
// "validateBySemantics" above)
2016-04-13 06:01:15 +02:00
$function = null ;
$field = null ;
2016-11-29 15:44:32 +01:00
$isSubContent = isset ( $semantics -> isSubContent ) && $semantics -> isSubContent === TRUE ;
if ( count ( $semantics -> fields ) == 1 && $flatten && ! $isSubContent ) {
2013-07-08 14:56:48 +02:00
$field = $semantics -> fields [ 0 ];
$function = $this -> typeMap [ $field -> type ];
$this -> $function ( $group , $field );
}
else {
foreach ( $group as $key => & $value ) {
2016-11-28 14:54:33 +01:00
// If subContentId is set, keep value
2016-11-29 15:44:32 +01:00
if ( $isSubContent && ( $key == 'subContentId' )){
2016-11-28 14:54:33 +01:00
continue ;
}
2013-07-08 14:56:48 +02:00
// Find semantics for name=$key
$found = FALSE ;
foreach ( $semantics -> fields as $field ) {
if ( $field -> name == $key ) {
2015-01-20 14:48:22 +01:00
if ( isset ( $semantics -> optional ) && $semantics -> optional ) {
$field -> optional = TRUE ;
}
2013-07-08 14:56:48 +02:00
$function = $this -> typeMap [ $field -> type ];
$found = TRUE ;
break ;
}
}
if ( $found ) {
2013-07-15 15:07:53 +02:00
if ( $function ) {
$this -> $function ( $value , $field );
2015-04-22 10:44:16 +02:00
if ( $value === NULL ) {
unset ( $group -> $key );
}
2013-07-15 15:07:53 +02:00
}
else {
// We have a field type in semantics for which we don't have a
// known validator.
$this -> h5pF -> setErrorMessage ( $this -> h5pF -> t ( 'H5P internal error: unknown content type "@type" in semantics. Removing content!' , array ( '@type' => $field -> type )));
unset ( $group -> $key );
}
2013-07-08 14:56:48 +02:00
}
else {
// If validator is not found, something exists in content that does
// not have a corresponding semantics field. Remove it.
2015-02-18 19:58:03 +01:00
// $this->h5pF->setErrorMessage($this->h5pF->t('H5P internal error: no validator exists for @key', array('@key' => $key)));
2013-07-08 14:56:48 +02:00
unset ( $group -> $key );
2013-07-05 17:35:59 +02:00
}
2013-07-09 10:10:32 +02:00
}
}
2015-01-20 14:48:22 +01:00
if ( ! ( isset ( $semantics -> optional ) && $semantics -> optional )) {
2015-05-27 10:11:01 +02:00
if ( $group === NULL ) {
// Error no value. Errors aren't printed...
return ;
}
2015-01-20 14:48:22 +01:00
foreach ( $semantics -> fields as $field ) {
if ( ! ( isset ( $field -> optional ) && $field -> optional )) {
// Check if field is in group.
if ( ! property_exists ( $group , $field -> name )) {
2015-02-18 19:58:03 +01:00
//$this->h5pF->setErrorMessage($this->h5pF->t('No value given for mandatory field ' . $field->name));
2015-01-20 14:48:22 +01:00
}
2013-07-09 10:10:32 +02:00
}
2013-07-05 17:35:59 +02:00
}
}
}
/**
* Validate given library value against library semantics .
2014-05-27 10:55:46 +02:00
* Check if provided library is within allowed options .
2013-07-05 17:35:59 +02:00
*
* Will recurse into validating the library ' s semantics too .
2016-04-13 06:01:15 +02:00
* @ param $value
* @ param $semantics
2013-07-05 17:35:59 +02:00
*/
public function validateLibrary ( & $value , $semantics ) {
2015-04-22 10:44:16 +02:00
if ( ! isset ( $value -> library )) {
$value = NULL ;
return ;
}
if ( ! in_array ( $value -> library , $semantics -> options )) {
2016-12-01 11:34:28 +01:00
$message = NULL ;
// Create an understandable error message:
2017-01-26 10:37:26 +01:00
$machineNameArray = explode ( ' ' , $value -> library );
$machineName = $machineNameArray [ 0 ];
2016-12-01 11:34:28 +01:00
foreach ( $semantics -> options as $semanticsLibrary ) {
2017-01-26 10:37:26 +01:00
$semanticsMachineNameArray = explode ( ' ' , $semanticsLibrary );
$semanticsMachineName = $semanticsMachineNameArray [ 0 ];
2016-12-01 11:34:28 +01:00
if ( $machineName === $semanticsMachineName ) {
// Using the wrong version of the library in the content
$message = $this -> h5pF -> t ( 'The version of the H5P library %machineName used in this content is not valid. Content contains %contentLibrary, but it should be %semanticsLibrary.' , array (
'%machineName' => $machineName ,
'%contentLibrary' => $value -> library ,
'%semanticsLibrary' => $semanticsLibrary
));
break ;
}
}
// Using a library in content that is not present at all in semantics
if ( $message === NULL ) {
$message = $this -> h5pF -> t ( 'The H5P library %library used in the content is not valid' , array (
'%library' => $value -> library
));
}
$this -> h5pF -> setErrorMessage ( $message );
2015-04-22 10:44:16 +02:00
$value = NULL ;
2014-05-27 10:55:46 +02:00
return ;
}
2014-10-10 13:56:48 +02:00
2014-05-27 10:55:46 +02:00
if ( ! isset ( $this -> libraries [ $value -> library ])) {
2016-04-13 06:01:15 +02:00
$libSpec = H5PCore :: libraryFromString ( $value -> library );
$library = $this -> h5pC -> loadLibrary ( $libSpec [ 'machineName' ], $libSpec [ 'majorVersion' ], $libSpec [ 'minorVersion' ]);
$library [ 'semantics' ] = $this -> h5pC -> loadLibrarySemantics ( $libSpec [ 'machineName' ], $libSpec [ 'majorVersion' ], $libSpec [ 'minorVersion' ]);
2014-05-27 10:55:46 +02:00
$this -> libraries [ $value -> library ] = $library ;
2013-07-05 17:35:59 +02:00
}
else {
2014-05-27 10:55:46 +02:00
$library = $this -> libraries [ $value -> library ];
}
$this -> validateGroup ( $value -> params , ( object ) array (
'type' => 'group' ,
'fields' => $library [ 'semantics' ],
), FALSE );
2016-04-13 06:01:15 +02:00
$validKeys = array ( 'library' , 'params' , 'subContentId' );
2014-05-27 10:55:46 +02:00
if ( isset ( $semantics -> extraAttributes )) {
2016-04-13 06:01:15 +02:00
$validKeys = array_merge ( $validKeys , $semantics -> extraAttributes );
2013-07-05 17:35:59 +02:00
}
2016-04-13 06:01:15 +02:00
$this -> filterParams ( $value , $validKeys );
2015-04-07 19:33:21 +02:00
if ( isset ( $value -> subContentId ) && ! preg_match ( '/^\{?[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\}?$/' , $value -> subContentId )) {
unset ( $value -> subContentId );
2015-03-21 16:45:38 +01:00
}
2015-02-11 14:40:01 +01:00
// Find all dependencies for this library
2016-04-13 06:01:15 +02:00
$depKey = 'preloaded-' . $library [ 'machineName' ];
if ( ! isset ( $this -> dependencies [ $depKey ])) {
$this -> dependencies [ $depKey ] = array (
2015-02-11 14:40:01 +01:00
'library' => $library ,
'type' => 'preloaded'
);
$this -> nextWeight = $this -> h5pC -> findLibraryDependencies ( $this -> dependencies , $library , $this -> nextWeight );
2016-04-13 06:01:15 +02:00
$this -> dependencies [ $depKey ][ 'weight' ] = $this -> nextWeight ++ ;
2015-02-11 14:40:01 +01:00
}
2013-07-05 17:35:59 +02:00
}
2013-07-12 14:49:37 +02:00
2013-07-17 11:41:23 +02:00
/**
* Check params for a whitelist of allowed properties
*
* @ param array / object $params
* @ param array $whitelist
*/
public function filterParams ( & $params , $whitelist ) {
foreach ( $params as $key => $value ) {
if ( ! in_array ( $key , $whitelist )) {
unset ( $params -> { $key });
}
}
}
2013-07-12 14:49:37 +02:00
// XSS filters copied from drupal 7 common.inc. Some modifications done to
// replace Drupal one-liner functions with corresponding flat PHP.
/**
* Filters HTML to prevent cross - site - scripting ( XSS ) vulnerabilities .
*
* Based on kses by Ulf Harnhammar , see http :// sourceforge . net / projects / kses .
* For examples of various XSS attacks , see : http :// ha . ckers . org / xss . html .
*
* This code does four things :
* - Removes characters and constructs that can trick browsers .
* - Makes sure all HTML entities are well - formed .
* - Makes sure all HTML tags and attributes are well - formed .
* - Makes sure no HTML tags contain URLs with a disallowed protocol ( e . g .
* javascript : ) .
*
* @ param $string
* The string with raw HTML in it . It will be stripped of everything that can
* cause an XSS attack .
2016-04-13 06:01:15 +02:00
* @ param array $allowed_tags
2013-07-12 14:49:37 +02:00
* An array of allowed tags .
*
2016-04-13 06:01:15 +02:00
* @ param bool $allowedStyles
* @ return mixed | string An XSS safe version of $string , or an empty string if $string is not
* An XSS safe version of $string , or an empty string if $string is not
* valid UTF - 8.
* @ ingroup sanitation
2013-07-12 14:49:37 +02:00
*/
2015-05-11 14:15:16 +02:00
private function filter_xss ( $string , $allowed_tags = array ( 'a' , 'em' , 'strong' , 'cite' , 'blockquote' , 'code' , 'ul' , 'ol' , 'li' , 'dl' , 'dt' , 'dd' ), $allowedStyles = FALSE ) {
2013-07-12 14:49:37 +02:00
if ( strlen ( $string ) == 0 ) {
return $string ;
}
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
// site scripting issues on Internet Explorer 6. (Line copied from
// drupal_validate_utf8)
if ( preg_match ( '/^./us' , $string ) != 1 ) {
return '' ;
}
2015-05-11 14:15:16 +02:00
$this -> allowedStyles = $allowedStyles ;
2013-07-12 14:49:37 +02:00
// Store the text format.
$this -> _filter_xss_split ( $allowed_tags , TRUE );
// Remove NULL characters (ignored by some browsers).
$string = str_replace ( chr ( 0 ), '' , $string );
// Remove Netscape 4 JS entities.
$string = preg_replace ( '%&\s*\{[^}]*(\}\s*;?|$)%' , '' , $string );
// Defuse all HTML entities.
$string = str_replace ( '&' , '&' , $string );
// Change back only well-formed entities in our whitelist:
// Decimal numeric entities.
$string = preg_replace ( '/&#([0-9]+;)/' , '&#\1' , $string );
// Hexadecimal numeric entities.
$string = preg_replace ( '/&#[Xx]0*((?:[0-9A-Fa-f]{2})+;)/' , '&#x\1' , $string );
// Named entities.
$string = preg_replace ( '/&([A-Za-z][A-Za-z0-9]*;)/' , '&\1' , $string );
return preg_replace_callback ( ' %
(
< ( ? = [ ^ a - zA - Z !/ ]) # a lone <
| # or
<!--.* ? --> # a comment
| # or
< [ ^> ] * ( >| $ ) # a string that starts with a <, up until the > or the end of the string
| # or
> # just a >
) % x ', array($this, ' _filter_xss_split ' ), $string );
}
/**
* Processes an HTML tag .
*
* @ param $m
* An array with various meaning depending on the value of $store .
* If $store is TRUE then the array contains the allowed tags .
* If $store is FALSE then the array has one element , the HTML tag to process .
2016-04-13 06:01:15 +02:00
* @ param bool $store
2013-07-12 14:49:37 +02:00
* Whether to store $m .
2016-04-13 06:01:15 +02:00
* @ return string If the element isn ' t allowed , an empty string . Otherwise , the cleaned up
* If the element isn ' t allowed , an empty string . Otherwise , the cleaned up
* version of the HTML element .
2013-07-12 14:49:37 +02:00
*/
private function _filter_xss_split ( $m , $store = FALSE ) {
static $allowed_html ;
if ( $store ) {
$allowed_html = array_flip ( $m );
2016-04-13 06:01:15 +02:00
return $allowed_html ;
2013-07-12 14:49:37 +02:00
}
$string = $m [ 1 ];
if ( substr ( $string , 0 , 1 ) != '<' ) {
// We matched a lone ">" character.
return '>' ;
}
elseif ( strlen ( $string ) == 1 ) {
// We matched a lone "<" character.
return '<' ;
}
2016-06-21 09:34:27 +02:00
if ( ! preg_match ( '%^<\s*(/\s*)?([a-zA-Z0-9\-]+)([^>]*)>?|(<!--.*?-->)$%' , $string , $matches )) {
2013-07-12 14:49:37 +02:00
// Seriously malformed.
return '' ;
}
$slash = trim ( $matches [ 1 ]);
$elem = & $matches [ 2 ];
2016-04-13 06:01:15 +02:00
$attrList = & $matches [ 3 ];
2013-07-12 14:49:37 +02:00
$comment = & $matches [ 4 ];
if ( $comment ) {
$elem = '!--' ;
}
if ( ! isset ( $allowed_html [ strtolower ( $elem )])) {
// Disallowed HTML element.
return '' ;
}
if ( $comment ) {
return $comment ;
}
if ( $slash != '' ) {
return " </ $elem > " ;
}
// Is there a closing XHTML slash at the end of the attributes?
2016-04-13 06:01:15 +02:00
$attrList = preg_replace ( '%(\s?)/\s*$%' , '\1' , $attrList , - 1 , $count );
2013-07-12 14:49:37 +02:00
$xhtml_slash = $count ? ' /' : '' ;
// Clean up attributes.
2015-09-09 13:28:16 +02:00
2016-04-13 06:01:15 +02:00
$attr2 = implode ( ' ' , $this -> _filter_xss_attributes ( $attrList , ( in_array ( $elem , self :: $allowed_styleable_tags ) ? $this -> allowedStyles : FALSE )));
2013-07-12 14:49:37 +02:00
$attr2 = preg_replace ( '/[<>]/' , '' , $attr2 );
$attr2 = strlen ( $attr2 ) ? ' ' . $attr2 : '' ;
return " < $elem $attr2 $xhtml_slash > " ;
}
/**
* Processes a string of HTML attributes .
*
2016-04-13 06:01:15 +02:00
* @ param $attr
* @ param array | bool | object $allowedStyles
* @ return array Cleaned up version of the HTML attributes .
* Cleaned up version of the HTML attributes .
2013-07-12 14:49:37 +02:00
*/
2015-05-11 14:15:16 +02:00
private function _filter_xss_attributes ( $attr , $allowedStyles = FALSE ) {
2016-04-13 06:01:15 +02:00
$attrArr = array ();
2013-07-12 14:49:37 +02:00
$mode = 0 ;
2016-04-13 06:01:15 +02:00
$attrName = '' ;
$skip = false ;
2013-07-12 14:49:37 +02:00
while ( strlen ( $attr ) != 0 ) {
// Was the last operation successful?
$working = 0 ;
switch ( $mode ) {
case 0 :
// Attribute name, href for instance.
if ( preg_match ( '/^([-a-zA-Z]+)/' , $attr , $match )) {
2016-04-13 06:01:15 +02:00
$attrName = strtolower ( $match [ 1 ]);
$skip = ( $attrName == 'style' || substr ( $attrName , 0 , 2 ) == 'on' );
2013-07-12 14:49:37 +02:00
$working = $mode = 1 ;
$attr = preg_replace ( '/^[-a-zA-Z]+/' , '' , $attr );
}
break ;
case 1 :
// Equals sign or valueless ("selected").
if ( preg_match ( '/^\s*=\s*/' , $attr )) {
$working = 1 ; $mode = 2 ;
$attr = preg_replace ( '/^\s*=\s*/' , '' , $attr );
break ;
}
if ( preg_match ( '/^\s+/' , $attr )) {
$working = 1 ; $mode = 0 ;
if ( ! $skip ) {
2016-04-13 06:01:15 +02:00
$attrArr [] = $attrName ;
2013-07-12 14:49:37 +02:00
}
$attr = preg_replace ( '/^\s+/' , '' , $attr );
}
break ;
case 2 :
// Attribute value, a URL after href= for instance.
if ( preg_match ( '/^"([^"]*)"(\s+|$)/' , $attr , $match )) {
2016-04-13 06:01:15 +02:00
if ( $allowedStyles && $attrName === 'style' ) {
2015-05-08 16:06:04 +02:00
// Allow certain styles
2015-05-11 14:15:16 +02:00
foreach ( $allowedStyles as $pattern ) {
2015-05-08 16:06:04 +02:00
if ( preg_match ( $pattern , $match [ 1 ])) {
2016-07-15 15:33:11 +02:00
// All patterns are start to end patterns, and CKEditor adds one span per style
2016-04-13 06:01:15 +02:00
$attrArr [] = 'style="' . $match [ 1 ] . '"' ;
2015-05-11 14:15:16 +02:00
break ;
2015-05-08 16:06:04 +02:00
}
}
break ;
}
2016-04-13 06:01:15 +02:00
$thisVal = $this -> filter_xss_bad_protocol ( $match [ 1 ]);
2013-07-12 14:49:37 +02:00
if ( ! $skip ) {
2016-04-13 06:01:15 +02:00
$attrArr [] = " $attrName = \" $thisVal\ " " ;
2013-07-12 14:49:37 +02:00
}
$working = 1 ;
$mode = 0 ;
$attr = preg_replace ( '/^"[^"]*"(\s+|$)/' , '' , $attr );
break ;
}
if ( preg_match ( " /^'([^']*)'( \ s+| $ )/ " , $attr , $match )) {
2016-04-13 06:01:15 +02:00
$thisVal = $this -> filter_xss_bad_protocol ( $match [ 1 ]);
2013-07-12 14:49:37 +02:00
if ( ! $skip ) {
2016-04-13 06:01:15 +02:00
$attrArr [] = " $attrName =' $thisVal ' " ;
2013-07-12 14:49:37 +02:00
}
$working = 1 ; $mode = 0 ;
$attr = preg_replace ( " /^'[^']*'( \ s+| $ )/ " , '' , $attr );
break ;
}
if ( preg_match ( " %^([^ \ s \" ']+)( \ s+| $ )% " , $attr , $match )) {
2016-04-13 06:01:15 +02:00
$thisVal = $this -> filter_xss_bad_protocol ( $match [ 1 ]);
2013-07-12 14:49:37 +02:00
if ( ! $skip ) {
2016-04-13 06:01:15 +02:00
$attrArr [] = " $attrName = \" $thisVal\ " " ;
2013-07-12 14:49:37 +02:00
}
$working = 1 ; $mode = 0 ;
$attr = preg_replace ( " %^[^ \ s \" ']+( \ s+| $ )% " , '' , $attr );
}
break ;
}
if ( $working == 0 ) {
// Not well formed; remove and try again.
$attr = preg_replace ( ' /
^
(
" [^ " ] * ( " | $ ) # - a string that starts with a double quote, up until the next double quote or the end of the string
| # or
\ ' [ ^ \ ' ] * ( \ ' | $ ) | # - a string that starts with a quote, up until the next quote or the end of the string
| # or
\S # - a non-whitespace character
) * # any number of the above three
\s * # any number of whitespaces
/ x ', ' ' , $attr );
$mode = 0 ;
}
}
// The attribute list ends with a valueless attribute like "selected".
if ( $mode == 1 && ! $skip ) {
2016-04-13 06:01:15 +02:00
$attrArr [] = $attrName ;
2013-07-12 14:49:37 +02:00
}
2016-04-13 06:01:15 +02:00
return $attrArr ;
2013-07-12 14:49:37 +02:00
}
2014-03-26 08:43:29 +01:00
// TODO: Remove Drupal related stuff in docs.
2013-07-12 14:49:37 +02:00
/**
* Processes an HTML attribute value and strips dangerous protocols from URLs .
2014-10-10 13:56:48 +02:00
*
2013-07-12 14:49:37 +02:00
* @ param $string
* The string with the attribute value .
2016-04-13 06:01:15 +02:00
* @ param bool $decode
2013-07-12 14:49:37 +02:00
* ( deprecated ) Whether to decode entities in the $string . Set to FALSE if the
* $string is in plain text , TRUE otherwise . Defaults to TRUE . This parameter
* is deprecated and will be removed in Drupal 8. To process a plain - text URI ,
* call _strip_dangerous_protocols () or check_url () instead .
2016-04-13 06:01:15 +02:00
* @ return string Cleaned up and HTML - escaped version of $string .
* Cleaned up and HTML - escaped version of $string .
2013-07-12 14:49:37 +02:00
*/
private function filter_xss_bad_protocol ( $string , $decode = TRUE ) {
// Get the plain text representation of the attribute value (i.e. its meaning).
// @todo Remove the $decode parameter in Drupal 8, and always assume an HTML
// string that needs decoding.
if ( $decode ) {
$string = html_entity_decode ( $string , ENT_QUOTES , 'UTF-8' );
}
2013-07-17 15:11:37 +02:00
return htmlspecialchars ( $this -> _strip_dangerous_protocols ( $string ), ENT_QUOTES , 'UTF-8' , FALSE );
2013-07-12 14:49:37 +02:00
}
/**
* Strips dangerous protocols ( e . g . 'javascript:' ) from a URI .
*
* This function must be called for all URIs within user - entered input prior
* to being output to an HTML attribute value . It is often called as part of
* check_url () or filter_xss (), but those functions return an HTML - encoded
* string , so this function can be called independently when the output needs to
* be a plain - text string for passing to t (), l (), drupal_attributes (), or
* another function that will call check_plain () separately .
*
* @ param $uri
* A plain - text URI that might contain dangerous protocols .
2016-04-13 06:01:15 +02:00
* @ return string A plain - text URI stripped of dangerous protocols . As with all plain - text
* A plain - text URI stripped of dangerous protocols . As with all plain - text
* strings , this return value must not be output to an HTML page without
* check_plain () being called on it . However , it can be passed to functions
* expecting plain - text strings .
2013-07-12 14:49:37 +02:00
* @ see check_url ()
*/
private function _strip_dangerous_protocols ( $uri ) {
static $allowed_protocols ;
if ( ! isset ( $allowed_protocols )) {
$allowed_protocols = array_flip ( array ( 'ftp' , 'http' , 'https' , 'mailto' ));
}
// Iteratively remove any invalid protocol found.
do {
$before = $uri ;
2016-04-13 06:01:15 +02:00
$colonPos = strpos ( $uri , ':' );
if ( $colonPos > 0 ) {
2013-07-12 14:49:37 +02:00
// We found a colon, possibly a protocol. Verify.
2016-04-13 06:01:15 +02:00
$protocol = substr ( $uri , 0 , $colonPos );
2013-07-12 14:49:37 +02:00
// If a colon is preceded by a slash, question mark or hash, it cannot
// possibly be part of the URL scheme. This must be a relative URL, which
// inherits the (safe) protocol of the base document.
if ( preg_match ( '![/?#]!' , $protocol )) {
break ;
}
// Check if this is a disallowed protocol. Per RFC2616, section 3.2.3
// (URI Comparison) scheme comparison must be case-insensitive.
if ( ! isset ( $allowed_protocols [ strtolower ( $protocol )])) {
2016-04-13 06:01:15 +02:00
$uri = substr ( $uri , $colonPos + 1 );
2013-07-12 14:49:37 +02:00
}
}
} while ( $before != $uri );
return $uri ;
}
2014-10-10 13:56:48 +02:00
2015-11-23 14:35:51 +01:00
public function getCopyrightSemantics () {
2014-03-26 08:43:29 +01:00
static $semantics ;
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
if ( $semantics === NULL ) {
$semantics = ( object ) array (
'name' => 'copyright' ,
'type' => 'group' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Copyright information' ),
2014-03-26 08:43:29 +01:00
'fields' => array (
( object ) array (
'name' => 'title' ,
'type' => 'text' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Title' ),
2014-03-26 08:43:29 +01:00
'placeholder' => 'La Gioconda' ,
'optional' => TRUE
),
( object ) array (
'name' => 'author' ,
'type' => 'text' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Author' ),
2014-03-26 08:43:29 +01:00
'placeholder' => 'Leonardo da Vinci' ,
'optional' => TRUE
),
( object ) array (
'name' => 'year' ,
'type' => 'text' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Year(s)' ),
2014-03-26 08:43:29 +01:00
'placeholder' => '1503 - 1517' ,
'optional' => TRUE
),
( object ) array (
'name' => 'source' ,
'type' => 'text' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Source' ),
2014-03-26 08:43:29 +01:00
'placeholder' => 'http://en.wikipedia.org/wiki/Mona_Lisa' ,
'optional' => true ,
'regexp' => ( object ) array (
'pattern' => '^http[s]?://.+' ,
'modifiers' => 'i'
)
),
( object ) array (
'name' => 'license' ,
'type' => 'select' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'License' ),
2014-03-26 08:43:29 +01:00
'default' => 'U' ,
'options' => array (
( object ) array (
'value' => 'U' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Undisclosed' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY-SA' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution-ShareAlike 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY-ND' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution-NoDerivs 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY-NC' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution-NonCommercial 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY-NC-SA' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution-NonCommercial-ShareAlike 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC BY-NC-ND' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'Attribution-NonCommercial-NoDerivs 4.0' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'GNU GPL' ,
2016-03-31 16:07:45 +02:00
'label' => $this -> h5pF -> t ( 'General Public License v3' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'PD' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Public Domain' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'ODC PDDL' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Public Domain Dedication and Licence' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'CC PDM' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Public Domain Mark' )
2014-03-26 08:43:29 +01:00
),
( object ) array (
'value' => 'C' ,
2015-11-23 14:35:51 +01:00
'label' => $this -> h5pF -> t ( 'Copyright' )
2014-03-26 08:43:29 +01:00
)
)
)
)
);
}
2014-10-10 13:56:48 +02:00
2014-03-26 08:43:29 +01:00
return $semantics ;
}
2013-07-05 17:35:59 +02:00
}