Merge branch 'master' of github.com:chamilo/chamilo-lms

snyk-fix-0707082af6333e258bba6802257d9315
Yannick Warnier 1 year ago
commit edb333e480
  1. 25
      assets/css/scss/atoms/_divider.scss
  2. 1
      assets/css/scss/index.scss
  3. 95
      assets/vue/components/admin/AdminBlock.vue
  4. 102
      assets/vue/components/admin/AdminBlockExtraContent.vue
  5. 32
      assets/vue/components/basecomponents/BaseDivider.vue
  6. 92
      assets/vue/composables/admin/indexBlocks.js
  7. 40
      assets/vue/services/adminService.js
  8. 32
      assets/vue/services/pageService.js
  9. 149
      assets/vue/views/admin/AdminIndex.vue
  10. 23
      src/CoreBundle/Component/Utils/CreateDefaultPages.php
  11. 62
      src/CoreBundle/Controller/Admin/IndexBlocksController.php
  12. 6
      src/CoreBundle/Entity/Page.php

@ -0,0 +1,25 @@
.divider {
@apply bg-white;
&[aria-orientation="vertical"] {
@apply before:absolute before:block before:left-1/2 before:top-0 before:h-full before:content-[""] before:border-l before:border-solid before:border-gray-25
flex min-h-full mx-2 py-2 relative justify-center;
div {
@apply first:py-2;
}
}
&[aria-orientation="horizontal"] {
@apply before:absolute before:block before:left-0 before:w-full before:top-1/2 before:content-[""] before:border-t before:border-solid before:border-gray-25
flex w-full relative items-center my-2 px-2;
div {
@apply first:px-2;
}
}
div {
@apply first:z-[1] first:bg-white first:text-gray-50 first:font-semibold first:text-caption;
}
}

@ -19,6 +19,7 @@
@import "atoms/buttons";
@import "atoms/calendar";
@import "atoms/checkbox";
@import "atoms/divider";
@import "atoms/dropdown";
@import "atoms/fieldset";
@import "atoms/inline_message";

@ -1,5 +1,8 @@
<template>
<div class="admin-index__block-container">
<div
class="admin-index__block-container"
:class="id"
>
<div class="admin-index__block">
<div class="flex gap-2 justify-between">
<h4>
@ -9,9 +12,17 @@
</div>
<div class="space-y-4">
<p v-if="props.description" class="text-body-4" v-text="props.description" />
<form v-if="props.searchUrl" :action="props.searchUrl" method="get">
<p
v-if="props.description"
class="text-body-4"
v-text="props.description"
/>
<form
v-if="props.searchUrl"
:action="props.searchUrl"
method="get"
>
<BaseInputGroup
:button-label="t('Search')"
:input-placeholder="t('Keyword')"
@ -22,7 +33,10 @@
</div>
<div class="p-menu p-component p-ripple-disabled">
<ul class="p-menu-list p-reset" role="menu">
<ul
class="p-menu-list p-reset"
role="menu"
>
<li
v-for="(item, index) in visibleItems"
:key="index"
@ -32,43 +46,88 @@
role="menuitem"
>
<div class="p-menuitem-content">
<a :href="item.url" class="p-menuitem-link">
<span class="p-menuitem-text" v-text="item.label" />
<router-link
v-if="item.route"
v-slot="{ href, navigate }"
:to="item.route"
custom
>
<a
:href="href"
class="p-menuitem-link"
v-bind="props.action"
@click="navigate"
>
<span
class="p-menuitem-text"
v-text="item.label"
/>
</a>
</router-link>
<a
v-else-if="item.url"
:href="item.url"
class="p-menuitem-link"
v-bind="props.action"
>
<span
class="p-menuitem-text"
v-text="item.label"
/>
</a>
</div>
</li>
<slot></slot>
</ul>
</div>
<AdminBlockExtraContent
:id="id"
v-model="modelExtraContent"
:editable="editable"
/>
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import BaseInputGroup from "../basecomponents/BaseInputGroup.vue";
import BaseIcon from "../basecomponents/BaseIcon.vue";
import { computed } from "vue"
import { useI18n } from "vue-i18n"
import BaseInputGroup from "../basecomponents/BaseInputGroup.vue"
import BaseIcon from "../basecomponents/BaseIcon.vue"
import AdminBlockExtraContent from "./AdminBlockExtraContent.vue"
const { t } = useI18n()
const { t } = useI18n();
const modelExtraContent = defineModel("extraContent", {
type: Object,
})
const props = defineProps({
id: {
type: String,
required: true,
},
editable: {
type: Boolean,
required: false,
default: false,
},
icon: { type: String, required: false, default: () => "admin-settings" },
title: { type: String, require: true, default: () => "" },
description: { type: String, required: false, default: () => null },
searchUrl: { type: String, required: false, default: () => null },
items: { type: Array, required: true, default: () => [] },
});
})
const visibleItems = computed(() =>
props.items
.map((item) => {
if (!Object.keys(item).includes("visible")) {
item.visible = true;
item.visible = true
}
return item;
return item
})
.filter((item) => item.visible)
);
.filter((item) => item.visible),
)
</script>

