-
-
Notifications
You must be signed in to change notification settings - Fork 82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement the "save content state" option of H5P core #1014
Comments
Thanks for the detailled list how the PHP implementation does it. This will help making sure we follow the "specification". State support is on the roadmap. If you know anybody who needs it right away:
server.post(
'/h5p/contentUserData/:contentId/:dataType/:subContentId',
(
req: express.Request<
{ contentId: string; dataType: string; subContentId: string },
{},
H5P.IPostContentUserData
>,
res
) => {
res.status(200).send();
}
);
// STUB // STUB, not implemented yet. You have to get the user id through a session
server.get('/h5p/contentUserData', (req, res) => { // cookie as h5P does not add it to the request. Alternatively you could add
res.status(200).send(); // it to the URL generator.
}); server.get(
'/h5p/contentUserData/:contentId/:dataType/:subContentId',
(
req: express.Request<{
contentId: string;
dataType: string;
subContentId: string;
}>,
res: express.Response<H5P.IGetContentUserData | {}>
) => {
res.status(200).json({});
}
); interfaces: export interface IPostContentUserData {
/**
* The actual data. 0 if the data should be deleted.
*/
data: any | 0;
/**
* Indicates that data should be invalidated when content changes.
*/
invalidate: 0 | 1;
/**
* Indicates that data should be loaded when content is loaded.
*/
preload: 0 | 1;
}
export interface IGetContentUserData extends IAjaxResponse {
data: any;
} |
@sr258 There may actually be interest in contributing the code, but it will rather not be me writing it. |
@otacke That sounded promising, are there any updates on such a contribution? This is an important feature for many people, would be great to have it supported by this (really cool) port. |
@ramonziai We've funding for the feature and it will probably be finished this year. |
@sr258 Cool, great to hear! |
@ramonziai The code exists, but for an older version of the node.js port (would need some rewrite to account for some changes that were done since forking) and it is still in review. It's nice though: features both file based and mongo db storage options for the state and uses JWT authentication/authorization to identify the user. The latter is kind of closely tied to the particular platform integration, so this would probably need to be implemented more openly - all the better that Lumi got funding to do it the way they want it right away. |
@otacke Sounds great! Will it also be possible to hook in custom storage for "save content state"? For example, if one wanted to use some RDBMS, or event data stored in a Learning Record Store, or something else? |
@ramonziai Storing/retrieving is done by implementing an interface, so you could add implementations for other storages easily, yes. But if I was not clear, sorry: The existing code has nothing to do with what Lumi will implement - although I am sure that they'll keep that in mind, too. |
I will start working on it today. You can review/see the progress in PR #1886 |
I have a question to how the client handles the contentUserData and hope @otacke or someone else can help me: There seem to be two ways to initialise the contentUserData: 1. load preloadedData from content.userContentData 2. load data via contentUserDataAjax When I load the contentUserData via the contentUserDataAjax it does not work. The server responds with an JSON Object with a When I provide it as actual JSON Object I get an error at https://github.com/h5p/h5p-php-library/blob/master/js/h5p.js#L2433 Has anyone any hints ? The content type is a Course Presentation with some SingleChoice/MultiChoice and fill in the words. |
And has anyone a php/wordpress instance running where I can test the behaviour of the original implementation? |
Update: If the contentUserData is directly integrated into the H5PIntegration on server side (method 1) it loads the state just fine. |
Just a guess: Looks like the data is an escaped string when line 2400 is reached and nit a proper JSON object. I use a local Drupal 7 docker container with inbuilt SQLite. It works very well but is local only. I recommend you spin one up yourself. |
I had the idea with a docker yesterday also. :) The data must be an escaped string because it gets parsed in line 2417. If I provide a proper JSON line 2417 throws an error. |
If you pass something to the JSON parser it must be a string, but it mustn't be escaped. That might be the problem. |
I tried it, but even a non escaped string does not work.. |
Maybe it is bugged, but not noticed by many because as far as I see most integrations put the contentUserData into the Integration object. |
I don't believe that there's a bug as serious as this in the core. Must be something about how the data is returned. |
How can I reproduce the issue? Can I simply run the code in the PR? |
@JPSchellenberg Sorry that I didn't reply earlier. I am currently trying to learn to work less on weekends :-) For your case: Have you checked your implementation of the Beyond that: I am not sure how relevant the And if the route was used here: There is in fact an issue in H5P core. The If you now check https://github.com/h5p/h5p-php-library/blob/22115c0aeaa34faef5109c3f378add2d1647c837/js/h5p.js#L107-L137 you'll notice that the previous state may still be undefined when the |
That's how it should be! :D
|
I will prepare the PR so you can test it, but keep in mind that it will be just a work in progress. I will delete the |
Exactly, that's the reason why the previous state would not be set for a content type if you leave out the Still, it might be worth to check the GET route itself (e.g. calling it in the browser with valid parameters directly). It might not return a JSON object the way that H5P expects it. |
@sr258 I added a commit where you can test the behaviour: ccce7a6 @otacke The structure of the returned object is {
data: "{"progress":2,"answers":[[["Norway"]],[{"progress":0,"answers":{"corrects":0,"wrongs":0}}],[{"answers":[]}],[[]]],"answered":[false,false,false,true]}"
} and as far as I figured it out that should be the correct format, but I might be wrong as my understanding of the client side of H5P is very limited. :) |
@JPSchellenberg Close. The response should match the AJAX response interface definition that's defined at https://github.com/Lumieducation/H5P-Nodejs-library/blob/master/packages/h5p-server/src/types.ts#L23-L51 You're lacking the |
I tried your suggestion, but it still does not work. So I think it is really that |
@JPSchellenberg Oh, sorry, I thought I made that clear! It will definitely not work without setting |
No, it was pretty clear that it won't work. I just wanted to make sure and give a feedback. :) The question is if we want to implement something that does not really work (and is probably never called/used). |
@JPSchellenberg I really don't know if that function is relevant (see initial comment). It doesn't seem to be used for anything except for the broken "fallback", so I assume it could be left out - if it turns out it will be used for something, it could be added easily later on. I'll reach out to Joubel in order to get some more information. |
@JPSchellenberg Doh, I should have checked the editor first. It is actually used (https://github.com/h5p/h5p-editor-php-library/blob/7bc192798f8f6e1dee34891b56f3bf60ab320f3d/scripts/h5peditor.js#L1471) - for storing the state of wizards having run, for instance. |
@otacke Alright. Thank you very much! Then I will implement it. |
* test(jest): run jest tests in watch mode via test:watch * chore(scripts): add start:rest script * feat(contentUserData): add parameters to contentUserDataUrl config * feat(contentUserData): read saveFreq from config * feat(contentUserData): add the contentUserData into the H5PIntegration * test(contentUserDataGET): add example for GET issue Lumieducation/H5P-Nodejs-library#1014 (comment) * test(contentUserData): revert example * chore(scripts): add h5p-server build script * test(launch): add DEBUG to vscode launch * refactor(script): rename start:rest to start:rest:server Lumieducation/H5P-Nodejs-library#1886 (comment) * refactor(saveFreq): rename saveFreq to contentUserStateSaveInterval Lumieducation/H5P-Nodejs-library#1886 (comment) * feat(contentUserData): delete contentUserData when content is deleted * test(contentUserData): use mock for H5PPlayer.render test * refactor(h5p-examples): add mock implementation to pass build * feat(contentUserData): build contentUserDataIntegration in Manager * feat(contentUserData): update interfaces * test(contentUserDataManager): add tests * refactor(h5p-examples): remove test/example conentUserDataStorage * refactor(saveContentUserData): add invalidate and preload parameters * feat(h5p-express): add ContentUserDataController * fix(contentManager): delete contentUserDataStorage after content Lumieducation/H5P-Nodejs-library#1886 (comment) * refactor(ContentUserDataManager): remove unnecessary code * refactor(ContentUserDataManager): use boolean instead of number Lumieducation/H5P-Nodejs-library#1886 (comment) * refactor(ContentManager): remove unnecessary code * refactor(ContentUserDataManager): use boolean instead of number Lumieducation/H5P-Nodejs-library#1886 (comment) * fix(ContentUserDataManager): sanitize userState before saving * feat(h5pexpress): add ContentUserDataRouter * refactor(h5p-server): add deleteAllContentUserDataforContentId method * refactor(h5p-examples): add reference implementation to h5p-example * docs(ContentUserDataStorage): add contentUserDataStorage docs * feat(contentUserData): add setFinished * fix(ContentUserDataManager): throw H5pError instead of regular error * fix(contentUserStateSaveInterval): change from seconds to milliseconds Lumieducation/H5P-Nodejs-library#1886 (comment) * refactor(code): remove redundant return statements * refactor(code): reorder import statements * refactor(log): change to debug from info * refactor(code): remove typos, reorder imports, remove redundant code * refactor(h5p-examples): don't use singleton * fix(H5PPlayer): use contentUserStateSaveInterval in milliseconds * refactor(code): fix typos * fix(contentUserDataManager): saveContentUserData: check arguments * fix(h5p-examples): correct json number declaration (#1991) * feat(listContentUserDataByUserId): add listContentUserDataByUserId * style(jsdocs): add divergence * refactor: formatting and minor issues * refactor: fixed imports * test: corrected test * refactor: decreased content user state save interval * test: fix test * fix(contentUserDataManager): remove userData when invalidate is true Lumieducation/H5P-Nodejs-library#1886 (comment) * fix(contentUserData): generate integration only when preload is true Lumieducation/H5P-Nodejs-library#1886 (comment) * feat(FileContentUserDataStorage): add FileContentUserDataStorage * feat(contentUserData): include contentUserData in rest-example * feat(contentUserData): include in rest example * test(contentUserDataRouter): fix test * chore(contentUserDataStorage): add json to gitignore * build(h5p-shared-state-server): build h5p-shared-state-server on install * fix(contentUserData): delete invalid contentUserData if content changes Lumieducation/H5P-Nodejs-library#1886 (comment) * refactor(contentUserData): rename saveContentUserData saveContentUserData-method is renamed to createOrUpdateContentUserData * feat(mongos3): add MongoContentUserDataStorage * refactor: made namings more consistent * refactor: minor cleanup * fix: examples work * test: use snapshot instead of inline HTML * feat: corrected and extended MongoContentUserDataStorage * fix: corrected FileContentUserDataStorage signature * test: added more tests * test: more tests * feat: delete finished data when content is deleted * feat: reimplemented FileContentUserDataStorage to work with directories * test: added tests for FileContentUserDataStorage * test: renamed generalized test file * fix: missing user data doesn't return 404 * fix: corrected CSFR token generation + own route for setFinished * fix: protected FileContentUserDataStorage against attacks * test: corrected tests * fix: corrected config to load falsy settings * fix: improved filename validation * feat: routes are closed when feature disabled in config * feat: added CSRF tokens to rest example & fixed bugs * fix: fileContentUserDataStorage saves more than one entry * test: corrected test * feat: improved CSRF * refactor: correct shared state example * refactor: cleanup * docs: added docs Co-authored-by: Oliver Tacke <[email protected]> Co-authored-by: Sebastian Rettig <[email protected]>
Is your feature request related to a problem? Please describe.
When using the node.js port of H5P core, you cannot save the current content's state and resume later.
Describe the solution you'd like
Describe alternatives you've considered
People who want the "save content state" option on their platform could come up with custom solutions to implement in content types, but that would not be efficient on a global scale and defeat the purpose of H5P content being shareable across platforms.
The text was updated successfully, but these errors were encountered: