add: full multi-tenancy control

This commit is contained in:
Cauê Faleiros
2026-02-02 15:31:15 -03:00
commit c6ec92802b
1711 changed files with 258106 additions and 0 deletions

View File

@@ -0,0 +1,219 @@
<?php ($placeholders = app('\Webkul\Automation\Helpers\Entity')->getEmailTemplatePlaceholders()); ?>
<v-tinymce <?php echo e($attributes); ?>></v-tinymce>
<?php if (! $__env->hasRenderedOnce('c834cd3d-c838-4d84-b005-4c4e0eeae7d7')): $__env->markAsRenderedOnce('c834cd3d-c838-4d84-b005-4c4e0eeae7d7');
$__env->startPush('scripts'); ?>
<!--
TODO (@devansh-webkul): Only this portion is pending; it just needs to be integrated using the Vite bundler. Currently,
there is an issue with relative paths in the plugins. I intend to address this task at the end.
-->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/6.6.2/tinymce.min.js"
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
<script
type="text/x-template"
id="v-tinymce-template"
>
</script>
<script type="module">
app.component('v-tinymce', {
template: '#v-tinymce-template',
props: ['selector', 'field'],
data() {
return {
currentSkin: document.documentElement.classList.contains('dark') ? 'oxide-dark' : 'oxide',
currentContentCSS: document.documentElement.classList.contains('dark') ? 'dark' : 'default',
isLoading: false,
};
},
mounted() {
this.destroyTinymceInstance();
this.init();
this.$emitter.on('change-theme', (theme) => {
this.destroyTinymceInstance();
this.currentSkin = (theme === 'dark') ? 'oxide-dark' : 'oxide';
this.currentContentCSS = (theme === 'dark') ? 'dark' : 'default';
this.init();
});
},
methods: {
destroyTinymceInstance() {
if (! tinymce.activeEditor) {
return;
}
tinymce.activeEditor.destroy();
},
init() {
let self = this;
let tinyMCEHelper = {
initTinyMCE: function(extraConfiguration) {
let self2 = this;
let config = {
relative_urls: false,
menubar: false,
remove_script_host: false,
document_base_url: '<?php echo e(asset('/')); ?>',
uploadRoute: '<?php echo e(route('admin.tinymce.upload')); ?>',
csrfToken: '<?php echo e(csrf_token()); ?>',
...extraConfiguration,
skin: self.currentSkin,
content_css: self.currentContentCSS,
};
const image_upload_handler = (blobInfo, progress) => new Promise((resolve, reject) => {
self2.uploadImageHandler(config, blobInfo, resolve, reject, progress);
});
tinymce.init({
...config,
file_picker_callback: function(cb, value, meta) {
self2.filePickerCallback(config, cb, value, meta);
},
images_upload_handler: image_upload_handler,
});
},
filePickerCallback: function(config, cb, value, meta) {
let input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.onchange = function() {
let file = this.files[0];
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
let id = 'blobid' + new Date().getTime();
let blobCache = tinymce.activeEditor.editorUpload.blobCache;
let base64 = reader.result.split(',')[1];
let blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
cb(blobInfo.blobUri(), {
title: file.name
});
};
};
input.click();
},
uploadImageHandler: function(config, blobInfo, resolve, reject, progress) {
let xhr, formData;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', config.uploadRoute);
xhr.upload.onprogress = ((e) => progress((e.loaded / e.total) * 100));
xhr.onload = function() {
let json;
if (xhr.status === 403) {
reject("<?php echo app('translator')->get('admin::app.components.tiny-mce.http-error'); ?>", {
remove: true
});
return;
}
if (xhr.status < 200 || xhr.status >= 300) {
reject("<?php echo app('translator')->get('admin::app.components.tiny-mce.http-error'); ?>");
return;
}
json = JSON.parse(xhr.responseText);
if (! json || typeof json.location != 'string') {
reject("<?php echo app('translator')->get('admin::app.components.tiny-mce.invalid-json'); ?>" + xhr.responseText);
return;
}
resolve(json.location);
};
xhr.onerror = (()=>reject("<?php echo app('translator')->get('admin::app.components.tiny-mce.upload-failed'); ?>"));
formData = new FormData();
formData.append('_token', config.csrfToken);
formData.append('file', blobInfo.blob(), blobInfo.filename());
xhr.send(formData);
},
};
tinyMCEHelper.initTinyMCE({
selector: this.selector,
plugins: 'image media wordcount save fullscreen code table lists link',
toolbar: 'placeholders | bold italic strikethrough forecolor backcolor image alignleft aligncenter alignright alignjustify | link hr | numlist bullist outdent indent | removeformat | code | table',
image_advtab: true,
directionality: 'ltr',
setup: (editor) => {
let toggleState = false;
editor.ui.registry.addMenuButton('placeholders', {
text: 'Placeholders',
fetch: function (callback) {
const items = [
<?php $__currentLoopData = $placeholders; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $placeholder): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
{
type: 'nestedmenuitem',
text: '<?php echo e($placeholder['text']); ?>',
getSubmenuItems: () => [
<?php $__currentLoopData = $placeholder['menu']; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $child): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
{
type: 'menuitem',
text: '<?php echo e($child['text']); ?>',
onAction: function () {
editor.insertContent('<?php echo e($child['value']); ?>');
},
},
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
],
},
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
];
callback(items);
}
});
['change', 'paste', 'keyup'].forEach((event) => {
editor.on(event, () => this.field.onInput(editor.getContent()));
});
}
});
},
},
})
</script>
<?php $__env->stopPush(); endif; ?>
<?php /**PATH /var/www/html/packages/Webkul/Admin/src/Resources/views/components/tinymce/index.blade.php ENDPATH**/ ?>