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.
269 lines
6.0 KiB
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>
|
|
|