Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/Controller/PollController.php
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,21 @@ public function addPollToPollGroup(int $pollId, int $pollGroupId): JSONResponse
]);
}

/**
* Update Pollgroup
*/
#[NoAdminRequired]
#[FrontpageRoute(verb: 'PUT', url: '/pollgroup/{pollGroupId}/update')]
public function updatePollGroup(
int $pollGroupId,
string $title,
string $titleExt,
string $description,
): JSONResponse {
return $this->response(fn () => [
'pollGroup' => $this->pollService->updatePollGroup($pollGroupId, $title, $titleExt, $description),
]);
}
/**
* Remove poll from pollgroup
* @param int $pollId Poll id
Expand Down
21 changes: 21 additions & 0 deletions lib/Service/PollService.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ public function listPollGroups(): array {
return $this->pollGroupMapper->list();
}

public function updatePollGroup(
int $pollGroupId,
string $title,
string $titleExt,
string $description,
): PollGroup {
try {
$pollGroup = $this->pollGroupMapper->find($pollGroupId);
if ($pollGroup->getOwner() !== $this->userSession->getCurrentUserId()) {
throw new ForbiddenException('You do not have permission to edit this poll group');
}
$pollGroup->setTitle($title);
$pollGroup->setTitleExt($titleExt);
$pollGroup->setDescription($description);

$pollGroup = $this->pollGroupMapper->update($pollGroup);
return $pollGroup;
} catch (DoesNotExistException $e) {
throw new NotFoundException('Poll group not found');
}
}
public function addPollToPollGroup(
int $pollId,
?int $pollGroupId = null,
Expand Down
21 changes: 21 additions & 0 deletions src/Api/modules/polls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,27 @@ const polls = {
})
},

updatePollGroup(
pollGroupId: number,
title: string,
titleExt: string,
description: string,
): Promise<AxiosResponse<{ pollGroup: PollGroup }>> {
return httpInstance.request({
method: 'PUT',
url: `pollgroup/${pollGroupId}/update`,
data: {
title,
titleExt,
description,
},
cancelToken:
cancelTokenHandlerObject[
this.updatePollGroup.name
].handleRequestCancellation().token,
})
},

getPolls(): Promise<
AxiosResponse<{
polls: Poll[]
Expand Down
57 changes: 57 additions & 0 deletions src/components/Actions/modules/ActionEditGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!--
- SPDX-FileCopyrightText: 2021 Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import { ref } from 'vue'

import { useSessionStore } from '../../../stores/session'

import { t } from '@nextcloud/l10n'

import ButtonModal from '../../Base/modules/ButtonModal.vue'
import { ButtonMode } from '../../../Types'
import PollGroupEditDlg from '../../PollGroup/PollGroupEditDlg.vue'

import EditIcon from 'vue-material-design-icons/Pencil.vue'

interface Props {
caption?: string
modalSize?: string
buttonMode?: ButtonMode
}

const {
caption = t('polls', 'Edit poll group'),
modalSize = 'normal',
buttonMode = ButtonMode.Native,
} = defineProps<Props>()

const sessionStore = useSessionStore()

const showModal = ref(false)

function updatedGroup() {
// close modal and show the confirmation dialog
showModal.value = false
}
</script>

<template>
<ButtonModal
v-if="sessionStore.appPermissions.pollCreation"
v-model:show-modal="showModal"
:aria-label="caption"
:button-caption="caption"
:modal-size="modalSize"
:button-mode="buttonMode"
:button-variant="'secondary'">
<template #icon>
<EditIcon size="20" decorative />
</template>
<template #modal-content>
<PollGroupEditDlg @updated="updatedGroup" @close="showModal = false" />
</template>
</ButtonModal>
</template>
200 changes: 200 additions & 0 deletions src/components/PollGroup/PollGroupEditDlg.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<!--
- SPDX-FileCopyrightText: 2018 Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { t } from '@nextcloud/l10n'

import NcButton from '@nextcloud/vue/components/NcButton'

import SpeakerIcon from 'vue-material-design-icons/Bullhorn.vue'
import SpeakerBigIcon from 'vue-material-design-icons/BullhornVariant.vue'
import DescriptionIcon from 'vue-material-design-icons/TextBox.vue'

import { ConfigBox, InputDiv } from '../Base/index.ts'

import { usePollsStore } from '../../stores/polls.ts'
import { showError } from '@nextcloud/dialogs'
import { useRoute } from 'vue-router'
import { router } from '../../router.ts'

const pollsStore = usePollsStore()
const route = useRoute()
const helperTexts = {
title: t('polls', 'Choose a brief title for the navigation bar and the slug'),
titleExt: t('polls', 'Choose a more meaningful title for the overview page'),
description: t('polls', 'Choose a description for the overview page'),
titleChangedNote: t('polls', 'Note: Changing the title, also changes the URL'),
}
const emit = defineEmits<{
(e: 'close'): void
(e: 'updated'): void
}>()

const newGroupAttributes = ref({
slug: route.params.slug as string,
title: pollsStore.currentGroup?.title || '',
titleExt: pollsStore.currentGroup?.titleExt || '',
description: pollsStore.currentGroup?.description || '',
})

watch(route, (route) => {
resetInputs(route.params.slug as string)
})

const pollGroupTitle = computed({
get() {
return pollsStore.currentGroup?.title || ''
},
set(newTitle: string) {
newGroupAttributes.value.title = newTitle
},
})
const pollGroupTitleExt = computed({
get() {
return pollsStore.currentGroup?.titleExt || ''
},
set(newTitleExt: string) {
newGroupAttributes.value.titleExt = newTitleExt
},
})

const pollGroupTitleDescription = computed({
get() {
return pollsStore.currentGroup?.description || ''
},
set(newDescription: string) {
newGroupAttributes.value.description = newDescription
},
})

const updating = ref(false)

const titleUpdated = computed(
() => pollGroupTitle.value !== pollsStore.currentGroup?.title,
)
const titleIsEmpty = computed(() => pollGroupTitle.value === '')
const disableEditButton = computed(() => titleIsEmpty.value || updating.value)

function resetInputs(slug: string) {
newGroupAttributes.value = {
slug,
title: pollsStore.currentGroup?.title || '',
titleExt: pollsStore.currentGroup?.titleExt || '',
description: pollsStore.currentGroup?.description || '',
}
}

async function updatePollGroup() {
try {
// block the modal to prevent double submission
updating.value = true
// add the poll
const pollGroup = await pollsStore.updatePollGroup(newGroupAttributes.value)

if (pollGroup) {
resetInputs(route.params.slug as string)
emit('updated')
router.push({
name: 'group',
params: { slug: pollGroup.slug },
})
}
} catch {
showError(t('polls', 'Error updating PollGroup'))
} finally {
// unblock the modal
updating.value = false
}
}
</script>

<template>
<div class="edit-poll-group">
<ConfigBox :name="t('polls', 'Title')">
<template #icon>
<SpeakerIcon />
</template>
<InputDiv
v-model="pollGroupTitle"
focus
type="text"
:placeholder="t('polls', 'Enter Title')"
:helper-text="helperTexts.title"
@submit="updatePollGroup" />

<div class="change-title-hint">
<p v-if="titleUpdated">
{{ helperTexts.titleChangedNote }}
</p>
</div>
</ConfigBox>

<ConfigBox :name="t('polls', 'Extended title')">
<template #icon>
<SpeakerBigIcon />
</template>
<InputDiv
v-model="pollGroupTitleExt"
type="text"
:placeholder="t('polls', 'Enter extended Title')"
:helper-text="helperTexts.titleExt"
@submit="updatePollGroup" />
</ConfigBox>

<ConfigBox :name="t('polls', 'Description')">
<template #icon>
<DescriptionIcon />
</template>
<textarea
v-model="pollGroupTitleDescription"
class="input-textarea"
:placeholder="t('polls', 'Enter a description')" />
<p class="helper">
{{ helperTexts.description }}
</p>
</ConfigBox>

<div class="create-buttons">
<NcButton @click="emit('close')">
<template #default>
{{ t('polls', 'Close') }}
</template>
</NcButton>
<NcButton
:disabled="disableEditButton"
:variant="'primary'"
@click="updatePollGroup">
<template #default>
{{ t('polls', 'Update') }}
</template>
</NcButton>
</div>
</div>
</template>

<style lang="scss">
.edit-poll-group {
background-color: var(--color-main-background);
padding: 8px 20px;

.create-buttons {
display: flex;
justify-content: flex-end;
gap: 8px;
}

.input-textarea {
width: 99%;
resize: vertical;
}

.helper {
min-height: 1.5rem;
font-size: 0.8em;
opacity: 0.8;
}
}
</style>
Loading