Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(urlParam): new locale urlparam embedding to setup language for traductions #31646

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 33 additions & 2 deletions superset-frontend/src/components/Loading/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/

import { useEffect, useState } from 'react';
import { styled } from '@superset-ui/core';
import cls from 'classnames';
import Loader from 'src/assets/images/loading.gif';

export type PositionOption =
| 'floating'
Expand Down Expand Up @@ -61,6 +60,38 @@
image,
className,
}: Props) {
const [Loader, setLoader] = useState<string | undefined>(undefined);
useEffect(() => {
console.log('Fetching loader...');
fetch('/superset/loader/', {
method: 'GET',
credentials: 'include',
})
.then(response => {
console.log('Response status:', response.status);
if (!response.ok) {
if (response.status === 401) {
console.error('User is not authenticated');
} else if (response.status === 403) {
console.error('User does not have permission to access this endpoint');

Check failure on line 76 in superset-frontend/src/components/Loading/index.tsx

View workflow job for this annotation

GitHub Actions / frontend-build

Replace `'User·does·not·have·permission·to·access·this·endpoint'` with `⏎··············'User·does·not·have·permission·to·access·this·endpoint',⏎············`
}
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Received data:', data);
// Vérifie si l'objet a une clé "src" et l'assigne
if (data?.src) {
setLoader(data.src);
} else {
console.warn('No "src" found in loader data');
}
})
.catch(error => {
console.error('Error fetching loader:', error);
});
}, []);
return (
<LoaderImg
className={cls('loading', position, className)}
Expand Down
4 changes: 4 additions & 0 deletions superset-frontend/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export const URL_PARAMS = {
name: 'focused_chart',
type: 'number',
},
locale: {
name: 'locale',
type: 'string',
},
} as const;

export const RESERVED_CHART_URL_PARAMS: string[] = [
Expand Down
7 changes: 6 additions & 1 deletion superset/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
import os
from typing import Optional

from flask import Flask
from flask import Flask, request, session
from flask_babel import Babel

from superset.initialization import SupersetAppInitializer

logger = logging.getLogger(__name__)

babel = Babel()

def create_app(superset_config_module: Optional[str] = None) -> Flask:
app = SupersetApp(__name__)
Expand All @@ -46,6 +48,9 @@
logger.exception("Failed to create app")
raise

@babel.localeselector
def get_locale():
return session.get('locale') or request.accept_languages.best_match(['en', 'fr']) # add here supported languages

Check warning on line 53 in superset/app.py

View check run for this annotation

Codecov / codecov/patch

superset/app.py#L53

Added line #L53 was not covered by tests

class SupersetApp(Flask):
pass
26 changes: 16 additions & 10 deletions superset/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,6 @@
PACKAGE_JSON_FILE = str(files("superset") / "static/assets/package.json")


# Multiple favicons can be specified here. The "href" property
# is mandatory, but "sizes," "type," and "rel" are optional.
# For example:
# {
# "href":path/to/image.png",
# "sizes": "16x16",
# "type": "image/png"
# "rel": "icon"
# },
FAVICONS = [{"href": "/static/assets/images/favicon.png"}]


def _try_json_readversion(filepath: str) -> str | None:
Expand Down Expand Up @@ -311,6 +301,22 @@ def _try_json_readsha(filepath: str, length: int) -> str | None:
# Specify the App icon
APP_ICON = "/static/assets/images/superset-logo-horiz.png"

# Multiple favicons can be specified here. The "href" property
# is mandatory, but "sizes," "type," and "rel" are optional.
# For example:
# {
# "href":path/to/image.png",
# "sizes": "16x16",
# "type": "image/png"
# "rel": "icon"
# },
FAVICONS = [{"href": "/static/assets/images/favicon.png"}]

# Specify the gif file path to setup your custom loader.
# In config.py this is the default superset loader.
# You can setup your own in superset_config.py
LOADER = {"src": "/static/assets/images/loading.gif"}

# Specify where clicking the logo would take the user'
# Default value of None will take you to '/superset/welcome'
# You can also specify a relative URL e.g. '/superset/welcome' or '/dashboards/list'
Expand Down
8 changes: 6 additions & 2 deletions superset/templates/superset/basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
{% import 'appbuilder/general/lib.html' as lib %} {% from
'superset/partials/asset_bundle.html' import css_bundle, js_bundle with context
%} {% set favicons = appbuilder.app.config['FAVICONS'] %}
{% set loaders = appbuilder.app.config['LOADER'] %}
{% import "superset/macros.html" as macros %}
<html>
<head>
Expand Down Expand Up @@ -74,9 +75,10 @@
<body {% if standalone_mode %}class="standalone" {% endif %}>
{% block navbar %} {% if not standalone_mode %} {% include
'appbuilder/navbar.html' %} {% endif %} {% endblock %} {% block body %}
<div id="app" data-bootstrap="{{ bootstrap_data }}">
<div id="app" data-bootstrap="{{ bootstrap_data }}" {% if standalone_mode %} {% endif %}>
{% for loader in loaders %}
<img
src="{{ assets_prefix }}/static/assets/images/loading.gif"
src="{{ assets_prefix }}{{loader.src}}"
style="
width: 50px;
position: absolute;
Expand All @@ -85,6 +87,7 @@
transform: translate(-50%, -50%);
"
/>
{% endfor %}
</div>
{% endblock %}

Expand Down Expand Up @@ -133,5 +136,6 @@ <h4 data-test="modal-title" class="modal-title"></h4>
});
});
</script>