@ -0,0 +1,102 @@
<script setup>
import { ref } from "vue"
import { useI18n } from "vue-i18n"
import Inplace from "primevue/inplace"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
import { useSecurityStore } from "../../store/securityStore"
import pageService from "../../services/pageService"
import BaseDivider from "../basecomponents/BaseDivider.vue"
const props = defineProps({
id: {
type: String,
required: true,
},
editable: {
type: Boolean,
required: true,
},
})
const modelExtraContent = defineModel({
type: Object,
})
const securityStore = useSecurityStore()
const { t, locale } = useI18n()
const newExtraContent = ref(modelExtraContent.value.content)
async function saveExtraContent() {
if (modelExtraContent.value["@id"]) {
if (!newExtraContent.value) {
await pageService.delete(modelExtraContent.value["@id"])
modelExtraContent.value = {
category: modelExtraContent.value.category,
}
return
}
const page = await pageService.update(modelExtraContent.value["@id"], {
content: newExtraContent.value,
})
modelExtraContent.value.content = page.content
return
}
if (newExtraContent.value) {
const page = await pageService.post({
title: props.id,
content: newExtraContent.value,
enabled: true,
locale: locale.value,
url: "/api/access_urls/" + window.access_url_id,
creator: securityStore.user["@id"],
category: modelExtraContent.value.category,
})
modelExtraContent.value = {
"@id": page["@id"],
content: page.content,
category: page.category["@id"],
}
}
}
</script>
<template>
<Inplace
v-if="editable"
:closable="true"
@close="saveExtraContent"
>
<template #display>
<BaseDivider
align="right"
:title="t('Editable content')"
/>
<div
v-if="modelExtraContent.content"
class="text-body-2"
v-html="modelExtraContent.content"
/>
</template>
<template #content>
<BaseTinyEditor
v-model="newExtraContent"
:editor-id="'new-description-editor' + id"
:full-page="false"
/>
</template>
</Inplace>
<div
v-else-if="modelExtraContent.content"
class="text-body-2"
v-html="modelExtraContent.content"
/>
</template>

@ -0,0 +1,32 @@
<script setup>
import Divider from "primevue/divider"
defineProps({
align: {
type: String,
required: false,
default: null,
},
layout: {
type: String,
required: false,
default: "horizontal",
},
title: {
type: [String, undefined],
required: false,
default: null,
},
})
</script>
<template>
<Divider
:align="align"
:layout="layout"
class="divider"
unstyled
>
{{ title }}
</Divider>
</template>

@ -0,0 +1,92 @@
import { onMounted, ref } from "vue"
import { usePlatformConfig } from "../../store/platformConfig"
import adminService from "../../services/adminService"
import { useToast } from "primevue/usetoast"
import { useSecurityStore } from "../../store/securityStore"
import { useI18n } from "vue-i18n"
export function useIndexBlocks() {
const { t } = useI18n()
const toast = useToast()
const platformConfigStore = usePlatformConfig()
const securityStore = useSecurityStore()
const blockVersionStatusEl = ref()
onMounted(() => {
if (!securityStore.isAdmin) {
return
}
if ("false" === platformConfigStore.getSetting("admin.admin_chamilo_announcements_disable")) {
adminService.findAnnouncements().then((announcement) => toast.add({ severity: "info", detail: announcement }))
}
if ("false" === platformConfigStore.getSetting("platform.registered")) {
blockVersionStatusEl.value = null
} else {
loadVersion()
}
})
/**
* @param {boolean} doNotListCampus
*/
function checkVersion(doNotListCampus) {
adminService.registerCampus(doNotListCampus).then(() => {
loadVersion()
toast.add({
severity: "success",
detail: t("Version check enabled"),
})
})
}
async function loadVersion() {
blockVersionStatusEl.value = t("Loading")
blockVersionStatusEl.value = await adminService.findVersion()
}
const blockUsers = ref(null)
const blockCourses = ref(null)
const blockSessions = ref(null)
const blockGradebook = ref(null)
const blockSkills = ref(null)
const blockPrivacy = ref(null)
const blockSettings = ref(null)
const blockPlatform = ref(null)
const blockChamilo = ref(null)
async function loadBlocks() {
const blocks = await adminService.findBlocks()
blockUsers.value = blocks.users || null
blockCourses.value = blocks.courses || null
blockSessions.value = blocks.sessions || null
blockGradebook.value = blocks.gradebook || null
blockSkills.value = blocks.skills || null
blockPrivacy.value = blocks.data_privacy || null
blockSettings.value = blocks.settings || null
blockPlatform.value = blocks.platform || null
blockChamilo.value = blocks.chamilo || null
}
return {
blockVersionStatusEl,
checkVersion,
blockUsers,
blockCourses,
blockSessions,
blockGradebook,
blockSkills,
blockPrivacy,
blockSettings,
blockPlatform,
blockChamilo,
loadBlocks,
}
}

