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

pull/5293/head
Yannick Warnier 2 years ago
commit aeda53e71b
  1. 52
      assets/vue/components/basecomponents/BaseEditor.vue
  2. 298
      assets/vue/components/basecomponents/BaseTinyEditor.vue
  3. 6
      assets/vue/components/basecomponents/TinyEditorOptions.js
  4. 180
      assets/vue/components/ctoolintro/Form.vue
  5. 37
      assets/vue/components/documents/FormNewDocument.vue
  6. 104
      assets/vue/components/page/Form.vue
  7. 4
      assets/vue/components/usergroup/GroupDiscussionTopics.vue
  8. 4
      assets/vue/components/usergroup/GroupDiscussions.vue
  9. 6
      assets/vue/views/ccalendarevent/CCalendarEventList.vue
  10. 23
      assets/vue/views/message/MessageCreate.vue
  11. 23
      assets/vue/views/message/MessageReply.vue
  12. 7
      assets/vue/views/page/EditorDemo.vue
  13. 26
      assets/vue/views/terms/TermsEdit.vue

@ -1,52 +0,0 @@
<template>
<div class="base-editor">
<label v-if="title" :for="editorId">{{ title }}</label>
<TinyEditor
:id="editorId"
:model-value="modelValue"
:init="editorConfig"
:required="required"
@update:model-value="updateValue"
@input="updateValue"
/>
<p v-if="helpText" class="help-text">{{ helpText }}</p>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
const props = defineProps({
editorId: String,
modelValue: String,
required: Boolean,
editorConfig: Object,
title: String,
helpText: String
})
const emit = defineEmits(['update:modelValue'])
const updateValue = (value) => {
emit('update:modelValue', value)
document.getElementById(props.editorId).value = value
}
const defaultEditorConfig = {
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 280,
toolbar_mode: 'sliding',
autosave_ask_before_unload: true,
plugins: [
'fullpage advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons',
],
toolbar: 'undo redo | bold italic underline strikethrough | ...',
}
const editorConfig = computed(() => ({
...defaultEditorConfig,
...props.editorConfig
}))
function browser(callback, value, meta) {
}
</script>

