namespace App\Http\Controllers\Admin\Operations\FluidbookPublication;
+use App\Fluidbook\Link\LinksData;
use App\Models\FluidbookPublication;
+use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
trait MarkdownOperation
{
+ static $_names = [];
protected function setupMarkdownRoutes($segment, $routeName, $controller)
{
Route::match(['get'], $segment . '/{id}/edit/markdown', $controller . '@markdown')->name('fluidbook_markdowneditor');
+ Route::match(['get'], $segment . '/{id}/edit/markdown/versions', $controller . '@getMarkdownsVersions');
Route::match(['get'], $segment . '/{id}/markdown', $controller . '@getFilesById');
+ Route::match(['put'], $segment . '/{id}/save/markdown', $controller . '@saveMarkdown');
}
public function markdown($id)
return view('fluidbook_publication.markdown_editor', ['contents' => $contents, 'version' => 'stable', 'id' => $id, 'fluidbook' => $fluidbook, 'access' => "", 'token' => $token]);
}
+
+ protected function saveMarkdown($fluidbook_id)
+ {
+ if (!FluidbookPublication::hasPermission($fluidbook_id)) {
+ abort(401);
+ }
+
+ $markdowns = request('markdowns', '[]');
+ $comments = request('message');
+ $user_id = backpack_user()->id;
+
+ /** @var FluidbookPublication $fluidbook */
+ $fluidbook = FluidbookPublication::withoutGlobalScopes()->find($fluidbook_id);
+ $meta = ['comments' => $comments, 'user' => $user_id];
+
+ $base = self::getMarkdownsDir($fluidbook_id) . '/' . time();
+ $latestMarkdown = self::getMarkdownsDir($fluidbook_id) . '/latest.markdown3.gz';
+ $latestMeta = self::getMarkdownsDir($fluidbook_id) . '/latest.meta3.gz';
+ file_put_contents($base . '.markdown3.gz', gzencode(json_encode($markdowns)));
+ file_put_contents($base . '.meta3.gz', gzencode(json_encode($meta)));
+ copy($base . '.markdown3.gz', $latestMarkdown);
+ copy($base . '.meta3.gz', $latestMeta);
+
+ $fluidbook->touch();
+
+ return response()->json(['versions' => self::getMarkdownsVersions($fluidbook_id)]);
+ }
+
+ public static function getMarkdownsDir($fluidbook_id)
+ {
+ return Files::mkdir(protected_path('fluidbookpublication/markdowns/' . $fluidbook_id));
+ }
+
+ public static function getMarkdownsVersions($book_id)
+ {
+ $dir = self::getMarkdownsDir($book_id);
+ $dr = opendir($dir);
+ $updates = [];
+ while ($f = readdir($dr)) {
+ if ($f === '.' || $f === '..') {
+ continue;
+ }
+ $e = explode('.', $f, 2);
+ if (($e[1] !== 'meta.gz' && $e[1] !== 'meta3.gz') || $e[0] === 'latest') {
+ continue;
+ }
+
+ $updates[$e[0]] = self::getMeta($book_id, $e[0]);
+ }
+ krsort($updates);
+
+
+ $res = [];
+ foreach ($updates as $timestamp => $u) {
+ $u['name'] = self::getName($u['user']);
+ $u['date'] = date('Y-m-d H:i:s', $timestamp);
+ $u['timestamp'] = $timestamp;
+ $res[] = $u;
+ }
+
+ return $res;
+ }
+
+ public static function getMeta($book_id, $update = 'latest')
+ {
+ return json_decode(gzdecode(file_get_contents(Files::firstThatExists(self::getMarkdownsDir($book_id) . '/' . $update . '.meta3.gz', self::getMarkdownsDir($book_id) . '/' . $update . '.meta.gz'))), true);
+ }
+
+ protected static function getName($u)
+ {
+ if (is_array($u)) {
+ if (isset($u['firstname'])) {
+ return $u['firstname'] . ' ' . $u['lastname'];
+ } else {
+ return '-';
+ }
+ }
+ if (!isset(static::$_names[$u])) {
+ try {
+ static::$_names[$u] = User::find($u)->name;
+ } catch (\Exception $e) {
+ static::$_names[$u] = '-';
+ }
+ }
+ return static::$_names[$u];
+ }
+
}
import Editor from '@toast-ui/editor';
import MarkdowneditorToolbar from "./markdowneditor.toolbar";
import MarkdowneditorUndo from "./markdowneditor.undo";
+import MarkdowneditorSave from "./markdowneditor.save";
+import MarkdowneditorVersions from "./markdowneditor.versions";
+import tippy from "tippy.js";
+import 'tippy.js/dist/tippy.css';
window.$ = window.jQuery = require('jquery');
$.ajaxSetup({
init: function() {
new MarkdowneditorToolbar(this);
this.undo = new MarkdowneditorUndo(this);
+ this.save = new MarkdowneditorSave(this);
+ this.versions = new MarkdowneditorVersions(this);
const $this = this
this.initIcons();
this.markdown();
this.changePage();
+
$(window).on('hashchange', function () {
/*if ($this.maskHashEvent) {
return;
});
},
+ initTooltips: function () {
+ $('[data-tooltip]:not(.init-tooltip)').each(function () {
+ let i = tippy($(this).get(0), {content: $(this).data('tooltip')});
+ $(this).addClass('init-tooltip');
+ $(this).data('tippyinstance', i)
+ });
+ },
+
loadPage: function() {
this.loadPageHtml(this.currentPage);
this.setContentMarkdown()
this.editor.setMarkdown(state[lastKey])
this.editor.moveCursorToStart(true)
}else {
- this.contentMarkdown = MARKDOWN_DATA["p"+this.currentPage]
+ this.contentMarkdown = MARKDOWN_DATA[this.currentPage]
this.editor.setMarkdown(this.contentMarkdown)
this.editor.moveCursorToStart(true)
}
this.editor.getMarkdown();
},
- setCurrentState: function (state) {
- this.editor.setMarkdown(state)
- this.editor.moveCursorToStart(true)
+ setCurrentState: function (state,scroll) {
+ MARKDOWN_DATA[this.currentPage] = state
+ this.editor.reset()
+ this.editor.insertText(state)
+ this.editor.blur()
+
+ this.editor.setScrollTop(20)
+
+ //this.editor.moveCursorToStart(true)
},
changePage: function(page) {
-function MarkdowneditorSave(linkeditor) {
- this.linkeditor = linkeditor;
+function MarkdowneditorSave(markdowneditor) {
+ this.markdowneditor = markdowneditor;
this.init();
}
}
$.ajax({
- url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/links', method: 'post', data: {
+ url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/save/markdown', method: 'post', data: {
_method: 'put',
message: message,
- rulers: JSON.stringify(window.RULERS),
- links: JSON.stringify(window.LINKS),
+ markdowns: MARKDOWN_DATA,
},
success: function (data) {
if (notify) {
- $this.linkeditor.notification(TRANSLATIONS.success_save);
+ //$this.linkeditor.notification(TRANSLATIONS.success_save);
}
clearTimeout($this.automaticSaveTimeout);
$this.unsavedChanges = false;
$this.runningAutomaticSaveTimeout = false;
- window.ASSETS = data.assets;
- $this.linkeditor.versions.setVersions(data.versions);
+ $this.markdowneditor.versions.setVersions(data.versions);
callback();
},
error: function (jqXHR, status, error) {
- $this.linkeditor.hasChanged();
- $this.linkeditor.notification(TRANSLATIONS.error_save + ' : ' + error, 'error');
+ //$this.linkeditor.hasChanged();
+ //$this.linkeditor.notification(TRANSLATIONS.error_save + ' : ' + error, 'error');
},
});
-
- $this.linkeditor.links.loadFontSize();
},
automaticSave: function () {
this.ignoreStatesChanges = false;
this.states = [];
this.indexes = [];
+ this.scrolls = [];
this.init();
}
let index = this.indexes[this.markdowneditor.getCurrentPage()];
if (index === 0) {
this.states[this.markdowneditor.getCurrentPage()] = [];
+ this.scrolls[this.markdowneditor.getCurrentPage()] = [];
}
let cs = this.markdowneditor.editor.getMarkdown();
this.states[this.markdowneditor.getCurrentPage()] = this.states[this.markdowneditor.getCurrentPage()].slice(0, index);
}
this.states[this.markdowneditor.getCurrentPage()].push(cs);
+ this.scrolls[this.markdowneditor.getCurrentPage()].push($(".toastui-editor-md-preview").scrollTop());
this.indexes[this.markdowneditor.getCurrentPage()]++;
//console.log('push current index', index, 'states length', this.states[this.markdowneditor.getCurrentPage()].length);
let index = this.indexes[this.markdowneditor.getCurrentPage()];
index--;
let state = this.states[this.markdowneditor.getCurrentPage()][index - 1];
+ let scroll = this.scrolls[this.markdowneditor.getCurrentPage()][index - 1];
this.ignoreStatesChanges = true;
- this.markdowneditor.setCurrentState(state);
+ this.markdowneditor.setCurrentState(state,scroll);
var $this = this;
setTimeout(function () {
$this.ignoreStatesChanges = false;
}
let index = this.indexes[this.markdowneditor.getCurrentPage()];
let state = this.states[this.markdowneditor.getCurrentPage()][index];
+ let scroll = this.scrolls[this.markdowneditor.getCurrentPage()][index];
this.ignoreStatesChanges = true;
- this.markdowneditor.setCurrentState(state);
+ this.markdowneditor.setCurrentState(state,scroll);
var $this = this;
setTimeout(function () {
$this.ignoreStatesChanges = false;
--- /dev/null
+function MarkdownVersions(markdowneditor) {
+ this.markdowneditor = markdowneditor;
+ this.init();
+}
+
+MarkdownVersions.prototype = {
+ init: function () {
+ this.refresh();
+ },
+
+ refresh: function () {
+ var $this = this;
+ $.ajax({
+ url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/edit/markdown/versions', method: 'get',
+ success: function (data) {
+ $this.setVersions(data);
+ }
+ });
+ },
+
+ setVersions: function (data) {
+ var list = $("#markdown-panel-versions-list");
+ list.html('');
+ $.each(data, function (k, version) {
+ let actionArgs = JSON.stringify([version.timestamp]);
+ var item = '<div class="row">';
+ item += '<div class="col1">';
+ item += '<div class="date">' + version.date + '</div>';
+ item += '<div class="name">' + version.name + '</div>';
+ item += '<div class="comments">' + version.comments + '</div>';
+ item += '<div class="col2">';
+ item += '<div class="actions"><a nohref data-action="versions.restoreVersion" data-action-args="' + actionArgs + '" data-icon="wayback-machine" data-tooltip="' + TRANSLATIONS.restore_version_tooltip + '" draggable="false"></a></div>';
+ item += '</div>'
+ item += '</div>'
+ list.append(item);
+ });
+ this.markdowneditor.initIcons();
+ this.markdowneditor.initTooltips();
+ },
+
+ restoreVersion: function (timestamp) {
+ var $this = this;
+ var callback = function () {
+ $this._restoreVersion(timestamp);
+ }
+
+ //Restore links from 2022-12-07 13:37:15
+ this.linkeditor.save.saveIfUnsavedChanges(TRANSLATIONS.before_restore_message,false,callback);
+ },
+
+ _restoreVersion(timestamp) {
+ $.ajax({
+ url: '/fluidbook-publication/' + FLUIDBOOK_DATA.id + '/edit/links/versions/restore/' + timestamp + '',
+ success: function (data) {
+ window.location.reload();
+ },
+ });
+ },
+
+ resize: function () {
+ var w = $("#linkeditor-panel-versions-list").outerWidth();
+ if (w <= 0) {
+ return;
+ }
+ if (w < 300) {
+ $("#linkeditor-panel-versions-list").addClass('small');
+ } else {
+ $("#linkeditor-panel-versions-list").removeClass('small');
+ }
+ },
+
+
+};
+export default MarkdownVersions;
body
padding: 0
margin: 0
+ font-family: "Montserrat", sans-serif
+ white-space: nowrap
&.user-select-none
user-select: none
&-panel
position: relative
height: 100%
+ flex: 1
+ overflow: hidden auto
+ width: 0
+
+ #markdown-panel-versions
+ user-select: none
+ padding: 5px 10px
+ &-list
+ font-size: 12px
+ width: 100%
+ border-collapse: collapse
+ color: #5d5d5d
+ .row
+ padding: 5px 0
+ vertical-align: top
+ border-bottom: 1px solid currentColor
+ position: relative
+ .col1
+ width: calc(100% - 25px)
+ .col2
+ position: absolute
+ top: 10px
+ right: 10px
+ width: 18px
+ padding: 0
+ .date
+ font-weight: bold
+ .comments
+ font-style: italic
+ .actions a
+ background: transparent
+ cursor: pointer
+ &:hover
+ color: inherit
&-toolbar
background-color: #dbdddf
&-wrapper
display: flex
align-items: center
+ user-select: none
&-page
width: 100%
$fbdata['settings']['pages']=$fbdata['pages']=$fluidbook->getPagesNumber();
$fbdata['settings']['imageFormat']=$fluidbook->getImageFormat();
$fbdata['page_dimensions']=[];
+ $translations=[
+ 'success_save'=>__('Liens sauvegardés'),
+ 'error_save'=>__('Une erreur s\'est produite lors de la sauvegarde des liens'),
+ 'error'=>__('Une erreur s\'est produite'),
+ 'manual_save_message'=>__('Sauvegarde manuelle'),
+ 'automatic_save_message'=>__('Sauvegarde automatique'),
+ 'warning_unsaved_changes'=>__('Des données n\'ont pas été sauvegardées'),
+ 'before_export_save_message'=>__("Sauvegarde avant export"),
+ 'before_restore_message'=>__("Sauvegarde avant la restauration des liens"),
+ 'upload_save_message'=>__("Après l'upload d'un fichier"),
+ 'restore_version_tooltip'=>__('Restaurer cette version'),
+ 'export_version_tooltip'=>__('Exporter les liens au format xlsx'),
+ 'delete_link'=>__('Supprimer le lien'),
+ 'edit_image_link'=>__('Editer les liens de l\'image'),
+ 'delete_selection'=>__('Supprimer la sélection'),
+ 'edit_link_order'=>__('Modifier l\'ordre'),
+ 'reorder_selection'=>__('Réordonner la sélection'),
+ 'reorder_lines'=>__('Par lignes'),
+ 'reorder_columns'=>__('Par colonnes'),
+ 'reorder_selection_lines'=>__('Réordonner la sélection par lignes'),
+ 'reorder_selection_columns'=>__('Réordonner la sélection par colonnes'),
+ 'move_order_start'=>__('Déplacer la sélection au début'),
+ 'move_order_up'=>__('Remonter la sélection'),
+ 'move_order_down'=>__('Descendre la sélection'),
+ 'move_order_end'=>__('Déplacer la sélection à la fin'),
+ 'move_up'=>__('Avant'),
+ 'move_down'=>__('Après'),
+ 'move_beginning'=>__('Au début'),
+ 'move_end'=>__('À la fin'),
+ 'order_all_lines'=>__('Réordonner les liens de toute la publication par lignes'),
+ 'order_all_columns'=>__('Réordonner les liens de toute la publication par colonnes'),
+ 'order_page_lines'=>__('Réordonner les liens de la page par lignes'),
+ 'order_page_columns'=>__('Réordonner les liens de la page par colonnes'),
+ 'select_all'=>__('Tout sélectionner'),
+ 'error_open_image_link'=>__('Impossible d\'ajouter des liens au contenu de ce lien'),
+ 'empty'=>__('Vide'),
+ 'copy_link_id'=>__('Copier l\'identifiant unique'),
+ 'level'=>__('Niveau'),
+ 'before_fix_drifted'=>__('Sauvegarde avant la correction de la dérive des liens'),
+ 'before_import_links_from_pdf'=>__('Sauvegarde avant de restaurer les liens du PDF'),
+ 'copy'=>__('Copier'),
+ 'cut'=>__('Couper'),
+ 'paste_here'=>__('Coller ici'),
+ 'paste_in_place'=>__('Coller en place'),
+ 'paste_on_left'=>__('Coller en décalant vers la gauche'),
+ 'paste_on_right'=>__('Coller en décalant vers la droite'),
+ "cover"=>__('Recouvrir').' ...',
+ 'cover_page_0'=>__('la page sans marge'),
+ 'cover_doublepage_0'=>__('la double-page sans marge'),
+ 'cover_page_1'=>__('la page avec une marge de :margin',['margin'=>'1px']),
+ 'cover_doublepage_1'=>__('la double-page avec une marge de :margin',['margin'=>'1px']),
+ 'n_links_copied'=>__('%nb% liens copiés'),
+ 'n_links_cut'=>__('%nb% liens coupés'),
+ 'click_to_copy_id'=>__('Cliquer pour copier l\'identifiant du lien'),
+ 'id_copied'=>__('Identifiant copié !'),
+ 'lock'=>__('Vérouiller'),
+ 'fix_offset'=>__('Corriger décalage de page'),
+ 'interactive_links'=>__('Liens interactifs'),
+ 'noninteractive_links'=>__('Liens non-interactifs'),
+ ];
@endphp
@extends('layouts.markdowneditor')
data-tooltip="{{__('Paramètres du lien')}} (F8)" data-key="f8"></a>
</div>
<div class="markdown-revision-panel">
+ <div id="markdown-panel-versions">
+ <div id="markdown-panel-versions-list"></div>
+ </div>
</div>
<div class="handle"></div>
</div>
data-tooltip="{{__('Annuler la dernière modification')}} (Ctrl+Z)"
data-key="ctrl+z" data-key-skipintextfields></a>
<a href="#" data-icon="redo" data-action="undo.redo"
- data-tooltip="{{__('Rétablir la derière modification')}} (Ctrl+Maj+Z)"
+ data-tooltip="{{__('Rétablir la dernière modification')}} (Ctrl+Maj+Z)"
data-key="ctrl+shift+z" data-key-skipintextfields></a>
<div class="separator"></div>
</nav>
@push('markdown_scripts')
<script>
+ var TRANSLATIONS = @json($translations);
var FLUIDBOOK_DATA = @json($fbdata);
var MARKDOWN_DATA = @json($contents['pages']);
+ console.log(MARKDOWN_DATA)
</script>
<script
src="/packages/markdowneditor/js/markdowneditor.js?v={{filemtime(public_path('packages/markdowneditor/js/markdowneditor.js'))}}"></script>