diff --git a/docs/en_US/images/preferences_editor_keyboard_shortcuts.png b/docs/en_US/images/preferences_editor_keyboard_shortcuts.png new file mode 100644 index 00000000000..ffc8ef18db9 Binary files /dev/null and b/docs/en_US/images/preferences_editor_keyboard_shortcuts.png differ diff --git a/docs/en_US/images/preferences_editor_options.png b/docs/en_US/images/preferences_editor_options.png new file mode 100644 index 00000000000..3b479b2140d Binary files /dev/null and b/docs/en_US/images/preferences_editor_options.png differ diff --git a/docs/en_US/images/preferences_editor_sql_formatting.png b/docs/en_US/images/preferences_editor_sql_formatting.png new file mode 100644 index 00000000000..0a219907880 Binary files /dev/null and b/docs/en_US/images/preferences_editor_sql_formatting.png differ diff --git a/docs/en_US/images/preferences_graph_visualiser.png b/docs/en_US/images/preferences_graph_visualiser.png index e6cecb4f3a0..ba543553def 100644 Binary files a/docs/en_US/images/preferences_graph_visualiser.png and b/docs/en_US/images/preferences_graph_visualiser.png differ diff --git a/docs/en_US/images/preferences_sql_auto_completion.png b/docs/en_US/images/preferences_sql_auto_completion.png index a666afff212..237b4aa4368 100644 Binary files a/docs/en_US/images/preferences_sql_auto_completion.png and b/docs/en_US/images/preferences_sql_auto_completion.png differ diff --git a/docs/en_US/images/preferences_sql_csv_output.png b/docs/en_US/images/preferences_sql_csv_output.png index e779467cf31..aac1fa3651f 100644 Binary files a/docs/en_US/images/preferences_sql_csv_output.png and b/docs/en_US/images/preferences_sql_csv_output.png differ diff --git a/docs/en_US/images/preferences_sql_display.png b/docs/en_US/images/preferences_sql_display.png index caa2aa5297c..905489bc294 100644 Binary files a/docs/en_US/images/preferences_sql_display.png and b/docs/en_US/images/preferences_sql_display.png differ diff --git a/docs/en_US/images/preferences_sql_editor.png b/docs/en_US/images/preferences_sql_editor.png deleted file mode 100644 index e417657e0b7..00000000000 Binary files a/docs/en_US/images/preferences_sql_editor.png and /dev/null differ diff --git a/docs/en_US/images/preferences_sql_explain.png b/docs/en_US/images/preferences_sql_explain.png index c84f3d39504..07b2544483d 100644 Binary files a/docs/en_US/images/preferences_sql_explain.png and b/docs/en_US/images/preferences_sql_explain.png differ diff --git a/docs/en_US/images/preferences_sql_formatting.png b/docs/en_US/images/preferences_sql_formatting.png deleted file mode 100644 index 79a37eb4486..00000000000 Binary files a/docs/en_US/images/preferences_sql_formatting.png and /dev/null differ diff --git a/docs/en_US/images/preferences_sql_keyboard_shortcuts.png b/docs/en_US/images/preferences_sql_keyboard_shortcuts.png index 9f03a44f8f6..cce323179d9 100644 Binary files a/docs/en_US/images/preferences_sql_keyboard_shortcuts.png and b/docs/en_US/images/preferences_sql_keyboard_shortcuts.png differ diff --git a/docs/en_US/images/preferences_sql_options.png b/docs/en_US/images/preferences_sql_options.png index 3a7f222a911..da63ba1efe4 100644 Binary files a/docs/en_US/images/preferences_sql_options.png and b/docs/en_US/images/preferences_sql_options.png differ diff --git a/docs/en_US/images/preferences_sql_results_grid.png b/docs/en_US/images/preferences_sql_results_grid.png index af538e6fe62..2143452b16f 100644 Binary files a/docs/en_US/images/preferences_sql_results_grid.png and b/docs/en_US/images/preferences_sql_results_grid.png differ diff --git a/docs/en_US/preferences.rst b/docs/en_US/preferences.rst index 038953430ae..07e5bc4b3f7 100644 --- a/docs/en_US/preferences.rst +++ b/docs/en_US/preferences.rst @@ -262,6 +262,90 @@ Use the fields on the *Options* panel to manage ERD preferences. It allows to set the limit on the depth level pgAdmin should traverse to find the relations. Use -1 to set no limit. + +The Editor Node +***************** + +Expand the *Editor* node to access panels that allow you to specify your preferences +for the editor across pgAdmin 4 + +.. image:: images/preferences_editor_keyboard_shortcuts.png + :alt: Preferences editor keyboard shortcuts section + :align: center + +Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the +editor. + +.. image:: images/preferences_editor_options.png + :alt: Preferences editor options section + :align: center + +Use the fields on the *Options* panel to manage editor preferences. + +* Use the *Auto-indent new line?* switch to specify whether the editor will + automatically indent new lines. When set to *True*, the editor will indent + new lines according to the indentation of the previous line. + +* When the *Brace matching?* switch is set to *True*, the editor will highlight + pairs of matched braces. + +* When the *Code folding?* switch is set to *False*, the editor will disable + code folding. Disabling will improve editor performance with large files. + +* Use the *Font family* field to be used for all SQL editors. The specified + font should already be installed on your system. If the font is not found, + the editor will fall back to the default font, Source Code Pro. + +* When the *Font ligatures?* switch is set to *True*, ligatures will be + enabled in SQL text boxes and editors provided the configured font family + supports them. + +* Use the *Font size* field to specify the font size that will be used in text + boxes and editors. + +* When the *Highlight selection matches?* switch is set to *True*, the editor will + highlight matched selected text. + +* When the *Insert bracket pairs?* switch is set to *True*, the editor will + automatically insert paired brackets. + +* When the *Line wrapping* switch is set to *True*, the editor will implement + line-wrapping behavior. + +* When the *Plain text mode?* switch is set to *True*, the editor mode will be + changed to text/plain. Keyword highlighting and code folding will be disabled. + This will improve editor performance with large files. + +.. image:: images/preferences_editor_sql_formatting.png + :alt: Preferences SQL Formatting section + :align: center + +Use the fields on the *SQL formatting* panel to specify your preferences for +reformatting of SQL. + +* Use the *Data type case* option to specify whether to change data types + into upper, lower, or preserve case. +* Use the *Expression width* option to specify maximum number of characters + in parenthesized expressions to be kept on single line. +* Use the *Function case* option to specify whether to change function + names into upper, lower, or preserve case. +* Use the *Identifier case* option to specify whether to change identifiers + (object names) into upper, lower, or capitalized case. +* Use the *Keyword case* option to specify whether to change keywords into + upper, lower, or preserve case. +* Use *Lines between queries* to specify how many empty lines to leave + between SQL statements. If set to zero it puts no new line. +* Use *Logical operator new line* to specify newline placement before or + after logical operators (AND, OR, XOR). +* Use *New line before semicolon?* to specify whether to place query + separator (;) on a separate line. +* Use the *Spaces around operators?* option to specify whether or not to include + spaces on either side of operators. +* Use the *Tab size* option to specify the number of spaces per tab or indent. +* Use the *Use spaces?* option to select whether to use spaces or tabs when + indenting. + + The Graphs Node *************** @@ -427,46 +511,6 @@ Tool display. * When the *Show query success notification?* switch is set to *True*, the Query Tool will show notifications on successful query execution. -.. image:: images/preferences_sql_editor.png - :alt: Preferences sqleditor editor settings - :align: center - -Use the fields on the *Editor* panel to change settings of the query editor. - -* Use the *Auto-indent new line?* switch to specify whether the editor will - automatically indent new lines. When set to *True*, the editor will indent - new lines according to the indentation of the previous line. - -* When the *Brace matching?* switch is set to *True*, the editor will highlight - pairs of matched braces. - -* When the *Code folding?* switch is set to *False*, the editor will disable - code folding. Disabling will improve editor performance with large files. - -* Use the *Font family* field to be used for all SQL editors. The specified - font should already be installed on your system. If the font is not found, - the editor will fall back to the default font, Source Code Pro. - -* When the *Font ligatures?* switch is set to *True*, ligatures will be - enabled in SQL text boxes and editors provided the configured font family - supports them. - -* Use the *Font size* field to specify the font size that will be used in text - boxes and editors. - -* When the *Highlight selection matches?* switch is set to *True*, the editor will - highlight matched selected text. - -* When the *Insert bracket pairs?* switch is set to *True*, the editor will - automatically insert paired brackets. - -* When the *Line wrapping* switch is set to *True*, the editor will implement - line-wrapping behavior. - -* When the *Plain text mode?* switch is set to *True*, the editor mode will be - changed to text/plain. Keyword highlighting and code folding will be disabled. - This will improve editor performance with large files. - .. image:: images/preferences_sql_explain.png :alt: Preferences sqleditor explain options :align: center @@ -592,34 +636,6 @@ preferences for copied data. Use the fields on the *Keyboard shortcuts* panel to configure shortcuts for the Query Tool window navigation. -.. image:: images/preferences_sql_formatting.png - :alt: Preferences SQL Formatting section - :align: center - -Use the fields on the *SQL formatting* panel to specify your preferences for -reformatting of SQL. - -* Use the *Data type case* option to specify whether to change data types - into upper, lower, or preserve case. -* Use the *Expression width* option to specify maximum number of characters - in parenthesized expressions to be kept on single line. -* Use the *Function case* option to specify whether to change function - names into upper, lower, or preserve case. -* Use the *Identifier case* option to specify whether to change identifiers - (object names) into upper, lower, or capitalized case. -* Use the *Keyword case* option to specify whether to change keywords into - upper, lower, or preserve case. -* Use *Lines between queries* to specify how many empty lines to leave - between SQL statements. If set to zero it puts no new line. -* Use *Logical operator new line* to specify newline placement before or - after logical operators (AND, OR, XOR). -* Use *New line before semicolon?* to specify whether to place query - separator (;) on a separate line. -* Use the *Spaces around operators?* option to specify whether or not to include - spaces on either side of operators. -* Use the *Tab size* option to specify the number of spaces per tab or indent. -* Use the *Use spaces?* option to select whether to use spaces or tabs when - indenting. The Schema Diff Node ******************** diff --git a/web/migrations/versions/e6ed5dac37c2_.py b/web/migrations/versions/e6ed5dac37c2_.py new file mode 100644 index 00000000000..48e962cb625 --- /dev/null +++ b/web/migrations/versions/e6ed5dac37c2_.py @@ -0,0 +1,107 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2025, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""Migrate the user saved preferences from the 'sqleditor' module to +the 'editor' module. + +Revision ID: e6ed5dac37c2 +Revises: e24b1c4def17 +Create Date: 2025-07-15 12:27:48.780562 + +""" +from alembic import op +from sqlalchemy.orm.session import Session +from pgadmin.model import Preferences, ModulePreference, PreferenceCategory,\ + UserPreference +from pgadmin.browser import register_editor_preferences + + +# revision identifiers, used by Alembic. +revision = 'e6ed5dac37c2' +down_revision = 'e24b1c4def17' +branch_labels = None +depends_on = None + + +class Migration: + """ + This is a placeholder class for registering editor preferences + """ + def __init__(self, value=None): + self.editor_preference = value + + +def upgrade(): + migration_obj = Migration() + register_editor_preferences(migration_obj, migration_gettext=lambda x: x) + + session = Session(bind=op.get_bind()) + pref_categories = ['keyboard_shortcuts', 'editor', 'Editor'] + new_categories = ['keyboard_shortcuts', 'sql_formatting', 'options'] + prefs = ['find', 'replace', 'goto_line_col', + 'comment', 'format_sql', 'plain_editor_mode', + 'code_folding', 'wrap_code', 'insert_pair_brackets', + 'highlight_selection_matches', 'brace_matching', 'sql_font_size', + 'sql_font_ligatures', 'sql_font_family', 'indent_new_line', + 'keyword_case', 'identifier_case', 'function_case', + 'data_type_case', 'spaces_around_operators', 'tab_size', + 'use_spaces', 'expression_width', 'logical_operator_new_line', + 'lines_between_queries', 'new_line_before_semicolon'] + category_ids = [] + new_ids = [] + pref_map = {} + + category_data = session.query(ModulePreference, PreferenceCategory,).join( + PreferenceCategory).filter( + ModulePreference.name == 'sqleditor', + PreferenceCategory.name.in_(pref_categories)).all() + + for module_data, pref_cat in category_data: + category_ids.append(pref_cat.id) + + new_data = session.query(ModulePreference, PreferenceCategory).join( + PreferenceCategory).filter( + ModulePreference.name == 'editor', PreferenceCategory.name.in_( + new_categories)).all() + + for module_data, pref_cat in new_data: + new_ids.append(pref_cat.id) + + prefs_data = session.query(Preferences).filter(Preferences.cid.in_( + category_ids), Preferences.name.in_(prefs)).all() + + new_prefs_data = session.query(Preferences).filter(Preferences.cid.in_( + new_ids), Preferences.name.in_(prefs)).all() + + for pref in prefs_data: + for new_pref in new_prefs_data: + if pref.name == new_pref.name: + pref_map[pref.id] = new_pref.id + + for key, val in pref_map.items(): + record_to_update = session.query(UserPreference).filter_by( + pid=key).first() + if record_to_update: + record_to_update.pid = val + + # Delete the old preferences and categories + session.query(Preferences).filter(Preferences.name.in_(prefs), + Preferences.cid.in_(category_ids) + ).delete(synchronize_session=False) + + session.query(PreferenceCategory).filter( + PreferenceCategory.name.in_(['editor', 'Editor'])).delete( + synchronize_session=False) + + session.commit() + + +def downgrade(): + # pgAdmin only upgrades, downgrade not implemented. + pass diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py index 95522d90cf6..3ae63b0a886 100644 --- a/web/pgadmin/browser/__init__.py +++ b/web/pgadmin/browser/__init__.py @@ -52,6 +52,8 @@ from pgadmin.utils.preferences import Preferences from pgadmin.browser.register_browser_preferences import \ register_browser_preferences +from pgadmin.browser.register_editor_preferences import \ + register_editor_preferences from pgadmin.utils.master_password import validate_master_password, \ set_masterpass_check_text, cleanup_master_password, get_crypt_key, \ set_crypt_key, process_masterpass_disabled, \ @@ -87,6 +89,7 @@ class BrowserModule(PgAdminModule): def register_preferences(self): register_browser_preferences(self) + register_editor_preferences(self) def get_exposed_url_endpoints(self): """ diff --git a/web/pgadmin/browser/register_editor_preferences.py b/web/pgadmin/browser/register_editor_preferences.py new file mode 100644 index 00000000000..524fee53310 --- /dev/null +++ b/web/pgadmin/browser/register_editor_preferences.py @@ -0,0 +1,357 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2025, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +from flask_babel import gettext +from pgadmin.utils.preferences import Preferences +from pgadmin.utils.constants import PREF_LABEL_OPTIONS,\ + PREF_LABEL_KEYBOARD_SHORTCUTS, PREF_LABEL_SQL_FORMATTING +from pgadmin.utils import SHORTCUT_FIELDS as shortcut_fields + +UPPER_CASE_STR = gettext('Upper case') +LOWER_CASE_STR = gettext('Lower case') +PRESERVE_STR = gettext('Preserve') + + +def register_editor_preferences(self, migration_gettext=None): + """ + Registers the editor preferences + """ + # migration_getttext is used once in case of migration + # In that case gettext will be used from migration_gettext + # instead of flask_babel gettext. + from flask_babel import gettext + if migration_gettext is not None: + gettext = migration_gettext + + self.editor_preference = Preferences( + 'editor', gettext('Editor') + ) + + self.editor_preference.register( + 'keyboard_shortcuts', + 'find', + gettext('Find'), + 'keyboardshortcut', + { + 'alt': False, + 'shift': False, + 'control': True, + 'ctrl_is_meta': True, + 'key': { + 'key_code': 70, + 'char': 'f' + } + }, + category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, + fields=shortcut_fields + ) + + self.editor_preference.register( + 'keyboard_shortcuts', + 'replace', + gettext('Replace'), + 'keyboardshortcut', + { + 'alt': True, + 'shift': False, + 'control': True, + 'ctrl_is_meta': True, + 'key': { + 'key_code': 70, + 'char': 'f' + } + }, + category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, + fields=shortcut_fields + ) + + self.editor_preference.register( + 'keyboard_shortcuts', + 'goto_line_col', + gettext('Go to line/column'), + 'keyboardshortcut', + { + 'alt': False, + 'shift': False, + 'control': True, + 'ctrl_is_meta': True, + 'key': { + 'key_code': 76, + 'char': 'l' + } + }, + category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, + fields=shortcut_fields + ) + + self.editor_preference.register( + 'keyboard_shortcuts', + 'comment', + gettext('Toggle comment'), + 'keyboardshortcut', + { + 'alt': False, + 'shift': False, + 'control': True, + 'ctrl_is_meta': True, + 'key': { + 'key_code': 191, + 'char': '/' + } + }, + category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, + fields=shortcut_fields + ) + + self.editor_preference.register( + 'keyboard_shortcuts', + 'format_sql', + gettext('Format SQL'), + 'keyboardshortcut', + { + 'alt': False, + 'shift': False, + 'control': True, + 'ctrl_is_meta': True, + 'key': { + 'key_code': 75, + 'char': 'k' + } + }, + category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, + fields=shortcut_fields + ) + + self.editor_preference.register( + 'options', 'plain_editor_mode', + gettext("Plain text mode?"), 'boolean', False, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'When set to True, keywords won\'t be highlighted and code ' + 'folding will be disabled. Plain text mode will improve editor ' + 'performance with large files.' + ) + ) + + self.editor_preference.register( + 'options', 'code_folding', + gettext("Code folding?"), 'boolean', True, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Enable or disable code folding. In plain text mode, this will ' + 'have no effect as code folding is always disabled in that mode. ' + 'Disabling will improve editor performance with large files.' + ) + ) + + self.editor_preference.register( + 'options', 'wrap_code', + gettext("Line wrapping?"), 'boolean', False, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specifies whether or not to wrap SQL code in the editor.' + ) + ) + + self.editor_preference.register( + 'options', 'insert_pair_brackets', + gettext("Insert bracket pairs?"), 'boolean', True, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specifies whether or not to insert paired brackets in the ' + 'editor.' + ) + ) + + self.editor_preference.register( + 'options', 'highlight_selection_matches', + gettext("Highlight selection matches?"), 'boolean', True, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specifies whether or not to highlight matched selected text ' + 'in the editor.' + ) + ) + + self.editor_preference.register( + 'options', 'brace_matching', + gettext("Brace matching?"), 'boolean', True, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specifies whether or not to highlight matched braces ' + 'in the editor.' + ) + ) + + self.editor_preference.register( + 'options', 'sql_font_size', + gettext("Font size"), 'numeric', '1', + min_val=0.1, + max_val=10, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'The font size to use for the SQL text boxes and editors. ' + 'The value specified is in "em" units, in which 1 is the ' + 'default relative font size. For example, to increase the ' + 'font size by 20 percent use a value of 1.2, or to reduce ' + 'by 20 percent, use a value of 0.8. Minimum 0.1, maximum 10.' + ) + ) + + self.editor_preference.register( + 'options', 'sql_font_ligatures', + gettext("Font ligatures?"), 'boolean', + False, category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'If set to true, ligatures will be enabled in SQL text boxes ' + 'and editors provided the configured font family supports them.' + ) + ) + + self.editor_preference.register( + 'options', 'sql_font_family', + gettext("Font family"), 'text', 'Source Code Pro', + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specify the font family to be used for all SQL editors. ' + 'The specified font should already be installed on your system. ' + 'If the font is not found, the editor will fall back to the ' + 'default font, Source Code Pro.' + ) + ) + + self.editor_preference.register( + 'options', 'indent_new_line', + gettext("Auto-indent new line?"), 'boolean', True, + category_label=PREF_LABEL_OPTIONS, + help_str=gettext( + 'Specifies whether the newly added line using enter key should ' + 'be auto-indented or not' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'keyword_case', + gettext("Keyword case"), 'radioModern', 'upper', + options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, + {'label': LOWER_CASE_STR, 'value': 'lower'}, + {'label': PRESERVE_STR, 'value': 'preserve'}], + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Convert keywords to upper, lower, or preserve casing.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'identifier_case', + gettext("Identifier case"), 'radioModern', 'upper', + options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, + {'label': LOWER_CASE_STR, 'value': 'lower'}, + {'label': PRESERVE_STR, 'value': 'preserve'}], + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Convert identifiers to upper, lower, or preserve casing.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'function_case', + gettext("Function case"), 'radioModern', 'upper', + options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, + {'label': LOWER_CASE_STR, 'value': 'lower'}, + {'label': PRESERVE_STR, 'value': 'preserve'}], + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Convert function names to upper, lower, or preserve casing.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'data_type_case', + gettext("Data type case"), 'radioModern', 'upper', + options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, + {'label': LOWER_CASE_STR, 'value': 'lower'}, + {'label': PRESERVE_STR, 'value': 'preserve'}], + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Convert data types to upper, lower, or preserve casing.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'spaces_around_operators', + gettext("Spaces around operators?"), 'boolean', True, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext('If set to True, spaces are used around all ' + 'operators.') + ) + + self.editor_preference.register( + 'sql_formatting', 'tab_size', + gettext("Tab size"), 'integer', 4, + min_val=2, + max_val=8, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'The number of spaces per tab. Minimum 2, maximum 8.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'use_spaces', + gettext("Use spaces?"), 'boolean', False, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Specifies whether or not to insert spaces instead of tabs ' + 'when the tab key or auto-indent are used.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'expression_width', + gettext("Expression Width"), 'integer', 50, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'maximum number of characters in parenthesized expressions to be ' + 'kept on single line.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'logical_operator_new_line', + gettext("Logical operator new line"), 'radioModern', 'before', + options=[{'label': gettext('Before'), 'value': 'before'}, + {'label': gettext('After'), 'value': 'after'}], + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Decides newline placement before or after logical operators ' + '(AND, OR, XOR).' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'lines_between_queries', + gettext("Lines between queries"), 'integer', 1, + min_val=0, + max_val=5, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Decides how many empty lines to leave between SQL statements. ' + 'If zero it puts no new line.' + ) + ) + + self.editor_preference.register( + 'sql_formatting', 'new_line_before_semicolon', + gettext("New line before semicolon?"), 'boolean', False, + category_label=PREF_LABEL_SQL_FORMATTING, + help_str=gettext( + 'Whether to place query separator (;) on a separate line.' + ) + ) diff --git a/web/pgadmin/model/__init__.py b/web/pgadmin/model/__init__.py index 7b3316bff6d..323412fdd3b 100644 --- a/web/pgadmin/model/__init__.py +++ b/web/pgadmin/model/__init__.py @@ -33,7 +33,7 @@ # ########################################################################## -SCHEMA_VERSION = 46 +SCHEMA_VERSION = 47 ########################################################################## # diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx b/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx index 6a0e50f87f5..1041251c60f 100644 --- a/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx +++ b/web/pgadmin/static/js/components/ReactCodeMirror/components/Editor.jsx @@ -285,14 +285,15 @@ export default function Editor({ useEffect(() => { if (!checkIsMounted()) return; let pref = preferencesStore.getPreferencesForModule('sqleditor'); + let editorPref = preferencesStore.getPreferencesForModule('editor'); let newConfigExtn = []; - const fontSize = calcFontSize(pref.sql_font_size); + const fontSize = calcFontSize(editorPref.sql_font_size); newConfigExtn.push(EditorView.theme({ '& .cm-scroller .cm-content': { fontSize: fontSize, - fontVariantLigatures: pref.sql_font_ligatures ? 'normal' : 'none', - fontFamily: `${pref.sql_font_family}, ${theme.typography.fontFamilySourceCode}`, + fontVariantLigatures: editorPref.sql_font_ligatures ? 'normal' : 'none', + fontFamily: `${editorPref.sql_font_family}, ${theme.typography.fontFamilySourceCode}`, }, '.cm-gutters': { fontSize: fontSize, @@ -336,11 +337,11 @@ export default function Editor({ } newConfigExtn.push( - EditorState.tabSize.of(pref.tab_size), + EditorState.tabSize.of(editorPref.tab_size), ); - if (pref.use_spaces) { + if (editorPref.use_spaces) { newConfigExtn.push( - indentUnit.of(' '.repeat(pref.tab_size)), + indentUnit.of(' '.repeat(editorPref.tab_size)), ); } else { newConfigExtn.push( @@ -348,34 +349,34 @@ export default function Editor({ ); } - if(pref.indent_new_line) { + if(editorPref.indent_new_line) { newConfigExtn.push(indentNewLine.of(true)); } else { newConfigExtn.push(indentNewLine.of(false)); } - if (pref.wrap_code) { + if (editorPref.wrap_code) { newConfigExtn.push( EditorView.lineWrapping ); } - if (pref.insert_pair_brackets) { + if (editorPref.insert_pair_brackets) { newConfigExtn.push(closeBrackets()); } - if (pref.highlight_selection_matches){ + if (editorPref.highlight_selection_matches){ newConfigExtn.push(highlightSelectionMatches()); } - if (pref.brace_matching) { + if (editorPref.brace_matching) { newConfigExtn.push(bracketMatching()); } if (pref.underline_query_cursor){ newConfigExtn.push(currentQueryHighlighterExtn()); } - if(!pref.plain_editor_mode) { + if(!editorPref.plain_editor_mode) { // lang override if(language == 'json') { newConfigExtn.push(json()); @@ -384,7 +385,7 @@ export default function Editor({ } } - if(pref.code_folding && finalOptions.foldGutter) { + if(editorPref.code_folding && finalOptions.foldGutter) { newConfigExtn.push(foldGutter({ markerDOM: (open)=>{ let icon = document.createElement('span'); @@ -399,7 +400,7 @@ export default function Editor({ } // add fold service conditionally - if(!pref.plain_editor_mode && pref.code_folding && language == 'pgsql') { + if(!editorPref.plain_editor_mode && editorPref.code_folding && language == 'pgsql') { newConfigExtn.push(plpgsqlFoldService); } diff --git a/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx b/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx index 079767fe245..63979a006dc 100644 --- a/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx +++ b/web/pgadmin/static/js/components/ReactCodeMirror/index.jsx @@ -69,6 +69,7 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu const [showGoto, setShowGoto] = useState(false); const [showCopy, setShowCopy] = useState(false); const preferences = usePreferences().getPreferencesForModule('sqleditor'); + const editorPrefs = usePreferences().getPreferencesForModule('editor'); const formatSQL = (view)=>{ let selection = true, sql = view.getSelection(); @@ -78,17 +79,17 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu */ let formatPrefs = { language: 'postgresql', - keywordCase: preferences.keyword_case === 'capitalize' ? 'preserve' : preferences.keyword_case, - identifierCase: preferences.identifier_case === 'capitalize' ? 'preserve' : preferences.identifier_case, - dataTypeCase: preferences.data_type_case, - functionCase: preferences.function_case, - logicalOperatorNewline: preferences.logical_operator_new_line, - expressionWidth: preferences.expression_width, - linesBetweenQueries: preferences.lines_between_queries, - tabWidth: preferences.tab_size, - useTabs: !preferences.use_spaces, - denseOperators: !preferences.spaces_around_operators, - newlineBeforeSemicolon: preferences.new_line_before_semicolon + keywordCase: editorPrefs.keyword_case === 'capitalize' ? 'preserve' : editorPrefs.keyword_case, + identifierCase: editorPrefs.identifier_case === 'capitalize' ? 'preserve' : editorPrefs.identifier_case, + dataTypeCase: editorPrefs.data_type_case, + functionCase: editorPrefs.function_case, + logicalOperatorNewline: editorPrefs.logical_operator_new_line, + expressionWidth: editorPrefs.expression_width, + linesBetweenQueries: editorPrefs.lines_between_queries, + tabWidth: editorPrefs.tab_size, + useTabs: !editorPrefs.use_spaces, + denseOperators: !editorPrefs.spaces_around_operators, + newlineBeforeSemicolon: editorPrefs.new_line_before_semicolon }; if(sql == '') { sql = view.getValue(); @@ -103,31 +104,31 @@ export default function CodeMirror({className, currEditor, showCopyBtn=false, cu }; const finalCustomKeyMap = useMemo(()=>[{ - key: toCodeMirrorKey(preferences.find), run: () => { + key: toCodeMirrorKey(editorPrefs.find), run: () => { setShowFind(prevVal => [true, false, !prevVal[2]]); }, preventDefault: true, stopPropagation: true, }, { - key: toCodeMirrorKey(preferences.replace), run: () => { + key: toCodeMirrorKey(editorPrefs.replace), run: () => { setShowFind(prevVal => [true, true, !prevVal[2]]); }, preventDefault: true, stopPropagation: true, }, { - key: toCodeMirrorKey(preferences.goto_line_col), run: () => { + key: toCodeMirrorKey(editorPrefs.goto_line_col), run: () => { setShowGoto(true); }, preventDefault: true, stopPropagation: true, }, { - key: toCodeMirrorKey(preferences.comment), run: () => { + key: toCodeMirrorKey(editorPrefs.comment), run: () => { editor.current?.execCommand('toggleComment'); }, preventDefault: true, stopPropagation: true, },{ - key: toCodeMirrorKey(preferences.format_sql), run: formatSQL, + key: toCodeMirrorKey(editorPrefs.format_sql), run: formatSQL, preventDefault: true, stopPropagation: true, },{ diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx index 055bc2d55e0..4ba305ade3e 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx @@ -123,6 +123,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN sqleditor: {...preferencesStore.getPreferencesForModule('sqleditor'), ...FIXED_PREF}, graphs: preferencesStore.getPreferencesForModule('graphs'), misc: preferencesStore.getPreferencesForModule('misc'), + editor: preferencesStore.getPreferencesForModule('editor'), }, is_new_tab: window.location == window.parent?.location, is_visible: true, @@ -467,6 +468,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN sqleditor: {...state.getPreferencesForModule('sqleditor'), ...FIXED_PREF}, graphs: state.getPreferencesForModule('graphs'), misc: state.getPreferencesForModule('misc'), + editor: state.getPreferencesForModule('editor'), }}); } ), []); diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx index 9147892db1a..33095f01e49 100644 --- a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx +++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx @@ -84,6 +84,7 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT const filterMenuRef = React.useRef(null); const queryToolPref = queryToolCtx.preferences.sqleditor; + const editorPref = queryToolCtx.preferences.editor; const setDisableButton = useCallback((name, disable=true)=>{ setButtonsDisabled((prev)=>({...prev, [name]: disable})); }, []); @@ -595,25 +596,25 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros, onAddT onClose={onMenuClose} label={gettext('Edit Menu')} > - {eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, false);}}>{gettext('Find')} - {eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, true);}}>{gettext('Replace')} - {executeCmd('gotoLineCol');}}>{gettext('Go to Line/Column')} {executeCmd('indentMore');}}>{gettext('Indent Selection')} {executeCmd('indentLess');}}>{gettext('Unindent Selection')} - {executeCmd('toggleComment');}}>{gettext('Toggle Comment')} {gettext('Toggle Case Of Selected Text')} {gettext('Clear Query')} - {executeCmd('formatSql');}}>{gettext('Format SQL')} + {executeCmd('formatSql');}}>{gettext('Format SQL')} { - let key = {}, gotolinecol = queryToolCtx.preferences.sqleditor.goto_line_col, - formatSql = queryToolCtx.preferences.sqleditor.format_sql; + let key = {}, gotolinecol = queryToolCtx.preferences.editor.goto_line_col, + formatSql = queryToolCtx.preferences.editor.format_sql; switch(cmd) { case 'gotoLineCol': key = createKeyObjectFromShortcut(gotolinecol); @@ -326,8 +326,8 @@ export default function Query({onTextSelect, setQtStatePartial}) { }); const unregisterFindReplace = eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, (replace=false)=>{ - let findShortcut = queryToolCtx.preferences.sqleditor.find; - let replaceShortcut = queryToolCtx.preferences.sqleditor.replace; + let findShortcut = queryToolCtx.preferences.editor.find; + let replaceShortcut = queryToolCtx.preferences.editor.replace; let key ={}; editor.current?.focus(); if (!replace) { diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py index df0c1893b87..149ba273054 100644 --- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py +++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py @@ -11,15 +11,11 @@ from flask_babel import gettext from pgadmin.utils.constants import PREF_LABEL_DISPLAY,\ PREF_LABEL_KEYBOARD_SHORTCUTS, PREF_LABEL_EXPLAIN, PREF_LABEL_OPTIONS,\ - PREF_LABEL_EDITOR, PREF_LABEL_CSV_TXT, PREF_LABEL_RESULTS_GRID,\ - PREF_LABEL_SQL_FORMATTING, PREF_LABEL_GRAPH_VISUALISER + PREF_LABEL_CSV_TXT, PREF_LABEL_RESULTS_GRID,\ + PREF_LABEL_GRAPH_VISUALISER from pgadmin.utils import SHORTCUT_FIELDS as shortcut_fields from config import DATA_RESULT_ROWS_PER_PAGE -UPPER_CASE_STR = gettext('Upper case') -LOWER_CASE_STR = gettext('Lower case') -PRESERVE_STR = gettext('Preserve') - def register_query_tool_preferences(self): self.explain_verbose = self.preference.register( @@ -188,67 +184,6 @@ def register_query_tool_preferences(self): ) ) - self.preference.register( - 'Editor', 'plain_editor_mode', - gettext("Plain text mode?"), 'boolean', False, - category_label=PREF_LABEL_EDITOR, - help_str=gettext( - 'When set to True, keywords won\'t be highlighted and code ' - 'folding will be disabled. Plain text mode will improve editor ' - 'performance with large files.' - ) - ) - - self.preference.register( - 'Editor', 'code_folding', - gettext("Code folding?"), 'boolean', True, - category_label=PREF_LABEL_EDITOR, - help_str=gettext( - 'Enable or disable code folding. In plain text mode, this will ' - 'have no effect as code folding is always disabled in that mode. ' - 'Disabling will improve editor performance with large files.' - ) - ) - - self.wrap_code = self.preference.register( - 'Editor', 'wrap_code', - gettext("Line wrapping?"), 'boolean', False, - category_label=PREF_LABEL_OPTIONS, - help_str=gettext( - 'Specifies whether or not to wrap SQL code in the editor.' - ) - ) - - self.insert_pair_brackets = self.preference.register( - 'Editor', 'insert_pair_brackets', - gettext("Insert bracket pairs?"), 'boolean', True, - category_label=PREF_LABEL_OPTIONS, - help_str=gettext( - 'Specifies whether or not to insert paired brackets in the ' - 'editor.' - ) - ) - - self.highlight_selection_matches = self.preference.register( - 'Editor', 'highlight_selection_matches', - gettext("Highlight selection matches?"), 'boolean', True, - category_label=PREF_LABEL_OPTIONS, - help_str=gettext( - 'Specifies whether or not to highlight matched selected text ' - 'in the editor.' - ) - ) - - self.brace_matching = self.preference.register( - 'Editor', 'brace_matching', - gettext("Brace matching?"), 'boolean', True, - category_label=PREF_LABEL_OPTIONS, - help_str=gettext( - 'Specifies whether or not to highlight matched braces ' - 'in the editor.' - ) - ) - self.csv_quoting = self.preference.register( 'CSV_output', 'csv_quoting', gettext("CSV quoting"), 'options', 'strings', @@ -394,43 +329,6 @@ def register_query_tool_preferences(self): ' data output cell.') ) - self.sql_font_size = self.preference.register( - 'Editor', 'sql_font_size', - gettext("Font size"), 'numeric', '1', - min_val=0.1, - max_val=10, - category_label=PREF_LABEL_DISPLAY, - help_str=gettext( - 'The font size to use for the SQL text boxes and editors. ' - 'The value specified is in "em" units, in which 1 is the ' - 'default relative font size. For example, to increase the ' - 'font size by 20 percent use a value of 1.2, or to reduce ' - 'by 20 percent, use a value of 0.8. Minimum 0.1, maximum 10.' - ) - ) - - self.sql_font_ligatures = self.preference.register( - 'Editor', 'sql_font_ligatures', - gettext("Font ligatures?"), 'boolean', - False, category_label=PREF_LABEL_DISPLAY, - help_str=gettext( - 'If set to true, ligatures will be enabled in SQL text boxes ' - 'and editors provided the configured font family supports them.' - ) - ) - - self.sql_font_family = self.preference.register( - 'Editor', 'sql_font_family', - gettext("Font family"), 'text', 'Source Code Pro', - category_label=PREF_LABEL_DISPLAY, - help_str=gettext( - 'Specify the font family to be used for all SQL editors. ' - 'The specified font should already be installed on your system. ' - 'If the font is not found, the editor will fall back to the ' - 'default font, Source Code Pro.' - ) - ) - self.display_connection_status = self.preference.register( 'display', 'connection_status', gettext("Connection status"), 'boolean', True, @@ -884,101 +782,6 @@ def register_query_tool_preferences(self): fields=shortcut_fields ) - self.preference.register( - 'keyboard_shortcuts', - 'find', - gettext('Find'), - 'keyboardshortcut', - { - 'alt': False, - 'shift': False, - 'control': True, - 'ctrl_is_meta': True, - 'key': { - 'key_code': 70, - 'char': 'f' - } - }, - category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, - fields=shortcut_fields - ) - - self.preference.register( - 'keyboard_shortcuts', - 'replace', - gettext('Replace'), - 'keyboardshortcut', - { - 'alt': True, - 'shift': False, - 'control': True, - 'ctrl_is_meta': True, - 'key': { - 'key_code': 70, - 'char': 'f' - } - }, - category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, - fields=shortcut_fields - ) - - self.preference.register( - 'keyboard_shortcuts', - 'goto_line_col', - gettext('Go to line/column'), - 'keyboardshortcut', - { - 'alt': False, - 'shift': False, - 'control': True, - 'ctrl_is_meta': True, - 'key': { - 'key_code': 76, - 'char': 'l' - } - }, - category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, - fields=shortcut_fields - ) - - self.preference.register( - 'keyboard_shortcuts', - 'comment', - gettext('Toggle comment'), - 'keyboardshortcut', - { - 'alt': False, - 'shift': False, - 'control': True, - 'ctrl_is_meta': True, - 'key': { - 'key_code': 191, - 'char': '/' - } - }, - category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, - fields=shortcut_fields - ) - - self.preference.register( - 'keyboard_shortcuts', - 'format_sql', - gettext('Format SQL'), - 'keyboardshortcut', - { - 'alt': False, - 'shift': False, - 'control': True, - 'ctrl_is_meta': True, - 'key': { - 'key_code': 75, - 'char': 'k' - } - }, - category_label=PREF_LABEL_KEYBOARD_SHORTCUTS, - fields=shortcut_fields - ) - self.preference.register( 'keyboard_shortcuts', 'auto_complete', @@ -998,136 +801,6 @@ def register_query_tool_preferences(self): fields=shortcut_fields ) - self.keyword_case = self.preference.register( - 'editor', 'keyword_case', - gettext("Keyword case"), 'radioModern', 'upper', - options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, - {'label': LOWER_CASE_STR, 'value': 'lower'}, - {'label': PRESERVE_STR, 'value': 'preserve'}], - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Convert keywords to upper, lower, or preserve casing.' - ) - ) - - self.identifier_case = self.preference.register( - 'editor', 'identifier_case', - gettext("Identifier case"), 'radioModern', 'upper', - options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, - {'label': LOWER_CASE_STR, 'value': 'lower'}, - {'label': PRESERVE_STR, 'value': 'preserve'}], - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Convert identifiers to upper, lower, or preserve casing.' - ) - ) - - self.function_case = self.preference.register( - 'editor', 'function_case', - gettext("Function case"), 'radioModern', 'upper', - options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, - {'label': LOWER_CASE_STR, 'value': 'lower'}, - {'label': PRESERVE_STR, 'value': 'preserve'}], - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Convert function names to upper, lower, or preserve casing.' - ) - ) - - self.data_type_case = self.preference.register( - 'editor', 'data_type_case', - gettext("Data type case"), 'radioModern', 'upper', - options=[{'label': UPPER_CASE_STR, 'value': 'upper'}, - {'label': LOWER_CASE_STR, 'value': 'lower'}, - {'label': PRESERVE_STR, 'value': 'preserve'}], - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Convert data types to upper, lower, or preserve casing.' - ) - ) - - self.spaces_around_operators = self.preference.register( - 'editor', 'spaces_around_operators', - gettext("Spaces around operators?"), 'boolean', True, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext('If set to True, spaces are used around all ' - 'operators.') - ) - - self.tab_size = self.preference.register( - 'editor', 'tab_size', - gettext("Tab size"), 'integer', 4, - min_val=2, - max_val=8, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'The number of spaces per tab. Minimum 2, maximum 8.' - ) - ) - - self.use_spaces = self.preference.register( - 'editor', 'use_spaces', - gettext("Use spaces?"), 'boolean', False, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Specifies whether or not to insert spaces instead of tabs ' - 'when the tab key or auto-indent are used.' - ) - ) - - self.preference.register( - 'Editor', 'indent_new_line', - gettext("Auto-indent new line?"), 'boolean', True, - category_label=PREF_LABEL_EDITOR, - help_str=gettext( - 'Specifies whether the newly added line using enter key should ' - 'be auto-indented or not' - ) - ) - - self.expression_width = self.preference.register( - 'editor', 'expression_width', - gettext("Expression Width"), 'integer', 50, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'maximum number of characters in parenthesized expressions to be ' - 'kept on single line.' - ) - ) - - self.logical_operator_new_line = self.preference.register( - 'editor', 'logical_operator_new_line', - gettext("Logical operator new line"), 'radioModern', 'before', - options=[{'label': gettext('Before'), 'value': 'before'}, - {'label': gettext('After'), 'value': 'after'}], - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Decides newline placement before or after logical operators ' - '(AND, OR, XOR).' - ) - ) - - self.lines_between_queries = self.preference.register( - 'editor', 'lines_between_queries', - gettext("Lines between queries"), 'integer', 1, - min_val=0, - max_val=5, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Decides how many empty lines to leave between SQL statements. ' - 'If zero it puts no new line.' - ) - ) - - self.new_line_before_semicolon = self.preference.register( - 'editor', 'new_line_before_semicolon', - gettext("New line before semicolon?"), 'boolean', False, - category_label=PREF_LABEL_SQL_FORMATTING, - help_str=gettext( - 'Whether to place query separator (;) on a separate line.' - ) - ) - self.row_limit = self.preference.register( 'graph_visualiser', 'row_limit', gettext("Row Limit"), 'integer', diff --git a/web/pgadmin/utils/preferences.py b/web/pgadmin/utils/preferences.py index 8d9077d73bb..40f7441fb3b 100644 --- a/web/pgadmin/utils/preferences.py +++ b/web/pgadmin/utils/preferences.py @@ -90,7 +90,7 @@ def __init__( db.session.add(res) db.session.commit() res = PrefTable.query.filter_by( - name=name + name=name, cid=cid ).first() # Save this id for letter use.