@ -1,6 +1,11 @@
<template>
<div class="base-tiny-editor">
<label v-if="title" :for="editorId">{{ title }}</label>
<label
v-if="title"
:for="editorId"
>
{{ title }}
</label>
<TinyEditor
:id="editorId"
:model-value="modelValue"
@ -9,30 +14,64 @@
@update:model-value="updateValue"
@input="updateValue"
/>
<p v-if="helpText" class="help-text">{{ helpText }}</p>
<p
v-if="helpText"
class="help-text"
>
{{ helpText }}
</p>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import TinyEditor from '@tinymce/tinymce-vue'
import { computed, ref } from "vue"
import TinyEditor from "@tinymce/tinymce-vue"
import { useRoute, useRouter } from "vue-router"
import { useCidReqStore } from "../../store/cidReq"
import { storeToRefs } from "pinia"
import { useStore } from "vuex"
import { TINYEDITOR_MODE_DOCUMENTS, TINYEDITOR_MODE_PERSONAL_FILES, TINYEDITOR_MODES } from "./TinyEditorOptions"
const props = defineProps({
editorId: String,
modelValue: String,
required: Boolean,
editorConfig: Object,
title: String,
helpText: String,
mode: { type: String, default: 'personal_files' },
useFileManager: { type: Boolean, default: false }
editorId: {
type: String,
required: true,
},
modelValue: {
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,
},
// change mode when useFileManager=True
mode: {
type: String,
default: TINYEDITOR_MODE_PERSONAL_FILES,
validator: (value) => TINYEDITOR_MODES.includes(value),
},
})
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(["update:modelValue"])
const router = useRouter()
const route = useRoute()
const parentResourceNodeId = ref(0)
@ -47,115 +86,188 @@ if (route.params.node) {
}
const updateValue = (value) => {
emit('update:modelValue', value)
emit("update:modelValue", 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_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
skin_url: "/build/libs/tinymce/skins/ui/oxide",
content_css: "/build/libs/tinymce/skins/content/default/content.css",
branding: false,
relative_urls: false,
height: 280,
toolbar_mode: 'sliding',
height: 500,
toolbar_mode: "sliding",
autosave_ask_before_unload: true,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons',
"advlist",
"anchor",
"autolink",
"charmap",
"code",
"codesample",
"directionality",
"fullpage",
"fullscreen",
"emoticons",
"image",
"insertdatetime",
"link",
"lists",
"media",
"paste",
"preview",
"print",
"pagebreak",
"save",
"searchreplace",
"table",
"template",
"visualblocks",
"wordcount",
],
toolbar: 'undo redo | bold italic underline strikethrough | ...',
file_picker_callback: filePickerCallback
toolbar:
toolbarUndo +
" | " +
toolbarFormatText +
" | " +
toolbarInsertMedia +
" | " +
toolbarFontConfig +
" | " +
toolbarAlign +
" | " +
toolbarIndent +
" | " +
toolbarList +
" | " +
toolbarColor +
" | " +
toolbarPageBreak +
" | " +
toolbarSpecialSymbols +
" | " +
toolbarOther +
" | " +
toolbarCode +
" | " +
toolbarTextDirection,
file_picker_callback: filePickerCallback,
}
const editorConfig = computed(() => ({
...defaultEditorConfig,
...props.editorConfig
...props.editorConfig,
}))
function filePickerCallback(callback, value, meta) {
async function filePickerCallback(callback, value, meta) {
if (!props.useFileManager) {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.style.display = 'none';
input.onchange = () => {
const file = input.files[0];
const title = file.name;
const comment = '';
const fileType = 'file';
const resourceLinkList = [];
const formData = new FormData();
formData.append('uploadFile', file);
formData.append('title', title);
formData.append('comment', comment);
formData.append('parentResourceNodeId', parentResourceNodeId.value);
formData.append('filetype', fileType);
formData.append('resourceLinkList', resourceLinkList);
fetch('/file-manager/upload-image', {
method: 'POST',
body: formData,
})
.then(response => response.json())
.then(data => {
if (data.location) {
callback(data.location);
} else {
console.error('Failed to upload file');
}
})
.catch(error => console.error('Error uploading file:', error))
.finally(() => document.body.removeChild(input));
};
document.body.appendChild(input);
input.click();
return
}
let url;
if (props.mode === 'personal_files') {
url = '/resources/filemanager/personal_list/' + parentResourceNodeId.value;
} else if (props.mode === 'documents') {
const cidReqStore = useCidReqStore();
const { course, session } = storeToRefs(cidReqStore);
let nodeId = course.value && course.value.resourceNode ? course.value.resourceNode.id : null;
if (!nodeId) {
console.error('Resource node ID is not available.');
return;
const input = document.createElement("input")
input.setAttribute("type", "file")
if ("image" === meta.filetype) {
input.accept = "image/*"
} else if ("media" === meta.filetype) {
input.accept = "audio/*, video/*"
}
let folderParams = Object.entries(route.query).map(([key, value]) => `${key}=${value}`).join('&');
url = router.resolve({ name: "DocumentForHtmlEditor", params: { id: nodeId }, query: route.query }).href;
input.style.display = "none"
input.onchange = inputFileHandler(callback, input)
document.body.appendChild(input)
input.click()
return
}
if (meta.filetype === 'image') {
url += "&type=images";
let url = getUrlForTinyEditor(props.mode)
if (meta.filetype === "image") {
url += "&type=images"
} else {
url += "&type=files";
url += "&type=files"
}
window.addEventListener("message", function (event) {
var data = event.data;
let data = event.data
if (data.url) {
url = data.url;
callback(url);
url = data.url
callback(url)
}
});
})
// tinymce is already in the global scope, set by backend and php
tinymce.activeEditor.windowManager.openUrl({
url: url,
title: "File manager",
onMessage: (api, message) => {
if (message.mceAction === 'fileSelected') {
const fileUrl = message.content;
callback(fileUrl);
api.close();
if (message.mceAction === "fileSelected") {
const fileUrl = message.content
callback(fileUrl)
api.close()
}
},
})
}
function inputFileHandler(callback, input) {
return async () => {
const file = input.files[0]
const title = file.name
const comment = ""
const fileType = "file"
const resourceLinkList = []
const formData = new FormData()
formData.append("uploadFile", file)
formData.append("title", title)
formData.append("comment", comment)
formData.append("parentResourceNodeId", parentResourceNodeId.value)
formData.append("filetype", fileType)
formData.append("resourceLinkList", resourceLinkList)
try {
let response = await fetch("/file-manager/upload-image", {
method: "POST",
body: formData,
})
const { data, location } = await response.json()
if (location) {
callback(location, { alt: data.title })
} else {
console.error("Failed to upload file")
}
} catch (error) {
console.error("Error uploading file:", error)
} finally {
document.body.removeChild(input)
}
}
}
function getUrlForTinyEditor(mode) {
if (props.mode === TINYEDITOR_MODE_PERSONAL_FILES) {
return "/resources/filemanager/personal_list/" + parentResourceNodeId.value
} else if (props.mode === TINYEDITOR_MODE_DOCUMENTS) {
const cidReqStore = useCidReqStore()
const { course } = storeToRefs(cidReqStore)
let nodeId = course.value && course.value.resourceNode ? course.value.resourceNode.id : null
if (!nodeId) {
console.error("Resource node ID is not available.")
return
}
});
return router.resolve({ name: "DocumentForHtmlEditor", params: { id: nodeId }, query: route.query }).href
} else {
console.error(`Mode "${mode}" is not valid. Check valid modes on TinyEditorOptions.js`)
}
}
</script>