@ -0,0 +1,40 @@
import axios from "axios"
export default {
/**
* @param {boolean} doNotListCampus
* @returns {Promise<void>}
*/
registerCampus: async (doNotListCampus) => {
await axios.post("/admin/register-campus", {
donotlistcampus: doNotListCampus,
})
},
/**
* @returns {Promise<string>}
*/
findAnnouncements: async () => {
const { data } = await axios.get("/main/inc/ajax/admin.ajax.php?a=get_latest_news")
return data
},
/**
* @returns {Promise<string>}
*/
findVersion: async () => {
const { data } = await axios.get("/main/inc/ajax/admin.ajax.php?a=version")
return data
},
/**
* @returns {Promise<Object>}
*/
findBlocks: async () => {
const { data } = await axios.get("/admin/index")
return data
},
}

@ -0,0 +1,32 @@
import api from "../config/api"
export default {
/**
* @param {Object} params
* @returns {Promise<Object>}
*/
async post(params) {
const { data } = await api.post("/api/pages", params)
return data
},
/**
* @param {string} iri
* @param {Object} params
* @returns {Promise<Object>}
*/
async update(iri, params) {
const { data } = await api.put(iri, params)
return data
},
/**
* @param {string} iri
* @returns {Promise<void>}
*/
async delete(iri) {
await api.delete(iri)
},
}

