Skip to content

Commit caf834b

Browse files
Merge pull request #6308 from christianbeeznest/GH-5454-2
Pages: Support public view by slug and admin preview by ID - refs #5454
2 parents f21ca4c + 16d722d commit caf834b

File tree

5 files changed

+131
-60
lines changed

5 files changed

+131
-60
lines changed

assets/vue/components/page/Form.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919
<p
2020
class="text-sm m-2 text-gray-500"
21-
v-if="props.modelValue.slug || v$.item.slug.$model"
21+
v-if="pageId"
2222
>
2323
{{ t("Preview") }}:
2424
<a
25-
:href="`/page/${v$.item.slug.$model || props.modelValue.slug}`"
25+
:href="`/pages/${pageId}/preview`"
2626
target="_blank"
2727
class="text-blue-600 underline"
2828
>
29-
{{ window.location.origin + "/page/" + (v$.item.slug.$model || props.modelValue.slug) }}
29+
{{ window.location.origin + `/pages/${pageId}/preview` }}
3030
</a>
3131
</p>
3232

@@ -109,6 +109,13 @@ let categories = ref([])
109109
110110
const findAllPageCategories = async () => (categories.value = await pageCategoryService.findAll())
111111
112+
const pageId = computed(() => {
113+
const rawId = props.modelValue?.["@id"]
114+
if (!rawId) return null
115+
const matches = rawId.match(/\/(\d+)$/)
116+
return matches ? matches[1] : null
117+
})
118+
112119
findAllPageCategories()
113120
114121
const validations = {

assets/vue/views/page/List.vue

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,25 @@
6666
<Column :exportable="false">
6767
<template #body="slotProps">
6868
<div class="text-right space-x-2">
69-
<!-- <Button-->
70-
<!-- class="p-button-icon-only p-button-plain p-button-outlined p-button-sm"-->
71-
<!-- icon="mdi mdi-information"-->
72-
<!-- @click="showHandler(slotProps.data)"-->
73-
<!-- />-->
74-
7569
<Button
7670
v-if="securityStore.isAuthenticated"
7771
class="p-button-icon-only p-button-plain p-button-outlined p-button-sm"
7872
icon="mdi mdi-pencil"
7973
@click="goToEditItem(slotProps.data)"
8074
/>
81-
8275
<Button
8376
v-if="securityStore.isAuthenticated"
8477
class="p-button-icon-only p-button-danger p-button-outlined p-button-sm"
8578
icon="mdi mdi-delete"
8679
@click="confirmDeleteItem(slotProps.data)"
8780
/>
81+
<Button
82+
v-if="slotProps.data.enabled && slotProps.data.slug"
83+
class="p-button-icon-only p-button-plain p-button-outlined p-button-sm"
84+
icon="mdi mdi-link-variant"
85+
:title="t('Show public link')"
86+
@click="showPublicLinkDialog(slotProps.data)"
87+
/>
8888
</div>
8989
</template>
9090
</Column>
@@ -121,16 +121,17 @@
121121

122122
<template #footer>
123123
<Button
124-
class="p-button-text"
125-
icon="pi pi-times"
126-
label="Cancel"
127-
@click="hideDialog"
124+
v-if="securityStore.isAuthenticated"
125+
class="p-button-icon-only p-button-plain p-button-outlined p-button-sm"
126+
icon="mdi mdi-pencil"
127+
@click="goToEditItem(slotProps.data)"
128128
/>
129+
129130
<Button
130-
class="p-button-text"
131-
icon="pi pi-check"
132-
label="Save"
133-
@click="saveItem"
131+
v-if="securityStore.isAuthenticated"
132+
class="p-button-icon-only p-button-danger p-button-outlined p-button-sm"
133+
icon="mdi mdi-delete"
134+
@click="confirmDeleteItem(slotProps.data)"
134135
/>
135136
</template>
136137
</Dialog>
@@ -198,6 +199,24 @@
198199
/>
199200
</template>
200201
</Dialog>
202+
203+
<Dialog
204+
v-model:visible="publicLinkDialogVisible"
205+
:header="t('Public link')"
206+
:modal="true"
207+
:style="{ width: '500px' }"
208+
>
209+
<div class="text-center">
210+
<p class="text-sm mb-2">{{ t("You can share or copy this link:") }}</p>
211+
<InputText
212+
v-model="publicLink"
213+
readonly
214+
class="w-full mb-3 cursor-pointer"
215+
@focus="$event.target.select()"
216+
/>
217+
<p class="text-xs text-gray-500 italic">{{ t("Select the link above and press Ctrl+C to copy it") }}</p>
218+
</div>
219+
</Dialog>
201220
</template>
202221

203222
<script setup>
@@ -242,8 +261,6 @@ onMounted(() => {
242261
})
243262
244263
const items = computed(() => store.state["page"].recents)
245-
246-
// const deletedPage = computed(() => store.state['page'].deleted);
247264
const isLoading = computed(() => store.state["page"].isLoading)
248265
const totalItems = computed(() => store.state["page"].totalItems)
249266
@@ -270,31 +287,6 @@ const sortingChanged = (event) => {
270287
onUpdateOptions(options.value)
271288
}
272289
273-
/*const openNew = () => {
274-
item.value = {};
275-
submitted.value = false;
276-
itemDialog.value = true;
277-
};*/
278-
279-
const saveItem = () => {
280-
submitted.value = true
281-
282-
if (item.value.title.trim()) {
283-
if (!item.value.id) {
284-
// item.value.creator
285-
//createCategory.value(item.value);
286-
toast.add({
287-
severity: "success",
288-
detail: t("Saved"),
289-
life: 3500,
290-
})
291-
}
292-
293-
itemDialog.value = false
294-
item.value = {}
295-
}
296-
}
297-
298290
const deleteMultipleItems = () => {
299291
console.log("deleteMultipleItems".selectedItems.value)
300292
@@ -311,31 +303,25 @@ const deleteMultipleItems = () => {
311303
312304
onUpdateOptions(options.value)
313305
}
314-
315-
const hideDialog = () => {
316-
itemDialog.value = false
317-
submitted.value = false
318-
}
319-
320-
/*const editItem = (item) => {
321-
item.value = { ...item };
322-
itemDialog.value = true;
323-
};*/
324-
325306
const confirmDeleteItem = (itemToDelete) => {
326307
item.value = itemToDelete
327308
deleteItemDialog.value = true
328309
}
329310
330-
/*const confirmDeleteMultiple = () => {
331-
deleteMultipleDialog.value = true;
332-
};*/
333-
334311
const btnCofirmSingleDeleteOnClick = () => {
335312
deleteItem(item)
336313
337314
item.value = {}
338315
339316
deleteItemDialog.value = false
340317
}
318+
319+
const publicLinkDialogVisible = ref(false)
320+
const publicLink = ref("")
321+
322+
const showPublicLinkDialog = (item) => {
323+
if (!item.slug) return
324+
publicLink.value = `${window.location.origin}/pages/${item.slug}`
325+
publicLinkDialogVisible.value = true
326+
}
341327
</script>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Chamilo\CoreBundle\Controller;
6+
7+
use Chamilo\CoreBundle\Repository\PageRepository;
8+
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
9+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
10+
use Symfony\Component\HttpFoundation\Response;
11+
use Symfony\Component\Routing\Attribute\Route;
12+
use Symfony\Component\Security\Http\Attribute\IsGranted;
13+
14+
#[Route('/pages')]
15+
class PageController extends AbstractController
16+
{
17+
public function __construct(
18+
private readonly AccessUrlHelper $accessUrlHelper
19+
) {}
20+
21+
#[Route('/{slug}', name: 'public_page_show', methods: ['GET'])]
22+
public function show(string $slug, PageRepository $pageRepo): Response
23+
{
24+
$accessUrl = $this->accessUrlHelper->getCurrent();
25+
26+
$page = $pageRepo->findOneBy([
27+
'slug' => $slug,
28+
'enabled' => true,
29+
'url' => $accessUrl,
30+
]);
31+
32+
if (!$page) {
33+
throw $this->createNotFoundException('Page not found or not available for this access URL');
34+
}
35+
36+
return $this->render('@ChamiloCore/Page/show.html.twig', [
37+
'page' => $page,
38+
]);
39+
}
40+
41+
#[Route('/{id}/preview', name: 'admin_page_preview', methods: ['GET'])]
42+
#[IsGranted('ROLE_ADMIN')]
43+
public function preview(int $id, PageRepository $pageRepo): Response
44+
{
45+
$page = $pageRepo->find($id);
46+
47+
if (!$page) {
48+
throw $this->createNotFoundException('Page not found');
49+
}
50+
51+
return $this->render('@ChamiloCore/Page/preview.html.twig', [
52+
'page' => $page,
53+
]);
54+
}
55+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{% extends '@ChamiloCore/Layout/layout_one_col.html.twig' %}
2+
3+
{% block content %}
4+
<section class="py-8 max-w-4xl mx-auto border border-yellow-300 bg-yellow-50 p-4 rounded">
5+
<div class="text-sm text-yellow-700 mb-2">
6+
{{ 'Page preview for administrators only'|trans }}
7+
</div>
8+
<h1 class="text-3xl font-bold mb-4">{{ page.title }}</h1>
9+
<article class="prose prose-lg">
10+
{{ page.content|raw }}
11+
</article>
12+
</section>
13+
{% endblock %}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% extends '@ChamiloCore/Layout/layout_one_col.html.twig' %}
2+
3+
{% block content %}
4+
<section class="py-8 max-w-4xl mx-auto">
5+
<h1 class="text-3xl font-bold mb-4">{{ page.title }}</h1>
6+
<article class="prose prose-lg">
7+
{{ page.content|raw }}
8+
</article>
9+
</section>
10+
{% endblock %}

0 commit comments

Comments
 (0)