From 5a74022e8abc91f8975c12fc4d694c766783ebe0 Mon Sep 17 00:00:00 2001 From: Yogesh Mahajan Date: Thu, 15 Feb 2024 14:58:31 +0530 Subject: [PATCH] Allow preferences customization using a configuration file. #6058 --- docs/en_US/container_deployment.rst | 22 +++++++-- docs/en_US/preferences.rst | 30 ++++++++++++ pkg/docker/entrypoint.sh | 11 +++++ web/pgadmin/preferences/__init__.py | 3 ++ .../js/components/PreferencesComponent.jsx | 2 +- web/regression/feature_utils/pgadmin_page.py | 2 +- web/setup.py | 47 +++++++++++++++---- 7 files changed, 102 insertions(+), 15 deletions(-) diff --git a/docs/en_US/container_deployment.rst b/docs/en_US/container_deployment.rst index cb0dbc93d..8a685b2ea 100644 --- a/docs/en_US/container_deployment.rst +++ b/docs/en_US/container_deployment.rst @@ -26,9 +26,9 @@ where ** is one of the following: +==========+===============================================================================+ | latest | The most recent release. | +----------+-------------------------------------------------------------------------------+ - | 7.4 | A specific version (7.4 in this case). | + | 8.4 | A specific version (8.4 in this case). | +----------+-------------------------------------------------------------------------------+ - | 7 | the latest release of a specific major version (major version 7 in this case).| + | 8 | the latest release of a specific major version (major version 8 in this case).| +----------+-------------------------------------------------------------------------------+ | snapshot | The latest nightly test build. | +----------+-------------------------------------------------------------------------------+ @@ -42,11 +42,11 @@ maintenance functions to be executed. Multiple versions are included in the following directories to allow use with different versions of the database server: -* PostgreSQL 11: */usr/local/pgsql-11* * PostgreSQL 12: */usr/local/pgsql-12* * PostgreSQL 13: */usr/local/pgsql-13* * PostgreSQL 14: */usr/local/pgsql-14* * PostgreSQL 15: */usr/local/pgsql-15* +* PostgreSQL 16: */usr/local/pgsql-16* The default binary paths set in the container are as follows: @@ -134,6 +134,14 @@ Override the default file path for the server definition list. See the /pgadmin4/servers.json mapped file below for more information. See the format of the `JSON file `_. +**PGADMIN_PREFERENCES_JSON_FILE** + +*Default: /pgadmin4/preferences.json* + +Override the default file path for the preferences customization at the container creation. See the +/pgadmin4/preferences.json mapped file below for more information. See the format +of the `JSON file `_. + **GUNICORN_ACCESS_LOGFILE** *Default: -* (stdout) @@ -228,6 +236,14 @@ pgAdmin in the container. Note that server definitions are only loaded on first launch, i.e. when the configuration database is created, and not on subsequent launches using the same configuration database. +**/pgadmin4/preferences.json** + +If this file is mapped, preferences defined in it will be updated at launch +time. This allows customization of preferences settings into the instance of +pgAdmin in the container. Note that preferences are only set on first +launch, i.e. when the configuration database is created, and not on subsequent +launches using the same configuration database. + **/certs/server.cert** If TLS is enabled, this file will be used as the servers TLS certificate. diff --git a/docs/en_US/preferences.rst b/docs/en_US/preferences.rst index f754340f4..6bd032aa3 100644 --- a/docs/en_US/preferences.rst +++ b/docs/en_US/preferences.rst @@ -590,3 +590,33 @@ If auth_source is not given, Internal authentication will be consider by default # to specify an auth_source /path/to/python /path/to/setup.py set-prefs user1@gmail.com sqleditor:editor:comma_first=true --auth-source=ldap + +Updating Preferences preferences.json +************************************* +To update preferences defined in json file, simply run ``setup.py `` with ``set-prefs`` command followed by username +and JSON file containing the preferences. + +.. code-block:: bash + + /path/to/python /path/to/setup.py set-prefs user1@gmail.com --input-file /Users/yogeshmahajan/Desktop/pref.json + +JSON format +*********** + +The JSON file simply contains preference_key=value. Preference key value mapping can be obtained by hovering the +individual preference in the Preference UI dialog. + +The following is an typical example for preferences.json : + +.. code-block:: python + + { + "preferences": + { + "browser:display:show_system_objects": true, + "browser:display:show_user_defined_templates": true, + "browser:display:confirm_on_refresh_close": false, + "misc:themes:theme": "dark", + + } + } diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 266477584..5156f5008 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -75,6 +75,17 @@ if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then /venv/bin/python3 /pgadmin4/setup.py load-servers "${PGADMIN_SERVER_JSON_FILE}" --user "${PGADMIN_DEFAULT_EMAIL}" fi fi + if [ -f "${PGADMIN_PREFERENCES_JSON_FILE}" ]; then + # When running in Desktop mode, no user is created + # so we have to import servers anonymously + if [ "${PGADMIN_CONFIG_SERVER_MODE}" = "False" ]; then + DESKTOP_USER=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.DESKTOP_USER)') + /venv/bin/python3 /pgadmin4/setup.py set-prefs "${DESKTOP_USER}" --input-file "${PGADMIN_PREFERENCES_JSON_FILE}" + else + /venv/bin/python3 /pgadmin4/setup.py set-prefs "${PGADMIN_DEFAULT_EMAIL}" --input-file "${PGADMIN_PREFERENCES_JSON_FILE}" + fi + fi + fi # Start Postfix to handle password resets etc. diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py index 9ada066b7..77365a4d4 100644 --- a/web/pgadmin/preferences/__init__.py +++ b/web/pgadmin/preferences/__init__.py @@ -282,6 +282,9 @@ def save_pref(data): and data['value'].isspace(): data['value'] = '' + if data['value'] in ['true','false']: + data['value'] = True if data['value'] == 'true' else False + res, _ = Preferences.save_cli( data['mid'], data['category_id'], data['id'], data['user_id'], data['value']) diff --git a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx index 8133e5804..435cda740 100644 --- a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx +++ b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx @@ -378,7 +378,7 @@ export default function PreferencesComponent({ ...props }) { if(field.visible && _.isNull(firstElement)) { firstElement = field; } - field.tooltip = item._parent._metadata.data.name + ':' + item._metadata.data.name + ':' + field.name; + field.tooltip = item._parent._metadata.data.name.toLowerCase() + ':' + item._metadata.data.name + ':' + field.name; }); setLoadTree(crypto.getRandomValues(new Uint16Array(1))); initTreeTimeout = setTimeout(() => { diff --git a/web/regression/feature_utils/pgadmin_page.py b/web/regression/feature_utils/pgadmin_page.py index 8c0182fa3..1da6579f6 100644 --- a/web/regression/feature_utils/pgadmin_page.py +++ b/web/regression/feature_utils/pgadmin_page.py @@ -852,7 +852,7 @@ class PgadminPage: backspaces = [Keys.BACKSPACE] * len(field.get_attribute('value')) field.send_keys(backspaces) field.send_keys(str(field_content)) - self.wait_for_input_by_element(field, field_content) + # self.wait_for_input_by_element(field, field_content) else: self.driver.execute_script("arguments[0].value = arguments[1]", field, field_content) diff --git a/web/setup.py b/web/setup.py index 0dfdf681d..7cf7f02a8 100644 --- a/web/setup.py +++ b/web/setup.py @@ -40,14 +40,16 @@ from pgadmin.model import db, Version, User, \ SCHEMA_VERSION as CURRENT_SCHEMA_VERSION from pgadmin import create_app from pgadmin.utils import clear_database_servers, dump_database_servers, \ - load_database_servers + load_database_servers, _handle_error from pgadmin.setup import db_upgrade, create_app_data_directory from typing import Optional, List from typing_extensions import Annotated -from pgadmin.utils.constants import MIMETYPE_APP_JS, INTERNAL, LDAP, OAUTH2, \ +from pgadmin.utils.constants import INTERNAL, LDAP, OAUTH2, \ KERBEROS, WEBSERVER from pgadmin.tools.user_management import create_user, delete_user, update_user from enum import Enum +from flask_babel import gettext + app = typer.Typer(pretty_exceptions_show_locals=False) @@ -406,11 +408,8 @@ class ManagePreferences: table = Table(title="Pref Details", box=box.ASCII) table.add_column("Preference", style="green") with app.app_context(): - from pgadmin.preferences import save_pref - from pgadmin.utils.preferences import Preferences - from pgadmin.model import db, Preferences as PrefTable, \ + from pgadmin.model import Preferences as PrefTable, \ ModulePreference as ModulePrefTable, \ - UserPreference as UserPrefTable, \ PreferenceCategory as PrefCategoryTbl module_prefs = ModulePrefTable.query.all() @@ -448,14 +447,42 @@ class ManagePreferences: print(table) @app.command() - def set_prefs(username, pref_options: List[str], + 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): + json: Optional[bool] = False, + input_file: Optional[str] = None): """Set User preferences.""" + + if input_file: + from urllib.parse import unquote + # generate full path of file + try: + file_path = unquote(input_file) + except Exception as e: + print(str(e)) + return _handle_error(str(e), True) + import json as json_utility + try: + with open(file_path) as f: + data = json_utility.load(f) + except json_utility.decoder.JSONDecodeError as e: + return _handle_error(gettext("Error parsing input file %s: %s" + % (file_path, e)), True) + except Exception as e: + return _handle_error( + gettext("Error reading input file %s: [%d] %s" % + (file_path, e.errno, e.strerror)), True) + + pref_data = data['preferences'] + + for k, v in pref_data.items(): + pref_options.append(k + "=" + str(v)) + user_id = ManagePreferences.get_user(username, auth_source) - app = create_app(config.APP_NAME + '-cli') - table = Table(title="Pref Details", box=box.ASCII) + table = Table(title="Updated Pref Details", box=box.ASCII) table.add_column("Preference", style="green") if not user_id: print("User not found.")