Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 33 additions & 9 deletions app/controllers/redirect.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
use Opencast\Models\VideosShares;
use Opencast\Models\LTI\LtiHelper;
use Opencast\Models\REST\ApiEventsClient;
use Opencast\Models\REST\RestClient;
use Opencast\Errors\Error;
use Opencast\Errors\MaintenanceError;

class RedirectController extends Opencast\Controller
{
Expand Down Expand Up @@ -231,10 +233,13 @@ private function getLtiLinkFor($lti, $endpoint)
}

/**
* Load preview image from opencast and show it
* Serves a video preview image for the given video token.
*
* @param String $episode_id
* Checks user permissions, fetches the preview image from Opencast if available,
* or falls back to a default image. Sets the appropriate content type header and outputs the image.
*
* @param string $token The video token.
* @throws \Exception If access is denied.
* @return void
*/
public function preview_action($token)
Expand All @@ -247,17 +252,36 @@ public function preview_action($token)
throw new \Exception('Access denied!');
}

// get preview image
$api_events = ApiEventsClient::getInstance($video->config_id);
$plugin_path = $GLOBALS['ABSOLUTE_PATH_STUDIP'] . $this->plugin->getPluginPath();
$default_image_relative_path = '/assets/images/default-preview.png';
$image_default_url = URLHelper::getURL($this->plugin->getPluginUrl() . $default_image_relative_path);
$image_mimetype = mime_content_type($plugin_path . $default_image_relative_path);
$image_content = null;
if ($video->preview) {
$rest_client = null;
try {
$rest_client = ApiEventsClient::getInstance($video->config_id);
} catch (\Throwable $th) {
if ($th instanceof MaintenanceError) {
$rest_client = RestClient::createReadOnlyInstance($video->config_id);
}
}

if (!empty($rest_client)) {
$response = $rest_client->fileRequest($video->preview);
$image_content = $response['body'] ?? null;
$image_mimetype = $response['mimetype'] ?? $image_mimetype;
}
}

$image = $video->preview ?
: URLHelper::getURL($this->plugin->getPluginUrl() . '/assets/images/default-preview.png');
if (empty($image_content)) {
$image_content = file_get_contents($image_default_url);
}

$response = $api_events->fileRequest($image);
header('Content-Type: '. $image_mimetype);

header('Content-Type: '. $response['mimetype'][0]);
echo $image_content;

echo $response['body'];
die;
}
}
16 changes: 15 additions & 1 deletion assets/css/opencast.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1477,4 +1477,18 @@ label.oc--file-upload {
.oc--loading-indicator {
left: 50%;
}
}
}

/* * * * * * * * * * * */
/* MESSAGE BOX */
/* * * * * * * * * * * */