@ -0,0 +1,6 @@
// option to show dialog to upload to personal files in Chamilo
export const TINYEDITOR_MODE_PERSONAL_FILES = "personal_files"
// ??
export const TINYEDITOR_MODE_DOCUMENTS = "documents"
export const TINYEDITOR_MODES = [TINYEDITOR_MODE_PERSONAL_FILES, TINYEDITOR_MODE_DOCUMENTS]

@ -1,27 +1,8 @@
<template>
<q-form>
<TinyEditor
id="introText"
<BaseTinyEditor
v-model="item.introText"
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: 'sliding',
file_picker_callback: browser,
autosave_ask_before_unload: true,
plugins: [
'fullpage advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons ' +
extraPlugins,
],
toolbar:
'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl | ' +
extraPlugins,
}"
editor-id="introText"
required
/>
<!-- For extra content-->
@ -29,131 +10,60 @@
</q-form>
</template>
<script>
import useVuelidate from "@vuelidate/core";
import { ref } from "vue";
import { usePlatformConfig } from "../../store/platformConfig";
import {useRoute} from "vue-router";
export default {
name: "ToolIntroForm",
props: {
values: {
type: Object,
required: true,
},
errors: {
type: Object,
default: () => {},
},
initialValues: {
type: Object,
default: () => {},
},
},
setup() {
const extraPlugins = ref("");
<script setup>
import useVuelidate from "@vuelidate/core"
import { computed, ref, defineExpose } from "vue"
import { usePlatformConfig } from "../../store/platformConfig"
import { useRoute } from "vue-router"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
const platformConfigStore = usePlatformConfig();
if ("true" === platformConfigStore.getSetting("editor.translate_html")) {
extraPlugins.value = "translatehtml";
}
const route = useRoute();
const parentResourceNodeId = ref(route.query.parentResourceNodeId);
return { v$: useVuelidate(), extraPlugins, parentResourceNodeId};
const props = defineProps({
values: {
type: Object,
required: true,
},
data() {
return {
introText: null,
parentResourceNodeId: null,
resourceNode: null,
};
errors: {
type: Object,
default: () => {},
},
computed: {
item() {
return this.initialValues || this.values;
},
violations() {
return this.errors || {};
},
initialValues: {
type: Object,
default: () => {},
},
methods: {
browser(callback, value, meta) {
let nodeId = this.parentResourceNodeId;
let folderParams = this.$route.query;
let url = this.$router.resolve({
name: "DocumentForHtmlEditor",
params: { node: nodeId },
query: folderParams,
});
url = url.fullPath;
console.log(url);
if (meta.filetype === "image") {
url = url + "&type=images";
} else {
url = url + "&type=files";
}
})
console.log(url);
const route = useRoute()
const introText = ref(null)
const parentResourceNodeId = ref(route.query.parentResourceNodeId)
const resourceNode = ref(null)
window.addEventListener("message", function (event) {
var data = event.data;
if (data.url) {
url = data.url;
console.log(meta); // {filetype: "image", fieldname: "src"}
callback(url);
}
});
const item = computed(() => {
return props.initialValues || props.values
})
tinymce.activeEditor.windowManager.openUrl(
{
url: url, // use an absolute path!
title: "file manager",
/*width: 900,
height: 450,
resizable: 'yes'*/
},
{
oninsert: function (file, fm) {
var url, reg, info;
const violations = computed(() => {
return props.errors || {}
})
// URL normalization
url = fm.convAbsUrl(file.url);
const extraPlugins = ref("")
// Make file info
info = file.name + " (" + fm.formatSize(file.size) + ")";
const platformConfigStore = usePlatformConfig()
// Provide file and text for the link dialog
if (meta.filetype === "file") {
callback(url, { text: info, title: info });
}
if ("true" === platformConfigStore.getSetting("editor.translate_html")) {
extraPlugins.value = "translatehtml"
}
// Provide image and alt text for the image dialog
if (meta.filetype === "image") {
callback(url, { alt: info });
}
// Provide alternative source and posted for the media dialog
if (meta.filetype === "media") {
callback(url);
}
},
}
);
return false;
const validations = {
item: {
introText: {
//required,
},
parentResourceNodeId: {},
resourceNode: {},
},
validations: {
item: {
introText: {
//required,
},
parentResourceNodeId: {},
resourceNode: {},
},
},
};
}
const v$ = useVuelidate(validations, { item })
defineExpose({ v$: v$ })
</script>

@ -10,34 +10,16 @@
<div class="field">
<div class="p-float-label">
<div class="html-editor-container">
<TinyEditor
<BaseTinyEditor
v-if="
(item.resourceNode &&
item.resourceNode.resourceFile &&
item.resourceNode.resourceFile.text) ||
item.newDocument
"
id="item_content"
(
item.resourceNode
&& item.resourceNode.resourceFile
&& item.resourceNode.resourceFile.text
)
|| item.newDocument"
v-model="item.contentFile"
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: 'sliding',
file_picker_callback: browser,
autosave_ask_before_unload: true,
plugins: [
'fullpage advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons ' +
extraPlugins,
],
toolbar:
'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl | ' +
extraPlugins,
}"
editor-id="item_content"
required
/>
</div>
@ -56,10 +38,11 @@ import { required } from "@vuelidate/validators";
import { ref } from "vue";
import { usePlatformConfig } from "../../store/platformConfig";
import BaseInputTextWithVuelidate from "../basecomponents/BaseInputTextWithVuelidate.vue"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
export default {
name: "DocumentsForm",
components: { BaseInputTextWithVuelidate },
components: { BaseTinyEditor, BaseInputTextWithVuelidate },
props: {
values: {
type: Object,

@ -40,44 +40,9 @@
/>
<div class="field">
<TinyEditor
id="item_content"
<BaseTinyEditor
v-model="v$.item.content.$model"
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: 'sliding',
file_picker_callback: function(callback, value, meta) {
if (meta.filetype === 'image') {
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.onchange = function() {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function(e) {
// Esta es la URL de la imagen que se pasará al editor
callback(e.target.result, {
alt: file.name
});
};
reader.readAsDataURL(file);
};
input.click();
}
},
autosave_ask_before_unload: true,
plugins: [
'fullpage advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount '
],
toolbar: 'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl | ' + extraPlugins,
}
"
editor-id="item_content"
required
/>
</div>
@ -95,35 +60,33 @@
</template>
<script setup>
import { computed, ref, watch } from 'vue';
import BaseInputText from "../basecomponents/BaseInputText.vue";
import BaseCheckbox from "../basecomponents/BaseCheckbox.vue";
import BaseDropdown from "../basecomponents/BaseDropdown.vue";
import useVuelidate from '@vuelidate/core';
import { required } from '@vuelidate/validators';
import isEmpty from 'lodash/isEmpty';
import { useI18n } from 'vue-i18n';
import { computed, ref, watch } from "vue"
import BaseInputText from "../basecomponents/BaseInputText.vue"
import BaseCheckbox from "../basecomponents/BaseCheckbox.vue"
import BaseDropdown from "../basecomponents/BaseDropdown.vue"
import useVuelidate from "@vuelidate/core"
import { required } from "@vuelidate/validators"
import isEmpty from "lodash/isEmpty"
import { useI18n } from "vue-i18n"
import pageCategoryService from "../../services/pageCategoryService"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
const props = defineProps({
modelValue: {
type: Object,
default: () => {},
}
});
},
})
const emit = defineEmits([
'update:modelValue',
'submit',
]);
const emit = defineEmits(["update:modelValue", "submit"])
const { t } = useI18n();
const { t } = useI18n()
let locales = ref(window.languages);
let locales = ref(window.languages)
let categories = ref([]);
let categories = ref([])
const findAllPageCategories = async () => categories.value = await pageCategoryService.findAll()
const findAllPageCategories = async () => (categories.value = await pageCategoryService.findAll())
findAllPageCategories()
@ -131,17 +94,17 @@ watch(
() => props.modelValue,
(newValue) => {
if (!newValue) {
return;
return
}
if (!isEmpty(newValue.category) && !isEmpty(newValue.category['@id'])) {
emit('update:modelValue', {
if (!isEmpty(newValue.category) && !isEmpty(newValue.category["@id"])) {
emit("update:modelValue", {
...newValue,
category: newValue.category['@id']
});
category: newValue.category["@id"],
})
}
}
);
},
)
const validations = {
item: {
@ -160,19 +123,16 @@ const validations = {
category: {
required,
},
}
};
},
}
const v$ = useVuelidate(
validations,
{ item: computed(() => props.modelValue) }
);
const v$ = useVuelidate(validations, { item: computed(() => props.modelValue) })
function btnSaveOnClick () {
const item = { ...props.modelValue, ...v$.value.item.$model };
function btnSaveOnClick() {
const item = { ...props.modelValue, ...v$.value.item.$model }
emit('update:modelValue', item)
emit("update:modelValue", item)
emit('submit', item)
emit("submit", item)
}
</script>

@ -27,7 +27,7 @@
<Dialog header="Reply/Edit Message" v-model:visible="showMessageDialog" modal closable>
<form @submit.prevent="handleSubmit">
<BaseInputText v-if="isEditMode" id="title" :label="t('Title')" v-model="messageTitle" :isInvalid="titleError" />
<BaseEditor editorId="messageEditor" v-model="messageContent" title="Message" />
<BaseTinyEditor v-model="messageContent" editor-id="messageEditor" title="Message" />
<BaseFileUploadMultiple v-model="files" :label="t('Add files')" accept="image/png, image/jpeg" />
<BaseButton type="button" :label="t('Send message')" icon="save" @click="handleSubmit" class="mt-8" />
</form>
@ -43,8 +43,8 @@ import MessageItem from "./MessageItem.vue"
import BaseButton from "../basecomponents/BaseButton.vue"
import { useI18n } from "vue-i18n"
import BaseInputText from "../basecomponents/BaseInputText.vue"
import BaseEditor from "../basecomponents/BaseEditor.vue"
import BaseFileUploadMultiple from "../basecomponents/BaseFileUploadMultiple.vue"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
const router = useRouter()
const route = useRoute()

@ -34,7 +34,7 @@
<Dialog header="Create Thread" v-model:visible="showCreateThreadDialog" modal closable>
<form @submit.prevent="handleSubmit">
<BaseInputText id="title" label="Title" v-model="title" :isInvalid="titleError" />
<BaseEditor editorId="messageEditor" v-model="message" title="Message" />
<BaseTinyEditor v-model="message" editor-id="messageEditor" title="Message" />
<BaseFileUploadMultiple v-model="files" label="Add files" accept="image/png, image/jpeg" />
<BaseButton type="button" label="Send message" icon="save" @click="handleSubmit" class="mt-8" />
</form>
@ -50,8 +50,8 @@ import { useFormatDate } from "../../composables/formatDate"
import { useSocialInfo } from "../../composables/useSocialInfo"
import BaseButton from "../basecomponents/BaseButton.vue"
import BaseInputText from "../basecomponents/BaseInputText.vue"
import BaseEditor from "../basecomponents/BaseEditor.vue"
import BaseFileUploadMultiple from "../basecomponents/BaseFileUploadMultiple.vue"
import BaseTinyEditor from "../basecomponents/BaseTinyEditor.vue"
const route = useRoute()
const discussions = ref([])

@ -1,10 +1,8 @@
<template>
<div class="flex flex-col gap-4">
<!--CalendarSectionHeader
<CalendarSectionHeader
@add-click="showAddEventDialog"
@my-students-schedule-click="goToMyStudentsSchedule"
@session-planning-click="goToSessionPanning"
/-->
/>
<FullCalendar
ref="cal"

@ -33,27 +33,7 @@
/>
<div class="field">
<TinyEditor
v-model="item.content"
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: 'sliding',
file_picker_callback: browser,
autosave_ask_before_unload: true,
plugins: [
'advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons',
],
toolbar:
'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl',
}"
required
/>
<BaseTinyEditor v-model="item.content" editor-id="message" required />
</div>
<BaseButton
@ -82,6 +62,7 @@ import userService from "../../services/user"
import BaseUserAvatar from "../../components/basecomponents/BaseUserAvatar.vue"
import { useNotification } from "../../composables/notification"
import { capitalize } from "lodash"
import BaseTinyEditor from "../../components/basecomponents/BaseTinyEditor.vue"
const store = useStore()
const router = useRouter()

@ -31,27 +31,7 @@
</div>
<div class="field">
<TinyEditor
v-model="item.content"
:init="{
skin_url: '/build/libs/tinymce/skins/ui/oxide',
content_css: '/build/libs/tinymce/skins/content/default/content.css',
branding: false,
relative_urls: false,
height: 500,
toolbar_mode: 'sliding',
file_picker_callback: browser,
autosave_ask_before_unload: true,
plugins: [
'fullpage advlist autolink lists link image charmap print preview anchor',
'searchreplace visualblocks code fullscreen',
'insertdatetime media table paste wordcount emoticons',
],
toolbar:
'undo redo | bold italic underline strikethrough | insertfile image media template link | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor removeformat | pagebreak | charmap emoticons | fullscreen preview save print | code codesample | ltr rtl',
}"
required
/>
<BaseTinyEditor v-model="item.content" editor-id="message" required />
</div>
<BaseButton
@ -78,6 +58,7 @@ import { useI18n } from "vue-i18n"
import { useSecurityStore } from "../../store/securityStore"
import { useNotification } from "../../composables/notification"
import { formatDateTimeFromISO } from "../../utils/dates"
import BaseTinyEditor from "../../components/basecomponents/BaseTinyEditor.vue"
const item = ref({})
const store = useStore()

