Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
fca4638
Update h5pcore dep to 1.28
edlib-oddarne Apr 27, 2026
719060d
Remove deprecation warning
edlib-oddarne Apr 27, 2026
2acd71f
Add theme styles
edlib-oddarne Apr 27, 2026
a55b119
Update tests to check for v1.28
edlib-oddarne Apr 27, 2026
55bcbbb
Squarify the "Show answer" and "Try again" button is question summary.
edlib-oddarne Jun 9, 2026
59baa25
Add possibility to include custom styles.
edlib-oddarne Jun 9, 2026
b4fea09
Small refactor
edlib-oddarne Jun 11, 2026
55b95dc
Remove test
edlib-oddarne Jun 11, 2026
cf5e827
WIP
edlib-oddarne Jun 11, 2026
b57adf3
Update deps
edlib-oddarne Jun 15, 2026
4711305
WIP
edlib-oddarne Jun 16, 2026
bb41beb
WIP
edlib-oddarne Jun 19, 2026
7b26e9b
WIP: Modify CSS selector menu item.
edlib-oddarne Jun 22, 2026
140568f
WIP: Modify CSS selector only if version >=1.27.
edlib-oddarne Jun 22, 2026
ff467de
WIP: Refactor
edlib-oddarne Jun 22, 2026
6415375
WIP: UI
edlib-oddarne Jun 22, 2026
a6a033b
WIP
edlib-oddarne Jun 23, 2026
d7bf9e5
Fix: PHPStan error on new attribute
edlib-oddarne Jun 23, 2026
4f9a38a
Fix: rendering stops
edlib-oddarne Jun 23, 2026
8d201d8
Fix: rendering stops
edlib-oddarne Jun 23, 2026
cd7bd33
Fix: rendering stops
edlib-oddarne Jun 23, 2026
8ec5bb5
Fix: rendering stops
edlib-oddarne Jun 23, 2026
de4059e
Merge remote-tracking branch 'origin/feat/upgrade-to-h5p-core-1.28' i…
edlib-oddarne Jun 23, 2026
6d0bf51
Fix: rendering stops
edlib-oddarne Jun 23, 2026
649c9ba
WIP: Add support for modifyCss property. Users can now enable CSS "re…
edlib-oddarne Jun 26, 2026
1a31741
Merge remote-tracking branch 'origin/master' into feat/upgrade-to-h5p…
edlib-oddarne Aug 4, 2026
086e456
Add H5P cached assets cleanup command and enhance asset handling logi…
edlib-oddarne Aug 31, 2026
5675c71
Add admin interface for managing and clearing H5P cached assets on di…
edlib-oddarne Sep 1, 2026
7d85489
Merge branch 'master' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Sep 1, 2026
e911cda
Improve formatting and add missing session_scope token handling for c…
edlib-oddarne Sep 2, 2026
736f9e3
Add "Select All" checkbox and bulk selection script in content exclus…
edlib-oddarne Sep 2, 2026
04448c7
Allow searching multiple content IDs (comma-separated) in content exc…
edlib-oddarne Sep 2, 2026
3d07e0f
Highlight excluded content in search results and add corresponding test
edlib-oddarne Sep 2, 2026
12fdd82
Allow searching multiple content titles (comma-separated) in content …
edlib-oddarne Sep 2, 2026
0cbb43c
Merge branch 'feat/search-multiple-ids-exclusion' into feat/upgrade-t…
edlib-oddarne Sep 8, 2026
9d0c33e
Merge branch 'master' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Sep 10, 2026
55af8d4
Add background deletion queue for cached files, implement status chec…
edlib-oddarne Sep 10, 2026
b1431d1
Add detailed logging for caching assets in `H5PCerpusStorage`, includ…
edlib-oddarne Sep 11, 2026
68a39ed
Refactor caching logic in `H5PCerpusStorage` to prioritize filesystem…
edlib-oddarne Sep 14, 2026
e272ad1
Improve `H5PCerpusStorage` logging by including asset paths in debug …
edlib-oddarne Sep 17, 2026
48687dd
Enhance `H5PCerpusStorage` logging with asset size details for filesy…
edlib-oddarne Sep 22, 2026
c5f5362
Update dependencies: add `laravel/boost`.
edlib-oddarne Sep 22, 2026
c5df4bf
Add unique run identifier to `H5PCerpusStorage` logging for improved …
edlib-oddarne Sep 23, 2026
226241a
Add `runNo` to `H5PCerpusStorage` logging and tests for consistent tr…
edlib-oddarne Sep 23, 2026
b28f334
Merge branch 'fix/content-edit-locks' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Sep 25, 2026
e240bf0
Merge branch 'fix/content-edit-locks' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Sep 25, 2026
5232baa
Clarify polling interval format description in hub setup documentation
edlib-oddarne Sep 25, 2026
444880e
Merge branch 'master' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Oct 14, 2026
d38f413
Merge branch 'master' into feat/upgrade-to-h5p-core-1.28
edlib-oddarne Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions sourcecode/apis/contentauthor/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,8 @@ buildid
test-storage-volume/
/storage/framework/testing/
/tools/vapor-cli/vendor

