Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions docs/en_US/preferences.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,24 @@ The Miscellaneous Node

Expand the *Miscellaneous* node to specify miscellaneous display preferences.

.. image:: images/preferences_misc_file_downloads.png
:alt: Preferences dialog file downloads section
:align: center

Use the fields on the *File Downloads* panel to manage file downloads related preferences.

* When the *Automatically open downloaded files?* switch is set to *True*
the downloaded file will automatically open in the system's default
application associated with that file type.

* When the *Prompt for the download location?* switch is set to *True*
a prompt will appear after clicking the download button, allowing you
to choose the download location.

**Note:** File Downloads related settings are applicable and visible only in desktop mode.

Use the fields on the *User Interface* panel to set the user interface related preferences.

.. image:: images/preferences_misc_user_interface.png
:alt: Preferences dialog user interface section
:align: center
Expand Down
1 change: 1 addition & 0 deletions runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"dependencies": {
"axios": "^1.8.1",
"electron-context-menu": "^4.0.5",
"electron-dl": "^4.0.0",
"electron-store": "^10.0.0"
}
}
24 changes: 24 additions & 0 deletions runtime/src/js/pgadmin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { setupMenu } from './menu.js';
import contextMenu from 'electron-context-menu';
import { CancelError, download } from 'electron-dl';

