mirror of https://github.com/sualko/cloud_bbb
chore: upgrade psalm, some deprecated, lint, use npm (not yarn), react sync with typescript
Signed-off-by: Sebastien Marinier <sebastien.marinier@arawa.fr>pull/407/head
parent
6b6ad44753
commit
77b86b5820
20
Makefile
20
Makefile
|
|
@ -21,34 +21,34 @@ install-composer-deps-dev: composer.phar
|
||||||
php composer.phar install -o
|
php composer.phar install -o
|
||||||
|
|
||||||
js-init:
|
js-init:
|
||||||
yarn install
|
npm install
|
||||||
|
|
||||||
yarn-update:
|
npm-update:
|
||||||
yarn update
|
npm update
|
||||||
|
|
||||||
# Building
|
# Building
|
||||||
build-js: js-init
|
build-js: js-init
|
||||||
yarn run dev
|
npm run dev
|
||||||
|
|
||||||
build-js-production: js-init
|
build-js-production: js-init
|
||||||
yarn run build
|
npm run build
|
||||||
|
|
||||||
watch-js: js-init
|
watch-js: js-init
|
||||||
yarn run watch
|
npm run watch
|
||||||
|
|
||||||
# Linting
|
# Linting
|
||||||
lint: js-init
|
lint: js-init
|
||||||
yarn run lint
|
npm run lint
|
||||||
|
|
||||||
lint-fix: js-init
|
lint-fix: js-init
|
||||||
yarn run fix
|
npm run fix
|
||||||
|
|
||||||
# Style linting
|
# Style linting
|
||||||
stylelint: js-init
|
stylelint: js-init
|
||||||
yarn run lint:style
|
npm run lint:style
|
||||||
|
|
||||||
stylelint-fix: js-init
|
stylelint-fix: js-init
|
||||||
yarn run lint:fix:style
|
npm run lint:fix:style
|
||||||
|
|
||||||
phplint:
|
phplint:
|
||||||
./vendor/bin/php-cs-fixer fix --dry-run
|
./vendor/bin/php-cs-fixer fix --dry-run
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,3 @@
|
||||||
module.exports = {
|
const babelConfig = require('@nextcloud/babel-config')
|
||||||
plugins: [
|
|
||||||
'@babel/plugin-syntax-dynamic-import',
|
module.exports = babelConfig
|
||||||
],
|
|
||||||
presets: ['@babel/preset-env'],
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
"nextcloud/coding-standard": "^1.1.0",
|
"nextcloud/coding-standard": "^1.1.0",
|
||||||
"phpstan/phpstan": "^2.1.16",
|
"phpstan/phpstan": "^2.1.16",
|
||||||
"nextcloud/ocp": "^29.0 || ^30.0 || ^31.0",
|
"nextcloud/ocp": "^29.0 || ^30.0 || ^31.0",
|
||||||
"vimeo/psalm": "5.9.0 || ^6.1.0",
|
"vimeo/psalm": "^6.1.0",
|
||||||
"psr/container": "^1.1.2 || ^1.1.4 || ^2.0.2"
|
"psr/container": "^1.1.2 || ^1.1.4 || ^2.0.2"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -17,7 +17,7 @@ use \OCA\BigBlueButton\Middleware\HookMiddleware;
|
||||||
use \OCA\BigBlueButton\Middleware\JoinMiddleware;
|
use \OCA\BigBlueButton\Middleware\JoinMiddleware;
|
||||||
use \OCA\BigBlueButton\Search\Provider;
|
use \OCA\BigBlueButton\Search\Provider;
|
||||||
use \OCP\AppFramework\App;
|
use \OCP\AppFramework\App;
|
||||||
use \OCP\IConfig;
|
use \OCP\IAppConfig;
|
||||||
use \OCP\Settings\IManager as ISettingsManager;
|
use \OCP\Settings\IManager as ISettingsManager;
|
||||||
use \OCP\User\Events\UserDeletedEvent;
|
use \OCP\User\Events\UserDeletedEvent;
|
||||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
|
|
@ -66,11 +66,11 @@ class Application extends App implements IBootstrap {
|
||||||
public function boot(IBootContext $context): void {
|
public function boot(IBootContext $context): void {
|
||||||
$context->injectFn([$this, 'registerAdminPage']);
|
$context->injectFn([$this, 'registerAdminPage']);
|
||||||
|
|
||||||
Util::addScript('bbb', 'filelist');
|
Util::addScript('bbb', 'bbb-filelist');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerAdminPage(ISettingsManager $settingsManager, INavigationManager $navigationManager, IURLGenerator $urlGenerator, IConfig $config):void {
|
public function registerAdminPage(ISettingsManager $settingsManager, INavigationManager $navigationManager, IURLGenerator $urlGenerator, IAppConfig $config):void {
|
||||||
if ($config->getAppValue(self::ID, 'app.navigation') === 'true') {
|
if ($config->getValueBool(self::ID, 'app.navigation')) {
|
||||||
$this->registerAsNavigationEntry($navigationManager, $urlGenerator, $config);
|
$this->registerAsNavigationEntry($navigationManager, $urlGenerator, $config);
|
||||||
} else {
|
} else {
|
||||||
$this->registerAsPersonalSetting($settingsManager);
|
$this->registerAsPersonalSetting($settingsManager);
|
||||||
|
|
@ -78,11 +78,11 @@ class Application extends App implements IBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerAsPersonalSetting(ISettingsManager $settingsManager): void {
|
private function registerAsPersonalSetting(ISettingsManager $settingsManager): void {
|
||||||
$settingsManager->registerSetting(ISettingsManager::KEY_PERSONAL_SETTINGS, \OCA\BigBlueButton\Settings\Personal::class);
|
$settingsManager->registerSetting(ISettingsManager::SETTINGS_PERSONAL, \OCA\BigBlueButton\Settings\Personal::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function registerAsNavigationEntry(INavigationManager $navigationManager, IURLGenerator $urlGenerator, IConfig $config): void {
|
private function registerAsNavigationEntry(INavigationManager $navigationManager, IURLGenerator $urlGenerator, IAppConfig $config): void {
|
||||||
$name = $config->getAppValue(self::ID, 'app.navigation.name', 'BBB');
|
$name = $config->getValueString(self::ID, 'app.navigation.name', 'BBB');
|
||||||
|
|
||||||
$navigationManager->add(function () use ($urlGenerator, $name) {
|
$navigationManager->add(function () use ($urlGenerator, $name) {
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
use OCA\BigBlueButton\AppInfo\Application;
|
use OCA\BigBlueButton\AppInfo\Application;
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
|
use OCP\IAppConfig;
|
||||||
use OCP\IAvatarManager;
|
use OCP\IAvatarManager;
|
||||||
use OCP\IConfig;
|
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
use OCP\Security\ISecureRandom;
|
use OCP\Security\ISecureRandom;
|
||||||
|
|
||||||
|
|
@ -13,27 +13,11 @@ class AvatarRepository {
|
||||||
public const CONF_KEY_PATH = 'avatar.path';
|
public const CONF_KEY_PATH = 'avatar.path';
|
||||||
public const CONF_KEY_URL = 'avatar.url';
|
public const CONF_KEY_URL = 'avatar.url';
|
||||||
|
|
||||||
/** @var IAvatarManager */
|
|
||||||
private $avatarManager;
|
|
||||||
|
|
||||||
/** @var ISecureRandom */
|
|
||||||
private $random;
|
|
||||||
|
|
||||||
/** @var IURLGenerator */
|
|
||||||
private $urlGenerator;
|
|
||||||
|
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IAvatarManager $avatarManager,
|
private IAvatarManager $avatarManager,
|
||||||
IURLGenerator $urlGenerator,
|
private IURLGenerator $urlGenerator,
|
||||||
ISecureRandom $random,
|
private ISecureRandom $random,
|
||||||
IConfig $config) {
|
private IAppConfig $config) {
|
||||||
$this->avatarManager = $avatarManager;
|
|
||||||
$this->urlGenerator = $urlGenerator;
|
|
||||||
$this->random = $random;
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAvatarUrl(Room $room, string $userId): string {
|
public function getAvatarUrl(Room $room, string $userId): string {
|
||||||
|
|
@ -137,7 +121,7 @@ class AvatarRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRootPath(): string {
|
private function getRootPath(): string {
|
||||||
$path = $this->config->getAppValue(Application::ID, self::CONF_KEY_PATH);
|
$path = $this->config->getValueString(Application::ID, self::CONF_KEY_PATH);
|
||||||
|
|
||||||
if (empty($path)) {
|
if (empty($path)) {
|
||||||
return '';
|
return '';
|
||||||
|
|
@ -147,7 +131,7 @@ class AvatarRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getBaseUrl(): string {
|
private function getBaseUrl(): string {
|
||||||
$url = $this->config->getAppValue(Application::ID, self::CONF_KEY_URL);
|
$url = $this->config->getValueString(Application::ID, self::CONF_KEY_URL);
|
||||||
|
|
||||||
if (empty($url)) {
|
if (empty($url)) {
|
||||||
return '';
|
return '';
|
||||||
|
|
|
||||||
|
|
@ -20,73 +20,35 @@ use OCA\BigBlueButton\UrlHelper;
|
||||||
use OCP\App\IAppManager;
|
use OCP\App\IAppManager;
|
||||||
use OCP\Defaults;
|
use OCP\Defaults;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IConfig;
|
use OCP\IAppConfig;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
class API {
|
class API {
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/** @var IURLGenerator */
|
|
||||||
private $urlGenerator;
|
|
||||||
|
|
||||||
/** @var BigBlueButton|null */
|
/** @var BigBlueButton|null */
|
||||||
private $server;
|
private $server;
|
||||||
|
|
||||||
/** @var Crypto */
|
|
||||||
private $crypto;
|
|
||||||
|
|
||||||
/** @var IEventDispatcher */
|
|
||||||
private $eventDispatcher;
|
|
||||||
|
|
||||||
/** @var IL10N */
|
|
||||||
private $l10n;
|
|
||||||
|
|
||||||
/** @var UrlHelper */
|
|
||||||
private $urlHelper;
|
|
||||||
|
|
||||||
/** @var Defaults */
|
|
||||||
private $defaults;
|
|
||||||
|
|
||||||
/** @var IAppManager */
|
|
||||||
private $appManager;
|
|
||||||
|
|
||||||
/** @var AvatarRepository */
|
|
||||||
private $avatarRepository;
|
|
||||||
|
|
||||||
/** @var IRequest */
|
|
||||||
private $request;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IConfig $config,
|
private IAppConfig $config,
|
||||||
IURLGenerator $urlGenerator,
|
private IURLGenerator $urlGenerator,
|
||||||
Crypto $crypto,
|
private Crypto $crypto,
|
||||||
IEventDispatcher $eventDispatcher,
|
private IEventDispatcher $eventDispatcher,
|
||||||
IL10N $l10n,
|
private IL10N $l10n,
|
||||||
UrlHelper $urlHelper,
|
private UrlHelper $urlHelper,
|
||||||
Defaults $defaults,
|
private Defaults $defaults,
|
||||||
IAppManager $appManager,
|
private IAppManager $appManager,
|
||||||
AvatarRepository $avatarRepository,
|
private AvatarRepository $avatarRepository,
|
||||||
IRequest $request
|
private IRequest $request
|
||||||
) {
|
) {
|
||||||
$this->config = $config;
|
$this->server = null;
|
||||||
$this->urlGenerator = $urlGenerator;
|
|
||||||
$this->crypto = $crypto;
|
|
||||||
$this->eventDispatcher = $eventDispatcher;
|
|
||||||
$this->l10n = $l10n;
|
|
||||||
$this->urlHelper = $urlHelper;
|
|
||||||
$this->defaults = $defaults;
|
|
||||||
$this->appManager = $appManager;
|
|
||||||
$this->avatarRepository = $avatarRepository;
|
|
||||||
$this->request = $request;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getServer(): BigBlueButton {
|
private function getServer(): BigBlueButton {
|
||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
$apiUrl = $this->config->getAppValue('bbb', 'api.url');
|
$apiUrl = $this->config->getValueString('bbb', 'api.url');
|
||||||
$secret = $this->config->getAppValue('bbb', 'api.secret');
|
$secret = $this->config->getValueString('bbb', 'api.secret');
|
||||||
|
|
||||||
$this->server = new BigBlueButton($apiUrl, $secret);
|
$this->server = new BigBlueButton($apiUrl, $secret);
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +85,7 @@ class API {
|
||||||
$joinMeetingParams->addUserData('bbb_show_public_chat_on_login', false);
|
$joinMeetingParams->addUserData('bbb_show_public_chat_on_login', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->config->getAppValue('bbb', 'join.theme') === 'true') {
|
if ($this->config->getValueBool('bbb', 'join.theme')) {
|
||||||
$primaryColor = $this->defaults->getColorPrimary();
|
$primaryColor = $this->defaults->getColorPrimary();
|
||||||
$textColor = $this->defaults->getTextColorPrimary();
|
$textColor = $this->defaults->getTextColorPrimary();
|
||||||
|
|
||||||
|
|
@ -160,7 +122,7 @@ class API {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($response->getMessageKey() !== 'duplicateWarning') {
|
if ($response->getMessageKey() !== 'duplicateWarning') {
|
||||||
$this->eventDispatcher->dispatch(MeetingStartedEvent::class, new MeetingStartedEvent($room));
|
$this->eventDispatcher->dispatchTyped(new MeetingStartedEvent($room));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response->getCreationTime();
|
return $response->getCreationTime();
|
||||||
|
|
@ -179,7 +141,7 @@ class API {
|
||||||
$createMeetingParams->addMeta('bbb-origin', \method_exists($this->defaults, 'getProductName') ? $this->defaults->getProductName() : 'Nextcloud');
|
$createMeetingParams->addMeta('bbb-origin', \method_exists($this->defaults, 'getProductName') ? $this->defaults->getProductName() : 'Nextcloud');
|
||||||
$createMeetingParams->addMeta('bbb-origin-server-name', $this->request->getServerHost());
|
$createMeetingParams->addMeta('bbb-origin-server-name', $this->request->getServerHost());
|
||||||
|
|
||||||
$analyticsCallbackUrl = $this->config->getAppValue('bbb', 'api.meta_analytics-callback-url');
|
$analyticsCallbackUrl = $this->config->getValueString('bbb', 'api.meta_analytics-callback-url');
|
||||||
if (!empty($analyticsCallbackUrl)) {
|
if (!empty($analyticsCallbackUrl)) {
|
||||||
// For more details: https://github.com/bigbluebutton/bigbluebutton/blob/develop/record-and-playback/core/scripts/post_events/post_events_analytics_callback.rb
|
// For more details: https://github.com/bigbluebutton/bigbluebutton/blob/develop/record-and-playback/core/scripts/post_events/post_events_analytics_callback.rb
|
||||||
$createMeetingParams->addMeta('analytics-callback-url', $analyticsCallbackUrl);
|
$createMeetingParams->addMeta('analytics-callback-url', $analyticsCallbackUrl);
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ class CircleHelper {
|
||||||
if ($this->api === null) {
|
if ($this->api === null) {
|
||||||
if ($this->appManager->isEnabledForUser('circles') && class_exists('\OCA\Circles\Api\v1\Circles')) {
|
if ($this->appManager->isEnabledForUser('circles') && class_exists('\OCA\Circles\Api\v1\Circles')) {
|
||||||
$container = $this->app->getContainer();
|
$container = $this->app->getContainer();
|
||||||
$this->api = $container->query(\OCA\Circles\Api\v1\Circles::class);
|
$this->api = $container->get('OCA\Circles\Api\v1\Circles');
|
||||||
} else {
|
} else {
|
||||||
$this->api = false;
|
$this->api = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class ClearAvatarCache extends Command {
|
||||||
$this->setDescription('Clear all avatars in cache');
|
$this->setDescription('Clear all avatars in cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
protected function execute(InputInterface $input, OutputInterface $output): int {
|
||||||
$stats = $this->avatarRepository->clearAllRooms();
|
$stats = $this->avatarRepository->clearAllRooms();
|
||||||
|
|
||||||
$output->writeln("Removed " . $stats["files"] . " avatars in " . $stats["rooms"] . " rooms");
|
$output->writeln("Removed " . $stats["files"] . " avatars in " . $stats["rooms"] . " rooms");
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class HookController extends Controller {
|
||||||
|
|
||||||
$this->avatarRepository->clearRoom($room->uid);
|
$this->avatarRepository->clearRoom($room->uid);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(MeetingEndedEvent::class, new MeetingEndedEvent($room, $recordingmarks));
|
$this->eventDispatcher->dispatchTyped(new MeetingEndedEvent($room, $recordingmarks));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,7 +78,7 @@ class HookController extends Controller {
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function recordingReady(): void {
|
public function recordingReady(): void {
|
||||||
$this->eventDispatcher->dispatch(RecordingReadyEvent::class, new RecordingReadyEvent($this->getRoom()));
|
$this->eventDispatcher->dispatchTyped(new RecordingReadyEvent($this->getRoom()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRoom(): ?Room {
|
private function getRoom(): ?Room {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use OCP\AppFramework\Db\Entity;
|
||||||
* @method void setMaxRooms(int $number)
|
* @method void setMaxRooms(int $number)
|
||||||
* @method void setMaxParticipants(int $number)
|
* @method void setMaxParticipants(int $number)
|
||||||
* @method void setAllowRecording(bool $allow)
|
* @method void setAllowRecording(bool $allow)
|
||||||
* @method void setGroupName(string $groupName)
|
* @method void setGroupName(?string $groupName)
|
||||||
*/
|
*/
|
||||||
class Restriction extends Entity implements JsonSerializable {
|
class Restriction extends Entity implements JsonSerializable {
|
||||||
public const ALL_ID = '';
|
public const ALL_ID = '';
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class RestrictionService {
|
||||||
return $this->mapper->insert($restriction);
|
return $this->mapper->insert($restriction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(int $id, string $groupId, int $maxRooms, array $roomTypes, int $maxParticipants, bool $allowRecording): Restriction {
|
public function update(int $id, string $groupId, int $maxRooms, array $roomTypes, int $maxParticipants, bool $allowRecording): Restriction | null {
|
||||||
try {
|
try {
|
||||||
$restriction = $this->mapper->find($id);
|
$restriction = $this->mapper->find($id);
|
||||||
|
|
||||||
|
|
@ -104,10 +104,11 @@ class RestrictionService {
|
||||||
return $this->mapper->update($restriction);
|
return $this->mapper->update($restriction);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->handleException($e);
|
$this->handleException($e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(int $id): Restriction {
|
public function delete(int $id): Restriction | null {
|
||||||
try {
|
try {
|
||||||
$restriction = $this->mapper->find($id);
|
$restriction = $this->mapper->find($id);
|
||||||
$this->mapper->delete($restriction);
|
$this->mapper->delete($restriction);
|
||||||
|
|
@ -115,6 +116,7 @@ class RestrictionService {
|
||||||
return $restriction;
|
return $restriction;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->handleException($e);
|
$this->handleException($e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use OCA\BigBlueButton\Event\RoomDeletedEvent;
|
||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IConfig;
|
use OCP\IAppConfig;
|
||||||
use OCP\IUser;
|
use OCP\IUser;
|
||||||
use OCP\Search\ISearchQuery;
|
use OCP\Search\ISearchQuery;
|
||||||
use OCP\Security\ISecureRandom;
|
use OCP\Security\ISecureRandom;
|
||||||
|
|
@ -21,7 +21,7 @@ class RoomService {
|
||||||
/** @var RoomMapper */
|
/** @var RoomMapper */
|
||||||
private $mapper;
|
private $mapper;
|
||||||
|
|
||||||
/** @var IConfig */
|
/** @var IAppConfig */
|
||||||
private $config;
|
private $config;
|
||||||
|
|
||||||
/** @var IEventDispatcher */
|
/** @var IEventDispatcher */
|
||||||
|
|
@ -32,7 +32,7 @@ class RoomService {
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
RoomMapper $mapper,
|
RoomMapper $mapper,
|
||||||
IConfig $config,
|
IAppConfig $config,
|
||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
ISecureRandom $random) {
|
ISecureRandom $random) {
|
||||||
$this->mapper = $mapper;
|
$this->mapper = $mapper;
|
||||||
|
|
@ -96,7 +96,7 @@ class RoomService {
|
||||||
public function create(string $name, string $welcome, int $maxParticipants, bool $record, string $access, string $userId): \OCP\AppFramework\Db\Entity {
|
public function create(string $name, string $welcome, int $maxParticipants, bool $record, string $access, string $userId): \OCP\AppFramework\Db\Entity {
|
||||||
$room = new Room();
|
$room = new Room();
|
||||||
|
|
||||||
$mediaCheck = $this->config->getAppValue('bbb', 'join.mediaCheck', 'true') === 'true';
|
$mediaCheck = $this->config->getValueBool('bbb', 'join.mediaCheck', true);
|
||||||
|
|
||||||
$room->setUid($this->humanReadableRandom(16));
|
$room->setUid($this->humanReadableRandom(16));
|
||||||
$room->setName($name);
|
$room->setName($name);
|
||||||
|
|
@ -118,7 +118,7 @@ class RoomService {
|
||||||
|
|
||||||
$createdRoom = $this->mapper->insert($room);
|
$createdRoom = $this->mapper->insert($room);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(RoomCreatedEvent::class, new RoomCreatedEvent($createdRoom));
|
$this->eventDispatcher->dispatchTyped(new RoomCreatedEvent($createdRoom));
|
||||||
|
|
||||||
return $createdRoom;
|
return $createdRoom;
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ class RoomService {
|
||||||
|
|
||||||
$this->mapper->delete($room);
|
$this->mapper->delete($room);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(RoomDeletedEvent::class, new RoomDeletedEvent($room));
|
$this->eventDispatcher->dispatchTyped(new RoomDeletedEvent($room));
|
||||||
|
|
||||||
return $room;
|
return $room;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ class RoomShareService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create(int $roomId, int $shareType, string $shareWith, int $permission): RoomShare {
|
public function create(int $roomId, int $shareType, string $shareWith, int $permission): RoomShare | null {
|
||||||
try {
|
try {
|
||||||
$roomShare = $this->mapper->findByRoomAndEntity($roomId, $shareWith, $shareType);
|
$roomShare = $this->mapper->findByRoomAndEntity($roomId, $shareWith, $shareType);
|
||||||
|
|
||||||
|
|
@ -67,13 +67,13 @@ class RoomShareService {
|
||||||
|
|
||||||
$createdRoomShare = $this->mapper->insert($roomShare);
|
$createdRoomShare = $this->mapper->insert($roomShare);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(RoomShareCreatedEvent::class, new RoomShareCreatedEvent($createdRoomShare));
|
$this->eventDispatcher->dispatchTyped(new RoomShareCreatedEvent($createdRoomShare));
|
||||||
|
|
||||||
return $createdRoomShare;
|
return $createdRoomShare;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(int $id, int $roomId, int $shareType, string $shareWith, int $permission): RoomShare {
|
public function update(int $id, int $roomId, int $shareType, string $shareWith, int $permission): RoomShare | null {
|
||||||
try {
|
try {
|
||||||
$roomShare = $this->mapper->find($id);
|
$roomShare = $this->mapper->find($id);
|
||||||
|
|
||||||
|
|
@ -85,19 +85,21 @@ class RoomShareService {
|
||||||
return $this->mapper->update($roomShare);
|
return $this->mapper->update($roomShare);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->handleException($e);
|
$this->handleException($e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(int $id): RoomShare {
|
public function delete(int $id): RoomShare | null {
|
||||||
try {
|
try {
|
||||||
$roomShare = $this->mapper->find($id);
|
$roomShare = $this->mapper->find($id);
|
||||||
$this->mapper->delete($roomShare);
|
$this->mapper->delete($roomShare);
|
||||||
|
|
||||||
$this->eventDispatcher->dispatch(RoomShareDeletedEvent::class, new RoomShareDeletedEvent($roomShare));
|
$this->eventDispatcher->dispatchTyped(new RoomShareDeletedEvent($roomShare));
|
||||||
|
|
||||||
return $roomShare;
|
return $roomShare;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$this->handleException($e);
|
$this->handleException($e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,17 @@
|
||||||
namespace OCA\BigBlueButton\Settings;
|
namespace OCA\BigBlueButton\Settings;
|
||||||
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\IConfig;
|
use OCP\IAppConfig;
|
||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
|
|
||||||
class Admin implements ISettings {
|
class Admin implements ISettings {
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Admin constructor.
|
* Admin constructor.
|
||||||
*
|
*
|
||||||
* @param IConfig $config
|
* @param IAppConfig $config
|
||||||
*/
|
*/
|
||||||
public function __construct(IConfig $config) {
|
public function __construct(private IAppConfig $config) {
|
||||||
$this->config = $config;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -24,12 +21,12 @@ class Admin implements ISettings {
|
||||||
*/
|
*/
|
||||||
public function getForm() {
|
public function getForm() {
|
||||||
$parameters = [
|
$parameters = [
|
||||||
'api.url' => $this->config->getAppValue('bbb', 'api.url'),
|
'api.url' => $this->config->getValueString('bbb', 'api.url'),
|
||||||
'api.secret' => $this->config->getAppValue('bbb', 'api.secret'),
|
'api.secret' => $this->config->getValueString('bbb', 'api.secret'),
|
||||||
'app.navigation' => $this->config->getAppValue('bbb', 'app.navigation') === 'true' ? 'checked' : '',
|
'app.navigation' => $this->config->getValueBool('bbb', 'app.navigation') ? 'checked' : '',
|
||||||
'join.theme' => $this->config->getAppValue('bbb', 'join.theme') === 'true' ? 'checked' : '',
|
'join.theme' => $this->config->getValueBool('bbb', 'join.theme') ? 'checked' : '',
|
||||||
'app.shortener' => $this->config->getAppValue('bbb', 'app.shortener'),
|
'app.shortener' => $this->config->getValueString('bbb', 'app.shortener'),
|
||||||
'join.mediaCheck' => $this->config->getAppValue('bbb', 'join.mediaCheck', 'true') === 'true' ? 'checked' : '',
|
'join.mediaCheck' => $this->config->getValueBool('bbb', 'join.mediaCheck', true) ? 'checked' : '',
|
||||||
];
|
];
|
||||||
|
|
||||||
return new TemplateResponse('bbb', 'admin', $parameters);
|
return new TemplateResponse('bbb', 'admin', $parameters);
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,16 @@
|
||||||
namespace OCA\BigBlueButton;
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\IConfig;
|
use OCP\IAppConfig;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
|
|
||||||
class TemplateProvider {
|
class TemplateProvider {
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/** @var IL10N */
|
|
||||||
private $l;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Admin constructor.
|
* Admin constructor.
|
||||||
*
|
*
|
||||||
* @param IConfig $config
|
|
||||||
*/
|
*/
|
||||||
public function __construct(IConfig $config, IL10N $l) {
|
public function __construct(private IAppConfig $config, private IL10N $l) {
|
||||||
$this->config = $config;
|
|
||||||
$this->l = $l;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -29,13 +21,13 @@ class TemplateProvider {
|
||||||
public function getManager(): TemplateResponse {
|
public function getManager(): TemplateResponse {
|
||||||
$warning = '';
|
$warning = '';
|
||||||
|
|
||||||
if (empty($this->config->getAppValue('bbb', 'api.url')) || empty($this->config->getAppValue('bbb', 'api.secret'))) {
|
if (empty($this->config->getValueString('bbb', 'api.url')) || empty($this->config->getValueString('bbb', 'api.secret'))) {
|
||||||
$warning = $this->l->t('API URL or secret not configured. Please contact your administrator.');
|
$warning = $this->l->t('API URL or secret not configured. Please contact your administrator.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TemplateResponse('bbb', 'manager', [
|
return new TemplateResponse('bbb', 'manager', [
|
||||||
'warning' => $warning,
|
'warning' => $warning,
|
||||||
'shortener' => $this->config->getAppValue('bbb', 'app.shortener', ''),
|
'shortener' => $this->config->getValueString('bbb', 'app.shortener', ''),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,26 +3,19 @@
|
||||||
namespace OCA\BigBlueButton;
|
namespace OCA\BigBlueButton;
|
||||||
|
|
||||||
use OCA\BigBlueButton\Db\Room;
|
use OCA\BigBlueButton\Db\Room;
|
||||||
use OCP\IConfig;
|
use OCP\IAppConfig;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
class UrlHelper {
|
class UrlHelper {
|
||||||
/** @var IConfig */
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/** @var IURLGenerator */
|
|
||||||
private $urlGenerator;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IConfig $config,
|
private IAppConfig $config,
|
||||||
IURLGenerator $urlGenerator
|
private IURLGenerator $urlGenerator
|
||||||
) {
|
) {
|
||||||
$this->config = $config;
|
|
||||||
$this->urlGenerator = $urlGenerator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function linkToInvitationAbsolute(Room $room): string {
|
public function linkToInvitationAbsolute(Room $room): string {
|
||||||
$url = $this->config->getAppValue('bbb', 'app.shortener', '');
|
$url = $this->config->getValueString('bbb', 'app.shortener', '');
|
||||||
|
|
||||||
if (empty($url) || strpos($url, 'https://') !== 0 || strpos($url, '{token}') === false) {
|
if (empty($url) || strpos($url, 'https://') !== 0 || strpos($url, '{token}') === false) {
|
||||||
return $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->getUid()]);
|
return $this->urlGenerator->linkToRouteAbsolute('bbb.join.index', ['token' => $room->getUid()]);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
|
|
@ -37,9 +37,10 @@
|
||||||
"@commitlint/cli": "^16.2.3",
|
"@commitlint/cli": "^16.2.3",
|
||||||
"@commitlint/config-conventional": "^17.8.1",
|
"@commitlint/config-conventional": "^17.8.1",
|
||||||
"@commitlint/travis-cli": "^16.2.3",
|
"@commitlint/travis-cli": "^16.2.3",
|
||||||
"@nextcloud/axios": "^1.11.0",
|
"@nextcloud/axios": "^2.5.1",
|
||||||
"@nextcloud/dialogs": "^3.1.2",
|
"@nextcloud/dialogs": "^6.0.1",
|
||||||
"@nextcloud/router": "^2.0.0",
|
"@nextcloud/files": "^3.12.0",
|
||||||
|
"@nextcloud/router": "^3.0.1",
|
||||||
"@octokit/rest": "^18.0.4",
|
"@octokit/rest": "^18.0.4",
|
||||||
"archiver": "^5.0.0",
|
"archiver": "^5.0.0",
|
||||||
"colors": "^1.4.0",
|
"colors": "^1.4.0",
|
||||||
|
|
@ -55,8 +56,8 @@
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"pre-commit": "yarn lint",
|
"pre-commit": "npm run lint",
|
||||||
"pre-push": "yarn test:php:unit",
|
"pre-push": "npm run test:php:unit",
|
||||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -64,27 +65,30 @@
|
||||||
"extends @nextcloud/browserslist-config"
|
"extends @nextcloud/browserslist-config"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": "^20.0.0",
|
||||||
|
"npm": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.9.0",
|
"@babel/core": "^7.12.0",
|
||||||
"@babel/eslint-parser": "^7.27.1",
|
"@babel/eslint-parser": "^7.27.1",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||||
"@babel/preset-env": "^7.9.0",
|
"@babel/preset-env": "^7.9.0",
|
||||||
"@nextcloud/browserslist-config": "^2.2.0",
|
"@nextcloud/babel-config": "^1.2.0",
|
||||||
|
"@nextcloud/browserslist-config": "^3.0.1",
|
||||||
"@nextcloud/eslint-plugin": "^2.0.0",
|
"@nextcloud/eslint-plugin": "^2.0.0",
|
||||||
"@nextcloud/files": "^2.1.0",
|
"@nextcloud/paths": "^2.3.0",
|
||||||
"@types/bootstrap": "^5.1.9",
|
"@nextcloud/webpack-vue-config": "^5.5.1",
|
||||||
|
"@types/bootstrap": "^5.2.10",
|
||||||
"@types/inquirer": "^8.2.0",
|
"@types/inquirer": "^8.2.0",
|
||||||
"@types/jquery": "^3.3.35",
|
"@types/jquery": "^3.3.35",
|
||||||
"@types/node": "^17.0.21",
|
"@types/node": "^17.0.21",
|
||||||
"@types/react": "^17.0.40",
|
"@types/react": "^17.0.89",
|
||||||
"@types/webpack": "^5.28.0",
|
"@types/react-dom": "^17.0.26",
|
||||||
"@types/webpack-env": "^1.15.2",
|
"@types/react-transition-group": "^4.4.12",
|
||||||
|
"@types/webpack": "^5.28.5",
|
||||||
|
"@types/webpack-env": "^1.18.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
"@typescript-eslint/eslint-plugin": "^5.15.0",
|
||||||
"@typescript-eslint/parser": "^5.15.0",
|
"@typescript-eslint/parser": "^5.15.0",
|
||||||
"babel-loader": "^8.1.0",
|
|
||||||
"css-loader": "^6.7.1",
|
|
||||||
"dotenv-cli": "^8.0.0",
|
"dotenv-cli": "^8.0.0",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.11.0",
|
||||||
"eslint-config-standard": "^17.0",
|
"eslint-config-standard": "^17.0",
|
||||||
|
|
@ -92,7 +96,7 @@
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^6.6.0",
|
"eslint-plugin-promise": "^6.6.0",
|
||||||
"eslint-plugin-react": "^7.19.0",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"eslint-plugin-standard": "^5.0.0",
|
"eslint-plugin-standard": "^5.0.0",
|
||||||
"eslint-webpack-plugin": "^3.1.1",
|
"eslint-webpack-plugin": "^3.1.1",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
|
|
@ -100,12 +104,11 @@
|
||||||
"inquirer": "^8.2.6",
|
"inquirer": "^8.2.6",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"raw-loader": "^4.0.2",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-flip-move": "^3.0.4",
|
"react-flip-move": "^3.0.4",
|
||||||
"react-hot-loader": "^4.12.20",
|
|
||||||
"react-select": "^5.2.2",
|
"react-select": "^5.2.2",
|
||||||
"sass-loader": "^12.6.0",
|
|
||||||
"style-loader": "^3.3.1",
|
"style-loader": "^3.3.1",
|
||||||
"stylelint": "^14.5.3",
|
"stylelint": "^14.5.3",
|
||||||
"stylelint-config-recommended-scss": "^5.0.2",
|
"stylelint-config-recommended-scss": "^5.0.2",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
</extraFiles>
|
</extraFiles>
|
||||||
<issueHandlers>
|
<issueHandlers>
|
||||||
<UndefinedClass>
|
<UndefinedClass>
|
||||||
<errorLevel type="suppress">
|
<errorLevel type="info">
|
||||||
<referencedClass name="OC" />
|
<referencedClass name="OC" />
|
||||||
</errorLevel>
|
</errorLevel>
|
||||||
</UndefinedClass>
|
</UndefinedClass>
|
||||||
|
|
@ -36,5 +36,6 @@
|
||||||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||||
</errorLevel>
|
</errorLevel>
|
||||||
</UndefinedDocblockClass>
|
</UndefinedDocblockClass>
|
||||||
|
<MissingOverrideAttribute errorLevel="suppress" />
|
||||||
</issueHandlers>
|
</issueHandlers>
|
||||||
</psalm>
|
</psalm>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
/** @var $l \OCP\IL10N */
|
/** @var $l \OCP\IL10N */
|
||||||
/** @var $_ array */
|
/** @var $_ array */
|
||||||
|
|
||||||
script('bbb', 'admin');
|
\OCP\Util::addScript('bbb', 'bbb', 'admin');
|
||||||
script('bbb', 'restrictions');
|
\OCP\Util::addScript('bbb', 'bbb-restrictions');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="bbb-settings" class="section">
|
<div id="bbb-settings" class="section">
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/** @var $_ array */
|
/** @var $_ array */
|
||||||
/** @var $l \OCP\IL10N */
|
/** @var $l \OCP\IL10N */
|
||||||
style('core', 'guest');
|
style('core', 'guest');
|
||||||
script('bbb', 'join');
|
\OCP\Util::addScript('bbb', 'bbb-join');
|
||||||
?>
|
?>
|
||||||
<form method="get" action="?">
|
<form method="get" action="?">
|
||||||
<fieldset class="warning bbb">
|
<fieldset class="warning bbb">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
script('bbb', 'manager');
|
\OCP\Util::addScript('bbb', 'bbb-manager');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="bbb-app">
|
<div id="bbb-app">
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
/** @var $_ array */
|
/** @var $_ array */
|
||||||
/** @var $l \OCP\IL10N */
|
/** @var $l \OCP\IL10N */
|
||||||
style('core', 'guest');
|
style('core', 'guest');
|
||||||
script('bbb', 'waiting');
|
\OCP\Util::addScript('bbb', 'bbb-waiting');
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="update bbb">
|
<div class="update bbb">
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ class Api {
|
||||||
public async updateRestriction(restriction: Restriction) {
|
public async updateRestriction(restriction: Restriction) {
|
||||||
if (!restriction.id) {
|
if (!restriction.id) {
|
||||||
const newRestriction = await this.createRestriction(
|
const newRestriction = await this.createRestriction(
|
||||||
restriction.groupId
|
restriction.groupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
restriction.id = newRestriction.id;
|
restriction.id = newRestriction.id;
|
||||||
|
|
@ -166,7 +166,7 @@ class Api {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createRoom(name: string, access: Access = Access.Public, maxParticipants = 0) {
|
public async createRoom(name: string, access: Access = Access.Public, maxParticipants = 0): Promise<Room> {
|
||||||
const response = await axios.post(this.getUrl('rooms'), {
|
const response = await axios.post(this.getUrl('rooms'), {
|
||||||
name,
|
name,
|
||||||
welcome: '',
|
welcome: '',
|
||||||
|
|
@ -175,7 +175,7 @@ class Api {
|
||||||
access,
|
access,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response.data;
|
return response.data as Room;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateRoom(room: Room) {
|
public async updateRoom(room: Room) {
|
||||||
|
|
@ -202,7 +202,7 @@ class Api {
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async publishRecording(id: string, publish: boolean,) {
|
public async publishRecording(id: string, publish: boolean) {
|
||||||
const response = await axios.post(this.getUrl(`server/record/${id}/publish`), {
|
const response = await axios.post(this.getUrl(`server/record/${id}/publish`), {
|
||||||
published: publish,
|
published: publish,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ type Props = {
|
||||||
invert?: boolean;
|
invert?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditableSelection: React.FC<Props> = ({ setValue, field, values: currentValues, options, placeholder, invert = false }) => {
|
const EditableSelection = ({ setValue, field, values: currentValues, options, placeholder, invert = false }: Props): JSX.Element => {
|
||||||
const [active, setActive] = useState<boolean>(false);
|
const [active, setActive] = useState<boolean>(false);
|
||||||
|
|
||||||
currentValues = currentValues || [];
|
currentValues = currentValues || [];
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type Props = {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShareSelection: React.FC<Props> = (props) => {
|
const ShareSelection = (props: Props): JSX.Element => {
|
||||||
const [search, setSearch] = useState<string>('');
|
const [search, setSearch] = useState<string>('');
|
||||||
const [hasFocus, setFocus] = useState<boolean>(false);
|
const [hasFocus, setFocus] = useState<boolean>(false);
|
||||||
const [showSearchResults, setShowSearchResults] = useState<boolean>(false);
|
const [showSearchResults, setShowSearchResults] = useState<boolean>(false);
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,5 @@ export const PermissionsOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function html_sanitize_and_parse(str: string): string {
|
export function html_sanitize_and_parse(str: string): string {
|
||||||
return parse(DOMPurify.sanitize(str, { USE_PROFILES: { html: true } }));
|
return parse(DOMPurify.sanitize(str, { USE_PROFILES: { html: true } })) as string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,7 @@ function sortRooms(key: SortKey, orderBy: SortOrder) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = {
|
const App = () => {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const App: React.FC<Props> = () => {
|
|
||||||
const [isLoaded, setLoaded] = useState(false);
|
const [isLoaded, setLoaded] = useState(false);
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
const [restriction, setRestriction] = useState<Restriction>();
|
const [restriction, setRestriction] = useState<Restriction>();
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,16 @@ import React from 'react';
|
||||||
type Props = {
|
type Props = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
title: string;
|
title: string;
|
||||||
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Dialog: React.FC<Props> = ({open, title, children, onClose = () => undefined}) => {
|
const Dialog = ({
|
||||||
|
open,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
onClose = () => undefined,
|
||||||
|
}: Props): JSX.Element => {
|
||||||
|
|
||||||
if (!open) {
|
if (!open) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|
@ -27,4 +33,4 @@ const Dialog: React.FC<Props> = ({open, title, children, onClose = () => undefin
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dialog;
|
export default Dialog;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ type Props = {
|
||||||
updateProperty: (key: string, value: string | boolean | number | null) => Promise<void>;
|
updateProperty: (key: string, value: string | boolean | number | null) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditRoom: React.FC<Props> = ({ room, restriction, updateProperty }) => {
|
const EditRoom = ({ room, restriction, updateProperty }: Props): JSX.Element => {
|
||||||
const [open, setOpen] = useState<boolean>(false);
|
const [open, setOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ type Props = {
|
||||||
setOpen: (open: boolean) => void;
|
setOpen: (open: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, open, setOpen }) => {
|
const EditRoomDialog = ({ room, restriction, updateProperty, open, setOpen }: Props): JSX.Element => {
|
||||||
const [shares, setShares] = useState<RoomShare[]>();
|
const [shares, setShares] = useState<RoomShare[]>();
|
||||||
|
|
||||||
const maxParticipantsLimit = (restriction?.maxParticipants || 0) < 0 ? undefined : restriction?.maxParticipants;
|
const maxParticipantsLimit = (restriction?.maxParticipants || 0) < 0 ? undefined : restriction?.maxParticipants;
|
||||||
|
|
@ -69,7 +69,7 @@ const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, op
|
||||||
<h3>{label}</h3>
|
<h3>{label}</h3>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<SubmitInput initialValue={room[field]} type={type} name={field} onSubmitValue={value => updateProperty(field, value)} min={minParticipantsLimit} max={maxParticipantsLimit} />
|
<SubmitInput initialValue={room[field]} type={type} name={field} onSubmitValue={(value) => updateProperty(field, value)} min={minParticipantsLimit} max={maxParticipantsLimit} />
|
||||||
{descriptions[field] && <em>{html_sanitize_and_parse(descriptions[field])}</em>}
|
{descriptions[field] && <em>{html_sanitize_and_parse(descriptions[field])}</em>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type EditableValueProps = {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditableValue: React.FC<EditableValueProps> = ({ setValue, field, value: currentValue, type, options }) => {
|
const EditableValue = ({ setValue, field, value: currentValue, type, options }: EditableValueProps): JSX.Element => {
|
||||||
const [active, setActive] = useState<boolean>(false);
|
const [active, setActive] = useState<boolean>(false);
|
||||||
|
|
||||||
const submit = (value: string | number) => {
|
const submit = (value: string | number) => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ type Props = {
|
||||||
addRoom: (name: string) => Promise<void>;
|
addRoom: (name: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NewRoomForm: React.FC<Props> = (props) => {
|
const NewRoomForm = (props: Props): JSX.Element => {
|
||||||
const [name, setName] = useState<string>('');
|
const [name, setName] = useState<string>('');
|
||||||
const [processing, setProcessing] = useState<boolean>(false);
|
const [processing, setProcessing] = useState<boolean>(false);
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
|
|
@ -40,4 +40,4 @@ const NewRoomForm: React.FC<Props> = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NewRoomForm;
|
export default NewRoomForm;
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,7 @@ type Props = {
|
||||||
publishRecording: (recording: Recording, publish: boolean) => void;
|
publishRecording: (recording: Recording, publish: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RecordingRow: React.FC<Props> = ({recording, isAdmin, deleteRecording, storeRecording, publishRecording}) => {
|
const RecordingRow = ({recording, isAdmin, deleteRecording, storeRecording, publishRecording}: Props): JSX.Element => {
|
||||||
|
|
||||||
|
|
||||||
function checkPublished(recording: Recording, onChange: (value: boolean) => void) {
|
function checkPublished(recording: Recording, onChange: (value: boolean) => void) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -26,7 +25,6 @@ const RecordingRow: React.FC<Props> = ({recording, isAdmin, deleteRecording, sto
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={recording.id}>
|
<tr key={recording.id}>
|
||||||
<td className="start icon-col">
|
<td className="start icon-col">
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ type RecordingsNumberProps = {
|
||||||
setShowRecordings: (showRecordings: boolean) => void;
|
setShowRecordings: (showRecordings: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RecordingsNumber: React.FC<RecordingsNumberProps> = ({ recordings, showRecordings, setShowRecordings }) => {
|
const RecordingsNumber = ({ recordings, showRecordings, setShowRecordings }: RecordingsNumberProps): JSX.Element => {
|
||||||
if (recordings === null) {
|
if (recordings === null) {
|
||||||
return <span className="icon icon-loading-small icon-visible"></span>;
|
return <span className="icon icon-loading-small icon-visible"></span>;
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ const RecordingsNumber: React.FC<RecordingsNumberProps> = ({ recordings, showRec
|
||||||
return <span>0</span>;
|
return <span>0</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoomRow: React.FC<Props> = (props) => {
|
const RoomRow = (props: Props): JSX.Element => {
|
||||||
const [recordings, setRecordings] = useState<Recording[] | null>(null);
|
const [recordings, setRecordings] = useState<Recording[] | null>(null);
|
||||||
const [showRecordings, setShowRecordings] = useState<boolean>(false);
|
const [showRecordings, setShowRecordings] = useState<boolean>(false);
|
||||||
const room = props.room;
|
const room = props.room;
|
||||||
|
|
@ -74,7 +74,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
props.deleteRoom(room.id);
|
props.deleteRoom(room.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,7 +92,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
OC.dialogs.alert(
|
OC.dialogs.alert(
|
||||||
t('bbb', 'URL to room could not be stored.'),
|
t('bbb', 'URL to room could not be stored.'),
|
||||||
t('bbb', 'Error'),
|
t('bbb', 'Error'),
|
||||||
() => undefined
|
() => undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, undefined, 'httpd/unix-directory');
|
}, undefined, 'httpd/unix-directory');
|
||||||
|
|
@ -112,7 +112,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
OC.dialogs.alert(
|
OC.dialogs.alert(
|
||||||
t('bbb', 'URL to presentation could not be stored.'),
|
t('bbb', 'URL to presentation could not be stored.'),
|
||||||
t('bbb', 'Error'),
|
t('bbb', 'Error'),
|
||||||
() => undefined
|
() => undefined,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, undefined, 'httpd/unix-directory');
|
}, undefined, 'httpd/unix-directory');
|
||||||
|
|
@ -151,7 +151,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ type Props = {
|
||||||
setShares: (shares: RoomShare[]) => void;
|
setShares: (shares: RoomShare[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShareWith: React.FC<Props> = ({ room, permission, shares: allShares, setShares }) => {
|
const ShareWith = ({ room, permission, shares: allShares, setShares }: Props): JSX.Element => {
|
||||||
const isOwner = room.userId === OC.currentUser;
|
const isOwner = room.userId === OC.currentUser;
|
||||||
|
|
||||||
const shares = (allShares && permission === Permission.Moderator) ?
|
const shares = (allShares && permission === Permission.Moderator) ?
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import * as React from 'react';
|
import React, {
|
||||||
import {
|
useState, useEffect, InputHTMLAttributes, SyntheticEvent,
|
||||||
Component, InputHTMLAttributes,
|
|
||||||
SyntheticEvent,
|
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
export interface SubmitInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
export interface SubmitInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||||
|
|
@ -12,37 +10,41 @@ export interface SubmitInputProps extends InputHTMLAttributes<HTMLInputElement>
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SubmitInputState {
|
export const SubmitInput = ({
|
||||||
value: string;
|
type = 'text',
|
||||||
}
|
initialValue = '',
|
||||||
|
name,
|
||||||
|
onSubmitValue,
|
||||||
|
focus,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
...rest
|
||||||
|
}: SubmitInputProps): JSX.Element => {
|
||||||
|
const [value, setValue] = useState<string>(initialValue);
|
||||||
|
|
||||||
export class SubmitInput extends Component<SubmitInputProps, SubmitInputState> {
|
useEffect(() => {
|
||||||
state: SubmitInputState = {
|
setValue(initialValue ?? '');
|
||||||
value: '',
|
}, [initialValue]);
|
||||||
|
|
||||||
|
const onSubmit = (e: SyntheticEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
onSubmitValue(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: SubmitInputProps) {
|
return (
|
||||||
super(props);
|
<form onSubmit={onSubmit}>
|
||||||
this.state.value = props.initialValue ?? '';
|
<input
|
||||||
}
|
value={value}
|
||||||
|
type={type}
|
||||||
private onSubmit = (event: SyntheticEvent<any>) => {
|
id={`bbb-${name}`}
|
||||||
event.preventDefault();
|
name={name}
|
||||||
this.props.onSubmitValue(this.state.value);
|
onChange={(ev) => setValue((ev.target as HTMLInputElement).value)}
|
||||||
};
|
onBlur={() => onSubmitValue(value)}
|
||||||
|
autoFocus={focus}
|
||||||
public render(): JSX.Element {
|
min={min}
|
||||||
return <form onSubmit={this.onSubmit}>
|
max={max}
|
||||||
<input value={this.state.value}
|
{...rest}
|
||||||
type={this.props.type}
|
/>
|
||||||
id={`bbb-${this.props.name}`}
|
</form>
|
||||||
name={this.props.name}
|
);
|
||||||
onChange={event => this.setState({value: event.currentTarget.value})}
|
};
|
||||||
onBlur={() => this.props.onSubmitValue(this.state.value)}
|
|
||||||
autoFocus={this.props.focus}
|
|
||||||
min={this.props.min}
|
|
||||||
max={this.props.max}
|
|
||||||
/>
|
|
||||||
</form>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,5 +8,8 @@ import ReactDom from 'react-dom';
|
||||||
window['React'] = React;
|
window['React'] = React;
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
ReactDom.render( <App/>, document.getElementById('bbb-root'));
|
const root = document.getElementById('bbb-root');
|
||||||
|
if (root) {
|
||||||
|
ReactDom.render( <App /> as any , root);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -113,3 +113,7 @@ declare module 'NC' {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare const OC: any;
|
||||||
|
declare const OCP: any;
|
||||||
|
declare const OCA: any;
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,12 @@ import { api, Restriction, ShareType } from '../Common/Api';
|
||||||
import RestrictionRow from './RestrictionRow';
|
import RestrictionRow from './RestrictionRow';
|
||||||
import ShareSelection from '../Common/ShareSelection';
|
import ShareSelection from '../Common/ShareSelection';
|
||||||
|
|
||||||
type Props = {
|
const App = (): JSX.Element => {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const App: React.FC<Props> = () => {
|
|
||||||
const [areRestrictionsLoaded, setRestrictionsLoaded] = useState(false);
|
const [areRestrictionsLoaded, setRestrictionsLoaded] = useState(false);
|
||||||
const [error, setError] = useState<string>('');
|
const [error, setError] = useState<string>('');
|
||||||
const [restrictions, setRestrictions] = useState<Restriction[]>([]);
|
const [restrictions, setRestrictions] = useState<Restriction[]>([]);
|
||||||
|
|
||||||
const rows = restrictions.sort((a, b) => a.groupId.localeCompare(b.groupId)).map(restriction => <RestrictionRow key={restriction.id} restriction={restriction} updateRestriction={updateRestriction} deleteRestriction={deleteRestriction} />);
|
const rows = restrictions.sort((a: Restriction, b: Restriction) => a.groupId.localeCompare(b.groupId)).map(restriction => <RestrictionRow key={restriction.id} restriction={restriction} updateRestriction={updateRestriction} deleteRestriction={deleteRestriction} />);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
api.getRestrictions().then(restrictions => {
|
api.getRestrictions().then(restrictions => {
|
||||||
|
|
@ -35,7 +31,7 @@ const App: React.FC<Props> = () => {
|
||||||
|
|
||||||
function updateRestriction(restriction: Restriction) {
|
function updateRestriction(restriction: Restriction) {
|
||||||
return api.updateRestriction(restriction).then(updatedRestriction => {
|
return api.updateRestriction(restriction).then(updatedRestriction => {
|
||||||
setRestrictions(restrictions.map(restriction => {
|
setRestrictions(restrictions.map((restriction: Restriction) => {
|
||||||
if (restriction.id === updatedRestriction.id || restriction.groupId === updatedRestriction.groupId) {
|
if (restriction.id === updatedRestriction.id || restriction.groupId === updatedRestriction.groupId) {
|
||||||
return updatedRestriction;
|
return updatedRestriction;
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +82,7 @@ const App: React.FC<Props> = () => {
|
||||||
placeholder={t('bbb', 'Group …')}
|
placeholder={t('bbb', 'Group …')}
|
||||||
selectShare={(share) => addRestriction(share.value.shareWith)}
|
selectShare={(share) => addRestriction(share.value.shareWith)}
|
||||||
shareType={[ShareType.Group]}
|
shareType={[ShareType.Group]}
|
||||||
excluded={{groupIds: restrictions.map(restriction => restriction.groupId)}} /> }
|
excluded={{groupIds: restrictions.map((restriction: Restriction) => restriction.groupId)}} /> }
|
||||||
{error && <><span className="icon icon-error icon-visible"></span> {error}</>}
|
{error && <><span className="icon icon-error icon-visible"></span> {error}</>}
|
||||||
</td>
|
</td>
|
||||||
<td colSpan={4} />
|
<td colSpan={4} />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { } from 'react';
|
import React from 'react';
|
||||||
import { Restriction } from '../Common/Api';
|
import { Restriction } from '../Common/Api';
|
||||||
import EditableValue from '../Manager/EditableValue';
|
import EditableValue from '../Manager/EditableValue';
|
||||||
import EditableSelection from '../Common/EditableSelection';
|
import EditableSelection from '../Common/EditableSelection';
|
||||||
|
|
@ -11,7 +11,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const RestrictionRoom: React.FC<Props> = (props) => {
|
const RestrictionRoom = (props: Props): JSX.Element => {
|
||||||
const restriction = props.restriction;
|
const restriction = props.restriction;
|
||||||
|
|
||||||
function updateRestriction(key: string, value: string | boolean | number | string[]) {
|
function updateRestriction(key: string, value: string | boolean | number | string[]) {
|
||||||
|
|
@ -32,7 +32,7 @@ const RestrictionRoom: React.FC<Props> = (props) => {
|
||||||
props.deleteRestriction(restriction.id);
|
props.deleteRestriction(restriction.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDom from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
|
|
||||||
// Enable React devtools
|
// Enable React devtools
|
||||||
window['React'] = React;
|
window['React'] = React;
|
||||||
|
|
||||||
$(document).ready(() => {
|
$(document).ready(() => {
|
||||||
ReactDom.render( <App/>, document.getElementById('bbb-restrictions'));
|
const root = document.getElementById('bbb-restrictions');
|
||||||
|
if (root) {
|
||||||
|
render(<App /> as any, root);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
39
ts/admin.ts
39
ts/admin.ts
|
|
@ -1,8 +1,6 @@
|
||||||
import {api} from './Common/Api';
|
import { api } from './Common/Api';
|
||||||
import './Manager/App.scss';
|
import './Manager/App.scss';
|
||||||
|
|
||||||
declare const OCP: any;
|
|
||||||
|
|
||||||
$(() => {
|
$(() => {
|
||||||
function generateWarningElement(message: string) {
|
function generateWarningElement(message: string) {
|
||||||
return $(`<div id="bbb-warning"><span class="icon icon-error-color icon-visible"></span> ${message}</div>`);
|
return $(`<div id="bbb-warning"><span class="icon icon-error-color icon-visible"></span> ${message}</div>`);
|
||||||
|
|
@ -23,14 +21,10 @@ $(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkPasswordConfirmation() {
|
function checkPasswordConfirmation() {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>((resolve: () => void) => {
|
||||||
if (OC.PasswordConfirmation && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
OC.PasswordConfirmation?.requiresPasswordConfirmation()
|
||||||
OC.PasswordConfirmation.requirePasswordConfirmation(() => resolve());
|
? OC.PasswordConfirmation.requirePasswordConfirmation(() => resolve())
|
||||||
|
: resolve();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +43,14 @@ $(() => {
|
||||||
|
|
||||||
const resultElement = $(this).find('.bbb-result').empty();
|
const resultElement = $(this).find('.bbb-result').empty();
|
||||||
|
|
||||||
saveApiSettings(this['api.url'].value, this['api.secret'].value).then(() => {
|
const apiUrl = this['api.url'] as HTMLInputElement;
|
||||||
|
const apiSecret = this['api.secret'] as HTMLInputElement;
|
||||||
|
|
||||||
|
if (apiUrl === null || apiSecret == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveApiSettings(apiUrl.value, apiSecret.value).then(() => {
|
||||||
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -95,7 +96,11 @@ $(() => {
|
||||||
|
|
||||||
const resultElement = $(this).find('.bbb-result').empty();
|
const resultElement = $(this).find('.bbb-result').empty();
|
||||||
|
|
||||||
saveAppSettings(this['app.shortener'].value).then(() => {
|
const shortenerInput = this['app.shortener'] as HTMLInputElement;
|
||||||
|
if (shortenerInput === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
saveAppSettings(shortenerInput.value).then(() => {
|
||||||
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
const successElement = generateSuccessElement(t('bbb', 'Settings saved'));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -123,7 +128,7 @@ $(() => {
|
||||||
$<HTMLInputElement>('#bbb-shortener [name="app.shortener"]').on('keyup', (ev) => {
|
$<HTMLInputElement>('#bbb-shortener [name="app.shortener"]').on('keyup', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const {value} = ev.target;
|
const {value} = ev.target as HTMLInputElement;
|
||||||
|
|
||||||
if (!value || value.indexOf('https://') !== 0 || value.indexOf('{token}') < 0) {
|
if (!value || value.indexOf('https://') !== 0 || value.indexOf('{token}') < 0) {
|
||||||
$('#bbb-shortener-example').text(t('bbb', 'URL has to start with https:// and contain {token}. Additionally the {user} placeholder can be used.'));
|
$('#bbb-shortener-example').text(t('bbb', 'URL has to start with https:// and contain {token}. Additionally the {user} placeholder can be used.'));
|
||||||
|
|
@ -154,8 +159,10 @@ return 307;</pre></details>
|
||||||
$<HTMLInputElement>('.bbb-setting[type="checkbox"]').on('change', (ev) => {
|
$<HTMLInputElement>('.bbb-setting[type="checkbox"]').on('change', (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
console.log(`checkbox ${ev.target.name} changed to ${ev.target.checked}`);
|
const inputElement = ev.target as HTMLInputElement;
|
||||||
|
|
||||||
OCP.AppConfig.setValue('bbb', ev.target.name, ev.target.checked);
|
console.log(`checkbox ${inputElement.name} changed to ${inputElement.checked}`);
|
||||||
|
|
||||||
|
OCP.AppConfig.setValue('bbb', inputElement.name, inputElement.checked);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import axios from '@nextcloud/axios';
|
import axios from '@nextcloud/axios';
|
||||||
import { generateOcsUrl, generateUrl } from '@nextcloud/router';
|
import { generateOcsUrl, generateUrl } from '@nextcloud/router';
|
||||||
import { showSuccess, showWarning, showError } from '@nextcloud/dialogs';
|
import { showSuccess, showWarning, showError } from '@nextcloud/dialogs';
|
||||||
import '@nextcloud/dialogs/styles/toast';
|
|
||||||
import { api } from './Common/Api';
|
import { api } from './Common/Api';
|
||||||
import './filelist.scss';
|
import './filelist.scss';
|
||||||
|
|
||||||
|
|
@ -80,7 +79,9 @@ async function openDialog(fileId: number, filename: string) {
|
||||||
const initContent = '<div id="bbb-file-action"><span className="icon icon-loading-small icon-visible"></span></div>';
|
const initContent = '<div id="bbb-file-action"><span className="icon icon-loading-small icon-visible"></span></div>';
|
||||||
const title = t('bbb', 'Send file to BBB');
|
const title = t('bbb', 'Send file to BBB');
|
||||||
|
|
||||||
await (OC.dialogs as ExtendedDialogs).message(initContent, title, 'none', -1, undefined, true, true);
|
const exDialogs = OC.dialogs as ExtendedDialogs;
|
||||||
|
|
||||||
|
await exDialogs.message(initContent, title, 'none', -1, undefined, true, true);
|
||||||
|
|
||||||
const rooms = await api.getRooms();
|
const rooms = await api.getRooms();
|
||||||
|
|
||||||
|
|
@ -150,4 +151,4 @@ const BBBFileListPlugin = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
OC.Plugins.register('OCA.Files.FileList', BBBFileListPlugin);
|
OC.Plugins.register('OCA.Files.FileList', BBBFileListPlugin);
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ $(() => {
|
||||||
'bbb',
|
'bbb',
|
||||||
'This room is not open yet. We will try it again in %n second. Please wait.',
|
'This room is not open yet. We will try it again in %n second. Please wait.',
|
||||||
'This room is not open yet. We will try it again in %n seconds. Please wait.',
|
'This room is not open yet. We will try it again in %n seconds. Please wait.',
|
||||||
--countdown
|
--countdown,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (countdown === 0) {
|
if (countdown === 0) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
"es2015.promise",
|
"es2015.promise",
|
||||||
"es6"
|
"es2017"
|
||||||
],
|
],
|
||||||
"module": "ES6",
|
"module": "ES6",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,30 @@
|
||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
process.env.npm_package_name = 'bbb';
|
||||||
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||||
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
const webpackRules = require('@nextcloud/webpack-vue-config/rules')
|
||||||
|
|
||||||
module.exports = {
|
webpackRules.RULE_TSX = {
|
||||||
entry: {
|
test: /\.tsx?$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
babelrc: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'ts-loader',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
webpackRules.RULE_RAW = {
|
||||||
|
test: /\.svg$/,
|
||||||
|
resourceQuery: /raw/,
|
||||||
|
type: 'asset/source'
|
||||||
|
};
|
||||||
|
|
||||||
|
webpackConfig.entry = {
|
||||||
admin: [
|
admin: [
|
||||||
path.join(__dirname, 'ts', 'admin.ts'),
|
path.join(__dirname, 'ts', 'admin.ts'),
|
||||||
],
|
],
|
||||||
|
|
@ -22,55 +43,12 @@ module.exports = {
|
||||||
waiting: [
|
waiting: [
|
||||||
path.join(__dirname, 'ts', 'waiting.ts'),
|
path.join(__dirname, 'ts', 'waiting.ts'),
|
||||||
],
|
],
|
||||||
},
|
};
|
||||||
output: {
|
|
||||||
path: path.resolve(__dirname, './js'),
|
webpackConfig.module.rules = Object.values(webpackRules);
|
||||||
publicPath: '/js/',
|
|
||||||
filename: '[name].js',
|
webpackConfig.plugins.push(new ESLintPlugin());
|
||||||
chunkFilename: 'chunks/[name]-[hash].js',
|
|
||||||
},
|
webpackConfig.resolve.extensions = [...webpackConfig.resolve.extensions, '.jsx', '.ts', '.tsx'];
|
||||||
module: {
|
|
||||||
rules: [
|
module.exports = webpackConfig
|
||||||
{
|
|
||||||
test: /\.tsx?$/,
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options: {
|
|
||||||
babelrc: false,
|
|
||||||
plugins: ['react-hot-loader/babel'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'ts-loader',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.css$/,
|
|
||||||
use: ['style-loader', 'css-loader'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.scss$/,
|
|
||||||
use: ['style-loader', 'css-loader', 'sass-loader'],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
loader: 'babel-loader',
|
|
||||||
exclude: /node_modules/,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(png|jpg|gif|svg)$/,
|
|
||||||
type: 'asset',
|
|
||||||
generator: {
|
|
||||||
filename: 'static/[name][ext]?[hash]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
new ESLintPlugin(),
|
|
||||||
],
|
|
||||||
resolve: {
|
|
||||||
extensions: ['*', '.tsx', '.ts', '.js', '.scss'],
|
|
||||||
symlinks: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue