Skip to content

Commit

Permalink
First image scenario working
Browse files Browse the repository at this point in the history
  • Loading branch information
murdercode committed Aug 17, 2023
1 parent 5477737 commit 2949ea8
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 83 deletions.
116 changes: 71 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,21 @@
![License Mit](https://img.shields.io/github/license/murdercode/Nova4-TinymceEditor)
<!--[![GitHub Tests Action Status](https://img.shields.io/github/workflow/status/murdercode/nova4-tinymce-editor/run-tests?label=tests)](https://github.com/murdercode/nova4-tinymce-editor/actions?query=workflow%3Arun-tests+branch%3Amain)-->

I'm proud to present a simple wrapper that allows you to use the excellent TinyMCE Editor within Laravel Nova 4.
## Introduction

* Dark mode support
* Switch between 5 or 6 version of TinyMCE
* Can be disabled (passing `readonly()` to `make` method)
Unleash creativity within Laravel Nova using the TinyMCE plugin, making content creation a breeze with its user-friendly
and dynamic editing capabilities.

### Todo
## Features

* Support for upload images
* Support for local and cdn script
* 📷 Upload images support in local
* 🌙 Dark mode support
* 🔀 Switch between 5 or 6 versions of TinyMCE
* ❌ Can be disabled (by passing readonly() to make method)

🚀🚀🚀 Want some steroids for your TinyMCE? [Check out](https://github.com/The-3Labs-Team/tinymce-chatgpt-plugin) our new *
*ChatGTP for TinyMCE** plugin!
> [!IMPORTANT]
> Want some steroids for your TinyMCE? [Check out](https://github.com/The-3Labs-Team/tinymce-chatgpt-plugin) our new *
*ChatGTP for TinyMCE** plugin! 🚀🚀🚀

## Demo & Screenshots

Expand All @@ -30,8 +32,7 @@ I'm proud to present a simple wrapper that allows you to use the excellent TinyM

This package follows the following versioning scheme:

* **v2.x** - TinyMCE 6 or 5 (new config, self hosted and more)
* **v1.x** - TinyMCE 6 (deprecated)
* **v1.x** - TinyMCE 5 or 6
* **v0.x** - TinyMCE version 5 (deprecated)

## Prerequisites
Expand All @@ -43,8 +44,6 @@ This package follows the following versioning scheme:

# How to install

Please note that this how-to is for **TinyMCE 6**. For _TinyMCE 5_, please see the *v.0.* branch.

In the root of your Laravel installation launch:

```bash
Expand All @@ -63,48 +62,75 @@ A file in `config/nova_tinymce_editor.php` will appear as follows (you can chang
<?php

return [
'init' => [
'menubar' => false,
'autoresize_bottom_margin' => 40,
'branding' => false,
'image_caption' => true,
'paste_as_text' => true,
'autosave_interval' => '20s',
'autosave_retention' => '30m',
'browser_spellcheck' => true,
'contextmenu' => false,
],
'plugins' => [
'advlist',
'anchor',
'autolink',
'autosave',
'fullscreen',
'lists',
'link',
'image',
'media',
'table',
'code',
'wordcount',
'autoresize',
],
'toolbar' => [
'undo redo restoredraft | h2 h3 h4 |
'cloudChannel' => '6', // 5 or 6

/**
* Get your API key at https://www.tiny.cloud and put it here or in your .env file
*/
'apiKey' => env('TINYMCE_API_KEY', ''),

/**
* The default skin to use.
*/
'skin' => 'oxide-dark',

/**
* The default options to send to the editor.
* See https://www.tiny.cloud/docs/configure/ for all available options (check for 5 or 6 version).
*/
'init' => [
'menubar' => false,
'autoresize_bottom_margin' => 40,
'branding' => false,
'image_caption' => true,
'paste_as_text' => true,
'autosave_interval' => '20s',
'autosave_retention' => '30m',
'browser_spellcheck' => true,
'contextmenu' => false,
'images_upload_url' => '/nova-vendor/murdercode/tinymce/upload',
],
'plugins' => [
'advlist',
'anchor',
'autolink',
'autosave',
'fullscreen',
'lists',
'link',
'image',
'media',
'table',
'code',
'wordcount',
'autoresize',
],
'toolbar' => [
'undo redo restoredraft | h2 h3 h4 |
bold italic underline strikethrough blockquote removeformat |
align bullist numlist outdent indent | link anchor table | code fullscreen',
align bullist numlist outdent indent | link anchor table | code fullscreen spoiler',
],

/**
* Extra configurations for the editor.
*/
'extra' => [
'upload_images' => [
'enabled' => true,
'folder' => 'images',
'maxSize' => 2048, // KB
],
'apiKey' => env('TINYMCE_API_KEY', ''),
],
];
```

In your `.env` file please add the key:
In your `.env` file please add the key (get one [here](https://www.tiny.cloud/)):

```
TINYMCE_API_KEY=[YOUR_PRECIOUS_PRIVATE_KEY]
```

**Please make sure** that you have added domain in your tiny.cloud account list.
Please make sure that you have added domain in your tiny.cloud account list or you will get an error notice message.

## Register the Field

Expand Down
12 changes: 12 additions & 0 deletions config/nova-tinymce-editor.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
'autosave_retention' => '30m',
'browser_spellcheck' => true,
'contextmenu' => false,
'images_upload_url' => '/nova-vendor/murdercode/tinymce/upload',
],
'plugins' => [
'advlist',
Expand All @@ -48,4 +49,15 @@
bold italic underline strikethrough blockquote removeformat |
align bullist numlist outdent indent | link anchor table | code fullscreen spoiler',
],

/**
* Extra configurations for the editor.
*/
'extra' => [
'upload_images' => [
'enabled' => true,
'folder' => 'images',
'maxSize' => 2048, // KB
],
],
];
77 changes: 39 additions & 38 deletions resources/js/components/FormField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,65 +14,66 @@
:toolbar="currentField.options.toolbar"

/>

</template>
</DefaultField>
</template>

<script>
import { DependentFormField, HandlesValidationErrors } from 'laravel-nova'
import {DependentFormField, HandlesValidationErrors} from 'laravel-nova'
import Editor from '@tinymce/tinymce-vue'
export default {
mixins: [DependentFormField, HandlesValidationErrors],
mixins: [DependentFormField, HandlesValidationErrors],
props: ['resourceName', 'resourceId', 'options'],
props: ['resourceName', 'resourceId', 'options'],
components: {
editor: Editor
},
components: {
editor: Editor
},
created () {
this.setEditorTheme()
},
created() {
this.setEditorTheme()
},
methods: {
setEditorTheme () {
const selectedNovaTheme = localStorage.novaTheme
methods: {
setEditorTheme() {
const selectedNovaTheme = localStorage.novaTheme
if (typeof selectedNovaTheme !== 'undefined') {
if (selectedNovaTheme === 'system') {
this.setSystemMode()
} else if (selectedNovaTheme === 'dark') {
this.field.options.init.skin = 'oxide-dark'
this.field.options.init.content_css = 'dark'
} else {
this.field.options.init.skin = 'oxide'
this.field.options.init.content_css = 'default'
}
} else {
this.setSystemMode()
}
},
if (typeof selectedNovaTheme !== 'undefined') {
if (selectedNovaTheme === 'system') {
this.setSystemMode()
} else if (selectedNovaTheme === 'dark') {
this.field.options.init.skin = 'oxide-dark'
this.field.options.init.content_css = 'dark'
} else {
this.field.options.init.skin = 'oxide'
this.field.options.init.content_css = 'default'
}
} else {
this.setSystemMode()
}
},
setSystemMode () {
this.field.options.init.skin =
setSystemMode() {
this.field.options.init.skin =
window.matchMedia('(prefers-color-scheme: dark)').matches ||
document.querySelector('html').classList.contains('dark')
? 'oxide-dark'
: 'oxide'
this.field.options.init.content_css =
? 'oxide-dark'
: 'oxide'
this.field.options.init.content_css =
window.matchMedia('(prefers-color-scheme: dark)').matches ||
document.querySelector('html').classList.contains('dark')
? 'dark'
: 'default'
},
? 'dark'
: 'default'
},
/**
/**
* Fill the given FormData object with the field's internal value.
*/
fill (formData) {
formData.append(this.field.attribute, this.value || '')
fill(formData) {
formData.append(this.field.attribute, this.value || '')
}
}
}
}
</script>
11 changes: 11 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Support\Facades\Route;
use Murdercode\TinymceEditor\Http\Controllers\TinyImageController;
use Murdercode\TinymceEditor\Http\Middleware\TinymceMiddleware;

// Without CSRF protection
Route::post('/upload', [TinyImageController::class, 'upload'])->name('tinymce.upload')
->withoutMiddleware([VerifyCsrfToken::class])
->middleware(TinymceMiddleware::class);
17 changes: 17 additions & 0 deletions src/FieldServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Murdercode\TinymceEditor;

use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Laravel\Nova\Events\ServingNova;
use Laravel\Nova\Nova;
Expand All @@ -23,6 +24,22 @@ public function boot()
$this->publishes([
__DIR__.'/../config/nova-tinymce-editor.php' => config_path('nova-tinymce-editor.php'),
], 'config');

// Routes
$this->app->booted(function () {
$this->routes();
});
}

protected function routes()
{
if ($this->app->routesAreCached()) {
return;
}

Route::middleware('nova:api')
->prefix('nova-vendor/murdercode/tinymce')
->group(__DIR__.'/../routes/api.php');
}

/**
Expand Down
56 changes: 56 additions & 0 deletions src/Http/Controllers/TinyImageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Murdercode\TinymceEditor\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class TinyImageController
{
public function upload(Request $request): JsonResponse
{

// Validate request
$validator = $this->validateRequest($request);
if ($validator->fails()) {
return response()->json(['error' => $validator->errors()->first()]);
}

$accepted_origins = [config('app.url')];
if (isset($_SERVER['HTTP_ORIGIN'])) {
if (in_array($_SERVER['HTTP_ORIGIN'], $accepted_origins)) {
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
} else {
header('HTTP/1.1 403 Origin Denied');
}
}

$imageFolder = config('nova-tinymce-editor.extra.upload_images.folder') ?? 'images';
reset($_FILES);
$temp = current($_FILES);
if (is_uploaded_file($temp['tmp_name'])) {
$file = Storage::disk('public')->putFile($imageFolder, $temp['tmp_name']);

return response()->json(['location' => Storage::disk('public')->url($file)]);

} else {
// Notify editor that the upload failed
header('HTTP/1.1 500 Server Error');

return response()->json(['error' => 'Server error']);
}

}

public function validateRequest(Request $request): \Illuminate\Validation\Validator
{
$maxSize = config('nova-tinymce-editor.extra.upload_images.maxSize') ?? 2048;
$validator = Validator::make($request->all(), [
'file' => 'required|image|mimes:jpeg,png,jpg,gif|max:'.$maxSize,
]);

return $validator;
}
}
Loading

0 comments on commit 2949ea8

Please sign in to comment.