Skip to content
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

Feature/share moderators and users #281

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0219cf5
feat: list all shared rooms for users and moderators
smarinier May 14, 2024
9538c35
fix: use querybuilder for user comparaison
smarinier May 15, 2024
081d2b9
Merge pull request #1 from arawa/task/2919/room_shared_list_for_moder…
smarinier May 15, 2024
e846404
feat: manage view of rooms for moderators and users
smarinier May 15, 2024
fbf3337
Merge pull request #2 from arawa/task/2916/design_room_list
smarinier May 29, 2024
9fed698
feat: sharing rooms with moderators and users
smarinier May 29, 2024
ac2626b
feat: videos for all users and moderators
smarinier May 29, 2024
dd48d55
feat: videos for all users and moderators
smarinier May 29, 2024
65f5cd6
Merge branch 'master' into feature/share_moderators_and_users
smarinier Jun 28, 2024
35a764d
Fix(l10n): Update translations from Transifex
nextcloud-bot Jul 26, 2024
81e6c01
Fix(l10n): Update translations from Transifex
nextcloud-bot Jul 27, 2024
ac8d235
Fix(l10n): Update translations from Transifex
nextcloud-bot Jul 29, 2024
7744169
fix: 261 wordings that aren't visible in Transifex
smarinier Jul 30, 2024
f853802
Fix(l10n): Update translations from Transifex
nextcloud-bot Jul 31, 2024
f50a040
Fix(l10n): Update translations from Transifex
nextcloud-bot Aug 9, 2024
57a21b7
Fix(l10n): Update translations from Transifex
nextcloud-bot Aug 15, 2024
fbb4ae1
Fix(l10n): Update translations from Transifex
nextcloud-bot Aug 16, 2024
2a29608
fix: 261 remove some backticks
smarinier Aug 15, 2024
4095cbe
feat: manage view of rooms for moderators and users
smarinier May 15, 2024
558cc95
feat: sharing rooms with moderators and users
smarinier May 29, 2024
8587530
feat: videos for all users and moderators
smarinier May 29, 2024
a859115
fix: don't use backticks
smarinier Aug 19, 2024
ab31439
fix: bug with name of shared groups
smarinier Aug 19, 2024
7f1760c
feat: manage view of rooms for moderators and users
smarinier May 15, 2024
b099136
feat: sharing rooms with moderators and users
smarinier May 29, 2024
e34c3b6
style: disabled checkbox
smarinier Aug 19, 2024
bcf33e9
style: respect tabs
smarinier Aug 19, 2024
f57c25e
fix: no clone action for non-admin
smarinier Aug 19, 2024
b3fd04b
Merge branch 'master' into feature/share_moderators_and_users
smarinier Aug 23, 2024
95e4e4f
Merge branch 'master' into feature/share_moderators_and_users
smarinier Sep 3, 2024
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
1 change: 1 addition & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
['name' => 'server#check', 'url' => '/server/check', 'verb' => 'POST'],
['name' => 'server#version', 'url' => '/server/version', 'verb' => 'GET'],
['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' => 'restriction#user', 'url' => '/restrictions/user', 'verb' => 'GET'],
['name' => 'hook#meetingEnded', 'url' => '/hook/ended/{token}/{mac}', 'verb' => 'GET'],
Expand Down
9 changes: 9 additions & 0 deletions lib/BigBlueButton/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use BigBlueButton\Parameters\InsertDocumentParameters;
use BigBlueButton\Parameters\IsMeetingRunningParameters;
use BigBlueButton\Parameters\JoinMeetingParameters;
use BigBlueButton\Parameters\PublishRecordingsParameters;
use OCA\BigBlueButton\AppInfo\Application;
use OCA\BigBlueButton\AvatarRepository;
use OCA\BigBlueButton\Crypto;
Expand Down Expand Up @@ -262,6 +263,14 @@ public function deleteRecording(string $recordingId): bool {
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)[]
*
Expand Down
14 changes: 14 additions & 0 deletions lib/Controller/RoomShareController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;

use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUserManager;

