diff --git a/src/app/Handlers/DomainHosting.php b/src/app/Handlers/DomainHosting.php
--- a/src/app/Handlers/DomainHosting.php
+++ b/src/app/Handlers/DomainHosting.php
@@ -13,4 +13,21 @@
{
return \App\Domain::class;
}
+
+ /**
+ * SKU handler metadata.
+ *
+ * @param \App\Sku $sku The SKU object
+ *
+ * @return array
+ */
+ public static function metadata(\App\Sku $sku): array
+ {
+ $data = parent::metadata($sku);
+
+ $data['readonly'] = true;
+ $data['enabled'] = true;
+
+ return $data;
+ }
}
diff --git a/src/app/Handlers/Group.php b/src/app/Handlers/Group.php
--- a/src/app/Handlers/Group.php
+++ b/src/app/Handlers/Group.php
@@ -13,4 +13,21 @@
{
return \App\Group::class;
}
+
+ /**
+ * SKU handler metadata.
+ *
+ * @param \App\Sku $sku The SKU object
+ *
+ * @return array
+ */
+ public static function metadata(\App\Sku $sku): array
+ {
+ $data = parent::metadata($sku);
+
+ $data['readonly'] = true;
+ $data['enabled'] = true;
+
+ return $data;
+ }
}
diff --git a/src/app/Handlers/Resource.php b/src/app/Handlers/Resource.php
--- a/src/app/Handlers/Resource.php
+++ b/src/app/Handlers/Resource.php
@@ -13,4 +13,21 @@
{
return \App\Resource::class;
}
+
+ /**
+ * SKU handler metadata.
+ *
+ * @param \App\Sku $sku The SKU object
+ *
+ * @return array
+ */
+ public static function metadata(\App\Sku $sku): array
+ {
+ $data = parent::metadata($sku);
+
+ $data['readonly'] = true;
+ $data['enabled'] = true;
+
+ return $data;
+ }
}
diff --git a/src/app/Handlers/SharedFolder.php b/src/app/Handlers/SharedFolder.php
--- a/src/app/Handlers/SharedFolder.php
+++ b/src/app/Handlers/SharedFolder.php
@@ -13,4 +13,21 @@
{
return \App\SharedFolder::class;
}
+
+ /**
+ * SKU handler metadata.
+ *
+ * @param \App\Sku $sku The SKU object
+ *
+ * @return array
+ */
+ public static function metadata(\App\Sku $sku): array
+ {
+ $data = parent::metadata($sku);
+
+ $data['readonly'] = true;
+ $data['enabled'] = true;
+
+ return $data;
+ }
}
diff --git a/src/app/Http/Controllers/API/V4/DomainsController.php b/src/app/Http/Controllers/API/V4/DomainsController.php
--- a/src/app/Http/Controllers/API/V4/DomainsController.php
+++ b/src/app/Http/Controllers/API/V4/DomainsController.php
@@ -205,16 +205,8 @@
// Status info
$response['statusInfo'] = self::statusInfo($domain);
- // Entitlements info
- $response['skus'] = \App\Entitlement::objectEntitlementsSummary($domain);
-
- // Some basic information about the domain wallet
- $wallet = $domain->wallet();
- $response['wallet'] = $wallet->toArray();
- if ($wallet->discount) {
- $response['wallet']['discount'] = $wallet->discount->discount;
- $response['wallet']['discount_description'] = $wallet->discount->description;
- }
+ // Entitlements/Wallet info
+ SkusController::objectEntitlements($domain, $response);
return response()->json($response);
}
diff --git a/src/app/Http/Controllers/API/V4/GroupsController.php b/src/app/Http/Controllers/API/V4/GroupsController.php
--- a/src/app/Http/Controllers/API/V4/GroupsController.php
+++ b/src/app/Http/Controllers/API/V4/GroupsController.php
@@ -184,6 +184,8 @@
return response()->json(['status' => 'error', 'errors' => $errors], 422);
}
+ // SkusController::updateEntitlements($group, $request->skus);
+
$group->members = $members;
$group->save();
diff --git a/src/app/Http/Controllers/API/V4/ResourcesController.php b/src/app/Http/Controllers/API/V4/ResourcesController.php
--- a/src/app/Http/Controllers/API/V4/ResourcesController.php
+++ b/src/app/Http/Controllers/API/V4/ResourcesController.php
@@ -132,6 +132,8 @@
return response()->json(['status' => 'error', 'errors' => $errors], 422);
}
+ // SkusController::updateEntitlements($resource, $request->skus);
+
$resource->save();
return response()->json([
diff --git a/src/app/Http/Controllers/API/V4/SharedFoldersController.php b/src/app/Http/Controllers/API/V4/SharedFoldersController.php
--- a/src/app/Http/Controllers/API/V4/SharedFoldersController.php
+++ b/src/app/Http/Controllers/API/V4/SharedFoldersController.php
@@ -118,6 +118,8 @@
DB::beginTransaction();
+ // SkusController::updateEntitlements($folder, $request->skus);
+
if ($name && $name != $folder->name) {
$folder->name = $name;
}
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
@@ -9,72 +9,45 @@
class SkusController extends ResourceController
{
/**
- * Get a list of SKUs available to the domain.
- *
- * @param int $id Domain identifier
- *
- * @return \Illuminate\Http\JsonResponse
- */
- public function domainSkus($id)
- {
- $domain = \App\Domain::find($id);
-
- if (!$this->checkTenant($domain)) {
- return $this->errorResponse(404);
- }
-
- if (!$this->guard()->user()->canRead($domain)) {
- return $this->errorResponse(403);
- }
-
- return $this->objectSkus($domain);
- }
-
- /**
* Get a list of active SKUs.
*
* @return \Illuminate\Http\JsonResponse
*/
public function index()
{
- // Note: Order by title for consistent ordering in tests
- $skus = Sku::withSubjectTenantContext()->where('active', true)->orderBy('title')->get();
-
- $response = [];
-
- foreach ($skus as $sku) {
- if ($data = $this->skuElement($sku)) {
- $response[] = $data;
- }
- }
-
- usort($response, function ($a, $b) {
- return ($b['prio'] <=> $a['prio']);
- });
-
- return response()->json($response);
- }
-
- /**
- * Get a list of SKUs available to the user.
- *
- * @param int $id User identifier
- *
- * @return \Illuminate\Http\JsonResponse
- */
- public function userSkus($id)
- {
- $user = \App\User::find($id);
+ $type = request()->input('type');
- if (!$this->checkTenant($user)) {
- return $this->errorResponse(404);
- }
+ // Note: Order by title for consistent ordering in tests
+ $response = Sku::withSubjectTenantContext()->where('active', true)->orderBy('title')
+ ->get()
+ ->transform(function ($sku) {
+ return $this->skuElement($sku);
+ })
+ ->filter(function ($sku) use ($type) {
+ return !$type || $sku['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(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;
+ }
+ }
- if (!$this->guard()->user()->canRead($user)) {
- return $this->errorResponse(403);
+ return $sku;
+ });
}
- return $this->objectSkus($user);
+ return response()->json($response->all());
}
/**
@@ -84,9 +57,9 @@
*
* @return \Illuminate\Http\JsonResponse
*/
- protected static function objectSkus($object)
+ public static function objectSkus($object)
{
- $type = $object instanceof \App\Domain ? 'domain' : 'user';
+ $type = \lcfirst(\class_basename($object::class));
$response = [];
// Note: Order by title for consistent ordering in tests
@@ -118,6 +91,75 @@
}
/**
+ * Include SKUs/Wallet information in the object's response.
+ *
+ * @param object $object User/Domain/etc object
+ * @param array $response The response to put the data into
+ */
+ public static function objectEntitlements($object, &$response = []): void
+ {
+ // Object's entitlements information
+ $response['skus'] = \App\Entitlement::objectEntitlementsSummary($object);
+
+ // Some basic information about the object's wallet
+ $wallet = $object->wallet();
+ $response['wallet'] = $wallet->toArray();
+ if ($wallet->discount) {
+ $response['wallet']['discount'] = $wallet->discount->discount;
+ $response['wallet']['discount_description'] = $wallet->discount->description;
+ }
+ }
+
+ /**
+ * Update object entitlements.
+ *
+ * @param object $object The object for update
+ * @param array $rSkus List of SKU IDs requested for the user in the form [id=>qty]
+ */
+ public static function updateEntitlements($object, $rSkus): void
+ {
+ if (!is_array($rSkus)) {
+ return;
+ }
+
+ // list of skus, [id=>obj]
+ $skus = Sku::withEnvTenantContext()->get()->mapWithKeys(
+ function ($sku) {
+ return [$sku->id => $sku];
+ }
+ );
+
+ // existing entitlement's SKUs
+ $eSkus = [];
+
+ $object->entitlements()->groupBy('sku_id')
+ ->selectRaw('count(*) as total, sku_id')->each(
+ function ($e) use (&$eSkus) {
+ $eSkus[$e->sku_id] = $e->total;
+ }
+ );
+
+ foreach ($skus as $skuID => $sku) {
+ $e = array_key_exists($skuID, $eSkus) ? $eSkus[$skuID] : 0;
+ $r = array_key_exists($skuID, $rSkus) ? $rSkus[$skuID] : 0;
+
+ if ($sku->handler_class == \App\Handlers\Mailbox::class) {
+ if ($r != 1) {
+ throw new \Exception("Invalid quantity of mailboxes");
+ }
+ }
+
+ if ($e > $r) {
+ // remove those entitled more than existing
+ $object->removeSku($sku, ($e - $r));
+ } elseif ($e < $r) {
+ // add those requested more than entitled
+ $object->assignSku($sku, ($r - $e));
+ }
+ }
+ }
+
+ /**
* Convert SKU information to metadata used by UI to
* display the form control
*
diff --git a/src/app/Http/Controllers/API/V4/UsersController.php b/src/app/Http/Controllers/API/V4/UsersController.php
--- a/src/app/Http/Controllers/API/V4/UsersController.php
+++ b/src/app/Http/Controllers/API/V4/UsersController.php
@@ -165,6 +165,7 @@
// Check if the user is a controller of his wallet
$isController = $user->canDelete($user);
+ $isDegraded = $user->isDegraded();
$hasCustomDomain = $user->wallet()->entitlements()
->where('entitleable_type', Domain::class)
->count() > 0;
@@ -301,7 +302,7 @@
DB::beginTransaction();
- $this->updateEntitlements($user, $request->skus);
+ SkusController::updateEntitlements($user, $request->skus);
if (!empty($settings)) {
$user->setSettings($settings);
@@ -334,55 +335,6 @@
}
/**
- * Update user entitlements.
- *
- * @param \App\User $user The user
- * @param array $rSkus List of SKU IDs requested for the user in the form [id=>qty]
- */
- protected function updateEntitlements(User $user, $rSkus)
- {
- if (!is_array($rSkus)) {
- return;
- }
-
- // list of skus, [id=>obj]
- $skus = Sku::withEnvTenantContext()->get()->mapWithKeys(
- function ($sku) {
- return [$sku->id => $sku];
- }
- );
-
- // existing entitlement's SKUs
- $eSkus = [];
-
- $user->entitlements()->groupBy('sku_id')
- ->selectRaw('count(*) as total, sku_id')->each(
- function ($e) use (&$eSkus) {
- $eSkus[$e->sku_id] = $e->total;
- }
- );
-
- foreach ($skus as $skuID => $sku) {
- $e = array_key_exists($skuID, $eSkus) ? $eSkus[$skuID] : 0;
- $r = array_key_exists($skuID, $rSkus) ? $rSkus[$skuID] : 0;
-
- if ($sku->handler_class == \App\Handlers\Mailbox::class) {
- if ($r != 1) {
- throw new \Exception("Invalid quantity of mailboxes");
- }
- }
-
- if ($e > $r) {
- // remove those entitled more than existing
- $user->removeSku($sku, ($e - $r));
- } elseif ($e < $r) {
- // add those requested more than entitled
- $user->assignSku($sku, ($r - $e));
- }
- }
- }
-
- /**
* Create a response data array for specified user.
*
* @param \App\User $user User object
diff --git a/src/app/Http/Controllers/RelationController.php b/src/app/Http/Controllers/RelationController.php
--- a/src/app/Http/Controllers/RelationController.php
+++ b/src/app/Http/Controllers/RelationController.php
@@ -321,10 +321,37 @@
$response['aliases'] = $resource->aliases()->pluck('alias')->all();
}
+ // Entitlements/Wallet info
+ if (method_exists($resource, 'wallet')) {
+ API\V4\SkusController::objectEntitlements($resource, $response);
+ }
+
return response()->json($response);
}
/**
+ * Get a list of SKUs available to the resource.
+ *
+ * @param int $id Resource identifier
+ *
+ * @return \Illuminate\Http\JsonResponse
+ */
+ public function skus($id)
+ {
+ $resource = $this->model::find($id);
+
+ if (!$this->checkTenant($resource)) {
+ return $this->errorResponse(404);
+ }
+
+ if (!$this->guard()->user()->canRead($resource)) {
+ return $this->errorResponse(403);
+ }
+
+ return API\V4\SkusController::objectSkus($resource);
+ }
+
+ /**
* Fetch resource status (and reload setup process)
*
* @param int $id Resource identifier
diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -367,18 +367,20 @@
return response
},
error => {
- let loader = error.config.loader
- if (loader) {
- stopLoading(loader)
- }
+ if (error.config) {
+ let loader = error.config.loader
+ if (loader) {
+ stopLoading(loader)
+ }
- // Do not display the error in a toast message, pass the error as-is
- if (axios.isCancel(error) || error.config.ignoreErrors) {
- return Promise.reject(error)
- }
+ // Do not display the error in a toast message, pass the error as-is
+ if (axios.isCancel(error) || error.config.ignoreErrors) {
+ return Promise.reject(error)
+ }
- if (error.config.onFinish) {
- error.config.onFinish()
+ if (error.config.onFinish) {
+ error.config.onFinish()
+ }
}
let error_msg
diff --git a/src/resources/vue/Distlist/Info.vue b/src/resources/vue/Distlist/Info.vue
--- a/src/resources/vue/Distlist/Info.vue
+++ b/src/resources/vue/Distlist/Info.vue
@@ -38,6 +38,10 @@