@ -13,12 +13,13 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref } from "vue"
import BaseTinyEditor from "../../components/basecomponents/BaseTinyEditor.vue"
import { TINYEDITOR_MODE_PERSONAL_FILES } from "../../components/basecomponents/TinyEditorOptions"
const editorContent = ref('')
const editorContent = ref("")
// Here you decide the mode, 'personal_files' or 'documents'
const editorMode = ref('personal_files')
const editorMode = ref(TINYEDITOR_MODE_PERSONAL_FILES)
// Decide if you want to use the file manager or not
const useFileManager = ref(true)
</script>

@ -24,16 +24,12 @@
<div v-if="termsLoaded">
<form @submit.prevent="saveTerms">
<BaseEditor
:editorId="'item_content'"
<BaseTinyEditor
v-model="termData.content"
editor-id="item_content"
:title="t('Personal Data Collection')"
>
<template #help-text>
<p>{{ t('Why do we collect this data?') }}</p>
</template>
</BaseEditor>
:help-text="t('Why do we collect this data?')"
/>
<BaseRadioButtons
:options="typeOptions"
@ -48,10 +44,12 @@
<!-- Extra fields -->
<div v-for="field in extraFields" :key="field.id" class="extra-field">
<component :is="getFieldComponent(field.type)" v-bind="field.props" @update:modelValue="field.props.modelValue = $event">
<template v-if="field.type === 'editor'" #help-text>
<p>{{ field.props.helpText }}</p>
</template>
<component
:is="getFieldComponent(field.type)"
v-bind="field.props"
:help-text="field.type === 'editor' ? field.props.helpText : '' "
@update:model-value="field.props.modelValue = $event"
>
</component>
</div>
@ -81,10 +79,10 @@ import Message from "primevue/message"
import BaseDropdown from "../../components/basecomponents/BaseDropdown.vue"
import BaseRadioButtons from "../../components/basecomponents/BaseRadioButtons.vue"
import BaseInputText from "../../components/basecomponents/BaseInputText.vue"
import BaseEditor from "../../components/basecomponents/BaseEditor.vue"
import { useI18n } from "vue-i18n"
import languageService from "../../services/languageService"
import legalService from "../../services/legalService"
import BaseTinyEditor from "../../components/basecomponents/BaseTinyEditor.vue"
const { t } = useI18n()
@ -169,7 +167,7 @@ function getFieldComponent(type) {
const componentMap = {
text: BaseInputText,
select: BaseDropdown,
editor: BaseEditor,
editor: BaseTinyEditor,
// Add more mappings as needed
}
return componentMap[type] || 'div'

Loading…
Cancel
Save