Expand All @@ -24,6 +25,9 @@ class RoomShareController extends Controller {
/** @var IUserManager */
private $userManager;

/** @var IGroupManager */
private $groupManager;

/** @var RoomService */
private $roomService;

Expand All @@ -37,13 +41,15 @@ public function __construct(
IRequest $request,
RoomShareService $service,
IUserManager $userManager,
IGroupManager $groupManager,
RoomService $roomService,
CircleHelper $circleHelper,
$userId
) {
parent::__construct($appName, $request);
$this->service = $service;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->roomService = $roomService;
$this->circleHelper = $circleHelper;
$this->userId = $userId;
Expand Down Expand Up @@ -90,6 +96,14 @@ public function index(): DataResponse {
}

$roomShare->setShareWithDisplayName($circle->getName());
} elseif ($roomShare->getShareType() === RoomShare::SHARE_TYPE_GROUP) {
$shareWithGroup = $this->groupManager->get($roomShare->getShareWith());

if ($shareWithGroup === null) {
continue;
}

$roomShare->setShareWithDisplayName($shareWithGroup->getDisplayName());
}

$shares[] = $roomShare;
Expand Down
29 changes: 28 additions & 1 deletion lib/Controller/ServerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,18 @@ public function records(string $roomUid): DataResponse {
return new DataResponse([], Http::STATUS_NOT_FOUND);
}

if (!$this->permission->isAdmin($room, $this->userId)) {
if (!$this->permission->isUser($room, $this->userId)) {
return new DataResponse([], Http::STATUS_FORBIDDEN);
}

$recordings = $this->server->getRecordings($room);

if (!$this->permission->isAdmin($room, $this->userId)) {
$recordings = array_values(array_filter($recordings, function ($recording) {
return $recording['published'];
}));
}

return new DataResponse($recordings);
}

Expand All @@ -118,6 +124,27 @@ public function deleteRecord(string $recordId): DataResponse {
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 {
if ($url === null || empty($url) || $secret === null || empty($secret)) {
return new DataResponse(false);
Expand Down
3 changes: 3 additions & 0 deletions lib/Db/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Room extends Entity implements JsonSerializable {
public $cleanLayout;
public $joinMuted;
public $running;
public $permission;

public function __construct() {
$this->addType('maxParticipants', 'integer');
Expand All @@ -86,6 +87,7 @@ public function __construct() {
$this->addType('cleanLayout', 'boolean');
$this->addType('joinMuted', 'boolean');
$this->addType('running', 'boolean');
$this->addType('permission', 'integer');
}

public function jsonSerialize(): array {
Expand All @@ -102,6 +104,7 @@ public function jsonSerialize(): array {
'everyoneIsModerator' => boolval($this->everyoneIsModerator),
'requireModerator' => boolval($this->requireModerator),
'shared' => boolval($this->shared),
'permission' => $this->permission,
'moderatorToken' => $this->moderatorToken,
'listenOnly' => boolval($this->listenOnly),
'mediaCheck' => boolval($this->mediaCheck),
Expand Down
31 changes: 15 additions & 16 deletions lib/Db/RoomMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,24 @@ public function __construct(IDBConnection $db) {
parent::__construct($db, 'bbb_rooms', Room::class);
}

private function joinShares(IQueryBuilder $qb): IQueryBuilder {
$qb->select('r.*')
->from($this->tableName, 'r')
->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id'))
->addSelect($qb->createFunction('count(case when `s`.`permission` IN ('.
RoomShare::PERMISSION_ADMIN.','.RoomShare::PERMISSION_MODERATOR.','.RoomShare::PERMISSION_USER
.') then 1 else null end) as shared'));
return $qb;
}

/**
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function find(int $id): Room {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->select('r.*')
->from($this->tableName, 'r')
->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id'))
->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared'))
$this->joinShares($qb)
->where($qb->expr()->eq('r.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->groupBy('r.id');
;
Expand All @@ -38,10 +45,7 @@ public function find(int $id): Room {
public function findByUid(string $uid): Room {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->select('r.*')
->from($this->tableName, 'r')
->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id'))
->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared'))
$this->joinShares($qb)
->where($qb->expr()->eq('r.uid', $qb->createNamedParameter($uid)))
->groupBy('r.id');
;
Expand Down Expand Up @@ -70,25 +74,20 @@ public function findByUserId(string $userId): array {
public function findAll(string $userId, array $groupIds, array $circleIds): array {
/* @var $qb IQueryBuilder */
$qb = $this->db->getQueryBuilder();
$qb->select('r.*')
->from($this->tableName, 'r')
->leftJoin('r', 'bbb_room_shares', 's', $qb->expr()->eq('r.id', 's.room_id'))
->addSelect($qb->createFunction('count(case when `s`.`permission` = 0 then 1 else null end) as shared'))
$this->joinShares($qb)
->addSelect($qb->createFunction('min(case when '.$qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)).' then '.RoomShare::PERMISSION_ADMIN.' else `s`.`permission` end) as permission'))
->where(
$qb->expr()->orX(
$qb->expr()->eq('r.user_id', $qb->createNamedParameter($userId)),
$qb->expr()->andX(
$qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_USER, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('s.share_with', $qb->createNamedParameter($userId))
),
$qb->expr()->andX(
$qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
$qb->expr()->in('s.share_with', $qb->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY))
),
$qb->expr()->andX(
$qb->expr()->eq('s.permission', $qb->createNamedParameter(RoomShare::PERMISSION_ADMIN, IQueryBuilder::PARAM_INT)),
$qb->expr()->eq('s.share_type', $qb->createNamedParameter(RoomShare::SHARE_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)),
$qb->expr()->in('s.share_with', $qb->createNamedParameter($circleIds, IQueryBuilder::PARAM_STR_ARRAY))
)
Expand All @@ -99,7 +98,7 @@ public function findAll(string $userId, array $groupIds, array $circleIds): arra
/** @var array<Room> */
return $this->findEntities($qb);
}

/**
* @return array<Room>
*/
Expand Down
4 changes: 4 additions & 0 deletions tests/Unit/Controller/RoomShareControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use OCA\BigBlueButton\Service\RoomService;
use OCA\BigBlueButton\Service\RoomShareService;
use OCP\AppFramework\Http;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\IUserManager;
use PHPUnit\Framework\TestCase;
Expand All @@ -19,6 +20,7 @@ class RoomShareControllerTest extends TestCase {
private $roomService;
private $circleHelper;
private $userManager;
private $groupManager;
private $controller;

private $userId = 'user_foo';
Expand All @@ -29,6 +31,7 @@ public function setUp(): void {
$this->request = $this->createMock(IRequest::class);
$this->service = $this->createMock(RoomShareService::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->roomService = $this->createMock(RoomService::class);
$this->circleHelper = $this->createMock(CircleHelper::class);

Expand All @@ -37,6 +40,7 @@ public function setUp(): void {
$this->request,
$this->service,
$this->userManager,
$this->groupManager,
$this->roomService,
$this->circleHelper,
$this->userId
Expand Down
9 changes: 9 additions & 0 deletions ts/Common/Api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface Room {
everyoneIsModerator: boolean;
requireModerator: boolean;
shared: boolean;
permission: Permission;
moderatorToken: string;
listenOnly: boolean,
mediaCheck: boolean,
Expand Down Expand Up @@ -200,6 +201,14 @@ class Api {
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) {
const startDate = new Date(recording.startTime);
const filename = `${encodeURIComponent(recording.name + ' ' + startDate.toISOString())}.url`;
Expand Down
8 changes: 7 additions & 1 deletion ts/Common/Translation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Access } from './Api';
import { Access, Permission } from './Api';

export const AccessOptions = {
[Access.Public]: t('bbb', 'Public'),
Expand All @@ -8,3 +8,9 @@ export const AccessOptions = {
[Access.Internal]: t('bbb', 'Internal'),
[Access.InternalRestricted]: t('bbb', 'Internal restricted'),
};

export const PermissionsOptions = {
[Permission.Admin]: t('bbb', 'admin'),
[Permission.Moderator]: t('bbb', 'moderator'),
[Permission.User]: t('bbb', 'user'),
};
21 changes: 20 additions & 1 deletion ts/Manager/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,23 @@ pre {
.bbb-shrink {
width: 44px;
white-space: nowrap;
}

input[type="checkbox"]{

&+label:before {
border-radius: 3px;
border-width: 2px;
}

&:disabled+label:before {
opacity: .5;
}

&:not(input:checked):disabled+label:before {
background-color: transparent !important;
}
}
}

th {
padding: 14px 6px;
Expand Down Expand Up @@ -282,6 +297,10 @@ pre {
}
}

.bbb-simple-menu {
min-width: auto;
}

.bbb-input-container {
display: flex;
}
Expand Down
17 changes: 9 additions & 8 deletions ts/Manager/EditRoomDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,17 +123,18 @@ const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, op
updateProperty('access', value);
})}

{room.access === Access.InternalRestricted && <div className="bbb-form-element bbb-form-shareWith">
<ShareWith permission={Permission.User} room={room} shares={shares} setShares={setShares} />
<em>{descriptions.internalRestrictedShareWith}</em>
</div>}

<div className="bbb-form-element">
<label htmlFor={'bbb-moderator'}>
<h3>Moderator</h3>
<label htmlFor={'bbb-sharing'}>
<h3>{t('bbb', 'Sharing')}</h3>
</label>

{!room.everyoneIsModerator && <ShareWith permission={Permission.Moderator} room={room} shares={shares} setShares={setShares} />}
{<ShareWith permission={Permission.User} room={room} shares={shares} setShares={setShares} />}

{room.access === Access.InternalRestricted &&
<div className="bbb-form-element bbb-form-shareWith">
<span className="icon icon-details icon-visible"></span><em>{t('bbb', 'Access') + ' : ' + descriptions.internalRestrictedShareWith}</em>
</div>
}

<div className="bbb-mt-1">
<input id={'bbb-everyoneIsModerator-' + room.id}
Expand Down
Loading