Skip to content

Commit 2127af6

Browse files
committed
Add v5 to branches
1 parent 5088c67 commit 2127af6

File tree

12 files changed

+339
-197
lines changed

12 files changed

+339
-197
lines changed

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
"minimum-stability": "dev",
1818
"prefer-stable": true,
1919
"require": {
20-
"php": "^8.1",
20+
"php": "^8.0",
2121
"ext-json": "*",
2222
"illuminate/support": "^9.0",
23-
"spatie/laravel-mailcoach": "^6.0"
23+
"spatie/laravel-mailcoach": "^5.2"
2424
},
2525
"require-dev": {
2626
"orchestra/testbench": "^7.0",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class CreateMailcoachUnlayerTables extends Migration
8+
{
9+
public function up()
10+
{
11+
Schema::create('mailcoach_uploads', function (Blueprint $table) {
12+
$table->bigIncrements('id');
13+
$table->timestamps();
14+
});
15+
}
16+
}

resources/lang/en.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"mailcoach - Template not found.": "Template not found.",
3+
"mailcoach - Load Unlayer template": "Load Unlayer template",
4+
"mailcoach - You can load an <a class=\"text-blue-500\" href=\"https://unlayer.com/templates\" target=\"_blank\">Unlayer template</a> by entering the slug or the id when you have a projectId set.": "You can load an <a class=\"text-blue-500\" href=\"https://unlayer.com/templates\" target=\"_blank\">Unlayer template</a> by entering the slug or the id when you have a projectId set.",
5+
"mailcoach - Cancel": "Cancel"
6+
}

resources/views/unlayer.blade.php

