Skip to content
Snippets Groups Projects
Commit e659de53 authored by Frode Petterson's avatar Frode Petterson
Browse files

HFJ-13: Added uploading and saving of h5p files.

parent 40d7447d
Branches
No related tags found
No related merge requests found
files/tmp/*
files/content/*
files/libraries/*
*~
......@@ -4,6 +4,35 @@
xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
>
<TABLES>
<TABLE NAME="hvp" COMMENT="Activity data">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="hvp_contents" COMMENT="H5P Content Data">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="content" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Start params stored in JSON."/>
<FIELD NAME="embed_type" TYPE="char" LENGTH="127" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="type" TYPE="char" LENGTH="127" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="author" TYPE="char" LENGTH="127" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="license" TYPE="char" LENGTH="7" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="keywords" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="library_id" UNIQUE="false" FIELDS="library_id"/>
</INDEXES>
</TABLE>
<TABLE NAME="hvp_libraries" COMMENT="Stores information about libraries.">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
......@@ -29,52 +58,33 @@
</TABLE>
<TABLE NAME="hvp_libraries_libraries" COMMENT="Library dependencies">
<FIELDS>
<FIELD NAME="library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="required_library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The dependency to load"/>
<FIELD NAME="dependency_type" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="library_id, required_library_id"/>
<KEY NAME="primary" TYPE="primary" FIELDS="id, required_library_id"/>
</KEYS>
</TABLE>
<TABLE NAME="hvp_libraries_languages" COMMENT="Translations for libraries">
<FIELDS>
<FIELD NAME="library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="language_code" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="translation" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="JSON"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="library_id, language_code"/>
<KEY NAME="primary" TYPE="primary" FIELDS="id, language_code"/>
</KEYS>
</TABLE>
<TABLE NAME="hvp_contents" COMMENT="Data for hvp content">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="content" TYPE="text" NOTNULL="true" SEQUENCE="false" COMMENT="Start params stored in JSON."/>
<FIELD NAME="embed_type" TYPE="char" LENGTH="127" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="type" TYPE="char" LENGTH="127" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="author" TYPE="char" LENGTH="127" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="license" TYPE="char" LENGTH="7" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="keywords" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="description" TYPE="text" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
</KEYS>
<INDEXES>
<INDEX NAME="library_id" UNIQUE="false" FIELDS="library_id"/>
</INDEXES>
</TABLE>
<TABLE NAME="hvp_contents_libraries" COMMENT="Store which content uses which libraries.">
<TABLE NAME="hvp_contents_libraries" COMMENT="Store which library is used in which content.">
<FIELDS>
<FIELD NAME="content_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="library_id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="preloaded" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false" COMMENT="1 = preload dependency, 0 = load it self"/>
<FIELD NAME="drop_css" TYPE="int" LENGTH="1" NOTNULL="true" SEQUENCE="false" COMMENT="1 = do not load css"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="content_id, library_id"/>
<KEY NAME="primary" TYPE="primary" FIELDS="id, library_id"/>
</KEYS>
<INDEXES>
<INDEX NAME="meta" UNIQUE="false" FIELDS="preloaded, drop_css"/>
......
h5p.php 0 → 100644
<?php
if (!defined('MOODLE_INTERNAL')) {
die('Direct access to this script is forbidden.');
}
require_once ($CFG->dirroot . '/mod/hvp/library/h5p.classes.php');
function hvp_get_instance($type) {
global $CFG;
static $interface, $core;
if (!isset($interface)) {
$interface = new H5PDrupal();
$core = new H5PCore($interface);
}
switch ($type) {
case 'validator':
return new H5PValidator($interface, $core);
case 'storage':
return new H5PStorage($interface, $core);
case 'contentvalidator':
return new H5PContentValidator($interface, $core);
case 'interface':
return $interface;
case 'core':
return $core;
}
}
class H5PDrupal implements H5PFrameworkInterface {
/**
* Implements setErrorMessage
*/
public function setErrorMessage($message) {
// TODO; Change core, do not send in translated error messages.
debugging($message, DEBUG_DEVELOPER);
//print_error($message, 'hvp');
}
/**
* Implements setInfoMessage
*/
public function setInfoMessage($message) {
echo $message;
}
/**
* Implements t
*/
public function t($message, $replacements = array()) {
// TODO; Change core, use keywords for translation.
return str_replace(array_keys($replacements), $replacements, $message);
//debugging($message, DEBUG_DEVELOPER);
//return get_string($message, 'hvp', $replacements);
}
/**
* Implements getH5PPath
*/
public function getH5pPath() {
global $CFG;
return $CFG->dirroot . '/mod/hvp/files';
}
/**
* Implements getUploadedH5PFolderPath
*/
public function getUploadedH5pFolderPath($setPath = NULL) {
static $path;
if ($setPath !== NULL) {
$path = $setPath;
}
// TODO: Throw error if path isn't set.
return $path;
}
/**
* Implements getUploadedH5PPath
*/
public function getUploadedH5pPath($setPath = NULL) {
static $path;
if ($setPath !== NULL) {
$path = $setPath;
}
// TODO: Throw error if path isn't set.
return $path;
}
/**
* Implements getLibraryId
*/
public function getLibraryId($machineName, $majorVersion, $minorVersion) {
global $DB;
$library = $DB->get_record('hvp_libraries', array('machine_name' => $machineName, 'major_version' => $majorVersion, 'minor_version' => $minorVersion));
return $library ? $library->id : FALSE;
}
/**
* Implements isPatchedLibrary
*/
public function isPatchedLibrary($library) {
global $DB;
$operator = $this->isInDevMode() ? '<=' : '<';
$library = $DB->get_record_sql('SELECT id FROM {hvp_libraries} WHERE machine_name = ? AND major_version = ? AND minor_version = ? AND patch_version ' . $operator . ' ?',
array($library['machineName'], $library['majorVersion'], $library['minorVersion'], $library['patchVersion']));
return $library ? TRUE : FALSE;
}
/**
* Implements isInDevMode
*/
public function isInDevMode() {
return FALSE; // TODO: Not supported
}
/**
* Implements mayUpdateLibraries
*/
public function mayUpdateLibraries() {
return TRUE; // TODO: Add permission support
}
/**
* Implements saveLibraryData
*/
public function saveLibraryData(&$libraryData, $new = TRUE) {
global $DB;
if ($new) {
// Create new library
$library = (object) array(
'machine_name' => $libraryData['machineName'],
'title' => $libraryData['title'],
'major_version' => $libraryData['majorVersion'],
'minor_version' => $libraryData['minorVersion'],
'patch_version' => $libraryData['patchVersion'],
'runnable' => $libraryData['runnable']
);
}
else {
// Load library
$library = $DB->get_record('hvp_libraries', array('id' => $libraryData['libraryId']));
$library->title = $libraryData['title'];
$library->patch_version = $libraryData['patchVersion'];
$library->runnable = $libraryData['runnable'];
}
// Some special properties needs some checking and converting before they can be saved.
$library->preloaded_js = $this->pathsToCsv($libraryData, 'preloadedJs');
$library->preloaded_css = $this->pathsToCsv($libraryData, 'preloadedCss');
if (isset($libraryData['dropLibraryCss'])) {
$libs = array();
foreach ($libraryData['dropLibraryCss'] as $lib) {
$libs[] = $lib['machineName'];
}
$library->drop_library_css = implode(', ', $libs);
}
else {
$library->drop_library_css = '';
}
$library->fullscreen = (! isset($libraryData['fullscreen']) ? 0 : $libraryData['fullscreen']);
$library->embed_types = (isset($libraryData['embedTypes']) ? implode(', ', $libraryData['embedTypes']) : '');
$library->semantics = (! isset($libraryData['semantics']) ? '' : $libraryData['semantics']);
if ($new) {
$library->id = $DB->insert_record('hvp_libraries', $library);
$libraryData['libraryId'] = $library->id;
}
else {
$DB->update_record('hvp_libraries', $library);
$this->deleteLibraryDependencies($library->id);
}
// Update languages
$DB->delete_records('hvp_libraries_languages', array('id' => "$library->id"));
if (isset($libraryData['language'])) {
foreach ($libraryData['language'] as $languageCode => $languageJson) {
$DB->insert_record_raw('hvp_libraries_languages', array(
'id' => $library->id,
'language_code' => $languageCode,
'translation' => $languageJson
), false, false, true);
}
}
}
/**
* Convert list of file paths to csv
*
* @param array $libraryData
* Library data as found in library.json files
* @param string $key
* Key that should be found in $libraryData
* @return string
* file paths separated by ', '
*/
private function pathsToCsv($libraryData, $key) {
if (isset($libraryData[$key])) {
$paths = array();
foreach ($libraryData[$key] as $file) {
$paths[] = $file['path'];
}
return implode(', ', $paths);
}
return '';
}
/**
* Implements deleteLibraryDependencies
*/
public function deleteLibraryDependencies($libraryId) {
global $DB;
$DB->delete_records('hvp_libraries_libraries', array('id' => "$libraryId"));
}
/**
* Implements saveLibraryDependencies
*/
public function saveLibraryDependencies($libraryId, $dependencies, $dependency_type) {
global $DB;
foreach ($dependencies as $dependency) {
// Find dependency library.
$dependencyLibrary = $DB->get_record('hvp_libraries', array(
'machine_name' => $dependency['machineName'],
'major_version' => $dependency['majorVersion'],
'minor_version' => $dependency['minorVersion']
));
// Create relation.
$DB->insert_record_raw('hvp_libraries_libraries', array(
'id' => $libraryId,
'required_library_id' => $dependencyLibrary->id,
'dependency_type' => $dependency_type
), false, false, true);
}
}
/**
* Implements saveContentData
*/
public function saveContentData($contentId, $contentJson, $mainJsonData, $mainLibraryId, $contentMainId = NULL) {
global $DB;
$DB->insert_record_raw('hvp_contents', array(
'id' => $contentId,
'content' => $contentJson,
'embed_type' => (isset($mainJsonData['embedTypes']) ? implode(', ', $mainJsonData['embedTypes']) : ''),
'library_id' => $mainLibraryId
), false, false, true);
}
/**
* Implement getWhitelist
*/
public function getWhitelist($isLibrary, $defaultContentWhitelist, $defaultLibraryWhitelist) {
return $defaultContentWhitelist . ($isLibrary ? ' ' . $defaultLibraryWhitelist : '');
}
/**
* Implements copyLibraryUsage
*/
public function copyLibraryUsage($contentId, $copyFromId, $contentMainId = NULL) {
global $DB;
$libraryUsage = $DB->get_record('hvp_contents_libraries', array(
'id' => $copyFromId
));
$libraryUsage->id = $contentId;
$DB->insert_record_raw('hvp_contents_libraries', (array)$libraryUsage, false, false, true);
}
/**
* Implements deleteContentData
*/
public function deleteContentData($contentId) {
global $DB;
$DB->delete_records('hvp_contents', array('id' => "$contentId"));
$this->deleteLibraryUsage($contentId);
}
/**
* Implements deleteLibraryUsage
*/
public function deleteLibraryUsage($contentId) {
global $DB;
$DB->delete_records('hvp_contents_libraries', array('id' => "$contentId"));
}
/**
* Implements saveLibraryUsage
*/
public function saveLibraryUsage($contentId, $librariesInUse) {
global $DB;
$dropLibraryCssList = array();
foreach ($librariesInUse as $machineName => $library) {
if (!empty($library['library']['dropLibraryCss'])) {
$dropLibraryCssList = array_merge($dropLibraryCssList, explode(', ', $library['library']['dropLibraryCss']));
}
}
foreach ($librariesInUse as $machineName => $library) {
$conditions = array(
'id' => $contentId,
'library_id' => $library['library']['libraryId']
);
$contentLibrary = $DB->get_record('hvp_contents_libraries', $conditions);
if (!$contentLibrary) {
$contentLibrary = $conditions;
}
else {
$contentLibrary = (array) $contentLibrary;
}
$contentLibrary['preloaded'] = $library['preloaded'];
$contentLibrary['drop_css'] = in_array($machineName, $dropLibraryCssList) ? 1 : 0;
$DB->insert_record_raw('hvp_contents_libraries', $contentLibrary, false, false, true);
}
}
/**
* Implements loadLibrary
*/
public function loadLibrary($machineName, $majorVersion, $minorVersion) {
global $DB;
$library = $DB->get_record('hvp_libraries', array(
'machine_name' => $machineName,
'major_version' => $majorVersion,
'minor_version' => $minorVersion
));
$libraryData = array(
'libraryId' => $library->id,
'machineName' => $library->machine_name,
'title' => $library->title,
'majorVersion' => $library->major_version,
'minorVersion' => $library->minor_version,
'patchVersion' => $library->patch_version,
'embedTypes' => $library->embed_types,
'preloadedJs' => $library->preloaded_js,
'preloadedCss' => $library->preloaded_css,
'dropLibraryCss' => $library->drop_library_css,
'fullscreen' => $library->fullscreen,
'runnable' => $library->runnable,
'semantics' => $library->semantics
);
$dependencies = $DB->get_records_sql('SELECT hl.machine_name, hl.major_version, hl.minor_version, hll.dependency_type
FROM {hvp_libraries_libraries} hll
JOIN {hvp_libraries} hl ON hll.required_library_id = hl.id
WHERE hll.id = ?', array("$library->id"));
foreach ($dependencies as $dependency) {
$libraryData[$dependency->dependency_type . 'Dependencies'][] = array(
'machineName' => $dependency->machine_name,
'majorVersion' => $dependency->major_version,
'minorVersion' => $dependency->minor_version
);
}
if ($this->isInDevMode()) {
// TODO: Get semantics from file.
}
return $libraryData;
}
/**
* Implements getLibrarySemantics
*
* Calls modules implementing hook_alter_h5p_semantics().
*/
public function getLibrarySemantics($machineName, $majorVersion, $minorVersion) {
if ($this->isInDevMode()) {
// TODO: Get semantics from file.
}
else {
$library = $DB->get_record('hvp_libraries', array(
'machine_name' => $machineName,
'major_version' => $majorVersion,
'minor_version' => $minorVersion
));
$semantics = $library->semantics;
}
return json_decode($semantics);
}
private function getSemanticsFromFile($machineName, $majorVersion, $minorVersion) {
// TODO
}
}
......@@ -5,4 +5,6 @@ $string['modulename_help'] = 'Help me, help you!';
$string['modulename_link'] = 'mod/hvp/view';
$string['modulenameplural'] = 'H5Ps';
$string['intro'] = 'Hello awesome!';
$string['h5pfile'] = 'H5P File';
$string['noth5pfile'] = 'The uploaded file is not a valid H5P.';
......@@ -22,6 +22,64 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
//function hvp_add_instance($hvp);
//function hvp_update_instance($hvp);
//function hvp_delete_instance($id);
require_once ($CFG->dirroot . '/mod/hvp/h5p.php');
function hvp_supports($feature) {
switch($feature) {
case FEATURE_GROUPS: return true;
case FEATURE_GROUPINGS: return true;
case FEATURE_GROUPMEMBERSONLY: return true;
case FEATURE_MOD_INTRO: return false;
case FEATURE_COMPLETION_TRACKS_VIEWS: return false;
case FEATURE_COMPLETION_HAS_RULES: return false;
case FEATURE_GRADE_HAS_GRADE: return false;
case FEATURE_GRADE_OUTCOMES: return false;
case FEATURE_BACKUP_MOODLE2: return false;
case FEATURE_SHOW_DESCRIPTION: return false;
default: return null;
}
}
function hvp_add_instance($hvp) {
global $DB;
$hvp->id = $DB->insert_record('hvp', $hvp);
$h5pCore = hvp_get_instance('storage');
$library_updated = $h5pCore->savePackage($hvp->id);
debugging('Added h5p ' . $hvp->id . ': ' . $library_updated , DEBUG_DEVELOPER);
return $hvp->id;
}
function hvp_update_instance($hvp) {
global $DB;
$hvp->id = $hvp->instance;
$result = $DB->update_record('hvp', $hvp);
// TODO: Update other datas.
debugging('Updated h5p ' . $hvp->id . ': ' . $result, DEBUG_DEVELOPER);
return $result;
}
function hvp_delete_instance($id) {
global $DB;
if (! $hvp = $DB->get_record('hvp', array('id' => "$id"))) {
return false;
}
$result = true;
// TODO: Delete other datas.
if (! $DB->delete_records('hvp', array('id' => "$hvp->id"))) {
$result = false;
}
debugging('Deleted h5p ' . $hvp->id . ': ' . $result, DEBUG_DEVELOPER);
return $result;
}
library @ 2720ec5f
Subproject commit 2720ec5f1614ef513bf361c74f4434db96f48be9
......@@ -4,6 +4,7 @@ if (!defined('MOODLE_INTERNAL')) {
}
require_once ($CFG->dirroot . '/course/moodleform_mod.php');
//require_once ($CFG->dirroot . '/mod/hvp/h5p.php');
class mod_hvp_mod_form extends moodleform_mod {
......@@ -12,12 +13,70 @@ class mod_hvp_mod_form extends moodleform_mod {
$mform =& $this->_form;
$mform->addElement('filepicker', 'h5pfile', get_string('h5pfile', 'hvp'), null, array('accepted_types' => '*'));
// Name.
$mform->addElement('text', 'name', get_string('name'));
$mform->setType('name', PARAM_TEXT);
$mform->addRule('name', null, 'required', null, 'client');
$mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
// H5P
// TODO: Use $course->maxbytes ?
$mform->addElement('filepicker', 'h5pfile', get_string('h5pfile', 'hvp'), null, array('maxbytes' => $CFG->maxbytes, 'accepted_types' => '*'));
$mform->addRule('h5pfile', null, 'required', null, 'client');
$this->standard_coursemodule_elements();
$this->add_action_buttons();
}
function data_preprocessing(&$default_values) {
// Aaah.. we meet again h5pfile!
$draftitemid = file_get_submitted_draft_itemid('h5pfile');
file_prepare_draft_area($draftitemid, $this->context->id, 'mod_hvp', 'package', 0);
$default_values['h5pfile'] = $draftitemid;
}
function validation($data, $files) {
global $CFG;
$errors = parent::validation($data, $files);
if (empty($data['h5pfile'])) {
$errors['h5pfile'] = get_string('required');
return $errors;
}
$files = $this->get_draft_files('h5pfile');
if (count($files) < 1) {
$errors['h5pfile'] = get_string('required');
return $errors;
}
$file = reset($files);
$interface = hvp_get_instance('interface');
$path = $CFG->dirroot . '/mod/hvp/files/tmp/' . uniqid('hvp-');
$interface->getUploadedH5pFolderPath($path);
$path .= '.h5p';
$interface->getUploadedH5pPath($path);
$file->copy_content_to($path);
$h5pValidator = hvp_get_instance('validator');
if (! $h5pValidator->isValidPackage()) {
$errors['h5pfile'] = get_string('noth5pfile', 'hvp');
}
return $errors;
}
function get_data() {
$data = parent::get_data();
if (!$data) {
return false;
}
return $data;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment