Skip to content

Commit b120101

Browse files
committed
Merge branch 'create-in-mm' into 'master'
Create diagrams in media manager See merge request innovakom/dokuwiki-plugin-drawio!1
2 parents 5758cff + 4e87b75 commit b120101

File tree

10 files changed

+416
-131
lines changed

10 files changed

+416
-131
lines changed

MenuItem.php

Lines changed: 0 additions & 32 deletions
This file was deleted.

action.php

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,98 @@
11
<?php
22

3-
if (!defined('DOKU_INC')) die();
4-
5-
class action_plugin_drawio extends DokuWiki_Action_Plugin
3+
class action_plugin_diagrams extends DokuWiki_Action_Plugin
64
{
75

6+
/**
7+
* Registers a callback function for a given event
8+
*
9+
* @param \Doku_Event_Handler $controller
10+
*/
811
public function register(Doku_Event_Handler $controller)
912
{
10-
$controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'handle_started');
11-
$controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'add_button');
13+
$controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'addJsinfo');
14+
$controller->register_hook('MEDIAMANAGER_STARTED', 'AFTER', $this, 'addJsinfo');
15+
$controller->register_hook('DOKUWIKI_STARTED', 'AFTER', $this, 'checkConf');
16+
$controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjax');
1217
}
1318

14-
public function handle_started(Doku_Event $event, $param)
19+
/**
20+
* Add security token to JSINFO, used for uploading
21+
*
22+
* @param Doku_Event $event
23+
*/
24+
public function addJsinfo(Doku_Event $event)
1525
{
1626
global $JSINFO;
17-
$JSINFO['iseditor'] = auth_quickaclcheck('*') >= AUTH_UPLOAD;
1827
$JSINFO['sectok'] = getSecurityToken();
1928
}
2029

21-
public function add_button(Doku_Event $event, $param)
30+
/**
31+
* Check if DokuWiki is properly configured to handle SVG diagrams
32+
*
33+
* @param Doku_Event $event
34+
*/
35+
public function checkConf(Doku_Event $event)
36+
{
37+
$mime = getMimeTypes();
38+
if (!array_key_exists('svg', $mime) || $mime['svg'] !== 'image/svg+xml') {
39+
msg($this->getLang('missingConfig'), -1);
40+
}
41+
}
42+
43+
/**
44+
* Check all supplied images and return only editable diagrams
45+
*
46+
* @param Doku_Event $event
47+
*/
48+
public function handleAjax(Doku_Event $event)
49+
{
50+
if ($event->data !== 'plugin_diagrams') return;
51+
$event->preventDefault();
52+
$event->stopPropagation();
53+
54+
global $INPUT;
55+
$images = $INPUT->arr('images');
56+
57+
echo json_encode($this->editableDiagrams($images));
58+
}
59+
60+
/**
61+
* Return an array of diagrams that are editable,
62+
* based on ACLs and image content ('embed.diagrams.net' or 'draw.io')
63+
*
64+
* @param array $images
65+
* @return array
66+
*/
67+
protected function editableDiagrams($images)
2268
{
23-
if ($event->data['view'] != 'page') return;
24-
array_splice($event->data['items'], -1, 0, [new dokuwiki\plugin\drawio\MenuItem()]);
69+
$editable = [];
70+
71+
foreach ($images as $image) {
72+
// skip non SVG files
73+
if (strpos($image, '.svg', - strlen('.svg')) === false) {
74+
continue;
75+
}
76+
// check ACLs
77+
if (auth_quickaclcheck($image) < AUTH_UPLOAD) {
78+
continue;
79+
}
80+
// is it our diagram?
81+
global $conf;
82+
$file = DOKU_INC .
83+
$conf['savedir'] .
84+
DIRECTORY_SEPARATOR .
85+
'media' .
86+
DIRECTORY_SEPARATOR .
87+
preg_replace(['/:/'], [DIRECTORY_SEPARATOR], $image);
88+
89+
$begin = file_get_contents($file, false, null, 0, 500);
90+
// TODO find a better way to detect diagrams
91+
if (strpos($begin, 'embed.diagrams.net') || strpos($begin, 'draw.io')) {
92+
$editable[] = $image;
93+
}
94+
}
95+
96+
return $editable;
2597
}
2698
}