.junie
.ai
boost.json
AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace App\Console\Commands;

use App\H5PLibrariesCachedAssets;
use App\Libraries\DataObjects\ContentStorageSettings;
use H5PFileStorage;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\Console\Command\Command as SymfonyCommand;

class H5PClearCachedAssets extends Command implements Isolatable
{
public const string CACHE_KEY_RUNNING = 'h5p_clearing_cached_assets';
public const int CACHE_KEY_RUNNING_TTL = 1 * 60;

/**
* Clear the running cache key and command isolation mutex.
*/
public static function clearLock(): void
{
Cache::forget(self::CACHE_KEY_RUNNING);

try {
$mutex = resolve(\Illuminate\Console\CommandMutex::class);
$command = resolve(self::class);
$mutex->forget($command);
} catch (\Throwable) {
}
}

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'h5p:clear-cached-assets {--unlock : Remove the running lock without deleting assets}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Delete aggregated library assets, they will be regenerated on demand';

/**
* Execute the console command.
*/
public function handle(H5PFileStorage $storage): int
{
if ($this->option('unlock')) {
self::clearLock();
$this->info('Deletion lock has been removed.');

return SymfonyCommand::SUCCESS;
}

Cache::put(self::CACHE_KEY_RUNNING, true, now()->addMinutes(self::CACHE_KEY_RUNNING_TTL));

try {
$hashes = H5PLibrariesCachedAssets::distinct()->pluck('hash')->all();
$storage->deleteCachedAssets($hashes);
H5PLibrariesCachedAssets::query()->delete();

// Files without a matching row will never be regenerated, so get rid of
// those as well.
$disk = Storage::disk();
$orphans = 0;
foreach ($disk->files(ContentStorageSettings::CACHEDASSETS_DIR) as $file) {
$disk->delete($file);
$orphans++;
}

$this->line(sprintf(
'Deleted <fg=yellow>%d</> cached asset bundle(s), <fg=yellow>%d</> file(s) removed.',
count($hashes),
$orphans,
));
} finally {
Cache::forget(self::CACHE_KEY_RUNNING);
}

return SymfonyCommand::SUCCESS;
}
}
3 changes: 3 additions & 0 deletions sourcecode/apis/contentauthor/app/H5PContent.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
* @property string $language_iso_639_3
* @property ?int $max_score
* @property int $bulk_calculated
* @property bool $modify_css
* @property bool $modifyCss
*
* @property Collection<Collaborator> $collaborators
* @property H5PLibrary $library
Expand Down Expand Up @@ -66,6 +68,7 @@ class H5PContent extends Content implements VersionableObject
protected $casts = [
'library_id' => "int",
'is_draft' => 'boolean',
'modify_css' => 'boolean',
];

