feat: videos for all users and moderators

Admin may change the "published" status of a video. users/moderators may only see the published ones.
pull/281/head
Sebastien Marinier 2024-05-29 17:25:29 +02:00
parent 9fed698723
commit ac2626bc2c
6 changed files with 71 additions and 7 deletions

View File

@ -14,6 +14,7 @@ return [
['name' => 'server#check', 'url' => '/server/check', 'verb' => 'POST'], ['name' => 'server#check', 'url' => '/server/check', 'verb' => 'POST'],
['name' => 'server#version', 'url' => '/server/version', 'verb' => 'GET'], ['name' => 'server#version', 'url' => '/server/version', 'verb' => 'GET'],
['name' => 'server#delete_record', 'url' => '/server/record/{recordId}', 'verb' => 'DELETE'], ['name' => 'server#delete_record', 'url' => '/server/record/{recordId}', 'verb' => 'DELETE'],
['name' => 'server#publish_record', 'url' => '/server/record/{recordId}/publish', 'verb' => 'POST'],
['name' => 'join#index', 'url' => '/b/{token}/{moderatorToken}', 'verb' => 'GET', 'defaults' => ['moderatorToken' => '']], ['name' => 'join#index', 'url' => '/b/{token}/{moderatorToken}', 'verb' => 'GET', 'defaults' => ['moderatorToken' => '']],
['name' => 'restriction#user', 'url' => '/restrictions/user', 'verb' => 'GET'], ['name' => 'restriction#user', 'url' => '/restrictions/user', 'verb' => 'GET'],
['name' => 'hook#meetingEnded', 'url' => '/hook/ended/{token}/{mac}', 'verb' => 'GET'], ['name' => 'hook#meetingEnded', 'url' => '/hook/ended/{token}/{mac}', 'verb' => 'GET'],

View File

@ -10,6 +10,7 @@ use BigBlueButton\Parameters\GetRecordingsParameters;
use BigBlueButton\Parameters\InsertDocumentParameters; use BigBlueButton\Parameters\InsertDocumentParameters;
use BigBlueButton\Parameters\IsMeetingRunningParameters; use BigBlueButton\Parameters\IsMeetingRunningParameters;
use BigBlueButton\Parameters\JoinMeetingParameters; use BigBlueButton\Parameters\JoinMeetingParameters;
use BigBlueButton\Parameters\PublishRecordingsParameters;
use OCA\BigBlueButton\AppInfo\Application; use OCA\BigBlueButton\AppInfo\Application;
use OCA\BigBlueButton\AvatarRepository; use OCA\BigBlueButton\AvatarRepository;
use OCA\BigBlueButton\Crypto; use OCA\BigBlueButton\Crypto;
@ -262,6 +263,14 @@ class API {
return $response->isDeleted(); return $response->isDeleted();
} }
public function publishRecording(string $recordingId, bool $published): bool {
$publishParams = new PublishRecordingsParameters($recordingId, $published);
$response = $this->getServer()->publishRecordings($publishParams);
return $response->isPublished();
}
/** /**
* @return (array|bool|int|string)[] * @return (array|bool|int|string)[]
* *

View File

@ -95,9 +95,9 @@ class ServerController extends Controller {
$recordings = $this->server->getRecordings($room); $recordings = $this->server->getRecordings($room);
if (!$this->permission->isAdmin($room, $this->userId)) { if (!$this->permission->isAdmin($room, $this->userId)) {
$recordings = array_filter($recordings, function ($recording) { $recordings = array_values(array_filter($recordings, function ($recording) {
return $recording['published']; return $recording['published'];
}); }));
} }
return new DataResponse($recordings); return new DataResponse($recordings);
@ -124,6 +124,27 @@ class ServerController extends Controller {
return new DataResponse($success); return new DataResponse($success);
} }
/**
* @NoAdminRequired
*/
public function publishRecord(string $recordId, bool $published): DataResponse {
$record = $this->server->getRecording($recordId);
$room = $this->service->findByUid($record['meetingId']);
if ($room === null) {
return new DataResponse(false, Http::STATUS_NOT_FOUND);
}
if (!$this->permission->isAdmin($room, $this->userId)) {
return new DataResponse(false, Http::STATUS_FORBIDDEN);
}
$success = $this->server->publishRecording($recordId, $published);
return new DataResponse($success);
}
public function check(?string $url, ?string $secret): DataResponse { public function check(?string $url, ?string $secret): DataResponse {
if ($url === null || empty($url) || $secret === null || empty($secret)) { if ($url === null || empty($url) || $secret === null || empty($secret)) {
return new DataResponse(false); return new DataResponse(false);

View File

@ -201,6 +201,14 @@ class Api {
return response.data; return response.data;
} }
public async publishRecording(id: string, publish: boolean,) {
const response = await axios.post(this.getUrl(`server/record/${id}/publish`), {
published: publish,
});
return response.data;
}
public async storeRecording(recording: Recording, path: string) { public async storeRecording(recording: Recording, path: string) {
const startDate = new Date(recording.startTime); const startDate = new Date(recording.startTime);
const filename = `${encodeURIComponent(recording.name + ' ' + startDate.toISOString())}.url`; const filename = `${encodeURIComponent(recording.name + ' ' + startDate.toISOString())}.url`;

View File

@ -4,11 +4,29 @@ import { Recording } from '../Common/Api';
type Props = { type Props = {
recording: Recording; recording: Recording;
isAdmin : boolean;
deleteRecording: (recording: Recording) => void; deleteRecording: (recording: Recording) => void;
storeRecording: (recording: Recording) => void; storeRecording: (recording: Recording) => void;
publishRecording: (recording: Recording, publish: boolean) => void;
} }
const RecordingRow: React.FC<Props> = ({recording, deleteRecording, storeRecording}) => { const RecordingRow: React.FC<Props> = ({recording, isAdmin, deleteRecording, storeRecording, publishRecording}) => {
function checkPublished(recording: Recording, onChange: (value: boolean) => void) {
return (
<div>
<input id={`bbb-record-state-${recording.id}`}
type="checkbox"
className="checkbox"
checked={recording.state === 'published'}
onChange={(event) => onChange(event.target.checked)} />
<label htmlFor={`bbb-record-state-${recording.id}`}>{t('bbb', 'Published')}</label>
</div>
);
}
return ( return (
<tr key={recording.id}> <tr key={recording.id}>
<td className="start icon-col"> <td className="start icon-col">
@ -40,10 +58,17 @@ const RecordingRow: React.FC<Props> = ({recording, deleteRecording, storeRecordi
<td> <td>
{recording.type} {recording.type}
</td> </td>
<td>
{isAdmin && checkPublished(recording, (checked) => {
publishRecording(recording, checked);
})}
</td>
<td className="remove icon-col"> <td className="remove icon-col">
<button className="action-item" onClick={() => deleteRecording(recording)} title={t('bbb', 'Delete')}> {isAdmin &&
<span className="icon icon-delete icon-visible"></span> <button className="action-item" onClick={() => deleteRecording(recording)} title={t('bbb', 'Delete')}>
</button> <span className="icon icon-delete icon-visible"></span>
</button>
}
</td> </td>
</tr> </tr>
); );

View File

@ -261,7 +261,7 @@ const RoomRow: React.FC<Props> = (props) => {
</td> </td>
} }
<td className="bbb-shrink"> <td className="bbb-shrink">
{(adminRoom || true ) && {
<RecordingsNumber recordings={recordings} showRecordings={showRecordings} setShowRecordings={setShowRecordings} /> <RecordingsNumber recordings={recordings} showRecordings={showRecordings} setShowRecordings={setShowRecordings} />
} }
</td> </td>