Fixed an issue while downloading ERD images in Safari and Firefox. Fixes #6177

This commit is contained in:
Aditya Toshniwal 2021-01-29 12:19:24 +05:30 committed by Akshay Joshi
parent 358af42c50
commit a0271c7656
14 changed files with 100 additions and 35 deletions

View File

@ -22,6 +22,7 @@ Bug fixes
| `Issue #6087 <https://redmine.postgresql.org/issues/6087>`_ - Fixed an issue where the dependencies tab showing multiple owners for the objects having shared dependencies.
| `Issue #6163 <https://redmine.postgresql.org/issues/6163>`_ - Fixed an issue where Zoom to fit button only works if the diagram is larger than the canvas.
| `Issue #6164 <https://redmine.postgresql.org/issues/6164>`_ - Ensure that the diagram should not vanish entirely if zooming out too far in ERD.
| `Issue #6177 <https://redmine.postgresql.org/issues/6177>`_ - Fixed an issue while downloading ERD images in Safari and Firefox.
| `Issue #6179 <https://redmine.postgresql.org/issues/6179>`_ - Fixed an issue where Generate SQL displayed twice in the ERD tool.
| `Issue #6180 <https://redmine.postgresql.org/issues/6180>`_ - Updated missing documentation for the 'Download Image' option in ERD.

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fee;}.cls-2{fill:#bd4949;}</style></defs><title>coll-schema</title><g id="_2" data-name="2"><rect class="cls-1" x="4.05" y="4.05" width="7.89" height="7.89" transform="translate(-3.31 8) rotate(-45)"/><path class="cls-2" d="M8,3l5,5L8,13,3,8,8,3M8,1.89,1.89,8,8,14.11,14.11,8,8,1.89Z"/><rect class="cls-2" x="6.44" y="6.44" width="3.11" height="3.11" transform="translate(19.31 8) rotate(135)"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#fee;}.cls-2{fill:#bd4949;}</style></defs><title>coll-schema</title><g id="_2" data-name="2"><rect class="cls-1" x="4.05" y="4.05" width="7.89" height="7.89" transform="translate(-3.31 8) rotate(-45)"/><path class="cls-2" d="M8,3l5,5L8,13,3,8,8,3M8,1.89,1.89,8,8,14.11,14.11,8,8,1.89Z"/><rect class="cls-2" x="6.44" y="6.44" width="3.11" height="3.11" transform="translate(19.31 8) rotate(135)"/></g></svg>

Before

Width:  |  Height:  |  Size: 491 B

After

Width:  |  Height:  |  Size: 515 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>column</title><g id="_2" data-name="2"><rect class="cls-1" x="5.3" y="2.15" width="5.4" height="11.7" rx="1" ry="1"/><line class="cls-2" x1="5.75" y1="8" x2="10.25" y2="8"/><line class="cls-2" x1="5.75" y1="10.7" x2="10.25" y2="10.7"/><path class="cls-3" d="M6,5.83l3.9,0V13a.15.15,0,0,1-.15.15H6.2A.15.15,0,0,1,6,13V5.83M5.3,5.08V13a.9.9,0,0,0,.9.9H9.8a.9.9,0,0,0,.9-.9V5.1l-5.4,0Z"/><path class="cls-3" d="M9.8,2.9A.15.15,0,0,1,10,3V5H6V3A.15.15,0,0,1,6.2,2.9H9.8m0-.75H6.2a.9.9,0,0,0-.9.9v2.7h5.4V3a.9.9,0,0,0-.9-.9Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#e1fff9;}.cls-2{fill:none;stroke:#aecec8;stroke-linejoin:round;stroke-width:0.75px;}.cls-3{fill:#16a085;}</style></defs><title>column</title><g id="_2" data-name="2"><rect class="cls-1" x="5.3" y="2.15" width="5.4" height="11.7" rx="1" ry="1"/><line class="cls-2" x1="5.75" y1="8" x2="10.25" y2="8"/><line class="cls-2" x1="5.75" y1="10.7" x2="10.25" y2="10.7"/><path class="cls-3" d="M6,5.83l3.9,0V13a.15.15,0,0,1-.15.15H6.2A.15.15,0,0,1,6,13V5.83M5.3,5.08V13a.9.9,0,0,0,.9.9H9.8a.9.9,0,0,0,.9-.9V5.1l-5.4,0Z"/><path class="cls-3" d="M9.8,2.9A.15.15,0,0,1,10,3V5H6V3A.15.15,0,0,1,6.2,2.9H9.8m0-.75H6.2a.9.9,0,0,0-.9.9v2.7h5.4V3a.9.9,0,0,0-.9-.9Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 768 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#f2f2f2;stroke:#797979;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>foreign_key</title><g id="_2" data-name="2"><path class="cls-1" d="M11.41,3.41A2.69,2.69,0,0,0,7.07,6.48L2.85,10.75A.9.9,0,0,0,4.12,12l.77-.8L7,12.46l-.92-2.32L8,10.66,7.07,9,8.34,7.75a2.69,2.69,0,0,0,3.07-4.34Z"/><path class="cls-1" d="M12.62,4.33A2.69,2.69,0,0,0,8.29,7.4L4.07,11.67a.9.9,0,1,0,1.27,1.27l.77-.8,2.12,1.24-.92-2.32,1.87.52L8.29,9.93,9.56,8.67a2.69,2.69,0,0,0,3.07-4.34ZM11.34,6.89a.9.9,0,1,1,0-1.27A.9.9,0,0,1,11.34,6.89Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#f2f2f2;stroke:#797979;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>foreign_key</title><g id="_2" data-name="2"><path class="cls-1" d="M11.41,3.41A2.69,2.69,0,0,0,7.07,6.48L2.85,10.75A.9.9,0,0,0,4.12,12l.77-.8L7,12.46l-.92-2.32L8,10.66,7.07,9,8.34,7.75a2.69,2.69,0,0,0,3.07-4.34Z"/><path class="cls-1" d="M12.62,4.33A2.69,2.69,0,0,0,8.29,7.4L4.07,11.67a.9.9,0,1,0,1.27,1.27l.77-.8,2.12,1.24-.92-2.32,1.87.52L8.29,9.93,9.56,8.67a2.69,2.69,0,0,0,3.07-4.34ZM11.34,6.89a.9.9,0,1,1,0-1.27A.9.9,0,0,1,11.34,6.89Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 645 B

After

Width:  |  Height:  |  Size: 669 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#f5f47e;stroke:#caa524;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>primary_key</title><g id="_2" data-name="2"><path class="cls-1" d="M12.46,3.41A3,3,0,0,0,7.64,6.82L3,11.56A1,1,0,0,0,4.37,13l.85-.89,2.36,1.38-1-2.58,2.08.58-1-1.83,1.41-1.4a3,3,0,0,0,3.41-4.82ZM11,6.25a1,1,0,1,1,0-1.41A1,1,0,0,1,11,6.25Z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#f5f47e;stroke:#caa524;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>primary_key</title><g id="_2" data-name="2"><path class="cls-1" d="M12.46,3.41A3,3,0,0,0,7.64,6.82L3,11.56A1,1,0,0,0,4.37,13l.85-.89,2.36,1.38-1-2.58,2.08.58-1-1.83,1.41-1.4a3,3,0,0,0,3.41-4.82ZM11,6.25a1,1,0,1,1,0-1.41A1,1,0,0,1,11,6.25Z"/></g></svg>

Before

Width:  |  Height:  |  Size: 445 B

After

Width:  |  Height:  |  Size: 469 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#f2f2f2;}.cls-2{fill:#2195e7;}.cls-3{fill:none;stroke:#c1cbd5;stroke-linejoin:round;}.cls-3,.cls-4{stroke-width:0.75px;}.cls-4{fill:#def4fd;stroke:#2195e7;stroke-miterlimit:1;}</style></defs><title>table</title><g id="_2" data-name="2"><rect class="cls-1" x="2.92" y="3.65" width="10.15" height="8.71" rx="0.53" ry="0.53"/><path class="cls-2" d="M12.55,4a.15.15,0,0,1,.15.15v7.66a.15.15,0,0,1-.15.15H3.45a.15.15,0,0,1-.15-.15V4.17A.15.15,0,0,1,3.45,4h9.1m0-.75H3.45a.9.9,0,0,0-.9.9v7.66a.9.9,0,0,0,.9.9h9.1a.9.9,0,0,0,.9-.9V4.17a.9.9,0,0,0-.9-.9Z"/><line class="cls-3" x1="3.32" y1="9.43" x2="12.69" y2="9.43"/><line class="cls-3" x1="8.01" y1="7.09" x2="8" y2="11.97"/><line class="cls-4" x1="8.01" y1="4.03" x2="8" y2="6.58"/><line class="cls-4" x1="12.68" y1="6.74" x2="3.32" y2="6.74"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="20" height="20"><defs><style>.cls-1{fill:#f2f2f2;}.cls-2{fill:#2195e7;}.cls-3{fill:none;stroke:#c1cbd5;stroke-linejoin:round;}.cls-3,.cls-4{stroke-width:0.75px;}.cls-4{fill:#def4fd;stroke:#2195e7;stroke-miterlimit:1;}</style></defs><title>table</title><g id="_2" data-name="2"><rect class="cls-1" x="2.92" y="3.65" width="10.15" height="8.71" rx="0.53" ry="0.53"/><path class="cls-2" d="M12.55,4a.15.15,0,0,1,.15.15v7.66a.15.15,0,0,1-.15.15H3.45a.15.15,0,0,1-.15-.15V4.17A.15.15,0,0,1,3.45,4h9.1m0-.75H3.45a.9.9,0,0,0-.9.9v7.66a.9.9,0,0,0,.9.9h9.1a.9.9,0,0,0,.9-.9V4.17a.9.9,0,0,0-.9-.9Z"/><line class="cls-3" x1="3.32" y1="9.43" x2="12.69" y2="9.43"/><line class="cls-3" x1="8.01" y1="7.09" x2="8" y2="11.97"/><line class="cls-4" x1="8.01" y1="4.03" x2="8" y2="6.58"/><line class="cls-4" x1="12.68" y1="6.74" x2="3.32" y2="6.74"/></g></svg>

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 909 B

View File

@ -303,7 +303,7 @@ export default class ERDCore {
this.addLink(newData, 'onetomany');
});
setTimeout(this.dagreDistributeNodes.bind(this), 0);
setTimeout(this.dagreDistributeNodes.bind(this), 250);
}
repaint() {

View File

@ -94,7 +94,7 @@ const CustomLinkEndWidget = props => {
return (
<g transform={'translate(' + point.getPosition().x + ', ' + point.getPosition().y + ')'}>
<g transform={'translate('+tx+','+ty+')'}>
<g style={{ transform: 'rotate(' + rotation + 'deg)' }}>
<g transform={'rotate(' + rotation + ')' }>
{svgForType(type)}
</g>
</g>

View File

@ -12,6 +12,11 @@ import { DefaultNodeModel, PortWidget } from '@projectstorm/react-diagrams';
import { AbstractReactFactory } from '@projectstorm/react-canvas-core';
import _ from 'lodash';
import { IconButton, DetailsToggleButton } from '../ui_components/ToolBar';
import SchemaIcon from 'top/browser/server_groups/servers/databases/schemas/static/img/schema.svg';
import TableIcon from 'top/browser/server_groups/servers/databases/schemas/tables/static/img/table.svg';
import PrimaryKeyIcon from 'top/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/primary_key.svg';
import ForeignKeyIcon from 'top/browser/server_groups/servers/databases/schemas/tables/constraints/foreign_key/static/img/foreign_key.svg';
import ColumnIcon from 'top/browser/server_groups/servers/databases/schemas/tables/columns/static/img/column.svg';
const TYPE = 'table';
@ -114,6 +119,14 @@ export class TableNodeModel extends DefaultNodeModel {
}
}
function RowIcon({icon}) {
return (
<div className="table-icon">
<img src={icon} crossOrigin="anonymous"/>
</div>
);
}
export class TableNodeWidget extends React.Component {
constructor(props) {
super(props);
@ -131,11 +144,17 @@ export class TableNodeWidget extends React.Component {
generateColumn(col) {
let port = this.props.node.getPort(this.props.node.getPortName(col.attnum));
let icon = ColumnIcon;
if(col.is_primary_key) {
icon = PrimaryKeyIcon;
} else if(port && port.getSubtype() == 'many') {
icon = ForeignKeyIcon;
}
return (
<div className='d-flex col-row' key={col.attnum}>
<div className='d-flex col-row-data'>
<div><span className={'wcTabIcon ' + (col.is_primary_key?'icon-primary_key':'icon-column')}></span></div>
<div>
<RowIcon icon={icon} />
<div className="my-auto">
<span className='col-name'>{col.name}</span>&nbsp;
{this.state.show_details &&
<span className='col-datatype'>{col.cltype}{col.attlen ? ('('+ col.attlen + (col.attprecision ? ','+col.attprecision : '') +')') : ''}</span>}
@ -172,12 +191,12 @@ export class TableNodeWidget extends React.Component {
}} title="Check note" />}
</div>
<div className="d-flex table-schema-data">
<div className="table-icon-schema"></div>
<div className="table-schema">{node_data.schema}</div>
<RowIcon icon={SchemaIcon}/>
<div className="table-schema my-auto">{node_data.schema}</div>
</div>
<div className="d-flex table-name-data">
<div><span className="wcTabIcon icon-table"></span></div>
<div className="table-name">{node_data.name}</div>
<RowIcon icon={TableIcon} />
<div className="table-name my-auto">{node_data.name}</div>
</div>
<div className="table-cols">
{_.map(node_data.columns, (col)=>this.generateColumn(col))}

View File

@ -542,6 +542,35 @@ export default class BodyWidget extends React.Component {
this.canvasEle.style.width = this.canvasEle.scrollWidth + 'px';
this.canvasEle.style.height = this.canvasEle.scrollHeight + 'px';
/* html2canvas ignores CSS styles, set the CSS styles to inline */
const setSvgInlineStyles = (targetElem) => {
const transformProperties = [
'fill',
'color',
'font-size',
'stroke',
'font'
];
let svgElems = Array.from(targetElem.getElementsByTagName("svg"));
for (let svgElement of svgElems) {
svgElement.setAttribute('width', svgElement.clientWidth);
svgElement.setAttribute('height', svgElement.clientHeight);
recurseElementChildren(svgElement);
}
function recurseElementChildren(node) {
if (!node.style)
return;
let styles = getComputedStyle(node);
for (let transformProperty of transformProperties) {
node.style[transformProperty] = styles[transformProperty];
}
for (let child of Array.from(node.childNodes)) {
recurseElementChildren(child);
}
}
}
html2canvas(this.canvasEle, {
width: this.canvasEle.scrollWidth + 10,
height: this.canvasEle.scrollHeight + 10,
@ -550,11 +579,16 @@ export default class BodyWidget extends React.Component {
useCORS: true,
allowTaint: true,
backgroundColor: window.getComputedStyle(this.canvasEle).backgroundColor,
onclone: (clonedEle)=>{
setSvgInlineStyles(clonedEle);
return clonedEle;
},
}).then((canvas)=>{
let link = document.createElement('a');
link.setAttribute('href', canvas.toDataURL('image/png'));
link.setAttribute('download', this.getCurrentProjectName() + '.png');
link.click();
link.remove();
}).catch((err)=>{
console.error(err);
let msg = gettext('Unknown error. Check console logs');

View File

@ -97,20 +97,8 @@
width: 175px;
font-size: 0.8em;
.table-icon-schema {
background-image: url('~top/browser/server_groups/servers/databases/schemas/static/img/schema.svg') !important;
// background-repeat: no-repeat;
// // background-size: 20px !important;
// align-content: center;
// vertical-align: middle;
// height: 100%;
// background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDIwLjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA2NCA2NCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNjQgNjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojOTk5OTk5O3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30KPC9zdHlsZT4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTIxLjksMjIuMmMwLDMuNS0yLjksNi40LTYuNCw2LjRzLTYuNC0yLjktNi40LTYuNHMyLjktNi40LDYuNC02LjRTMjEuOSwxOC43LDIxLjksMjIuMnogTTQuMiw1MWwyMi4xLTE5CgkgTTQ2LjUsNTEuMUwyNi40LDMyIE0zNy42LDQyLjZsMTEuNi0xMCBNNjIuNCw0NS4xTDQ5LjIsMzIuNyBNNjMsMUgxdjYyaDYyVjF6IE0xLDUxLjRoNjIiLz4KPC9zdmc+Cg==');
width: 20px;
height: 20px;
// background: transparent url('~top/browser/server_groups/servers/databases/schemas/static/img/schema.svg') no-repeat center center;
// height: 100%;
// width: 20px;
.table-icon {
padding: 0rem 0.125rem;
}
&.selected {
@ -175,6 +163,7 @@
.svg-link-ele.path {
pointer-events: all;
cursor: move;
}
@keyframes svg-link-ele-selected {

View File

@ -304,7 +304,7 @@ describe('ERDCore', ()=>{
setTimeout(()=>{
expect(erdCoreObj.dagreDistributeNodes).toHaveBeenCalled();
done();
}, 10);
}, 500);
});
it('clearSelection', ()=>{

View File

@ -275,13 +275,6 @@ describe('ERD TableNodeWidget', ()=>{
expect(nodeWidget.find('.table-node .table-cols .col-row').length).toBe(3);
});
it('icons', ()=>{
let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
expect(cols.at(0).find('.wcTabIcon').hasClass('icon-primary_key')).toBeTruthy();
expect(cols.at(1).find('.wcTabIcon').hasClass('icon-column')).toBeTruthy();
expect(cols.at(2).find('.wcTabIcon').hasClass('icon-column')).toBeTruthy();
});
it('column names', ()=>{
let cols = nodeWidget.find('.table-node .table-cols .col-row-data');
expect(cols.at(0).find('.col-name').text()).toBe('id');

View File

@ -44,6 +44,35 @@ module.exports = {
}, {
test: /\.css$/,
use: [ 'style-loader', 'raw-loader' ],
}, {
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [{
loader: 'url-loader',
options: {
emitFile: true,
name: 'img/[name].[ext]',
limit: 4096,
},
}, {
loader: 'image-webpack-loader',
query: {
bypassOnDebug: true,
mozjpeg: {
progressive: true,
},
gifsicle: {
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
pngquant: {
quality: '75-90',
speed: 3,
},
},
}],
exclude: /vendor/,
}, {
test: /.*slickgrid[\\\/]+slick\.(?!core)*/,
loader: 'imports-loader?' +