diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py index 77365a4d4..c5035a465 100644 --- a/web/pgadmin/preferences/__init__.py +++ b/web/pgadmin/preferences/__init__.py @@ -134,6 +134,7 @@ def _iterate_categories(pref_d, label, res): "open": True, "children": [], "value": gettext(pref_d['label']), + "name": pref_d['name'] } for c in pref_d['categories']: diff --git a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx index 435cda740..b60a47441 100644 --- a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx +++ b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx @@ -185,7 +185,7 @@ export default function PreferencesComponent({ ...props }) { 'id': id.toString(), 'label': node.label, '_label': node.label, - 'name': node.label, + 'name': node.name, 'icon': '', 'inode': true, 'type': 2, @@ -378,7 +378,7 @@ export default function PreferencesComponent({ ...props }) { if(field.visible && _.isNull(firstElement)) { firstElement = field; } - field.tooltip = item._parent._metadata.data.name.toLowerCase() + ':' + item._metadata.data.name + ':' + field.name; + field.labelTooltip = item._parent._metadata.data.name.toLowerCase() + ':' + item._metadata.data.name + ':' + field.name; }); setLoadTree(crypto.getRandomValues(new Uint16Array(1))); initTreeTimeout = setTimeout(() => { diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx index f454f3c51..5aa5c12ae 100644 --- a/web/pgadmin/static/js/SchemaView/FormView.jsx +++ b/web/pgadmin/static/js/SchemaView/FormView.jsx @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////// import React, { useContext, useEffect, useRef, useState } from 'react'; -import { Box, makeStyles, Tab, Tabs, Tooltip } from '@material-ui/core'; +import { Box, makeStyles, Tab, Tabs } from '@material-ui/core'; import _ from 'lodash'; import PropTypes from 'prop-types'; import clsx from 'clsx'; @@ -363,10 +363,6 @@ export default function FormView({ ]} />; - if(field.tooltip) { - currentControl = {currentControl}; - } - if(field.isFullTab && field.helpMessage) { currentControl = ( diff --git a/web/pgadmin/static/js/SchemaView/MappedControl.jsx b/web/pgadmin/static/js/SchemaView/MappedControl.jsx index bcb1eb2a5..127456d13 100644 --- a/web/pgadmin/static/js/SchemaView/MappedControl.jsx +++ b/web/pgadmin/static/js/SchemaView/MappedControl.jsx @@ -212,7 +212,7 @@ const ALLOWED_PROPS_FIELD_COMMON = [ 'label', 'options', 'optionsLoaded', 'controlProps', 'schema', 'inputRef', 'visible', 'autoFocus', 'helpMessage', 'className', 'optionsReloadBasis', 'orientation', 'isvalidate', 'fields', 'radioType', 'hideBrowseButton', 'btnName', 'hidden', - 'withContainer', 'controlGridBasis', 'hasCheckbox', 'treeData', 'title' + 'withContainer', 'controlGridBasis', 'hasCheckbox', 'treeData', 'labelTooltip' ]; const ALLOWED_PROPS_FIELD_FORM = [ diff --git a/web/pgadmin/static/js/components/FormComponents.jsx b/web/pgadmin/static/js/components/FormComponents.jsx index eb5d83761..9889a4e81 100644 --- a/web/pgadmin/static/js/components/FormComponents.jsx +++ b/web/pgadmin/static/js/components/FormComponents.jsx @@ -12,7 +12,7 @@ import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } import { makeStyles } from '@material-ui/core/styles'; import { Box, FormControl, OutlinedInput, FormHelperText, - Grid, IconButton, FormControlLabel, Switch, Checkbox, useTheme, InputLabel, Paper, Select as MuiSelect, Radio, + Grid, IconButton, FormControlLabel, Switch, Checkbox, useTheme, InputLabel, Paper, Select as MuiSelect, Radio, Tooltip, } from '@material-ui/core'; import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab'; import ErrorRoundedIcon from '@material-ui/icons/ErrorOutlineRounded'; @@ -127,7 +127,7 @@ FormIcon.propTypes = { }; /* Wrapper on any form component to add label, error indicator and help message */ -export function FormInput({ children, error, className, label, helpMessage, required, testcid, lid, withContainer=true, labelGridBasis=3, controlGridBasis=9 }) { +export function FormInput({ children, error, className, label, helpMessage, required, testcid, lid, withContainer=true, labelGridBasis=3, controlGridBasis=9, labelTooltip='' }) { const classes = useStyles(); const cid = testcid || _.uniqueId('c'); const helpid = `h${cid}`; @@ -149,13 +149,20 @@ export function FormInput({ children, error, className, label, helpMessage, requ ); } + + let labelComponent = + {label} + + ; return ( - - {label} - - + { + labelTooltip ? + + {labelComponent} + : labelComponent + } @@ -178,6 +185,7 @@ FormInput.propTypes = { withContainer: PropTypes.bool, labelGridBasis: PropTypes.number, controlGridBasis: PropTypes.number, + labelTooltip: PropTypes.string }; export function InputSQL({ value, options, onChange, className, controlProps, inputRef, ...props }) { @@ -325,9 +333,9 @@ InputDateTimePicker.propTypes = { controlProps: PropTypes.object, }; -export function FormInputDateTimePicker({ hasError, required, label, className, helpMessage, testcid, ...props }) { +export function FormInputDateTimePicker({ hasError, required, label, className, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -342,6 +350,7 @@ FormInputDateTimePicker.propTypes = { value: PropTypes.string, controlProps: PropTypes.object, change: PropTypes.func, + labelTooltip: PropTypes.string }; /* Use forwardRef to pass ref prop to OutlinedInput */ @@ -426,9 +435,9 @@ InputText.propTypes = { inputStyle: PropTypes.object }; -export function FormInputText({ hasError, required, label, className, helpMessage, testcid, ...props }) { +export function FormInputText({ hasError, required, label, className, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -440,6 +449,7 @@ FormInputText.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, + labelTooltip: PropTypes.string }; export function InputFileSelect({ controlProps, onChange, disabled, readonly, isvalidate = false, hideBrowseButton=false,validate, ...props }) { @@ -488,10 +498,10 @@ InputFileSelect.propTypes = { }; export function FormInputFileSelect({ - hasError, required, label, className, helpMessage, testcid, ...props }) { + hasError, required, label, className, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -503,6 +513,7 @@ FormInputFileSelect.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, + labelTooltip: PropTypes.string }; export function InputSwitch({ cid, helpid, value, onChange, readonly, controlProps, ...props }) { @@ -533,10 +544,10 @@ InputSwitch.propTypes = { controlProps: PropTypes.object, }; -export function FormInputSwitch({ hasError, required, label, className, helpMessage, testcid, withContainer, controlGridBasis, ...props }) { +export function FormInputSwitch({ hasError, required, label, className, helpMessage, testcid, withContainer, controlGridBasis, labelTooltip, ...props }) { return ( + helpMessage={helpMessage} testcid={testcid} withContainer={withContainer} controlGridBasis={controlGridBasis} labelTooltip={labelTooltip}> ); @@ -550,6 +561,7 @@ FormInputSwitch.propTypes = { testcid: PropTypes.string, withContainer: PropTypes.bool, controlGridBasis: PropTypes.number, + labelTooltip: PropTypes.string }; export function InputCheckbox({ cid, helpid, value, onChange, controlProps, readonly, labelPlacement, ...props }) { @@ -581,10 +593,10 @@ InputCheckbox.propTypes = { }; export function FormInputCheckbox({ hasError, required, label, - className, helpMessage, testcid, ...props }) { + className, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -596,6 +608,7 @@ FormInputCheckbox.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, + labelTooltip: PropTypes.string }; export function InputRadio({ helpid, value, onChange, controlProps, readonly, labelPlacement, ...props }) { @@ -675,9 +688,9 @@ InputToggle.propTypes = { }; export function FormInputToggle({ hasError, required, label, - className, helpMessage, testcid, inputRef, ...props }) { + className, helpMessage, testcid, inputRef, labelTooltip, ...props }) { return ( - + ); @@ -689,7 +702,8 @@ FormInputToggle.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, - inputRef: CustomPropTypes.ref + inputRef: CustomPropTypes.ref, + labelTooltip: PropTypes.string }; /* react-select package is used for select input @@ -1022,9 +1036,9 @@ InputSelect.propTypes = { export function FormInputSelect({ - hasError, required, className, label, helpMessage, testcid, ...props }) { + hasError, required, className, label, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -1036,7 +1050,8 @@ FormInputSelect.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, - inputRef: CustomPropTypes.ref + inputRef: CustomPropTypes.ref, + labelTooltip: PropTypes.string }; const ColorButton = withColorPicker(PgIconButton); @@ -1062,10 +1077,10 @@ InputColor.propTypes = { }; export function FormInputColor({ - hasError, required, className, label, helpMessage, testcid, ...props }) { + hasError, required, className, label, helpMessage, testcid, labelTooltip, ...props }) { return ( - + ); @@ -1076,7 +1091,8 @@ FormInputColor.propTypes = { className: CustomPropTypes.className, label: PropTypes.string, helpMessage: PropTypes.string, - testcid: PropTypes.string + testcid: PropTypes.string, + labelTooltip: PropTypes.string }; export function PlainString({ controlProps, value }) { @@ -1193,10 +1209,10 @@ const useStylesKeyboardShortcut = makeStyles(() => ({ } })); -export function FormInputKeyboardShortcut({ hasError, label, className, helpMessage, onChange, ...props }) { +export function FormInputKeyboardShortcut({ hasError, label, className, helpMessage, onChange, labelTooltip, ...props }) { const classes = useStylesKeyboardShortcut(); return ( - + @@ -1208,14 +1224,15 @@ FormInputKeyboardShortcut.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, - onChange: PropTypes.func + onChange: PropTypes.func, + labelTooltip: PropTypes.string }; -export function FormInputQueryThreshold({ hasError, label, className, helpMessage, testcid, onChange, ...props }) { +export function FormInputQueryThreshold({ hasError, label, className, helpMessage, testcid, onChange, labelTooltip, ...props }) { const cid = _.uniqueId('c'); const helpid = `h${cid}`; return ( - + @@ -1227,15 +1244,16 @@ FormInputQueryThreshold.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, - onChange: PropTypes.func + onChange: PropTypes.func, + labelTooltip: PropTypes.string }; -export function FormInputSelectThemes({ hasError, label, className, helpMessage, testcid, onChange, ...props }) { +export function FormInputSelectThemes({ hasError, label, className, helpMessage, testcid, onChange, labelTooltip, ...props }) { const cid = _.uniqueId('c'); const helpid = `h${cid}`; return ( - + ); @@ -1247,7 +1265,8 @@ FormInputSelectThemes.propTypes = { className: CustomPropTypes.className, helpMessage: PropTypes.string, testcid: PropTypes.string, - onChange: PropTypes.func + onChange: PropTypes.func, + labelTooltip: PropTypes.string }; diff --git a/web/pgadmin/static/js/components/KeyboardShortcuts.jsx b/web/pgadmin/static/js/components/KeyboardShortcuts.jsx index daee13320..d158a09e1 100644 --- a/web/pgadmin/static/js/components/KeyboardShortcuts.jsx +++ b/web/pgadmin/static/js/components/KeyboardShortcuts.jsx @@ -27,7 +27,7 @@ const useStyles = makeStyles((theme) => ({ } })); -export default function KeyboardShortcuts({ value, onChange, fields, title }) { +export default function KeyboardShortcuts({ value, onChange, fields }) { const classes = useStyles(); const keyCid = _.uniqueId('c'); const keyhelpid = `h${keyCid}`; @@ -91,7 +91,7 @@ export default function KeyboardShortcuts({ value, onChange, fields, title }) { { onKeyDown: onKeyDown, } - } title={title} /> + }/> ; } else if (element.name == 'shift') { @@ -128,6 +128,5 @@ export default function KeyboardShortcuts({ value, onChange, fields, title }) { KeyboardShortcuts.propTypes = { value: PropTypes.object, onChange: PropTypes.func, - fields: PropTypes.array, - title: PropTypes.string + fields: PropTypes.array }; diff --git a/web/setup.py b/web/setup.py index 7cf7f02a8..412d674e3 100644 --- a/web/setup.py +++ b/web/setup.py @@ -17,6 +17,7 @@ from rich.console import Console from rich.table import Table from rich import box, print import json as jsonlib +from functools import wraps console = Console() app = typer.Typer() @@ -54,9 +55,26 @@ from flask_babel import gettext app = typer.Typer(pretty_exceptions_show_locals=False) +def update_sqlite_path(f): + """ + This function will behave as a decorator which will check if + sqlite path is provided and will update it in config + """ + + @wraps(f) + def wrap(*args, **kwargs): + if kwargs and kwargs.get('sqlite_path') is not None: + # update the sqlite path + config.SQLITE_PATH = kwargs['sqlite_path'] + return f(*args, **kwargs) + + return wrap + + class ManageServers: @app.command() + @update_sqlite_path def dump_servers(output_file: str, user: Optional[str] = None, auth_source: Optional[str] = INTERNAL, sqlite_path: Optional[str] = None, @@ -66,10 +84,6 @@ class ManageServers: # What user? dump_user = user if user is not None else config.DESKTOP_USER - # And the sqlite path - if sqlite_path is not None: - config.SQLITE_PATH = sqlite_path - print('----------') print('Dumping servers with:') print('User:', dump_user) @@ -85,6 +99,7 @@ class ManageServers: print(str(e)) @app.command() + @update_sqlite_path def load_servers(input_file: str, user: Optional[str] = None, auth_source: Optional[str] = INTERNAL, sqlite_path: Optional[str] = None, @@ -96,10 +111,6 @@ class ManageServers: # What user? load_user = user if user is not None else config.DESKTOP_USER - # And the sqlite path - if sqlite_path is not None: - config.SQLITE_PATH = sqlite_path - print('----------') print('Loading servers with:') print('User:', load_user) @@ -136,13 +147,15 @@ class AuthType(str, Enum): class ManageUsers: @app.command() + @update_sqlite_path def add_user(email: str, password: str, role: Annotated[Optional[bool], typer.Option( "--admin/--nonadmin")] = False, active: Annotated[Optional[bool], typer.Option("--active/--inactive")] = True, console: Optional[bool] = True, - json: Optional[bool] = False + json: Optional[bool] = False, + sqlite_path: Optional[str] = None, ): """Add Internal user. """ @@ -157,6 +170,7 @@ class ManageUsers: ManageUsers.create_user(data, console, json) @app.command() + @update_sqlite_path def add_external_user(username: str, auth_source: AuthExtTypes = AuthExtTypes.oauth2, email: Optional[str] = None, @@ -167,7 +181,8 @@ class ManageUsers: typer.Option( "--active/--inactive")] = True, console: Optional[bool] = True, - json: Optional[bool] = False + json: Optional[bool] = False, + sqlite_path: Optional[str] = None, ): """Add external user, other than Internal like Ldap, Ouath2, Kerberos, Webserver. """ @@ -182,14 +197,18 @@ class ManageUsers: ManageUsers.create_user(data, console, json) @app.command() + @update_sqlite_path def delete_user(username: str, auth_source: AuthType = AuthType.internal, auto_confirm: Annotated[Optional[bool], typer.Option( - "--yes")] = False + "--yes")] = False, + sqlite_path: Optional[str] = None, ): """Delete the user. """ + confirm_msg = "Are you sure you want to delete it?" + if auto_confirm or typer.confirm(confirm_msg): app = create_app(config.APP_NAME + '-cli') with app.test_request_context(): @@ -205,6 +224,7 @@ class ManageUsers: print('Something went wrong. ' + str(msg)) @app.command() + @update_sqlite_path def update_user(email: str, password: Optional[str] = None, role: Annotated[Optional[bool], @@ -214,7 +234,8 @@ class ManageUsers: typer.Option("--active/--inactive" )] = None, console: Optional[bool] = True, - json: Optional[bool] = False + json: Optional[bool] = False, + sqlite_path: Optional[str] = None, ): """Update internal user.""" @@ -247,19 +268,22 @@ class ManageUsers: print('Something went wrong. ' + str(msg)) @app.command() + @update_sqlite_path def get_users(username: Optional[str] = None, auth_source: AuthType = None, - json: Optional[bool] = False + json: Optional[bool] = False, + sqlite_path: Optional[str] = None, ): + ManageUsers.get_users_from_db(username, auth_source, True, json) - @app.command() def get_users_from_db(username: Optional[str] = None, auth_source: AuthType = None, console: Optional[bool] = True, - json: Optional[bool] = False + json: Optional[bool] = False, ): """Get user(s) details.""" + app = create_app(config.APP_NAME + '-cli') with app.test_request_context(): if username and auth_source: @@ -288,6 +312,7 @@ class ManageUsers: return users_data @app.command() + @update_sqlite_path def update_external_user(username: str, auth_source: AuthExtTypes = AuthExtTypes.oauth2, email: Optional[str] = None, @@ -298,7 +323,8 @@ class ManageUsers: Optional[bool], typer.Option("--active/--inactive")] = None, console: Optional[bool] = True, - json: Optional[bool] = False + json: Optional[bool] = False, + sqlite_path: Optional[str] = None, ): """Update external users other than Internal like Ldap, Ouath2, Kerberos, Webserver.""" @@ -320,9 +346,11 @@ class ManageUsers: else: status, msg = update_user(uid, data) if status: - _user = ManageUsers.get_users(username=username, - auth_source=auth_source, - console=False) + _user = ManageUsers.get_users_from_db( + username=username, + auth_source=auth_source, + console=False + ) ManageUsers.display_user(_user[0], console, json) else: print('Something went wrong. ' + str(msg)) @@ -399,7 +427,11 @@ class ManagePreferences: return usr.id @app.command() - def get_prefs(json: Optional[bool] = False): + @update_sqlite_path + def get_prefs(json: Optional[bool] = False, + sqlite_path: Optional[str] = None, + ): + return ManagePreferences.fetch_prefs() def fetch_prefs(id: Optional[bool] = None, json: Optional[bool] = False): @@ -447,13 +479,16 @@ class ManagePreferences: print(table) @app.command() + @update_sqlite_path def set_prefs(username, pref_options: Annotated[Optional[List[str]], typer.Argument()] = None, auth_source: AuthType = AuthType.internal, console: Optional[bool] = True, json: Optional[bool] = False, - input_file: Optional[str] = None): + input_file: Optional[str] = None, + sqlite_path: Optional[str] = None, + ): """Set User preferences.""" if input_file: @@ -593,5 +628,9 @@ def setup_db(app: Annotated[str, typer.Argument( run_migration_for_sqlite() -if __name__ == "__main__": +def main(): app() + + +if __name__ == "__main__": + main()