@ -16,78 +16,97 @@
>
<AdminBlock
v-if="blockUsers"
:id="blockUsers.id"
v-model:extra-content="blockUsers.extraContent"
:description="t('Here you can manage registered users within your platform')"
:editable="blockUsers.editable"
:items="blockUsers.items"
:search-url="blockUsers.searchUrl"
:title="t('User management')"
class="block-admin-users"
icon="account"
/>
<AdminBlock
v-if="blockCourses"
:id="blockCourses.id"
v-model:extra-content="blockCourses.extraContent"
:description="t('Create and manage your courses in a simple way')"
:editable="blockCourses.editable"
:items="blockCourses.items"
:search-url="blockCourses.searchUrl"
:title="t('Course management')"
class="block-admin-courses"
icon="courses"
/>
<AdminBlock
v-if="blockSessions"
:id="blockSessions.id"
v-model:extra-content="blockSessions.extraContent"
:editable="blockSessions.editable"
:description="t('Create course packages for a certain time with training sessions')"
:items="blockSessions.items"
:search-url="blockSessions.searchUrl"
:title="t('Sessions management')"
class="block-admin-sessions"
icon="sessions"
/>
<AdminBlock
v-if="blockGradebook"
:id="blockGradebook.id"
v-model:extra-content="blockGradebook.extraContent"
:editable="blockGradebook.editable"
:items="blockGradebook.items"
:title="t('Assessments')"
class="block-admin-gradebook"
icon="gradebook"
/>
<AdminBlock
v-if="blockSkills"
:id="blockSkills.id"
v-model:extra-content="blockSkills.extraContent"
:editable="blockSkills.editable"
:description="t('Manage the skills of your users, through courses and badges')"
:items="blockSkills.items"
:title="t('Skills')"
class="block-admin-skills"
icon="gradebook"
/>
<AdminBlock
v-if="blockPrivacy"
:id="blockPrivacy.id"
v-model:extra-content="blockPrivacy.extraContent"
:editable="blockPrivacy.editable"
:items="blockPrivacy.items"
:title="t('Personal data protection')"
class="block-admin-privacy"
icon="anonymous"
/>
<AdminBlock
v-if="blockSettings"
:id="blockSettings.id"
v-model:extra-content="blockSettings.extraContent"
:description="t('View the status of your server, perform performance tests')"
:editable="blockSettings.editable"
:items="blockSettings.items"
:title="t('System')"
class="block-admin-settings"
icon="settings"
/>
<div
v-if="isAdmin"
v-if="securityStore.isAdmin"
class="admin-index__block-container block-admin-version"
>
<div class="admin-index__block">
<h4 v-t="'Version check'" />
<div
v-if="'false' === platformConfigurationStore.getSetting('platform.registered')"
class="admin-block-version"
v-if="blockVersionStatusEl"
class="block-admin-version__status"
v-html="blockVersionStatusEl"
/>
<div
v-else
class="block-admin-version__form"
>
<i18n-t
class="mb-3"
@ -135,40 +154,28 @@
/>
</form>
</div>
<div
ref="blockAdminVersionCheck"
class="block-admin-version_check"
/>
</div>
</div>
<AdminBlock
v-if="blockPlatform"
:id="blockPlatform.id"
v-model:extra-content="blockPlatform.extraContent"
:editable="blockPlatform.editable"
:description="t('Configure your platform, view reports, publish and send announcements globally')"
:items="blockPlatform.items"
:search-url="blockPlatform.searchUrl"
:title="t('Platform management')"
class="block-admin-platform"
icon="admin-settings"
>
<li
:aria-label="t('Colors')"
class="p-menuitem"
role="menuitem"
>
<div class="p-menuitem-content">
<router-link class="p-menuitem-link" :to="{name: 'AdminConfigurationColors'}">
<span class="p-menuitem-text" v-text="t('Colors')" />
</router-link>
</div>
</li>
</AdminBlock>
/>
<AdminBlock
v-if="blockChamilo"
:id="blockChamilo.id"
v-model:extra-content="blockChamilo.extraContent"
:editable="blockChamilo.editable"
:description="t('Learn more about Chamilo and its use, official references links')"
:items="blockChamilo.items"
class="block-admin-chamilo"
icon="admin-settings"
title="Chamilo.org"
/>
@ -176,83 +183,43 @@
</template>
<script setup>
import { computed, onMounted, ref } from "vue"
import { ref } from "vue"
import { useI18n } from "vue-i18n"
import { useStore } from "vuex"
import { useToast } from "primevue/usetoast"
import Button from "primevue/button"
import Checkbox from "primevue/checkbox"
import Skeleton from "primevue/skeleton"
import AdminBlock from "../../components/admin/AdminBlock"
import axios from "axios"
import { usePlatformConfig } from "../../store/platformConfig";
import AdminConfigureColors from "./AdminConfigureColors.vue";
import { useSecurityStore } from "../../store/securityStore"
const { t } = useI18n()
import { useIndexBlocks } from "../../composables/admin/indexBlocks"
const store = useStore()
const platformConfigurationStore = usePlatformConfig()
const toast = useToast()
const { t } = useI18n()
const isAdmin = computed(() => store.getters["security/isAdmin"])
const securityStore = useSecurityStore()
const doNotListCampus = ref(false)
const {
blockVersionStatusEl,
checkVersion,
blockUsers,
blockCourses,
blockSessions,
blockGradebook,
blockSkills,
blockPrivacy,
blockSettings,
blockPlatform,
blockChamilo,
loadBlocks,
} = useIndexBlocks()
function checkVersionOnSubmit() {
axios
.post("/admin/register-campus", {
donotlistcampus: doNotListCampus.value,
})
.then(() =>
toast.add({
severity: "success",
detail: t("Version check enabled"),
}),
)
checkVersion(doNotListCampus.value)
}
const blockAdminVersionCheck = ref()
onMounted(() => {
if (isAdmin.value) {
if ("false" === platformConfigurationStore.getSetting("admin.admin_chamilo_announcements_disable")) {
axios
.get("/main/inc/ajax/admin.ajax.php?a=get_latest_news")
.then(({ data }) => toast.add({ severity: "info", detail: data }))
}
axios.get("/main/inc/ajax/admin.ajax.php?a=version").then(({ data }) => {
if (blockAdminVersionCheck.value) {
blockAdminVersionCheck.value.innerHTML += data
}
})
}
})
const isLoadingBlocks = ref(true)
const blockUsers = ref(null)
const blockCourses = ref(null)
const blockSessions = ref(null)
const blockGradebook = ref(null)
const blockSkills = ref(null)
const blockPrivacy = ref(null)
const blockSettings = ref(null)
const blockPlatform = ref(null)
const blockChamilo = ref(null)
axios.get("/admin/index").then(({ data }) => {
isLoadingBlocks.value = false
blockUsers.value = data.users || null
blockCourses.value = data.courses || null
blockSessions.value = data.sessions || null
blockGradebook.value = data.gradebook || null
blockSkills.value = data.skills || null
blockPrivacy.value = data.data_privacy || null
blockSettings.value = data.settings || null
blockPlatform.value = data.platform || null
blockChamilo.value = data.chamilo || null
})
loadBlocks().then(() => (isLoadingBlocks.value = false))
</script>