Lines changed: 144 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,170 @@
11
@push('endHead')
2-
<style>
3-
#unlayer-wrapper {
4-
margin-top: -1.5rem;
5-
margin-right: -0.75rem;
6-
}
7-
8-
@media (min-width: 768px) {
9-
#unlayer-wrapper {
10-
margin-top: -2.55rem;
11-
margin-right: -0.5rem;
12-
}
13-
}
14-
</style>
2+
<script id="unlayer" src="https://editor.unlayer.com/embed.js" defer></script>
153
@endpush
16-
<div id="unlayer-wrapper">
17-
<script>
18-
function loadTemplate() {
19-
document.getElementById('unlayer_template_error').classList.add('hidden');
20-
let slug = document.getElementById('unlayer_template').value;
21-
slug = slug.split('/').slice(-1)[0];
22-
23-
fetch('https://api.graphql.unlayer.com/graphql', {
24-
method: 'POST',
25-
headers: {
26-
'Content-Type': 'application/json',
27-
},
28-
body: JSON.stringify({
29-
query: `
30-
query StockTemplateLoad($slug: String!) {
31-
StockTemplate(slug: $slug) {
32-
StockTemplatePages {
33-
design
34-
}
35-
}
4+
5+
<script>
6+
window.unlayerInitialized = false;
7+
8+
document.getElementById('unlayer').addEventListener('load', initUnlayer);
9+
10+
document.addEventListener('turbo:before-visit', confirmBeforeLeaveAndDestroyUnlayer);
11+
document.addEventListener("turbo:load", initUnlayer);
12+
window.addEventListener('beforeunload', confirmBeforeLeaveAndDestroyUnlayer);
13+
14+
function loadTemplate() {
15+
document.getElementById('unlayer_template_error').classList.add('hidden');
16+
const slug = document.getElementById('unlayer_template').value;
17+
18+
fetch('https://api.graphql.unlayer.com/graphql', {
19+
method: 'POST',
20+
headers: {
21+
'Content-Type': 'application/json',
22+
},
23+
body: JSON.stringify({
24+
query: `
25+
query StockTemplateLoad($slug: String!) {
26+
StockTemplate(slug: $slug) {
27+
StockTemplatePages {
28+
design
3629
}
37-
`,
38-
variables: {
39-
slug: slug,
40-
},
41-
}),
42-
})
43-
.then((res) => res.json())
44-
.then((result) => {
45-
if (! result.data.StockTemplate) {
46-
@if (config('mailcoach.unlayer.options.projectId'))
30+
}
31+
}
32+
`,
33+
variables: {
34+
slug: slug,
35+
},
36+
}),
37+
})
38+
.then((res) => res.json())
39+
.then((result) => {
40+
if (! result.data.StockTemplate) {
41+
42+
@if (config('mailcoach.unlayer.options.projectId'))
4743
unlayer.loadTemplate(slug);
48-
Alpine.store('modals').close('load-unlayer-template');
49-
@else
50-
document.getElementById('unlayer_template_error').innerHTML = '{{ __mc('Template not found.') }}';
44+
document.querySelector('[data-modal="load-unlayer-template"]').dispatchEvent(new Event('dismiss'));
45+
@else
46+
document.getElementById('unlayer_template_error').innerHTML = '{{ __('mailcoach - Template not found.') }}';
5147
document.getElementById('unlayer_template_error').classList.remove('hidden');
52-
@endif
48+
@endif
49+
return;
50+
}
5351
54-
return;
55-
}
52+
unlayer.loadDesign(result.data.StockTemplate.StockTemplatePages[0].design);
53+
document.querySelector('[data-modal="load-unlayer-template"]').dispatchEvent(new Event('dismiss'));
54+
});
55+
}
5656
57-
unlayer.loadDesign(result.data.StockTemplate.StockTemplatePages[0].design);
58-
Alpine.store('modals').close('load-unlayer-template');
59-
});
57+
function initUnlayer() {
58+
document.getElementById('load-template').addEventListener('click', loadTemplate);
59+
60+
if (window.unlayerInitialized || unlayer.init === undefined) {
61+
return;
6062
}
6163
62-
window.init = function() {
63-
document.getElementById('load-template').addEventListener('click', loadTemplate);
64-
65-
unlayer.init(@json($options));
66-
67-
unlayer.loadDesign(JSON.parse(JSON.stringify(this.json)));
68-
69-
if (! this.json) {
70-
unlayer.loadBlank({
71-
backgroundColor: '#ffffff'
72-
});
73-
}
74-
75-
unlayer.registerCallback('image', (file, done) => {
76-
let data = new FormData();
77-
data.append('file', file.attachments[0]);
78-
79-
fetch('{{ action(\Spatie\Mailcoach\Http\Api\Controllers\UploadsController::class) }}', {
80-
method: 'POST',
81-
headers: {
82-
'Accept': 'application/json',
83-
'X-CSRF-TOKEN': '{{ csrf_token() }}',
84-
},
85-
body: data
86-
})
87-
.then(response => {
88-
// Make sure the response was valid
89-
if (response.status >= 200 && response.status < 300) {
90-
return response.json()
91-
}
64+
unlayer.init(@json($options));
9265
93-
let error = new Error(response.statusText);
94-
error.response = response;
95-
throw error
96-
}).then(data => done({ progress: 100, url: data.file.url }))
97-
});
66+
window.unlayerInitialized = true;
9867
99-
const mergeTags = {};
100-
@foreach ($replacers as $replacerName => $replacerDescription)
101-
mergeTags["{{ $replacerName }}"] = {
102-
name: "{{ $replacerName }}",
103-
value: "::{{ $replacerName }}::"
104-
};
105-
@endforeach
106-
107-
unlayer.setMergeTags(mergeTags);
108-
109-
const component = this;
110-
unlayer.addEventListener('design:updated', () => {
111-
unlayer.exportHtml(function(data) {
112-
component.html = data.html;
113-
component.json = data.design;
114-
document.getElementById('editor').dirty = true;
115-
});
116-
});
68+
unlayer.loadDesign({!! $structuredHtml !!});
69+
70+
unlayer.registerCallback('image', (file, done) => {
71+
let data = new FormData();
72+
data.append('file', file.attachments[0]);
11773
118-
unlayer.addEventListener('design:loaded', function(data) {
119-
unlayer.exportHtml(function(data) {
120-
component.html = data.html;
121-
component.json = data.design;
122-
});
74+
fetch('{{ route('mailcoach-unlayer.upload') }}', {
75+
method: 'POST',
76+
headers: {
77+
'Accept': 'application/json',
78+
'X-CSRF-TOKEN': '{{ csrf_token() }}',
79+
},
80+
body: data
81+
}).then(response => {
82+
// Make sure the response was valid
83+
if (response.status >= 200 && response.status < 300) {
84+
return response.json()
85+
}
86+
87+
let error = new Error(response.statusText);
88+
error.response = response;
89+
throw error
90+
}).then(data => done({ progress: 100, url: data.url }))
91+
});
92+
93+
const mergeTags = {};
94+
@foreach ($replacers as $replacerName => $replacerDescription)
95+
mergeTags["{{ $replacerName }}"] = {
96+
name: "{{ $replacerName }}",
97+
value: "::{{ $replacerName }}::"
98+
};
99+
@endforeach
100+
101+
unlayer.setMergeTags(mergeTags);
102+
103+
document.getElementById('save').addEventListener('click', event => {
104+
event.preventDefault();
105+
106+
unlayer.exportHtml(function(data) {
107+
document.getElementById('html').value = data.html;
108+
document.getElementById('structured_html').value = JSON.stringify(data.design);
109+
document.getElementById('html').dataset.dirty = "";
110+
document.querySelector('main form').submit();
123111
});
112+
});
113+
114+
unlayer.addEventListener('design:updated', function(data) {
115+
document.getElementById('html').dataset.dirty = "dirty";
116+
});
117+
}
118+
119+
function confirmBeforeLeaveAndDestroyUnlayer(event) {
120+
if (document.getElementById('html').dataset.dirty === "dirty" && ! confirm('Are you sure you want to leave this page? Any unsaved changes will be lost.')) {
121+
event.preventDefault();
122+
return;
124123
}
125-
</script>
126-
127-
<div class="max-w-full flex flex-col">
128-
<div wire:ignore x-data="{
129-
html: @entangle('templateFieldValues.html'),
130-
json: @entangle('templateFieldValues.json'),
131-
init: init,
132-
}" class="overflow-hidden -mx-10 flex-1 h-full mb-6">
133-
<div id="editor" class="h-full -ml-2 pr-3 py-1" style="min-height: 75vh; height: 75vh" data-dirty-check></div>
134-
</div>
135124
136-
<x-mailcoach::replacer-help-texts :model="$model" />
125+
window.unlayerInitialized = false;
137126
138-
<x-mailcoach::editor-buttons :preview-html="$fullHtml" :model="$model">
139-
@isset($errors)
140-
@error('html')
141-
<p class="form-error" role="alert">{{ $message }}</p>
142-
@enderror
143-
@endisset
127+
document.removeEventListener('turbo:before-visit', confirmBeforeLeaveAndDestroyUnlayer);
128+
document.removeEventListener("turbo:load", initUnlayer);
129+
window.removeEventListener('beforeunload', confirmBeforeLeaveAndDestroyUnlayer);
130+
document.getElementById('load-template').removeEventListener('click', loadTemplate);
131+
}
144132
145-
<x-mailcoach::button-secondary x-on:click.prevent="$store.modals.open('load-unlayer-template')" :label="__mc('Load Unlayer template')"/>
146-
</x-mailcoach::editor-buttons>
133+
</script>
134+
<div class="h-full">
135+
<div class="form-row max-w-full h-full">
136+
<label class="label" for="html">{{ __('Body') }}</label>
137+
@isset($errors)
138+
@error('html')
139+
<p class="form-error" role="alert">{{ $message }}</p>
140+
@enderror
141+
@endisset
142+
<div class="overflow-hidden -mx-10 h-full">
143+
<div id="editor" class="h-full -ml-2 pr-3 py-1" style="min-height: 75vh"></div>
144+
</div>
145+
<input type="hidden" name="html" id="html" value="{{ $html }}">
146+
<input type="hidden" name="structured_html" id="structured_html" value="{{ json_encode($structuredHtml) }}">
147147
</div>
148148
</div>
149149

150+
<div class="form-buttons">
151+
<x-mailcoach::button id="save" :label="__('Save content')"/>
152+
@if ($showTestButton)
153+
<x-mailcoach::button-secondary data-modal-trigger="send-test" :label="__('Send Test')"/>
154+
@endif
155+
<x-mailcoach::button-secondary data-modal-trigger="load-unlayer-template" :label="__('mailcoach - Load Unlayer template')"/>
156+
</div>
157+
150158
@push('modals')
151-
<x-mailcoach::modal :title="__mc('Load Unlayer template')" name="load-unlayer-template">
152-
<p>{!! __mc('You can load an <a class="text-blue-500" href="https://unlayer.com/templates" target="_blank">Unlayer template</a> by entering the URL') !!}</p>
153-
@if(config('mailcoach.unlayer.options.projectId'))
154-
<p>{{ __mc('A template id from your Unlayer project also works') }}</p>
155-
@endif
156-
157-
<div>
158-
<x-mailcoach::text-field label="Unlayer template" name="unlayer_template" :placeholder="config('mailcoach.unlayer.options.projectId') ? __mc('URL or template id') : __mc('https://unlayer.com/templates/<template>')" />
159-
<p id="unlayer_template_error" class="form-error hidden mt-1" role="alert"></p>
160-
</div>
159+
<x-mailcoach::modal :title="__('mailcoach - Load Unlayer template')" name="load-unlayer-template">
160+
<p class="mb-4">{!! __('mailcoach - You can load an <a class="text-blue-500" href="https://unlayer.com/templates" target="_blank">Unlayer template</a> by entering the slug or the id when you have a projectId set.') !!}</p>
161+
162+
<x-mailcoach::text-field label="Unlayer template" name="unlayer_template" />
163+
<p id="unlayer_template_error" class="form-error hidden mt-1" role="alert"></p>
161164

162165
<div class="form-buttons">
163-
<x-mailcoach::button class="mt-auto" id="load-template" label="Load" type="button" />
164-
<x-mailcoach::button-cancel x-on:click.prevent="$store.modals.close('load-unlayer-template')" :label=" __mc('Cancel')" />
166+
<x-mailcoach::button class="mt-auto ml-2" id="load-template" label="Load" type="button" />
167+
<x-mailcoach::button-cancel :label=" __('mailcoach - Cancel')" />
165168
</div>
166169
</x-mailcoach::modal>
167170
@endpush

routes/api.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
use Spatie\MailcoachUnlayer\Http\Controllers\UploadController;
4+
5+
Route::post('uploads', '\\' . UploadController::class)->name('mailcoach-unlayer.upload');
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Spatie\MailcoachUnlayer\Http\Controllers;
4+
5+
use Spatie\MailcoachUnlayer\Http\Requests\UploadRequest;
6+
use Spatie\MailcoachUnlayer\Models\Upload;
7+
8+
class UploadController
9+
{
10+
public function __invoke(UploadRequest $request)
11+
{
12+
$diskName = config('mailcoach.unlayer.disk_name') ?? config('media-library.disk_name') ?? config('medialibrary.disk_name') ?? 'public';
13+
14+
$upload = Upload::create();
15+
$media = $upload
16+
->addMediaFromRequest('file')
17+
->toMediaCollection(
18+
'default',
19+
$diskName,
20+
);
21+
22+
return response()->json(['url' => $media->getFullUrl('image')]);
23+
}
24+
}

src/Http/Requests/UploadRequest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Spatie\MailcoachUnlayer\Http\Requests;
4+
5+
use Illuminate\Foundation\Http\FormRequest;
6+
7+
class UploadRequest extends FormRequest
8+
{
9+
public function rules(): array
10+
{
11+
return [
12+
'file' => 'required|image',
13+
];
14+
}
15+
}

0 commit comments

Comments
 (0)