mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Allow users to select UI language at login or from Preferences rather than unpredictable behaviour from browsers. Fixes #2190
This commit is contained in:
parent
6cb9ece6fd
commit
0eda6033df
@ -14,7 +14,7 @@ import os, sys, time
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
from flask import Flask, abort, request, current_app
|
from flask import Flask, abort, request, current_app, session
|
||||||
from flask_babel import Babel, gettext
|
from flask_babel import Babel, gettext
|
||||||
from flask_htmlmin import HTMLMIN
|
from flask_htmlmin import HTMLMIN
|
||||||
from flask_login import user_logged_in
|
from flask_login import user_logged_in
|
||||||
@ -29,6 +29,8 @@ from pgadmin.utils.session import create_session_interface
|
|||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
from werkzeug.utils import find_modules
|
from werkzeug.utils import find_modules
|
||||||
|
|
||||||
|
from pgadmin.utils.preferences import Preferences
|
||||||
|
|
||||||
from pgadmin.model import db, Role, Server, ServerGroup, User, Version, Keys
|
from pgadmin.model import db, Role, Server, ServerGroup, User, Version, Keys
|
||||||
|
|
||||||
# If script is running under python3, it will not have the xrange function
|
# If script is running under python3, it will not have the xrange function
|
||||||
@ -189,8 +191,29 @@ def create_app(app_name=None):
|
|||||||
|
|
||||||
@babel.localeselector
|
@babel.localeselector
|
||||||
def get_locale():
|
def get_locale():
|
||||||
"""Get the best language for the user."""
|
"""Get the language for the user."""
|
||||||
language = request.accept_languages.best_match(config.LANGUAGES.keys())
|
language = 'en'
|
||||||
|
if config.SERVER_MODE is False:
|
||||||
|
# Get the user language preference from the miscellaneous module
|
||||||
|
misc_preference = Preferences.module('miscellaneous', False)
|
||||||
|
if misc_preference:
|
||||||
|
user_languages = misc_preference.preference(
|
||||||
|
'user_language'
|
||||||
|
)
|
||||||
|
if user_languages:
|
||||||
|
language = user_languages.get() or language
|
||||||
|
else:
|
||||||
|
# If language is available in get request then return the same
|
||||||
|
# otherwise check the session or cookie
|
||||||
|
data = request.form
|
||||||
|
if 'language' in data:
|
||||||
|
language = data['language'] or language
|
||||||
|
setattr(session, 'PGADMIN_LANGUAGE', language)
|
||||||
|
elif hasattr(session, 'PGADMIN_LANGUAGE'):
|
||||||
|
language = getattr(session, 'PGADMIN_LANGUAGE', language)
|
||||||
|
elif hasattr(request.cookies, 'PGADMIN_LANGUAGE'):
|
||||||
|
language = getattr(request.cookies, 'PGADMIN_LANGUAGE', language)
|
||||||
|
|
||||||
return language
|
return language
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -269,6 +292,27 @@ def create_app(app_name=None):
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
driver.init_app(app)
|
driver.init_app(app)
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Register language to the preferences after login
|
||||||
|
##########################################################################
|
||||||
|
@user_logged_in.connect_via(app)
|
||||||
|
def register_language(sender, user):
|
||||||
|
# After logged in, set the language in the preferences if we get from
|
||||||
|
# the login page
|
||||||
|
data = request.form
|
||||||
|
if 'language' in data:
|
||||||
|
language = data['language']
|
||||||
|
|
||||||
|
# Set the user language preference
|
||||||
|
misc_preference = Preferences.module('miscellaneous')
|
||||||
|
user_languages = misc_preference.preference(
|
||||||
|
'user_language'
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_languages and language:
|
||||||
|
language = user_languages.set(language)
|
||||||
|
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# Register any local servers we can discover
|
# Register any local servers we can discover
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -11,7 +11,8 @@ import json
|
|||||||
from abc import ABCMeta, abstractmethod, abstractproperty
|
from abc import ABCMeta, abstractmethod, abstractproperty
|
||||||
|
|
||||||
import six
|
import six
|
||||||
from flask import current_app, render_template, url_for, make_response, flash
|
from flask import current_app, render_template, url_for, make_response, flash,\
|
||||||
|
Response, request
|
||||||
from flask_babel import gettext
|
from flask_babel import gettext
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_security import login_required
|
from flask_security import login_required
|
||||||
@ -473,13 +474,26 @@ def index():
|
|||||||
|
|
||||||
flash(msg, 'warning')
|
flash(msg, 'warning')
|
||||||
|
|
||||||
return render_template(
|
response = Response(render_template(
|
||||||
MODULE_NAME + "/index.html",
|
MODULE_NAME + "/index.html",
|
||||||
username=current_user.email,
|
username=current_user.email,
|
||||||
is_admin=current_user.has_role("Administrator"),
|
is_admin=current_user.has_role("Administrator"),
|
||||||
_=gettext
|
_=gettext
|
||||||
)
|
))
|
||||||
|
|
||||||
|
# Set the language cookie after login, so next time the user will have that
|
||||||
|
# same option at the login time.
|
||||||
|
misc_preference = Preferences.module('miscellaneous')
|
||||||
|
user_languages = misc_preference.preference(
|
||||||
|
'user_language'
|
||||||
|
)
|
||||||
|
language = 'en'
|
||||||
|
if user_languages:
|
||||||
|
language = user_languages.get() or 'en'
|
||||||
|
|
||||||
|
response.set_cookie("PGADMIN_LANGUAGE", language)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
@blueprint.route("/js/browser.js")
|
@blueprint.route("/js/browser.js")
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -252,7 +252,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backform) {
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
tags: true,
|
tags: true,
|
||||||
allowClear: data.disabled ? false : true,
|
allowClear: data.disabled ? false : true,
|
||||||
placeholder: data.disabled ? "" : "Select members",
|
placeholder: data.disabled ? "" : "{{ _('Select members') }}",
|
||||||
width: 'style'
|
width: 'style'
|
||||||
}).on("change", function(e) {
|
}).on("change", function(e) {
|
||||||
$(e.target).find(':selected').each(function() {
|
$(e.target).find(':selected').each(function() {
|
||||||
|
@ -45,22 +45,27 @@ class ServerType(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def register_preferences(cls):
|
def register_preferences(cls):
|
||||||
paths = Preferences('paths', _('Paths'))
|
# Introduce inner function as we need to register the preferences after
|
||||||
|
# we get the user language and its translation.
|
||||||
|
def register_paths():
|
||||||
|
paths = Preferences('paths', _('Paths'))
|
||||||
|
|
||||||
for key in cls.registry:
|
for key in cls.registry:
|
||||||
st = cls.registry[key]
|
st = cls.registry[key]
|
||||||
default_path = config.DEFAULT_BINARY_PATHS[st.stype] or ""
|
default_path = config.DEFAULT_BINARY_PATHS[st.stype] or ""
|
||||||
|
|
||||||
st.utility_path = paths.register(
|
st.utility_path = paths.register(
|
||||||
'bin_paths', st.stype + '_bin_dir',
|
'bin_paths', st.stype + '_bin_dir',
|
||||||
_("{0} Binary Path").format(st.desc),
|
_("{0} Binary Path").format(st.desc),
|
||||||
'text', default_path, category_label=_('Binary paths'),
|
'text', default_path, category_label=_('Binary paths'),
|
||||||
help_str=_(
|
help_str=_(
|
||||||
"Path to the directory containing the {0} utility programs (pg_dump, pg_restore etc).".format(
|
"Path to the directory containing the {0} utility"
|
||||||
st.desc
|
" programs (pg_dump, pg_restore etc).".format(
|
||||||
|
st.desc
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
return register_paths
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def priority(self):
|
def priority(self):
|
||||||
|
@ -32,10 +32,10 @@ function($, _, pgAdmin, Backbone) {
|
|||||||
},
|
},
|
||||||
schema: [
|
schema: [
|
||||||
{
|
{
|
||||||
id: 'id', label: 'ID', type: 'int', group: null,
|
id: 'id', label: '{{ _('ID') }}', type: 'int', group: null,
|
||||||
mode: ['properties']
|
mode: ['properties']
|
||||||
},{
|
},{
|
||||||
id: 'name', label:'Name', type: 'text', group: null,
|
id: 'name', label:'{{ _('Name') }}', type: 'text', group: null,
|
||||||
mode: ['properties', 'edit', 'create']
|
mode: ['properties', 'edit', 'create']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -13,6 +13,7 @@ import pgadmin.utils.driver as driver
|
|||||||
from flask import url_for, render_template, Response
|
from flask import url_for, render_template, Response
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
from pgadmin.utils import PgAdminModule
|
from pgadmin.utils import PgAdminModule
|
||||||
|
from pgadmin.utils.preferences import Preferences
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
|
||||||
@ -41,6 +42,25 @@ class MiscModule(PgAdminModule):
|
|||||||
)
|
)
|
||||||
return stylesheets
|
return stylesheets
|
||||||
|
|
||||||
|
def register_preferences(self):
|
||||||
|
"""
|
||||||
|
Register preferences for this module.
|
||||||
|
"""
|
||||||
|
self.misc_preference = Preferences('miscellaneous', _('Miscellaneous'))
|
||||||
|
|
||||||
|
lang_options = []
|
||||||
|
for lang in config.LANGUAGES:
|
||||||
|
lang_options.append({'label': config.LANGUAGES[lang],
|
||||||
|
'value': lang})
|
||||||
|
|
||||||
|
# Register options for the User language settings
|
||||||
|
language = self.misc_preference.register(
|
||||||
|
'miscellaneous', 'user_language',
|
||||||
|
_("User language"), 'options', 'en',
|
||||||
|
category_label=_('User language'),
|
||||||
|
options=lang_options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Initialise the module
|
# Initialise the module
|
||||||
blueprint = MiscModule(MODULE_NAME, __name__)
|
blueprint = MiscModule(MODULE_NAME, __name__)
|
||||||
|
@ -13,7 +13,7 @@ side and for getting/setting preferences.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import simplejson as json
|
import simplejson as json
|
||||||
from flask import render_template, url_for, Response, request
|
from flask import render_template, url_for, Response, request, session
|
||||||
from flask_babel import gettext
|
from flask_babel import gettext
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_security import login_required
|
from flask_security import login_required
|
||||||
@ -109,23 +109,28 @@ def preferences(module=None, preference=None):
|
|||||||
res = []
|
res = []
|
||||||
|
|
||||||
def label(p):
|
def label(p):
|
||||||
return p['label']
|
return gettext(p['label'])
|
||||||
|
|
||||||
for m in pref:
|
for m in pref:
|
||||||
if len(m['categories']):
|
if len(m['categories']):
|
||||||
om = {
|
om = {
|
||||||
"id": m['id'],
|
"id": m['id'],
|
||||||
"label": m['label'],
|
"label": gettext(m['label']),
|
||||||
"inode": True,
|
"inode": True,
|
||||||
"open": True,
|
"open": True,
|
||||||
"branch": []
|
"branch": []
|
||||||
}
|
}
|
||||||
|
|
||||||
for c in m['categories']:
|
for c in m['categories']:
|
||||||
|
for p in c['preferences']:
|
||||||
|
if 'label' in p and p['label'] is not None:
|
||||||
|
p['label'] = gettext(p['label'])
|
||||||
|
if 'help_str' in p and p['help_str'] is not None:
|
||||||
|
p['help_str'] = gettext(p['help_str'])
|
||||||
oc = {
|
oc = {
|
||||||
"id": c['id'],
|
"id": c['id'],
|
||||||
"mid": m['id'],
|
"mid": m['id'],
|
||||||
"label": c['label'],
|
"label": gettext(c['label']),
|
||||||
"inode": False,
|
"inode": False,
|
||||||
"open": False,
|
"open": False,
|
||||||
"preferences": sorted(c['preferences'], key=label)
|
"preferences": sorted(c['preferences'], key=label)
|
||||||
@ -155,4 +160,23 @@ def save(pid):
|
|||||||
if not res:
|
if not res:
|
||||||
return internal_server_error(errormsg=msg)
|
return internal_server_error(errormsg=msg)
|
||||||
|
|
||||||
return success_return()
|
response = success_return()
|
||||||
|
|
||||||
|
# Set cookie & session for language settings.
|
||||||
|
# This will execute every time as could not find the better way to know
|
||||||
|
# that which preference is getting updated.
|
||||||
|
|
||||||
|
misc_preference = Preferences.module('miscellaneous')
|
||||||
|
user_languages = misc_preference.preference(
|
||||||
|
'user_language'
|
||||||
|
)
|
||||||
|
|
||||||
|
language = 'en'
|
||||||
|
if user_languages:
|
||||||
|
language = user_languages.get() or language
|
||||||
|
|
||||||
|
setattr(session, 'PGADMIN_LANGUAGE', language)
|
||||||
|
response.set_cookie("PGADMIN_LANGUAGE", language)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@ -219,7 +219,11 @@ define(
|
|||||||
var opts = [];
|
var opts = [];
|
||||||
// Convert the array to SelectControl understandable options.
|
// Convert the array to SelectControl understandable options.
|
||||||
_.each(p.options, function(o) {
|
_.each(p.options, function(o) {
|
||||||
opts.push({'label': o, 'value': o});
|
if('label' in o && 'value' in o){
|
||||||
|
opts.push({'label': o.label, 'value': o.value});
|
||||||
|
} else {
|
||||||
|
opts.push({'label': o, 'value': o});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
p.options = opts;
|
p.options = opts;
|
||||||
return 'select2';
|
return 'select2';
|
||||||
|
@ -767,3 +767,16 @@ lgg-el-container[el=md] .pg-el-lg-8,
|
|||||||
.show_progress {
|
.show_progress {
|
||||||
cursor: progress;
|
cursor: progress;
|
||||||
}
|
}
|
||||||
|
.user-language {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.user-language label{
|
||||||
|
float:left;
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
|
.user-language div{
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
.user-language select{
|
||||||
|
height: 25px !important;
|
||||||
|
}
|
@ -3,10 +3,21 @@
|
|||||||
{% block panel_body %}
|
{% block panel_body %}
|
||||||
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
|
||||||
{{ login_user_form.hidden_tag() }}
|
{{ login_user_form.hidden_tag() }}
|
||||||
|
{% set user_language = request.cookies.get('PGADMIN_LANGUAGE') or 'en' %}
|
||||||
<fieldset>
|
<fieldset>
|
||||||
{{ render_field_with_errors(login_user_form.email, "text") }}
|
{{ render_field_with_errors(login_user_form.email, "text") }}
|
||||||
{{ render_field_with_errors(login_user_form.password, "password") }}
|
{{ render_field_with_errors(login_user_form.password, "password") }}
|
||||||
<input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Login') }}">
|
<input class="btn btn-lg btn-success btn-block" type="submit" value="{{ _('Login') }}">
|
||||||
|
<div class="pgadmin-control-group form-group pg-el-xs-12 user-language">
|
||||||
|
<label class="help-block pg-el-sm-3">{{ _("Language") }}</label>
|
||||||
|
<div class="pgadmin-controls pg-el-sm-9">
|
||||||
|
<select class="form-control" name="language" value="{{user_language}}">
|
||||||
|
{% for key, lang in config.LANGUAGES.items() %}
|
||||||
|
<option value="{{key}}" {% if user_language == key %}selected{% endif %}>{{lang}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<span class="help-block">{{ _('Forgotten your <a href="%(url)s">password</a>?', url=url_for('security.forgot_password')) }}</span>
|
<span class="help-block">{{ _('Forgotten your <a href="%(url)s">password</a>?', url=url_for('security.forgot_password')) }}</span>
|
||||||
|
@ -126,8 +126,9 @@ class _Preference(object):
|
|||||||
current_app.logger.exeception(e)
|
current_app.logger.exeception(e)
|
||||||
return self.default
|
return self.default
|
||||||
if self._type == 'options':
|
if self._type == 'options':
|
||||||
if res.value in self.options:
|
for opt in self.options:
|
||||||
return res.value
|
if 'value' in opt and opt['value'] == res.value:
|
||||||
|
return res.value
|
||||||
return self.default
|
return self.default
|
||||||
if self._type == 'text':
|
if self._type == 'text':
|
||||||
if res.value == '':
|
if res.value == '':
|
||||||
@ -184,7 +185,12 @@ class _Preference(object):
|
|||||||
current_app.logger.exeception(e)
|
current_app.logger.exeception(e)
|
||||||
return False, gettext("Invalid value for a datetime option.")
|
return False, gettext("Invalid value for a datetime option.")
|
||||||
elif self._type == 'options':
|
elif self._type == 'options':
|
||||||
if value not in self.options:
|
has_value = False
|
||||||
|
for opt in self.options:
|
||||||
|
if 'value' in opt and opt['value'] == value:
|
||||||
|
has_value = True
|
||||||
|
|
||||||
|
if not has_value:
|
||||||
return False, gettext("Invalid value for an options option.")
|
return False, gettext("Invalid value for an options option.")
|
||||||
|
|
||||||
pref = UserPrefTable.query.filter_by(
|
pref = UserPrefTable.query.filter_by(
|
||||||
|
Loading…
Reference in New Issue
Block a user