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

@ -119,23 +119,23 @@ interface H5PFrameworkInterface {
* Object holding the information that is to be stored
*/
public function saveLibraryData(&$libraryData, $new = TRUE);
/**
* Stores contentData
*
* @param string $contentJson
* 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
* Insert new content.
*
* @param object $content
* @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
*
@ -262,6 +262,44 @@ interface H5PFrameworkInterface {
* @return array
*/
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');
}
}
// 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) {
@ -1017,7 +1058,7 @@ class H5PStorage {
}
$content['library'] = $librariesInUse['preloaded-' . $this->h5pC->mainJsonData['mainLibrary']]['library'];
$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;
$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.
*
@ -1298,6 +1358,7 @@ class H5PCore {
);
unset($content['libraryId'], $content['libraryName'], $content['libraryEmbedTypes'], $content['libraryFullscreen']);
// TODO: Move to filterParameters?
if ($this->development_mode & H5PDevelopment::MODE_CONTENT) {
// TODO: Remove Drupal specific stuff
$json_content_path = file_create_path(file_directory_path() . '/' . variable_get('h5p_default_path', 'h5p') . '/content/' . $id . '/content.json');
@ -1308,12 +1369,45 @@ class H5PCore {
}
$content['params'] = $json_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.
*
@ -1408,7 +1502,7 @@ class H5PCore {
/**
* Load library semantics.
*
* @return string 'div' or 'iframe'.
* @return string
*/
public function loadLibrarySemantics($name, $majorVersion, $minorVersion) {
$semantics = NULL;
@ -1607,7 +1701,6 @@ class H5PCore {
/**
* Determine the correct embed type to use.
* TODO: Use constants.
*
* @return string 'div' or 'iframe'.
*/
@ -1635,7 +1728,7 @@ class H5PContentValidator {
public $h5pF;
public $h5pC;
private $typeMap;
private $semanticsCache;
private $libraries, $dependencies;
/**
* Constructor for the H5PContentValidator
@ -1661,33 +1754,24 @@ class H5PContentValidator {
'select' => 'validateSelect',
'library' => 'validateLibrary',
);
// Cache for semantics used within this validation to avoid unneccessary
// json_decodes if a library is used multiple times.
$this->semanticsCache = array();
// Keep track of the libraries we load to avoid loading it multiple times.
$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
* object from semantics
*
* Function will recurse via external functions for container objects like
* '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.
* Get the flat dependecy tree.
*
* @return array
*/
public function validateBySemantics(&$value, $semantics) {
$fakebaseobject = (object) array(
'type' => 'group',
'fields' => $semantics,
);
$this->validateGroup($value, $fakebaseobject, FALSE);
public function getDependencies() {
return $this->dependencies;
}
/**
* Validate given text value against text semantics.
*/
@ -2028,31 +2112,47 @@ class H5PContentValidator {
/**
* Validate given library value against library semantics.
* Check if provided library is within allowed options.
*
* Will recurse into validating the library's semantics too.
*/
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($this->semanticsCache[$value->library])) {
$librarySemantics = $this->semanticsCache[$value->library];
}
else {
$libspec = $this->h5pC->libraryFromString($value->library);
$librarySemantics = $this->h5pC->loadLibrarySemantics($libspec['machineName'], $libspec['majorVersion'], $libspec['minorVersion']);
$this->semanticsCache[$value->library] = $librarySemantics;
}
$this->validateBySemantics($value->params, $librarySemantics);
$validkeys = array('library', 'params');
if (isset($semantics->extraAttributes)) {
$validkeys = array_merge($validkeys, $semantics->extraAttributes);
}
$this->filterParams($value, $validkeys);
}
else {
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'));
$value = new stdClass();
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 {
$library = $this->libraries[$value->library];
}
$this->validateGroup($value->params, (object) array(
'type' => 'group',
'fields' => $library['semantics'],
), FALSE);
$validkeys = array('library', 'params');
if (isset($semantics->extraAttributes)) {
$validkeys = array_merge($validkeys, $semantics->extraAttributes);
}
$this->filterParams($value, $validkeys);
}
/**