drawing.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

lang/de/lang.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
$lang['missingConfig'] = 'Fehlende Konfiguration für SVG Diagramme: Bitte "svg image/svg+xml" in mime.local.conf ergänzen';
4+
5+
$lang['js']['createButton'] = 'Erstellen';
6+
$lang['js']['createLink'] = 'Diagramm erstellen';
7+
$lang['js']['createIntro'] = 'Diagramm im aktuellen Namensraum erstellen:';
8+
$lang['js']['editButton'] = 'Diagramm editieren';
9+
$lang['js']['errorInvalidId'] = 'Name ist leer oder enthält ungültige Zeichen!';
10+
$lang['js']['errorSaving'] = 'Fehler beim Speichern';
11+
$lang['js']['errorUnsupportedFormat'] = 'Nicht unterstützt!';
12+
$lang['js']['saving'] = 'Speichern';

lang/en/lang.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
$lang['missingConfig'] = 'Missing configuration for SVG diagrams: Remeber to add "svg image/svg+xml" to mime.local.conf';
4+
5+
$lang['js']['createButton'] = 'Create';
6+
$lang['js']['createLink'] = 'Create a diagram';
7+
$lang['js']['createIntro'] = 'Create a diagram in current namespace';
8+
$lang['js']['editButton'] = 'Edit diagram';
9+
$lang['js']['errorInvalidId'] = 'Name is empty or contains invalid characters!';
10+
$lang['js']['errorSaving'] = 'Saving failed';
11+
$lang['js']['errorUnsupportedFormat'] = 'Unsupported file format!';
12+
$lang['js']['saving'] = 'Saving';

plugin.info.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
base drawio
1+
base diagrams
22
author Innovakom + CosmoCode
33
4-
date 2020-09-24
5-
name Draw.io plugin
6-
desc Embed Draw.io editor
4+
date 2021-01-05
5+
name Diagrams plugin
6+
desc Embed diagrams.net editor (formerly draw.io)
77
url https://innovakom.de

script.js