.messagebox {
.messagebox-html-container {
font-weight: 400 !important;
margin: 0px !important;
> p {
margin: 0px !important;
}
}
}
4 changes: 2 additions & 2 deletions cronjobs/opencast_clear_recycle_bin.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public function execute($last_result, $parameters = array())
foreach ($videos_by_config as $config_id => $videos) {
echo 'Working on config with id #' . $config_id . "\n";

// Checking the connection.
if (!$this->isOpencastReachable($config_id)) {
// Checking the connection and maintenance status.
if (!$this->isOpencastReachable($config_id) || $this->isOpencastUnderMaintenance($config_id)) {
continue;
}

Expand Down
3 changes: 2 additions & 1 deletion cronjobs/opencast_discover_videos.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ public function execute($last_result, $parameters = array())
foreach ($configs as $config) {
echo 'Working on config with id #' . $config->id . "\n";

if (!$this->isOpencastReachable($config->id)) {
// Checking the connection and maintenance status.
if (!$this->isOpencastReachable($config->id) || $this->isOpencastUnderMaintenance($config->id)) {
continue;
}

Expand Down
3 changes: 2 additions & 1 deletion cronjobs/opencast_refresh_scheduling.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ public function execute($last_result, $parameters = array())

echo 'Working on config with id #' . $config_id . "\n";

if (!$this->isOpencastReachable($config_id)) {
// Checking the connection and maintenance status.
if (!$this->isOpencastReachable($config_id) || $this->isOpencastUnderMaintenance($config_id)) {
continue;
}

Expand Down
3 changes: 2 additions & 1 deletion cronjobs/opencast_sync_acls.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public function execute($last_result, $parameters = array())
foreach ($configs as $config) {
echo 'Working on config with id #' . $config->id . "\n";

if (!$this->isOpencastReachable($config->id)) {
// Checking the connection and maintenance status.
if (!$this->isOpencastReachable($config->id) || $this->isOpencastUnderMaintenance($config->id)) {
continue;
}

Expand Down
3 changes: 2 additions & 1 deletion cronjobs/opencast_sync_playlists.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public function execute($last_result, $parameters = array())
foreach ($configs as $config) {
echo 'Working on config with id #' . $config->id . "\n";

if (!$this->isOpencastReachable($config->id)) {
// Checking the connection and maintenance status.
if (!$this->isOpencastReachable($config->id) || $this->isOpencastUnderMaintenance($config->id)) {
continue;
}

Expand Down
12 changes: 12 additions & 0 deletions lib/Errors/MaintenanceError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Opencast\Errors;

class MaintenanceError extends Error
{
function __construct($message = null)
{
$message = $message ?? _('Opencast befindet sich derzeit in Wartung');
parent::__construct($message, 503);
}
}
28 changes: 28 additions & 0 deletions lib/Helpers/CronjobUtils/OpencastConnectionCheckerTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Opencast\Helpers\CronjobUtils;

use Opencast\Models\REST\Config as OCConfig;
use Opencast\Models\Config as ConfigModel;

trait OpencastConnectionCheckerTrait
{
Expand All @@ -11,6 +12,11 @@ trait OpencastConnectionCheckerTrait
*/
private $not_reachable_message = 'Opencast is currently unreachable. Process postponed.';

/**
* @var string $not_reachable_message Message to display when Opencast is not reachable.
*/
private $maintenance_message = 'Opencast is currently under maintenance. Process postponed.';

/**
* Checks if the Opencast instance is reachable for the given configuration ID.
*
Expand All @@ -35,4 +41,26 @@ protected function isOpencastReachable(int $config_id, bool $check_engage_node =
}
return $is_opencast_reachable;
}

/**
* Checks if the Opencast instance is under maintenance for the given configuration ID.
*
* This method checks if the Opencast instance is currently under maintenance.
* If it is, it prints a message and returns true. Otherwise, it returns false.
*
* @param int $config_id The ID of the Opencast configuration to check.
* @return bool True if Opencast is under maintenance, false otherwise.
*/
protected function isOpencastUnderMaintenance(int $config_id)
{
$oc_config = ConfigModel::findOneBySql("id = ?", [$config_id]);
if ($oc_config) {
list($maintenance_active, $read_only) = $oc_config->isUnderMaintenance();
if ($maintenance_active) {
echo $this->maintenance_message . "\n";
return true;
}
}
return false;
}
}
87 changes: 78 additions & 9 deletions lib/Models/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class Config extends \SimpleOrMap
'lti_consumerkey', 'lti_consumersecret', 'debug', 'ssl_ignore_cert_errors', 'episode_id_role_access'
];

const MAINTENANCE_MODE_OFF = 'off';
const MAINTENANCE_MODE_ON = 'on';
const MAINTENANCE_MODE_READONLY = 'read-only';

protected static function configure($config = [])
{
$config['db_table'] = 'oc_config';
Expand All @@ -28,27 +32,62 @@ protected static function configure($config = [])
}

/**
* function getConfigForService - retries configutation for a given REST-Service-Client
*
* @param string $service_type - client label
* Returns the merged configuration and endpoint data for a given service type and config ID.
*
* @return array configuration for corresponding client
* Retrieves the Opencast configuration and endpoint for the specified service type and configuration ID,
* taking maintenance mode into account. Returns false if configuration or endpoint is missing,
* or null if access is not allowed due to maintenance restrictions.
*
* @param string $service_type The type of Opencast service (e.g., 'ingest', 'search').
* @param int $config_id The configuration ID (default: 1).
* @return array|false|null Merged configuration and endpoint data, false if not found, or null if not accessible.
* @throws \Exception In case something goes wrong!
*/
public static function getConfigForService($service_type, $config_id = 1)
{
if (isset($service_type)) {
$config = Endpoints::findOneBySQL(
$result = [];

$oc_config = self::find($config_id);
$endpoint_config = Endpoints::findOneBySQL(
'service_type = ? AND config_id = ?' ,
[$service_type, $config_id]
);

if ($config) {
return $config->toArray() + self::find($config_id)->toArray();
} else {
if (empty($oc_config) || empty($endpoint_config)) {
return false;
}

list($maintenance_on, $maintenance_readonly) = $oc_config->isUnderMaintenance();

$can_access = !$maintenance_on ||
($maintenance_readonly && in_array($service_type, RESTConfig::ENGAGE_NODE_SERVICE_TYPES));

if (!$can_access) {
return null;
}

$oc_config_array = $oc_config->toArray();
$endpoint_config_array = $endpoint_config->toArray();

// Here we need to replace the service_url with the maintenance_engage_url_fallback (1. priority) if provided in config
// or the endpoint service_url (2. priority)
if ($maintenance_readonly) {
$replacing_server_url = $oc_config_array['maintenance_engage_url_fallback'];
if (empty($replacing_server_url)) {
$replacing_server_url = $endpoint_config_array['service_url'];
}
$replacing_server_url_parsed = parse_url($replacing_server_url);

$replacing_server_url_clean = $replacing_server_url_parsed['scheme'] . '://'. $replacing_server_url_parsed['host']
. (isset($replacing_server_url_parsed['port']) ? ':' . $replacing_server_url_parsed['port'] : '');

$oc_config_array['service_url'] = $replacing_server_url_clean;
}

$result = $endpoint_config_array + $oc_config_array;

return $result;
} else {
throw new \Exception(_("Es wurde kein Servicetyp angegeben."));
}
Expand Down Expand Up @@ -163,6 +202,18 @@ public function updateSettings($json)
*/
public function updateEndpoints()
{

list($maintenance_on, $maintenance_readonly) = $this->isUnderMaintenance();

// Prevent updating endpoints if the server is under maintenance.
if ($maintenance_on) {
$message = [
'type' => 'warning',
'text' => _('Diese Opencast-Instanz ist derzeit im Wartungsmodus, daher können Endpunkte nicht aktualisiert werden.')
];
return $message;
}

$service_url = parse_url($this->service_url);

// check the selected url for validity
Expand Down Expand Up @@ -257,7 +308,7 @@ public function updateEndpoints()
}

// create new entries for workflow_config table
WorkflowConfig::createAndUpdateByConfigId($this->id, $workflows);
WorkflowConfig::createAndUpdateByConfigId($this->id);

$success_message[] = sprintf(
_('Die Opencast-Installation "%s" wurde erfolgreich konfiguriert.'),
Expand All @@ -282,4 +333,22 @@ public function updateEndpoints()

return $message;
}

/**
* Checks if the Opencast instance is in maintenance mode.
*
* @param bool $with_keys Whether to return an associative array with keys ('active', 'read_only').
* @return array [maintenance_on, maintenance_readonly] or ['active' => bool, 'read_only' => bool] if $with_keys is true.
*/
public function isUnderMaintenance($with_keys = false)
{
$maintenance_on = ($this->maintenance_mode === self::MAINTENANCE_MODE_ON
|| $this->maintenance_mode === self::MAINTENANCE_MODE_READONLY);

$res = [
'active' => $maintenance_on,
'read_only' => $this->maintenance_mode === self::MAINTENANCE_MODE_READONLY
];
return $with_keys ? $res : array_values($res);
}
}
15 changes: 14 additions & 1 deletion lib/Models/LTI/LtiHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Opencast\Models\Config;
use Opencast\Models\Endpoints;
use Opencast\Providers\Perm;
use Opencast\Models\REST\Config as RESTConfig;

/**
* LTI Helper class to create launch data
Expand All @@ -22,15 +23,27 @@ class LtiHelper
public static function getLtiLinks($config_id)
{
$links = [];
$endpoints = Endpoints::findByConfig_id($config_id);
$config = Config::find($config_id);

list($maintenance_active, $read_only) = $config->isUnderMaintenance();

if ($maintenance_active && !$read_only) {
return [];
}

$endpoints = Endpoints::findByConfig_id($config_id);

foreach ($endpoints as $endpoint) {
// skip 'services' endpoints
if ($endpoint->service_type == 'services') {
continue;
}

// In read-only maintenance mode, we only allow engage node services!
if ($read_only && !in_array($endpoint->service_type, RESTConfig::ENGAGE_NODE_SERVICE_TYPES)) {
continue;
}

$url = parse_url($endpoint['service_url']);

$lti_url = $url['scheme'] . '://'. $url['host']
Expand Down
12 changes: 4 additions & 8 deletions lib/Models/REST/ApiEventsClient.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
<?php
namespace Opencast\Models\REST;

use Opencast\Models\Config;
use Opencast\Models\Helpers;
use Opencast\Models\Pager;

class ApiEventsClient extends RestClient
{
public static $me;
public $serviceName = 'ApiEvents';

public function __construct($config_id = 1)
{
if ($config = Config::getConfigForService('apievents', $config_id)) {
parent::__construct($config);
} else {
throw new \Exception ($this->serviceName . ': '
. _('Die Opencast-Konfiguration wurde nicht korrekt angegeben'));
}
$this->serviceName = 'ApiEvents';
$this->serviceType = 'apievents';
$config = $this->getConfigForClient($config_id);
parent::__construct($config);
}

/**
Expand Down
Loading