protected $dispatchesEvents = [
Expand Down
20 changes: 17 additions & 3 deletions sourcecode/apis/contentauthor/app/H5PLibrary.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
use App\Observers\H5PLibraryObserver;
use H5PFrameworkInterface;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;

/**
Expand Down Expand Up @@ -40,6 +41,7 @@
* @property int $major_version
* @property int $minor_version
* @property int $patch_version
* @property string $version
* @property int $runnable
* @property int $restricted
* @property int $fullscreen
Expand Down Expand Up @@ -337,7 +339,7 @@ public function getAddons()
->whereNotNull('l1.add_to')
->get()
->map(function ($addon) {
return (array) $addon;
return (array)$addon;
})
->toArray();
}
Expand Down Expand Up @@ -383,4 +385,16 @@ public static function canBeDeleted(int $libraryId, int|null $usageCount = null)

return $usageCount === 0 && H5PContentLibrary::where('library_id', $libraryId)->doesntExist();
}

protected function version(): Attribute
{
return Attribute::make(
get: fn(mixed $value, array $attributes) => $attributes['major_version'] . '.' . $attributes['minor_version'] . '.' . $attributes['patch_version'],
);
}

public function versionCompare(string $version, string $operation = '>='): bool
{
return version_compare($this->version, $version, $operation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Console\Commands\H5PClearCachedAssets;
use App\H5PLibrariesCachedAssets;
use App\Libraries\DataObjects\ContentStorageSettings;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;

final class CachedFilesController
{
public function index(): View
{
$disk = Storage::disk();
$cachedFiles = $disk->files(ContentStorageSettings::CACHEDASSETS_DIR);
$cachedAssetRowsCount = H5PLibrariesCachedAssets::count();
$isJobRunning = Cache::has(H5PClearCachedAssets::CACHE_KEY_RUNNING);

return view('admin.cached-files.index', [
'cachedFilesCount' => count($cachedFiles),
'cachedAssetRowsCount' => $cachedAssetRowsCount,
'isJobRunning' => $isJobRunning,
]);
}

public function delete(): RedirectResponse
{
Cache::put(H5PClearCachedAssets::CACHE_KEY_RUNNING, true, now()->addMinutes(H5PClearCachedAssets::CACHE_KEY_RUNNING_TTL));

Artisan::queue('h5p:clear-cached-assets');

return redirect(route('admin.cached-files.index'))
->with('message', 'Cached files deletion has been queued.');
}

public function status(): JsonResponse
{
return response()->json([
'isRunning' => Cache::has(H5PClearCachedAssets::CACHE_KEY_RUNNING),
]);
}

public function unlock(): RedirectResponse
{
H5PClearCachedAssets::clearLock();

return redirect(route('admin.cached-files.index'))
->with('message', 'The deletion lock has been removed.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
use stdClass;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Throwable;

use function app;
use function config;
use function request;
Expand All @@ -71,10 +70,11 @@ class H5PController extends Controller
private string $viewDataCacheName = 'viewData-';

public function __construct(
private Lti $lti,
private h5p $h5p,
private Lti $lti,
private h5p $h5p,
private H5PLibraryAdmin $h5pLibraryAdmin,
) {
)
{
$this->middleware('adaptermode', ['only' => ['show', 'edit', 'update', 'store', 'create']]);
$this->middleware('core.return', ['only' => ['create', 'edit']]);
$this->middleware('core.locale', ['only' => ['create', 'edit', 'store']]);
Expand Down Expand Up @@ -168,7 +168,8 @@ public function create(Request $request, H5PCore $core, $contenttype = null): Vi
$editorSetup = H5PEditorConfigObject::create([
'canList' => true,
'showDisplayOptions' => config('h5p.showDisplayOptions'),
'useLicense' => config('feature.licensing') === true || config('feature.licensing') === '1',
'showModifyCss' => false, // New content always uses the default css
'modifyCss' => false,
'adapterName' => config('feature.allow-mode-switch') === true ? $adapter->getAdapterName() : null,
'adapterList' => $adapter::getAllAdapters(),
'h5pLanguage' => Iso639p3::code2letters($language),
Expand All @@ -178,15 +179,15 @@ public function create(Request $request, H5PCore $core, $contenttype = null): Vi
]);

$state = H5PStateDataObject::create($displayOptions + [
'library' => $contenttype,
'license' => License::getDefaultLicense(),
'isPublished' => $ltiRequest?->getPublished() ?? false,
'isShared' => $ltiRequest?->getShared() ?? false,
'language_iso_639_3' => $language,
'redirectToken' => $request->get('redirectToken'),
'route' => route('h5p.store'),
'_method' => "POST",
])->toJson();
'library' => $contenttype,
'license' => License::getDefaultLicense(),
'isPublished' => $ltiRequest?->getPublished() ?? false,
'isShared' => $ltiRequest?->getShared() ?? false,
'language_iso_639_3' => $language,
'redirectToken' => $request->get('redirectToken'),
'route' => route('h5p.store'),
'_method' => "POST",
])->toJson();

return view(
'h5p.create',
Expand Down Expand Up @@ -264,6 +265,8 @@ public function edit(Request $request, int $id): View
$editorSetup = H5PEditorConfigObject::create([
'canList' => $h5pContent->canList($request),
'showDisplayOptions' => config('h5p.showDisplayOptions'),
'showModifyCss' => $adapter->showCustomCssForNewContentTypes($h5pContent),
'modifyCss' => $h5pContent->modify_css,
'useLicense' => config('feature.licensing') === true || config('feature.licensing') === '1',
'h5pLanguage' => $h5pLanguage,
'editorLanguage' => Session::get('locale', config('app.fallback_locale')),
Expand All @@ -286,29 +289,29 @@ public function edit(Request $request, int $id): View
$displayOptions['download'] = $displayOptions['export'];

$editorSetup->setContentProperties(ResourceInfoDataObject::create([
'id' => (string) $content['id'],
'id' => (string)$content['id'],
'createdAt' => $h5pContent->created_at->toIso8601String(),
'type' => $library->getTitleAndVersionString(),
'maxScore' => $library->supportsMaxScore() ? $h5pContent->max_score : null,
'ownerName' => null,
]));

$state = H5PStateDataObject::create($displayOptions + [
'id' => $h5pContent->id,
'library' => $library->getLibraryString(false),
'libraryid' => $h5pContent->library_id,
'parameters' => $params,
'language_iso_639_3' => $contentLanguage,
'title' => $h5pContent->title,
'license' => $h5pContent->license ?: License::getDefaultLicense(),
'isPublished' => $ltiRequest?->getPublished() ?? false,
'isDraft' => $h5pContent->isDraft(),
'isShared' => $ltiRequest?->getShared() ?? false,
'redirectToken' => $request->get('redirectToken'),
'route' => route('h5p.update', ['h5p' => $id]),
'max_score' => $h5pContent->max_score,
'_method' => "PUT",
])->toJson();
'id' => $h5pContent->id,
'library' => $library->getLibraryString(false),
'libraryid' => $h5pContent->library_id,
'parameters' => $params,
'language_iso_639_3' => $contentLanguage,
'title' => $h5pContent->title,
'license' => $h5pContent->license ?: License::getDefaultLicense(),
'isPublished' => $ltiRequest?->getPublished() ?? false,
'isDraft' => $h5pContent->isDraft(),
'isShared' => $ltiRequest?->getShared() ?? false,
'redirectToken' => $request->get('redirectToken'),
'route' => route('h5p.update', ['h5p' => $id]),
'max_score' => $h5pContent->max_score,
'_method' => "PUT",
])->toJson();

return view(
'h5p.edit',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use App\H5PContent;
use App\Rules\LicenseContent;
use Illuminate\Foundation\Http\FormRequest;

use function assert;

class H5PStorageRequest extends FormRequest
Expand All @@ -25,6 +24,16 @@ protected function prepareForValidation(): void
'isShared' => $this->boolean('isShared'),
]);
}

if ($this->has('modifyCss')) {
$this->merge([
'modifyCss' => $this->boolean('modifyCss'),
]);
} else {
$this->merge([
'modifyCss' => false,
]);
}
}

public function rules(): array
Expand All @@ -47,6 +56,7 @@ public function rules(): array
config('app.enable_licensing') ? 'string' : 'nullable',
new LicenseContent(),
],
'modifyCss' => ['sometimes', 'boolean'],
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class EditorConfigObject

public $canList;
public $useLicense = false;
public $showModifyCss = false;
public $modifyCss = false;

protected $contentProperties;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Libraries\H5P\Adapters;

use App\H5PContent;
use App\Libraries\H5P\Dataobjects\H5PAlterParametersSettingsDataObject;
use App\Libraries\H5P\Interfaces\H5PAdapterInterface;
use App\Libraries\H5P\Interfaces\H5PAudioInterface;
Expand Down Expand Up @@ -173,4 +174,9 @@ public function filterEditorScripts(): array
{
return [];
}

public function showCustomCssForNewContentTypes(?H5PContent $h5pContent = null): bool
{
return false;
}
}
Loading
Loading