Lines changed: 104 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,112 @@
1-
jQuery( document ).ready( function() {
2-
if( JSINFO['iseditor'] ) {
3-
jQuery( 'img, object' ).filter( '.media, .medialeft, .mediacenter, .mediaright' ).add( 'iframe.svgpureinsert' ).each( function() {
4-
var current = jQuery( this );
5-
var src = this.nodeName == 'OBJECT' ? current.attr( 'data' ) : current.attr( 'src' );
6-
var extension = src.split( '.' ).pop().toLowerCase();
7-
if( extension == 'svg' ) {
8-
var editlink = '<br><button class="drawio-btn btn btn-default btn-xs" style="clear:both" data-id="' + src.split('media=')[1].split('&')[0] + '">Editieren</button>';
9-
if( current.parent()[0].nodeName == 'A' ) {
10-
current.parent().after( editlink );
1+
jQuery(function () {
2+
/* DOKUWIKI:include script/helpers.js */
3+
/* DOKUWIKI:include script/service.js */
4+
/* DOKUWIKI:include script/elements.js */
5+
6+
// add diagram edit button to all SVGs included in wiki pages
7+
const $images = jQuery('img').filter('.media, .medialeft, .mediacenter, .mediaright');
8+
9+
// collect image IDs with file extension
10+
const imageIds = $images.map(function (key, image) {
11+
return extractIdFromMediaUrl(image.src);
12+
}).toArray();
13+
14+
let ajaxData = {};
15+
ajaxData['call'] = 'plugin_diagrams';
16+
ajaxData['images'] = imageIds;
17+
18+
// callback to attach buttons to editable diagrams
19+
const attachButtons = function (result) {
20+
const diagrams = JSON.parse(result);
21+
$images.each(function () {
22+
const id = extractIdFromMediaUrl(this.src);
23+
const $current = jQuery(this);
24+
if (diagrams.includes(id)) {
25+
let $editButton = editDiagramButton(id);
26+
if ($current.parent()[0].nodeName === 'A') {
27+
$current.parent().after("<br>", $editButton);
1128
} else {
12-
current.after( editlink );
29+
$current.after("<br>", $editButton);
1330
}
1431
}
15-
} );
16-
}
32+
});
33+
};
1734

18-
jQuery( 'button.drawio-btn' ).on( 'click', function() {
19-
var drawio_url = 'https://embed.diagrams.net/?embed=1&proto=json&spin=1';
35+
// query backend about permissions and SVG properties before attaching edit buttons
36+
jQuery.get(
37+
DOKU_BASE + 'lib/exe/ajax.php',
38+
ajaxData,
39+
attachButtons
40+
);
2041

21-
if( !jQuery( '#drawio-frame' )[0] ) {
22-
var fullId = jQuery( this ).data( 'id' );
23-
var id = fullId;
24-
var ns = '';
25-
var ext = id.split( '.' ).pop().toLowerCase();
26-
var idParts = id.split( ':' );
27-
if( idParts.length > 1 ) {
28-
ns = idParts[0];
29-
id = idParts.slice( 1 ).join( ':' );
30-
}
31-
jQuery( 'body' ).append( '<iframe id="drawio-frame" style="border: 0;position: fixed; top: 0; left: 0; right:0; bottom: 0; width:100%; height:100%; z-index: 9999;"></iframe>' );
32-
var onmessage = function( e ) {
33-
var msg = JSON.parse( e.originalEvent.data );
34-
var drawio = jQuery( '#drawio-frame' )[0].contentWindow;
35-
if( msg.event == 'init' ) {
36-
jQuery.get( DOKU_BASE + 'lib/exe/fetch.php?media=' + fullId, function( data ) {
37-
drawio.postMessage( JSON.stringify( {action: 'load', xml: data} ), '*' );
38-
}, 'text' );
39-
} else if( msg.event == 'save' ) {
40-
drawio.postMessage( JSON.stringify( {action: 'export', format: 'xmlsvg', spin: 'Speichern' } ), '*' );
41-
} else if( msg.event == 'export' ) {
42-
if( msg.format != 'svg' ) {
43-
alert( 'Nicht unterstützt!' );
44-
} else {
45-
var datastr = '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' +
46-
decodeURIComponent( atob( msg.data.split( ',' )[1] ).split( '' ).map( function( c ) {
47-
return '%' + ( '00' + c.charCodeAt( 0 ).toString( 16 ) ).slice( -2 );
48-
} ).join( '' ) );
49-
jQuery.post( DOKU_BASE + 'lib/exe/ajax.php?call=mediaupload&ow=true&ns=' + ns + '&qqfile=' + id + '&sectok=' + JSINFO['sectok'], datastr )
50-
.done( function() {
51-
jQuery( window ).off( 'message', onmessage );
52-
jQuery( '#drawio-frame' ).remove();
53-
setTimeout( function() {
54-
location.reload();
55-
}, 200 );
56-
} ).fail( function() {
57-
alert( 'Fehler beim Speichern' );
58-
} );
59-
}
60-
} else if( msg.event == 'exit' ) {
61-
jQuery( window ).off( 'message', onmessage );
62-
jQuery( '#drawio-frame' ).remove();
42+
/**
43+
* Media manager
44+
* FIXME this should be moved to a separate file
45+
*/
46+
47+
/* are we in media manager context? */
48+
const $mm_page = jQuery('#mediamanager__page');
49+
const $mm_popup = jQuery('#media__manager');
50+
const isMMPage = $mm_page.length > 0;
51+
const isMMPopup = $mm_popup.length > 0;
52+
if (!isMMPage && !isMMPopup) return;
53+
54+
/* in the namespace tree add a link to create a new diagram */
55+
const $mm_tree = jQuery("#media__tree");
56+
const $createLink = jQuery('<a href="#">' + LANG.plugins.diagrams.createLink + '</a>')
57+
.on('click', function (e) {
58+
e.preventDefault();
59+
e.stopPropagation();
60+
newDiagramForm().dialog({
61+
title: LANG.plugins.diagrams.createLink,
62+
width: 600,
63+
appendTo: '.dokuwiki',
64+
modal: true,
65+
open: function () {
66+
const ns = isMMPage ? jQuery('.panelHeader h3 strong').text() : jQuery('#media__ns').text();
67+
jQuery('#diagrams__current-ns').text(ns);
68+
},
69+
close: function () {
70+
// do not reuse the dialog
71+
// https://stackoverflow.com/a/2864783
72+
jQuery(this).dialog('destroy').remove();
6373
}
64-
};
65-
jQuery( window ).on( 'message', onmessage );
66-
jQuery( '#drawio-frame' ).attr( 'src', drawio_url );
67-
}
68-
} );
74+
});
75+
});
76+
$mm_tree.prepend($createLink);
6977

70-
jQuery( 'a#drawio-newfile-create' ).on( 'click', function( e ) {
71-
e.preventDefault();
72-
var ns = NS;
73-
var id = prompt( 'Name des neuen Diagramms' );
74-
if( !/^[\w][\w\.\-]*$/.test( id ) ) {
75-
alert( 'Dateiname enthält ungültige Zeichen' );
76-
return;
77-
}
78-
id += '.svg';
79-
var datastr = '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' +
80-
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1px" height="1px" version="1.1" content="&lt;mxfile userAgent=&quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36&quot; version=&quot;7.9.5&quot; editor=&quot;www.draw.io&quot;&gt;&lt;diagram id=&quot;8c846276-93cf-00fc-3101-d1fabb6ae99a&quot; name=&quot;Seite-1&quot;&gt;dZHBEoIgEIafhrtCNXY2q0snD51JEJjQdRBH6+nTwIyxuLB8/7+7sCCSVsPJ0EZegHGNcMQGRA4I43iDd+M2kYcjSbJ3QBjFvGkBuXpyDyNPO8V4GxgtgLaqCWEBdc0LGzBqDPShrQQddm2o4CuQF1Sv6VUxK/0rttHCz1wJOXeOI6/caHEXBrra90OYlO/l5IrOtby/lZRB/4VIhkhqAKyLqiHleprtPDaXd/yjfu5teG1/JIzBUns8BB9Ishc=&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);"><defs/><g transform="translate(0.5,0.5)"/></svg>';
81-
jQuery.post( DOKU_BASE + 'lib/exe/ajax.php?call=mediaupload&ns=' + ns + '&qqfile=' + id + '&sectok=' + JSINFO['sectok'], datastr )
82-
.done( function( response ) {
83-
if( response.error ) {
84-
alert( 'Fehler beim Speichern: ' + response.error );
85-
} else {
86-
alert( 'Diagramm ' + response.id + ' angelegt' );
78+
// attach edit button to detail view of SVG files
79+
if (!isMMPage) return;
80+
$mm_page.on('click', '.panel.filelist .panelContent a', function (e) {
81+
82+
// observe div.file for mutations
83+
const $df = jQuery('div.file');
84+
const targetNode = $df[0];
85+
86+
// observe the target node descendants
87+
const config = {childList: true, subtree: true};
88+
89+
// add edit diagram button to file actions
90+
const addEditButton = function (mutationsList, observer) {
91+
for (let mutation of mutationsList) {
92+
// div.file has been filled with new content (detail view)
93+
if (mutation.type === 'childList') {
94+
const $svgLink = jQuery('a.mf_svg');
95+
// only add buttons to SVG files
96+
if ($svgLink.length !== 0) {
97+
const $actionsList = jQuery('ul.actions');
98+
// disconnect now so we don't observe the mutation we are about to trigger
99+
observer.disconnect();
100+
// FIXME why do they multiply when non-svg link is clicked before?!!!
101+
if ($actionsList.find('button.diagrams-btn').length === 0) {
102+
$actionsList.append(editDiagramButton($svgLink.html()));
103+
}
104+
}
87105
}
88-
} ).fail( function() {
89-
alert( 'Fehler beim Speichern' );
90-
} );
91-
} );
92-
} );
106+
}
107+
};
108+
109+
const observer = new MutationObserver(addEditButton);
110+
observer.observe(targetNode, config);
111+
});
112+
});

0 commit comments

Comments
 (0)