Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117753068
D3014.1775192522.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
59 KB
Referenced Files
None
Subscribers
None
D3014.1775192522.diff
View Options
diff --git a/src/app/Backends/LDAP.php b/src/app/Backends/LDAP.php
--- a/src/app/Backends/LDAP.php
+++ b/src/app/Backends/LDAP.php
@@ -62,10 +62,8 @@
$config = self::getConfig('admin');
$ldap = self::initLDAP($config);
- $hostedRootDN = \config('ldap.hosted.root_dn');
$mgmtRootDN = \config('ldap.admin.root_dn');
-
- $domainBaseDN = "ou={$domain->namespace},{$hostedRootDN}";
+ $domainBaseDN = self::baseDN($domain->namespace);
$aci = [
'(targetattr = "*")'
@@ -101,14 +99,12 @@
self::setDomainAttributes($domain, $entry);
if (!$ldap->get_entry($dn)) {
- $result = $ldap->add_entry($dn, $entry);
-
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $dn,
+ $entry,
+ "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
+ );
}
// create ou, roles, ous
@@ -153,62 +149,56 @@
);
if (!$ldap->get_entry($domainBaseDN)) {
- $result = $ldap->add_entry($domainBaseDN, $entry);
-
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $domainBaseDN,
+ $entry,
+ "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
+ );
}
foreach (['Groups', 'People', 'Resources', 'Shared Folders'] as $item) {
- if (!$ldap->get_entry("ou={$item},{$domainBaseDN}")) {
- $result = $ldap->add_entry(
- "ou={$item},{$domainBaseDN}",
- [
- 'ou' => $item,
- 'description' => $item,
- 'objectclass' => [
- 'top',
- 'organizationalunit'
- ]
+ $itemDN = self::baseDN($domain->namespace, $item);
+ if (!$ldap->get_entry($itemDN)) {
+ $itemEntry = [
+ 'ou' => $item,
+ 'description' => $item,
+ 'objectclass' => [
+ 'top',
+ 'organizationalunit'
]
- );
+ ];
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $itemDN,
+ $itemEntry,
+ "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
+ );
}
}
foreach (['kolab-admin'] as $item) {
- if (!$ldap->get_entry("cn={$item},{$domainBaseDN}")) {
- $result = $ldap->add_entry(
- "cn={$item},{$domainBaseDN}",
- [
- 'cn' => $item,
- 'description' => "{$item} role",
- 'objectclass' => [
- 'top',
- 'ldapsubentry',
- 'nsmanagedroledefinition',
- 'nsroledefinition',
- 'nssimpleroledefinition'
- ]
+ $itemDN = "cn={$item},{$domainBaseDN}";
+ if (!$ldap->get_entry($itemDN)) {
+ $itemEntry = [
+ 'cn' => $item,
+ 'description' => "{$item} role",
+ 'objectclass' => [
+ 'top',
+ 'ldapsubentry',
+ 'nsmanagedroledefinition',
+ 'nsroledefinition',
+ 'nssimpleroledefinition'
]
- );
+ ];
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $itemDN,
+ $itemEntry,
+ "Failed to create domain {$domain->namespace} in LDAP (" . __LINE__ . ")"
+ );
}
}
@@ -231,46 +221,27 @@
$config = self::getConfig('admin');
$ldap = self::initLDAP($config);
- list($cn, $domainName) = explode('@', $group->email);
-
- $domain = $group->domain();
-
- if (empty($domain)) {
- self::throwException(
- $ldap,
- "Failed to create group {$group->email} in LDAP (" . __LINE__ . ")"
- );
- }
-
- $hostedRootDN = \config('ldap.hosted.root_dn');
-
- $domainBaseDN = "ou={$domain->namespace},{$hostedRootDN}";
-
- $groupBaseDN = "ou=Groups,{$domainBaseDN}";
-
- $dn = "cn={$cn},{$groupBaseDN}";
+ $domainName = explode('@', $group->email, 2)[1];
+ $cn = $ldap->quote_string($group->name);
+ $dn = "cn={$cn}," . self::baseDN($domainName, 'Groups');
$entry = [
- 'cn' => $cn,
'mail' => $group->email,
'objectclass' => [
'top',
'groupofuniquenames',
'kolabgroupofuniquenames'
],
- 'uniquemember' => []
];
self::setGroupAttributes($ldap, $group, $entry);
- $result = $ldap->add_entry($dn, $entry);
-
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create group {$group->email} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $dn,
+ $entry,
+ "Failed to create group {$group->email} in LDAP (" . __LINE__ . ")"
+ );
if (empty(self::$ldap)) {
$ldap->close();
@@ -325,14 +296,12 @@
self::setUserAttributes($user, $entry);
- $result = $ldap->add_entry($dn, $entry);
-
- if (!$result) {
- self::throwException(
- $ldap,
- "Failed to create user {$user->email} in LDAP (" . __LINE__ . ")"
- );
- }
+ self::addEntry(
+ $ldap,
+ $dn,
+ $entry,
+ "Failed to create user {$user->email} in LDAP (" . __LINE__ . ")"
+ );
}
if (empty(self::$ldap)) {
@@ -352,10 +321,7 @@
$config = self::getConfig('admin');
$ldap = self::initLDAP($config);
- $hostedRootDN = \config('ldap.hosted.root_dn');
- $mgmtRootDN = \config('ldap.admin.root_dn');
-
- $domainBaseDN = "ou={$domain->namespace},{$hostedRootDN}";
+ $domainBaseDN = self::baseDN($domain->namespace);
if ($ldap->get_entry($domainBaseDN)) {
$result = $ldap->delete_entry_recursive($domainBaseDN);
@@ -568,41 +534,18 @@
$config = self::getConfig('admin');
$ldap = self::initLDAP($config);
- list($cn, $domainName) = explode('@', $group->email);
-
- $domain = $group->domain();
+ $newEntry = $oldEntry = self::getGroupEntry($ldap, $group->email, $dn);
- if (empty($domain)) {
+ if (empty($oldEntry)) {
self::throwException(
$ldap,
"Failed to update group {$group->email} in LDAP (group not found)"
);
}
- $hostedRootDN = \config('ldap.hosted.root_dn');
-
- $domainBaseDN = "ou={$domain->namespace},{$hostedRootDN}";
-
- $groupBaseDN = "ou=Groups,{$domainBaseDN}";
-
- $dn = "cn={$cn},{$groupBaseDN}";
-
- $entry = [
- 'cn' => $cn,
- 'mail' => $group->email,
- 'objectclass' => [
- 'top',
- 'groupofuniquenames',
- 'kolabgroupofuniquenames'
- ],
- 'uniquemember' => []
- ];
-
- $oldEntry = $ldap->get_entry($dn);
-
- self::setGroupAttributes($ldap, $group, $entry);
+ self::setGroupAttributes($ldap, $group, $newEntry);
- $result = $ldap->modify_entry($dn, $oldEntry, $entry);
+ $result = $ldap->modify_entry($dn, $oldEntry, $newEntry);
if (!is_array($result)) {
self::throwException(
@@ -712,15 +655,13 @@
$settings = $group->getSettings(['sender_policy']);
$entry['kolaballowsmtpsender'] = json_decode($settings['sender_policy'] ?: '[]', true);
+ $entry['cn'] = $group->name;
+ $entry['uniquemember'] = [];
+ $groupDomain = explode('@', $group->email, 2)[1];
+ $domainBaseDN = self::baseDN($groupDomain);
$validMembers = [];
- $domain = $group->domain();
-
- $hostedRootDN = \config('ldap.hosted.root_dn');
-
- $domainBaseDN = "ou={$domain->namespace},{$hostedRootDN}";
-
foreach ($group->members as $member) {
list($local, $domainName) = explode('@', $member);
@@ -728,7 +669,7 @@
$memberEntry = $ldap->get_entry($memberDN);
// if the member is in the local domain but doesn't exist, drop it
- if ($domainName == $domain->namespace && !$memberEntry) {
+ if ($domainName == $groupDomain && !$memberEntry) {
continue;
}
@@ -755,10 +696,12 @@
// Update members in sql (some might have been removed),
// skip model events to not invoke another update job
- $group->members = $validMembers;
- Group::withoutEvents(function () use ($group) {
- $group->save();
- });
+ if ($group->members !== $validMembers) {
+ $group->members = $validMembers;
+ Group::withoutEvents(function () use ($group) {
+ $group->save();
+ });
+ }
}
/**
@@ -868,20 +811,26 @@
*/
private static function getGroupEntry($ldap, $email, &$dn = null)
{
- list($_local, $_domain) = explode('@', $email, 2);
+ $domainName = explode('@', $email, 2)[1];
+ $base_dn = self::baseDN($domainName, 'Groups');
- $domain = $ldap->find_domain($_domain);
+ $attrs = ['dn', 'cn', 'mail', 'uniquemember', 'objectclass', 'kolaballowsmtpsender'];
- if (!$domain) {
- return $domain;
- }
+ // For groups we're using search() instead of get_entry() because
+ // a group name is not constant, so e.g. on update we might have
+ // the new name, but not the old one. Email address is constant.
+ $result = $ldap->search($base_dn, "(mail=$email)", "sub", $attrs);
- $base_dn = $ldap->domain_root_dn($_domain);
- $dn = "cn={$_local},ou=Groups,{$base_dn}";
+ if ($result && $result->count() == 1) {
+ $entries = $result->entries(true);
+ $dn = key($entries);
+ $entry = $entries[$dn];
+ $entry['dn'] = $dn;
- $entry = $ldap->get_entry($dn);
+ return $entry;
+ }
- return $entry ?: null;
+ return null;
}
/**
@@ -892,20 +841,13 @@
* @param string $dn Reference to user DN
* @param bool $full Get extra attributes, e.g. nsroledn
*
- * @return false|null|array User entry, False on error, NULL if not found
+ * @return ?array User entry, NULL if not found
*/
private static function getUserEntry($ldap, $email, &$dn = null, $full = false)
{
- list($_local, $_domain) = explode('@', $email, 2);
-
- $domain = $ldap->find_domain($_domain);
+ $domainName = explode('@', $email, 2)[1];
- if (!$domain) {
- return $domain;
- }
-
- $base_dn = $ldap->domain_root_dn($_domain);
- $dn = "uid={$email},ou=People,{$base_dn}";
+ $dn = "uid={$email}," . self::baseDN($domainName, 'People');
$entry = $ldap->get_entry($dn);
@@ -975,6 +917,39 @@
\Log::{$function}($msg);
}
+ /**
+ * A wrapper for Net_LDAP3::add_entry() with error handler
+ *
+ * @param \Net_LDAP3 $ldap Ldap connection
+ * @param string $dn Entry DN
+ * @param array $entry Entry attributes
+ * @param ?string $errorMsg A message to throw as an exception on error
+ *
+ * @throws \Exception
+ */
+ private static function addEntry($ldap, string $dn, array $entry, $errorMsg = null)
+ {
+ // try/catch because Laravel converts warnings into exceptions
+ // and we want more human-friendly error message than that
+ try {
+ $result = $ldap->add_entry($dn, $entry);
+ } catch (\Exception $e) {
+ $result = false;
+ }
+
+ if (!$result) {
+ if (!$errorMsg) {
+ $errorMsg = "LDAP Error (" . __LINE__ . ")";
+ }
+
+ if (isset($e)) {
+ $errorMsg .= ": " . $e->getMessage();
+ }
+
+ self::throwException($ldap, $errorMsg);
+ }
+ }
+
/**
* Throw exception and close the connection when needed
*
@@ -991,4 +966,25 @@
throw new \Exception($message);
}
+
+ /**
+ * Create a base DN string for specified object
+ *
+ * @param string $domainName Domain namespace
+ * @param ?string $ouName Optional name of the sub-tree (OU)
+ *
+ * @return string Full base DN
+ */
+ private static function baseDN(string $domainName, string $ouName = null): string
+ {
+ $hostedRootDN = \config('ldap.hosted.root_dn');
+
+ $dn = "ou={$domainName},{$hostedRootDN}";
+
+ if ($ouName) {
+ $dn = "ou={$ouName},{$dn}";
+ }
+
+ return $dn;
+ }
}
diff --git a/src/app/Console/Commands/GroupsCommand.php b/src/app/Console/Commands/GroupsCommand.php
new file mode 100644
--- /dev/null
+++ b/src/app/Console/Commands/GroupsCommand.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Console\ObjectListCommand;
+
+class GroupsCommand extends ObjectListCommand
+{
+ protected $objectClass = \App\Group::class;
+ protected $objectName = 'group';
+ protected $objectTitle = 'email';
+}
diff --git a/src/app/Group.php b/src/app/Group.php
--- a/src/app/Group.php
+++ b/src/app/Group.php
@@ -17,6 +17,7 @@
* @property int $id The group identifier
* @property string $email An email address
* @property string $members A comma-separated list of email addresses
+ * @property string $name The group name
* @property int $status The group status
* @property int $tenant_id Tenant identifier
*/
@@ -42,8 +43,9 @@
protected $fillable = [
'email',
+ 'members',
+ 'name',
'status',
- 'members'
];
/**
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
@@ -28,7 +28,7 @@
});
}
- $result = $result->sortBy('namespace')->values();
+ $result = $result->sortBy('name')->values();
}
} elseif (!empty($search)) {
if ($group = Group::where('email', $search)->first()) {
@@ -41,6 +41,7 @@
$data = [
'id' => $group->id,
'email' => $group->email,
+ 'name' => $group->name,
];
$data = array_merge($data, self::groupStatuses($group));
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
@@ -5,6 +5,7 @@
use App\Http\Controllers\Controller;
use App\Domain;
use App\Group;
+use App\Rules\GroupName;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
@@ -73,11 +74,12 @@
{
$user = $this->guard()->user();
- $result = $user->groups()->orderBy('email')->get()
+ $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));
@@ -284,13 +286,26 @@
return $this->errorResponse(403);
}
- $email = request()->input('email');
- $members = request()->input('members');
+ $email = $request->input('email');
+ $members = $request->input('members');
$errors = [];
+ $rules = [
+ 'name' => 'required|string|max:191',
+ ];
// Validate group address
if ($error = GroupsController::validateGroupEmail($email, $owner)) {
$errors['email'] = $error;
+ } else {
+ list(, $domainName) = explode('@', $email);
+ $rules['name'] = ['required', 'string', new GroupName($owner, $domainName)];
+ }
+
+ // Validate the group name
+ $v = Validator::make($request->all(), $rules);
+
+ if ($v->fails()) {
+ $errors = array_merge($errors, $v->errors()->toArray());
}
// Validate members' email addresses
@@ -318,6 +333,7 @@
// Create the group
$group = new Group();
+ $group->name = $request->input('name');
$group->email = $email;
$group->members = $members;
$group->save();
@@ -355,11 +371,24 @@
}
$owner = $group->wallet()->owner;
-
- // It is possible to update members property only for now
- $members = request()->input('members');
+ $name = $request->input('name');
+ $members = $request->input('members');
$errors = [];
+ // Validate the group name
+ if ($name !== null && $name != $group->name) {
+ list(, $domainName) = explode('@', $group->email);
+ $rules = ['name' => ['required', 'string', new GroupName($owner, $domainName)]];
+
+ $v = Validator::make($request->all(), $rules);
+
+ if ($v->fails()) {
+ $errors = array_merge($errors, $v->errors()->toArray());
+ } else {
+ $group->name = $name;
+ }
+ }
+
// Validate members' email addresses
if (empty($members) || !is_array($members)) {
$errors['members'] = \trans('validation.listmembersrequired');
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
@@ -27,7 +27,7 @@
});
}
- $result = $result->sortBy('namespace')->values();
+ $result = $result->sortBy('name')->values();
}
} elseif (!empty($search)) {
if ($group = Group::withSubjectTenantContext()->where('email', $search)->first()) {
@@ -40,6 +40,7 @@
$data = [
'id' => $group->id,
'email' => $group->email,
+ 'name' => $group->name,
];
$data = array_merge($data, self::groupStatuses($group));
diff --git a/src/app/Observers/GroupObserver.php b/src/app/Observers/GroupObserver.php
--- a/src/app/Observers/GroupObserver.php
+++ b/src/app/Observers/GroupObserver.php
@@ -17,6 +17,10 @@
public function creating(Group $group): void
{
$group->status |= Group::STATUS_NEW | Group::STATUS_ACTIVE;
+
+ if (!isset($group->name) && isset($group->email)) {
+ $group->name = explode('@', $group->email)[0];
+ }
}
/**
diff --git a/src/app/Rules/GroupName.php b/src/app/Rules/GroupName.php
new file mode 100644
--- /dev/null
+++ b/src/app/Rules/GroupName.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace App\Rules;
+
+use Illuminate\Contracts\Validation\Rule;
+use Illuminate\Support\Facades\Validator;
+use Illuminate\Support\Str;
+
+class GroupName implements Rule
+{
+ private $message;
+ private $owner;
+ private $domain;
+
+ /**
+ * Class constructor.
+ *
+ * @param \App\User $owner The account owner
+ * @param string $domain The domain name of the group
+ */
+ public function __construct($owner, $domain)
+ {
+ $this->owner = $owner;
+ $this->domain = Str::lower($domain);
+ }
+
+ /**
+ * Determine if the validation rule passes.
+ *
+ * @param string $attribute Attribute name
+ * @param mixed $name The value to validate
+ *
+ * @return bool
+ */
+ public function passes($attribute, $name): bool
+ {
+ if (empty($name) || !is_string($name)) {
+ $this->message = \trans('validation.nameinvalid');
+ return false;
+ }
+
+ // Check the max length, according to the database column length
+ if (strlen($name) > 191) {
+ $this->message = \trans('validation.nametoolong');
+ return false;
+ }
+
+ // Check if the name is unique in the domain
+ // FIXME: Maybe just using the whole groups table would be faster than groups()?
+ $exists = $this->owner->groups()
+ ->where('groups.name', $name)
+ ->where('groups.email', 'like', '%@' . $this->domain)
+ ->exists();
+
+ if ($exists) {
+ $this->message = \trans('validation.nameexists');
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the validation error message.
+ *
+ * @return string
+ */
+ public function message(): ?string
+ {
+ return $this->message;
+ }
+}
diff --git a/src/config/ldap.php b/src/config/ldap.php
--- a/src/config/ldap.php
+++ b/src/config/ldap.php
@@ -21,10 +21,7 @@
// probably proxy credentials?
],
- 'user_base_dn' => env('LDAP_USER_BASE_DN', null),
- 'base_dn' => env('LDAP_BASE_DN', null),
'root_dn' => env('LDAP_ROOT_DN', null),
- 'unique_attribute' => env('LDAP_UNIQUE_ATTRIBUTE', 'nsuniqueid'),
'service_bind_dn' => env('LDAP_SERVICE_BIND_DN', null),
'service_bind_pw' => env('LDAP_SERVICE_BIND_PW', null),
'login_filter' => env('LDAP_LOGIN_FILTER', '(&(objectclass=kolabinetorgperson)(uid=%s))'),
diff --git a/src/database/migrations/2021_11_10_100000_add_group_name_column.php b/src/database/migrations/2021_11_10_100000_add_group_name_column.php
new file mode 100644
--- /dev/null
+++ b/src/database/migrations/2021_11_10_100000_add_group_name_column.php
@@ -0,0 +1,52 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+// phpcs:ignore
+class AddGroupNameColumn extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table(
+ 'groups',
+ function (Blueprint $table) {
+ $table->string('name')->nullable()->after('email');
+ }
+ );
+
+ // Fill the name with the local part of the email address
+ DB::table('groups')->update([
+ 'name' => DB::raw("SUBSTRING_INDEX(`email`, '@', 1)")
+ ]);
+
+ Schema::table(
+ 'groups',
+ function (Blueprint $table) {
+ $table->string('name')->nullable(false)->change();
+ }
+ );
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table(
+ 'groups',
+ function (Blueprint $table) {
+ $table->dropColumn('name');
+ }
+ );
+ }
+}
diff --git a/src/resources/lang/en/ui.php b/src/resources/lang/en/ui.php
--- a/src/resources/lang/en/ui.php
+++ b/src/resources/lang/en/ui.php
@@ -56,6 +56,7 @@
'delete' => "Delete list",
'email' => "Email",
'list-empty' => "There are no distribution lists in this account.",
+ 'name' => "Name",
'new' => "New distribution list",
'recipients' => "Recipients",
'sender-policy' => "Sender Access List",
diff --git a/src/resources/lang/en/validation.php b/src/resources/lang/en/validation.php
--- a/src/resources/lang/en/validation.php
+++ b/src/resources/lang/en/validation.php
@@ -141,6 +141,9 @@
'spf-entry-invalid' => 'The entry format is invalid. Expected a domain name starting with a dot.',
'sp-entry-invalid' => 'The entry format is invalid. Expected an email, domain, or part of it.',
'invalid-config-parameter' => 'The requested configuration parameter is not supported.',
+ 'nameexists' => 'The specified name is not available.',
+ 'nameinvalid' => 'The specified name is invalid.',
+ 'nametoolong' => 'The specified name is too long.',
/*
|--------------------------------------------------------------------------
diff --git a/src/resources/vue/Admin/Distlist.vue b/src/resources/vue/Admin/Distlist.vue
--- a/src/resources/vue/Admin/Distlist.vue
+++ b/src/resources/vue/Admin/Distlist.vue
@@ -21,6 +21,12 @@
<span :class="$root.distlistStatusClass(list) + ' form-control-plaintext'" id="status">{{ $root.distlistStatusText(list) }}</span>
</div>
</div>
+ <div class="row plaintext">
+ <label for="name" class="col-sm-4 col-form-label">{{ $t('distlist.name') }}</label>
+ <div class="col-sm-8">
+ <span class="form-control-plaintext" id="name">{{ list.name }}</span>
+ </div>
+ </div>
<div class="row plaintext">
<label for="members" class="col-sm-4 col-form-label">{{ $t('distlist.recipients') }}</label>
<div class="col-sm-8">
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
@@ -290,6 +290,7 @@
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
+ <th scope="col">{{ $t('distlist.name') }}</th>
<th scope="col">{{ $t('form.email') }}</th>
</tr>
</thead>
@@ -297,13 +298,16 @@
<tr v-for="list in distlists" :key="list.id" @click="$root.clickRecord">
<td>
<svg-icon icon="users" :class="$root.distlistStatusClass(list)" :title="$root.distlistStatusText(list)"></svg-icon>
+ <router-link :to="{ path: '/distlist/' + list.id }">{{ list.name }}</router-link>
+ </td>
+ <td>
<router-link :to="{ path: '/distlist/' + list.id }">{{ list.email }}</router-link>
</td>
</tr>
</tbody>
<tfoot class="table-fake-body">
<tr>
- <td>{{ $t('user.distlists-none') }}</td>
+ <td colspan="2">{{ $t('user.distlists-none') }}</td>
</tr>
</tfoot>
</table>
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
@@ -33,6 +33,12 @@
<span :class="$root.distlistStatusClass(list) + ' form-control-plaintext'" id="status">{{ $root.distlistStatusText(list) }}</span>
</div>
</div>
+ <div class="row mb-3">
+ <label for="name" class="col-sm-4 col-form-label">{{ $t('distlist.name') }}</label>
+ <div class="col-sm-8">
+ <input type="text" class="form-control" id="name" required v-model="list.name">
+ </div>
+ </div>
<div class="row mb-3">
<label for="email" class="col-sm-4 col-form-label">{{ $t('form.email') }}</label>
<div class="col-sm-8">
@@ -50,10 +56,10 @@
</div>
<div class="tab-pane" id="settings" role="tabpanel" aria-labelledby="tab-settings">
<form @submit.prevent="submitSettings" class="card-body">
- <div class="row checkbox mb-3">
+ <div class="row mb-3">
<label for="sender-policy-input" class="col-sm-4 col-form-label">{{ $t('distlist.sender-policy') }}</label>
<div class="col-sm-8 pt-2">
- <list-input id="sender-policy" :list="list.config.sender_policy"></list-input>
+ <list-input id="sender-policy" :list="list.config.sender_policy" class="mb-1"></list-input>
<small id="sender-policy-hint" class="text-muted">
{{ $t('distlist.sender-policy-text') }}
</small>
@@ -100,6 +106,9 @@
.catch(this.$root.errorHandler)
}
},
+ mounted() {
+ $('#name').focus()
+ },
methods: {
deleteList() {
axios.delete('/api/v4/groups/' + this.list_id)
diff --git a/src/resources/vue/Distlist/List.vue b/src/resources/vue/Distlist/List.vue
--- a/src/resources/vue/Distlist/List.vue
+++ b/src/resources/vue/Distlist/List.vue
@@ -12,6 +12,7 @@
<table class="table table-sm table-hover">
<thead>
<tr>
+ <th scope="col">{{ $t('distlist.name') }}</th>
<th scope="col">{{ $t('distlist.email') }}</th>
</tr>
</thead>
@@ -19,13 +20,16 @@
<tr v-for="list in lists" :key="list.id" @click="$root.clickRecord">
<td>
<svg-icon icon="users" :class="$root.distlistStatusClass(list)" :title="$root.distlistStatusText(list)"></svg-icon>
+ <router-link :to="{ path: 'distlist/' + list.id }">{{ list.name }}</router-link>
+ </td>
+ <td>
<router-link :to="{ path: 'distlist/' + list.id }">{{ list.email }}</router-link>
</td>
</tr>
</tbody>
<tfoot class="table-fake-body">
<tr>
- <td>{{ $t('distlist.list-empty') }}</td>
+ <td colspan="2">{{ $t('distlist.list-empty') }}</td>
</tr>
</tfoot>
</table>
diff --git a/src/tests/Browser/Admin/DistlistTest.php b/src/tests/Browser/Admin/DistlistTest.php
--- a/src/tests/Browser/Admin/DistlistTest.php
+++ b/src/tests/Browser/Admin/DistlistTest.php
@@ -59,7 +59,7 @@
$this->browse(function (Browser $browser) {
$user = $this->getTestUser('john@kolab.org');
- $group = $this->getTestGroup('group-test@kolab.org');
+ $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']);
$group->assignToWallet($user->wallets->first());
$group->members = ['test1@gmail.com', 'test2@gmail.com'];
$group->save();
@@ -80,14 +80,16 @@
->on($distlist_page)
->assertSeeIn('@distlist-info .card-title', $group->email)
->with('@distlist-info form', function (Browser $browser) use ($group) {
- $browser->assertElementsCount('.row', 3)
+ $browser->assertElementsCount('.row', 4)
->assertSeeIn('.row:nth-child(1) label', 'ID (Created)')
->assertSeeIn('.row:nth-child(1) #distlistid', "{$group->id} ({$group->created_at})")
->assertSeeIn('.row:nth-child(2) label', 'Status')
->assertSeeIn('.row:nth-child(2) #status.text-danger', 'Not Ready')
- ->assertSeeIn('.row:nth-child(3) label', 'Recipients')
- ->assertSeeIn('.row:nth-child(3) #members', $group->members[0])
- ->assertSeeIn('.row:nth-child(3) #members', $group->members[1]);
+ ->assertSeeIn('.row:nth-child(3) label', 'Name')
+ ->assertSeeIn('.row:nth-child(3) #name', $group->name)
+ ->assertSeeIn('.row:nth-child(4) label', 'Recipients')
+ ->assertSeeIn('.row:nth-child(4) #members', $group->members[0])
+ ->assertSeeIn('.row:nth-child(4) #members', $group->members[1]);
})
->assertElementsCount('ul.nav-tabs', 1)
->assertSeeIn('ul.nav-tabs .nav-link', 'Settings')
diff --git a/src/tests/Browser/Admin/UserTest.php b/src/tests/Browser/Admin/UserTest.php
--- a/src/tests/Browser/Admin/UserTest.php
+++ b/src/tests/Browser/Admin/UserTest.php
@@ -195,7 +195,7 @@
$wallet->discount()->associate($discount);
$wallet->debit(2010);
$wallet->save();
- $group = $this->getTestGroup('group-test@kolab.org');
+ $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']);
$group->assignToWallet($john->wallets->first());
$john->setSetting('greylist_enabled', null);
@@ -276,8 +276,9 @@
->click('@nav #tab-distlists')
->with('@user-distlists table', function (Browser $browser) {
$browser->assertElementsCount('tbody tr', 1)
- ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'group-test@kolab.org')
+ ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'Test Group')
->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-danger')
+ ->assertSeeIn('tbody tr:nth-child(1) td:last-child a', 'group-test@kolab.org')
->assertMissing('tfoot');
});
diff --git a/src/tests/Browser/DistlistTest.php b/src/tests/Browser/DistlistTest.php
--- a/src/tests/Browser/DistlistTest.php
+++ b/src/tests/Browser/DistlistTest.php
@@ -82,7 +82,7 @@
// Create a single group, add beta+distlist entitlements
$john = $this->getTestUser('john@kolab.org');
$this->addDistlistEntitlement($john);
- $group = $this->getTestGroup('group-test@kolab.org');
+ $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']);
$group->assignToWallet($john->wallets->first());
// Test distribution lists page
@@ -93,9 +93,12 @@
->on(new DistlistList())
->whenAvailable('@table', function (Browser $browser) {
$browser->waitFor('tbody tr')
+ ->assertSeeIn('thead tr th:nth-child(1)', 'Name')
+ ->assertSeeIn('thead tr th:nth-child(2)', 'Email')
->assertElementsCount('tbody tr', 1)
- ->assertSeeIn('tbody tr:nth-child(1) a', 'group-test@kolab.org')
- ->assertText('tbody tr:nth-child(1) svg.text-danger title', 'Not Ready')
+ ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(1) a', 'Test Group')
+ ->assertText('tbody tr:nth-child(1) td:nth-child(1) svg.text-danger title', 'Not Ready')
+ ->assertSeeIn('tbody tr:nth-child(1) td:nth-child(2) a', 'group-test@kolab.org')
->assertMissing('tfoot');
});
});
@@ -130,10 +133,13 @@
->with('@general', function (Browser $browser) {
// Assert form content
$browser->assertMissing('#status')
- ->assertSeeIn('div.row:nth-child(1) label', 'Email')
+ ->assertFocused('#name')
+ ->assertSeeIn('div.row:nth-child(1) label', 'Name')
->assertValue('div.row:nth-child(1) input[type=text]', '')
- ->assertSeeIn('div.row:nth-child(2) label', 'Recipients')
- ->assertVisible('div.row:nth-child(2) .list-input')
+ ->assertSeeIn('div.row:nth-child(2) label', 'Email')
+ ->assertValue('div.row:nth-child(2) input[type=text]', '')
+ ->assertSeeIn('div.row:nth-child(3) label', 'Recipients')
+ ->assertVisible('div.row:nth-child(3) .list-input')
->with(new ListInput('#members'), function (Browser $browser) {
$browser->assertListInputValue([])
->assertValue('@input', '');
@@ -141,15 +147,17 @@
->assertSeeIn('button[type=submit]', 'Submit');
})
// Test error conditions
+ ->type('#name', str_repeat('A', 192))
->type('#email', 'group-test@kolabnow.com')
->click('@general button[type=submit]')
- ->waitFor('#email + .invalid-feedback')
- ->assertSeeIn('#email + .invalid-feedback', 'The specified domain is not available.')
- ->assertFocused('#email')
->waitFor('#members + .invalid-feedback')
+ ->assertSeeIn('#email + .invalid-feedback', 'The specified domain is not available.')
+ ->assertSeeIn('#name + .invalid-feedback', 'The name may not be greater than 191 characters.')
->assertSeeIn('#members + .invalid-feedback', 'At least one recipient is required.')
+ ->assertFocused('#name')
->assertToast(Toast::TYPE_ERROR, 'Form validation error')
// Test successful group creation
+ ->type('#name', 'Test Group')
->type('#email', 'group-test@kolab.org')
->with(new ListInput('#members'), function (Browser $browser) {
$browser->addListEntry('test1@gmail.com')
@@ -161,17 +169,20 @@
->assertElementsCount('@table tbody tr', 1);
// Test group update
- $browser->click('@table tr:nth-child(1) a')
+ $browser->click('@table tr:nth-child(1) td:first-child a')
->on(new DistlistInfo())
->assertSeeIn('#distlist-info .card-title', 'Distribution list')
->with('@general', function (Browser $browser) {
// Assert form content
- $browser->assertSeeIn('div.row:nth-child(1) label', 'Status')
+ $browser->assertFocused('#name')
+ ->assertSeeIn('div.row:nth-child(1) label', 'Status')
->assertSeeIn('div.row:nth-child(1) span.text-danger', 'Not Ready')
- ->assertSeeIn('div.row:nth-child(2) label', 'Email')
- ->assertValue('div.row:nth-child(2) input[type=text]:disabled', 'group-test@kolab.org')
- ->assertSeeIn('div.row:nth-child(3) label', 'Recipients')
- ->assertVisible('div.row:nth-child(3) .list-input')
+ ->assertSeeIn('div.row:nth-child(2) label', 'Name')
+ ->assertValue('div.row:nth-child(2) input[type=text]', 'Test Group')
+ ->assertSeeIn('div.row:nth-child(3) label', 'Email')
+ ->assertValue('div.row:nth-child(3) input[type=text]:disabled', 'group-test@kolab.org')
+ ->assertSeeIn('div.row:nth-child(4) label', 'Recipients')
+ ->assertVisible('div.row:nth-child(4) .list-input')
->with(new ListInput('#members'), function (Browser $browser) {
$browser->assertListInputValue(['test1@gmail.com', 'test2@gmail.com'])
->assertValue('@input', '');
@@ -201,7 +212,7 @@
$this->assertSame(['test1@gmail.com'], $group->members);
// Test group deletion
- $browser->click('@table tr:nth-child(1) a')
+ $browser->click('@table tr:nth-child(1) td:first-child a')
->on(new DistlistInfo())
->assertSeeIn('button.button-delete', 'Delete list')
->click('button.button-delete')
diff --git a/src/tests/Browser/Reseller/DistlistTest.php b/src/tests/Browser/Reseller/DistlistTest.php
--- a/src/tests/Browser/Reseller/DistlistTest.php
+++ b/src/tests/Browser/Reseller/DistlistTest.php
@@ -59,7 +59,7 @@
$this->browse(function (Browser $browser) {
$user = $this->getTestUser('john@kolab.org');
- $group = $this->getTestGroup('group-test@kolab.org');
+ $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']);
$group->assignToWallet($user->wallets->first());
$group->members = ['test1@gmail.com', 'test2@gmail.com'];
$group->save();
@@ -80,14 +80,16 @@
->on($distlist_page)
->assertSeeIn('@distlist-info .card-title', $group->email)
->with('@distlist-info form', function (Browser $browser) use ($group) {
- $browser->assertElementsCount('.row', 3)
+ $browser->assertElementsCount('.row', 4)
->assertSeeIn('.row:nth-child(1) label', 'ID (Created)')
->assertSeeIn('.row:nth-child(1) #distlistid', "{$group->id} ({$group->created_at})")
->assertSeeIn('.row:nth-child(2) label', 'Status')
->assertSeeIn('.row:nth-child(2) #status.text-danger', 'Not Ready')
- ->assertSeeIn('.row:nth-child(3) label', 'Recipients')
- ->assertSeeIn('.row:nth-child(3) #members', $group->members[0])
- ->assertSeeIn('.row:nth-child(3) #members', $group->members[1]);
+ ->assertSeeIn('.row:nth-child(3) label', 'Name')
+ ->assertSeeIn('.row:nth-child(3) #name', $group->name)
+ ->assertSeeIn('.row:nth-child(4) label', 'Recipients')
+ ->assertSeeIn('.row:nth-child(4) #members', $group->members[0])
+ ->assertSeeIn('.row:nth-child(4) #members', $group->members[1]);
})
->assertElementsCount('ul.nav-tabs', 1)
->assertSeeIn('ul.nav-tabs .nav-link', 'Settings')
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
@@ -183,7 +183,7 @@
$wallet->discount()->associate($discount);
$wallet->debit(2010);
$wallet->save();
- $group = $this->getTestGroup('group-test@kolab.org');
+ $group = $this->getTestGroup('group-test@kolab.org', ['name' => 'Test Group']);
$group->assignToWallet($john->wallets->first());
// Click the managed-by link on Jack's page
@@ -263,8 +263,9 @@
->click('@nav #tab-distlists')
->with('@user-distlists table', function (Browser $browser) {
$browser->assertElementsCount('tbody tr', 1)
- ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'group-test@kolab.org')
+ ->assertSeeIn('tbody tr:nth-child(1) td:first-child a', 'Test Group')
->assertVisible('tbody tr:nth-child(1) td:first-child svg.text-danger')
+ ->assertSeeIn('tbody tr:nth-child(1) td:last-child a', 'group-test@kolab.org')
->assertMissing('tfoot');
});
diff --git a/src/tests/Feature/Backends/LDAPTest.php b/src/tests/Feature/Backends/LDAPTest.php
--- a/src/tests/Feature/Backends/LDAPTest.php
+++ b/src/tests/Feature/Backends/LDAPTest.php
@@ -174,7 +174,9 @@
$this->assertSame(['member3@testldap.com'], $group->fresh()->members);
// Update members (add non-existing local member, expect it to be aot-removed from the group)
+ // Update group name and sender_policy
$group->members = ['member3@testldap.com', 'member-local@kolab.org'];
+ $group->name = 'Te(=ść)1';
$group->save();
$group->setSetting('sender_policy', null);
@@ -183,6 +185,8 @@
// TODO: Should we force this to be always an array?
$expected['uniquemember'] = 'uid=member3@testldap.com,ou=People,ou=kolab.org,' . $root_dn;
$expected['kolaballowsmtpsender'] = null;
+ $expected['dn'] = 'cn=Te(\\3dść)1,ou=Groups,ou=kolab.org,' . $root_dn;
+ $expected['cn'] = 'Te(=ść)1';
$ldap_group = LDAP::getGroup($group->email);
@@ -314,6 +318,25 @@
$this->assertSame(null, LDAP::getUser($user->email));
}
+ /**
+ * Test handling errors on a group creation
+ *
+ * @group ldap
+ */
+ public function testCreateGroupException(): void
+ {
+ $this->expectException(\Exception::class);
+ $this->expectExceptionMessageMatches('/Failed to create group/');
+
+ $group = new Group([
+ 'name' => 'test',
+ 'email' => 'test@testldap.com',
+ 'status' => Group::STATUS_NEW | Group::STATUS_ACTIVE,
+ ]);
+
+ LDAP::createGroup($group);
+ }
+
/**
* Test handling errors on user creation
*
@@ -362,6 +385,7 @@
$this->expectExceptionMessageMatches('/group not found/');
$group = new Group([
+ 'name' => 'test',
'email' => 'test@testldap.com',
'status' => Group::STATUS_NEW | Group::STATUS_ACTIVE,
]);
diff --git a/src/tests/Feature/Controller/Admin/GroupsTest.php b/src/tests/Feature/Controller/Admin/GroupsTest.php
--- a/src/tests/Feature/Controller/Admin/GroupsTest.php
+++ b/src/tests/Feature/Controller/Admin/GroupsTest.php
@@ -80,6 +80,7 @@
$this->assertSame(1, $json['count']);
$this->assertCount(1, $json['list']);
$this->assertSame($group->email, $json['list'][0]['email']);
+ $this->assertSame($group->name, $json['list'][0]['name']);
// Search by owner (Ned is a controller on John's wallets,
// here we expect only domains assigned to Ned's wallet(s))
@@ -114,6 +115,7 @@
$this->assertEquals($group->id, $json['id']);
$this->assertEquals($group->email, $json['email']);
+ $this->assertEquals($group->name, $json['name']);
$this->assertEquals($group->status, $json['status']);
}
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
@@ -18,6 +18,7 @@
parent::setUp();
$this->deleteTestGroup('group-test@kolab.org');
+ $this->deleteTestGroup('group-test2@kolab.org');
}
/**
@@ -26,6 +27,7 @@
public function tearDown(): void
{
$this->deleteTestGroup('group-test@kolab.org');
+ $this->deleteTestGroup('group-test2@kolab.org');
parent::tearDown();
}
@@ -102,6 +104,7 @@
$this->assertCount(1, $json);
$this->assertSame($group->id, $json[0]['id']);
$this->assertSame($group->email, $json[0]['email']);
+ $this->assertSame($group->name, $json[0]['name']);
$this->assertArrayHasKey('isDeleted', $json[0]);
$this->assertArrayHasKey('isSuspended', $json[0]);
$this->assertArrayHasKey('isActive', $json[0]);
@@ -227,6 +230,7 @@
$this->assertSame($group->id, $json['id']);
$this->assertSame($group->email, $json['email']);
+ $this->assertSame($group->name, $json['name']);
$this->assertSame($group->members, $json['members']);
$this->assertTrue(!empty($json['statusInfo']));
$this->assertArrayHasKey('isDeleted', $json);
@@ -385,9 +389,12 @@
$this->assertSame('error', $json['status']);
$this->assertSame("The email field is required.", $json['errors']['email']);
+ $this->assertSame("At least one recipient is required.", $json['errors']['members']);
+ $this->assertSame("The name field is required.", $json['errors']['name'][0]);
$this->assertCount(2, $json);
+ $this->assertCount(3, $json['errors']);
- // Test missing members
+ // Test missing members and name
$post = ['email' => 'group-test@kolab.org'];
$response = $this->actingAs($john)->post("/api/v4/groups", $post);
$json = $response->json();
@@ -396,10 +403,12 @@
$this->assertSame('error', $json['status']);
$this->assertSame("At least one recipient is required.", $json['errors']['members']);
+ $this->assertSame("The name field is required.", $json['errors']['name'][0]);
$this->assertCount(2, $json);
+ $this->assertCount(2, $json['errors']);
- // Test invalid email
- $post = ['email' => 'invalid'];
+ // Test invalid email and too long name
+ $post = ['email' => 'invalid', 'name' => str_repeat('A', 192)];
$response = $this->actingAs($john)->post("/api/v4/groups", $post);
$response->assertStatus(422);
@@ -407,10 +416,13 @@
$this->assertSame('error', $json['status']);
$this->assertCount(2, $json);
- $this->assertSame('The specified email is invalid.', $json['errors']['email']);
+ $this->assertSame("The specified email is invalid.", $json['errors']['email']);
+ $this->assertSame("The name may not be greater than 191 characters.", $json['errors']['name'][0]);
+ $this->assertCount(3, $json['errors']);
// Test successful group creation
$post = [
+ 'name' => 'Test Group',
'email' => 'group-test@kolab.org',
'members' => ['test1@domain.tld', 'test2@domain.tld']
];
@@ -429,6 +441,19 @@
$this->assertSame($post['email'], $group->email);
$this->assertSame($post['members'], $group->members);
$this->assertTrue($john->groups()->get()->contains($group));
+
+ // Group name must be unique within a domain
+ $post['email'] = 'group-test2@kolab.org';
+ $post['members'] = ['test1@domain.tld'];
+
+ $response = $this->actingAs($john)->post("/api/v4/groups", $post);
+ $response->assertStatus(422);
+ $json = $response->json();
+
+ $this->assertSame('error', $json['status']);
+ $this->assertCount(2, $json);
+ $this->assertCount(1, $json['errors']);
+ $this->assertSame("The specified name is not available.", $json['errors']['name'][0]);
}
/**
@@ -474,8 +499,9 @@
$this->assertCount(2, $json);
$this->assertSame('The specified email address is invalid.', $json['errors']['members'][1]);
- // Valid data - members changed
+ // Valid data - members and name changed
$post = [
+ 'name' => 'Test Gr',
'members' => ['member1@test.domain', 'member2@test.domain']
];
@@ -487,7 +513,11 @@
$this->assertSame('success', $json['status']);
$this->assertSame("Distribution list updated successfully.", $json['message']);
$this->assertCount(2, $json);
- $this->assertSame($group->fresh()->members, $post['members']);
+
+ $group->refresh();
+
+ $this->assertSame($post['name'], $group->name);
+ $this->assertSame($post['members'], $group->members);
}
/**
diff --git a/src/tests/Feature/Controller/Reseller/GroupsTest.php b/src/tests/Feature/Controller/Reseller/GroupsTest.php
--- a/src/tests/Feature/Controller/Reseller/GroupsTest.php
+++ b/src/tests/Feature/Controller/Reseller/GroupsTest.php
@@ -86,6 +86,7 @@
$this->assertSame(1, $json['count']);
$this->assertCount(1, $json['list']);
$this->assertSame($group->email, $json['list'][0]['email']);
+ $this->assertSame($group->name, $json['list'][0]['name']);
// Search by owner (Ned is a controller on John's wallets,
// here we expect only domains assigned to Ned's wallet(s))
@@ -144,6 +145,7 @@
$this->assertEquals($group->id, $json['id']);
$this->assertEquals($group->email, $json['email']);
+ $this->assertEquals($group->name, $json['name']);
$this->assertEquals($group->status, $json['status']);
}
diff --git a/src/tests/Feature/GroupTest.php b/src/tests/Feature/GroupTest.php
--- a/src/tests/Feature/GroupTest.php
+++ b/src/tests/Feature/GroupTest.php
@@ -65,65 +65,6 @@
$this->assertSame('["test","-"]', $group->getSetting('sender_policy'));
$this->assertSame([], $result);
}
-
- /**
- * Test group status assignment and is*() methods
- */
- public function testStatus(): void
- {
- $group = new Group();
-
- $this->assertSame(false, $group->isNew());
- $this->assertSame(false, $group->isActive());
- $this->assertSame(false, $group->isDeleted());
- $this->assertSame(false, $group->isLdapReady());
- $this->assertSame(false, $group->isSuspended());
-
- $group->status = Group::STATUS_NEW;
-
- $this->assertSame(true, $group->isNew());
- $this->assertSame(false, $group->isActive());
- $this->assertSame(false, $group->isDeleted());
- $this->assertSame(false, $group->isLdapReady());
- $this->assertSame(false, $group->isSuspended());
-
- $group->status |= Group::STATUS_ACTIVE;
-
- $this->assertSame(true, $group->isNew());
- $this->assertSame(true, $group->isActive());
- $this->assertSame(false, $group->isDeleted());
- $this->assertSame(false, $group->isLdapReady());
- $this->assertSame(false, $group->isSuspended());
-
- $group->status |= Group::STATUS_LDAP_READY;
-
- $this->assertSame(true, $group->isNew());
- $this->assertSame(true, $group->isActive());
- $this->assertSame(false, $group->isDeleted());
- $this->assertSame(true, $group->isLdapReady());
- $this->assertSame(false, $group->isSuspended());
-
- $group->status |= Group::STATUS_DELETED;
-
- $this->assertSame(true, $group->isNew());
- $this->assertSame(true, $group->isActive());
- $this->assertSame(true, $group->isDeleted());
- $this->assertSame(true, $group->isLdapReady());
- $this->assertSame(false, $group->isSuspended());
-
- $group->status |= Group::STATUS_SUSPENDED;
-
- $this->assertSame(true, $group->isNew());
- $this->assertSame(true, $group->isActive());
- $this->assertSame(true, $group->isDeleted());
- $this->assertSame(true, $group->isLdapReady());
- $this->assertSame(true, $group->isSuspended());
-
- // Unknown status value
- $this->expectException(\Exception::class);
- $group->status = 111;
- }
-
/**
* Test creating a group
*/
@@ -134,6 +75,7 @@
$group = Group::create(['email' => 'GROUP-test@kolabnow.com']);
$this->assertSame('group-test@kolabnow.com', $group->email);
+ $this->assertSame('group-test', $group->name);
$this->assertMatchesRegularExpression('/^[0-9]{1,20}$/', $group->id);
$this->assertSame([], $group->members);
$this->assertTrue($group->isNew());
@@ -271,6 +213,64 @@
$this->assertSame(null, $group->fresh()->getSetting('sender_policy'));
}
+ /**
+ * Test group status assignment and is*() methods
+ */
+ public function testStatus(): void
+ {
+ $group = new Group();
+
+ $this->assertSame(false, $group->isNew());
+ $this->assertSame(false, $group->isActive());
+ $this->assertSame(false, $group->isDeleted());
+ $this->assertSame(false, $group->isLdapReady());
+ $this->assertSame(false, $group->isSuspended());
+
+ $group->status = Group::STATUS_NEW;
+
+ $this->assertSame(true, $group->isNew());
+ $this->assertSame(false, $group->isActive());
+ $this->assertSame(false, $group->isDeleted());
+ $this->assertSame(false, $group->isLdapReady());
+ $this->assertSame(false, $group->isSuspended());
+
+ $group->status |= Group::STATUS_ACTIVE;
+
+ $this->assertSame(true, $group->isNew());
+ $this->assertSame(true, $group->isActive());
+ $this->assertSame(false, $group->isDeleted());
+ $this->assertSame(false, $group->isLdapReady());
+ $this->assertSame(false, $group->isSuspended());
+
+ $group->status |= Group::STATUS_LDAP_READY;
+
+ $this->assertSame(true, $group->isNew());
+ $this->assertSame(true, $group->isActive());
+ $this->assertSame(false, $group->isDeleted());
+ $this->assertSame(true, $group->isLdapReady());
+ $this->assertSame(false, $group->isSuspended());
+
+ $group->status |= Group::STATUS_DELETED;
+
+ $this->assertSame(true, $group->isNew());
+ $this->assertSame(true, $group->isActive());
+ $this->assertSame(true, $group->isDeleted());
+ $this->assertSame(true, $group->isLdapReady());
+ $this->assertSame(false, $group->isSuspended());
+
+ $group->status |= Group::STATUS_SUSPENDED;
+
+ $this->assertSame(true, $group->isNew());
+ $this->assertSame(true, $group->isActive());
+ $this->assertSame(true, $group->isDeleted());
+ $this->assertSame(true, $group->isLdapReady());
+ $this->assertSame(true, $group->isSuspended());
+
+ // Unknown status value
+ $this->expectException(\Exception::class);
+ $group->status = 111;
+ }
+
/**
* Tests for Group::suspend()
*/
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Apr 3, 5:02 AM (22 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18822651
Default Alt Text
D3014.1775192522.diff (59 KB)
Attached To
Mode
D3014: Groups: Display name
Attached
Detach File
Event Timeline