Skip to content

Commit c6a240d

Browse files
committed
Merge remote-tracking branch 'origin/SXDEDPCXZIC-435' into release-2.10.1
2 parents 6aea295 + 15c16de commit c6a240d

8 files changed

Lines changed: 114 additions & 41 deletions

File tree

ckanext/datavic_odp_theme/helpers.py

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import ckan.plugins.toolkit as toolkit
1111
import ckan.model as model
1212

13-
from ckanext.toolbelt.decorators import Collector
13+
from ckanext.toolbelt.decorators import Collector, Cache
1414

1515
from ckanext.datavic_odp_theme import config as conf, const
1616

@@ -20,38 +20,41 @@
2020

2121

2222
@helper
23-
def organization_list():
24-
org_list = toolkit.get_action("organization_list")({}, {})
25-
organizations = []
26-
for org in org_list:
27-
org_dict = toolkit.get_action("organization_show")({}, {"id": org})
28-
organizations.append(org_dict)
29-
30-
return organizations
23+
@Cache()
24+
def group_list(is_organization: bool) -> list[dict[str, str]]:
25+
"""Returns a list of active groups or organizations.
3126
27+
Results are cached separately for groups and organizations.
28+
Cache is invalidated on create, update, or delete actions
29+
(see `clear_group_list_cache` in plugins.py).
30+
"""
31+
groups = (
32+
model.Session.query(model.Group.id, model.Group.title, model.Group.name)
33+
.filter(model.Group.is_organization.is_(is_organization))
34+
.filter(model.Group.state == "active")
35+
.order_by(func.coalesce(model.Group.title, model.Group.name).asc())
36+
.all()
37+
)
3238

33-
@helper
34-
def group_list():
35-
return toolkit.get_action("group_list")({}, {"all_fields": True})
39+
return [
40+
{"id": g.id, "display_name": g.title or g.name, "name": g.name} for g in groups
41+
]
3642

3743

3844
@helper
3945
def format_list() -> list[str]:
40-
"""Return a list of all available resources on portal"""
46+
"""Return a sorted list of unique resource formats."""
47+
48+
# Clean format: lower-case and trimmed
49+
cleaned_format = func.lower(func.trim(model.Resource.format))
4150

4251
query = (
43-
model.Session.query(model.Resource.format)
52+
model.Session.query(func.distinct(cleaned_format))
4453
.filter(model.Resource.state == model.State.ACTIVE)
45-
.group_by(model.Resource.format)
46-
.order_by(func.lower(model.Resource.format))
54+
.filter(model.Resource.format.isnot(None))
55+
.filter(model.Resource.format != "")
4756
)
48-
49-
formats = [
50-
resource.format.upper().split(".")[-1] for resource in query if resource.format
51-
]
52-
unique_formats = set(formats)
53-
54-
return sorted(list(unique_formats))
57+
return sorted({fmt.upper().split(".")[-1] for (fmt,) in query})
5558

5659

