Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117766167
D5658.1775232005.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
94 KB
Referenced Files
None
Subscribers
None
D5658.1775232005.diff
View Options
diff --git a/src/app/Device.php b/src/app/Device.php
--- a/src/app/Device.php
+++ b/src/app/Device.php
@@ -127,7 +127,7 @@
DB::beginTransaction();
// Check if a device already exists
- $device = Device::withTrashed()->where('hash', $token)->first();
+ $device = self::withTrashed()->where('hash', $token)->first();
if ($device) {
// FIXME: Should we remove the user (if it's a role=device user)?
diff --git a/src/app/Entitlement.php b/src/app/Entitlement.php
--- a/src/app/Entitlement.php
+++ b/src/app/Entitlement.php
@@ -86,7 +86,7 @@
*
* @param object $object Entitleable object
*
- * @return array Skus list with some metadata
+ * @return array<string, array> Skus list with some metadata
*/
public static function objectEntitlementsSummary($object): array
{
diff --git a/src/app/Http/Controllers/API/V4/CompanionAppsController.php b/src/app/Http/Controllers/API/V4/CompanionAppsController.php
--- a/src/app/Http/Controllers/API/V4/CompanionAppsController.php
+++ b/src/app/Http/Controllers/API/V4/CompanionAppsController.php
@@ -54,8 +54,6 @@
*/
public function store(Request $request): JsonResponse
{
- $user = $this->guard()->user();
-
$v = Validator::make(
$request->all(),
[
@@ -64,12 +62,12 @@
);
if ($v->fails()) {
- return response()->json(['status' => 'error', 'errors' => $v->errors()], 422);
+ return response()->json(['status' => 'error', /* @var array */ 'errors' => $v->errors()], 422);
}
$app = CompanionApp::create([
'name' => $request->name,
- 'user_id' => $user->id,
+ 'user_id' => $this->guard()->user()->id,
]);
return response()->json([
@@ -98,7 +96,7 @@
);
if ($v->fails()) {
- return response()->json(['status' => 'error', 'errors' => $v->errors()], 422);
+ return response()->json(['status' => 'error', /* @var array */ 'errors' => $v->errors()], 422);
}
$notificationToken = $request->notificationToken;
@@ -184,7 +182,7 @@
*
* @param string $id Companion app identifier
*/
- public function show($id): CompanionAppResource|JsonResponse
+ public function show($id): JsonResponse
{
$result = CompanionApp::find($id);
if (!$result) {
@@ -196,7 +194,7 @@
return $this->errorResponse(403);
}
- return new CompanionAppResource($result);
+ return (new CompanionAppResource($result))->response();
}
/**
diff --git a/src/app/Http/Controllers/API/V4/MeetController.php b/src/app/Http/Controllers/API/V4/MeetController.php
--- a/src/app/Http/Controllers/API/V4/MeetController.php
+++ b/src/app/Http/Controllers/API/V4/MeetController.php
@@ -3,7 +3,9 @@
namespace App\Http\Controllers\API\V4;
use App\Http\Controllers\Controller;
+use App\Http\Resources\RoomSessionResource;
use App\Meet\Room;
+use Dedoc\Scramble\Attributes\BodyParameter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@@ -12,11 +14,18 @@
class MeetController extends Controller
{
/**
- * Join the room session. Each room has one owner, and the room isn't open until the owner
+ * Join the room session.
+ *
+ * Each room has one owner, and the room isn't open until the owner
* joins (and effectively creates the session).
*
* @param string $id Room identifier (name)
*/
+ #[BodyParameter('password', description: 'Room password', type: 'string')]
+ #[BodyParameter('requestId', description: 'Unique client identifier', type: 'string')]
+ #[BodyParameter('picture', description: 'User image', type: 'string')]
+ #[BodyParameter('nickname', description: 'User nickname', type: 'string')]
+ #[BodyParameter('canPublish', description: 'Client has media device(s)', type: 'bool')]
public function joinRoom($id): JsonResponse
{
$room = Room::where('name', $id)->first();
@@ -27,21 +36,27 @@
}
$user = Auth::guard()->user();
- $isOwner = $user && (
+ $init = !empty(request()->input('init'));
+ $settings = $room->getSettings(['locked', 'nomedia', 'password']);
+ $password = (string) $settings['password'];
+
+ $response = new RoomSessionResource($settings);
+ $response->isOwner = $user && (
$user->id == $wallet->owner->id || $room->permissions()->where('user', $user->email)->exists()
);
- $init = !empty(request()->input('init'));
// There's no existing session
if (!$room->hasSession()) {
// Participants can't join the room until the session is created by the owner
- if (!$isOwner) {
- return $this->errorResponse(422, self::trans('meet.session-not-found'), ['code' => 323]);
+ if (!$response->isOwner) {
+ $response->code = 323;
+ return $response->response()->setStatusCode(422);
}
// The room owner can create the session on request
if (!$init) {
- return $this->errorResponse(422, self::trans('meet.session-not-found'), ['code' => 324]);
+ $response->code = 324;
+ return $response->response()->setStatusCode(422);
}
$session = $room->createSession();
@@ -51,49 +66,37 @@
}
}
- $settings = $room->getSettings(['locked', 'nomedia', 'password']);
- $password = (string) $settings['password'];
-
- $config = [
- 'locked' => $settings['locked'] === 'true',
- 'nomedia' => $settings['nomedia'] === 'true',
- 'password' => $isOwner ? $password : '',
- 'requires_password' => !$isOwner && strlen($password),
- ];
-
- $response = ['config' => $config];
-
// Validate room password
- if (!$isOwner && strlen($password)) {
+ if (!$response->isOwner && strlen($password)) {
$request_password = request()->input('password');
if ($request_password !== $password) {
- $response['code'] = 325;
- return $this->errorResponse(422, self::trans('meet.session-password-error'), $response);
+ $response->code = 325;
+ return $response->response()->setStatusCode(422);
}
}
// Handle locked room
- if (!$isOwner && $config['locked']) {
+ if (!$response->isOwner && $settings['locked']) {
$nickname = request()->input('nickname');
$picture = request()->input('picture');
$requestId = request()->input('requestId');
$request = $requestId ? $room->requestGet($requestId) : null;
- $error = self::trans('meet.session-room-locked-error');
-
// Request already has been processed (not accepted yet, but it could be denied)
if (empty($request['status']) || $request['status'] != Room::REQUEST_ACCEPTED) {
if (!$request) {
if (empty($nickname) || empty($requestId) || !preg_match('/^[a-z0-9]{8,32}$/i', $requestId)) {
- return $this->errorResponse(422, $error, $response + ['code' => 326]);
+ $response->code = 326;
+ return $response->response()->setStatusCode(422);
}
if (empty($picture)) {
$svg = file_get_contents(resource_path('images/user.svg'));
$picture = 'data:image/svg+xml;base64,' . base64_encode($svg);
} elseif (!preg_match('|^data:image/png;base64,[a-zA-Z0-9=+/]+$|', $picture)) {
- return $this->errorResponse(422, $error, $response + ['code' => 326]);
+ $response->code = 326;
+ return $response->response()->setStatusCode(422);
}
// TODO: Resize when big/make safe the user picture?
@@ -102,43 +105,39 @@
if (!$room->requestSave($requestId, $request)) {
// FIXME: should we use error code 500?
- return $this->errorResponse(422, $error, $response + ['code' => 326]);
+ $response->code = 326;
+ return $response->response()->setStatusCode(422);
}
// Send the request (signal) to all moderators
- $result = $room->signal('joinRequest', $request, Room::ROLE_MODERATOR);
+ $room->signal('joinRequest', $request, Room::ROLE_MODERATOR);
}
- return $this->errorResponse(422, $error, $response + ['code' => 327]);
+ $response->code = 327;
+ return $response->response()->setStatusCode(422);
}
}
- // Initialize connection tokens
- if ($init) {
- // Choose the connection role
- $canPublish = !empty(request()->input('canPublish')) && (empty($config['nomedia']) || $isOwner);
- $role = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
- if ($isOwner) {
- $role |= Room::ROLE_MODERATOR;
- $role |= Room::ROLE_OWNER;
- }
+ if (!$init) {
+ $response->code = 322;
+ return $response->response()->setStatusCode(422);
+ }
- // Create session token for the current user/connection
- $response = $room->getSessionToken($role);
+ // Choose the connection role
+ $canPublish = !empty(request()->input('canPublish')) && (empty($settings['nomedia']) || $response->isOwner);
+ $response->role = $canPublish ? Room::ROLE_PUBLISHER : Room::ROLE_SUBSCRIBER;
+ if ($response->isOwner) {
+ $response->role |= Room::ROLE_MODERATOR | Room::ROLE_OWNER;
+ }
- if (empty($response)) {
- return $this->errorResponse(500, self::trans('meet.session-join-error'));
- }
+ // Create session token for the current user/connection
+ $response->token = $room->getSessionToken($response->role);
- $response_code = 200;
- $response['role'] = $role;
- $response['config'] = $config;
- } else {
- $response_code = 422;
- $response['code'] = 322;
+ if (empty($response->token)) {
+ return $this->errorResponse(500, self::trans('meet.session-join-error'));
}
- return response()->json($response, $response_code);
+ return $response->response();
}
/**
diff --git a/src/app/Http/Controllers/API/V4/PolicyController.php b/src/app/Http/Controllers/API/V4/PolicyController.php
--- a/src/app/Http/Controllers/API/V4/PolicyController.php
+++ b/src/app/Http/Controllers/API/V4/PolicyController.php
@@ -61,8 +61,9 @@
}
/**
- * Fetch the account policies for the current user account.
- * The result includes all supported policy rules.
+ * List the account policies.
+ *
+ * The result includes all supported policy rules for the current user account.
*/
public function index(Request $request): JsonResponse
{
@@ -114,7 +115,9 @@
return Mailfilter::handle($request);
}
- // Apply a sensible rate limitation to a request.
+ /**
+ * Apply a sensible rate limitation to a request.
+ */
public function ratelimit(): JsonResponse
{
$response = RateLimit::handle(\request()->input());
@@ -132,7 +135,9 @@
return $response->jsonResponse();
}
- // Apply the sender policy framework to a request.
+ /**
+ * Apply the sender policy framework to a request.
+ */
public function senderPolicyFramework(): JsonResponse
{
$response = SPF::handle(\request()->input());
@@ -140,7 +145,9 @@
return $response->jsonResponse();
}
- // Validate sender/recipients in an SMTP submission request.
+ /**
+ * Validate sender/recipients in an SMTP submission request.
+ */
public function submission(): JsonResponse
{
$response = SmtpAccess::submission(\request()->input());
diff --git a/src/app/Http/Controllers/API/V4/RoomsController.php b/src/app/Http/Controllers/API/V4/RoomsController.php
--- a/src/app/Http/Controllers/API/V4/RoomsController.php
+++ b/src/app/Http/Controllers/API/V4/RoomsController.php
@@ -2,10 +2,12 @@
namespace App\Http\Controllers\API\V4;
-use App\Entitlement;
use App\Http\Controllers\RelationController;
+use App\Http\Resources\RoomInfoResource;
+use App\Http\Resources\RoomResource;
use App\Meet\Room;
use App\Permission;
+use Dedoc\Scramble\Attributes\BodyParameter;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@@ -28,7 +30,7 @@
/**
* Delete a room
*
- * @param string $id Room identifier
+ * @param int|string $id Room identifier (or name)
*/
public function destroy($id): JsonResponse
{
@@ -65,19 +67,16 @@
$room->assignToWallet($user->wallets()->first());
}
- $rooms = $user->rooms(true)
- ->union($shared)
- ->orderBy('name')
- ->get()
- ->map(function ($room) {
- return $this->objectToClient($room);
- });
+ $rooms = $user->rooms(true)->union($shared)->orderBy('name')->get();
$result = [
- // @var array List of rooms
- 'list' => $rooms,
+ 'status' => 'success',
+ // List of rooms
+ 'list' => RoomResource::collection($rooms),
// @var int Number of entries in the list
'count' => count($rooms),
+ // @var bool Indicates that there are more entries available
+ 'hasMore' => false,
];
return response()->json($result);
@@ -105,7 +104,7 @@
$errors = $room->setConfig($request);
if (!empty($errors)) {
- return response()->json(['status' => 'error', 'errors' => $errors], 422);
+ return response()->json(['status' => 'error', /* @var array */ 'errors' => $errors], 422);
}
return response()->json([
@@ -117,7 +116,7 @@
/**
* Get room information.
*
- * @param string $id Room identifier
+ * @param string $id Room identifier (or name)
*/
public function show($id): JsonResponse
{
@@ -126,41 +125,16 @@
return $this->errorResponse($room);
}
- $wallet = $room->wallet();
- $user = $this->guard()->user();
-
- $response = $this->objectToClient($room, true);
-
- unset($response['session_id']);
-
- $response['config'] = $room->getConfig();
-
- // Room sharees can't manage/see room ACL
- if ($permission) {
- unset($response['config']['acl']);
- }
-
- $response['skus'] = Entitlement::objectEntitlementsSummary($room);
- $response['wallet'] = $wallet->toArray();
+ $resource = new RoomInfoResource($room);
+ $resource->permission = $permission;
- if ($wallet->discount) {
- $response['wallet']['discount'] = $wallet->discount->discount;
- $response['wallet']['discount_description'] = $wallet->discount->description;
- }
-
- $isOwner = $user->canDelete($room);
- $response['canUpdate'] = $isOwner || $room->permissions()->where('user', $user->email)->exists();
- $response['canDelete'] = $isOwner && $user->wallet()->isController($user);
- $response['canShare'] = $isOwner && $room->hasSKU('group-room');
- $response['isOwner'] = $isOwner;
-
- return response()->json($response);
+ return $resource->response();
}
/**
* Get a list of SKUs available to the room.
*
- * @param int $id Room identifier
+ * @param int $id Room identifier (or name)
*/
public function skus($id): JsonResponse
{
@@ -173,10 +147,11 @@
}
/**
- * Create a new room.
+ * Create a room.
*
* @param Request $request the API request
*/
+ #[BodyParameter('skus', description: 'Enabled SKUs', type: 'array')]
public function store(Request $request): JsonResponse
{
$user = $this->guard()->user();
@@ -187,11 +162,12 @@
}
$v = Validator::make($request->all(), [
+ // Room description
'description' => 'nullable|string|max:191',
]);
if ($v->fails()) {
- return response()->json(['status' => 'error', 'errors' => $v->errors()], 422);
+ return response()->json(['status' => 'error', /* @var array */ 'errors' => $v->errors()], 422);
}
DB::beginTransaction();
@@ -220,6 +196,7 @@
* @param Request $request the API request
* @param string $id Room identifier
*/
+ #[BodyParameter('skus', description: 'Enabled SKUs', type: 'array')]
public function update(Request $request, $id): JsonResponse
{
$room = $this->inputRoom($id, Permission::ADMIN);
@@ -228,6 +205,7 @@
}
$v = Validator::make($request->all(), [
+ // Room description
'description' => 'nullable|string|max:191',
]);
@@ -242,7 +220,7 @@
SkusController::updateEntitlements($room, $request->skus);
- if (!$room->hasSKU('group-room')) {
+ if (!$room->hasSku('group-room')) {
$room->setSetting('acl', null);
}
diff --git a/src/app/Http/Controllers/API/V4/SkusController.php b/src/app/Http/Controllers/API/V4/SkusController.php
--- a/src/app/Http/Controllers/API/V4/SkusController.php
+++ b/src/app/Http/Controllers/API/V4/SkusController.php
@@ -4,6 +4,7 @@
use App\Handlers\Mailbox;
use App\Http\Controllers\ResourceController;
+use App\Http\Resources\SkuResource;
use App\Sku;
use App\Wallet;
use Dedoc\Scramble\Attributes\QueryParameter;
@@ -19,38 +20,26 @@
public function index(): JsonResponse
{
$type = request()->input('type');
+ $wallet = $this->guard()->user()->wallet();
// Note: Order by title for consistent ordering in tests
- $response = Sku::withSubjectTenantContext()->where('active', true)->orderBy('title')
+ $list = Sku::withSubjectTenantContext()->where('active', true)->orderBy('title')
->get()
- ->transform(function ($sku) {
- return $this->skuElement($sku);
+ ->transform(function ($sku, $type) {
+ return $this->skuElement($sku, $type);
})
->filter(static function ($sku) use ($type) {
- return $sku && (!$type || $sku['type'] === $type);
+ return $sku && (!$type || $sku->metadata['type'] === $type);
})
->sortByDesc('prio')
->values();
- if ($type) {
- $wallet = $this->guard()->user()->wallet();
-
- // Figure out the cost for a new object of the specified type
- $response = $response->map(static function ($sku) use ($wallet) {
- $sku['nextCost'] = $sku['cost'];
- if ($sku['cost'] && $sku['units_free']) {
- $count = $wallet->entitlements()->where('sku_id', $sku['id'])->count();
-
- if ($count < $sku['units_free']) {
- $sku['nextCost'] = 0;
- }
- }
-
- return $sku;
- });
- }
-
- return response()->json($response->all());
+ return response()->json([
+ // @var array<SkuResource> List of SKUs
+ 'list' => $list,
+ // @var int Number of entries in the list
+ 'count' => $list->count(),
+ ]);
}
/**
@@ -60,41 +49,31 @@
*/
public static function objectSkus($object): JsonResponse
{
- $response = [];
+ $user = Auth::guard()->user();
// Note: Order by title for consistent ordering in tests
- $skus = Sku::withObjectTenantContext($object)->orderBy('title')->get();
-
- foreach ($skus as $sku) {
- if (!class_exists($sku->handler_class)) {
- continue;
- }
-
- if ($object::class != $sku->handler_class::entitleableClass()) {
- continue;
- }
-
- if (!$sku->handler_class::isAvailable($sku, $object)) {
- continue;
- }
-
- if ($data = self::skuElement($sku)) {
- if (!empty($data['controllerOnly'])) {
- $user = Auth::guard()->user();
- if (!$user->wallet()->isController($user)) {
- continue;
- }
- }
-
- $response[] = $data;
- }
- }
-
- usort($response, static function ($a, $b) {
- return $b['prio'] <=> $a['prio'];
- });
+ $list = Sku::withObjectTenantContext($object)->orderBy('title')
+ ->get()
+ ->filter(static function ($sku) use ($object) {
+ return class_exists($sku->handler_class)
+ && $object::class == $sku->handler_class::entitleableClass()
+ && $sku->handler_class::isAvailable($sku, $object);
+ })
+ ->transform(function ($sku) {
+ return self::skuElement($sku);
+ })
+ ->filter(static function ($sku) use ($user) {
+ return !empty($sku) && (empty($sku->controllerOnly) || $user->wallet()->isController($user));
+ })
+ ->sortByDesc('prio')
+ ->values();
- return response()->json($response);
+ return response()->json([
+ // @var array<SkuResource> List of SKUs
+ 'list' => $list,
+ // @var int Number of entries in the list
+ 'count' => $list->count(),
+ ]);
}
/**
@@ -157,31 +136,26 @@
* Convert SKU information to metadata used by UI to
* display the form control
*
- * @param Sku $sku SKU object
- *
- * @return array|null Metadata
+ * @param Sku $sku SKU object
+ * @param string $type Type filter
*/
- protected static function skuElement($sku): ?array
+ protected static function skuElement($sku, $type = null): ?SkuResource
{
if (!class_exists($sku->handler_class)) {
\Log::warning("Missing handler {$sku->handler_class}");
return null;
}
- $data = array_merge($sku->toArray(), $sku->handler_class::metadata($sku));
+ $resource = new SkuResource($sku);
// ignore incomplete handlers
- if (empty($data['type'])) {
+ if (empty($resource->metadata['type'])) {
\Log::warning("Incomplete handler {$sku->handler_class}");
return null;
}
- // Use localized value, toArray() does not get them right
- $data['name'] = $sku->name;
- $data['description'] = $sku->description;
-
- unset($data['handler_class'], $data['created_at'], $data['updated_at'], $data['fee'], $data['tenant_id']);
+ $resource->type = $type;
- return $data;
+ return $resource;
}
}
diff --git a/src/app/Http/Controllers/API/V4/SupportController.php b/src/app/Http/Controllers/API/V4/SupportController.php
--- a/src/app/Http/Controllers/API/V4/SupportController.php
+++ b/src/app/Http/Controllers/API/V4/SupportController.php
@@ -32,7 +32,7 @@
]);
if ($v->fails()) {
- return response()->json(['status' => 'error', 'errors' => $v->errors()], 422);
+ return response()->json(['status' => 'error', /* @var array */ 'errors' => $v->errors()], 422);
}
$params = $request->only(array_keys($rules));
diff --git a/src/app/Http/Controllers/API/V4/VPNController.php b/src/app/Http/Controllers/API/V4/VPNController.php
--- a/src/app/Http/Controllers/API/V4/VPNController.php
+++ b/src/app/Http/Controllers/API/V4/VPNController.php
@@ -15,13 +15,13 @@
class VPNController extends Controller
{
/**
- * Token request from the vpn module
+ * Get a token from the VPN module
*/
public function token(Request $request): JsonResponse
{
- $signingKey = \config("app.vpn.token_signing_key");
+ $signingKey = \config('app.vpn.token_signing_key');
if (empty($signingKey)) {
- throw new \Exception("app.vpn.token_signing_key is not set");
+ throw new \Exception('app.vpn.token_signing_key is not set');
}
$tokenBuilder = (new Builder(new JoseEncoder(), ChainedFormatter::default()));
@@ -29,7 +29,7 @@
->issuedAt(Carbon::now()->toImmutable())
// The entitlement is hardcoded for now to default.
// Can be extended in the future based on user entitlements.
- ->withClaim('entitlement', "default")
+ ->withClaim('entitlement', 'default')
->getToken(new Rsa\Sha256(), InMemory::plainText($signingKey));
return response()->json(['status' => 'ok', 'token' => $token->toString()]);
diff --git a/src/app/Http/Controllers/API/V4/WalletsController.php b/src/app/Http/Controllers/API/V4/WalletsController.php
--- a/src/app/Http/Controllers/API/V4/WalletsController.php
+++ b/src/app/Http/Controllers/API/V4/WalletsController.php
@@ -4,6 +4,7 @@
use App\Documents\Receipt;
use App\Http\Controllers\ResourceController;
+use App\Http\Resources\TransactionResource;
use App\Http\Resources\WalletInfoResource;
use App\Payment;
use App\ReferralCode;
@@ -238,7 +239,6 @@
$pageSize = 10;
$page = (int) (request()->input('page')) ?: 1;
$hasMore = false;
- $isAdmin = $this instanceof Admin\WalletsController;
if ($transaction = request()->input('transaction')) {
// Get sub-transactions for the specified transaction ID, first
@@ -271,27 +271,15 @@
}
}
- $result = $result->map(static function ($item) use ($isAdmin, $wallet) {
- $entry = [
- 'id' => $item->id,
- 'createdAt' => $item->created_at->format('Y-m-d H:i'),
- 'type' => $item->type,
- 'description' => $item->shortDescription(),
- 'amount' => $item->amount,
- 'currency' => $wallet->currency,
- 'hasDetails' => !empty($item->cnt),
- ];
-
- if ($isAdmin && $item->user_email) {
- $entry['user'] = $item->user_email;
- }
-
+ $result = $result->map(static function ($item) use ($wallet) {
+ $entry = new TransactionResource($item);
+ $entry->wallet = $wallet;
return $entry;
});
return response()->json([
'status' => 'success',
- // @var array<array> List of transactions (properties: id, createdAt, type, description, amount, currency, hasDetails, user)
+ // @var array<TransactionResource> List of transactions
'list' => $result,
// @var int Number of entries in the list
'count' => count($result),
diff --git a/src/app/Http/Resources/ApiResource.php b/src/app/Http/Resources/ApiResource.php
--- a/src/app/Http/Resources/ApiResource.php
+++ b/src/app/Http/Resources/ApiResource.php
@@ -23,18 +23,16 @@
/**
* Include SKUs/Wallet information in the object's response.
- *
- * @param object $object User/Domain/etc object
*/
- public static function objectEntitlements($object): array
+ public function objectEntitlements(): array
{
- $wallet = $object->wallet();
+ $wallet = $this->resource->wallet();
return [
- // Entitlements information
- 'skus' => Entitlement::objectEntitlementsSummary($object),
+ // @var array<string, array> Entitlements information
+ 'skus' => Entitlement::objectEntitlementsSummary($this->resource),
// Wallet information
- 'wallet' => $wallet ? new WalletResource($wallet) : null,
+ 'wallet' => $this->when($wallet, new WalletResource($wallet)),
];
}
}
diff --git a/src/app/Http/Resources/DeviceInfoResource.php b/src/app/Http/Resources/DeviceInfoResource.php
--- a/src/app/Http/Resources/DeviceInfoResource.php
+++ b/src/app/Http/Resources/DeviceInfoResource.php
@@ -19,7 +19,10 @@
public function toArray(Request $request): array
{
return [
- // Device registration date-time
+ /*
+ * @var string Device registration date-time
+ * @format date-time
+ */
'created_at' => (string) $this->resource->created_at,
];
}
diff --git a/src/app/Http/Resources/DomainInfoResource.php b/src/app/Http/Resources/DomainInfoResource.php
--- a/src/app/Http/Resources/DomainInfoResource.php
+++ b/src/app/Http/Resources/DomainInfoResource.php
@@ -21,12 +21,6 @@
// @var int Domain status
'status' => $this->resource->status,
- // Domain creation date-time
- 'created_at' => (string) $this->resource->created_at,
- // Domain modification date-time
- 'updated_at' => (string) $this->resource->updated_at,
- // @var string|null Domain deletion date-time
- 'deleted_at' => (string) $this->resource->deleted_at,
// Domain DNS hash
'hash_text' => $this->resource->hash(Domain::HASH_TEXT),
@@ -47,7 +41,7 @@
'statusInfo' => DomainsController::statusInfo($this->resource),
// Entitlements/Wallet information
- $this->merge(self::objectEntitlements($this->resource)),
+ $this->merge($this->objectEntitlements()),
];
}
diff --git a/src/app/Http/Resources/DomainResource.php b/src/app/Http/Resources/DomainResource.php
--- a/src/app/Http/Resources/DomainResource.php
+++ b/src/app/Http/Resources/DomainResource.php
@@ -28,6 +28,19 @@
// Domain type
'type' => $this->resource->type,
+ $this->mergeWhen(self::isAdmin(), [
+ /*
+ * @var string Domain creation date-time
+ * @format date-time
+ */
+ 'created_at' => (string) $this->resource->created_at,
+ /*
+ * @var string Domain deletion date-time
+ * @format date-time
+ */
+ 'deleted_at' => (string) $this->resource->deleted_at,
+ ]),
+
// @var bool Is domain active?
'isActive' => $state['isActive'] ?? false,
// @var bool Is domain deleted?
diff --git a/src/app/Http/Resources/GroupInfoResource.php b/src/app/Http/Resources/GroupInfoResource.php
--- a/src/app/Http/Resources/GroupInfoResource.php
+++ b/src/app/Http/Resources/GroupInfoResource.php
@@ -21,12 +21,6 @@
// @var int Group status
'status' => $this->resource->status,
- // Group creation date-time
- 'created_at' => (string) $this->resource->created_at,
- // Group modification date-time
- 'updated_at' => (string) $this->resource->updated_at,
- // @var string|null Group deletion date-time
- 'deleted_at' => (string) $this->resource->deleted_at,
// @var array<string, mixed> Group configuration, e.g. spf whitelist
'config' => $this->resource->getConfig(),
@@ -38,7 +32,7 @@
'statusInfo' => GroupsController::statusInfo($this->resource),
// Entitlements/Wallet information
- $this->merge(self::objectEntitlements($this->resource)),
+ $this->merge($this->objectEntitlements()),
];
}
}
diff --git a/src/app/Http/Resources/GroupResource.php b/src/app/Http/Resources/GroupResource.php
--- a/src/app/Http/Resources/GroupResource.php
+++ b/src/app/Http/Resources/GroupResource.php
@@ -28,6 +28,19 @@
// Group name
'name' => $this->resource->name,
+ $this->mergeWhen(self::isAdmin(), [
+ /*
+ * @var string Group creation date-time
+ * @format date-time
+ */
+ 'created_at' => (string) $this->resource->created_at,
+ /*
+ * @var string Group deletion date-time
+ * @format date-time
+ */
+ 'deleted_at' => (string) $this->resource->deleted_at,
+ ]),
+
// @var bool Is group active?
'isActive' => $state['isActive'] ?? false,
// @var bool Is group deleted?
diff --git a/src/app/Http/Resources/ResourceInfoResource.php b/src/app/Http/Resources/ResourceInfoResource.php
--- a/src/app/Http/Resources/ResourceInfoResource.php
+++ b/src/app/Http/Resources/ResourceInfoResource.php
@@ -21,12 +21,6 @@
// @var int Resource status
'status' => $this->resource->status,
- // Resource creation date-time
- 'created_at' => (string) $this->resource->created_at,
- // Resource modification date-time
- 'updated_at' => (string) $this->resource->updated_at,
- // @var string|null Resource deletion date-time
- 'deleted_at' => (string) $this->resource->deleted_at,
// @var array<string, mixed> Resource configuration
'config' => $this->resource->getConfig(),
@@ -35,7 +29,7 @@
'statusInfo' => ResourcesController::statusInfo($this->resource),
// Entitlements/Wallet information
- $this->merge(self::objectEntitlements($this->resource)),
+ $this->merge($this->objectEntitlements()),
];
}
}
diff --git a/src/app/Http/Resources/ResourceResource.php b/src/app/Http/Resources/ResourceResource.php
--- a/src/app/Http/Resources/ResourceResource.php
+++ b/src/app/Http/Resources/ResourceResource.php
@@ -28,6 +28,19 @@
// Resource name
'name' => $this->resource->name,
+ $this->mergeWhen(self::isAdmin(), [
+ /*
+ * @var string Resource creation date-time
+ * @format date-time
+ */
+ 'created_at' => (string) $this->resource->created_at,
+ /*
+ * @var string Resource deletion date-time
+ * @format date-time
+ */
+ 'deleted_at' => (string) $this->resource->deleted_at,
+ ]),
+
// @var bool Is resource active?
'isActive' => $state['isActive'] ?? false,
// @var bool Is resource deleted?
diff --git a/src/app/Http/Resources/RoomInfoResource.php b/src/app/Http/Resources/RoomInfoResource.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Resources/RoomInfoResource.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace App\Http\Resources;
+
+use App\Meet\Room;
+use App\Permission;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+/**
+ * Room information response
+ */
+class RoomInfoResource extends RoomResource
+{
+ // Current user permission
+ public ?Permission $permission = null;
+
+ /**
+ * Transform the resource into an array.
+ */
+ public function toArray(Request $request): array
+ {
+ $user = Auth::guard()->user();
+ $isOwner = $user->canDelete($this->resource);
+ $config = $this->resource->getConfig();
+
+ // Room sharees can't manage/see room ACL
+ if ($this->permission) {
+ unset($config['acl']);
+ }
+
+ return [
+ $this->merge(parent::toArray($request)),
+
+ // @var array<string, mixed> Room configuration
+ 'config' => $config,
+
+ // @var bool Can the user update the room?
+ 'canUpdate' => $isOwner || $this->resource->permissions()->where('user', $user->email)->exists(),
+
+ // @var bool Can the user delete the room?
+ 'canDelete' => $isOwner && $user->wallet()->isController($user),
+
+ // @var bool Can the user share the room?
+ 'canShare' => $isOwner && $this->resource->hasSku('group-room'),
+
+ // @var bool Is the user an owner of this room?
+ 'isOwner' => $isOwner,
+
+ // Entitlements/Wallet information
+ $this->merge($this->objectEntitlements()),
+ ];
+ }
+}
diff --git a/src/app/Http/Resources/RoomResource.php b/src/app/Http/Resources/RoomResource.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Resources/RoomResource.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace App\Http\Resources;
+
+use App\Http\Controllers\RelationController;
+use App\Meet\Room;
+use Illuminate\Http\Request;
+
+/**
+ * Room response
+ *
+ * @mixin Room
+ */
+class RoomResource extends ApiResource
+{
+ /**
+ * Transform the resource into an array.
+ */
+ public function toArray(Request $request): array
+ {
+ $state = RelationController::objectState($this->resource);
+
+ return [
+ // Room identifier
+ 'id' => $this->resource->id,
+ // Room name
+ 'name' => $this->resource->name,
+ // Room description
+ 'description' => $this->resource->description,
+
+ $this->mergeWhen(self::isAdmin(), [
+ /*
+ * @var string Room creation date-time
+ * @format date-time
+ */
+ 'created_at' => (string) $this->resource->created_at,
+ /*
+ * @var string Room deletion date-time
+ * @format date-time
+ */
+ 'deleted_at' => (string) $this->resource->deleted_at,
+ ]),
+
+ // @var bool Is room deleted?
+ 'isDeleted' => $state['isDeleted'] ?? false,
+ // @var bool Readiness state
+ 'isReady' => $state['isReady'],
+ ];
+ }
+}
diff --git a/src/app/Http/Resources/RoomSessionResource.php b/src/app/Http/Resources/RoomSessionResource.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Resources/RoomSessionResource.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Http\Resources;
+
+use App\Http\Controllers\Controller;
+use Illuminate\Http\Request;
+
+/**
+ * Meet room session response
+ */
+class RoomSessionResource extends ApiResource
+{
+ public ?int $code = null;
+ public bool $isOwner = false;
+ public ?string $token = null;
+ public ?int $role = null;
+
+ /**
+ * Transform the resource into an array.
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ // @var int Response status code
+ 'code' => $this->when(isset($this->code), $this->code),
+ // @var string Response message
+ 'message' => $this->when(isset($this->code), $this->message($this->code)),
+ // @var string Response status
+ 'status' => $this->code ? 'error' : 'success',
+ // Room configuration
+ 'config' => [
+ // @var bool Whether the room is locked
+ 'locked' => $this->resource['locked'] === 'true',
+ // @var bool Whether use of media is disabled in the room
+ 'nomedia' => $this->resource['nomedia'] === 'true',
+ // @var string Room password
+ 'password' => $this->isOwner ? (string) $this->resource['password'] : '',
+ // @var bool Whether the password is required or not
+ 'requires_password' => !$this->isOwner && strlen((string) $this->resource['password']),
+ ],
+ // @var int User role in the room
+ 'role' => $this->when(isset($this->role), $this->role),
+ // @var string Session token
+ 'token' => $this->when(isset($this->token), $this->token),
+ ];
+ }
+
+ /**
+ * Get a status message for the status code
+ */
+ private function message($code): ?string
+ {
+ switch ((int) $code) {
+ case 323:
+ case 324:
+ return Controller::trans('meet.session-not-found');
+ case 325:
+ return Controller::trans('meet.session-password-error');
+ case 326:
+ case 327:
+ return Controller::trans('meet.session-room-locked-error');
+ }
+
+ return null;
+ }
+}
diff --git a/src/app/Http/Resources/SharedFolderInfoResource.php b/src/app/Http/Resources/SharedFolderInfoResource.php
--- a/src/app/Http/Resources/SharedFolderInfoResource.php
+++ b/src/app/Http/Resources/SharedFolderInfoResource.php
@@ -20,12 +20,6 @@
// @var int Folder status
'status' => $this->resource->status,
- // Folder creation date-time
- 'created_at' => (string) $this->resource->created_at,
- // Folder modification date-time
- 'updated_at' => (string) $this->resource->updated_at,
- // @var string|null Folder deletion date-time
- 'deleted_at' => (string) $this->resource->deleted_at,
// @var array Folder aliases (email addresses)
'aliases' => $this->resource->aliases()->pluck('alias')->all(),
@@ -37,7 +31,7 @@
'statusInfo' => SharedFoldersController::statusInfo($this->resource),
// Entitlements/Wallet information
- $this->merge(self::objectEntitlements($this->resource)),
+ $this->merge($this->objectEntitlements()),
];
}
}
diff --git a/src/app/Http/Resources/SharedFolderResource.php b/src/app/Http/Resources/SharedFolderResource.php
--- a/src/app/Http/Resources/SharedFolderResource.php
+++ b/src/app/Http/Resources/SharedFolderResource.php
@@ -30,6 +30,19 @@
// Folder type
'type' => $this->resource->type,
+ $this->mergeWhen(self::isAdmin(), [
+ /*
+ * @var string Folder creation date-time
+ * @format date-time
+ */
+ 'created_at' => (string) $this->resource->created_at,
+ /*
+ * @var string Folder deletion date-time
+ * @format date-time
+ */
+ 'deleted_at' => (string) $this->resource->deleted_at,
+ ]),
+
// @var bool Is folder active?
'isActive' => $state['isActive'] ?? false,
// @var bool Is folder deleted?
diff --git a/src/app/Http/Resources/SkuResource.php b/src/app/Http/Resources/SkuResource.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Resources/SkuResource.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace App\Http\Resources;
+
+use App\Sku;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\Auth;
+
+/**
+ * SKU response
+ *
+ * @mixin Sku
+ */
+class SkuResource extends ApiResource
+{
+ public bool $controllerOnly = false;
+ public ?string $type = null;
+ public int $prio = 0;
+ public array $metadata = [];
+
+ /**
+ * Create a new resource instance.
+ *
+ * @param mixed $resource
+ */
+ public function __construct($resource)
+ {
+ parent::__construct($resource);
+
+ $this->metadata = $resource->handler_class::metadata($resource);
+ $this->controllerOnly = !empty($this->metadata['controllerOnly']);
+ $this->prio = $this->metadata['prio'];
+ }
+
+ /**
+ * Transform the resource into an array.
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ // @var string SKU identifier
+ 'id' => $this->resource->id,
+ // @var string SKU identifier
+ 'title' => $this->resource->title,
+ // @var string SKU name
+ 'name' => $this->resource->name,
+ // @var string SKU description
+ 'description' => $this->resource->description,
+ // @var int SKU (monthly) cost (in cents)
+ 'cost' => $this->resource->cost,
+ // @var bool SKU is active
+ 'active' => $this->resource->active,
+ // @var string SKU period (monthly, yearly)
+ 'period' => $this->resource->period,
+ // @var int Number of free units
+ 'units_free' => $this->resource->units_free,
+
+ // @var string SKU type
+ 'type' => $this->metadata['type'],
+ // @var string SKU handler class
+ 'handler' => $this->metadata['handler'],
+ // @var bool Is the SKU readonly?
+ 'readonly' => $this->metadata['readonly'] ?? false,
+ // @var bool Is the SKU enabled?
+ 'enabled' => $this->metadata['enabled'] ?? false,
+ // @var int SKU priority (for list order)
+ 'prio' => $this->metadata['prio'],
+ // @var array<string> Forbidden SKUs (by handler name)
+ 'forbidden' => $this->metadata['forbidden'] ?? [],
+ // @var array<string> Exclusive SKUs (by handler name)
+ 'exclusive' => $this->metadata['exclusive'] ?? [],
+ // @var array<string> Required SKUs (by handler name)
+ 'required' => $this->metadata['required'] ?? [],
+ // @var array<string, mixed> SKU value range
+ 'range' => $this->when(isset($this->metadata['range']), $this->metadata['range'] ?? []),
+
+ // Cost for a new object of the specified type
+ 'nextCost' => $this->when(!empty($this->type), fn () => $this->nextCost()),
+ ];
+ }
+
+ /**
+ * Calculate SKU cost for a new object of the specified type
+ */
+ private function nextCost(): int
+ {
+ $wallet = Auth::guard()->user()->wallet();
+ $nextCost = $this->resource->cost;
+
+ if ($wallet && $this->resource->cost && $this->resource->units_free) {
+ $count = $wallet->entitlements()->where('sku_id', $this->resource->id)->count();
+ if ($count < $this->resource->units_free) {
+ $nextCost = 0;
+ }
+ }
+
+ return $nextCost;
+ }
+}
diff --git a/src/app/Http/Resources/TransactionResource.php b/src/app/Http/Resources/TransactionResource.php
new file mode 100644
--- /dev/null
+++ b/src/app/Http/Resources/TransactionResource.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace App\Http\Resources;
+
+use App\Transaction;
+use App\Wallet;
+use Illuminate\Http\Request;
+
+/**
+ * Transaction response
+ *
+ * @mixin Transaction
+ */
+class TransactionResource extends ApiResource
+{
+ public ?Wallet $wallet;
+
+ /**
+ * Transform the resource into an array.
+ */
+ public function toArray(Request $request): array
+ {
+ return [
+ // Transaction identifier
+ 'id' => $this->resource->id,
+ // Transaction date (Y-m-d H:i)
+ 'createdAt' => $this->resource->created_at->format('Y-m-d H:i'),
+ // Transaction type
+ 'type' => $this->resource->type,
+ // Transaction description
+ 'description' => $this->resource->shortDescription(),
+ // @var int Transaction amount (in cents)
+ 'amount' => $this->resource->amount,
+ // Transaction currency
+ 'currency' => $this->wallet->currency,
+
+ // @var bool Indicates that the entry has sub-transactions
+ 'hasDetails' => !empty($this->resource->cnt),
+
+ // Acting user email address
+ 'user' => $this->when(self::isAdmin(), $this->resource->user_email),
+ ];
+ }
+}
diff --git a/src/app/Http/Resources/UserInfoResource.php b/src/app/Http/Resources/UserInfoResource.php
--- a/src/app/Http/Resources/UserInfoResource.php
+++ b/src/app/Http/Resources/UserInfoResource.php
@@ -56,7 +56,7 @@
'statusInfo' => UsersController::statusInfo($this->resource),
// Entitlements/Wallet information
- $this->merge(self::objectEntitlements($this->resource)),
+ $this->merge($this->objectEntitlements()),
];
}
}
diff --git a/src/app/Http/Resources/UserResource.php b/src/app/Http/Resources/UserResource.php
--- a/src/app/Http/Resources/UserResource.php
+++ b/src/app/Http/Resources/UserResource.php
@@ -29,9 +29,15 @@
'status' => $this->resource->status,
$this->mergeWhen(self::isAdmin(), [
- // User creation date-time
+ /*
+ * @var string User creation date-time
+ * @format date-time
+ */
'created_at' => (string) $this->resource->created_at,
- // User deletion date-time
+ /*
+ * @var string User deletion date-time
+ * @format date-time
+ */
'deleted_at' => (string) $this->resource->deleted_at,
]),
diff --git a/src/app/Http/Resources/WalletMandateResource.php b/src/app/Http/Resources/WalletMandateResource.php
--- a/src/app/Http/Resources/WalletMandateResource.php
+++ b/src/app/Http/Resources/WalletMandateResource.php
@@ -29,10 +29,10 @@
'isPending' => $this->resource['isPending'] ?? false,
// @var bool Is the mandate existing and valid?
'isValid' => $this->resource['isValid'] ?? false,
- // @var string|null Payment method name
+ // @var string|null Payment method description
'method' => $this->resource['method'] ?? null,
// @var string|null Payment method identifier
- 'methodId' => $this->resource['method'] ?? null,
+ 'methodId' => $this->resource['methodId'] ?? null,
];
}
}
diff --git a/src/app/Meet/Room.php b/src/app/Meet/Room.php
--- a/src/app/Meet/Room.php
+++ b/src/app/Meet/Room.php
@@ -97,11 +97,11 @@
*
* @param int $role User role (see self::ROLE_* constants)
*
- * @return array|null Token data on success, NULL otherwise
+ * @return string|null Session token on success, NULL otherwise
*
* @throws \Exception if session does not exist
*/
- public function getSessionToken($role = self::ROLE_SUBSCRIBER): ?array
+ public function getSessionToken($role = self::ROLE_SUBSCRIBER): ?string
{
if (!$this->session_id) {
throw new \Exception("The room session does not exist");
@@ -115,10 +115,7 @@
$response = $this->client()->post($url, $post);
if ($response->status() == 200) {
- return [
- 'token' => $response->json('token'),
- 'role' => $role,
- ];
+ return $response->json('token');
}
$this->logError("Failed to create the meet peer connection", $response);
diff --git a/src/app/Providers/PaymentProvider.php b/src/app/Providers/PaymentProvider.php
--- a/src/app/Providers/PaymentProvider.php
+++ b/src/app/Providers/PaymentProvider.php
@@ -251,7 +251,7 @@
*
* @param string $type the payment type for which we require a method
*
- * @return array Array of methods
+ * @return array<array> Array of methods
*/
private static function applyMethodWhitelist($type, $availableMethods): array
{
@@ -274,15 +274,15 @@
* List supported payment methods for $wallet
*
* @param Wallet $wallet The wallet
- * @param string $type the payment type for which we require a method (oneoff/recurring)
+ * @param string $type The payment type for which we require a method (oneoff/recurring)
*
- * @return array Array of array with available payment methods:
- * - id: id of the method
- * - name: User readable name of the payment method
- * - minimumAmount: Minimum amount to be charged in cents
- * - currency: Currency used for the method
- * - exchangeRate: The projected exchange rate (actual rate is determined during payment)
- * - icon: An icon (icon name) representing the method
+ * @return array<array> Array of array with available payment methods:
+ * - id: id of the method
+ * - name: User readable name of the payment method
+ * - minimumAmount: Minimum amount to be charged in cents
+ * - currency: Currency used for the method
+ * - exchangeRate: The projected exchange rate (actual rate is determined during payment)
+ * - icon: An icon (icon name) representing the method
*/
public static function paymentMethods(Wallet $wallet, $type): array
{
diff --git a/src/app/Traits/Meet/RoomConfigTrait.php b/src/app/Traits/Meet/RoomConfigTrait.php
--- a/src/app/Traits/Meet/RoomConfigTrait.php
+++ b/src/app/Traits/Meet/RoomConfigTrait.php
@@ -43,7 +43,7 @@
} elseif ($key == 'locked' || $key == 'nomedia') {
$this->setSetting($key, $value ? 'true' : null);
} elseif ($key == 'acl') {
- if (!empty($value) && !$this->hasSKU('group-room')) {
+ if (!empty($value) && !$this->hasSku('group-room')) {
$errors[$key] = \trans('validation.invalid-config-parameter');
continue;
}
diff --git a/src/package-lock.json b/src/package-lock.json
--- a/src/package-lock.json
+++ b/src/package-lock.json
@@ -2255,6 +2255,29 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@lukeed/csprng": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz",
+ "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@lukeed/uuid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@lukeed/uuid/-/uuid-2.0.1.tgz",
+ "integrity": "sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@lukeed/csprng": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
"version": "5.1.1-v1",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
@@ -2468,6 +2491,14 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/events-alias": {
+ "name": "@types/events",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
+ "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
@@ -2611,17 +2642,6 @@
"undici-types": "~5.26.4"
}
},
- "node_modules/@types/node-fetch": {
- "version": "2.6.12",
- "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz",
- "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "form-data": "^4.0.0"
- }
- },
"node_modules/@types/node-forge": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz",
@@ -2632,14 +2652,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/npm-events-package": {
- "name": "@types/events",
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.3.tgz",
- "integrity": "sha512-trOc4AAUThEz9hapPtSd7wf5tiQKvTtu5b371UxXdTuqzIh0ArcRspRP0i0Viu+LXstIQ1z96t1nsPxT9ol01g==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/parse-json": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
@@ -3329,16 +3341,16 @@
}
},
"node_modules/awaitqueue": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.2.0.tgz",
- "integrity": "sha512-yFka1VP9+ucU7n3wGerwZ9OzrdxluWl6qngiOlSe+nQyn6PtrR2BQt6HEoShI6sjo1sXAYQHQlUtPXzXSYAJfA==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/awaitqueue/-/awaitqueue-3.3.0.tgz",
+ "integrity": "sha512-zLxDhzQbzHmOyvxi7g3OlfR7jLrcmElStPxfLPpJkrFSDm71RSrY/MvsDA8Btlx8X1nOHUzGhQvc6bdUjL2f2w==",
"dev": true,
"license": "ISC",
"dependencies": {
- "debug": "^4.4.0"
+ "debug": "^4.4.3"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"type": "opencollective",
@@ -3346,9 +3358,9 @@
}
},
"node_modules/awaitqueue/node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4805,27 +4817,6 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
- "node_modules/detect-europe-js": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/detect-europe-js/-/detect-europe-js-0.1.2.tgz",
- "integrity": "sha512-lgdERlL3u0aUdHocoouzT10d9I89VVhk0qNRmll7mXdGfJT1/wqZ2ZLA4oJAjeACPY5fT1wsbq2AT+GkuInsow==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/ua-parser-js"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/faisalman"
- }
- ],
- "license": "MIT"
- },
"node_modules/detect-node": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
@@ -5624,6 +5615,17 @@
"node": ">=0.8.x"
}
},
+ "node_modules/events-alias": {
+ "name": "events",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
"node_modules/evp_bytestokey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
@@ -5724,16 +5726,16 @@
"license": "MIT"
},
"node_modules/fake-mediastreamtrack": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-2.1.0.tgz",
- "integrity": "sha512-A/xmNJtQxtwTQEZqYGE1SDbcA2rIm93TJtGnoaLqG+iL6JB7HdfoTzDZCSmmg3TvqE/xDbkcN0Mswv1rYjF0jw==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/fake-mediastreamtrack/-/fake-mediastreamtrack-2.2.1.tgz",
+ "integrity": "sha512-SITLc7UTDirSdgLGORrlmqjWLJtbtfIz/xO7DwVbJH3f0ds+NQok4ccl/WEzz49NSgiUlXf2wDW0+y5C+TO6EA==",
"dev": true,
"license": "ISC",
"dependencies": {
- "uuid": "^11.1.0"
+ "@lukeed/uuid": "^2.0.1"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
}
},
"node_modules/fast-deep-equal": {
@@ -6349,16 +6351,16 @@
"license": "MIT"
},
"node_modules/h264-profile-level-id": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.2.0.tgz",
- "integrity": "sha512-CwCzxvUWuOvJ0zI8Ltysq+7BBQ9kCoP2FnfYg5ZLLY3pZ2YfH+o6IqP9Eu8XHWqJM2JlmHMHfAApiQ5IDWSRQA==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/h264-profile-level-id/-/h264-profile-level-id-2.3.1.tgz",
+ "integrity": "sha512-x2P85PdoRsjCQE7sZjmQQDEf61vTTxOSPNFyqevXaiiq6DzvLVnd0VBq/C7wplrYEqTXvMW5CLIDUMBekNN4Vw==",
"dev": true,
"license": "ISC",
"dependencies": {
- "debug": "^4.4.0"
+ "debug": "^4.4.3"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"type": "opencollective",
@@ -6366,9 +6368,9 @@
}
},
"node_modules/h264-profile-level-id/node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7114,27 +7116,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-standalone-pwa": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-standalone-pwa/-/is-standalone-pwa-0.1.1.tgz",
- "integrity": "sha512-9Cbovsa52vNQCjdXOzeQq5CnCbAcRk05aU62K20WO372NrTv0NxibLFCK6lQ4/iZEFdEA3p3t2VNOn8AJ53F5g==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/ua-parser-js"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/faisalman"
- }
- ],
- "license": "MIT"
- },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -7803,25 +7784,24 @@
}
},
"node_modules/mediasoup-client": {
- "version": "3.11.0",
- "resolved": "https://registry.npmjs.org/mediasoup-client/-/mediasoup-client-3.11.0.tgz",
- "integrity": "sha512-MVKngePWhZ6AjHbHGvIf67XSJa3fEP5cvz2bCflfbY+6cGaM51oJIuYj73C4S3y+QRsrHJhPHYRz8swnovTH5g==",
+ "version": "3.16.7",
+ "resolved": "https://registry.npmjs.org/mediasoup-client/-/mediasoup-client-3.16.7.tgz",
+ "integrity": "sha512-MvuflyRMtdcR/+7Km18o4FdKfVb4GF5zdDFoIDx9dRtCH+K5jAU1n7LAs/Pl7ejIVC1BEWIalIH5Q31JdomtBQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"@types/debug": "^4.1.12",
- "@types/npm-events-package": "npm:@types/events@^3.0.3",
- "awaitqueue": "^3.2.0",
- "debug": "^4.4.1",
- "fake-mediastreamtrack": "^2.1.0",
- "h264-profile-level-id": "^2.2.0",
- "npm-events-package": "npm:events@^3.3.0",
+ "@types/events-alias": "npm:@types/events@^3.0.3",
+ "awaitqueue": "^3.3.0",
+ "debug": "^4.4.3",
+ "events-alias": "npm:events@^3.3.0",
+ "fake-mediastreamtrack": "^2.2.1",
+ "h264-profile-level-id": "^2.3.1",
"sdp-transform": "^2.15.0",
- "supports-color": "^10.0.0",
- "ua-parser-js": "^2.0.3"
+ "supports-color": "^10.2.2"
},
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"funding": {
"type": "opencollective",
@@ -7829,9 +7809,9 @@
}
},
"node_modules/mediasoup-client/node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7854,9 +7834,9 @@
"license": "MIT"
},
"node_modules/mediasoup-client/node_modules/supports-color": {
- "version": "10.0.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.0.0.tgz",
- "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
"dev": true,
"license": "MIT",
"engines": {
@@ -8173,27 +8153,6 @@
"tslib": "^2.0.3"
}
},
- "node_modules/node-fetch": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
- "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "whatwg-url": "^5.0.0"
- },
- "engines": {
- "node": "4.x || >=6.0.0"
- },
- "peerDependencies": {
- "encoding": "^0.1.0"
- },
- "peerDependenciesMeta": {
- "encoding": {
- "optional": true
- }
- }
- },
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@@ -8314,17 +8273,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npm-events-package": {
- "name": "events",
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
- "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.x"
- }
- },
"node_modules/npm-run-path": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
@@ -11427,13 +11375,6 @@
"node": ">=0.6"
}
},
- "node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@@ -11488,61 +11429,6 @@
"node": ">= 0.6"
}
},
- "node_modules/ua-is-frozen": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/ua-is-frozen/-/ua-is-frozen-0.1.2.tgz",
- "integrity": "sha512-RwKDW2p3iyWn4UbaxpP2+VxwqXh0jpvdxsYpZ5j/MLLiQOfbsV5shpgQiw93+KMYQPcteeMQ289MaAFzs3G9pw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
- },
- {
- "type": "opencollective",
- "url": "https://opencollective.com/ua-parser-js"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/faisalman"
- }
- ],
- "license": "MIT"
- },
- "node_modules/ua-parser-js": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-2.0.3.tgz",
- "integrity": "sha512-LZyXZdNttONW8LjzEH3Z8+6TE7RfrEiJqDKyh0R11p/kxvrV2o9DrT2FGZO+KVNs3k+drcIQ6C3En6wLnzJGpw==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/ua-parser-js"
- },
- {
- "type": "paypal",
- "url": "https://paypal.me/faisalman"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/faisalman"
- }
- ],
- "license": "AGPL-3.0-or-later",
- "dependencies": {
- "@types/node-fetch": "^2.6.12",
- "detect-europe-js": "^0.1.2",
- "is-standalone-pwa": "^0.1.1",
- "node-fetch": "^2.7.0",
- "ua-is-frozen": "^0.1.2"
- },
- "bin": {
- "ua-parser-js": "script/cli.js"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
@@ -11710,20 +11596,6 @@
"node": ">= 0.4.0"
}
},
- "node_modules/uuid": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
- "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
- "dev": true,
- "funding": [
- "https://github.com/sponsors/broofa",
- "https://github.com/sponsors/ctavan"
- ],
- "license": "MIT",
- "bin": {
- "uuid": "dist/esm/bin/uuid"
- }
- },
"node_modules/v8-compile-cache": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz",
@@ -11969,13 +11841,6 @@
"minimalistic-assert": "^1.0.0"
}
},
- "node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
"node_modules/webpack": {
"version": "5.99.9",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz",
@@ -12515,17 +12380,6 @@
"node": ">=0.8.0"
}
},
- "node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- }
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
diff --git a/src/resources/vue/Admin/User.vue b/src/resources/vue/Admin/User.vue
--- a/src/resources/vue/Admin/User.vue
+++ b/src/resources/vue/Admin/User.vue
@@ -449,7 +449,7 @@
axios.get('/api/v4/users/' + user_id + '/skus')
.then(response => {
// "merge" SKUs with user entitlement-SKUs
- response.data.forEach(sku => {
+ response.data.list.forEach(sku => {
const userSku = this.user.skus[sku.id]
if (userSku) {
let cost = userSku.costs.reduce((sum, current) => sum + current)
diff --git a/src/resources/vue/Widgets/SubscriptionSelect.vue b/src/resources/vue/Widgets/SubscriptionSelect.vue
--- a/src/resources/vue/Widgets/SubscriptionSelect.vue
+++ b/src/resources/vue/Widgets/SubscriptionSelect.vue
@@ -81,11 +81,11 @@
axios.get(url, { loader: true })
.then(response => {
if (this.readonly && this.object.skus) {
- response.data = response.data.filter(sku => { return sku.id in this.object.skus })
+ response.data.list = response.data.list.filter(sku => { return sku.id in this.object.skus })
}
// "merge" SKUs with user entitlement-SKUs
- this.skus = response.data
+ this.skus = response.data.list
.map(sku => {
const objSku = this.object.skus ? this.object.skus[sku.id] : null
if (objSku) {
@@ -113,7 +113,7 @@
// Mark 'exclusive' SKUs as readonly, they can't be unchecked
this.skus.forEach(item => {
- if (item.exclusive && item.enabled) {
+ if (item.exclusive.length && item.enabled) {
$('#s' + item.id).find('input[type=checkbox]')[0].readOnly = true
}
})
@@ -148,7 +148,7 @@
if (input.checked) {
// Check if a required SKU is selected, alert the user if not
- (sku.required || []).forEach(handler => {
+ sku.required.forEach(handler => {
this.skus.forEach(item => {
if (item.handler == handler) {
if (!$('#s' + item.id).find('input[type=checkbox]:checked').length) {
@@ -164,7 +164,7 @@
}
// Make sure there must be only one of 'exclusive' SKUs
- if (sku.exclusive) {
+ if (sku.exclusive.length) {
input.readOnly = true
this.skus.forEach(item => {
@@ -187,7 +187,7 @@
}
// Uncheck+lock/unlock conflicting SKUs
- (sku.forbidden || []).forEach(handler => {
+ sku.forbidden.forEach(handler => {
this.skus.forEach(item => {
let checkbox
if (item.handler == handler && (checkbox = $('#s' + item.id).find('input[type=checkbox]')[0])) {
diff --git a/src/tests/Browser/Pages/Meet/Room.php b/src/tests/Browser/Pages/Meet/Room.php
--- a/src/tests/Browser/Pages/Meet/Room.php
+++ b/src/tests/Browser/Pages/Meet/Room.php
@@ -89,8 +89,8 @@
'@chat-list' => '#meet-chat .chat',
'@login-form' => '#meet-auth',
- '@login-email-input' => '#inputEmail',
- '@login-password-input' => '#inputPassword',
+ '@login-email-input' => '#email',
+ '@login-password-input' => '#password',
'@login-second-factor-input' => '#secondfactor',
'@login-button' => '#meet-auth button',
];
diff --git a/src/tests/Browser/PasswordResetTest.php b/src/tests/Browser/PasswordResetTest.php
--- a/src/tests/Browser/PasswordResetTest.php
+++ b/src/tests/Browser/PasswordResetTest.php
@@ -235,7 +235,8 @@
// Submit invalid data
$browser->with('@step3', static function ($step) use ($browser) {
- $step->assertFocused('#reset_password')
+ $step->waitFor('#reset_password')
+ ->assertFocused('#reset_password')
->whenAvailable('#reset_password_policy', static function (Browser $browser) {
$browser->assertElementsCount('li', 2)
->assertMissing('li:first-child svg.text-success')
diff --git a/src/tests/Browser/Reseller/UserTest.php b/src/tests/Browser/Reseller/UserTest.php
--- a/src/tests/Browser/Reseller/UserTest.php
+++ b/src/tests/Browser/Reseller/UserTest.php
@@ -28,6 +28,7 @@
$john->setSettings([
'phone' => '+48123123123',
'external_email' => 'john.doe.external@gmail.com',
+ 'greylist_enabled' => null,
'greylist_policy' => null,
]);
if ($john->isSuspended()) {
@@ -47,6 +48,8 @@
$john->setSettings([
'phone' => null,
'external_email' => 'john.doe.external@gmail.com',
+ 'greylist_enabled' => null,
+ 'greylist_policy' => null,
]);
if ($john->isSuspended()) {
User::where('email', $john->email)->update(['status' => $john->status - User::STATUS_SUSPENDED]);
@@ -189,7 +192,7 @@
->whenAvailable('@user-settings form', static function (Browser $browser) {
$browser->assertElementsCount('.row', 3)
->assertSeeIn('.row:first-child label', 'Greylisting')
- ->assertSeeIn('.row:first-child .text-success', 'enabled')
+ ->assertSeeIn('.row:first-child .text-danger', 'disabled')
->assertSeeIn('.row:nth-child(2) label', 'IMAP proxy')
->assertSeeIn('.row:nth-child(2) .text-danger', 'disabled')
->assertSeeIn('.row:nth-child(3) label', 'Geo-lockin')
diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php
--- a/src/tests/Browser/UsersTest.php
+++ b/src/tests/Browser/UsersTest.php
@@ -224,7 +224,7 @@
$browser->assertQuotaValue(5)->setQuotaValue(6);
})
->assertSeeIn('tr:nth-child(2) td.price', '0,25 CHF/month')
- // groupware SKU
+ // Groupware SKU
->assertSeeIn('tbody tr:nth-child(3) td.name', 'Groupware Features')
->assertSeeIn('tbody tr:nth-child(3) td.price', '4,90 CHF/month')
->assertChecked('tbody tr:nth-child(3) td.selection input')
diff --git a/src/tests/Browser/WalletTest.php b/src/tests/Browser/WalletTest.php
--- a/src/tests/Browser/WalletTest.php
+++ b/src/tests/Browser/WalletTest.php
@@ -218,11 +218,10 @@
$browser->refresh()
->on(new WalletPage())
->click('@nav #tab-refprograms')
- ->whenAvailable('@refprograms-tab', static function (Browser $browser) use ($program, $user) {
+ ->whenAvailable('@refprograms-tab ul', static function (Browser $browser) use ($program, $user) {
$code = $program->codes()->where('user_id', $user->id)->first();
- $browser->waitFor('ul')
- ->assertElementsCount('ul > li', 1)
+ $browser->assertElementsCount('li', 1)
->assertVisible('li:nth-child(1) img')
->assertSeeIn('li:nth-child(1) p.name', $program->name)
->assertSeeIn('li:nth-child(1) p.description', $program->description)
diff --git a/src/tests/Feature/Controller/Admin/SkusTest.php b/src/tests/Feature/Controller/Admin/SkusTest.php
--- a/src/tests/Feature/Controller/Admin/SkusTest.php
+++ b/src/tests/Feature/Controller/Admin/SkusTest.php
@@ -48,7 +48,7 @@
$json = $response->json();
- $this->assertCount(1, $json);
+ $this->assertCount(1, $json['list']);
// Note: Details are tested where we test API\V4\SkusController
}
@@ -74,18 +74,18 @@
$json = $response->json();
- $this->assertCount(12, $json);
- $this->assertSame(100, $json[0]['prio']);
- $this->assertSame($sku->id, $json[0]['id']);
- $this->assertSame($sku->title, $json[0]['title']);
- $this->assertSame($sku->name, $json[0]['name']);
- $this->assertSame($sku->description, $json[0]['description']);
- $this->assertSame($sku->cost, $json[0]['cost']);
- $this->assertSame($sku->units_free, $json[0]['units_free']);
- $this->assertSame($sku->period, $json[0]['period']);
- $this->assertSame($sku->active, $json[0]['active']);
- $this->assertSame('user', $json[0]['type']);
- $this->assertSame('Mailbox', $json[0]['handler']);
+ $this->assertCount(12, $json['list']);
+ $this->assertSame(100, $json['list'][0]['prio']);
+ $this->assertSame($sku->id, $json['list'][0]['id']);
+ $this->assertSame($sku->title, $json['list'][0]['title']);
+ $this->assertSame($sku->name, $json['list'][0]['name']);
+ $this->assertSame($sku->description, $json['list'][0]['description']);
+ $this->assertSame($sku->cost, $json['list'][0]['cost']);
+ $this->assertSame($sku->units_free, $json['list'][0]['units_free']);
+ $this->assertSame($sku->period, $json['list'][0]['period']);
+ $this->assertSame($sku->active, $json['list'][0]['active']);
+ $this->assertSame('user', $json['list'][0]['type']);
+ $this->assertSame('Mailbox', $json['list'][0]['handler']);
}
/**
@@ -109,7 +109,7 @@
$json = $response->json();
- $this->assertCount(5, $json);
+ $this->assertCount(5, $json['list']);
// Note: Details are tested where we test API\V4\SkusController
}
}
diff --git a/src/tests/Feature/Controller/DomainsTest.php b/src/tests/Feature/Controller/DomainsTest.php
--- a/src/tests/Feature/Controller/DomainsTest.php
+++ b/src/tests/Feature/Controller/DomainsTest.php
@@ -414,8 +414,8 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSkuElement('domain-hosting', $json[0], [
+ $this->assertCount(1, $json['list']);
+ $this->assertSkuElement('domain-hosting', $json['list'][0], [
'prio' => 0,
'type' => 'domain',
'handler' => 'DomainHosting',
diff --git a/src/tests/Feature/Controller/GroupsTest.php b/src/tests/Feature/Controller/GroupsTest.php
--- a/src/tests/Feature/Controller/GroupsTest.php
+++ b/src/tests/Feature/Controller/GroupsTest.php
@@ -283,8 +283,8 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSkuElement('group', $json[0], [
+ $this->assertCount(1, $json['list']);
+ $this->assertSkuElement('group', $json['list'][0], [
'prio' => 0,
'type' => 'group',
'handler' => 'Group',
diff --git a/src/tests/Feature/Controller/MeetTest.php b/src/tests/Feature/Controller/MeetTest.php
--- a/src/tests/Feature/Controller/MeetTest.php
+++ b/src/tests/Feature/Controller/MeetTest.php
@@ -117,7 +117,6 @@
$json = $response->json();
- $this->assertCount(4, $json);
$this->assertSame(325, $json['code']);
$this->assertSame('error', $json['status']);
$this->assertSame('Failed to join the session. Invalid password.', $json['message']);
@@ -195,7 +194,6 @@
$json = $response->json();
- $this->assertCount(4, $json);
$this->assertSame(326, $json['code']);
$this->assertSame('error', $json['status']);
$this->assertSame('Failed to join the session. Room locked.', $json['message']);
diff --git a/src/tests/Feature/Controller/Reseller/SkusTest.php b/src/tests/Feature/Controller/Reseller/SkusTest.php
--- a/src/tests/Feature/Controller/Reseller/SkusTest.php
+++ b/src/tests/Feature/Controller/Reseller/SkusTest.php
@@ -59,7 +59,7 @@
$json = $response->json();
- $this->assertCount(1, $json);
+ $this->assertCount(1, $json['list']);
// Note: Details are tested where we test API\V4\SkusController
}
@@ -91,19 +91,19 @@
$json = $response->json();
- $this->assertCount(12, $json);
+ $this->assertCount(12, $json['list']);
- $this->assertSame(100, $json[0]['prio']);
- $this->assertSame($sku->id, $json[0]['id']);
- $this->assertSame($sku->title, $json[0]['title']);
- $this->assertSame($sku->name, $json[0]['name']);
- $this->assertSame($sku->description, $json[0]['description']);
- $this->assertSame($sku->cost, $json[0]['cost']);
- $this->assertSame($sku->units_free, $json[0]['units_free']);
- $this->assertSame($sku->period, $json[0]['period']);
- $this->assertSame($sku->active, $json[0]['active']);
- $this->assertSame('user', $json[0]['type']);
- $this->assertSame('Mailbox', $json[0]['handler']);
+ $this->assertSame(100, $json['list'][0]['prio']);
+ $this->assertSame($sku->id, $json['list'][0]['id']);
+ $this->assertSame($sku->title, $json['list'][0]['title']);
+ $this->assertSame($sku->name, $json['list'][0]['name']);
+ $this->assertSame($sku->description, $json['list'][0]['description']);
+ $this->assertSame($sku->cost, $json['list'][0]['cost']);
+ $this->assertSame($sku->units_free, $json['list'][0]['units_free']);
+ $this->assertSame($sku->period, $json['list'][0]['period']);
+ $this->assertSame($sku->active, $json['list'][0]['active']);
+ $this->assertSame('user', $json['list'][0]['type']);
+ $this->assertSame('Mailbox', $json['list'][0]['handler']);
// Test with another tenant
$sku = Sku::where('title', 'mailbox')->where('tenant_id', $reseller2->tenant_id)->first();
@@ -112,19 +112,19 @@
$json = $response->json();
- $this->assertCount(6, $json);
-
- $this->assertSame(100, $json[0]['prio']);
- $this->assertSame($sku->id, $json[0]['id']);
- $this->assertSame($sku->title, $json[0]['title']);
- $this->assertSame($sku->name, $json[0]['name']);
- $this->assertSame($sku->description, $json[0]['description']);
- $this->assertSame($sku->cost, $json[0]['cost']);
- $this->assertSame($sku->units_free, $json[0]['units_free']);
- $this->assertSame($sku->period, $json[0]['period']);
- $this->assertSame($sku->active, $json[0]['active']);
- $this->assertSame('user', $json[0]['type']);
- $this->assertSame('Mailbox', $json[0]['handler']);
+ $this->assertCount(6, $json['list']);
+
+ $this->assertSame(100, $json['list'][0]['prio']);
+ $this->assertSame($sku->id, $json['list'][0]['id']);
+ $this->assertSame($sku->title, $json['list'][0]['title']);
+ $this->assertSame($sku->name, $json['list'][0]['name']);
+ $this->assertSame($sku->description, $json['list'][0]['description']);
+ $this->assertSame($sku->cost, $json['list'][0]['cost']);
+ $this->assertSame($sku->units_free, $json['list'][0]['units_free']);
+ $this->assertSame($sku->period, $json['list'][0]['period']);
+ $this->assertSame($sku->active, $json['list'][0]['active']);
+ $this->assertSame('user', $json['list'][0]['type']);
+ $this->assertSame('Mailbox', $json['list'][0]['handler']);
}
/**
@@ -159,7 +159,7 @@
$json = $response->json();
- $this->assertCount(5, $json);
+ $this->assertCount(5, $json['list']);
// Note: Details are tested where we test API\V4\SkusController
}
}
diff --git a/src/tests/Feature/Controller/ResourcesTest.php b/src/tests/Feature/Controller/ResourcesTest.php
--- a/src/tests/Feature/Controller/ResourcesTest.php
+++ b/src/tests/Feature/Controller/ResourcesTest.php
@@ -277,8 +277,8 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSkuElement('resource', $json[0], [
+ $this->assertCount(1, $json['list']);
+ $this->assertSkuElement('resource', $json['list'][0], [
'prio' => 0,
'type' => 'resource',
'handler' => 'Resource',
diff --git a/src/tests/Feature/Controller/RoomsTest.php b/src/tests/Feature/Controller/RoomsTest.php
--- a/src/tests/Feature/Controller/RoomsTest.php
+++ b/src/tests/Feature/Controller/RoomsTest.php
@@ -321,11 +321,11 @@
$json = $response->json();
- $this->assertCount(2, $json);
- $this->assertSame('room', $json[0]['title']);
- $this->assertTrue($json[0]['enabled']);
- $this->assertSame('group-room', $json[1]['title']);
- $this->assertFalse($json[1]['enabled']);
+ $this->assertCount(2, $json['list']);
+ $this->assertSame('room', $json['list'][0]['title']);
+ $this->assertTrue($json['list'][0]['enabled']);
+ $this->assertSame('group-room', $json['list'][1]['title']);
+ $this->assertFalse($json['list'][1]['enabled']);
// Room's wallet controller, not owner
$response = $this->actingAs($ned)->get("api/v4/rooms/{$room->id}/skus");
@@ -333,11 +333,11 @@
$json = $response->json();
- $this->assertCount(2, $json);
- $this->assertSame('room', $json[0]['title']);
- $this->assertTrue($json[0]['enabled']);
- $this->assertSame('group-room', $json[1]['title']);
- $this->assertFalse($json[1]['enabled']);
+ $this->assertCount(2, $json['list']);
+ $this->assertSame('room', $json['list'][0]['title']);
+ $this->assertTrue($json['list'][0]['enabled']);
+ $this->assertSame('group-room', $json['list'][1]['title']);
+ $this->assertFalse($json['list'][1]['enabled']);
// Test non-controller user, expect no group-room SKU on the list
$room = $this->getTestRoom('test', $jack->wallets()->first());
@@ -347,9 +347,9 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSame('room', $json[0]['title']);
- $this->assertTrue($json[0]['enabled']);
+ $this->assertCount(1, $json['list']);
+ $this->assertSame('room', $json['list'][0]['title']);
+ $this->assertTrue($json['list'][0]['enabled']);
}
/**
diff --git a/src/tests/Feature/Controller/SharedFoldersTest.php b/src/tests/Feature/Controller/SharedFoldersTest.php
--- a/src/tests/Feature/Controller/SharedFoldersTest.php
+++ b/src/tests/Feature/Controller/SharedFoldersTest.php
@@ -281,8 +281,8 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSkuElement('shared-folder', $json[0], [
+ $this->assertCount(1, $json['list']);
+ $this->assertSkuElement('shared-folder', $json['list'][0], [
'prio' => 0,
'type' => 'sharedFolder',
'handler' => 'SharedFolder',
diff --git a/src/tests/Feature/Controller/SkusTest.php b/src/tests/Feature/Controller/SkusTest.php
--- a/src/tests/Feature/Controller/SkusTest.php
+++ b/src/tests/Feature/Controller/SkusTest.php
@@ -59,19 +59,19 @@
$json = $response->json();
- $this->assertCount(12, $json);
-
- $this->assertSame(100, $json[0]['prio']);
- $this->assertSame($sku->id, $json[0]['id']);
- $this->assertSame($sku->title, $json[0]['title']);
- $this->assertSame($sku->name, $json[0]['name']);
- $this->assertSame($sku->description, $json[0]['description']);
- $this->assertSame($sku->cost, $json[0]['cost']);
- $this->assertSame($sku->units_free, $json[0]['units_free']);
- $this->assertSame($sku->period, $json[0]['period']);
- $this->assertSame($sku->active, $json[0]['active']);
- $this->assertSame('user', $json[0]['type']);
- $this->assertSame('Mailbox', $json[0]['handler']);
+ $this->assertCount(12, $json['list']);
+
+ $this->assertSame(100, $json['list'][0]['prio']);
+ $this->assertSame($sku->id, $json['list'][0]['id']);
+ $this->assertSame($sku->title, $json['list'][0]['title']);
+ $this->assertSame($sku->name, $json['list'][0]['name']);
+ $this->assertSame($sku->description, $json['list'][0]['description']);
+ $this->assertSame($sku->cost, $json['list'][0]['cost']);
+ $this->assertSame($sku->units_free, $json['list'][0]['units_free']);
+ $this->assertSame($sku->period, $json['list'][0]['period']);
+ $this->assertSame($sku->active, $json['list'][0]['active']);
+ $this->assertSame('user', $json['list'][0]['type']);
+ $this->assertSame('Mailbox', $json['list'][0]['handler']);
// Test the type filter, and nextCost property (user with one domain)
$response = $this->actingAs($john)->get("api/v4/skus?type=domain");
@@ -79,9 +79,9 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSame('domain-hosting', $json[0]['title']);
- $this->assertSame(100, $json[0]['nextCost']); // second domain costs 100
+ $this->assertCount(1, $json['list']);
+ $this->assertSame('domain-hosting', $json['list'][0]['title']);
+ $this->assertSame(100, $json['list'][0]['nextCost']); // second domain costs 100
// Test the type filter, and nextCost property (user with no domain)
$jane = $this->getTestUser('jane@kolabnow.com');
@@ -92,9 +92,9 @@
$json = $response->json();
- $this->assertCount(1, $json);
- $this->assertSame('domain-hosting', $json[0]['title']);
- $this->assertSame(0, $json[0]['nextCost']); // first domain costs 0
+ $this->assertCount(1, $json['list']);
+ $this->assertSame('domain-hosting', $json['list'][0]['title']);
+ $this->assertSame(0, $json['list'][0]['nextCost']); // first domain costs 0
}
/**
diff --git a/src/tests/Feature/Controller/UsersTest.php b/src/tests/Feature/Controller/UsersTest.php
--- a/src/tests/Feature/Controller/UsersTest.php
+++ b/src/tests/Feature/Controller/UsersTest.php
@@ -464,9 +464,9 @@
$json = $response->json();
- $this->assertCount(5, $json);
+ $this->assertCount(5, $json['list']);
- $this->assertSkuElement('mailbox', $json[0], [
+ $this->assertSkuElement('mailbox', $json['list'][0], [
'prio' => 100,
'type' => 'user',
'handler' => 'Mailbox',
@@ -474,7 +474,7 @@
'readonly' => true,
]);
- $this->assertSkuElement('storage', $json[1], [
+ $this->assertSkuElement('storage', $json['list'][1], [
'prio' => 90,
'type' => 'user',
'handler' => 'Storage',
@@ -487,7 +487,7 @@
],
]);
- $this->assertSkuElement('groupware', $json[2], [
+ $this->assertSkuElement('groupware', $json['list'][2], [
'prio' => 80,
'type' => 'user',
'handler' => 'Groupware',
@@ -495,7 +495,7 @@
'readonly' => false,
]);
- $this->assertSkuElement('activesync', $json[3], [
+ $this->assertSkuElement('activesync', $json['list'][3], [
'prio' => 70,
'type' => 'user',
'handler' => 'Activesync',
@@ -504,7 +504,7 @@
'required' => ['Groupware'],
]);
- $this->assertSkuElement('2fa', $json[4], [
+ $this->assertSkuElement('2fa', $json['list'][4], [
'prio' => 60,
'type' => 'user',
'handler' => 'Auth2F',
@@ -521,9 +521,9 @@
$json = $response->json();
- $this->assertCount(6, $json);
+ $this->assertCount(6, $json['list']);
- $this->assertSkuElement('beta', $json[5], [
+ $this->assertSkuElement('beta', $json['list'][5], [
'prio' => 10,
'type' => 'user',
'handler' => 'Beta',
diff --git a/src/tests/TestCaseTrait.php b/src/tests/TestCaseTrait.php
--- a/src/tests/TestCaseTrait.php
+++ b/src/tests/TestCaseTrait.php
@@ -172,8 +172,6 @@
foreach ($other as $key => $value) {
$this->assertSame($value, $result[$key]);
}
-
- $this->assertCount(8 + count($other), $result);
}
/**
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 4:00 PM (21 h, 55 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18824611
Default Alt Text
D5658.1775232005.diff (94 KB)
Attached To
Mode
D5658: API documentation improvements
Attached
Detach File
Event Timeline