Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F3529536
D25960.1744863344.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
14 KB
Referenced Files
None
Subscribers
None
D25960.1744863344.diff
View Options
diff --git a/rsrc/remarkup-image.css b/rsrc/remarkup-image.css
--- a/rsrc/remarkup-image.css
+++ b/rsrc/remarkup-image.css
@@ -2,7 +2,62 @@
* @provides diagram-remarkup-image-css
*/
-.diagram-container > .diagram-content {
+.diagram-content {
+ display: flex;
+}
+
+.diagram-content.diagram-center {
+ justify-content: center;
+}
+
+.diagram-content.diagram-right {
+ justify-content: right;
+}
+
+.diagram-content.diagram-float-left {
+ float: left;
+ margin-right: 1em;
max-width: 100%;
- cursor: pointer;
+}
+
+.diagram-content.diagram-float-right {
+ float: right;
+ margin-left: 1em;
+ max-width: 100%;
+}
+
+.diagram-content > .diagram-container {
+ max-width: 100%;
+ overflow: hidden !important;
+}
+
+.diagram-content > .diagram-container.full {
+ flex-grow: 1;
+}
+
+/* GraphViewer is only rendering when container has width */
+.diagram-content > .diagram-container:empty {
+ min-width: 1px;
+}
+
+/* Fixing images in GraphViewer lightbox toolbar */
+.geDiagramContainer + .geAdaptiveAsset + div > span > img {
+ display: initial;
+}
+
+/* Fixing centering of button in GraphViewer toolbar */
+body > div[style*="align-items:"]:not([class]) {
+ line-height: initial;
+ display: flex;
+}
+
+/* reset some styles for correct svg rendering */
+
+.diagram-content > .diagram-container, .geDiagramContainer {
+ line-height: initial;
+}
+
+.diagram-content > .diagram-container td,
+.geDiagramContainer td {
+ padding: revert-layer;
}
diff --git a/rsrc/remarkup-image.js b/rsrc/remarkup-image.js
--- a/rsrc/remarkup-image.js
+++ b/rsrc/remarkup-image.js
@@ -4,24 +4,58 @@
JX.onload(function() {
- var singleClickTimeout = null;
-
- JX.Stratcom.listen(
- 'click',
- ['diagram-remarkup-image'],
- function(evt) {
- var detail = evt.getRawEvent().detail;
-
- if (detail === 1) {
- singleClickTimeout = window.setTimeout(function() {
- window.open('/diagram/data/' + evt.getTarget().dataset.diagramVersion);
- singleClickTimeout = null;
- }, 300);
- } else if (detail === 2) {
- window.clearTimeout(singleClickTimeout);
- window.open('/diagram/DIAG' + evt.getTarget().dataset.diagramId);
+ var viewerScriptURI = 'https://viewer.diagrams.net/js/viewer-static.min.js';
+ var viewerScriptPrepended = false;
+ var viewerScriptBlocked = false;
+
+ document.addEventListener('securitypolicyviolation', function(e) {
+ if (viewerScriptURI === e.blockedURI) {
+ viewerScriptBlocked = true;
+ processElements();
+ }
+ });
+
+ function processViewer(viewer) {
+ viewer.toolbar.appendChild(
+ viewer.createToolbarButton(
+ function() { window.open(viewer.graphConfig.edit); },
+ 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE0LjA2IDkuMDJsLjkyLjkyTDUuOTIgMTlINXYtLjkybDkuMDYtOS4wNk0xNy42NiAzYy0uMjUgMC0uNTEuMS0uNy4yOWwtMS44MyAxLjgzIDMuNzUgMy43NSAxLjgzLTEuODNjLjM5LS4zOS4zOS0xLjAyIDAtMS40MWwtMi4zNC0yLjM0Yy0uMi0uMi0uNDUtLjI5LS43MS0uMjl6bS0zLjYgMy4xOUwzIDE3LjI1VjIxaDMuNzVMMTcuODEgOS45NGwtMy43NS0zLjc1eiIvPjwvc3ZnPg==',
+ null,
+ true
+ )
+ );
+ }
+
+ function processElements() {
+ console.log('processElements: ', document.querySelectorAll('.diagram-container:empty'));
+ document.querySelectorAll('.diagram-container:empty').forEach(function(container) {
+ if (window.GraphViewer) {
+ GraphViewer.createViewerForElement(container, processViewer);
+ } else if (viewerScriptBlocked) {
+ container.innerHTML = '<em>Diagram Viewer could not be loaded.</em>';
}
+ });
+ }
+
+ function init() {
+ if (!document.querySelector('.diagram-container')) {
+ return;
}
- );
+ if (viewerScriptPrepended || viewerScriptBlocked) {
+ processElements();
+ return;
+ }
+
+ var viewerScript = document.createElement('script');
+ viewerScript.setAttribute('src', viewerScriptURI);
+ viewerScript.addEventListener('load', processElements);
+
+ document.body.prepend(viewerScript);
+
+ viewerScriptPrepended = true;
+ }
+
+ init();
+ JX.Request.listen('done', function() { setTimeout(init); });
});
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -26,6 +26,7 @@
'DiagramTransactionType' => 'xaction/DiagramTransactionType.php',
'DiagramUploadConduitAPIMethod' => 'conduit/DiagramUploadConduitAPIMethod.php',
'DiagramVersion' => 'storage/DiagramVersion.php',
+ 'DrawioPngParser' => 'parser/DrawioPngParser.php',
'PhabricatorDiagramQuery' => 'query/PhabricatorDiagramQuery.php',
'PhabricatorDiagramTransactionQuery' => 'query/PhabricatorDiagramTransactionQuery.php',
'PhabricatorDiagramVersionQuery' => 'query/PhabricatorDiagramVersionQuery.php',
@@ -61,6 +62,7 @@
'PhabricatorDestructibleInterface',
'PhabricatorPolicyInterface',
),
+ 'DrawioPngParser' => 'Phobject',
'PhabricatorDiagramQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorDiagramTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorDiagramVersionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
diff --git a/src/application/DiagramApplication.php b/src/application/DiagramApplication.php
--- a/src/application/DiagramApplication.php
+++ b/src/application/DiagramApplication.php
@@ -1,6 +1,11 @@
<?php
final class DiagramApplication extends PhabricatorApplication {
+ public function __construct() {
+ CelerityAPI::getStaticResourceResponse()
+ ->addContentSecurityPolicyURI('script-src', 'https://viewer.diagrams.net');
+ }
+
public function getName() {
return pht('Diagrams');
}
diff --git a/src/celerity/map.php b/src/celerity/map.php
--- a/src/celerity/map.php
+++ b/src/celerity/map.php
@@ -11,15 +11,15 @@
'diagram-extension.css' => '7ad39d5d',
'iframe-toolbtn.css' => '35ad6f49',
'iframe-toolbtn.js' => '26d75a35',
- 'remarkup-image.css' => '42b46bf1',
- 'remarkup-image.js' => '64e7e9e1',
+ 'remarkup-image.css' => '51ddb637',
+ 'remarkup-image.js' => '842accb1',
),
'symbols' => array(
'diagram-css-extension' => '7ad39d5d',
'diagram-css-iframe-toolbtn' => '35ad6f49',
'diagram-js-iframe-toolbtn' => '26d75a35',
- 'diagram-remarkup-image-css' => '42b46bf1',
- 'diagram-remarkup-image-js' => '64e7e9e1',
+ 'diagram-remarkup-image-css' => '51ddb637',
+ 'diagram-remarkup-image-js' => '842accb1',
'javelin-behavior-diagram-extension' => 'a0b36dca',
),
'requires' => array(),
diff --git a/src/controller/DiagramController.php b/src/controller/DiagramController.php
--- a/src/controller/DiagramController.php
+++ b/src/controller/DiagramController.php
@@ -243,53 +243,19 @@
* They are cut out before the 2 strings are compared.
*/
public static function equalPngMetaData($base64_1, $base64_2) {
- $base64 = array($base64_1, $base64_2);
- $textData = array();
- for ($i = 0; $i < 2; $i++) {
- $data = base64_decode($base64[$i]);
- $fp = fopen('data://text/plain;base64,' . base64_encode($data), 'rb');
- $sig = fread($fp, 8);
- if ($sig != "\x89PNG\x0d\x0a\x1a\x0a") {
- fclose($fp);
- return false;
- }
- $textData[$i] = array();
- while (!feof($fp)) {
- try {
- $chunk = unpack('Nlength/a4type', fread($fp, 8));
- } catch (Exception $e) {
- // invalid base64 data
- return false;
- }
- if ($chunk['type'] == 'IEND') break;
- if ($chunk['type'] == 'tEXt') {
- list($key, $val) = explode("\0", fread($fp, $chunk['length']));
- if ($key == 'mxfile') {
- // Decode the URL-encoded XML data
- $decodedVal = urldecode($val);
- // Load the XML and remove the modified and etag attributes
- $xml = simplexml_load_string($decodedVal);
- unset($xml->attributes()->modified);
- unset($xml->attributes()->etag);
- // Save the modified XML as the value
- $val = $xml->asXML();
- }
- $textData[$i][$key] = $val;
- fseek($fp, 4, SEEK_CUR);
- } else {
- fseek($fp, $chunk['length'] + 4, SEEK_CUR);
- }
- }
- fclose($fp);
- }
+ $mxfile1 = DrawioPngParser::getMxfile($base64_1);
+ $mxfile2 = DrawioPngParser::getMxfile($base64_2);
- if (isset($textData[0]['mxfile']) && isset($textData[1]['mxfile'])) {
- // Both arrays contain the mxfile key, compare their values
- return $textData[0]['mxfile'] == $textData[1]['mxfile'];
- } else {
- // At least one of the arrays doesn't contain mxfile key, return false
+ if ($mxfile1 === null || $mxfile2 === null) {
return false;
}
+
+ unset($mxfile1->attributes()->modified);
+ unset($mxfile1->attributes()->etag);
+ unset($mxfile2->attributes()->modified);
+ unset($mxfile2->attributes()->etag);
+
+ return $mxfile1->asXML() == $mxfile2->asXML();
}
/**
diff --git a/src/parser/DrawioPngParser.php b/src/parser/DrawioPngParser.php
new file mode 100644
--- /dev/null
+++ b/src/parser/DrawioPngParser.php
@@ -0,0 +1,38 @@
+<?php
+
+class DrawioPngParser extends Phobject {
+
+ static public function getMxfile($base64Data) {
+ $fp = fopen('data://text/plain;base64,' . $base64Data, 'rb');
+ $sig = fread($fp, 8);
+
+ if ($sig != "\x89PNG\x0d\x0a\x1a\x0a") {
+ fclose($fp);
+ return null;
+ }
+
+ while (!feof($fp)) {
+ try {
+ $chunk = unpack('Nlength/a4type', fread($fp, 8));
+ } catch (Exception $e) {
+ // invalid base64 data
+ return null;
+ }
+ if ($chunk['type'] == 'IEND') break;
+ if ($chunk['type'] == 'tEXt') {
+ list($key, $val) = explode("\0", fread($fp, $chunk['length']));
+ if ($key == 'mxfile') {
+ fclose($fp);
+ // Decode the URL-encoded XML data
+ return simplexml_load_string(urldecode($val), null, LIBXML_NOBLANKS);
+ }
+ fseek($fp, 4, SEEK_CUR);
+ } else {
+ fseek($fp, $chunk['length'] + 4, SEEK_CUR);
+ }
+ }
+
+ fclose($fp);
+ return null;
+ }
+}
diff --git a/src/remarkup/PhabricatorRemarkupDiagramRule.php b/src/remarkup/PhabricatorRemarkupDiagramRule.php
--- a/src/remarkup/PhabricatorRemarkupDiagramRule.php
+++ b/src/remarkup/PhabricatorRemarkupDiagramRule.php
@@ -11,10 +11,6 @@
return 'DIAG';
}
- public function getRuleVersion() {
- return '1.0';
- }
-
protected function loadObjects(array $ids) {
$viewer = $this->getEngine()->getConfig('viewer');
@@ -31,70 +27,66 @@
PhabricatorObjectHandle $handle,
$options) {
- if ($options) {
- $params = explode(',', $options);
- $params = array_map('trim', $params);
- } else {
- $params = array();
+ $options = $this->getOptions($options);
+ $fullSize = !!$options['full'];
+ $contentClass = 'diagram-content';
+ $contentStyle = '';
+
+ switch ($options['layout'] && !$fullSize) {
+ case 'right':
+ case 'center':
+ $contentClass .= ' diagram-' . $options['layout'];
+ break;
+ case 'left':
+ default:
+ $contentClass .= ' diagram-left';
+ break;
}
- // Generate the appropriate HTML using the data from the Diagram and
- // file objects.
- $style = '';
- $class = 'diagram-content';
- $alt = '';
-
- $has_layout = false;
- foreach ($params as $param) {
- if (strpos($param, '=') !== false) {
- list($key, $value) = explode('=', $param, 2);
- } else {
- $key = $param;
- $value = null;
- }
- switch ($key) {
- case 'layout':
- $has_layout = true;
- if ($value === 'left') {
- $class .= ' phabricator-remarkup-embed-layout-left';
- } else if ($value === 'right') {
- $class .= ' phabricator-remarkup-embed-layout-right';
- }
+ if ($options['float'] && !$fullSize) {
+ switch ($options['layout']) {
+ case 'right':
+ $contentClass .= ' diagram-float-right';
break;
- case 'float':
- $class .= ' phabricator-remarkup-embed-float-left';
- break;
- case 'size':
- if ($value === 'full') {
- $style .= 'width: 100%;';
- }
- break;
- case 'alt':
- $alt = phutil_escape_html($value);
+ case 'left':
+ default:
+ $contentClass .= ' diagram-float-left';
break;
}
}
- if ($has_layout == false) {
- $class .= ' phabricator-remarkup-embed-layout-left';
+ $width = $options['width'];
+ if ($width && !$fullSize && preg_match('/^(?:\d*\\.)?\d+(%|px)?$/', $width)) {
+ if (is_numeric($width)) {
+ $contentStyle = 'width: ' . $width . 'px;';
+ } else {
+ $contentStyle = 'width: ' . $width . ';';
+ }
}
+ $mxGraphConfig = array(
+ 'responsive' => $fullSize,
+ 'resize' => true,
+ 'editable' => true,
+ 'edit' => PhabricatorEnv::getURI('/diagram/DIAG' . $diagram->getDiagramID()),
+ 'lightbox' => true,
+ 'toolbar' => 'pages' . ($fullSize ? '' : ' zoom'),
+ 'toolbar-position' => 'inline',
+ 'page' => $options['page'],
+ 'xml' => DrawioPngParser::getMxfile($diagram->getBase64Data())->asXML()
+ );
+
$output = phutil_tag(
'div',
array(
- 'class' => 'diagram-container',
+ 'class' => $contentClass,
+ 'style' => $contentStyle,
),
phutil_tag(
- 'img',
+ 'div',
array(
- 'style' => $style,
- 'class' => $class,
- 'src' => 'data:image/png;base64,' . $diagram->getBase64Data(),
- 'alt' => $alt,
- 'data-sigil' => 'diagram-remarkup-image',
- 'data-diagram-version' => $diagram->getPHID(),
- 'data-diagram-id' => $diagram->getDiagramID(),
- 'title' => 'Double click to edit...'
+ 'class' => 'diagram-container' . ($fullSize ? ' full' : ''),
+ 'data-mxgraph' => json_encode($mxGraphConfig)
)
)
);
@@ -102,6 +94,24 @@
return $output;
}
+ private function getOptions($option_string) {
+ $options = array(
+ 'full' => false,
+ 'layout' => 'left',
+ 'float' => false,
+ 'width' => '',
+ 'page' => 0
+ );
+
+ if ($option_string) {
+ $option_string = trim($option_string, ', ');
+ $parser = new PhutilSimpleOptions();
+ $options = $parser->parse($option_string) + $options;
+ }
+
+ return $options;
+ }
+
public function getDocumentation() {
return <<<EOT
= Diagrams
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Apr 17, 04:15 (3 d, 12 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1285727
Default Alt Text
D25960.1744863344.diff (14 KB)
Attached To
Mode
D25960: Reverts faulty git merge
Attached
Detach File
Event Timeline
Log In to Comment