Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
chamilo-lms/assets/vue/components/basecomponents/BaseTinyEditor.vue

269 lines
6.0 KiB

<template>
<div class="field">
<FloatLabel>
<TinyEditor
:id="editorId"
v-model="modelValue"
:init="editorConfig"
:required="required"
/>
<label
v-if="title"
:for="editorId"
v-text="title"
/>
</FloatLabel>
<small
v-if="helpText"
v-text="helpText"
/>
</div>
</template>
<script setup>
import { computed, ref, watch } from "vue"
import TinyEditor from "../../components/Editor"
import { useRoute, useRouter } from "vue-router"
import { useCidReqStore } from "../../store/cidReq"
import { storeToRefs } from "pinia"
import { useSecurityStore } from "../../store/securityStore"
import FloatLabel from "primevue/floatlabel"
import { useLocale } from "../../composables/locale"
const modelValue = defineModel({
type: String,
required: true,
})
const props = defineProps({
editorId: {
type: String,
required: true,
},
required: {
type: Boolean,
default: false,
},
title: {
type: String,
default: "",
},
editorConfig: {
type: Object,
default: () => {},
},
// A helper text shown below editor
helpText: {
type: String,
default: "",
},
// if true the Chamilo inner file manager will be shown
// if false the system file picker will be shown
useFileManager: {
type: Boolean,
default: false,
},
fullPage: {
type: Boolean,
required: false,
default: true,
},
})
const router = useRouter()
const route = useRoute()
const parentResourceNodeId = ref(0)
const securityStore = useSecurityStore()
const cidReqStore = useCidReqStore()
const { course } = storeToRefs(cidReqStore)
// Set the parent node ID based on the user's resource node ID or route parameter
parentResourceNodeId.value = securityStore.user.resourceNode.id
if (route.params.node) {
parentResourceNodeId.value = Number(route.params.node)
}
const supportedLanguages = {
ar: "ar.js",
de: "de.js",
en: "en.js",
es: "es.js",
fr_FR: "fr_FR.js",
it: "it.js",
nl: "nl.js",
pt_PT: "pt_PT.js",
ru: "ru.js",
zh_CN: "zh_CN.js",
}
const { appLocale } = useLocale()
function getLanguageConfig(locale) {
const defaultLang = "en"
const url = "/libs/editor/langs/"
const isoCode = locale.split("_")[0]
let languageFile = supportedLanguages[isoCode]
let finalLanguage = isoCode
if (!languageFile) {
const regionalMatch = Object.entries(supportedLanguages).find(([key, value]) => key.startsWith(isoCode))
if (regionalMatch) {
languageFile = regionalMatch[1]
finalLanguage = regionalMatch[0]
} else {
languageFile = `${defaultLang}.js`
finalLanguage = defaultLang
}
}
return {
language: finalLanguage,
language_url: `${url}${languageFile}`,
}
}
const languageConfig = getLanguageConfig(appLocale.value)
const toolbarUndo = "undo redo"
const toolbarFormatText = "bold italic underline strikethrough"
const toolbarInsertMedia = "image media template link"
const toolbarFontConfig = "fontselect fontsizeselect formatselect"
const toolbarAlign = "alignleft aligncenter alignright alignjustify"
const toolbarIndent = "outdent indent"
const toolbarList = "numlist bullist"
const toolbarColor = "forecolor backcolor removeformat"
const toolbarPageBreak = "pagebreak"
const toolbarSpecialSymbols = "charmap emoticons"
const toolbarOther = "fullscreen preview save print"
const toolbarCode = "code codesample"
const toolbarTextDirection = "ltr rtl"
const defaultEditorConfig = {
skin: false,
content_css: ["/build/css/editor_content.css"],
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: "sliding",
autosave_ask_before_unload: true,
language: languageConfig.language,
language_url: languageConfig.language_url,
plugins: [
"advlist",
"anchor",
"autolink",
"charmap",
"code",
"codesample",
"directionality",
"fullscreen",
"emoticons",
"image",
"insertdatetime",
"link",
"lists",
"media",
"paste",
"preview",
"print",
"pagebreak",
"save",
"searchreplace",
"table",
"template",
"visualblocks",
"wordcount",
],
toolbar:
toolbarUndo +
" | " +
toolbarFormatText +
" | " +
toolbarInsertMedia +
" | " +
toolbarFontConfig +
" | " +
toolbarAlign +
" | " +
toolbarIndent +
" | " +
toolbarList +
" | " +
toolbarColor +
" | " +
toolbarPageBreak +
" | " +
toolbarSpecialSymbols +
" | " +
toolbarOther +
" | " +
toolbarCode +
" | " +
toolbarTextDirection,
content_style: ".tiny-content { font-family: Arial, sans-serif; }",
body_class: "tiny-content",
}
if (props.fullPage) {
defaultEditorConfig.plugins.push("fullpage")
defaultEditorConfig.toolbar += " | fullpage"
}
const editorConfig = computed(() => ({
...defaultEditorConfig,
...props.editorConfig,
file_picker_callback: filePickerCallback,
}))
watch(modelValue, (newValue) => {
if (newValue && !newValue.includes("tiny-content")) {
modelValue.value = `<div class="tiny-content">${newValue}</div>`
}
})
async function filePickerCallback(callback, value, meta) {
let url = getUrlForTinyEditor()
if ("image" === meta.filetype) {
url += "&type=images"
} else {
url += "&type=files"
}
window.addEventListener("message", function (event) {
let data = event.data
if (data.url) {
callback(data.url)
}
})
window.tinymce.activeEditor.windowManager.openUrl({
url: url,
title: "File Manager",
onMessage: (api, message) => {
if (message.mceAction === "fileSelected") {
callback(message.content.url)
api.close()
}
},
})
}
function getUrlForTinyEditor() {
if (!course.value) {
return router.resolve({
name: "FileManagerList",
params: {
node: parentResourceNodeId.value,
},
}).href
}
let queryParams = { cid: course.value.id, sid: 0, gid: 0, filetype: "file" }
return router.resolve({
name: "FileManagerList",
params: { node: parentResourceNodeId.value },
query: queryParams,
}).href
}
</script>