Merge pull request #5113 from christianbeeznest/GH-3804-2

Document: Add template list and tinyMCE integration - refs #3804
pull/5115/head
christianbeeznest 2 years ago committed by GitHub
commit deb6c3a7ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 57
      assets/css/app.scss
  2. 10
      assets/vue/components/documents/FormNewDocument.vue
  3. 26
      assets/vue/components/documents/TemplateList.vue
  4. 44
      assets/vue/views/documents/CreateFile.vue
  5. 57
      assets/vue/views/documents/UpdateFile.vue
  6. 2
      public/main/admin/settings.php
  7. 6
      src/CoreBundle/Controller/Admin/IndexBlocksController.php
  8. 33
      src/CoreBundle/Controller/TemplateController.php
  9. 3
      src/CoreBundle/Entity/SystemTemplate.php
  10. 19
      src/CoreBundle/Repository/SystemTemplateRepository.php

@ -520,6 +520,63 @@ table#skill_holder {
justify-content: center; justify-content: center;
} }
.documents-layout {
display: flex;
justify-content: space-between;
}
.template-list-container {
flex-basis: 20%;
max-height: 600px;
overflow-y: auto;
}
.documents-form-container {
flex-basis: 78%;
}
.template-item img {
border: 1px solid #ccc;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
border-radius: 4px;
transition: transform 0.2s ease;
}
.template-item img:hover {
transform: scale(1.05);
cursor: pointer;
}
.template-list {
display: flex;
flex-direction: column;
gap: 10px;
}
.template-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f9f9f9;
}
.template-item img {
width: 100%;
max-width: 200px;
height: auto;
object-fit: cover;
border-radius: 4px;
margin-bottom: 5px;
}
.template-item:hover {
background-color: #ececec;
cursor: pointer;
}
//@import 'primevue-md-light-indigo/theme.css'; //@import 'primevue-md-light-indigo/theme.css';
//@import '~primevue/resources/primevue.min.css'; //@import '~primevue/resources/primevue.min.css';

