diff --git a/changelog/unreleased/enhancement-enable-rename-groups b/changelog/unreleased/enhancement-enable-rename-groups
new file mode 100644
index 00000000000..62a28eac60c
--- /dev/null
+++ b/changelog/unreleased/enhancement-enable-rename-groups
@@ -0,0 +1,4 @@
+Enhancement: Enable rename groups
+
+https://github.com/owncloud/web/pull/8715
+https://github.com/owncloud/web/issues/8714
diff --git a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue
index de1e316ef4f..c346e777707 100644
--- a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue
+++ b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue
@@ -9,7 +9,7 @@ import { useActionsShowDetails } from '../../../../web-pkg/src/composables/actio
import { computed, defineComponent, PropType, unref } from 'vue'
import ContextActionMenu from 'web-pkg/src/components/ContextActions/ContextActionMenu.vue'
import { GroupActionOptions } from 'web-pkg/src/composables/actions'
-import { useGroupActionsDelete } from '../../composables/actions/groups/useGroupActionsDelete'
+import { useGroupActionsEdit, useGroupActionsDelete } from '../../composables/actions/groups'
import { useStore } from 'web-pkg/src/composables'
export default defineComponent({
@@ -25,9 +25,12 @@ export default defineComponent({
const store = useStore()
const { actions: showDetailsActions } = useActionsShowDetails()
const { actions: deleteActions } = useGroupActionsDelete({ store })
+ const { actions: editActions } = useGroupActionsEdit()
const menuItemsPrimaryActions = computed(() =>
- [...unref(deleteActions)].filter((item) => item.isEnabled(props.actionOptions))
+ [...unref(editActions), ...unref(deleteActions)].filter((item) =>
+ item.isEnabled(props.actionOptions)
+ )
)
const menuItemsSidebar = computed(() =>
diff --git a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue
index 24bc3babaf9..cdf081bc76a 100644
--- a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue
+++ b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue
@@ -27,19 +27,17 @@
+
diff --git a/packages/web-app-admin-settings/src/composables/actions/groups/index.ts b/packages/web-app-admin-settings/src/composables/actions/groups/index.ts
index b8647746c40..307a19f2241 100644
--- a/packages/web-app-admin-settings/src/composables/actions/groups/index.ts
+++ b/packages/web-app-admin-settings/src/composables/actions/groups/index.ts
@@ -1 +1,2 @@
export * from './useGroupActionsDelete'
+export * from './useGroupActionsEdit'
diff --git a/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts
new file mode 100644
index 00000000000..9cc32e5bb06
--- /dev/null
+++ b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts
@@ -0,0 +1,27 @@
+import { eventBus } from 'web-pkg'
+import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar'
+import { useGettext } from 'vue3-gettext'
+import { computed } from 'vue'
+import { UserAction } from 'web-pkg/src/composables/actions'
+
+export const useGroupActionsEdit = () => {
+ const { $gettext } = useGettext()
+
+ const actions = computed((): UserAction[] => [
+ {
+ name: 'edit',
+ icon: 'pencil',
+ label: () => $gettext('Edit'),
+ handler: () => eventBus.publish(SideBarEventTopics.openWithPanel, 'EditPanel'),
+ isEnabled: ({ resources }) => {
+ return resources.length > 0
+ },
+ componentType: 'button',
+ class: 'oc-groups-actions-edit-trigger'
+ }
+ ])
+
+ return {
+ actions
+ }
+}
diff --git a/packages/web-app-admin-settings/src/views/Groups.vue b/packages/web-app-admin-settings/src/views/Groups.vue
index c49a5b7c2a2..67cc8f99fdc 100644
--- a/packages/web-app-admin-settings/src/views/Groups.vue
+++ b/packages/web-app-admin-settings/src/views/Groups.vue
@@ -63,7 +63,6 @@
@@ -184,10 +183,11 @@ export default defineComponent({
title: this.$gettext('Edit group'),
component: EditPanel,
default: false,
- enabled: false // this.selectedGroups.length === 1
- /**
- * Editing groups is currently not supported by backend
- */
+ enabled: this.selectedGroups.length === 1,
+ componentAttrs: {
+ group: this.selectedGroups.length === 1 ? this.selectedGroups[0] : null,
+ onConfirm: this.editGroup
+ }
}
].filter((p) => p.enabled)
}
@@ -236,12 +236,21 @@ export default defineComponent({
try {
const client = this.clientService.graphAuthenticated
await client.groups.editGroup(editGroup.id, editGroup)
- const group = this.groups.find((group) => group.id === editGroup.id)
- Object.assign(group, editGroup)
- this.showMessage({
- title: this.$gettext('Group was edited successfully')
- })
+ const { data: updatedGroup } = await client.groups.getGroup(editGroup.id)
+ const groupIndex = this.groups.findIndex((group) => group.id === editGroup.id)
+ this.groups[groupIndex] = updatedGroup
+ const selectedGroupIndex = this.selectedGroups.findIndex(
+ (group) => group.id === updatedGroup.id
+ )
+ if (selectedGroupIndex >= 0) {
+ // FIXME: why do we need to update selectedUsers?
+ this.selectedGroups[selectedGroupIndex] = updatedGroup
+ }
+
+ eventBus.publish('sidebar.entity.saved')
+
+ return updatedGroup
} catch (error) {
console.error(error)
this.showMessage({
diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts
index 0b1b3286a5a..2862f9a79f1 100644
--- a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts
+++ b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts
@@ -1,17 +1,26 @@
import EditPanel from '../../../../../src/components/Groups/SideBar/EditPanel.vue'
-import { defaultPlugins, mount } from 'web-test-helpers'
+import {
+ createStore,
+ defaultComponentMocks,
+ defaultPlugins,
+ defaultStoreMockOptions,
+ mockAxiosReject,
+ mount
+} from 'web-test-helpers'
+import { mock } from 'jest-mock-extended'
+import { AxiosResponse } from 'axios'
describe('EditPanel', () => {
+ it('renders all available inputs', () => {
+ const { wrapper } = getWrapper()
+ expect(wrapper.html()).toMatchSnapshot()
+ })
describe('method "revertChanges"', () => {
it('should revert changes on property editGroup', () => {
- const { wrapper } = getWrapper({
- propsData: {
- groups: [{ displayName: 'group' }]
- }
- })
- wrapper.vm.editGroup = { displayName: 'my group' }
+ const { wrapper } = getWrapper()
+ wrapper.vm.editGroup.dispayName = 'users'
wrapper.vm.revertChanges()
- expect(wrapper.vm.editGroup).toEqual({ displayName: 'group' })
+ expect(wrapper.vm.editGroup.displayName).toEqual('group')
})
it('should revert changes on property formData', () => {
const { wrapper } = getWrapper({
@@ -28,17 +37,28 @@ describe('EditPanel', () => {
})
describe('method "validateDisplayName"', () => {
- it('should return true if displayName is valid', () => {
- const { wrapper } = getWrapper()
- wrapper.vm.editGroup.displayName = 'jan'
- expect(wrapper.vm.validateDisplayName()).toBeTruthy()
- expect(wrapper.vm.formData.displayName.valid).toBeTruthy()
+ it('should return true if displayName is valid', async () => {
+ const { wrapper, mocks } = getWrapper()
+ wrapper.vm.editGroup.displayName = 'users'
+ const graphMock = mocks.$clientService.graphAuthenticated
+ const getGroupStub = graphMock.groups.getGroup.mockRejectedValue(() => mockAxiosReject())
+ expect(await wrapper.vm.validateDisplayName()).toBeTruthy()
+ expect(getGroupStub).toHaveBeenCalled()
})
- it('should return false if displayName is not valid', () => {
+ it('should return false if displayName is empty', async () => {
const { wrapper } = getWrapper()
wrapper.vm.editGroup.displayName = ''
- expect(wrapper.vm.validateDisplayName()).toBeFalsy()
- expect(wrapper.vm.formData.displayName.valid).toBeFalsy()
+ expect(await wrapper.vm.validateDisplayName()).toBeFalsy()
+ })
+ it('should return false if displayName is already existing', async () => {
+ const { wrapper, mocks } = getWrapper()
+ wrapper.vm.editGroup.displayName = 'users'
+ const graphMock = mocks.$clientService.graphAuthenticated
+ const getGroupStub = graphMock.groups.getGroup.mockResolvedValue(
+ mock({ data: { displayName: 'group' } })
+ )
+ expect(await wrapper.vm.validateDisplayName()).toBeFalsy()
+ expect(getGroupStub).toHaveBeenCalled()
})
})
@@ -51,20 +71,26 @@ describe('EditPanel', () => {
it('should be true if formData is valid', () => {
const { wrapper } = getWrapper()
wrapper.vm.formData.displayName.valid = false
- expect(wrapper.vm.invalidFormData).toBeTruthy()
+ console.log(wrapper.vm.formData)
+ expect(wrapper.vm.invalidFormData).toBeFalsy()
})
})
})
function getWrapper({ propsData = {} } = {}) {
+ const mocks = defaultComponentMocks()
+ const storeOptions = defaultStoreMockOptions
+ const store = createStore(storeOptions)
+
return {
+ mocks,
wrapper: mount(EditPanel, {
props: {
- groups: [{ displayName: 'group' }],
- ...propsData
+ group: { displayName: 'group', members: [] },
},
global: {
- plugins: [...defaultPlugins()],
+ mocks,
+ plugins: [...defaultPlugins(), store],
stubs: {
'oc-text-input': true,
'avatar-image': true,
diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap
new file mode 100644
index 00000000000..5951d01f178
--- /dev/null
+++ b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap
@@ -0,0 +1,21 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EditPanel renders all available inputs 1`] = `
+
+`;
diff --git a/packages/web-client/src/graph.ts b/packages/web-client/src/graph.ts
index 90a11267eb8..c622cc95066 100644
--- a/packages/web-client/src/graph.ts
+++ b/packages/web-client/src/graph.ts
@@ -148,7 +148,8 @@ export const graph = (baseURI: string, axiosClient: AxiosInstance): Graph => {
groups: {
createGroup: (group: Group) => groupsApiFactory.createGroup(group),
editGroup: (groupId: string, group: Group) => groupApiFactory.updateGroup(groupId, group),
- getGroup: (groupId: string) => groupApiFactory.getGroup(groupId),
+ getGroup: (groupId: string) =>
+ groupApiFactory.getGroup(groupId, new Set([]), new Set(['members'])),
deleteGroup: (groupId: string) => groupApiFactory.deleteGroup(groupId),
listGroups: (orderBy?: any) =>
groupsApiFactory.listGroups(