alexusmai / laravel-file-manager

File manager for Laravel
MIT License
1.11k stars 288 forks source link

ckeditor 5 integration #331

Open esmaei1 opened 1 year ago

esmaei1 commented 1 year ago

Is it possible to integrate with ckeditor 5?

Jeffersonceo commented 1 year ago

Has anyone managed to integrate?

entense commented 5 months ago

Да, вам потребуется написать собственный плагин для ckeditor 5

image

ckeditor5.blade.php

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'File Manager') }}</title>

    <!-- Styles -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
    <link rel="stylesheet" href="{{ asset('vendor/file-manager/css/file-manager.css') }}">
</head>

<body>
    <div class="container-fluid">
        <div class="row">
            <div class="col-md-12" id="fm-main-block">
                <div id="fm"></div>
            </div>
        </div>
    </div>

    <!-- File manager -->
    <script src="{{ asset('vendor/file-manager/js/file-manager.js') }}"></script>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // set fm height
            document.getElementById('fm-main-block').setAttribute('style', 'height:' + window.innerHeight + 'px');

            fm.$store.commit('fm/setFileCallBack', function(fileUrl) {
                const data = {
                    url: fileUrl
                };

                window.opener.postMessage(data, document.referrer);
            });

            window.addEventListener('message', event => {
                if (event.origin === new URL(document.referrer).origin) {
                    alert(event.data);
                }
            });
        });
    </script>
</body>

</html>

ckeditor.js

import ClassicEditor from "@ckeditor/ckeditor5-editor-classic/src/classiceditor.js";
import Autoformat from "@ckeditor/ckeditor5-autoformat/src/autoformat.js";
import BlockQuote from "@ckeditor/ckeditor5-block-quote/src/blockquote.js";
import Bold from "@ckeditor/ckeditor5-basic-styles/src/bold.js";
import CloudServices from "@ckeditor/ckeditor5-cloud-services/src/cloudservices.js";
import Essentials from "@ckeditor/ckeditor5-essentials/src/essentials.js";
import FontBackgroundColor from "@ckeditor/ckeditor5-font/src/fontbackgroundcolor.js";
import FontColor from "@ckeditor/ckeditor5-font/src/fontcolor.js";
import FontFamily from "@ckeditor/ckeditor5-font/src/fontfamily.js";
import FontSize from "@ckeditor/ckeditor5-font/src/fontsize.js";
import Heading from "@ckeditor/ckeditor5-heading/src/heading.js";
import Image from "@ckeditor/ckeditor5-image/src/image.js";
import ImageCaption from "@ckeditor/ckeditor5-image/src/imagecaption.js";
import ImageInsert from "@ckeditor/ckeditor5-image/src/imageinsert.js";
import ImageStyle from "@ckeditor/ckeditor5-image/src/imagestyle.js";
import ImageToolbar from "@ckeditor/ckeditor5-image/src/imagetoolbar.js";
import ImageUpload from "@ckeditor/ckeditor5-image/src/imageupload.js";
import Indent from "@ckeditor/ckeditor5-indent/src/indent.js";
import Italic from "@ckeditor/ckeditor5-basic-styles/src/italic.js";
import Link from "@ckeditor/ckeditor5-link/src/link.js";
import List from "@ckeditor/ckeditor5-list/src/list.js";
import MediaEmbed from "@ckeditor/ckeditor5-media-embed/src/mediaembed.js";
import Paragraph from "@ckeditor/ckeditor5-paragraph/src/paragraph.js";
import PasteFromOffice from "@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js";
import SourceEditing from "@ckeditor/ckeditor5-source-editing/src/sourceediting.js";
import Table from "@ckeditor/ckeditor5-table/src/table.js";
import TableToolbar from "@ckeditor/ckeditor5-table/src/tabletoolbar.js";
import TextTransformation from "@ckeditor/ckeditor5-typing/src/texttransformation.js";
import TodoList from "@ckeditor/ckeditor5-list/src/todolist";
import GeneralHtmlSupport from "@ckeditor/ckeditor5-html-support/src/generalhtmlsupport.js";
import FileManager from "./FileManager";

import {
    ImageResizeEditing,
    ImageResizeHandles,
} from "@ckeditor/ckeditor5-image";

class Editor extends ClassicEditor {}

