diff --git a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php --- a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php +++ b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php @@ -51,11 +51,11 @@ } // Process the result - $result = $result->map(function ($domain) { - $data = $domain->toArray(); - $data = array_merge($data, self::domainStatuses($domain)); - return $data; - }); + $result = $result->map( + function ($domain) { + return $this->domainToClient($domain); + } + ); $result = [ 'list' => $result, diff --git a/src/app/Http/Controllers/API/V4/Admin/GroupsController.php b/src/app/Http/Controllers/API/V4/Admin/GroupsController.php --- a/src/app/Http/Controllers/API/V4/Admin/GroupsController.php +++ b/src/app/Http/Controllers/API/V4/Admin/GroupsController.php @@ -37,16 +37,11 @@ } // Process the result - $result = $result->map(function ($group) { - $data = [ - 'id' => $group->id, - 'email' => $group->email, - 'name' => $group->name, - ]; - - $data = array_merge($data, self::groupStatuses($group)); - return $data; - }); + $result = $result->map( + function ($group) { + return $this->groupToClient($group); + } + ); $result = [ 'list' => $result, diff --git a/src/app/Http/Controllers/API/V4/Admin/UsersController.php b/src/app/Http/Controllers/API/V4/Admin/UsersController.php --- a/src/app/Http/Controllers/API/V4/Admin/UsersController.php +++ b/src/app/Http/Controllers/API/V4/Admin/UsersController.php @@ -128,9 +128,7 @@ // Process the result $result = $result->map( function ($user) { - $data = $user->toArray(); - $data = array_merge($data, self::userStatuses($user)); - return $data; + return $this->userToClient($user, true); } ); 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 @@ -6,7 +6,6 @@ use App\Http\Controllers\Controller; use App\Backends\LDAP; use App\Rules\UserEmailDomain; -use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -21,19 +20,17 @@ public function index() { $user = $this->guard()->user(); - $list = []; - foreach ($user->domains() as $domain) { - if (!$domain->isPublic()) { - $data = $domain->toArray(); - $data = array_merge($data, self::domainStatuses($domain)); - $list[] = $data; - } - } - - usort($list, function ($a, $b) { - return strcmp($a['namespace'], $b['namespace']); - }); + $list = \collect($user->domains()) + ->filter(function ($domain) { + return !$domain->isPublic(); + }) + ->map(function ($domain) { + return $this->domainToClient($domain); + }) + ->sortBy('namespace') + ->values() + ->all(); return response()->json($list); } @@ -252,7 +249,7 @@ return $this->errorResponse(403); } - $response = $domain->toArray(); + $response = self::domainToClient($domain, true); // Add hash information to the response $response['hash_text'] = $domain->hash(Domain::HASH_TEXT); @@ -272,8 +269,6 @@ // Entitlements info $response['skus'] = \App\Entitlement::objectEntitlementsSummary($domain); - $response = array_merge($response, self::domainStatuses($domain)); - // Some basic information about the domain wallet $wallet = $domain->wallet(); $response['wallet'] = $wallet->toArray(); @@ -304,35 +299,7 @@ return $this->errorResponse(403); } - $response = self::statusInfo($domain); - - if (!empty(request()->input('refresh'))) { - $updated = false; - $last_step = 'none'; - - foreach ($response['process'] as $idx => $step) { - $last_step = $step['label']; - - if (!$step['state']) { - if (!$this->execProcessStep($domain, $step['label'])) { - break; - } - - $updated = true; - } - } - - if ($updated) { - $response = self::statusInfo($domain); - } - - $success = $response['isReady']; - $suffix = $success ? 'success' : 'error-' . $last_step; - - $response['status'] = $success ? 'success' : 'error'; - $response['message'] = \trans('app.process-' . $suffix); - } - + $response = $this->processStateUpdate($domain); $response = array_merge($response, self::domainStatuses($domain)); return response()->json($response); @@ -432,58 +399,50 @@ } /** - * Domain status (extended) information. + * Prepare a domain for the UI. * * @param \App\Domain $domain Domain object + * @param bool $full Include all domain properties * - * @return array Status information + * @return array Domain info */ - public static function statusInfo(Domain $domain): array + protected static function domainToClient(Domain $domain, bool $full = false): array { - $process = []; - - // If that is not a public domain, add domain specific steps - $steps = [ - 'domain-new' => true, - 'domain-ldap-ready' => $domain->isLdapReady(), - 'domain-verified' => $domain->isVerified(), - 'domain-confirmed' => $domain->isConfirmed(), - ]; - - $count = count($steps); - - // Create a process check list - foreach ($steps as $step_name => $state) { - $step = [ - 'label' => $step_name, - 'title' => \trans("app.process-{$step_name}"), - 'state' => $state, + if ($full) { + $result = $domain->toArray(); + } else { + $result = [ + 'id' => $domain->id, + 'namespace' => $domain->namespace, + 'status' => $domain->status, + 'type' => $domain->type, ]; - - if ($step_name == 'domain-confirmed' && !$state) { - $step['link'] = "/domain/{$domain->id}"; - } - - $process[] = $step; - - if ($state) { - $count--; - } } - $state = $count === 0 ? 'done' : 'running'; + $result = array_merge($result, self::domainStatuses($domain)); - // After 180 seconds assume the process is in failed state, - // this should unlock the Refresh button in the UI - if ($count > 0 && $domain->created_at->diffInSeconds(Carbon::now()) > 180) { - $state = 'failed'; - } + return $result; + } - return [ - 'process' => $process, - 'processState' => $state, - 'isReady' => $count === 0, - ]; + /** + * Domain status (extended) information. + * + * @param \App\Domain $domain Domain object + * + * @return array Status information + */ + public static function statusInfo(Domain $domain): array + { + // If that is not a public domain, add domain specific steps + return self::processStateInfo( + $domain, + [ + 'domain-new' => true, + 'domain-ldap-ready' => $domain->isLdapReady(), + 'domain-verified' => $domain->isVerified(), + 'domain-confirmed' => [$domain->isConfirmed(), "/domain/{$domain->id}"], + ] + ); } /** 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 @@ -7,7 +7,6 @@ use App\Group; use App\Rules\GroupName; use App\User; -use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -75,15 +74,8 @@ $user = $this->guard()->user(); $result = $user->groups()->orderBy('name')->orderBy('email')->get() - ->map(function (Group $group) { - $data = [ - 'id' => $group->id, - 'email' => $group->email, - 'name' => $group->name, - ]; - - $data = array_merge($data, self::groupStatuses($group)); - return $data; + ->map(function ($group) { + return $this->groupToClient($group); }); return response()->json($result); @@ -139,9 +131,8 @@ return $this->errorResponse(403); } - $response = $group->toArray(); + $response = self::groupToClient($group, true); - $response = array_merge($response, self::groupStatuses($group)); $response['statusInfo'] = self::statusInfo($group); // Group configuration, e.g. sender_policy @@ -169,48 +160,7 @@ return $this->errorResponse(403); } - $response = self::statusInfo($group); - - if (!empty(request()->input('refresh'))) { - $updated = false; - $async = false; - $last_step = 'none'; - - foreach ($response['process'] as $idx => $step) { - $last_step = $step['label']; - - if (!$step['state']) { - $exec = $this->execProcessStep($group, $step['label']); - - if (!$exec) { - if ($exec === null) { - $async = true; - } - - break; - } - - $updated = true; - } - } - - if ($updated) { - $response = self::statusInfo($group); - } - - $success = $response['isReady']; - $suffix = $success ? 'success' : 'error-' . $last_step; - - $response['status'] = $success ? 'success' : 'error'; - $response['message'] = \trans('app.process-' . $suffix); - - if ($async && !$success) { - $response['processState'] = 'waiting'; - $response['status'] = 'success'; - $response['message'] = \trans('app.process-async'); - } - } - + $response = $this->processStateUpdate($group); $response = array_merge($response, self::groupStatuses($group)); return response()->json($response); @@ -225,49 +175,13 @@ */ public static function statusInfo(Group $group): array { - $process = []; - $steps = [ - 'distlist-new' => true, - 'distlist-ldap-ready' => $group->isLdapReady(), - ]; - - // Create a process check list - foreach ($steps as $step_name => $state) { - $step = [ - 'label' => $step_name, - 'title' => \trans("app.process-{$step_name}"), - 'state' => $state, - ]; - - $process[] = $step; - } - - $domain = $group->domain(); - - // If that is not a public domain, add domain specific steps - if ($domain && !$domain->isPublic()) { - $domain_status = DomainsController::statusInfo($domain); - $process = array_merge($process, $domain_status['process']); - } - - $all = count($process); - $checked = count(array_filter($process, function ($v) { - return $v['state']; - })); - - $state = $all === $checked ? 'done' : 'running'; - - // After 180 seconds assume the process is in failed state, - // this should unlock the Refresh button in the UI - if ($all !== $checked && $group->created_at->diffInSeconds(Carbon::now()) > 180) { - $state = 'failed'; - } - - return [ - 'process' => $process, - 'processState' => $state, - 'isReady' => $all === $checked, - ]; + return self::processStateInfo( + $group, + [ + 'distlist-new' => true, + 'distlist-ldap-ready' => $group->isLdapReady(), + ] + ); } /** @@ -469,6 +383,32 @@ ]; } + /** + * Convert a group object to an array for the UI. + * + * @param \App\Group $group Group object + * @param bool $full Add all group properties, e.g. members + * + * @return array Statuses array + */ + public static function groupToClient(Group $group, bool $full = false): array + { + if ($full) { + $response = $group->toArray(); + } else { + $response = [ + 'id' => $group->id, + 'email' => $group->email, + 'name' => $group->name, + 'status' => $group->status, + ]; + } + + $response = array_merge($response, self::groupStatuses($group)); + + return $response; + } + /** * Validate an email address for use as a group email * diff --git a/src/app/Http/Controllers/API/V4/Reseller/DomainsController.php b/src/app/Http/Controllers/API/V4/Reseller/DomainsController.php --- a/src/app/Http/Controllers/API/V4/Reseller/DomainsController.php +++ b/src/app/Http/Controllers/API/V4/Reseller/DomainsController.php @@ -38,11 +38,11 @@ } // Process the result - $result = $result->map(function ($domain) { - $data = $domain->toArray(); - $data = array_merge($data, self::domainStatuses($domain)); - return $data; - }); + $result = $result->map( + function ($domain) { + return $this->domainToClient($domain); + } + ); $result = [ 'list' => $result, diff --git a/src/app/Http/Controllers/API/V4/Reseller/GroupsController.php b/src/app/Http/Controllers/API/V4/Reseller/GroupsController.php --- a/src/app/Http/Controllers/API/V4/Reseller/GroupsController.php +++ b/src/app/Http/Controllers/API/V4/Reseller/GroupsController.php @@ -36,16 +36,11 @@ } // Process the result - $result = $result->map(function ($group) { - $data = [ - 'id' => $group->id, - 'email' => $group->email, - 'name' => $group->name, - ]; - - $data = array_merge($data, self::groupStatuses($group)); - return $data; - }); + $result = $result->map( + function ($group) { + return $this->groupToClient($group); + } + ); $result = [ 'list' => $result, diff --git a/src/app/Http/Controllers/API/V4/Reseller/UsersController.php b/src/app/Http/Controllers/API/V4/Reseller/UsersController.php --- a/src/app/Http/Controllers/API/V4/Reseller/UsersController.php +++ b/src/app/Http/Controllers/API/V4/Reseller/UsersController.php @@ -91,11 +91,11 @@ } // Process the result - $result = $result->map(function ($user) { - $data = $user->toArray(); - $data = array_merge($data, self::userStatuses($user)); - return $data; - }); + $result = $result->map( + function ($user) { + return $this->userToClient($user, true); + } + ); $result = [ 'list' => $result, 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 @@ -9,7 +9,6 @@ use App\Rules\UserEmailLocal; use App\Sku; use App\User; -use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Validator; @@ -114,9 +113,7 @@ // Process the result $result = $result->map( function ($user) { - $data = $user->toArray(); - $data = array_merge($data, self::userStatuses($user)); - return $data; + return $this->userToClient($user); } ); @@ -206,48 +203,7 @@ return $this->errorResponse(403); } - $response = self::statusInfo($user); - - if (!empty(request()->input('refresh'))) { - $updated = false; - $async = false; - $last_step = 'none'; - - foreach ($response['process'] as $idx => $step) { - $last_step = $step['label']; - - if (!$step['state']) { - $exec = $this->execProcessStep($user, $step['label']); - - if (!$exec) { - if ($exec === null) { - $async = true; - } - - break; - } - - $updated = true; - } - } - - if ($updated) { - $response = self::statusInfo($user); - } - - $success = $response['isReady']; - $suffix = $success ? 'success' : 'error-' . $last_step; - - $response['status'] = $success ? 'success' : 'error'; - $response['message'] = \trans('app.process-' . $suffix); - - if ($async && !$success) { - $response['processState'] = 'waiting'; - $response['status'] = 'success'; - $response['message'] = \trans('app.process-async'); - } - } - + $response = $this->processStateUpdate($user); $response = array_merge($response, self::userStatuses($user)); return response()->json($response); @@ -262,45 +218,14 @@ */ public static function statusInfo(User $user): array { - $process = []; - $steps = [ - 'user-new' => true, - 'user-ldap-ready' => $user->isLdapReady(), - 'user-imap-ready' => $user->isImapReady(), - ]; - - // Create a process check list - foreach ($steps as $step_name => $state) { - $step = [ - 'label' => $step_name, - 'title' => \trans("app.process-{$step_name}"), - 'state' => $state, - ]; - - $process[] = $step; - } - - list ($local, $domain) = explode('@', $user->email); - $domain = Domain::where('namespace', $domain)->first(); - - // If that is not a public domain, add domain specific steps - if ($domain && !$domain->isPublic()) { - $domain_status = DomainsController::statusInfo($domain); - $process = array_merge($process, $domain_status['process']); - } - - $all = count($process); - $checked = count(array_filter($process, function ($v) { - return $v['state']; - })); - - $state = $all === $checked ? 'done' : 'running'; - - // After 180 seconds assume the process is in failed state, - // this should unlock the Refresh button in the UI - if ($all !== $checked && $user->created_at->diffInSeconds(Carbon::now()) > 180) { - $state = 'failed'; - } + $process = self::processStateInfo( + $user, + [ + 'user-new' => true, + 'user-ldap-ready' => $user->isLdapReady(), + 'user-imap-ready' => $user->isImapReady(), + ] + ); // Check if the user is a controller of his wallet $isController = $user->canDelete($user); @@ -318,7 +243,7 @@ ->values() ->all(); - return [ + $result = [ 'skus' => $skus, // TODO: This will change when we enable all users to create domains 'enableDomains' => $isController && $hasCustomDomain, @@ -326,10 +251,9 @@ 'enableDistlists' => $isController && $hasCustomDomain && in_array('distlist', $skus), 'enableUsers' => $isController, 'enableWallets' => $isController, - 'process' => $process, - 'processState' => $state, - 'isReady' => $all === $checked, ]; + + return array_merge($process, $result); } /** @@ -521,7 +445,7 @@ */ public static function userResponse(User $user): array { - $response = $user->toArray(); + $response = self::userToClient($user, true); // Settings $response['settings'] = []; @@ -538,8 +462,6 @@ // Status info $response['statusInfo'] = self::statusInfo($user); - $response = array_merge($response, self::userStatuses($user)); - // Add more info to the wallet object output $map_func = function ($wallet) use ($user) { $result = $wallet->toArray(); @@ -585,6 +507,31 @@ ]; } + /** + * Prepare a user information for the UI + * + * @param \App\User $user User object + * @param bool $full Include all user properties + * + * @return array User data as an array + */ + protected static function userToClient(User $user, bool $full = false): array + { + if ($full) { + $response = $user->toArray(); + } else { + $response = [ + 'id' => $user->id, + 'email' => $user->email, + 'status' => $user->status, + ]; + } + + $response = array_merge($response, self::userStatuses($user)); + + return $response; + } + /** * Validate user input * diff --git a/src/app/Http/Controllers/Controller.php b/src/app/Http/Controllers/Controller.php --- a/src/app/Http/Controllers/Controller.php +++ b/src/app/Http/Controllers/Controller.php @@ -81,4 +81,118 @@ { return Auth::guard(); } + + /** + * Object status' process information. + * + * @param object $object The object to process + * @param array $steps The steps definition + * + * @return array Process state information + */ + protected static function processStateInfo($object, array $steps): array + { + $process = []; + + // Create a process check list + foreach ($steps as $step_name => $state) { + $step = [ + 'label' => $step_name, + 'title' => \trans("app.process-{$step_name}"), + ]; + + if (is_array($state)) { + $step['link'] = $state[1]; + $state = $state[0]; + } + + $step['state'] = $state; + + $process[] = $step; + } + + // Add domain specific steps + if (method_exists($object, 'domain')) { + $domain = $object->domain(); + + // If that is not a public domain + if ($domain && !$domain->isPublic()) { + $domain_status = API\V4\DomainsController::statusInfo($domain); + $process = array_merge($process, $domain_status['process']); + } + } + + $all = count($process); + $checked = count(array_filter($process, function ($v) { + return $v['state']; + })); + + $state = $all === $checked ? 'done' : 'running'; + + // After 180 seconds assume the process is in failed state, + // this should unlock the Refresh button in the UI + if ($all !== $checked && $object->created_at->diffInSeconds(\Carbon\Carbon::now()) > 180) { + $state = 'failed'; + } + + return [ + 'process' => $process, + 'processState' => $state, + 'isReady' => $all === $checked, + ]; + } + + /** + * Object status' process information update. + * + * @param object $object The object to process + * + * @return array Process state information + */ + protected function processStateUpdate($object): array + { + $response = $this->statusInfo($object); // @phpstan-ignore-line + + if (!empty(request()->input('refresh'))) { + $updated = false; + $async = false; + $last_step = 'none'; + + foreach ($response['process'] as $idx => $step) { + $last_step = $step['label']; + + if (!$step['state']) { + $exec = $this->execProcessStep($object, $step['label']); // @phpstan-ignore-line + + if (!$exec) { + if ($exec === null) { + $async = true; + } + + break; + } + + $updated = true; + } + } + + if ($updated) { + $response = $this->statusInfo($object); // @phpstan-ignore-line + } + + $success = $response['isReady']; + $suffix = $success ? 'success' : 'error-' . $last_step; + + $response['status'] = $success ? 'success' : 'error'; + $response['message'] = \trans('app.process-' . $suffix); + + if ($async && !$success) { + $response['processState'] = 'waiting'; + $response['status'] = 'success'; + $response['message'] = \trans('app.process-async'); + } + } + + return $response; + } }