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
|
||||
|
||||
js-init:
|
||||
yarn install
|
||||
npm install
|
||||
|
||||
yarn-update:
|
||||
yarn update
|
||||
npm-update:
|
||||
npm update
|
||||
|
||||
# Building
|
||||
build-js: js-init
|
||||
yarn run dev
|
||||
npm run dev
|
||||
|
||||
build-js-production: js-init
|
||||
yarn run build
|
||||
npm run build
|
||||
|
||||
watch-js: js-init
|
||||
yarn run watch
|
||||
npm run watch
|
||||
|
||||
# Linting
|
||||
lint: js-init
|
||||
yarn run lint
|
||||
npm run lint
|
||||
|
||||
lint-fix: js-init
|
||||
yarn run fix
|
||||
npm run fix
|
||||
|
||||
# Style linting
|
||||
stylelint: js-init
|
||||
yarn run lint:style
|
||||
npm run lint:style
|
||||
|
||||
stylelint-fix: js-init
|
||||
yarn run lint:fix:style
|
||||
npm run lint:fix:style
|
||||
|
||||
phplint:
|
||||
./vendor/bin/php-cs-fixer fix --dry-run
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
],
|
||||
presets: ['@babel/preset-env'],
|
||||
}
|
||||
const babelConfig = require('@nextcloud/babel-config')
|
||||
|
||||
module.exports = babelConfig
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"nextcloud/coding-standard": "^1.1.0",
|
||||
"phpstan/phpstan": "^2.1.16",
|
||||
"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"
|
||||
},
|
||||
"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\Search\Provider;
|
||||
use \OCP\AppFramework\App;
|
||||
use \OCP\IConfig;
|
||||
use \OCP\IAppConfig;
|
||||
use \OCP\Settings\IManager as ISettingsManager;
|
||||
use \OCP\User\Events\UserDeletedEvent;
|
||||
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||
|
|
@ -66,11 +66,11 @@ class Application extends App implements IBootstrap {
|
|||
public function boot(IBootContext $context): void {
|
||||
$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 {
|
||||
if ($config->getAppValue(self::ID, 'app.navigation') === 'true') {
|
||||
public function registerAdminPage(ISettingsManager $settingsManager, INavigationManager $navigationManager, IURLGenerator $urlGenerator, IAppConfig $config):void {
|
||||
if ($config->getValueBool(self::ID, 'app.navigation')) {
|
||||
$this->registerAsNavigationEntry($navigationManager, $urlGenerator, $config);
|
||||
} else {
|
||||
$this->registerAsPersonalSetting($settingsManager);
|
||||
|
|
@ -78,11 +78,11 @@ class Application extends App implements IBootstrap {
|
|||
}
|
||||
|
||||
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 {
|
||||
$name = $config->getAppValue(self::ID, 'app.navigation.name', 'BBB');
|
||||
private function registerAsNavigationEntry(INavigationManager $navigationManager, IURLGenerator $urlGenerator, IAppConfig $config): void {
|
||||
$name = $config->getValueString(self::ID, 'app.navigation.name', 'BBB');
|
||||
|
||||
$navigationManager->add(function () use ($urlGenerator, $name) {
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ namespace OCA\BigBlueButton;
|
|||
|
||||
use OCA\BigBlueButton\AppInfo\Application;
|
||||
use OCA\BigBlueButton\Db\Room;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IAvatarManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
||||
|
|
@ -13,27 +13,11 @@ class AvatarRepository {
|
|||
public const CONF_KEY_PATH = 'avatar.path';
|
||||
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(
|
||||
IAvatarManager $avatarManager,
|
||||
IURLGenerator $urlGenerator,
|
||||
ISecureRandom $random,
|
||||
IConfig $config) {
|
||||
$this->avatarManager = $avatarManager;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->random = $random;
|
||||
$this->config = $config;
|
||||
private IAvatarManager $avatarManager,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private ISecureRandom $random,
|
||||
private IAppConfig $config) {
|
||||
}
|
||||
|
||||
public function getAvatarUrl(Room $room, string $userId): string {
|
||||
|
|
@ -137,7 +121,7 @@ class AvatarRepository {
|
|||
}
|
||||
|
||||
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)) {
|
||||
return '';
|
||||
|
|
@ -147,7 +131,7 @@ class AvatarRepository {
|
|||
}
|
||||
|
||||
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)) {
|
||||
return '';
|
||||
|
|
|
|||
|
|
@ -20,73 +20,35 @@ use OCA\BigBlueButton\UrlHelper;
|
|||
use OCP\App\IAppManager;
|
||||
use OCP\Defaults;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class API {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var BigBlueButton|null */
|
||||
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(
|
||||
IConfig $config,
|
||||
IURLGenerator $urlGenerator,
|
||||
Crypto $crypto,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
IL10N $l10n,
|
||||
UrlHelper $urlHelper,
|
||||
Defaults $defaults,
|
||||
IAppManager $appManager,
|
||||
AvatarRepository $avatarRepository,
|
||||
IRequest $request
|
||||
private IAppConfig $config,
|
||||
private IURLGenerator $urlGenerator,
|
||||
private Crypto $crypto,
|
||||
private IEventDispatcher $eventDispatcher,
|
||||
private IL10N $l10n,
|
||||
private UrlHelper $urlHelper,
|
||||
private Defaults $defaults,
|
||||
private IAppManager $appManager,
|
||||
private AvatarRepository $avatarRepository,
|
||||
private IRequest $request
|
||||
) {
|
||||
$this->config = $config;
|
||||
$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;
|
||||
$this->server = null;
|
||||
}
|
||||
|
||||
private function getServer(): BigBlueButton {
|
||||
if (!$this->server) {
|
||||
$apiUrl = $this->config->getAppValue('bbb', 'api.url');
|
||||
$secret = $this->config->getAppValue('bbb', 'api.secret');
|
||||
$apiUrl = $this->config->getValueString('bbb', 'api.url');
|
||||
$secret = $this->config->getValueString('bbb', 'api.secret');
|
||||
|
||||
$this->server = new BigBlueButton($apiUrl, $secret);
|
||||
}
|
||||
|
|
@ -123,7 +85,7 @@ class API {
|
|||
$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();
|
||||
$textColor = $this->defaults->getTextColorPrimary();
|
||||
|
||||
|
|
@ -160,7 +122,7 @@ class API {
|
|||
}
|
||||
|
||||
if ($response->getMessageKey() !== 'duplicateWarning') {
|
||||
$this->eventDispatcher->dispatch(MeetingStartedEvent::class, new MeetingStartedEvent($room));
|
||||
$this->eventDispatcher->dispatchTyped(new MeetingStartedEvent($room));
|
||||
}
|
||||
|
||||
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-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)) {
|
||||
// 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);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class CircleHelper {
|
|||
if ($this->api === null) {
|
||||
if ($this->appManager->isEnabledForUser('circles') && class_exists('\OCA\Circles\Api\v1\Circles')) {
|
||||
$container = $this->app->getContainer();
|
||||
$this->api = $container->query(\OCA\Circles\Api\v1\Circles::class);
|
||||
$this->api = $container->get('OCA\Circles\Api\v1\Circles');
|
||||
} else {
|
||||
$this->api = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class ClearAvatarCache extends Command {
|
|||
$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();
|
||||
|
||||
$output->writeln("Removed " . $stats["files"] . " avatars in " . $stats["rooms"] . " rooms");
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ class HookController extends Controller {
|
|||
|
||||
$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
|
||||
*/
|
||||
public function recordingReady(): void {
|
||||
$this->eventDispatcher->dispatch(RecordingReadyEvent::class, new RecordingReadyEvent($this->getRoom()));
|
||||
$this->eventDispatcher->dispatchTyped(new RecordingReadyEvent($this->getRoom()));
|
||||
}
|
||||
|
||||
private function getRoom(): ?Room {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use OCP\AppFramework\Db\Entity;
|
|||
* @method void setMaxRooms(int $number)
|
||||
* @method void setMaxParticipants(int $number)
|
||||
* @method void setAllowRecording(bool $allow)
|
||||
* @method void setGroupName(string $groupName)
|
||||
* @method void setGroupName(?string $groupName)
|
||||
*/
|
||||
class Restriction extends Entity implements JsonSerializable {
|
||||
public const ALL_ID = '';
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class RestrictionService {
|
|||
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 {
|
||||
$restriction = $this->mapper->find($id);
|
||||
|
||||
|
|
@ -104,10 +104,11 @@ class RestrictionService {
|
|||
return $this->mapper->update($restriction);
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(int $id): Restriction {
|
||||
public function delete(int $id): Restriction | null {
|
||||
try {
|
||||
$restriction = $this->mapper->find($id);
|
||||
$this->mapper->delete($restriction);
|
||||
|
|
@ -115,6 +116,7 @@ class RestrictionService {
|
|||
return $restriction;
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use OCA\BigBlueButton\Event\RoomDeletedEvent;
|
|||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IConfig;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IUser;
|
||||
use OCP\Search\ISearchQuery;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
|
@ -21,7 +21,7 @@ class RoomService {
|
|||
/** @var RoomMapper */
|
||||
private $mapper;
|
||||
|
||||
/** @var IConfig */
|
||||
/** @var IAppConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IEventDispatcher */
|
||||
|
|
@ -32,7 +32,7 @@ class RoomService {
|
|||
|
||||
public function __construct(
|
||||
RoomMapper $mapper,
|
||||
IConfig $config,
|
||||
IAppConfig $config,
|
||||
IEventDispatcher $eventDispatcher,
|
||||
ISecureRandom $random) {
|
||||
$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 {
|
||||
$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->setName($name);
|
||||
|
|
@ -118,7 +118,7 @@ class RoomService {
|
|||
|
||||
$createdRoom = $this->mapper->insert($room);
|
||||
|
||||
$this->eventDispatcher->dispatch(RoomCreatedEvent::class, new RoomCreatedEvent($createdRoom));
|
||||
$this->eventDispatcher->dispatchTyped(new RoomCreatedEvent($createdRoom));
|
||||
|
||||
return $createdRoom;
|
||||
}
|
||||
|
|
@ -195,7 +195,7 @@ class RoomService {
|
|||
|
||||
$this->mapper->delete($room);
|
||||
|
||||
$this->eventDispatcher->dispatch(RoomDeletedEvent::class, new RoomDeletedEvent($room));
|
||||
$this->eventDispatcher->dispatchTyped(new RoomDeletedEvent($room));
|
||||
|
||||
return $room;
|
||||
} 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 {
|
||||
$roomShare = $this->mapper->findByRoomAndEntity($roomId, $shareWith, $shareType);
|
||||
|
||||
|
|
@ -67,13 +67,13 @@ class RoomShareService {
|
|||
|
||||
$createdRoomShare = $this->mapper->insert($roomShare);
|
||||
|
||||
$this->eventDispatcher->dispatch(RoomShareCreatedEvent::class, new RoomShareCreatedEvent($createdRoomShare));
|
||||
$this->eventDispatcher->dispatchTyped(new RoomShareCreatedEvent($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 {
|
||||
$roomShare = $this->mapper->find($id);
|
||||
|
||||
|
|
@ -85,19 +85,21 @@ class RoomShareService {
|
|||
return $this->mapper->update($roomShare);
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(int $id): RoomShare {
|
||||
public function delete(int $id): RoomShare | null {
|
||||
try {
|
||||
$roomShare = $this->mapper->find($id);
|
||||
$this->mapper->delete($roomShare);
|
||||
|
||||
$this->eventDispatcher->dispatch(RoomShareDeletedEvent::class, new RoomShareDeletedEvent($roomShare));
|
||||
$this->eventDispatcher->dispatchTyped(new RoomShareDeletedEvent($roomShare));
|
||||
|
||||
return $roomShare;
|
||||
} catch (Exception $e) {
|
||||
$this->handleException($e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,17 @@
|
|||
namespace OCA\BigBlueButton\Settings;
|
||||
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\Settings\ISettings;
|
||||
|
||||
class Admin implements ISettings {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Admin constructor.
|
||||
*
|
||||
* @param IConfig $config
|
||||
* @param IAppConfig $config
|
||||
*/
|
||||
public function __construct(IConfig $config) {
|
||||
$this->config = $config;
|
||||
public function __construct(private IAppConfig $config) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -24,12 +21,12 @@ class Admin implements ISettings {
|
|||
*/
|
||||
public function getForm() {
|
||||
$parameters = [
|
||||
'api.url' => $this->config->getAppValue('bbb', 'api.url'),
|
||||
'api.secret' => $this->config->getAppValue('bbb', 'api.secret'),
|
||||
'app.navigation' => $this->config->getAppValue('bbb', 'app.navigation') === 'true' ? 'checked' : '',
|
||||
'join.theme' => $this->config->getAppValue('bbb', 'join.theme') === 'true' ? 'checked' : '',
|
||||
'app.shortener' => $this->config->getAppValue('bbb', 'app.shortener'),
|
||||
'join.mediaCheck' => $this->config->getAppValue('bbb', 'join.mediaCheck', 'true') === 'true' ? 'checked' : '',
|
||||
'api.url' => $this->config->getValueString('bbb', 'api.url'),
|
||||
'api.secret' => $this->config->getValueString('bbb', 'api.secret'),
|
||||
'app.navigation' => $this->config->getValueBool('bbb', 'app.navigation') ? 'checked' : '',
|
||||
'join.theme' => $this->config->getValueBool('bbb', 'join.theme') ? 'checked' : '',
|
||||
'app.shortener' => $this->config->getValueString('bbb', 'app.shortener'),
|
||||
'join.mediaCheck' => $this->config->getValueBool('bbb', 'join.mediaCheck', true) ? 'checked' : '',
|
||||
];
|
||||
|
||||
return new TemplateResponse('bbb', 'admin', $parameters);
|
||||
|
|
|
|||
|
|
@ -3,24 +3,16 @@
|
|||
namespace OCA\BigBlueButton;
|
||||
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IL10N;
|
||||
|
||||
class TemplateProvider {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IL10N */
|
||||
private $l;
|
||||
|
||||
/**
|
||||
* Admin constructor.
|
||||
*
|
||||
* @param IConfig $config
|
||||
*/
|
||||
public function __construct(IConfig $config, IL10N $l) {
|
||||
$this->config = $config;
|
||||
$this->l = $l;
|
||||
public function __construct(private IAppConfig $config, private IL10N $l) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -29,13 +21,13 @@ class TemplateProvider {
|
|||
public function getManager(): TemplateResponse {
|
||||
$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.');
|
||||
}
|
||||
|
||||
return new TemplateResponse('bbb', 'manager', [
|
||||
'warning' => $warning,
|
||||
'shortener' => $this->config->getAppValue('bbb', 'app.shortener', ''),
|
||||
'shortener' => $this->config->getValueString('bbb', 'app.shortener', ''),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,26 +3,19 @@
|
|||
namespace OCA\BigBlueButton;
|
||||
|
||||
use OCA\BigBlueButton\Db\Room;
|
||||
use OCP\IConfig;
|
||||
use OCP\IAppConfig;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class UrlHelper {
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
public function __construct(
|
||||
IConfig $config,
|
||||
IURLGenerator $urlGenerator
|
||||
private IAppConfig $config,
|
||||
private IURLGenerator $urlGenerator
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
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) {
|
||||
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/config-conventional": "^17.8.1",
|
||||
"@commitlint/travis-cli": "^16.2.3",
|
||||
"@nextcloud/axios": "^1.11.0",
|
||||
"@nextcloud/dialogs": "^3.1.2",
|
||||
"@nextcloud/router": "^2.0.0",
|
||||
"@nextcloud/axios": "^2.5.1",
|
||||
"@nextcloud/dialogs": "^6.0.1",
|
||||
"@nextcloud/files": "^3.12.0",
|
||||
"@nextcloud/router": "^3.0.1",
|
||||
"@octokit/rest": "^18.0.4",
|
||||
"archiver": "^5.0.0",
|
||||
"colors": "^1.4.0",
|
||||
|
|
@ -55,8 +56,8 @@
|
|||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "yarn lint",
|
||||
"pre-push": "yarn test:php:unit",
|
||||
"pre-commit": "npm run lint",
|
||||
"pre-push": "npm run test:php:unit",
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
|
|
@ -64,27 +65,30 @@
|
|||
"extends @nextcloud/browserslist-config"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
"node": "^20.0.0",
|
||||
"npm": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/core": "^7.12.0",
|
||||
"@babel/eslint-parser": "^7.27.1",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@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/files": "^2.1.0",
|
||||
"@types/bootstrap": "^5.1.9",
|
||||
"@nextcloud/paths": "^2.3.0",
|
||||
"@nextcloud/webpack-vue-config": "^5.5.1",
|
||||
"@types/bootstrap": "^5.2.10",
|
||||
"@types/inquirer": "^8.2.0",
|
||||
"@types/jquery": "^3.3.35",
|
||||
"@types/node": "^17.0.21",
|
||||
"@types/react": "^17.0.40",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@types/react": "^17.0.89",
|
||||
"@types/react-dom": "^17.0.26",
|
||||
"@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/parser": "^5.15.0",
|
||||
"babel-loader": "^8.1.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"dotenv-cli": "^8.0.0",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-config-standard": "^17.0",
|
||||
|
|
@ -92,7 +96,7 @@
|
|||
"eslint-plugin-import": "^2.20.2",
|
||||
"eslint-plugin-node": "^11.1.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-webpack-plugin": "^3.1.1",
|
||||
"file-loader": "^6.0.0",
|
||||
|
|
@ -100,12 +104,11 @@
|
|||
"inquirer": "^8.2.6",
|
||||
"install": "^0.13.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"raw-loader": "^4.0.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-flip-move": "^3.0.4",
|
||||
"react-hot-loader": "^4.12.20",
|
||||
"react-select": "^5.2.2",
|
||||
"sass-loader": "^12.6.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"stylelint": "^14.5.3",
|
||||
"stylelint-config-recommended-scss": "^5.0.2",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
</extraFiles>
|
||||
<issueHandlers>
|
||||
<UndefinedClass>
|
||||
<errorLevel type="suppress">
|
||||
<errorLevel type="info">
|
||||
<referencedClass name="OC" />
|
||||
</errorLevel>
|
||||
</UndefinedClass>
|
||||
|
|
@ -36,5 +36,6 @@
|
|||
<referencedClass name="Doctrine\DBAL\Schema\Table" />
|
||||
</errorLevel>
|
||||
</UndefinedDocblockClass>
|
||||
<MissingOverrideAttribute errorLevel="suppress" />
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
/** @var $l \OCP\IL10N */
|
||||
/** @var $_ array */
|
||||
|
||||
script('bbb', 'admin');
|
||||
script('bbb', 'restrictions');
|
||||
\OCP\Util::addScript('bbb', 'bbb', 'admin');
|
||||
\OCP\Util::addScript('bbb', 'bbb-restrictions');
|
||||
?>
|
||||
|
||||
<div id="bbb-settings" class="section">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/** @var $_ array */
|
||||
/** @var $l \OCP\IL10N */
|
||||
style('core', 'guest');
|
||||
script('bbb', 'join');
|
||||
\OCP\Util::addScript('bbb', 'bbb-join');
|
||||
?>
|
||||
<form method="get" action="?">
|
||||
<fieldset class="warning bbb">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
script('bbb', 'manager');
|
||||
\OCP\Util::addScript('bbb', 'bbb-manager');
|
||||
?>
|
||||
|
||||
<div id="bbb-app">
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/** @var $_ array */
|
||||
/** @var $l \OCP\IL10N */
|
||||
style('core', 'guest');
|
||||
script('bbb', 'waiting');
|
||||
\OCP\Util::addScript('bbb', 'bbb-waiting');
|
||||
?>
|
||||
|
||||
<div class="update bbb">
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ class Api {
|
|||
public async updateRestriction(restriction: Restriction) {
|
||||
if (!restriction.id) {
|
||||
const newRestriction = await this.createRestriction(
|
||||
restriction.groupId
|
||||
restriction.groupId,
|
||||
);
|
||||
|
||||
restriction.id = newRestriction.id;
|
||||
|
|
@ -166,7 +166,7 @@ class Api {
|
|||
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'), {
|
||||
name,
|
||||
welcome: '',
|
||||
|
|
@ -175,7 +175,7 @@ class Api {
|
|||
access,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
return response.data as Room;
|
||||
}
|
||||
|
||||
public async updateRoom(room: Room) {
|
||||
|
|
@ -202,7 +202,7 @@ class Api {
|
|||
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`), {
|
||||
published: publish,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ type Props = {
|
|||
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);
|
||||
|
||||
currentValues = currentValues || [];
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type Props = {
|
|||
placeholder?: string;
|
||||
}
|
||||
|
||||
const ShareSelection: React.FC<Props> = (props) => {
|
||||
const ShareSelection = (props: Props): JSX.Element => {
|
||||
const [search, setSearch] = useState<string>('');
|
||||
const [hasFocus, setFocus] = 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 {
|
||||
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: React.FC<Props> = () => {
|
||||
const App = () => {
|
||||
const [isLoaded, setLoaded] = useState(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
const [restriction, setRestriction] = useState<Restriction>();
|
||||
|
|
|
|||
|
|
@ -3,10 +3,16 @@ import React from 'react';
|
|||
type Props = {
|
||||
open: boolean;
|
||||
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) {
|
||||
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>;
|
||||
}
|
||||
|
||||
const EditRoom: React.FC<Props> = ({ room, restriction, updateProperty }) => {
|
||||
const EditRoom = ({ room, restriction, updateProperty }: Props): JSX.Element => {
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ type Props = {
|
|||
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 maxParticipantsLimit = (restriction?.maxParticipants || 0) < 0 ? undefined : restriction?.maxParticipants;
|
||||
|
|
@ -69,7 +69,7 @@ const EditRoomDialog: React.FC<Props> = ({ room, restriction, updateProperty, op
|
|||
<h3>{label}</h3>
|
||||
</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>}
|
||||
</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 submit = (value: string | number) => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ type Props = {
|
|||
addRoom: (name: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const NewRoomForm: React.FC<Props> = (props) => {
|
||||
const NewRoomForm = (props: Props): JSX.Element => {
|
||||
const [name, setName] = useState<string>('');
|
||||
const [processing, setProcessing] = useState<boolean>(false);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return (
|
||||
|
|
@ -26,7 +25,6 @@ const RecordingRow: React.FC<Props> = ({recording, isAdmin, deleteRecording, sto
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<tr key={recording.id}>
|
||||
<td className="start icon-col">
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ type RecordingsNumberProps = {
|
|||
setShowRecordings: (showRecordings: boolean) => void;
|
||||
}
|
||||
|
||||
const RecordingsNumber: React.FC<RecordingsNumberProps> = ({ recordings, showRecordings, setShowRecordings }) => {
|
||||
const RecordingsNumber = ({ recordings, showRecordings, setShowRecordings }: RecordingsNumberProps): JSX.Element => {
|
||||
if (recordings === null) {
|
||||
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>;
|
||||
};
|
||||
|
||||
const RoomRow: React.FC<Props> = (props) => {
|
||||
const RoomRow = (props: Props): JSX.Element => {
|
||||
const [recordings, setRecordings] = useState<Recording[] | null>(null);
|
||||
const [showRecordings, setShowRecordings] = useState<boolean>(false);
|
||||
const room = props.room;
|
||||
|
|
@ -74,7 +74,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
|||
props.deleteRoom(room.id);
|
||||
}
|
||||
},
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
|||
OC.dialogs.alert(
|
||||
t('bbb', 'URL to room could not be stored.'),
|
||||
t('bbb', 'Error'),
|
||||
() => undefined
|
||||
() => undefined,
|
||||
);
|
||||
});
|
||||
}, undefined, 'httpd/unix-directory');
|
||||
|
|
@ -112,7 +112,7 @@ const RoomRow: React.FC<Props> = (props) => {
|
|||
OC.dialogs.alert(
|
||||
t('bbb', 'URL to presentation could not be stored.'),
|
||||
t('bbb', 'Error'),
|
||||
() => undefined
|
||||
() => undefined,
|
||||
);
|
||||
});
|
||||
}, 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;
|
||||
}
|
||||
|
||||
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 shares = (allShares && permission === Permission.Moderator) ?
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import {
|
||||
Component, InputHTMLAttributes,
|
||||
SyntheticEvent,
|
||||
import React, {
|
||||
useState, useEffect, InputHTMLAttributes, SyntheticEvent,
|
||||
} from 'react';
|
||||
|
||||
export interface SubmitInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
|
|
@ -12,37 +10,41 @@ export interface SubmitInputProps extends InputHTMLAttributes<HTMLInputElement>
|
|||
focus?: boolean;
|
||||
}
|
||||
|
||||
export interface SubmitInputState {
|
||||
value: string;
|
||||
}
|
||||
export const SubmitInput = ({
|
||||
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> {
|
||||
state: SubmitInputState = {
|
||||
value: '',
|
||||
useEffect(() => {
|
||||
setValue(initialValue ?? '');
|
||||
}, [initialValue]);
|
||||
|
||||
const onSubmit = (e: SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
onSubmitValue(value);
|
||||
};
|
||||
|
||||
constructor(props: SubmitInputProps) {
|
||||
super(props);
|
||||
this.state.value = props.initialValue ?? '';
|
||||
}
|
||||
|
||||
private onSubmit = (event: SyntheticEvent<any>) => {
|
||||
event.preventDefault();
|
||||
this.props.onSubmitValue(this.state.value);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return <form onSubmit={this.onSubmit}>
|
||||
<input value={this.state.value}
|
||||
type={this.props.type}
|
||||
id={`bbb-${this.props.name}`}
|
||||
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>;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<form onSubmit={onSubmit}>
|
||||
<input
|
||||
value={value}
|
||||
type={type}
|
||||
id={`bbb-${name}`}
|
||||
name={name}
|
||||
onChange={(ev) => setValue((ev.target as HTMLInputElement).value)}
|
||||
onBlur={() => onSubmitValue(value)}
|
||||
autoFocus={focus}
|
||||
min={min}
|
||||
max={max}
|
||||
{...rest}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,5 +8,8 @@ import ReactDom from 'react-dom';
|
|||
window['React'] = React;
|
||||
|
||||
$(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 ShareSelection from '../Common/ShareSelection';
|
||||
|
||||
type Props = {
|
||||
|
||||
}
|
||||
|
||||
const App: React.FC<Props> = () => {
|
||||
const App = (): JSX.Element => {
|
||||
const [areRestrictionsLoaded, setRestrictionsLoaded] = useState(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
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(() => {
|
||||
api.getRestrictions().then(restrictions => {
|
||||
|
|
@ -35,7 +31,7 @@ const App: React.FC<Props> = () => {
|
|||
|
||||
function updateRestriction(restriction: Restriction) {
|
||||
return api.updateRestriction(restriction).then(updatedRestriction => {
|
||||
setRestrictions(restrictions.map(restriction => {
|
||||
setRestrictions(restrictions.map((restriction: Restriction) => {
|
||||
if (restriction.id === updatedRestriction.id || restriction.groupId === updatedRestriction.groupId) {
|
||||
return updatedRestriction;
|
||||
}
|
||||
|
|
@ -86,7 +82,7 @@ const App: React.FC<Props> = () => {
|
|||
placeholder={t('bbb', 'Group …')}
|
||||
selectShare={(share) => addRestriction(share.value.shareWith)}
|
||||
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}</>}
|
||||
</td>
|
||||
<td colSpan={4} />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { } from 'react';
|
||||
import React from 'react';
|
||||
import { Restriction } from '../Common/Api';
|
||||
import EditableValue from '../Manager/EditableValue';
|
||||
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;
|
||||
|
||||
function updateRestriction(key: string, value: string | boolean | number | string[]) {
|
||||
|
|
@ -32,7 +32,7 @@ const RestrictionRoom: React.FC<Props> = (props) => {
|
|||
props.deleteRestriction(restriction.id);
|
||||
}
|
||||
},
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
import App from './App';
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import { render } from 'react-dom';
|
||||
|
||||
// Enable React devtools
|
||||
window['React'] = React;
|
||||
|
||||
$(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';
|
||||
|
||||
declare const OCP: any;
|
||||
|
||||
$(() => {
|
||||
function generateWarningElement(message: string) {
|
||||
return $(`<div id="bbb-warning"><span class="icon icon-error-color icon-visible"></span> ${message}</div>`);
|
||||
|
|
@ -23,14 +21,10 @@ $(() => {
|
|||
}
|
||||
|
||||
function checkPasswordConfirmation() {
|
||||
return new Promise<void>(resolve => {
|
||||
if (OC.PasswordConfirmation && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
|
||||
OC.PasswordConfirmation.requirePasswordConfirmation(() => resolve());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
resolve();
|
||||
return new Promise<void>((resolve: () => void) => {
|
||||
OC.PasswordConfirmation?.requiresPasswordConfirmation()
|
||||
? OC.PasswordConfirmation.requirePasswordConfirmation(() => resolve())
|
||||
: resolve();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +43,14 @@ $(() => {
|
|||
|
||||
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'));
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
@ -95,7 +96,11 @@ $(() => {
|
|||
|
||||
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'));
|
||||
|
||||
setTimeout(() => {
|
||||
|
|
@ -123,7 +128,7 @@ $(() => {
|
|||
$<HTMLInputElement>('#bbb-shortener [name="app.shortener"]').on('keyup', (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
const {value} = ev.target;
|
||||
const {value} = ev.target as HTMLInputElement;
|
||||
|
||||
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.'));
|
||||
|
|
@ -154,8 +159,10 @@ return 307;</pre></details>
|
|||
$<HTMLInputElement>('.bbb-setting[type="checkbox"]').on('change', (ev) => {
|
||||
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 { generateOcsUrl, generateUrl } from '@nextcloud/router';
|
||||
import { showSuccess, showWarning, showError } from '@nextcloud/dialogs';
|
||||
import '@nextcloud/dialogs/styles/toast';
|
||||
import { api } from './Common/Api';
|
||||
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 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();
|
||||
|
||||
|
|
@ -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',
|
||||
'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.',
|
||||
--countdown
|
||||
)
|
||||
--countdown,
|
||||
),
|
||||
);
|
||||
|
||||
if (countdown === 0) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"lib": [
|
||||
"dom",
|
||||
"es2015.promise",
|
||||
"es6"
|
||||
"es2017"
|
||||
],
|
||||
"module": "ES6",
|
||||
"moduleResolution": "node",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,30 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
process.env.npm_package_name = 'bbb';
|
||||
|
||||
const path = require('path');
|
||||
const ESLintPlugin = require('eslint-webpack-plugin');
|
||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||
const webpackRules = require('@nextcloud/webpack-vue-config/rules')
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
webpackRules.RULE_TSX = {
|
||||
test: /\.tsx?$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
babelrc: false,
|
||||
},
|
||||
},
|
||||
'ts-loader',
|
||||
],
|
||||
};
|
||||
webpackRules.RULE_RAW = {
|
||||
test: /\.svg$/,
|
||||
resourceQuery: /raw/,
|
||||
type: 'asset/source'
|
||||
};
|
||||
|
||||
webpackConfig.entry = {
|
||||
admin: [
|
||||
path.join(__dirname, 'ts', 'admin.ts'),
|
||||
],
|
||||
|
|
@ -22,55 +43,12 @@ module.exports = {
|
|||
waiting: [
|
||||
path.join(__dirname, 'ts', 'waiting.ts'),
|
||||
],
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, './js'),
|
||||
publicPath: '/js/',
|
||||
filename: '[name].js',
|
||||
chunkFilename: 'chunks/[name]-[hash].js',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
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,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
webpackConfig.module.rules = Object.values(webpackRules);
|
||||
|
||||
webpackConfig.plugins.push(new ESLintPlugin());
|
||||
|
||||
webpackConfig.resolve.extensions = [...webpackConfig.resolve.extensions, '.jsx', '.ts', '.tsx'];
|
||||
|
||||
module.exports = webpackConfig
|
||||
|
|
|
|||
Loading…
Reference in New Issue