escolalms / h5p Goto Github PK
View Code? Open in Web Editor NEWHeadless H5P Laravel REST API
License: MIT License
Headless H5P Laravel REST API
License: MIT License
you should be able to fetch content by authors
each content should save by whom it was created
admin/hh5p/content
should have an query param author_id
There should be new permissions as well
Request URL: http://localhost:1000/api/hh5p/libraries/ckeditor/config.js?t=KA9B
scenario
This might be good starting point https://github.com/EscolaLMS/H5P/blob/main/src/Services/HeadlessH5PService.php#L383
How this should work
All of this is taken from API
https://api-stage.escolalms.com/api/admin/hh5p/content?pageSize=20¤t=1&per_page=20&page=1
message: "SQLSTATE[42702]: Ambiguous column: 7 ERROR: column reference \"id\" is ambiguous\nLINE 1: ...elect count(*) as aggregate from (select \"title\", \"id\", \"lib...\n ^ (SQL: select count(*) as aggregate from (select \"title\", \"id\", \"library_id\", COUNT(topic_h5ps.value) as count_h5p from \"hh5p_contents\" left join \"topic_h5ps\" on (\"topic_h5ps\".\"value\" = \"hh5p_contents\".\"id\") group by \"hh5p_contents\".\"id\") as \"aggregate_table\")"
we should have this option by setting the settings from https://github.com/EscolaLMS/H5P/blob/main/config/hh5p.php with our settings API https://github.com/EscolaLMS/Settings
This tasks have 2 subtasks
backend
frontend
Testing
see all TODO update interface and swagger
see src/Repositories/H5PEditorAjaxRepository.php
there are some methods that have empty implmentation eg
/**
* Gets recently used libraries for the current author
*
* @return array machine names. The first element in the array is the
* most recently used.
*/
public function getAuthorsRecentlyUsedLibraries()
{
// TODO implment this
return [];
}
/**
* Checks if the provided token is valid for this endpoint
*
* @param string $token The token that will be validated for.
*
* @return bool True if successful validation
*/
public function validateEditorToken($token)
{
// TODO this is better resolved
return true;
}
/**
* Get translations for a language for a list of libraries
*
* @param array $libraries An array of libraries, in the form "<machineName> <majorVersion>.<minorVersion>
* @param string $language_code
* @return array
*/
public function getTranslations($libraries, $language_code)
{
}
scenario
editor call library
endpoint and receives an error
curl 'http://localhost/api/admin/hh5p/content?page=1' \
-H 'Accept: application/json' \
-H 'Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7' \
-H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiZGRkMzMyN2ZkNzRlNmU5NDNkNWQwZmMwYzM0MTVlNDQxNGNlYmMyNDY5MzE2NzAyMDc4NzczMDFlODFkMDg3NzcwYWM0ZjExZmNkODU5NzMiLCJpYXQiOjE2NTg3NTcyODEuODg4NzkyLCJuYmYiOjE2NTg3NTcyODEuODg4NzkzLCJleHAiOjE2NjE0MzU2ODEuODg1OTA0LCJzdWIiOiIzIiwic2NvcGVzIjpbXX0.gfBd_IqK6BAWhupmJCRj_kATuDMHwK5LPmzCBUHE4IG0mMvaKsQPMKZh1lu8MeUUCLQSthBcS-55SpT_NvhzlWMJEwmcRgMykDhESEyGt6j2dnqgk4_tNHOm7t8ie8bnPLVqBY20VZ8BqAvHJVM1iv47NVw1g2B_2vU8_uw1my1J5zXodfqgoJbnSo1lzA60s7M85M7K1fOMONHZpCyG5Xj1bQV4RUb9QyfAXWEX0-8S3ouNNq2EHEdeW1cA5RXWmWaTyCk6YkRIRMdN2E4wugrlLIPFUhF8orWje1qqyfiZoFBN3wJAeztlInN0eJF570AbQnTrQpdIeiyRa3NlYYF4nm7mfW9yZIGGZkQmxYCxwnCP-XCgPonFS2AsAn3TmInJ3Elk1pL9aV83XicEYuK5UtQU98d4WwnF_ZxGOEfcufpUvDHwjt5ulWygwNiA3fLdprpbZJ-TdpUzD09KFKzg93yNMnFf6E8Qrt049W14WWLgqD-olR8XRwtyanINHvPFgAAH912RVXRP3V-HBeIfzLtpglLwuYWD0U7D3xWJ9krkvVk0wljt934mQ2SL2TDRvZ8rwA892eYagXTqliX79HHA5j9R_YYzPCLfYiUaR-94rWZHrxofR3AlJgjKZZZ36jhrpNenFLYk5TJtIm-AJNQ1__h0CzIYuURWqaU' \
-H 'Cache-Control: no-cache' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json' \
-H 'Cookie: __clnds=nnvvc412h1; structurizr.hideDiagramEditorIntroduction=true; adminer_key=660639f1065b11d01f7c274f1f992846; adminer_sid=0decc96225e3943497a16b10fb9c2991; adminer_permanent=cGdzcWw%3D-cG9zdGdyZXM%3D-ZGVmYXVsdA%3D%3D-ZGVmYXVsdA%3D%3D%3AvdhEIsqHtPkkyJxp; adminer_settings=; adminer_import=output%3Dtext%26format%3Dsql; XSRF-TOKEN=eyJpdiI6IjZQMFlXNHh0SVdBYUpncGVON296Z1E9PSIsInZhbHVlIjoiRTVVVWZnMW11Nmk0K2hwZ05seDhKMW1SYVpEYWlERUNnWkNLTnpFbDhFYnIxOUlyMlZSUmhTYkRsVGxDMWNIZlN5UEJSckZ2akdIQUR0T01UWVNxMHNzbHhEUnhkTTNXa2JzOFlmbG5mc3dZemhmQlo1RHRUditkdU9Ed1RyVDUiLCJtYWMiOiI3MTAzYjM2OTUzOGE2N2ZiNzI2MjE5N2E3NDg3ZTEyODc2NjMwYmYzY2U4MjU3YzA2YzZhYTk4YTU0MGY2YmYxIiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6InJ1L09wemdTSGsvRXVvU2VCTThLK3c9PSIsInZhbHVlIjoibExuOXR4TFRweHUrWUtRV3gvVHIyWlVzbjBkWkdCSE1WQTVrMnZrYjlkNnluakpmRVRLT3JXYjJGWjhjWU9TZkNCS1NGNUIxSHhFU0R5UlFrSGhaK0FYRkZEalBmRjA3RWswVm1KSVFJdXNHNFF0eVFQREI3WmxER0t0bnR0ekwiLCJtYWMiOiJhZTdhZmZlZjRkNjA1YWUwZTYwOWQ3ZjM5Zjc0MDMzYWZkOTZkNDFkZmFlOTU3ODQ2NWM5ZTdkZTAyNDA2ZDY4IiwidGFnIjoiIn0%3D' \
-H 'Pragma: no-cache' \
-H 'Referer: http://localhost/index' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Site: same-origin' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36' \
-H 'sec-ch-ua: ".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "macOS"' \
--compressed
{
"message": "Attempt to read property \"name\" on null",
"exception": "ErrorException",
"file": "/app/vendor/h5p/h5p-editor/h5peditor.class.php",
"line": 124,
"trace": [
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php",
"line": 257,
"function": "handleError",
"class": "Illuminate\\Foundation\\Bootstrap\\HandleExceptions",
"type": "->"
},
{
"file": "/app/vendor/h5p/h5p-editor/h5peditor.class.php",
"line": 124,
"function": "Illuminate\\Foundation\\Bootstrap\\{closure}",
"class": "Illuminate\\Foundation\\Bootstrap\\HandleExceptions",
"type": "->"
},
{
"file": "/app/vendor/escolalms/headless-h5p/src/Services/HeadlessH5PService.php",
"line": 182,
"function": "getLibraries",
"class": "H5peditor",
"type": "->"
},
{
"file": "/app/vendor/escolalms/headless-h5p/src/Http/Controllers/LibraryApiController.php",
"line": 52,
"function": "getLibraries",
"class": "EscolaLms\\HeadlessH5P\\Services\\HeadlessH5PService",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Controller.php",
"line": 54,
"function": "libraries",
"class": "EscolaLms\\HeadlessH5P\\Http\\Controllers\\LibraryApiController",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
"line": 45,
"function": "callAction",
"class": "Illuminate\\Routing\\Controller",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 261,
"function": "dispatch",
"class": "Illuminate\\Routing\\ControllerDispatcher",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 204,
"function": "runController",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 725,
"function": "run",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 141,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 116,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 726,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 703,
"function": "runRouteWithinStack",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 667,
"function": "runRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 656,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 167,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 141,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/app/vendor/escolalms/core/src/Http/Middleware/SetTimezoneForUserMiddleware.php",
"line": 26,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "EscolaLms\\Core\\Http\\Middleware\\SetTimezoneForUserMiddleware",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
"line": 31,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
"line": 40,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
"line": 86,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",
"line": 62,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\HandleCors",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php",
"line": 39,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 180,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\TrustProxies",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 116,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 142,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/app/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 111,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/app/public/index.php",
"line": 52,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
}
]
}
index endpoint should allow to search by
h5p-core
& h5p-editor
h5p
libs & contenthelper that takes array of files, combine them into one and return it
you might actually leave this in database after investigation yet
title
when you create/edit h5p content - this is already in the processed jason payload - editor requires that every content must have titlethe title in the responses/resouces should remain in the response but taken from json in other table
Route::get('content-type-cache', [LibraryApiController::class, 'contentTypeCache'])->name('hh5p.library.content-type-cache');
Route::get('content-hub-metadata-cache', [LibraryApiController::class, 'contentHubMetadata'])->name('hh5p.library.content-hub-metadata-cache');
Route::post('library-install', [LibraryApiController::class, 'libraryInstall'])->name('hh5p.ajax.library-install');
Route::post('library-upload', [LibraryApiController::class, 'libraryUpload'])->name('hh5p.ajax.library-upload');
those calls are automaticaly called by hub, yet without cookies we should be able to inject any type of security if user has permissions to call those
Scenario
It calls in first place content-type-cache
`'content-hub-metadata-cache', those might be public as this isn't very insecure yet latter ones
Route::post('library-install', [LibraryApiController::class, 'libraryInstall'])->name('hh5p.ajax.library-install');
Route::post('library-upload', [LibraryApiController::class, 'libraryUpload'])->name('hh5p.ajax.library-upload');
definitely should have security validation
except of /api/hh5p/content/{id}
all routes are for admin only so they should should be prefixed /api/admin/*
getting content by incemental integer is dummy idea
there should be uuid as content h5p attribute
/admin/hh5p/content
should list with the uuid
public route /content/{id}
should be changed into /content/{uuid}
co the player should fetch content by uuid not by id
ex https://h5p-laravel-demo.herokuapp.com/player/1 should be replaced with https://h5p-laravel-demo.herokuapp.com/player/DFG$#%DFSGSD$#
GET /api/hh5p/content/{id}
example
ID | Title | Used n times |
---|---|---|
1 | Content #1 | 0 |
2 | Content #2 | 10 |
3 | Content #3 | 1 |
4 | Content #4 | 0 |
this package should just contain method of adding this query to index endpoint
but the actuall query/method should be added later in courses or topic types
This package is missing permission policy
src/Repositories/H5PEditorAjaxRepository.php
// TODO those libraries should be filtered by maximum sem ver
/*
$versions = ['1.0.8', '1.0.9', '1.0.10'];
usort($versions, 'version_compare');
*/
so if there are two or more same libraries one on the list
return $libraries_result;
should be one with latest versions $versions = ['1.0.8', '1.0.9', '1.0.10'];
-> '1.0.10'
see the wordpress implemenation ๐ฎ https://github.com/h5p/h5p-wordpress-plugin/blob/d9072b6d1b1f40b5b37e9f57e6248ea6851346d9/admin/class-h5p-editor-wordpress-ajax.php#L10
check the code against all // TODO annotation as add configuration where it should be
details in readme.md about
cd public
ln -sf ../vendor/h5p/h5p-editor/
ln -sf ../vendor/h5p/h5p-core/
ln -sf ../storage/app/h5p/
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.