2012-11-21 14:14:07 +01:00
< ? php
interface h5pFramework {
2012-11-22 14:00:06 +01:00
public function setErrorMessage ( $message );
public function setInfoMessage ( $message );
public function t ( $message , $replacements );
public function getUploadedH5pDir ();
public function getTempPath ();
public function getUploadedH5pPath ();
2012-11-28 23:29:22 +01:00
public function isStoredLibrary ( $machineName , $minimumVersion );
2012-11-21 14:14:07 +01:00
}
2012-11-26 22:48:51 +01:00
class h5pValidator {
2012-11-22 14:00:06 +01:00
public $h5pF ;
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}$/' ,
'mainVersion' => '/^[0-9]{1,5}$/' ,
'language' => '/^[a-z]{1,5}$/' ,
'machineName' => '/^[a-z0-9\-]{1,255}$/' ,
2012-11-24 01:09:25 +01:00
'preloadedDependencies' => array (
2012-11-28 14:06:55 +01:00
'machineName' => '/^[a-z0-9\-]{1,255}$/' ,
'minimumVersion' => '/^[0-9]{1,5}$/' ,
2012-11-24 01:09:25 +01:00
),
2012-11-28 14:06:55 +01:00
'init' => '/^[$a-z_][0-9a-z_\.$]{1,254}$/i' ,
2012-11-24 01:09:25 +01:00
'embedTypes' => array ( 'iframe' , 'div' ),
);
private $h5pOptional = array (
2012-11-28 14:06:55 +01:00
'contentType' => '/^.{1,255}$/' ,
'utilization' => '/^.{1,}$/' ,
'subVersion' => '/^[0-9]{1,5}$/' ,
'author' => '/^.{1,255}$/' ,
'lisence' => '/^(iframe|div)$/' ,
2012-11-24 01:09:25 +01:00
'dynamicDependencies' => array (
2012-11-28 14:06:55 +01:00
'machineName' => '/^[a-z0-9\-]{1,255}$/' ,
'minimumVersion' => '/^[0-9]{1,5}$/' ,
2012-11-24 01:09:25 +01:00
),
2012-11-28 14:06:55 +01:00
'preloadedJs' => '/^(\\[a-z_\-\s0-9\.]+)+\.(?i)(js)$/' ,
'preloadedCss' => '/^(\\[a-z_\-\s0-9\.]+)+\.(?i)(js)$/' ,
'w' => '/^[0-9]{1,4}$/' ,
'h' => '/^[0-9]{1,4}$/' ,
'metaKeywords' => '/^.{1,}$/' ,
2012-11-28 14:45:54 +01:00
'metaDescription' => '/^.{1,}$/k' ,
2012-11-24 01:09:25 +01:00
);
2012-11-22 14:00:06 +01:00
2012-11-21 14:14:07 +01:00
public function __construct ( $h5pFramework ) {
2012-11-22 14:00:06 +01:00
$this -> h5pF = $h5pFramework ;
}
public function validatePackage () {
// Requires PEAR
require_once 'Archive/Tar.php' ;
// Create a temporary dir to extract package in.
2012-11-23 17:06:03 +01:00
$tmp_dir = $this -> h5pFramework -> getUploadedH5pDir ();
$tmp_path = $this -> h5pFramework -> getUploadedH5pPath ();
2012-11-22 14:00:06 +01:00
2012-11-28 15:21:34 +01:00
$valid = TRUE ;
2012-11-22 14:00:06 +01:00
// Extract and then remove the package file.
$tar = new Archive_Tar ( $tmp_path , 'bz2' );
if ( ! $tar -> extract ( $tmp_dir )) {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( 'The file you uploaded is not a valid HTML5 Pack.' ));
2012-11-22 14:00:06 +01:00
$this -> rRmdir ( $tmp_dir );
return ;
}
unlink ( $tmp_path );
// Process content and libraries
2012-11-28 15:31:55 +01:00
$libraries = array ();
2012-11-23 17:06:03 +01:00
$files = scandir ( $tmp_dir );
2012-11-22 14:00:06 +01:00
2012-11-23 17:06:03 +01:00
$json_exists = $image_exists = $content_exists = FALSE ;
foreach ( $files as $file ) {
if ( in_array ( $file , array ( '.' , '..' ))) {
continue ;
}
$file_path = $tmp_dir . DIRECTORY_SEPARATOR . $file ;
if ( strtolower ( $file ) == 'h5p.json' ) {
$json_exists = TRUE ;
2012-11-28 23:29:22 +01:00
// TODO: Validate this main h5p.json file
// TODO: Make sure preloadedDependencies isn't required in the libraries
2012-11-23 17:06:03 +01:00
}
2012-11-22 14:00:06 +01:00
2012-11-23 17:06:03 +01:00
elseif ( strtolower ( $file ) == 'h5p.jpg' ) {
$image_exists = TRUE ;
}
elseif ( $file == 'content' ) {
2012-11-28 15:21:34 +01:00
$json = file_get_contents ( $file_path . DIRECTORY_SEPARATOR . 'content.json' );
if ( ! $json ) {
$this -> h5pF -> setErrorMessage ( $this -> t ( 'Could not find content.json file' ));
$valid = FALSE ;
continue ;
}
$contentData = json_decode ( $json );
if ( ! $contentData ) {
$this -> h5pF -> setErrorMessage ( 'Invalid content.json file format. Json is required' );
$valid = FALSE ;
continue ;
}
2012-11-28 15:31:55 +01:00
$content_exists = TRUE ;
2012-11-28 15:21:34 +01:00
// In the future we might let the librarys provide validation functions for content.json
2012-11-23 17:06:03 +01:00
}
elseif ( strpos ( $file , '.' ) !== FALSE ) {
// Illegal file fond. This is ignored.
continue ;
}
2012-11-22 14:00:06 +01:00
2012-11-23 17:06:03 +01:00
else {
2012-11-28 15:21:34 +01:00
if ( preg_match ( '/[^a-z0-9\-]/' , $file ) === 0 ) {
$this -> h5pF -> setErrorMessage ( $this -> t ( 'Invalid library name: %name' , array ( '%name' => $file )));
$valid = FALSE ;
2012-11-22 14:00:06 +01:00
continue ;
}
2012-11-23 17:06:03 +01:00
$json = file_get_contents ( $file_path . DIRECTORY_SEPARATOR . 'h5p.json' );
2012-11-22 14:00:06 +01:00
if ( ! $json ) {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( 'Could not find h5p.json file: %name' , array ( '%name' => $file )));
$valid = FALSE ;
2012-11-22 14:00:06 +01:00
continue ;
}
2012-11-26 22:48:51 +01:00
$h5pData = json_decode ( $json );
if ( ! $h5pData ) {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( 'Invalid h5p.json file format: %name' , array ( '%name' => $file )));
$valid = FALSE ;
2012-11-22 14:00:06 +01:00
continue ;
}
2012-11-28 15:31:55 +01:00
$validLibrary = $this -> isValidH5pData ( $h5pData , $file ) && $valid ;
2012-11-26 22:48:51 +01:00
if ( isset ( $h5pData -> preloadedJs )) {
2012-11-28 15:31:55 +01:00
$validLibrary = $this -> isExcistingFiles ( $h5pData -> preloadedJs , $tmp_dir , $file ) && $validLibrary ;
2012-11-22 14:00:06 +01:00
}
2012-11-26 22:48:51 +01:00
if ( isset ( $h5pData -> preloadedCss )) {
2012-11-28 15:31:55 +01:00
$validLibrary = $this -> isExcistingFiles ( $h5pData -> preloadedCss , $tmp_dir , $file ) && $validLibrary ;
2012-11-22 14:00:06 +01:00
}
2012-11-28 15:31:55 +01:00
if ( $validLibrary ) {
2012-11-28 23:29:22 +01:00
$libraries [ $file ][ $h5pData [ 'mainVersion' ]] = $h5pData ;
2012-11-28 15:31:55 +01:00
}
$valid = $validLibrary && $valid ;
2012-11-22 14:00:06 +01:00
}
}
2012-11-28 15:31:55 +01:00
if ( $valid ) {
2012-11-28 23:29:22 +01:00
// TODO: Also validate the main h5p.json file
$missingLibraries = $this -> getMissingLibraries ( $libraries );
foreach ( $missingLibraries as $missing ) {
if ( $this -> h5pF -> isStoredLibrary ( $missing [ 'machineName' ], $missing [ 'minimumVersion' ])) {
unset ( $missingLibraries [ $missing [ 'machineName' ]]);
}
}
$valid = empty ( $missingLibraries ) && $valid ;
}
if ( ! $valid ) {
$this -> delTree ( $tmp_dir );
}
return $valid ;
}
private function getMissingLibraries ( $libraries ) {
$missing = array ();
foreach ( $libraries as $library ) {
if ( isset ( $library [ 'preloadedDependencies' ])) {
array_merge ( $missing , $this -> getMissingDependencies ( $library [ 'preloadedDependencies' ], $libraries ));
}
if ( isset ( $library [ 'dynamicDependencies' ])) {
array_merge ( $missing , $this -> getMissingDependencies ( $library [ 'dynamicDependencies' ], $libraries ));
}
2012-11-28 15:31:55 +01:00
}
2012-11-28 23:29:22 +01:00
return $missing ;
}
private function getMissingDependencies ( $dependencies , $libraries ) {
$missing = array ();
foreach ( $library [ 'preloadedDependencies' ] as $dependency ) {
if ( isset ( $libraries [ $dependency [ 'machineName' ]])) {
if ( $libraries [ $dependency [ 'machineName' ]][ 'minimumVersion' ] < $dependency [ 'minimumVersion' ]) {
$missing [ $dependency [ 'machineName' ]] = $dependency ;
}
}
else {
$missing [ $dependency [ 'machineName' ]] = $dependency ;
}
}
return $missing ;
2012-11-26 22:48:51 +01:00
}
private function isExcistingFiles ( $files , $tmp_dir , $library ) {
foreach ( $files as $file_path ) {
$path = str_replace ( array ( '/' , '\\' ), DIRECTORY_SEPARATOR , $file_path );
if ( ! file_exists ( $tmp_dir . DIRECTORY_SEPARATOR . $path )) {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( 'The JS file %file is missing from library: %name' , array ( '%file' => $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-28 15:21:34 +01:00
private function isValidH5pData ( $h5pData , $library_name ) {
$valid = $this -> isValidRequiredH5pData ( $h5pData , $this -> h5pRequired , $library_name );
$valid = $this -> isValidOptionalH5pData ( $h5pData , $this -> h5pOptional , $library_name ) && $valid ;
return $valid ;
2012-11-28 13:56:42 +01:00
}
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-11-28 15:21:34 +01:00
$valid = isValidRequirement ( $h5pData , $requirement , $library_name , $property_name ) && $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-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 )) {
// The requirement is a regexp, match it against the data
if ( is_string ( $h5pData )) {
if ( preg_match ( $requirement , $h5pData ) === 0 ) {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( " Ivalid data provided for %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
}
else {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( " Ivalid 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-11-28 15:21:34 +01:00
$valid = $this -> isValidRequiredH5pData ( $h5pData , $requirement , $library_name ) && $valid ;
2012-11-28 13:56:42 +01:00
}
else {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( " Ivalid data provided for %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
$valid = FALSE ;
2012-11-28 13:56:42 +01:00
}
}
else {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( " Can't read the property %property in %library " , array ( '%property' => $property_name , '%library' => $library_name )));
$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
}
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-11-28 15:21:34 +01:00
return 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-11-28 15:21:34 +01:00
$valid = validateRequirement ( $h5pData [ $required ], $requirement , $library_name , $required ) && $valid ;
2012-11-28 13:56:42 +01:00
}
else {
2012-11-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( 'The required property %property is missing from %library' , array ( '%property' => $required , '%library' => $library_name )));
$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
}
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-28 15:21:34 +01:00
$this -> h5pF -> setErrorMessage ( $this -> t ( 'Illegal option %option in %library' , array ( '%option' => $value , '%library' => $library_name )));
$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
}
2012-11-22 14:00:06 +01:00
/**
* Recursive function for removing directories .
*
* @ param string $dir Directory .
* @ return boolean Indicates if the directory existed .
*/
2012-11-28 23:29:22 +01:00
public static function delTree ( $dir ) {
$files = array_diff ( scandir ( $dir ), array ( '.' , '..' ));
foreach ( $files as $file ) {
( is_dir ( " $dir / $file " )) ? delTree ( " $dir / $file " ) : unlink ( " $dir / $file " );
}
return rmdir ( $dir );
}
2012-11-21 14:14:07 +01:00
}
?>