@ -99,6 +99,29 @@ class CreateDefaultPages
$this->pageCategoryRepository->update($footerPrivateCategory);
// Categories for extra content in admin blocks
$adminBlocks = [
'block-admin-users',
'block-admin-courses',
'block-admin-sessions',
'block-admin-gradebook',
'block-admin-skills',
'block-admin-privacy',
'block-admin-settings',
'block-admin-platform',
'block-admin-chamilo',
];
foreach ($adminBlocks as $nameBlock) {
$usersAdminBlock = (new PageCategory())
->setTitle($nameBlock)
->setType('grid')
->setCreator($user)
;
$this->pageCategoryRepository->update($usersAdminBlock);
}
return true;
}
}

@ -7,10 +7,15 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Controller\Admin;
use Chamilo\CoreBundle\Controller\BaseController;
use Chamilo\CoreBundle\Entity\Page;
use Chamilo\CoreBundle\Entity\PageCategory;
use Chamilo\CoreBundle\Repository\PageCategoryRepository;
use Chamilo\CoreBundle\Repository\PageRepository;
use Chamilo\CoreBundle\Settings\SettingsManager;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
#[Security("is_granted('ROLE_ADMIN') or is_granted('ROLE_SESSION_MANAGER')")]
@ -23,7 +28,10 @@ class IndexBlocksController extends BaseController
public function __construct(
private readonly TranslatorInterface $translator,
private readonly SettingsManager $settingsManager
private readonly SettingsManager $settingsManager,
private readonly PageRepository $pageRepository,
private readonly PageCategoryRepository $pageCategoryRepository,
private readonly SerializerInterface $serializer
) {
$this->extAuthSource = [
'extldap' => [],
@ -38,70 +46,110 @@ class IndexBlocksController extends BaseController
$json = [];
$json['users'] = [
'id' => 'block-admin-users',
'searchUrl' => $this->generateUrl('legacy_main', ['name' => 'admin/user_list.php']),
'editable' => $this->isAdmin,
'items' => $this->getItemsUsers(),
'extraContent' => $this->getExtraContent('block-admin-users'),
];
if ($this->isAdmin) {
$json['courses'] = [
'id' => 'block-admin-courses',
'searchUrl' => $this->generateUrl('legacy_main', ['name' => 'admin/course_list.php']),
'editable' => true,
'items' => $this->getItemsCourses(),
'extraContent' => $this->getExtraContent('block-admin-courses'),
];
$json['platform'] = [
'id' => 'block-admin-platform',
'searchUrl' => $this->generateUrl('chamilo_platform_settings_search'),
'editable' => true,
'items' => $this->getItemsPlatform(),
'extraContent' => $this->getExtraContent('block-admin-platform'),
];
/* Settings */
$json['settings'] = [
'id' => 'block-admin-settings',
'editable' => false,
'items' => $this->getItemsSettings(),
'extraContent' => $this->getExtraContent('block-admin-settings'),
];
// Skills
if ('true' === $this->settingsManager->getSetting('skill.allow_skills_tool')) {
$json['skills'] = [
'id' => 'block-admin-skills',
'editable' => false,
'items' => $this->getItemsSkills(),
'extraContent' => $this->getExtraContent('block-admin-skills'),
];
}
if ('true' === $this->settingsManager->getSetting('gradebook.gradebook_dependency')) {
$json['gradebook'] = [
'id' => 'block-admin-gradebook',
'editable' => false,
'items' => $this->getItemsGradebook(),
'extraContent' => $this->getExtraContent('block-admin-gradebook'),
];
}
// Data protection
if ('true' !== $this->settingsManager->getSetting('profile.disable_gdpr')) {
$json['data_privacy'] = [
'id' => 'block-admin-privacy',
'editable' => false,
'items' => $this->getItemsPrivacy(),
'extraContent' => $this->getExtraContent('block-admin-privacy'),
];
}
/* Chamilo.org */
$json['chamilo'] = [
'id' => 'block-admin-chamilo',
'editable' => false,
'items' => $this->getItemsChamilo(),
'extraContent' => $this->getExtraContent('block-admin-chamilo'),
];
}
/* Sessions */
$json['sessions'] = [
'id' => 'block-admin-sessions',
'searchUrl' => $this->generateUrl('legacy_main', ['name' => 'session/session_list.php']),
'editable' => $this->isAdmin,
'items' => $this->getItemsSessions(),
'extraContent' => $this->getExtraContent('block-admin-sessions'),
];
return $this->json($json);
}
private function getExtraContent(string $title): ?array
{
/** @var Page|null $page */
$page = $this->pageRepository->findOneBy(['title' => $title]);
$pageJsonld = $this->serializer->serialize($page, 'jsonld', ['groups' => ['adminblock:read']]);
$pageArray = json_decode($pageJsonld, true);
if ($page) {
return $pageArray;
}
/** @var PageCategory $category */
$category = $this->pageCategoryRepository->findOneBy(['title' => $title]);
$categoryJsonld = $this->serializer->serialize($category, 'jsonld', ['groups' => ['page:read']]);
$categoryArray = json_decode($categoryJsonld, true);
return [
'category' => $categoryArray['@id'],
];
}
private function getItemsUsers(): array
{
$items = [];
@ -339,7 +387,7 @@ class IndexBlocksController extends BaseController
];
$items[] = [
'class' => 'item-global-agenda',
'url' => '/resources/ccalendarevent?type=global',
'route' => ['name' => 'CCalendarEventList', 'query' => ['type' => 'global']],
'label' => $this->translator->trans('Global agenda'),
];
@ -353,7 +401,7 @@ class IndexBlocksController extends BaseController
$items[] = [
'class' => 'item-pages-list',
'url' => '/resources/pages',
'route' => ['name' => 'PageList'],
'label' => $this->translator->trans('Pages'),
];
$items[] = [
@ -414,7 +462,7 @@ class IndexBlocksController extends BaseController
if ('true' === $this->settingsManager->getSetting('registration.allow_terms_conditions')) {
$items[] = [
'class' => 'item-terms-and-conditions',
'url' => '/resources/terms-conditions',
'route' => ['name' => 'TermsConditions'],
'label' => $this->translator->trans('Terms and Conditions'),
];
}
@ -608,6 +656,12 @@ class IndexBlocksController extends BaseController
];
}
$items[] = [
'class' => 'item-colors',
'route' => ['name' => 'AdminConfigurationColors'],
'label' => $this->translator->trans('Colors'),
];
return $items;
}