const configStore = new Store({
defaults: {
Expand Down Expand Up @@ -126,6 +127,28 @@ function reloadApp() {
currWin.webContents.reload();
}

async function desktopFileDownload(payload) {
const currWin = BrowserWindow.getFocusedWindow();
try {
await download(currWin, payload.downloadUrl, {
filename: payload.fileName,
saveAs: payload.prompt_for_download_location,
onProgress: (progress) => {
currWin.webContents.send('download-progress', progress);
},
onCompleted: (item) => {
currWin.webContents.send('download-complete', item);
if (payload.automatically_open_downloaded_file)
shell.openPath(item.path);
},
});
} catch (error) {
if (!(error instanceof CancelError)) {
misc.writeServerLog(error);
}
}
}

// This functions is used to start the pgAdmin4 server by spawning a
// separate process.
function startDesktopMode() {
Expand Down Expand Up @@ -369,6 +392,7 @@ ipcMain.on('log', (text) => ()=>{
misc.writeServerLog(text);
});
ipcMain.on('reloadApp', reloadApp);
ipcMain.on('onFileDownload', (_, payload) => desktopFileDownload(payload));
ipcMain.handle('checkPortAvailable', async (_e, fixedPort)=>{
try {
await misc.getAvailablePort(fixedPort);
Expand Down
1 change: 1 addition & 0 deletions runtime/src/js/pgadmin_preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ contextBridge.exposeInMainWorld('electronUI', {
showSaveDialog: (options) => ipcRenderer.invoke('showSaveDialog', options),
log: (text)=> ipcRenderer.send('log', text),
reloadApp: ()=>{ipcRenderer.send('reloadApp');},
onFileDownload: (payload) => ipcRenderer.send('onFileDownload', payload),
});
1 change: 1 addition & 0 deletions runtime/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,7 @@ __metadata:
axios: ^1.8.1
electron: 34.3.0
electron-context-menu: ^4.0.5
electron-dl: ^4.0.0
electron-store: ^10.0.0
eslint: ^9.21.0
languageName: unknown
Expand Down
19 changes: 2 additions & 17 deletions web/pgadmin/dashboard/static/js/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import Replication from './Replication';
import { getExpandCell } from '../../../static/js/components/PgReactTableStyled';
import CodeMirror from '../../../static/js/components/ReactCodeMirror';
import GetAppRoundedIcon from '@mui/icons-material/GetAppRounded';
import { getBrowser } from '../../../static/js/utils';
import { downloadFile } from '../../../static/js/utils';
import RefreshButton from './components/RefreshButtons';

function parseData(data) {
Expand Down Expand Up @@ -453,22 +453,7 @@ function Dashboard({
let fileName = 'data-' + new Date().getTime() + extension;

try {
let respBlob = new Blob([respData], {type : 'text/'+type}),
urlCreator = window.URL || window.webkitURL,
download_url = urlCreator.createObjectURL(respBlob),
link = document.createElement('a');

document.body.appendChild(link);

if (getBrowser() == 'IE' && window.navigator.msSaveBlob) {
// IE10: (has Blob, but not a[download] or URL)
window.navigator.msSaveBlob(respBlob, fileName);
} else {
link.setAttribute('href', download_url);
link.setAttribute('download', fileName);
link.click();
}
document.body.removeChild(link);
downloadFile(respData, fileName, `text/${type}`);
} catch {
setSsMsg(gettext('Failed to download the logs.'));
}
Expand Down
29 changes: 28 additions & 1 deletion web/pgadmin/misc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
from pgadmin.user_login_check import pga_login_required
from pathlib import Path
from pgadmin.utils import PgAdminModule, get_binary_path_versions
from pgadmin.utils.constants import PREF_LABEL_USER_INTERFACE
from pgadmin.utils.constants import PREF_LABEL_USER_INTERFACE, \
PREF_LABEL_FILE_DOWNLOADS
from pgadmin.utils.csrf import pgCSRFProtect
from pgadmin.utils.session import cleanup_session_files
from pgadmin.misc.themes import get_all_themes
Expand Down Expand Up @@ -120,6 +121,32 @@ def register_preferences(self):
)
)

if not config.SERVER_MODE:
self.preference.register(
'file_downloads', 'automatically_open_downloaded_file',
gettext("Automatically open downloaded file?"),
'boolean', False,
category_label=PREF_LABEL_FILE_DOWNLOADS,
help_str=gettext(
'''This setting is applicable and visible only in
desktop mode. When set to True, the downloaded file
will automatically open in the system's default
application associated with that file type.'''
)
)
self.preference.register(
'file_downloads', 'prompt_for_download_location',
gettext("Prompt for the download location?"),
'boolean', True,
category_label=PREF_LABEL_FILE_DOWNLOADS,
help_str=gettext(
'This setting is applicable and visible only '
'in desktop mode. When set to True, a prompt '
'will appear after clicking the download button, '
'allowing you to choose the download location'
)
)

def get_exposed_url_endpoints(self):
"""
Returns:
Expand Down
10 changes: 2 additions & 8 deletions web/pgadmin/static/js/Explain/svg_download.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//
//////////////////////////////////////////////////////////////
import getApiInstance from '../api_instance';
import { downloadFile } from '../utils';

function convertImageURLtoDataURI(api, image) {
return new Promise(function(resolve, reject) {
Expand Down Expand Up @@ -42,13 +43,6 @@ export function downloadSvg(svg, svgName) {
}

Promise.all(image_promises).then(function() {
let blob = new Blob([svgElement.outerHTML], {type: 'image/svg+xml'});
let svgURL = (window.URL || window.webkitURL).createObjectURL(blob);
let newElement = document.createElement('a');
newElement.href = svgURL;
newElement.setAttribute('download', svgName);
document.body.appendChild(newElement);
newElement.click();
document.body.removeChild(newElement);
downloadFile(svgElement.outerHTML, svgName, 'image/svg+xml');
});
}
25 changes: 20 additions & 5 deletions web/pgadmin/static/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,14 +369,16 @@ export function checkTrojanSource(content, isPasteEvent) {
}
}

export function downloadBlob(blob, fileName) {
if (getBrowser() == 'IE' && window.navigator.msSaveBlob) {
export async function downloadBlob(blob, fileName) {
const {automatically_open_downloaded_file, prompt_for_download_location} = usePreferences.getState().getPreferencesForModule('misc');
const urlCreator = window.URL || window.webkitURL;
const downloadUrl = urlCreator.createObjectURL(blob);
if (getBrowser().name == 'IE' && window.navigator.msSaveBlob) {
// IE10+ : (has Blob, but not a[download] or URL)
window.navigator.msSaveBlob(blob, fileName);
} else if (getBrowser().name == 'Electron') {
await window.electronUI.onFileDownload({downloadUrl, fileName, automatically_open_downloaded_file, prompt_for_download_location});
} else {
const urlCreator = window.URL || window.webkitURL;
const downloadUrl = urlCreator.createObjectURL(blob);

const link = document.createElement('a');
link.setAttribute('href', downloadUrl);
link.setAttribute('download', fileName);
Expand All @@ -388,6 +390,19 @@ export function downloadBlob(blob, fileName) {
}
}

export async function downloadUrlData(downloadUrl, fileName) {
const {automatically_open_downloaded_file, prompt_for_download_location} = usePreferences.getState().getPreferencesForModule('misc');
if (getBrowser().name == 'Electron') {
window.electronUI.onFileDownload({downloadUrl, fileName, automatically_open_downloaded_file, prompt_for_download_location});
} else {
let link = document.createElement('a');
link.setAttribute('href', downloadUrl);
link.setAttribute('download', fileName);
link.click();
link.remove();
}
}

export function downloadFile(textData, fileName, fileType) {
const respBlob = new Blob([textData], {type : fileType});
downloadBlob(respBlob, fileName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import pgAdmin from 'sources/pgadmin';
import { styled } from '@mui/material/styles';
import BeforeUnload from './BeforeUnload';
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
import { downloadUrlData } from '../../../../../../static/js/utils';

/* Custom react-diagram action for keyboard events */
export class KeyboardShortcutAction extends Action {
Expand Down Expand Up @@ -760,11 +761,7 @@ export default class ERDTool extends React.Component {
}
toPng(this.canvasEle, {width, height})
.then((dataUrl)=>{
let link = document.createElement('a');
link.setAttribute('href', dataUrl);
link.setAttribute('download', this.getCurrentProjectName() + '.png');
link.click();
link.remove();
downloadUrlData(dataUrl, `${this.getCurrentProjectName()}.png`);
}).catch((err)=>{
console.error(err);
let msg = gettext('Unknown error. Check console logs');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { LineChart, BarChart, PieChart, DATA_POINT_STYLE, DATA_POINT_SIZE,
LightenDarkenColor} from 'sources/chartjs';
import { QueryToolEventsContext, QueryToolContext } from '../QueryToolComponent';
import { QUERY_TOOL_EVENTS, PANELS } from '../QueryToolConstants';
import { getChartColor } from '../../../../../../static/js/utils';
import { downloadUrlData, getChartColor } from '../../../../../../static/js/utils';

const StyledBox = styled(Box)(({theme}) => ({
width: '100%',
Expand Down Expand Up @@ -377,11 +377,10 @@ export function GraphVisualiser({initColumns}) {
};

// Download button callback
const onDownloadGraph = ()=> {
let a = document.createElement('a');
a.href = chartObjRef.current.toBase64Image();
a.download = 'graph_visualiser-' + new Date().getTime() + '.png';
a.click();
const onDownloadGraph = async ()=> {
let downloadUrl = chartObjRef.current.toBase64Image(),
fileName = 'graph_visualiser-' + new Date().getTime() + '.png';
downloadUrlData(downloadUrl, fileName);
};

// This plugin is used to set the background color of the canvas. Very useful
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { LayoutDockerContext } from '../../../../../../static/js/helpers/Layout'
import { GeometryViewer } from './GeometryViewer';
import Explain from '../../../../../../static/js/Explain';
import { QuerySources } from './QueryHistory';
import { getBrowser } from '../../../../../../static/js/utils';
import { downloadFile } from '../../../../../../static/js/utils';
import CopyData from '../QueryToolDataGrid/CopyData';
import moment from 'moment';
import ConfirmSaveContent from '../../../../../../static/js/Dialogs/ConfirmSaveContent';
Expand Down Expand Up @@ -478,22 +478,7 @@ export class ResultSetUtils {
}
} else {
this.hasQueryCommitted = false;
let respBlob = new Blob([respData], {type : 'text/csv'}),
urlCreator = window.URL || window.webkitURL,
download_url = urlCreator.createObjectURL(respBlob),
link = document.createElement('a');

document.body.appendChild(link);

if (getBrowser() == 'IE' && window.navigator.msSaveBlob) {
// IE10+ : (has Blob, but not a[download] or URL)
window.navigator.msSaveBlob(respBlob, fileName);
} else {
link.setAttribute('href', download_url);
link.setAttribute('download', fileName);
link.click();
}
document.body.removeChild(link);
downloadFile(respData, fileName, 'text/csv');
}
this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS_END);
} catch (error) {
Expand Down
1 change: 1 addition & 0 deletions web/pgadmin/utils/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
PREF_LABEL_REFRESH_RATES = gettext('Refresh rates')
PREF_LABEL_GRAPH_VISUALISER = gettext('Graph Visualiser')
PREF_LABEL_USER_INTERFACE = gettext('User Interface')
PREF_LABEL_FILE_DOWNLOADS = gettext('File Downloads')

PGADMIN_STRING_SEPARATOR = '_$PGADMIN$_'
PGADMIN_NODE = 'pgadmin.node.%s'
Expand Down
Loading