diff options
author | Christoph Wurst <ChristophWurst@users.noreply.github.com> | 2018-11-23 11:58:43 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-23 11:58:43 +0300 |
commit | 1cdb4c131f3eb0741b9746456a897026f48d280c (patch) | |
tree | 7dbdee4ffe06fae8179c896eb8ed5c48b706df38 | |
parent | d3f1d5a23ce59fc4b47800817d9bdac2bd64e653 (diff) | |
parent | 897742cb7545e3021747f170e612062cad5ceedd (diff) |
Merge pull request #1250 from nextcloud/enhancement/vue-recipient-multiselectv0.12.0-alpha-5
Use multiselects for the recipient selection
-rwxr-xr-x | css/mail.scss | 129 | ||||
-rw-r--r-- | lib/Service/AutoCompletion/AutoCompleteService.php | 2 | ||||
-rw-r--r-- | lib/Service/ContactsIntegration.php | 5 | ||||
-rw-r--r-- | lib/Service/GroupsIntegration.php | 2 | ||||
-rw-r--r-- | package-lock.json | 5 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/components/Address.vue | 22 | ||||
-rw-r--r-- | src/components/Composer.vue | 333 | ||||
-rw-r--r-- | src/service/AutocompleteService.js | 32 | ||||
-rw-r--r-- | src/store.js | 1 | ||||
-rw-r--r-- | src/tests/unit/components/Address.spec.js | 32 | ||||
-rw-r--r-- | tests/Service/Autocompletion/AutoCompleteServiceTest.php | 14 | ||||
-rw-r--r-- | tests/Service/ContactsIntegrationTest.php | 8 | ||||
-rw-r--r-- | tests/Service/GroupsIntegrationTest.php | 2 |
14 files changed, 370 insertions, 218 deletions
diff --git a/css/mail.scss b/css/mail.scss index a2cfb193c..fba7b0822 100755 --- a/css/mail.scss +++ b/css/mail.scss @@ -18,14 +18,6 @@ height: 100%; } -.mail-account { - max-width: 100%; - height: 44px; - padding-left: 30px; - background-color: transparent; - border: none; -} - .folders .ui-droppable-active { background-color: rgb(240, 240, 240); } @@ -251,127 +243,6 @@ background-image: var(--icon-star-000) !important; } -.message-composer { - margin: 0; - margin-bottom: 10px; /* line up with the send button */ - z-index: 100; -} -#reply-composer .message-composer { - margin: 0; -} -.composer-fields { - position: relative; -} -input.to, -input.cc, -input.bcc, -input.subject, -textarea.message-body { - width: 100%; -} -input.to, -input.cc, -input.bcc, -input.subject, -textarea.message-body { - padding: 12px; - padding-left: 64px; - border: 1px solid #eee; - border-right: none; - border-left: none; - border-radius: 0; - margin: 0; -} -input.to { - padding-right: 60px; /* for cc-bcc-toggle */ -} -input.cc, -input.bcc, -input.subject, -textarea.message-body { - border-top: none; -} -input.subject { - font-size: 20px; - font-weight: 300; -} -input.subject, -textarea.message-body { - padding-left: 30px; -} -textarea.message-body { - min-height: 300px; - resize: none; - padding-right: 25%; -} - -#content .message-composer .composer-fields .noreply-box { - margin-top: 0; - background: #fdffc3; - padding-left: 64px; -} - -.composer-cc-bcc { - position: relative; -} -label.to-label, -label.cc-label, -label.bcc-label { - position: absolute; - left: 0; - top: 0; - padding: 12px; - padding-left: 30px; - cursor: text; - opacity: .5; -} -label.bcc-label { - top: initial; - bottom: 0; -} -.composer-cc-bcc-toggle { - position: absolute; - right: 0; - padding: 12px; -} -textarea.reply { - min-height: 100px; -} -input.submit-message, -.submit-message-wrapper { - position: fixed; - bottom: 10px; - right: 15px; -} -.submit-message-wrapper { - position: fixed; - height: 36px; - width: 60px; -} - -.submit-message-wrapper-inside { - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - z-index: 102; -} -#reply-msg { - float: left; -} - -/* extra button styles */ -.submit-message.send, -#mail_new_attachment, -#forward-button { - padding: 12px; -} -.new-message-attachments, -#forward-button { - margin-left: 30px; -} - .unread { font-weight: bold; } diff --git a/lib/Service/AutoCompletion/AutoCompleteService.php b/lib/Service/AutoCompletion/AutoCompleteService.php index a8df813ec..ddf8a88e2 100644 --- a/lib/Service/AutoCompletion/AutoCompleteService.php +++ b/lib/Service/AutoCompletion/AutoCompleteService.php @@ -55,7 +55,7 @@ class AutoCompleteService { return [ 'id' => $address->getId(), 'label' => $address->getDisplayName(), - 'value' => '"' . $address->getDisplayName() . '" <' . $address->getEmail() . '>', + 'email' => $address->getEmail(), ]; }, $fromCollector); diff --git a/lib/Service/ContactsIntegration.php b/lib/Service/ContactsIntegration.php index 3c23d9ed5..fd5db798d 100644 --- a/lib/Service/ContactsIntegration.php +++ b/lib/Service/ContactsIntegration.php @@ -75,10 +75,13 @@ class ContactsIntegration { // loop through all email addresses of this contact foreach ($email as $e) { + if ($e === '') { + continue; + } $receivers[] = [ 'id' => $id, 'label' => "$fn ($e)", - 'value' => "\"$fn\" <$e>", + 'email' => $e, 'photo' => $photo, ]; } diff --git a/lib/Service/GroupsIntegration.php b/lib/Service/GroupsIntegration.php index 8eb2911a1..6f761a5fa 100644 --- a/lib/Service/GroupsIntegration.php +++ b/lib/Service/GroupsIntegration.php @@ -54,7 +54,7 @@ class GroupsIntegration { $receivers[] = [ 'id' => $gid, 'label' => $g['name'] . " (" . $gs->getNamespace() . ")", - 'value' => $gid, + 'email' => $gid, 'photo' => null, ]; } diff --git a/package-lock.json b/package-lock.json index e10ee5949..7df68a009 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3051,6 +3051,11 @@ "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", "dev": true }, + "debounce-promise": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.0.tgz", + "integrity": "sha1-JQNfS0UBe9Uae++LO9n2QB3EdCM=" + }, "debug": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", diff --git a/package.json b/package.json index f0629a885..9bc258e92 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@vue/babel-preset-app": "^3.1.1", "color-convert": "^1.9.3", "davclient.js": "^0.1.0", + "debounce-promise": "^3.1.0", "exports-loader": "^0.7.0", "html-to-text": "^4.0.0", "ical.js": "^1.3.0", diff --git a/src/components/Address.vue b/src/components/Address.vue index e36956704..a02bd9152 100644 --- a/src/components/Address.vue +++ b/src/components/Address.vue @@ -1,6 +1,8 @@ <template> - <span :title="email" - v-tooltip.bottom="label">{{ label }}</span> + <router-link :to="newMessageRoute" + exact + v-tooltip.bottom="label">{{ label }} + </router-link> </template> <script> @@ -9,6 +11,20 @@ props: [ 'email', 'label', - ] + ], + computed: { + newMessageRoute () { + return { + name: 'message', + params: { + accountId: this.$route.params.accountId, + folderId: this.$route.params.folderId, + messageUid: 'new' + }, query: { + to: this.email + } + } + } + } } </script> diff --git a/src/components/Composer.vue b/src/components/Composer.vue index 853a4e89a..04331723c 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -1,57 +1,84 @@ <template> <div v-if="state === STATES.EDITING" class="message-composer"> - <select class="mail-account" - v-model="selectedAlias" - v-on:keyup="onInputChanged"> - <option v-for="alias in aliases" :value="alias.id"> - {{ t('mail', 'from') }} {{alias.name}} <{{alias.emailAddress}}> - </option> - </select> + <div class="composer-fields mail-account"> + <label class="to-label transparency" for="from"> + {{ t('mail', 'from') }} + </label> + <Multiselect :options="aliases" + id="from" + v-model="selectedAlias" + @keyup="onInputChanged" + label="name" track-by="id" + :customLabel="formatAliases" /> + </div> + <div class="composer-fields"> + <a href="#" + class="composer-cc-bcc-toggle transparency" + :class="{ hidden: hasCC }"> + {{ t ('mail', '+ cc/bcc') }} + </a> + <label class="to-label transparency" for="to"> + {{ t('mail', 'to') }} + </label> + <Multiselect :options="selectableRecipients" + id="to" + @keyup="onInputChanged" + :taggable="true" + @tag="onNewToAddr" + @search-change="onAutocomplete" + v-model="selectTo" + label="label" + track-by="email" + :multiple="true" /> + </div> + <div class="composer-fields" + v-if="hasCC"> + <label for="cc" class="cc-label transparency"> + {{ t('mail', 'cc') }} + </label> + <Multiselect :options="selectableRecipients" + id="cc" + @keyup="onInputChanged" + :taggable="true" + @tag="onNewCcAddr" + @search-change="onAutocomplete" + v-model="selectCc" + label="label" track-by="email" + :multiple="true" /> + </div> + <div class="composer-fields" + v-if="hasCC"> + <label for="bcc" class="bcc-label transparency"> + {{ t('mail', 'bcc') }} + </label> + <Multiselect :options="selectableRecipients" + id="bcc" + @keyup="onInputChanged" + :taggable="true" + @tag="onNewBccAddr" + @search-change="onAutocomplete" + v-model="selectBcc" + label="label" track-by="email" + :multiple="true" /> + </div> <div class="composer-fields"> - <a href="#" :class="{ - 'composer-cc-bcc-toggle': true, - transparency: true, - hidden: hasCC, - }">{{ t ('mail', '+ cc/bcc') }}</a> + <label for="subject" class="subject-label transparency"> + {{ t('mail', 'Subject') }} + </label> <input type="text" - id="to" - v-model="toVal" - v-on:keyup="onInputChanged" - class="to recipient-autocomplete"/> - <label class="to-label transparency" for="to">{{ t('mail', 'to') - }}</label> - <div :class="{ 'composer-cc-bcc': true, hidden: !hasCC }"> - <input type="text" - id="cc" - class="cc recipient-autocomplete" - v-model="ccVal" - v-on:keyup="onInputChanged"> - <label for="cc" class="cc-label transparency"> - {{ t('mail', 'cc') }} - </label> - <input type="text" - id="bcc" - class="bcc recipient-autocomplete" - v-model="bccVal" - v-on:keyup="onInputChanged"> - <label for="bcc" class="bcc-label transparency"> - {{ t('mail', 'bcc') }} - </label> - </div> - - <div v-if="noReply" - class="warning noreply-box"> - {{ t('mail', 'Note that the mail came from a noreply address so your reply will probably not be read.') }} - </div> - <input v-else - type="text" + id="subject" name="subject" v-model="subjectVal" v-on:keyup="onInputChanged" class="subject" autocomplete="off" :placeholder="t('mail', 'Subject')"/> - + </div> + <div v-if="noReply" + class="warning noreply-box"> + {{ t('mail', 'Note that the mail came from a noreply address so your reply will probably not be read.') }} + </div> + <div class="composer-fields"> <textarea name="body" class="message-body" v-autosize @@ -93,11 +120,16 @@ <script> import _ from 'lodash' import Autosize from 'vue-autosize' + import debouncePromise from 'debounce-promise' + import {Multiselect} from 'nextcloud-vue' import Vue from 'vue' + import {findRecipient} from '../service/AutocompleteService' import Loading from './Loading' import ComposerAttachments from './ComposerAttachments' + const debouncedSearch = debouncePromise(findRecipient, 500) + Vue.use(Autosize) const STATES = Object.seal({ @@ -112,6 +144,7 @@ components: { ComposerAttachments, Loading, + Multiselect, }, props: { replyTo: { @@ -153,9 +186,8 @@ return { hasCC: true, selectedAlias: -1, // Fixed in `beforeMount` - toVal: this.addressListPlain(this.to), - ccVal: this.addressListPlain(this.cc), - bccVal: this.addressListPlain(this.bcc), + autocompleteRecipients: this.to.concat(this.cc).concat(this.bcc), + newRecipients: [], subjectVal: this.subject, bodyVal: this.body, attachments: [], @@ -169,40 +201,53 @@ saveDraftDebounced: _.debounce(this.saveDraft, 700), state: STATES.EDITING, errorText: undefined, - STATES + STATES, + selectTo: this.to, + selectCc: this.cc, + selectBcc: this.bcc, } }, beforeMount () { - this.selectedAlias = this.fromAccount || this.aliases[0].id + if (this.fromAccount) { + this.selectedAlias = this.aliases.find(alias => alias.id === this.fromAccount) + } + this.selectedAlias = this.aliases[0] }, computed: { aliases () { return this.$store.getters.getAccounts() .filter(a => !a.isUnified) }, + selectableRecipients () { + return this.newRecipients.concat(this.autocompleteRecipients) + }, isReply () { return !_.isUndefined(this.replyTo) } }, methods: { - addressListPlain (addresses) { - return addresses - .map(addr => { - if (addr.label && addr.label !== addr.email) { - return `"${addr.label}" <${addr.email}>` - } else { - return addr.email - } - }) - .join('; ') + recipientToRfc822 (recipient) { + if (recipient.email === recipient.label) { + // From mailto or sender without proper label + return recipient.email + } else if (recipient.label === '') { + // Invalid label + return recipient.email + } else if (recipient.email.search(/^[a-zA-Z]+:[a-zA-Z]+$/) === 0) { + // Group integration + return recipient.email + } else { + // Proper layout with label + return `"${recipient.label}" <${recipient.email}>` + } }, getMessageData () { return uid => { return { - account: this.selectedAlias, - to: this.toVal, - cc: this.ccVal, - bcc: this.bccVal, + account: this.selectedAlias.id, + to: this.selectTo.map(this.recipientToRfc822).join(', '), + cc: this.selectCc.map(this.recipientToRfc822).join(', '), + bcc: this.selectBcc.map(this.recipientToRfc822).join(', '), draftUID: uid, subject: this.subjectVal, body: this.bodyVal, @@ -225,6 +270,35 @@ onInputChanged () { this.saveDraftDebounced(this.getMessageData()) }, + onAutocomplete (term) { + if (_.isUndefined(term) || term === '') { + return + } + debouncedSearch(term) + .then(results => { + this.autocompleteRecipients = _.uniqBy( + this.autocompleteRecipients.concat(results), + 'email', + ) + }) + }, + onNewToAddr (addr) { + this.onNewAddr(addr, this.selectTo) + }, + onNewCcAddr (addr) { + this.onNewAddr(addr, this.selectCc) + }, + onNewBccAddr (addr) { + this.onNewAddr(addr, this.selectBcc) + }, + onNewAddr (addr, list) { + const res = { + label: addr, // TODO: parse if possible + email: addr, // TODO: parse if possible + } + this.newRecipients.push(res) + list.push(res) + }, onSend () { this.state = STATES.SENDING @@ -242,23 +316,148 @@ }) }, reset () { - this.toVal = '' - this.ccVal = '' - this.bccVal = '' + this.selectTo = [] + this.selectCc = [] + this.selectBcc = [] this.subjectVal = '' this.bodyVal = '' this.attachments = [] this.errorText = undefined this.state = STATES.EDITING + }, + /** + * Format aliases for the Multiselect + * @returns {string} + */ + formatAliases(alias) { + return `${alias.name} <${alias.emailAddress}>` } } } </script> -<style> +<style scoped> + .message-composer { + margin: 0; + margin-bottom: 10px; /* line up with the send button */ + z-index: 100; + } + + #reply-composer .message-composer { + margin: 0; + } + + .composer-fields.mail-account > .multiselect { + max-width: none; + min-height: auto; + width: 350px; + } + + .composer-fields { + display: flex; + align-items: center; + border-top: 1px solid #eee; + padding-right: 30px; + } + .composer-fields .multiselect, + .composer-fields input, + .composer-fields textarea { + flex-grow: 1; + max-width: none; + border: none; + border-radius: 0px; + } + .noreply-box { + margin-top: 0; + background: #fdffc3; + padding-left: 64px; + } + + #to, + #cc, + #bcc, + input.subject, + textarea.message-body { + padding: 12px; + margin: 0; + } + + #to { + padding-right: 60px; /* for cc-bcc-toggle */ + } + + input.cc, + input.bcc, + input.subject, + textarea.message-body { + border-top: none; + } + + input.subject { + font-size: 20px; + font-weight: 300; + } + + textarea.message-body { + min-height: 300px; + resize: none; + padding-right: 25%; + } + #draft-status { padding: 5px; opacity: 0.5; font-size: small; } + + label.to-label, + label.cc-label, + label.bcc-label, + label.subject-label { + padding: 12px; + padding-left: 30px; + cursor: text; + opacity: .5; + width: 90px; + text-align: right; + } + + label.bcc-label { + top: initial; + bottom: 0; + } + + .composer-cc-bcc-toggle { + position: absolute; + right: 0; + padding: 12px; + } + + textarea.reply { + min-height: 100px; + } + + input.submit-message, + .submit-message-wrapper { + position: fixed; + bottom: 10px; + right: 15px; + } + + .submit-message-wrapper { + position: fixed; + height: 36px; + width: 60px; + } + + .submit-message.send { + padding: 12px; + } + +</style> + +<style> + .multiselect .multiselect__tags { + border: none !important; + } </style> diff --git a/src/service/AutocompleteService.js b/src/service/AutocompleteService.js new file mode 100644 index 000000000..ab825d4db --- /dev/null +++ b/src/service/AutocompleteService.js @@ -0,0 +1,32 @@ +/* + * @copyright 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2018 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +import Axios from 'nextcloud-axios' +import {generateUrl} from 'nextcloud-server/dist/router' + +export const findRecipient = term => { + const url = generateUrl('/apps/mail/api/autoComplete?term={term}', { + term, + }) + + return Axios.get(url) + .then(resp => resp.data) +} diff --git a/src/store.js b/src/store.js index 88dc4e1d0..7c272447f 100644 --- a/src/store.js +++ b/src/store.js @@ -583,6 +583,7 @@ export default new Vuex.Store({ }, envelopes: {}, messages: {}, + autocompleteEntries: [], }, getters, mutations, diff --git a/src/tests/unit/components/Address.spec.js b/src/tests/unit/components/Address.spec.js index b4b8ad573..1241ebd69 100644 --- a/src/tests/unit/components/Address.spec.js +++ b/src/tests/unit/components/Address.spec.js @@ -1,12 +1,36 @@ -import sinon from 'sinon' import {shallowMount} from '@vue/test-utils' -import Address from '../../../components/Address.vue'; +import Address from '../../../components/Address.vue' describe('Address', () => { + let $route + + beforeEach(() => { + $route = { + params: {}, + query: {}, + } + }) + it('renders', () => { - const addr = shallowMount(Address) + $route.params = { + accountId: 1, + folderId: 'folder1', + } + const addr = shallowMount(Address, { + mocks: { + $route + }, + propsData: { + label: 'Test User', + email: 'user@domain.com', + }, + }) - expect(addr.contains('span')).to.be.true + expect(addr.contains('router-link')).to.be.true + expect(addr.vm.newMessageRoute.name).to.equal('message') + expect(addr.vm.newMessageRoute.params.accountId).to.equal(1) + expect(addr.vm.newMessageRoute.params.folderId).to.equal('folder1') + expect(addr.vm.newMessageRoute.query.to).to.equal('user@domain.com') }); }); diff --git a/tests/Service/Autocompletion/AutoCompleteServiceTest.php b/tests/Service/Autocompletion/AutoCompleteServiceTest.php index c75b504b0..bd95fbde3 100644 --- a/tests/Service/Autocompletion/AutoCompleteServiceTest.php +++ b/tests/Service/Autocompletion/AutoCompleteServiceTest.php @@ -52,8 +52,8 @@ class AutoCompleteServiceTest extends TestCase { $term = 'jo'; $contactsResult = [ - ['id' => 12, 'label' => '"john doe" <john@doe.cz>', 'value' => '"john doe" <john@doe.cz>'], - ['id' => 13, 'label' => '"joe doe" <joe@doe.se>', 'value' => '"joe doe" <joe@doe.se>'], + ['id' => 12, 'label' => '"john doe" <john@doe.cz>', 'email' => 'john@doe.cz'], + ['id' => 13, 'label' => '"joe doe" <joe@doe.se>', 'email' => 'joe@doe.se'], ]; $john = new CollectedAddress(); $john->setId(1234); @@ -65,7 +65,7 @@ class AutoCompleteServiceTest extends TestCase { ]; $groupsResult = [ - ['id' => 20, 'label' => 'Journalists', 'value' => 'Journalists'] + ['id' => 20, 'label' => 'Journalists', 'email' => 'Journalists'] ]; $this->contactsIntegration->expects($this->once()) @@ -84,10 +84,10 @@ class AutoCompleteServiceTest extends TestCase { $response = $this->service->findMatches($term); $expected = [ - ['id' => 12, 'label' => '"john doe" <john@doe.cz>', 'value' => '"john doe" <john@doe.cz>'], - ['id' => 13, 'label' => '"joe doe" <joe@doe.se>', 'value' => '"joe doe" <joe@doe.se>'], - ['id' => 1234, 'label' => 'John Doe', 'value' => '"John Doe" <john@doe.com>'], - ['id' => 20, 'label' => 'Journalists', 'value' => 'Journalists'], + ['id' => 12, 'label' => '"john doe" <john@doe.cz>', 'email' => 'john@doe.cz'], + ['id' => 13, 'label' => '"joe doe" <joe@doe.se>', 'email' => 'joe@doe.se'], + ['id' => 1234, 'label' => 'John Doe', 'email' => 'john@doe.com'], + ['id' => 20, 'label' => 'Journalists', 'email' => 'Journalists'], ]; $this->assertEquals($expected, $response); } diff --git a/tests/Service/ContactsIntegrationTest.php b/tests/Service/ContactsIntegrationTest.php index bff62586c..7dd59036f 100644 --- a/tests/Service/ContactsIntegrationTest.php +++ b/tests/Service/ContactsIntegrationTest.php @@ -98,19 +98,19 @@ class ContactsIntegrationTest extends TestCase { [ 'id' => 1, 'label' => 'Jonathan Frakes (jonathan@frakes.com)', - 'value' => '"Jonathan Frakes" <jonathan@frakes.com>', + 'email' => 'jonathan@frakes.com', 'photo' => null, ], [ 'id' => 2, 'label' => 'John Doe (john@doe.info)', - 'value' => '"John Doe" <john@doe.info>', + 'email' => 'john@doe.info', 'photo' => null, ], [ 'id' => 2, 'label' => 'John Doe (doe@john.info)', - 'value' => '"John Doe" <doe@john.info>', + 'email' => 'doe@john.info', 'photo' => null, ], ]; @@ -160,7 +160,7 @@ class ContactsIntegrationTest extends TestCase { [ 'id' => 1, 'label' => 'Jonathan Frakes (jonathan@frakes.com)', - 'value' => '"Jonathan Frakes" <jonathan@frakes.com>', + 'email' => 'jonathan@frakes.com', 'photo' => null, ], ]; diff --git a/tests/Service/GroupsIntegrationTest.php b/tests/Service/GroupsIntegrationTest.php index ca0aa282b..5fa04f86e 100644 --- a/tests/Service/GroupsIntegrationTest.php +++ b/tests/Service/GroupsIntegrationTest.php @@ -59,7 +59,7 @@ class GroupsIntegrationTest extends TestCase { [ 'id' => 'namespace1:testgroup', 'label' => 'first test group (Namespace1)', - 'value' => 'namespace1:testgroup', + 'email' => 'namespace1:testgroup', 'photo' => null, ] ]; |