</body>
</html>
16 changes: 14 additions & 2 deletions superset/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
redirect,
Response,
session,
request
)
from flask_appbuilder import BaseView, expose, Model, ModelView
from flask_appbuilder.actions import action
Expand All @@ -46,7 +47,7 @@
)
from flask_appbuilder.security.sqla.models import User
from flask_appbuilder.widgets import ListWidget
from flask_babel import get_locale, gettext as __
from flask_babel import get_locale, gettext as __, refresh
from flask_jwt_extended.exceptions import NoAuthorizationError
from flask_wtf.form import FlaskForm
from sqlalchemy.orm import Query
Expand Down Expand Up @@ -348,8 +349,19 @@


def common_bootstrap_payload() -> dict[str, Any]:
# get locale from URL, else use default locale
locale = get_locale()
if request.args.get('locale'):
try:
locale = Locale.parse(request.args.get('locale'))
session['locale'] = str(locale)
refresh()
except Exception as e:

Check warning on line 359 in superset/views/base.py

View check run for this annotation

Codecov / codecov/patch

superset/views/base.py#L355-L359

Added lines #L355 - L359 were not covered by tests
# Manage invalid locale : keep default locale
app.logger.warning(f"Invalid locale '{request.args.get('locale')}': {e}")

Check warning on line 361 in superset/views/base.py

View check run for this annotation

Codecov / codecov/patch

superset/views/base.py#L361

Added line #L361 was not covered by tests

return {
**cached_common_bootstrap_data(utils.get_user_id(), get_locale()),
**cached_common_bootstrap_data(utils.get_user_id(), locale),
"flash_messages": get_flashed_messages(with_categories=True),
}

Expand Down
12 changes: 11 additions & 1 deletion superset/views/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from typing import Any, Callable, cast
from urllib import parse

from flask import abort, flash, g, redirect, request, Response
from flask import abort, flash, g, redirect, request, Response, jsonify
from flask_appbuilder import expose
from flask_appbuilder.security.decorators import (
has_access,
Expand Down Expand Up @@ -855,7 +855,7 @@
url = f"{url}&{original_params}"
if hash_ := state.get("anchor", state.get("hash")):
url = f"{url}#{hash_}"
return redirect(url)

Check warning

Code scanning / CodeQL

URL redirection from remote source Medium

Untrusted URL redirection depends on a
user-provided value
.

@api
@has_access
Expand Down Expand Up @@ -925,3 +925,13 @@
@deprecated(new_target="/sqllab/history")
def sqllab_history(self) -> FlaskResponse:
return redirect("/sqllab/history")

@has_access
@api
@expose("/loader/", methods=("GET",))
def get_loader(self) -> FlaskResponse:
"""
Expose the custom LOADERS configuration via an API endpoint.
"""
loader = app.config.get("LOADER", {})
return jsonify(loader)

Check warning on line 937 in superset/views/core.py

View check run for this annotation

Codecov / codecov/patch

superset/views/core.py#L936-L937

Added lines #L936 - L937 were not covered by tests
Loading