@ -31,7 +31,7 @@ use Symfony\Component\Validator\Constraints as Assert;
new Post(security: 'is_granted(\'ROLE_ADMIN\')'),
],
normalizationContext: [
'groups' => ['page:read', 'timestampable_created:read', 'timestampable_updated:read'],
'groups' => ['page:read', 'timestampable_created:read', 'timestampable_updated:read', 'adminblock:read'],
],
denormalizationContext: [
'groups' => ['page:write'],
@ -61,7 +61,7 @@ class Page
#[Groups(['page:read', 'page:write'])]
#[ORM\Column(name: 'title', type: 'string', length: 255)]
protected string $title;
#[Groups(['page:read', 'page:write'])]
#[Groups(['page:read', 'page:write', 'adminblock:read'])]
#[Assert\NotBlank]
#[ORM\Column(name: 'content', type: 'text')]
protected string $content;
@ -88,7 +88,7 @@ class Page
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(name: 'creator_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
protected User $creator;
#[Groups(['page:read', 'page:write'])]
#[Groups(['page:read', 'page:write', 'adminblock:read'])]
#[Gedmo\SortableGroup]
#[ORM\ManyToOne(targetEntity: PageCategory::class, inversedBy: 'pages')]
#[ORM\JoinColumn(name: 'category_id', referencedColumnName: 'id', onDelete: 'SET NULL')]

Loading…
Cancel
Save