Allow users to select UI language at login or from Preferences rather than unpredictable behaviour from browsers. Fixes #2190

This commit is contained in:
Khushboo Vashi 2017-03-24 14:20:10 +00:00 committed by Dave Page
parent 6cb9ece6fd
commit 0eda6033df
11 changed files with 171 additions and 30 deletions

View File

@ -14,7 +14,7 @@ import os, sys, time
from collections import defaultdict
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_htmlmin import HTMLMIN
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.utils import find_modules
from pgadmin.utils.preferences import Preferences
from pgadmin.model import db, Role, Server, ServerGroup, User, Version, Keys
# 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
def get_locale():
"""Get the best language for the user."""
language = request.accept_languages.best_match(config.LANGUAGES.keys())
"""Get the language for the user."""
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
##########################################################################
@ -269,6 +292,27 @@ def create_app(app_name=None):
##########################################################################
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
##########################################################################

View File

@ -11,7 +11,8 @@ import json
from abc import ABCMeta, abstractmethod, abstractproperty
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_login import current_user
from flask_security import login_required
@ -473,13 +474,26 @@ def index():
flash(msg, 'warning')
return render_template(
response = Response(render_template(
MODULE_NAME + "/index.html",
username=current_user.email,
is_admin=current_user.has_role("Administrator"),
_=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")
@login_required

View File

@ -252,7 +252,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backform) {
multiple: true,
tags: true,
allowClear: data.disabled ? false : true,
placeholder: data.disabled ? "" : "Select members",
placeholder: data.disabled ? "" : "{{ _('Select members') }}",
width: 'style'
}).on("change", function(e) {
$(e.target).find(':selected').each(function() {

View File

@ -45,22 +45,27 @@ class ServerType(object):
@classmethod
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:
st = cls.registry[key]
default_path = config.DEFAULT_BINARY_PATHS[st.stype] or ""
for key in cls.registry:
st = cls.registry[key]
default_path = config.DEFAULT_BINARY_PATHS[st.stype] or ""
st.utility_path = paths.register(
'bin_paths', st.stype + '_bin_dir',
_("{0} Binary Path").format(st.desc),
'text', default_path, category_label=_('Binary paths'),
help_str=_(
"Path to the directory containing the {0} utility programs (pg_dump, pg_restore etc).".format(
st.desc
st.utility_path = paths.register(
'bin_paths', st.stype + '_bin_dir',
_("{0} Binary Path").format(st.desc),
'text', default_path, category_label=_('Binary paths'),
help_str=_(
"Path to the directory containing the {0} utility"
" programs (pg_dump, pg_restore etc).".format(
st.desc
)
)
)
)
return register_paths
@property
def priority(self):

View File

@ -32,10 +32,10 @@ function($, _, pgAdmin, Backbone) {
},
schema: [
{
id: 'id', label: 'ID', type: 'int', group: null,
id: 'id', label: '{{ _('ID') }}', type: 'int', group: null,
mode: ['properties']
},{
id: 'name', label:'Name', type: 'text', group: null,
id: 'name', label:'{{ _('Name') }}', type: 'text', group: null,
mode: ['properties', 'edit', 'create']
}
],

View File

@ -13,6 +13,7 @@ import pgadmin.utils.driver as driver
from flask import url_for, render_template, Response
from flask_babel import gettext as _
from pgadmin.utils import PgAdminModule
from pgadmin.utils.preferences import Preferences
import config
@ -41,6 +42,25 @@ class MiscModule(PgAdminModule):
)
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
blueprint = MiscModule(MODULE_NAME, __name__)

View File

@ -13,7 +13,7 @@ side and for getting/setting preferences.
"""
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_login import current_user
from flask_security import login_required
@ -109,23 +109,28 @@ def preferences(module=None, preference=None):
res = []
def label(p):
return p['label']
return gettext(p['label'])
for m in pref:
if len(m['categories']):
om = {
"id": m['id'],
"label": m['label'],
"label": gettext(m['label']),
"inode": True,
"open": True,
"branch": []
}
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 = {
"id": c['id'],
"mid": m['id'],
"label": c['label'],
"label": gettext(c['label']),
"inode": False,
"open": False,
"preferences": sorted(c['preferences'], key=label)
@ -155,4 +160,23 @@ def save(pid):
if not res:
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

View File

@ -219,7 +219,11 @@ define(
var opts = [];
// Convert the array to SelectControl understandable options.
_.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;
return 'select2';

View File

@ -767,3 +767,16 @@ lgg-el-container[el=md] .pg-el-lg-8,
.show_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;
}

View File

@ -3,10 +3,21 @@
{% block panel_body %}
<form action="{{ url_for_security('login') }}" method="POST" name="login_user_form">
{{ login_user_form.hidden_tag() }}
{% set user_language = request.cookies.get('PGADMIN_LANGUAGE') or 'en' %}
<fieldset>
{{ render_field_with_errors(login_user_form.email, "text") }}
{{ render_field_with_errors(login_user_form.password, "password") }}
<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>
</form>
<span class="help-block">{{ _('Forgotten your <a href="%(url)s">password</a>?', url=url_for('security.forgot_password')) }}</span>

View File

@ -126,8 +126,9 @@ class _Preference(object):
current_app.logger.exeception(e)
return self.default
if self._type == 'options':
if res.value in self.options:
return res.value
for opt in self.options:
if 'value' in opt and opt['value'] == res.value:
return res.value
return self.default
if self._type == 'text':
if res.value == '':
@ -184,7 +185,12 @@ class _Preference(object):
current_app.logger.exeception(e)
return False, gettext("Invalid value for a datetime option.")
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.")
pref = UserPrefTable.query.filter_by(