@ -87,11 +87,16 @@ export default {
data() { data() {
return { return {
title: null, title: null,
contentFile: null, contentFile: this.initialValues ? this.initialValues.contentFile : '',
parentResourceNodeId: null, parentResourceNodeId: null,
resourceNode: null, resourceNode: null,
}; };
}, },
watch: {
contentFile(newContent) {
tinymce.get('item_content').setContent(newContent);
}
},
computed: { computed: {
item() { item() {
return this.initialValues || this.values; return this.initialValues || this.values;
@ -180,6 +185,9 @@ export default {
); );
return false; return false;
}, },
updateContent(content) {
this.contentFile = content;
},
}, },
validations: { validations: {
item: { item: {

@ -0,0 +1,26 @@
<template>
<div class="template-list">
<div
v-for="template in templates"
:key="template.id"
class="template-item"
@click="selectTemplate(template.content)">
<img :src="template.image" />
<div>{{ template.title }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'TemplateList',
props: {
templates: []
},
methods: {
selectTemplate(content) {
this.$emit('template-selected', content);
},
}
}
</script>

@ -3,11 +3,23 @@
:handle-reset="resetForm" :handle-reset="resetForm"
:handle-submit="onSendFormData" :handle-submit="onSendFormData"
/> />
<DocumentsForm
ref="createForm" <div class="documents-layout">
:errors="violations" <div class="template-list-container">
:values="item" <TemplateList
/> :templates="templates"
@template-selected="addTemplateToEditor"
/>
</div>
<div class="documents-form-container">
<DocumentsForm
ref="createForm"
:errors="violations"
:values="item"
/>
</div>
</div>
<Panel <Panel
v-if="$route.query.cert === '1'" v-if="$route.query.cert === '1'"
:header="$t('Create your certificate copy-pasting the following tags. They will be replaced in the document by their student-specific value:')" :header="$t('Create your certificate copy-pasting the following tags. They will be replaced in the document by their student-specific value:')"
@ -26,6 +38,8 @@ import Toolbar from "../../components/Toolbar.vue"
import CreateMixin from "../../mixins/CreateMixin" import CreateMixin from "../../mixins/CreateMixin"
import { RESOURCE_LINK_PUBLISHED } from "../../components/resource_links/visibility" import { RESOURCE_LINK_PUBLISHED } from "../../components/resource_links/visibility"
import Panel from "primevue/panel" import Panel from "primevue/panel"
import TemplateList from "../../components/documents/TemplateList.vue"
import axios from "axios"
const servicePrefix = "Documents" const servicePrefix = "Documents"
@ -38,6 +52,7 @@ export default {
name: "DocumentsCreateFile", name: "DocumentsCreateFile",
servicePrefix, servicePrefix,
components: { components: {
TemplateList,
Loading, Loading,
Toolbar, Toolbar,
DocumentsForm, DocumentsForm,
@ -55,6 +70,7 @@ export default {
resourceLinkList: null, resourceLinkList: null,
contentFile: null, contentFile: null,
}, },
templates: [],
finalTags, finalTags,
}; };
}, },
@ -74,6 +90,19 @@ export default {
}, },
methods: { methods: {
addTemplateToEditor(templateContent) {
this.item.contentFile = templateContent;
},
fetchTemplates() {
axios.get('/system-templates')
.then(response => {
console.log(response.data);
this.templates = response.data;
})
.catch(error => {
console.error('There was an error fetching the templates:', error);
});
},
getCertificateTags(){ getCertificateTags(){
let finalTags = ""; let finalTags = "";
let tags = [ let tags = [
@ -107,6 +136,9 @@ export default {
return finalTags; return finalTags;
}, },
...mapActions('documents', ['createWithFormData', 'reset']) ...mapActions('documents', ['createWithFormData', 'reset'])
} },
mounted() {
this.fetchTemplates();
},
}; };
</script> </script>

@ -5,17 +5,27 @@
:handle-reset="resetForm" :handle-reset="resetForm"
:handle-submit="onSendFormData" :handle-submit="onSendFormData"
/> />
<DocumentsForm <div class="documents-layout">
ref="updateForm" <div class="template-list-container">
:errors="violations" <TemplateList
:values="item" :templates="templates"
> @template-selected="addTemplateToEditor"
<EditLinks />
:item="item" </div>
:show-share-with-user="false" <div class="documents-form-container">
links-type="users" <DocumentsForm
/> ref="updateForm"
</DocumentsForm> :errors="violations"
:values="item"
>
<EditLinks
:item="item"
:show-share-with-user="false"
links-type="users"
/>
</DocumentsForm>
</div>
</div>
<Loading :visible="isLoading || deleteLoading" /> <Loading :visible="isLoading || deleteLoading" />
</div> </div>
@ -29,6 +39,8 @@ import Loading from "../../components/Loading.vue"
import Toolbar from "../../components/Toolbar.vue" import Toolbar from "../../components/Toolbar.vue"
import UpdateMixin from "../../mixins/UpdateMixin" import UpdateMixin from "../../mixins/UpdateMixin"
import EditLinks from "../../components/resource_links/EditLinks.vue" import EditLinks from "../../components/resource_links/EditLinks.vue"
import TemplateList from "../../components/documents/TemplateList.vue"
import axios from "axios";
const servicePrefix = "Documents" const servicePrefix = "Documents"
@ -36,11 +48,17 @@ export default {
name: "DocumentsUpdate", name: "DocumentsUpdate",
servicePrefix, servicePrefix,
components: { components: {
TemplateList,
EditLinks, EditLinks,
Loading, Loading,
Toolbar, Toolbar,
DocumentsForm, DocumentsForm,
}, },
data() {
return {
templates: [],
};
},
mixins: [UpdateMixin], mixins: [UpdateMixin],
computed: { computed: {
...mapFields("documents", { ...mapFields("documents", {
@ -56,6 +74,20 @@ export default {
}), }),
}, },
methods: { methods: {
fetchTemplates() {
axios.get('/system-templates')
.then(response => {
this.templates = response.data;
})
.catch(error => {
console.error('Error fetching the templates:', error);
});
},
addTemplateToEditor(templateContent) {
if (this.$refs.updateForm && typeof this.$refs.updateForm.updateContent === 'function') {
this.$refs.updateForm.updateContent(templateContent);
}
},
...mapActions("documents", { ...mapActions("documents", {
createReset: "resetCreate", createReset: "resetCreate",
deleteItem: "del", deleteItem: "del",
@ -65,5 +97,8 @@ export default {
updateReset: "resetUpdate", updateReset: "resetUpdate",
}), }),
}, },
mounted() {
this.fetchTemplates();
},
} }
</script> </script>

@ -33,7 +33,7 @@ api_protect_admin_script();
$table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT); $table_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
// Setting breadcrumbs. // Setting breadcrumbs.
$interbreadcrumb[] = ['url' => 'index.php', 'name' => get_lang('Administration')]; $interbreadcrumb[] = ['url' => api_get_path(WEB_PATH).'admin', 'name' => get_lang('Administration')];
// Setting the name of the tool. // Setting the name of the tool.
$tool_name = get_lang('Configuration settings'); $tool_name = get_lang('Configuration settings');

@ -457,6 +457,12 @@ class IndexBlocksController extends BaseController
'label' => $this->translator->trans('Contact categories'), 'label' => $this->translator->trans('Contact categories'),
]; ];
$items[] = [
'class' => 'item-system-template-admin',
'url' => $this->generateUrl('legacy_main', ['name' => 'admin/settings.php', 'category' => 'Templates']),
'label' => $this->translator->trans('System Templates'),
];
return $items; return $items;
} }

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Controller;
use Chamilo\CoreBundle\Repository\AssetRepository;
use Chamilo\CoreBundle\Repository\SystemTemplateRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class TemplateController extends AbstractController
{
#[Route('/system-templates', name: 'system-templates')]
public function getTemplates(SystemTemplateRepository $templateRepository, AssetRepository $assetRepository): JsonResponse
{
$templates = $templateRepository->findAll();
$data = array_map(function ($template) use ($assetRepository) {
return [
'id' => $template->getId(),
'title' => $template->getTitle(),
'comment' => $template->getComment(),
'content' => $template->getContent(),
'image' => $template->getImage() ? $assetRepository->getAssetUrl($template->getImage()) : null,
];
}, $templates);
return $this->json($data);
}
}

@ -6,13 +6,14 @@ declare(strict_types=1);
namespace Chamilo\CoreBundle\Entity; namespace Chamilo\CoreBundle\Entity;
use Chamilo\CoreBundle\Repository\SystemTemplateRepository;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
/** /**
* SystemTemplate. * SystemTemplate.
*/ */
#[ORM\Table(name: 'system_template')] #[ORM\Table(name: 'system_template')]
#[ORM\Entity] #[ORM\Entity(repositoryClass: SystemTemplateRepository::class)]
class SystemTemplate class SystemTemplate
{ {
#[ORM\Column(name: 'id', type: 'integer')] #[ORM\Column(name: 'id', type: 'integer')]

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Repository;
use Chamilo\CoreBundle\Entity\SystemTemplate;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class SystemTemplateRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, SystemTemplate::class);
}
}
Loading…
Cancel
Save