From 34e0cb33ac4ef40d4244befa12953be61a281caf Mon Sep 17 00:00:00 2001 From: yacchin1205 <968739+yacchin1205@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:58:18 +0900 Subject: [PATCH 1/6] Make the JAIRO Cloud index picker searchable by full index path because repositories with many indices were hard to scroll through --- addons/weko/static/wekoNodeConfig.js | 40 +++++++++++-------- addons/weko/templates/weko_node_settings.mako | 5 ++- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/addons/weko/static/wekoNodeConfig.js b/addons/weko/static/wekoNodeConfig.js index 7b1fcd0a430..5e34a8314b0 100644 --- a/addons/weko/static/wekoNodeConfig.js +++ b/addons/weko/static/wekoNodeConfig.js @@ -8,6 +8,7 @@ var bootbox = require('bootbox'); var Raven = require('raven-js'); var $osf = require('js/osfHelpers'); +require('js/rdmSelect2'); var $modal = $('#wekoInputCredentials'); @@ -29,31 +30,38 @@ function _getIndexById(indices, id) { return null; } -function _getIndexDisplayTitle(index, level) { - if (level === 0) { - return index.title; - } - var prefix = ''; - for (var i = 0; i < level; i ++) { - prefix += ' '; - } - return prefix + '- ' + index.title; -} - -function _flattenIndices(indices, level) { +function _flattenIndices(indices, ancestorTitles) { const r = []; indices.forEach(function(data) { - const displayTitle = _getIndexDisplayTitle(data, level); + const titles = ancestorTitles.concat([data.title]); r.push(Object.assign({ - displayTitle: displayTitle, + displayTitle: titles.join(' / '), }, data)); - _flattenIndices(data.children, level + 1).forEach(function(child) { + _flattenIndices(data.children, titles).forEach(function(child) { r.push(child); }); }); return r; } +ko.bindingHandlers.wekoIndexSelect2 = { + // select2 mirrors the keeps form-control because existing selectors rely on it; + // copied onto the select2 box it would draw a second border + adaptContainerCssClass: function() { + return null; + } + }); + }, + update: function(element, valueAccessor) { + $(element).select2('val', ko.unwrap(valueAccessor())); + } +}; + function ViewModel(url) { var self = this; @@ -172,7 +180,7 @@ function ViewModel(url) { }); self.flattenIndices = ko.pureComputed(function() { - return _flattenIndices(self.indices(), 0); + return _flattenIndices(self.indices(), []); }); // Flashed messages diff --git a/addons/weko/templates/weko_node_settings.mako b/addons/weko/templates/weko_node_settings.mako index 1f40c270815..0d92208468c 100644 --- a/addons/weko/templates/weko_node_settings.mako +++ b/addons/weko/templates/weko_node_settings.mako @@ -59,13 +59,14 @@
-
+
${_("Index:")}
From 6bf603431e63d57fa898dd1e2c6bd5b0823f2728 Mon Sep 17 00:00:00 2001 From: yacchin1205 <968739+yacchin1205@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:58:24 +0900 Subject: [PATCH 2/6] Show the JAIRO Cloud index save confirmation, which threw on folder helpers this view model never had --- addons/weko/static/wekoNodeConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/weko/static/wekoNodeConfig.js b/addons/weko/static/wekoNodeConfig.js index 5e34a8314b0..f0f3fc4ab98 100644 --- a/addons/weko/static/wekoNodeConfig.js +++ b/addons/weko/static/wekoNodeConfig.js @@ -126,7 +126,7 @@ function ViewModel(url) { setInfoSuccess: ko.pureComputed(function() { var filesUrl = window.contextVars.node.urls.web + 'files/'; return sprintf(_('Successfully linked index "%1$s". Go to the Files page to view your content.'), - $osf.htmlEscape(self.options.decodeFolder(self.folder().name)), filesUrl); + $osf.htmlEscape(self.savedIndexTitle()), filesUrl); }), setIndexError: ko.pureComputed(function() { return sprintf(_('Could not connect to this index. Please refresh the page or ' + From dc29e79acc0ebb0c9321b30e587e34b273fb88e8 Mon Sep 17 00:00:00 2001 From: yacchin1205 <968739+yacchin1205@users.noreply.github.com> Date: Mon, 5 Oct 2026 13:58:33 +0900 Subject: [PATCH 3/6] Remove the unused connectAccount from the JAIRO Cloud node config, which referenced an undefined message --- addons/weko/static/wekoNodeConfig.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/addons/weko/static/wekoNodeConfig.js b/addons/weko/static/wekoNodeConfig.js index f0f3fc4ab98..ce0df78fd8f 100644 --- a/addons/weko/static/wekoNodeConfig.js +++ b/addons/weko/static/wekoNodeConfig.js @@ -345,20 +345,6 @@ ViewModel.prototype.onImportError = function(xhr, status, error) { }); }; -/** - * Allows a user to create an access token from the nodeSettings page - */ -ViewModel.prototype.connectAccount = function() { - var self = this; - - window.oauthComplete = function(res) { - // Update view model based on response - self.changeMessage(self.messages.connectAccountSuccess(), 'text-success', 3000); - self.importAuth.call(self); - }; - window.open(self.urls().auth); -}; - ViewModel.prototype.connectExistingAccount = function(account_id) { var self = this; From 787e30e965c174cd17991564fb8170353b95fc06 Mon Sep 17 00:00:00 2001 From: yacchin1205 <968739+yacchin1205@users.noreply.github.com> Date: Mon, 5 Oct 2026 14:38:45 +0900 Subject: [PATCH 4/6] Expand the children WEKO hides behind an index's more entry so every index can be found and deposited to --- addons/weko/client.py | 27 ++++++++++++++++- addons/weko/tests/test_client.py | 50 ++++++++++++++++++++++++++++++++ addons/weko/tests/utils.py | 3 -- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/addons/weko/client.py b/addons/weko/client.py index f7b00fcf459..84bd0c374af 100644 --- a/addons/weko/client.py +++ b/addons/weko/client.py @@ -15,6 +15,19 @@ def _flatten_indices(indices): return r +def _get_truncated_index_ids(descs): + # WEKO replaces the children beyond an index's display number with a "more" entry + # unless the index is listed in more_ids + r = [] + for desc in descs: + if 'children' not in desc: + continue + if any(child['id'] == 'more' for child in desc['children']): + r.append(str(desc['id'])) + r += _get_truncated_index_ids(desc['children']) + return r + + def _is_valid_index(desc): if 'name' in desc and 'id' in desc: return True @@ -62,7 +75,19 @@ def get_indices(self): """ Get all indices from the WEKO. """ - root = self._get('api/tree?action=browsing') + more_ids = [] + while True: + path = 'api/tree?action=browsing' + if more_ids: + path += '&more_ids=' + '/'.join(more_ids) + root = self._get(path) + truncated_ids = _get_truncated_index_ids(root) + if not truncated_ids: + break + unexpanded_ids = set(truncated_ids) & set(more_ids) + if unexpanded_ids: + raise ValueError(f'WEKO did not expand indices: {unexpanded_ids}') + more_ids += truncated_ids indices = [] for desc in root: if not _is_valid_index(desc): diff --git a/addons/weko/tests/test_client.py b/addons/weko/tests/test_client.py index d616196ec50..f1e600e644b 100644 --- a/addons/weko/tests/test_client.py +++ b/addons/weko/tests/test_client.py @@ -19,6 +19,36 @@ def mock_requests_get(url, **kwargs): return utils.mock_response_404 +def _fake_truncated_indices(expanded_ids): + grandchildren = [{'id': '300', 'name': 'Grandchild 1', 'children': []}] + if '201' in expanded_ids: + grandchildren.append({'id': '301', 'name': 'Grandchild 2', 'children': []}) + else: + grandchildren.append({'id': 'more'}) + children = [{'id': '200', 'name': 'Child 1', 'children': []}] + if '100' in expanded_ids: + children.append({'id': '201', 'name': 'Child 2', 'children': grandchildren}) + else: + children.append({'id': 'more'}) + return [{'id': '100', 'name': 'Sample Index', 'children': children}] + + +def mock_requests_get_truncated(url, **kwargs): + prefix = 'https://test.sample.nii.ac.jp/api/tree?action=browsing' + if url == prefix: + return utils.MockResponse(_fake_truncated_indices([]), 200) + if url.startswith(prefix + '&more_ids='): + expanded_ids = url[len(prefix + '&more_ids='):].split('/') + return utils.MockResponse(_fake_truncated_indices(expanded_ids), 200) + return utils.mock_response_404 + + +def mock_requests_get_never_expanded(url, **kwargs): + if url.startswith('https://test.sample.nii.ac.jp/api/tree?action=browsing'): + return utils.MockResponse(_fake_truncated_indices([]), 200) + return utils.mock_response_404 + + class TestWEKOClient(OsfTestCase): def setUp(self): self.host = utils.fake_weko_host @@ -35,6 +65,26 @@ def test_weko_get_indices(self, get_req_mock): assert_equal(indices[0].title, 'Sample Index') assert_equal(indices[0].identifier, 100) + @mock.patch('requests.get', side_effect=mock_requests_get_truncated) + def test_weko_get_indices_expands_truncated_children(self, get_req_mock): + indices = self.conn.get_indices() + + titles = [i.title for i in client._flatten_indices(indices)] + assert_equal(titles, ['Sample Index', 'Child 1', 'Child 2', 'Grandchild 1', 'Grandchild 2']) + assert_equal( + [c[0][0] for c in get_req_mock.call_args_list], + [ + 'https://test.sample.nii.ac.jp/api/tree?action=browsing', + 'https://test.sample.nii.ac.jp/api/tree?action=browsing&more_ids=100', + 'https://test.sample.nii.ac.jp/api/tree?action=browsing&more_ids=100/201', + ] + ) + + @mock.patch('requests.get', side_effect=mock_requests_get_never_expanded) + def test_weko_get_indices_fails_when_weko_keeps_children_truncated(self, get_req_mock): + with assert_raises(ValueError): + self.conn.get_indices() + @mock.patch('requests.get', side_effect=mock_requests_get) def test_weko_get_index_by_id(self, get_req_mock): index = self.conn.get_index_by_id(100) diff --git a/addons/weko/tests/utils.py b/addons/weko/tests/utils.py index 076064d897a..f04a75ae656 100644 --- a/addons/weko/tests/utils.py +++ b/addons/weko/tests/utils.py @@ -11,9 +11,6 @@ 'id': 100, 'name': 'Sample Index', 'children': [ - { - 'id': 'more', - }, { 'id': 'dummy', } From 5ff320b825639acc41d47cf028cb8c65789569bf Mon Sep 17 00:00:00 2001 From: yacchin1205 <968739+yacchin1205@users.noreply.github.com> Date: Mon, 5 Oct 2026 17:34:06 +0900 Subject: [PATCH 5/6] Show each JAIRO Cloud index's ID in the picker and match it in search because index names are not unique --- addons/weko/static/wekoNodeConfig.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/weko/static/wekoNodeConfig.js b/addons/weko/static/wekoNodeConfig.js index ce0df78fd8f..36965c5b6c0 100644 --- a/addons/weko/static/wekoNodeConfig.js +++ b/addons/weko/static/wekoNodeConfig.js @@ -44,16 +44,33 @@ function _flattenIndices(indices, ancestorTitles) { return r; } +function _withIndexId(title, id) { + return title + ' (ID:' + id + ')'; +} + ko.bindingHandlers.wekoIndexSelect2 = { // select2 mirrors the relies on it $(element).select2({ width: '100%', // The , so the element must be up to date first after: ['options', 'value'], - init: function(element) { + init: function(element, valueAccessor) { const defaults = $.fn.select2.defaults; - // The ID is shown and matched here; the option text stays the plain path - // because label-based selection of the relies on it $(element).select2({ width: '100%', // The
diff --git a/addons/weko/tests/test_client.py b/addons/weko/tests/test_client.py index f1e600e644b..a624e293adc 100644 --- a/addons/weko/tests/test_client.py +++ b/addons/weko/tests/test_client.py @@ -85,6 +85,17 @@ def test_weko_get_indices_fails_when_weko_keeps_children_truncated(self, get_req with assert_raises(ValueError): self.conn.get_indices() + @mock.patch('requests.get', side_effect=mock_requests_get) + def test_weko_get_indices_asks_for_the_given_language(self, get_req_mock): + self.conn.get_indices(accept_language='ja') + + get_req_mock.assert_called_once_with( + 'https://test.sample.nii.ac.jp/api/tree?action=browsing', + auth=(None, None), + headers={'Accept-Language': 'ja'}, + timeout=client.DEFAULT_TIMEOUT, + ) + @mock.patch('requests.get', side_effect=mock_requests_get) def test_weko_get_index_by_id(self, get_req_mock): index = self.conn.get_index_by_id(100) diff --git a/addons/weko/tests/test_serializer.py b/addons/weko/tests/test_serializer.py index 51d56e40930..6fbf5e5a1fe 100644 --- a/addons/weko/tests/test_serializer.py +++ b/addons/weko/tests/test_serializer.py @@ -82,6 +82,42 @@ def test_serialize_settings_authorized(self): assert_equal(serialized['ownerName'], self.user_settings.owner.fullname) assert_in('savedIndex', serialized) + def test_serialize_settings_names_indices_in_the_page_language(self): + def requests_get(url, **kwargs): + if kwargs.get('headers', {}).get('Accept-Language') == 'ja': + return utils.MockResponse([{'id': 100, 'name': 'サンプルインデックス'}], 200) + return utils.MockResponse([{'id': 100, 'name': 'Sample Index'}], 200) + + with mock.patch.object(type(self.node_settings), 'has_auth', return_value=True), \ + mock.patch.object(WEKOSerializer, 'credentials_are_valid', return_value=True), \ + mock.patch('requests.get', side_effect=requests_get), \ + self.app.app.test_request_context(headers={'Accept-Language': 'ja-JP'}): + serialized = self.ser.serialize_settings(self.node_settings, self.user, self.client) + + assert_equal(serialized['indices'], [{ + 'title': 'Sample Index', + 'localizedTitle': 'サンプルインデックス', + 'id': 100, + 'children': [], + }]) + + def test_serialize_settings_falls_back_to_the_default_name_and_warns(self): + def requests_get(url, **kwargs): + if kwargs.get('headers', {}).get('Accept-Language') == 'ja': + return utils.MockResponse([{'id': 100, 'name': 'サンプルインデックス'}], 200) + return utils.MockResponse([{'id': 100, 'name': 'Sample Index'}, {'id': 101, 'name': 'New Index'}], 200) + + with mock.patch.object(type(self.node_settings), 'has_auth', return_value=True), \ + mock.patch.object(WEKOSerializer, 'credentials_are_valid', return_value=True), \ + mock.patch('requests.get', side_effect=requests_get), \ + self.app.app.test_request_context(headers={'Accept-Language': 'ja'}), \ + self.assertLogs('addons.weko.serializer', level='WARNING') as logs: + serialized = self.ser.serialize_settings(self.node_settings, self.user, self.client) + + assert_equal([i['localizedTitle'] for i in serialized['indices']], ['サンプルインデックス', 'New Index']) + assert_equal(len(logs.records), 1) + assert_in('101', logs.records[0].getMessage()) + def test_serialize_settings_authorized_folder_is_set(self): pass