diff --git a/README.txt b/README.txt index 840b6e8..f6ff53d 100644 --- a/README.txt +++ b/README.txt @@ -5,6 +5,9 @@ the following interfaces in order for the h5p libraries to work: - TODO: Fill in here -In addition frameworks need to implement: +In addition frameworks need to do the following: - - TODO: Fill in here \ No newline at end of file + - Provide a form for uploading h5p packages. + - Place the uploaded h5p packages in a temporary directory + +See existing implementations for details. For instance the Drupal h5p module located on drupal.org/project/h5p \ No newline at end of file diff --git a/h5p.classes.php b/h5p.classes.php index b50a5c6..ecf9a42 100644 --- a/h5p.classes.php +++ b/h5p.classes.php @@ -1,14 +1,184 @@ h5pFramework = $h5pFramework; + $this->h5pF = $h5pFramework; + } + + public function validatePackage() { + // Requires PEAR + require_once 'Archive/Tar.php'; + + // Create a temporary dir to extract package in. + $tmp_dir = $this->h5pFramework->getTempDir(); + $tmp_path = $this->h5pFramework->getTempPath(); + + // Extract and then remove the package file. + $tar = new Archive_Tar($tmp_path, 'bz2'); + if (!$tar->extract($tmp_dir)) { + $this->setErrorMessage($this->t('The file you uploaded is not a valid HTML5 Pack.')); + $this->rRmdir($tmp_dir); + return; + } + unlink($tmp_path); + + // Process content and libraries + $contents = array(); + $tars = scandir($tmp_dir); + for ($i = 2, $s = count($tars); $i < $s; $i++) { + if (substr($tars[$i], -4) == '.tar') { + $tar_file = $tmp_dir . DIRECTORY_SEPARATOR . $tars[$i]; + $tar_name = substr($tars[$i], 0, -4); + $tar_id = md5_file($tar_file); + $tar_time = filemtime($tar_file); + + $content_path = $files_path . DIRECTORY_SEPARATOR . $tar_id; + + // Extract content to our files dir. + if (is_dir($content_path)) { + continue; // We already have this content/library. + } + + mkdir($content_path); + $tar = new Archive_Tar($tar_file); + if (!$tar->extract($content_path)) { + continue; // Wasn't a valid tar file. + } + + if (preg_match('/[^a-z0-9\-]/', $tar_name)) { + $this->setErrorMessage($this->t('Invalid name: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + + // Go to extracted stuff and check it. + $json = file_get_contents($content_path . DIRECTORY_SEPARATOR . 'index.js'); + if (!$json) { + $this->setErrorMessage($this->t('Could not find index.js file: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + + $content = json_decode($json); + if (!$content) { + $this->setErrorMessage($this->t('Invalid index.js file format: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + + if ($content->type != H5P_CONTENT && $content->type != H5P_LIBRARY) { + $this->setErrorMessage($this->t('Invalid content type: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + + if ($content->type == H5P_CONTENT) { + if (!isset($content->options)) { + $this->setErrorMessage($this->t('Missing start options: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + if (!isset($content->class)) { + $this->setErrorMessage($this->t('Missing start class: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + if (preg_match('/^[A-Z][A-Za-z0-9]*$/', $content->class)) { + $this->setErrorMessage($this->t('Invalid class name: %name', array('%name' => $content->class))); + $this->rRmdir($content_path); + continue; + } + $content->options = json_encode($content->options); + } + else { + if (!isset($content->js) || !empty($content->js)) { + $this->setErrorMessage($this->t('Library without any JS files: %name', array('%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + + // Check if JS files exist. + for ($j = 0, $l = count($content->js); $j < $l; $j++) { + $content->js[$j] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $content->js[$j]); + if (!file_exists($content_path . DIRECTORY_SEPARATOR . $content->js[$j])) { + $this->setErrorMessage($this->t('The JS file %file is missing from library: %name', array('%file' => $content->js[$j], '%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + } + + // Check if CSS files exist. + for ($j = 0, $l = count($content->css); $j < $l; $j++) { + $content->css[$j] = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $content->css[$j]); + if (!file_exists($content_path . DIRECTORY_SEPARATOR . $content->css[$j])) { + $this->setErrorMessage($this->t('The CSS file %file is missing from library: %name', array('%file' => $content->css[$j], '%name' => $tar_name))); + $this->rRmdir($content_path); + continue; + } + } + } + + $content->id = $tar_id; + $content->name = $tar_name; + $content->time = $tar_time; + + $contents[$content->id] = $content; + } + } + + + $this->rRmdir($tmp_dir); + + foreach ($contents as &$content) { + if ($content->type == H5P_CONTENT) { + // Check dependencies for content + $name = strtolower(preg_replace('/.+([A-Z])/', '-$0', $content->class)); + $missing_dependencies = h5p_find_missing_dependencies($contents, $name); + if ($missing_dependencies) { + $this->setErrorMessage($this->t('%name is missing the following dependencies: %dependencies', array('%name' => $content->name, '%dependencies' => implode(',', $missing_dependencies)))); + } + else { + // TODO: Insert stuff? + // TODO: Insert into database + // TODO: Insert all files into files table. + } + } + } + + // TODO: rmdir for libraries that doesn't have db records. + } + + /** + * Recursive function for removing directories. + * + * @param string $dir Directory. + * @return boolean Indicates if the directory existed. + */ + private function rRmdir($dir) { + if (is_dir($dir)) { + $files = scandir($dir); + for ($i = 2, $s = count($files); $i < $s; $i++) { + $file = $dir . DIRECTORY_SEPARATOR . $files[$i]; + if (!$this->rRmdir($file)) { + unlink($file); + } + } + rmdir($dir); + return TRUE; + } + else { + return FALSE; + } } } ?>