5760
@helper
@@ -136,19 +139,14 @@ def featured_resource_preview(package: dict[str, Any]) -> Optional[dict[str, Any
136139

137140

138141
@helper
139-
def get_digital_twin_resources(pkg_id: str) -> list[dict[str, Any]]:
142+
def get_digital_twin_resources(pkg: dict[str, Any]) -> list[dict[str, Any]]:
140143
"""Select resource suitable for DTV(Digital Twin Visualization).
141144
142145
Additional info:
143146
https://gist.github.com/steve9164/b9781b517c99486624c02fdc7af0f186
144147
"""
145148
supported_formats: set[str] = conf.get_dtv_supported_formats()
146149

147-
try:
148-
pkg = toolkit.get_action("package_show")({}, {"id": pkg_id})
149-
except (toolkit.ObjectNotFound, toolkit.NotAuthorized):
150-
return []
151-
152150
# Additional info #2
153151
if pkg["state"] != "active":
154152
return []
@@ -307,9 +305,7 @@ def datavic_update_org_error_dict(
307305
to show it as an error on the Logo field."""
308306
if error_dict.pop("upload", "") == ["File upload too large"]:
309307
error_dict["Logo"] = [
310-
(
311-
f"File size is too large. Select an image which is no larger than {datavic_max_image_size()}MB."
312-
)
308+
f"File size is too large. Select an image which is no larger than {datavic_max_image_size()}MB."
313309
]
314310
elif "Unsupported upload type" in error_dict.pop("image_upload", [""])[0]:
315311
error_dict["Logo"] = [

ckanext/datavic_odp_theme/plugin.py

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
import ckan.plugins as p
88
import ckan.plugins.toolkit as tk
9-
import ckan.model as model
9+
from ckan import model, types
1010

1111
from ckanext.search_autocomplete.interfaces import ISearchAutocomplete
1212
from ckanext.xloader.plugin import xloaderPlugin
1313

1414
from ckanext.datavic_odp_theme.logic import auth_functions, actions, get_validators
1515
from ckanext.datavic_odp_theme.views import get_blueprints
16-
from ckanext.datavic_odp_theme.helpers import get_helpers
16+
from ckanext.datavic_odp_theme.helpers import get_helpers, group_list
1717

1818

1919
class DatavicODPTheme(p.SingletonPlugin):
@@ -25,6 +25,7 @@ class DatavicODPTheme(p.SingletonPlugin):
2525
p.implements(p.IPackageController, inherit=True)
2626
p.implements(ISearchAutocomplete)
2727
p.implements(p.IAuthenticator, inherit=True)
28+
p.implements(p.ISignal)
2829

2930
# IConfigurer
3031

@@ -33,6 +34,10 @@ def update_config(self, config_):
3334
tk.add_public_directory(config_, "public")
3435
tk.add_resource("webassets", "datavic_odp_theme")
3536

37+
# Reset group/organization cache on server restart
38+
group_list.reset(is_organization=False)
39+
group_list.reset(is_organization=True)
40+
3641
# ITemplateHelpers
3742

3843
def get_helpers(self):
@@ -112,6 +117,55 @@ def logout(self) -> Optional[Response]:
112117
session.modified = True
113118

114119

120+
# ISignal
121+
122+
def get_signal_subscriptions(self) -> types.SignalMapping:
123+
return {
124+
tk.signals.action_succeeded: [
125+
{
126+
"receiver": clear_group_list_cache,
127+
"sender": "group_create",
128+
},
129+
{
130+
"receiver": clear_group_list_cache,
131+
"sender": "group_update",
132+
},
133+
{
134+
"receiver": clear_group_list_cache,
135+
"sender": "group_delete",
136+
},
137+
{
138+
"receiver": clear_group_list_cache,
139+
"sender": "organization_create",
140+
},
141+
{
142+
"receiver": clear_group_list_cache,
143+
"sender": "organization_update",
144+
},
145+
{
146+
"receiver": clear_group_list_cache,
147+
"sender": "organization_delete",
148+
},
149+
]
150+
}
151+
152+
153+
def clear_group_list_cache(
154+
action_name: str,
155+
context: types.Context,
156+
data_dict: dict[str, Any],
157+
result: dict[str, Any],
158+
):
159+
"""Invalidates the cached group or organization list after
160+
create, update, or delete actions."""
161+
162+
is_organization = (
163+
action_name.startswith("organization")
164+
or data_dict.get("type") == "organization"
165+
)
166+
group_list.reset(is_organization=is_organization)
167+
168+
115169
@tk.blanket.auth_functions(auth_functions)
116170
class DatavicODPThemeAuth(p.SingletonPlugin):
117171
"""Register auth function inside separate extension.

ckanext/datavic_odp_theme/templates/package/snippets/datavic_dtv.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% set dtv_resources = h.get_digital_twin_resources(pkg.id)|map(attribute="id")|list %}
1+
{% set dtv_resources = h.get_digital_twin_resources(pkg)|map(attribute="id")|list %}
22
{% set dtv_preview = pkg.dtv_preview %}
33
{% set dtv_exceeds_limit = h.dtv_exceeds_max_size_limit(dtv_resources[0]) %}
44
{% set dtv_url = h.datavic_get_dtv_url() %}

ckanext/datavic_odp_theme/templates/snippets/dataset_search_form.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ <h1>
6363
{% endblock %}
6464
</div>
6565
{% if facets %}
66-
{% set organization_list = h.organization_list() %}
66+
{% set organization_list = h.group_list(is_organization=True) %}
67+
{% set group_list = h.group_list(is_organization=False) %}
6768

6869
<button class="rpl-search-form__show-filters rpl-search-form__show-filters--expanded" aria-expanded="true">
6970
<span>Advanced search</span>
@@ -138,7 +139,7 @@ <h1>
138139
<div class="rpl-checklist__main-row">
139140
<button type="button" class="rpl-checklist__info" data-checklist="checklist-group">
140141
{% if group %}
141-
{% for item in h.group_list() %}
142+
{% for item in group_list %}
142143
{% if item.name == group %}
143144
<span>{{ item.display_name }}</span>
144145
{% endif %}
@@ -152,7 +153,7 @@ <h1>
152153
</svg>
153154
</button>
154155
</div>
155-
{% with items = h.group_list() %}
156+
{% with items = group_list %}
156157
{% if items %}
157158
<div aria-hidden="true" class="rpl-checklist__list rpl-checklist__list--dropdown" style="display: none;">
158159
<div class="rpl-checklist__list-row rpl-checklist__list-row--single">

ckanext/datavic_odp_theme/templates/snippets/package_item.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ <h4 class="search-result-organisation">
8080
{% endif %}
8181
<ul class="list-unstyled dataset-api">
8282
<ul>
83-
{% set dtv_resources = h.get_digital_twin_resources(package.id)|map(attribute="id")|list %}
83+
{% set dtv_resources = h.get_digital_twin_resources(package)|map(attribute="id")|list %}
8484
{% set featured_resource = h.featured_resource_preview(package) %}
8585

8686
{% if dtv_resources %}

ckanext/datavic_odp_theme/templates/user/snippets/login_form.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616
{{ h.csrf_input() }}
1717
{{ form.errors(errors=error_summary) }}
1818

19-
<div id="error-container" class="alert alert-error" style="display: none;">
19+
<div id="mfa-error-container" class="alert alert-error" style="display: none;">
2020
<p>{{ _('The form contains invalid entries:') }}</p>
2121
<ul>
22-
<li id="error-message"></li>
22+
<li class="mfa-error-message"></li>
2323
</ul>
2424
</div>
2525

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
ckan.module('-datavic-odp-auth-login-form', function ($){
2+
setTimeout(() => {
3+
const authLoginFormModule = ckan.module.registry["auth-login-form"];
4+
if (!authLoginFormModule) return;
5+
6+
const modulePrototype = authLoginFormModule.prototype;
7+
const originalShowError = modulePrototype._showError;
8+
9+
if (originalShowError) {
10+
modulePrototype._showError = function(message) {
11+
const errorMessages = {
12+
"Invalid reCAPTCHA": "CAPTCHA verification failed. Please try again.",
13+
"Invalid login or password": "Login failed. Incorrect username or password."
14+
};
15+
16+
message = errorMessages[message] || message;
17+
return originalShowError.call(this, message);
18+
};
19+
}
20+
}, 100);
21+
})

ckanext/datavic_odp_theme/webassets/webassets.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ datavic_odp_theme-js:
1212
- js/datavic-advanced-search.js
1313
- js/datavic-api-info.js
1414
- js/datavic-search-autocomplete.js
15+
- js/datavic-2fa-error-messages.js
1516
extra:
1617
preload:
1718
- base/main

0 commit comments

Comments
 (0)