// Plugins to include in the build.
Editor.builtinPlugins = [
    // Autoformat,
    BlockQuote,
    Bold,
    CloudServices,
    Essentials,
    FontBackgroundColor,
    FontColor,
    FontFamily,
    FontSize,
    GeneralHtmlSupport,
    Heading,
    Image,
    ImageCaption,
    ImageInsert,
    ImageStyle,
    ImageToolbar,
    ImageUpload,
    ImageResizeEditing,
    ImageResizeHandles,
    Indent,
    Italic,
    Link,
    List,
    MediaEmbed,
    Paragraph,
    PasteFromOffice,
    SourceEditing,
    Table,
    TableToolbar,
    TextTransformation,
    TodoList,
    FileManager,
];

Editor.defaultConfig = {
    toolbar: {
        items: [
            "heading",
            "|",
            "undo",
            "redo",
            "|",
            "bold",
            "italic",
            "fontFamily",
            "fontSize",
            "fontBackgroundColor",
            "fontColor",
            "|",
            "todoList",
            "bulletedList",
            "numberedList",
            "|",
            "outdent",
            "indent",
            "|",
            "link",
            "imageInsert",
            "mediaEmbed",
            "blockQuote",
            "insertTable",
            "|",
            "fileManager",
            "|",
            "sourceEditing",
        ],
    },
    language: "ru",
    image: {
        toolbar: [
            "imageTextAlternative",
            "imageStyle:inline",
            "imageStyle:block",
            "imageStyle:side",
        ],
    },
    table: {
        contentToolbar: ["tableColumn", "tableRow", "mergeTableCells"],
    },
    htmlSupport: {
        allow: [
            {
                name: /.*/,
                attributes: true,
                classes: true,
                styles: true,
            },
        ],
    },
    licenseKey: "",
};

export default Editor;

FileManager.js

import ButtonView from "@ckeditor/ckeditor5-ui/src/button/buttonview";
import Plugin from "@ckeditor/ckeditor5-core/src/plugin";
import BrowseFilesIcon from "./icons/browse-files.svg";
import extToMime from "./extToMime";

export default class FileManager extends Plugin {
    init() {
        const editor = this.editor;

        let popupWindow = null;

        // register toolbar button
        editor.ui.componentFactory.add("fileManager", () => {
            const button = new ButtonView();

            button.set({
                label: "Файловый менеджер",
                icon: BrowseFilesIcon,
                withText: true,
            });

            button.on("execute", handleButtonClick);

            return button;
        });

        // subscribe to messages from popup
        window.addEventListener("message", handlePopupMessage);

        function handleButtonClick(e) {
            const { w, h, x, y } = calculatePopupSize();
            const settings = `toolbar=no, width=${w}, height=${h}, top=${y}, left=${x}`;

            popupWindow = window.open(window.urls.fileManager, "fm", settings);
        }

        function handlePopupMessage(e) {
            const popupOrigin = new URL(window.urls.fileManager).origin;

            if (e.origin !== popupOrigin) return;

            const TEXT_NO_SUPPORT = "Ваш браузер не поддерживает";

            const fileUrl = e.data.url;
            const fileExtension = fileUrl.split(".").reverse()[0].toLowerCase();
            const fileMimeType = extToMime["." + fileExtension];

            let html = "";
            if (isImage(fileMimeType)) {
                html = `<img src="${fileUrl}" />`;
            } else if (isVideo(fileMimeType)) {
                html = `<video width="640" height="360" controls>
                    <source src="${fileUrl}" type="${fileMimeType}">
                    ${TEXT_NO_SUPPORT} видео. <a href="${fileUrl}">Скачать видеозапись</a>
                </video>`;
            } else if (isAudio(fileMimeType)) {
                html = `<audio controls>
                    <source src="${fileUrl}" type="${fileMimeType}">
                    ${TEXT_NO_SUPPORT} аудио. <a href="${fileUrl}">Скачать аудиозапись</a>
                </audio>`;
            } else if (isPdf(fileMimeType)) {
                html = `
                <object data="${fileUrl}" type="application/pdf" width="700" height="1050">
                    ${TEXT_NO_SUPPORT} PDF. <a href="${fileUrl}">Скачать PDF</a>
                </object>`;
            } else {
                html = `<a href="${fileUrl}">${fileUrl}</a>`;
            }

            const viewFragment = editor.data.processor.toView(html);
            const modelFragment = editor.data.toModel(viewFragment);
            editor.model.insertContent(modelFragment);

            popupWindow.close();
        }

        function calculatePopupSize() {
            const w = 1050;
            const h = 650;
            const x = window.top.outerWidth / 2 + window.top.screenX - w / 2;
            const y = window.top.outerHeight / 2 + window.top.screenY - h / 2;

            return { w, h, x, y };
        }

        function isImage(mimeType) {
            return mimeType && mimeType.split("/")[0] === "image";
        }

        function isVideo(mimeType) {
            return mimeType && mimeType.split("/")[0] === "video";
        }

        function isAudio(mimeType) {
            return mimeType && mimeType.split("/")[0] === "audio";
        }

        function isPdf(mimeType) {
            return mimeType === "application/pdf";
        }
    }
}

