Moved content cache control to core. Made sure updating a library invalidates caches for all content who uses it. All saving of content and filtering of parameters is now handled by core.

ContentValidator now collects a flat list of content dependencies.
namespaces
Frode Petterson 2014-05-27 10:55:46 +02:00
parent 4a32f976e5
commit c5b32fb621
1 changed files with 160 additions and 60 deletions

View File

@ -121,20 +121,20 @@ interface H5PFrameworkInterface {
public function saveLibraryData(&$libraryData, $new = TRUE); public function saveLibraryData(&$libraryData, $new = TRUE);
/** /**
* Stores contentData * Insert new content.
* *
* @param string $contentJson * @param object $content
* The content data that is to be stored
* @param array $mainJsonData
* The data extracted from the h5p.json file
* @param array $mainLibraryId
* Main library identifier
* @param int $contentMainId * @param int $contentMainId
* Any contentMainId defined by the framework, for instance to support revisioning
* @param int $contentId
* Framework specific id identifying the content
*/ */
public function saveContentData($content, $contentMainId = NULL); public function insertContent($content, $contentMainId = NULL);
/**
* Update old content.
*
* @param object $content
* @param int $contentMainId
*/
public function updateContent($content, $contentMainId = NULL);
/** /**
* Save what libraries a library is dependending on * Save what libraries a library is dependending on
@ -262,6 +262,44 @@ interface H5PFrameworkInterface {
* @return array * @return array
*/ */
public function loadContentDependencies($id, $type = NULL); public function loadContentDependencies($id, $type = NULL);
/**
* Get data from cache.
*
* @param string $group
* @param string $key
*/
public function cacheGet($group, $key);
/**
* Store data in cache.
*
* @param string $group
* @param string $key
* @param mixed $data
*/
public function cacheSet($group, $key, $data);
/**
* Delete data from cache.
*
* @param string $group
* @param string $key
*/
public function cacheDel($group, $key = NULL);
/**
* Will invalidate the cache for the content that uses the specified library.
* This means that the content dependencies has to be rebuilt, and the parameters refiltered.
*
* @param int $library_id
*/
public function invalidateContentCache($library_id);
/**
* Get content without cache.
*/
public function getNotCached();
} }
/** /**
@ -999,6 +1037,9 @@ class H5PStorage {
$this->h5pF->saveLibraryDependencies($library['libraryId'], $library['editorDependencies'], 'editor'); $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->invalidateContentCache($library['libraryId']);
} }
if (!$skipContent) { if (!$skipContent) {
@ -1017,7 +1058,7 @@ class H5PStorage {
} }
$content['library'] = $librariesInUse['preloaded-' . $this->h5pC->mainJsonData['mainLibrary']]['library']; $content['library'] = $librariesInUse['preloaded-' . $this->h5pC->mainJsonData['mainLibrary']]['library'];
$content['params'] = file_get_contents($current_path . DIRECTORY_SEPARATOR . 'content.json'); $content['params'] = file_get_contents($current_path . DIRECTORY_SEPARATOR . 'content.json');
$contentId = $this->h5pF->saveContentData($content, $contentMainId); $contentId = $this->h5pC->saveContent($content, $contentMainId);
$this->contentId = $contentId; $this->contentId = $contentId;
$contents_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content'; $contents_path = $this->h5pF->getH5pPath() . DIRECTORY_SEPARATOR . 'content';
@ -1278,6 +1319,25 @@ class H5PCore {
} }
} }
/**
* Save content and clear cache.
*
* @param array $content
* @return int Content ID
*/
public function saveContent($content, $contentMainId) {
if (isset($content['id'])) {
$this->h5pF->updateContent($content, $contentMainId);
}
else {
$content['id'] = $this->h5pF->insertContent($content, $contentMainId);
}
$this->h5pF->cacheDel('parameters', $content['id']);
return $content['id'];
}
/** /**
* Load content. * Load content.
* *
@ -1298,6 +1358,7 @@ class H5PCore {
); );
unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']); unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']);
// TODO: Move to filterParameters?
if ($this->development_mode & H5PDevelopment::MODE_CONTENT) { if ($this->development_mode & H5PDevelopment::MODE_CONTENT) {
// TODO: Remove Drupal specific stuff // TODO: Remove Drupal specific stuff
$json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json'); $json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json');
@ -1314,6 +1375,39 @@ class H5PCore {
return $content; return $content;
} }
/**
* TODO: Add support for development modes.
* TODO: Should we regenerate export here as well?
*
* @param Object $content
* @return Object NULL on failure.
*/
public function filterParameters($content) {
$params = $this->h5pF->cacheGet('parameters', $content['id']);
if ($params !== NULL) {
return $params;
}
// 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'])
);
$validator->validateLibrary($params, (object) array('options' => array($params->library)));
$params = json_encode($params->params);
// Update content dependencies.
$dependencies = $validator->getDependencies();
$this->h5pF->deleteLibraryUsage($content['id']);
$this->h5pF->saveLibraryUsage($content['id'], $dependencies);
// Cache.
$this->h5pF->cacheSet('parameters', $content['id'], $params);
return $params;
}
/** /**
* Find the files required for this content to work. * Find the files required for this content to work.
* *
@ -1408,7 +1502,7 @@ class H5PCore {
/** /**
* Load library semantics. * Load library semantics.
* *
* @return string 'div' or 'iframe'. * @return string
*/ */
public function loadLibrarySemantics($name, $majorVersion, $minorVersion) { public function loadLibrarySemantics($name, $majorVersion, $minorVersion) {
$semantics = NULL; $semantics = NULL;
@ -1607,7 +1701,6 @@ class H5PCore {
/** /**
* Determine the correct embed type to use. * Determine the correct embed type to use.
* TODO: Use constants.
* *
* @return string 'div' or 'iframe'. * @return string 'div' or 'iframe'.
*/ */
@ -1635,7 +1728,7 @@ class H5PContentValidator {
public $h5pF; public $h5pF;
public $h5pC; public $h5pC;
private $typeMap; private $typeMap;
private $semanticsCache; private $libraries, $dependencies;
/** /**
* Constructor for the H5PContentValidator * Constructor for the H5PContentValidator
@ -1661,31 +1754,22 @@ class H5PContentValidator {
'select' => 'validateSelect', 'select' => 'validateSelect',
'library' => 'validateLibrary', 'library' => 'validateLibrary',
); );
// Cache for semantics used within this validation to avoid unneccessary
// json_decodes if a library is used multiple times. // Keep track of the libraries we load to avoid loading it multiple times.
$this->semanticsCache = array(); $this->libraries = array();
// TODO: Should this possible be done in core's loadLibrary? This might be done multiple places.
// Keep track of all dependencies for the given content.
$this->dependencies = array();
} }
/** /**
* Validate the given value from content with the matching semantics * Get the flat dependecy tree.
* object from semantics
* *
* Function will recurse via external functions for container objects like * @return array
* 'list', 'group' and 'library'.
*
* @param object $value
* Object to be verified. May be a string or an array. (normal or keyed)
* @param object $semantics
* Semantics object from semantics.json for main library. Further
* semantics will be loaded from H5PFramework if any libraries are
* found within the value data.
*/ */
public function validateBySemantics(&$value, $semantics) { public function getDependencies() {
$fakebaseobject = (object) array( return $this->dependencies;
'type' => 'group',
'fields' => $semantics,
);
$this->validateGroup($value, $fakebaseobject, FALSE);
} }
/** /**
@ -2028,32 +2112,48 @@ class H5PContentValidator {
/** /**
* Validate given library value against library semantics. * Validate given library value against library semantics.
* Check if provided library is within allowed options.
* *
* Will recurse into validating the library's semantics too. * Will recurse into validating the library's semantics too.
*/ */
public function validateLibrary(&$value, $semantics) { public function validateLibrary(&$value, $semantics) {
// Check if provided library is within allowed options if (!isset($value->library) || !in_array($value->library, $semantics->options)) {
if (isset($value->library) && in_array($value->library, $semantics->options)) { $this->h5pF->setErrorMessage($this->h5pF->t('Library used in content is not a valid library according to semantics'));
if (isset($this->semanticsCache[$value->library])) { $value = new stdClass();
$librarySemantics = $this->semanticsCache[$value->library]; return;
}
if (!isset($this->libraries[$value->library])) {
$libspec = $this->h5pC->libraryFromString($value->library);
$library = $this->h5pC->loadLibrary($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$library['semantics'] = json_decode($library['semantics']);
$this->libraries[$value->library] = $library;
// Find all dependencies for this library
$depkey = 'preloaded-' . $libspec['machineName'];
if (!isset($this->dependencies[$depkey])) {
$this->dependencies[$depkey] = array(
'library' => $library,
'type' => 'preloaded'
);
$this->h5pC->findLibraryDependencies($this->dependencies, $library);
}
} }
else { else {
$libspec = $this->h5pC->libraryFromString($value->library); $library = $this->libraries[$value->library];
$librarySemantics = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$this->semanticsCache[$value->library] = $librarySemantics;
} }
$this->validateBySemantics($value->params, $librarySemantics);
$this->validateGroup($value->params, (object) array(
'type' => 'group',
'fields' => $library['semantics'],
), FALSE);
$validkeys = array('library', 'params'); $validkeys = array('library', 'params');
if (isset($semantics->extraAttributes)) { if (isset($semantics->extraAttributes)) {
$validkeys = array_merge($validkeys, $semantics->extraAttributes); $validkeys = array_merge($validkeys, $semantics->extraAttributes);
} }
$this->filterParams($value, $validkeys); $this->filterParams($value, $validkeys);
} }
else {
$this->h5pF->setErrorMessage($this->h5pF->t('Library used in content is not a valid library according to semantics'));
$value = new stdClass();
}
}
/** /**
* Check params for a whitelist of allowed properties * Check params for a whitelist of allowed properties