extToMime.js

const extToMime = {
    ".mp4": "video/mp4",
    ".webm": "video/webm",
    ".ogg": "video/ogg",
    ".qt": "video/quicktime",
    ".mov": "video/quicktime",
    ".avi": "video/x-msvideo",

    ".aac": "audio/aac",
    ".aiff": "audio/aiff",
    ".amr": "audio/amr",
    ".au": "audio/basic",
    ".flac": "audio/flac",
    ".m4a": "audio/mp4",
    ".mid": "audio/midi",
    ".mp3": "audio/mpeg",
    ".ogg": "audio/ogg",
    ".wav": "audio/wav",
    ".wma": "audio/x-ms-wma",

    ".bmp": "image/bmp",
    ".gif": "image/gif",
    ".jpeg": "image/jpeg",
    ".jpg": "image/jpeg",
    ".png": "image/png",
    ".svg": "image/svg+xml",
    ".webp": "image/webp",
    ".heic": "image/heic",
    ".heif": "image/heif",
    ".ico": "image/vnd.microsoft.icon",

    ".pdf": "application/pdf",
};

export default extToMime;

icons/browse-files.svg

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M11.627 16.5zm5.873-.196zm0-7.001V8h-13v8.5h4.341c.191.54.457 1.044.785 1.5H2a1.5 1.5 0 0 1-1.5-1.5v-13A1.5 1.5 0 0 1 2 2h4.5a1.5 1.5 0 0 1 1.06.44L9.122 4H16a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 19 8v2.531a6.027 6.027 0 0 0-1.5-1.228zM16 6.5v-1H8.5l-2-2H2v13h1V8a1.5 1.5 0 0 1 1.5-1.5H16z"/><path d="M14.5 19.5a5 5 0 1 1 0-10 5 5 0 0 1 0 10zM15 14v-2h-1v2h-2v1h2v2h1v-2h2v-1h-2z"/></svg>

routes/filemanager.php

<?php

use Alexusmai\LaravelFileManager\Services\ConfigService\ConfigRepository;
use App\Http\Controllers\FilemanagerController;
use Illuminate\Support\Facades\Route;

$config = resolve(ConfigRepository::class);

$middleware = $config->getMiddleware();

if ($config->getAcl()) {
    $middleware[] = 'fm-acl';
}

Route::group([
    'middleware' => $middleware,
    'prefix' => $config->getRoutePrefix()
], function () {
    Route::get('ckeditor5', [FilemanagerController::class, 'ckeditor5'])
        ->name('fm.ckeditor5');
});

RouteServiceProvider.php

...
    public function boot(): void
    {
           ...

            Route::middleware(config('file-manager.middleware'))
                ->group(base_path('routes/filemanager.php'));
        });
    }
...

FilemanagerController.php


<?php

namespace App\Http\Controllers;

use Alexusmai\LaravelFileManager\Controllers\FileManagerController as BaseFileManagerController;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;

class FilemanagerController extends BaseFileManagerController
{
    public function ckeditor5(): Factory|View
    {
        return view('file-manager::ckeditor5');
    }
}

AppServiceProvider.php

<?php

declare(strict_types=1);

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

final class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        ...
        $this->app->bind(\Alexusmai\LaravelFileManager\Controllers\FileManagerController::class, \App\Http\Controllers\FilemanagerController::class);
    }
}

там где будете выводить редактор

window.urls = {
    fileManager: 'ваш хост/ckeditor5',
};

это можно было бы включить в сам пакет, но инициализация ckeditor 5, требует свой собственный билд, для добавления своего плагина