iqvoc 4.14.5 → 4.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile +9 -16
- data/Gemfile.lock +313 -257
- data/README.md +1 -1
- data/app/aides/entity_logger.rb +27 -0
- data/app/aides/maker.rb +1 -1
- data/app/aides/origin.rb +1 -1
- data/app/aides/{rdfapi.rb → rdf_api.rb} +1 -1
- data/app/aides/skos_importer.rb +1 -1
- data/app/assets/javascripts/iqvoc/iqvoc.js +4 -3
- data/app/assets/javascripts/iqvoc/treeview.js +3 -3
- data/app/assets/stylesheets/_framework.scss +1 -1
- data/app/controllers/collections/alphabetical_controller.rb +75 -0
- data/app/controllers/collections/expired_controller.rb +37 -0
- data/app/controllers/collections_controller.rb +22 -28
- data/app/controllers/concepts/alphabetical_controller.rb +4 -6
- data/app/controllers/concepts/expired_controller.rb +1 -1
- data/app/controllers/concepts/hierarchical_controller.rb +18 -15
- data/app/controllers/concepts_controller.rb +0 -3
- data/app/controllers/concepts_movement_controller.rb +1 -1
- data/app/controllers/concerns/controller_extensions.rb +15 -0
- data/app/controllers/dashboard_controller.rb +1 -1
- data/app/controllers/exports_controller.rb +17 -8
- data/app/controllers/rdf_controller.rb +0 -2
- data/app/controllers/remote_labels_controller.rb +0 -2
- data/app/controllers/search_results_controller.rb +59 -43
- data/app/helpers/concepts_helper.rb +2 -2
- data/app/helpers/navigation_helper.rb +7 -7
- data/app/helpers/rdf_helper.rb +2 -0
- data/app/jobs/export_job.rb +4 -7
- data/app/models/collection/base.rb +6 -8
- data/app/models/collection/member/skos/base.rb +2 -2
- data/app/models/collection/skos/base.rb +1 -1
- data/app/models/collection/skos/unordered.rb +1 -1
- data/app/models/collection/unordered.rb +2 -2
- data/app/models/concept/base.rb +2 -10
- data/app/models/concept/relation/base.rb +1 -1
- data/app/models/concept/relation/skos/base.rb +2 -2
- data/app/models/concept/relation/skos/broader/base.rb +2 -2
- data/app/models/concept/relation/skos/broader/mono.rb +1 -1
- data/app/models/concept/relation/skos/broader/poly.rb +1 -1
- data/app/models/concept/relation/skos/narrower/base.rb +1 -1
- data/app/models/concept/relation/skos/related.rb +1 -1
- data/app/models/concept/skos/base.rb +1 -1
- data/app/models/concept/skos/scheme.rb +2 -2
- data/app/models/concept/validations.rb +14 -0
- data/app/models/concerns/expirable.rb +14 -0
- data/app/models/export.rb +21 -9
- data/app/models/label/base.rb +19 -1
- data/app/models/label/skos/base.rb +1 -1
- data/app/models/labeling/base.rb +12 -0
- data/app/models/labeling/skos/alt_label.rb +1 -1
- data/app/models/labeling/skos/base.rb +4 -4
- data/app/models/labeling/skos/hidden_label.rb +1 -1
- data/app/models/labeling/skos/pref_label.rb +1 -1
- data/app/models/match/skos/base.rb +3 -3
- data/app/models/match/skos/broad_match.rb +2 -2
- data/app/models/match/skos/close_match.rb +1 -1
- data/app/models/match/skos/exact_match.rb +1 -1
- data/app/models/match/skos/mapping_relation.rb +1 -1
- data/app/models/match/skos/narrow_match.rb +2 -2
- data/app/models/match/skos/related_match.rb +1 -1
- data/app/models/note/base.rb +2 -2
- data/app/models/note/rdfs/see_also.rb +1 -1
- data/app/models/note/skos/base.rb +4 -4
- data/app/models/note/skos/change_note.rb +2 -2
- data/app/models/note/skos/definition.rb +1 -1
- data/app/models/note/skos/editorial_note.rb +1 -1
- data/app/models/note/skos/example.rb +1 -1
- data/app/models/note/skos/history_note.rb +1 -1
- data/app/models/note/skos/scope_note.rb +1 -1
- data/app/presenters/alphabetical_search_result.rb +2 -2
- data/app/services/rdf_sync_service.rb +1 -1
- data/app/uploaders/base.rb +4 -3
- data/app/view_models/concept_view.rb +1 -1
- data/app/views/collections/_data.html.erb +1 -1
- data/app/views/collections/_form.html.erb +14 -2
- data/app/views/collections/alphabetical/_search_result.html.erb +17 -0
- data/app/views/collections/alphabetical/_search_result_remote.html.erb +14 -0
- data/app/views/collections/alphabetical/index.html.erb +23 -0
- data/app/views/collections/edit.html.erb +1 -1
- data/app/views/collections/expired/index.html.erb +23 -0
- data/app/views/collections/index.html.erb +1 -1
- data/app/views/collections/new.html.erb +1 -1
- data/app/views/collections/show_published.html.erb +1 -1
- data/app/views/collections/show_unpublished.html.erb +1 -1
- data/app/views/collections/sidebars/_plural.html.erb +28 -0
- data/app/views/collections/sidebars/_singular.html.erb +22 -0
- data/app/views/concepts/_form.html.erb +1 -1
- data/app/views/concepts/alphabetical/index.html.erb +1 -1
- data/app/views/concepts/expired/index.html.erb +1 -1
- data/app/views/concepts/glance.html.erb +1 -1
- data/app/views/concepts/scheme/edit.html.erb +1 -1
- data/app/views/concepts/sidebars/_plural.html.erb +1 -1
- data/app/views/concepts/sidebars/_singular.html.erb +1 -1
- data/app/views/dashboard/glance.html.erb +1 -1
- data/app/views/exports/index.html.erb +5 -1
- data/app/views/exports/show.html.erb +1 -1
- data/app/views/partials/collection/_inline_base.html.erb +4 -0
- data/app/views/search_results/_sidebar.html.erb +1 -1
- data/config/application.rb +7 -2
- data/config/ci.rb +20 -0
- data/config/database.yml +15 -21
- data/config/database.yml.postgresql +13 -20
- data/config/engine.rb +1 -0
- data/config/environments/development.rb +1 -1
- data/config/environments/production.rb +1 -1
- data/config/environments/test.rb +1 -1
- data/config/initializers/content_security_policy.rb +6 -2
- data/config/initializers/filter_parameter_logging.rb +4 -4
- data/config/initializers/new_framework_defaults_7_1.rb +280 -0
- data/config/initializers/new_framework_defaults_8_0.rb +30 -0
- data/config/initializers/new_framework_defaults_8_1.rb +74 -0
- data/config/initializers/permissions_policy.rb +11 -9
- data/config/initializers/zeitwerk.rb +1 -3
- data/config/locales/de.yml +4 -1
- data/config/locales/en.yml +4 -1
- data/config/locales/pt.yml +2 -1
- data/config/puma.rb +34 -35
- data/config/routes.rb +3 -1
- data/db/migrate/20110510162719_use_mono_hierarchy_instead_of_poly_hierarchy.rb +2 -2
- data/db/migrate/20130227145825_fix_collection_type.rb +3 -3
- data/db/migrate/20130502151221_fix_collection_member_types.rb +1 -1
- data/db/migrate/20250218160045_adapt_zeitwerk_naming_to_iqvoc.rb +25 -0
- data/db/migrate/20250326182601_adapt_zeitwerk_skos_naming_to_instance_configuration.rb +11 -0
- data/db/schema.rb +100 -1
- data/iqvoc.gemspec +4 -4
- data/lib/iqvoc/configuration/collection.rb +16 -4
- data/lib/iqvoc/configuration/concept.rb +22 -18
- data/lib/iqvoc/configuration/core.rb +9 -23
- data/lib/iqvoc/configuration/label.rb +1 -1
- data/lib/iqvoc/environments/development.rb +62 -57
- data/lib/iqvoc/environments/production.rb +70 -67
- data/lib/iqvoc/environments/test.rb +44 -38
- data/lib/iqvoc/version.rb +1 -1
- data/test/controllers/concepts_movement_controller_test.rb +14 -14
- data/test/controllers/hierarchy_test.rb +0 -1
- data/test/controllers/reverse_match_test.rb +6 -6
- data/test/integration/alphabetical_test.rb +5 -5
- data/test/integration/browse_concepts_and_labels_test.rb +4 -4
- data/test/integration/client_edit_concept_test.rb +1 -1
- data/test/integration/collection_browsing_test.rb +2 -2
- data/test/integration/collection_circularity_test.rb +10 -10
- data/test/integration/collection_test.rb +79 -0
- data/test/integration/concept_browsing_test.rb +3 -3
- data/test/integration/concept_collection_assignment_test.rb +4 -4
- data/test/integration/concept_scheme_browsing_test.rb +7 -7
- data/test/integration/edit_collections_test.rb +1 -1
- data/test/integration/edit_concepts_test.rb +2 -2
- data/test/integration/instance_configuration_browsing_test.rb +1 -1
- data/test/integration/note_annotations_test.rb +2 -2
- data/test/integration/reverse_match_job_test.rb +9 -9
- data/test/integration/search_test.rb +20 -10
- data/test/integration/tree_test.rb +5 -5
- data/test/integration/untranslated_test.rb +2 -2
- data/test/integration_test_helper.rb +7 -0
- data/test/models/concept_scheme_test.rb +6 -6
- data/test/models/concept_test.rb +38 -38
- data/test/models/deep_cloning_test.rb +6 -6
- data/test/models/note_test.rb +4 -4
- data/test/models/rdf_sync_test.rb +1 -1
- data/test/models/rdfapi_test.rb +24 -24
- data/test/models/skos_collection_import_test.rb +3 -3
- data/test/models/skos_import_test.rb +4 -4
- metadata +44 -32
- data/app/views/collections/_sidebar.html.erb +0 -20
- data/config/environments/heroku.rb +0 -81
- data/config/initializers/heroku.rb +0 -24
- data/config/secrets.yml +0 -35
- /data/{app/helpers → lib}/iqvoc_module_helper.rb +0 -0
- /data/{app/models/concerns → lib}/search_extension.rb +0 -0
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# iQvoc
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/iqvoc)
|
|
4
|
-

|
|
5
5
|
[](https://codeclimate.com/github/innoq/iqvoc)
|
|
6
6
|
|
|
7
7
|
iQvoc is a vocabulary management tool that combines easy-to-use human interfaces
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EntityLogger
|
|
4
|
+
def initialize(object)
|
|
5
|
+
@object = object
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def info(message)
|
|
9
|
+
append_log(message)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def warn(message)
|
|
13
|
+
append_log("[WARNING] #{message}")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def error(message)
|
|
17
|
+
append_log("[ERROR] #{message}")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def append_log(message)
|
|
23
|
+
@object.output ||= ""
|
|
24
|
+
@object.output += "#{Time.now} - #{message}\n"
|
|
25
|
+
@object.save!(touch: false)
|
|
26
|
+
end
|
|
27
|
+
end
|
data/app/aides/maker.rb
CHANGED
|
@@ -120,7 +120,7 @@ module Maker
|
|
|
120
120
|
}
|
|
121
121
|
attributes = defaults.merge(attributes)
|
|
122
122
|
|
|
123
|
-
klass = Iqvoc::
|
|
123
|
+
klass = Iqvoc::Xllabel rescue Iqvoc::Label # FIXME: breaks encapsulation (hard-coded iqvoc_skosxl dependency)
|
|
124
124
|
label = klass.base_class.create!(attributes)
|
|
125
125
|
|
|
126
126
|
inflectionals.each { |inf|
|
data/app/aides/origin.rb
CHANGED
|
@@ -25,7 +25,7 @@ class Origin
|
|
|
25
25
|
def valid?
|
|
26
26
|
valid = true
|
|
27
27
|
|
|
28
|
-
if blank_node = initial_value.match(
|
|
28
|
+
if blank_node = initial_value.match(RdfApi::BLANK_NODE_REGEXP)
|
|
29
29
|
# blank node validation, should not contain special chars
|
|
30
30
|
valid = false if CGI.escape(blank_node[1]) != blank_node[1]
|
|
31
31
|
else
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# See the License for the specific language governing permissions and
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class RdfApi
|
|
18
18
|
FIRST_LEVEL_OBJECT_CLASSES = [Iqvoc::Concept.base_class, Iqvoc::Collection.base_class]
|
|
19
19
|
SECOND_LEVEL_OBJECT_CLASSES = Iqvoc::Concept.labeling_classes.keys +
|
|
20
20
|
Iqvoc::Concept.note_classes +
|
data/app/aides/skos_importer.rb
CHANGED
|
@@ -84,11 +84,12 @@ jQuery(document).ready(function($) {
|
|
|
84
84
|
|
|
85
85
|
$('.datepicker').datepicker({
|
|
86
86
|
autoclose: true,
|
|
87
|
-
todayHighlight: true,
|
|
88
|
-
todayBtn: 'linked',
|
|
89
87
|
clearBtn: true,
|
|
90
88
|
format: "yyyy-mm-dd",
|
|
91
|
-
|
|
89
|
+
keyboardNavigation: false,
|
|
90
|
+
language: locale,
|
|
91
|
+
todayHighlight: true,
|
|
92
|
+
todayBtn: 'linked',
|
|
92
93
|
});
|
|
93
94
|
|
|
94
95
|
//$("tr.highlightable").click(function(ev) {
|
|
@@ -96,9 +96,9 @@ import 'jqtree/tree.jquery.js';
|
|
|
96
96
|
'glance-url': node.glance_url
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
var saveButton = $('<button type="button" class="btn btn-primary btn-
|
|
100
|
-
var copyButton = $('<button type="button" class="btn btn-primary btn-
|
|
101
|
-
var undoButton = $('<button type="button" class="btn btn-primary btn-
|
|
99
|
+
var saveButton = $('<button type="button" class="btn btn-primary btn-sm node-btn" data-tree-action="move"><i class="fa fa-save"></i> ' + saveLabel + '</button>');
|
|
100
|
+
var copyButton = $('<button type="button" class="btn btn-primary btn-sm node-btn" data-tree-action="copy"><i class="fa fa-copy"></i> ' + copyLabel + '</button>');
|
|
101
|
+
var undoButton = $('<button type="button" class="btn btn-primary btn-sm reset-node-btn"><i class="fa fa-undo"></i> ' + undoLabel + '</button>');
|
|
102
102
|
|
|
103
103
|
// add icon only to the first element of the collection.
|
|
104
104
|
// the second one could be a nodelist for parents nodes.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
# Copyright 2011-2025 innoQ Deutschland GmbH
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
class Collections::AlphabeticalController < CollectionsController
|
|
18
|
+
include DatasetInitialization
|
|
19
|
+
|
|
20
|
+
def index
|
|
21
|
+
authorize! :read, Concept::Base
|
|
22
|
+
|
|
23
|
+
# only initialize dataset if dataset param is set
|
|
24
|
+
# prevent obsolet http request when using matches widget
|
|
25
|
+
datasets = params[:dataset] ? init_datasets : []
|
|
26
|
+
|
|
27
|
+
identify_used_first_letters
|
|
28
|
+
|
|
29
|
+
if dataset = datasets.detect { |dataset| dataset.name == params[:dataset] }
|
|
30
|
+
query = params[:prefix].to_s.downcase
|
|
31
|
+
@search_results = dataset.alphabetical_search(query, I18n.locale) || []
|
|
32
|
+
@search_results = Kaminari.paginate_array(@search_results).page(params[:page])
|
|
33
|
+
else
|
|
34
|
+
# When in single query mode, AR handles ALL includes to be loaded by that
|
|
35
|
+
# one query. We don't want that! So let's do it manually :-)
|
|
36
|
+
includes = Iqvoc::Collection.base_class.default_includes
|
|
37
|
+
if Iqvoc::Collection.note_classes.include?(Note::Skos::Definition)
|
|
38
|
+
includes << Note::Skos::Definition.name.to_relation_name
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
search_results_size = find_labelings.count
|
|
42
|
+
search_results = find_labelings.page(params[:page])
|
|
43
|
+
Iqvoc::Collection.pref_labeling_class.preload(search_results, owner: includes)
|
|
44
|
+
|
|
45
|
+
@search_results = search_results.to_a.map { |pl| AlphabeticalSearchResult.new(pl) }
|
|
46
|
+
@search_results = Kaminari.paginate_array(@search_results, total_count: search_results_size).page(params[:page])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
respond_to do |format|
|
|
50
|
+
format.html { render :index, layout: with_layout? }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
protected
|
|
55
|
+
|
|
56
|
+
def identify_used_first_letters
|
|
57
|
+
@letters = Label::Base.where("#{Label::Base.table_name}.language = ?", I18n.locale).joins(:pref_labeled_collections).where("concepts.published_at IS NOT NULL").where("concepts.expired_at IS NULL OR concepts.expired_at >= ?", Time.now).where("concepts.type = ?", Iqvoc::Collection.base_class_name).select("DISTINCT UPPER(SUBSTR(value, 1, 1)) AS letter").order("letter").map(&:letter)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def find_labelings
|
|
61
|
+
letter = (@letters.include?('A')) ? 'a' : @letters.first
|
|
62
|
+
query = (params[:prefix] || letter)&.to_s&.downcase
|
|
63
|
+
|
|
64
|
+
Iqvoc::Collection.pref_labeling_class
|
|
65
|
+
.collection_published
|
|
66
|
+
.collection_not_expired
|
|
67
|
+
.label_begins_with(query)
|
|
68
|
+
.by_label_language(I18n.locale)
|
|
69
|
+
.includes(:target)
|
|
70
|
+
.order(Arel.sql("LOWER(#{Label::Base.table_name}.value)"))
|
|
71
|
+
.joins(:owner)
|
|
72
|
+
.where(concepts: { type: Iqvoc::Collection.base_class_name })
|
|
73
|
+
.references(:collections, :labels, :labelings)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
# Copyright 2011-2025 innoQ Deutschland GmbH
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
class Collections::ExpiredController < Collections::AlphabeticalController
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
def identify_used_first_letters
|
|
21
|
+
@letters = Label::Base.where("#{Label::Base.table_name}.language = ?", I18n.locale).joins(:pref_labeled_collections).where("concepts.expired_at < ?", Time.now).where("concepts.type = ?", Iqvoc::Collection.base_class_name).select("DISTINCT UPPER(SUBSTR(value, 1, 1)) AS letter").order("letter").map(&:letter)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def find_labelings
|
|
25
|
+
query = (params[:prefix] || @letters.first || 'a').to_s.downcase
|
|
26
|
+
|
|
27
|
+
Iqvoc::Collection.pref_labeling_class
|
|
28
|
+
.collection_expired
|
|
29
|
+
.label_begins_with(query)
|
|
30
|
+
.by_label_language(I18n.locale)
|
|
31
|
+
.includes(:target)
|
|
32
|
+
.order(Arel.sql("LOWER(#{Label::Base.table_name}.value)"))
|
|
33
|
+
.joins(:owner)
|
|
34
|
+
.where(concepts: { type: Iqvoc::Collection.base_class_name })
|
|
35
|
+
.references(:collections, :labels, :labelings)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -18,14 +18,18 @@ class CollectionsController < ApplicationController
|
|
|
18
18
|
def index
|
|
19
19
|
authorize! :read, Iqvoc::Collection.base_class
|
|
20
20
|
|
|
21
|
+
scope = Iqvoc::Collection.base_class
|
|
22
|
+
.with_pref_labels
|
|
23
|
+
.published
|
|
24
|
+
.not_expired
|
|
25
|
+
|
|
21
26
|
respond_to do |format|
|
|
22
27
|
format.html do
|
|
23
|
-
@top_collections = Iqvoc::Collection.base_class.with_pref_labels.published
|
|
24
28
|
@top_collections = if params[:root].present?
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
scope.by_parent_id(params[:root])
|
|
30
|
+
else
|
|
31
|
+
scope.tops
|
|
32
|
+
end
|
|
29
33
|
|
|
30
34
|
@top_collections.to_a.sort! { |a, b| a.pref_label.to_s <=> b.pref_label.to_s }
|
|
31
35
|
|
|
@@ -33,30 +37,22 @@ class CollectionsController < ApplicationController
|
|
|
33
37
|
end
|
|
34
38
|
format.json do # For the widget and treeview
|
|
35
39
|
response = if params[:root].present?
|
|
36
|
-
collections =
|
|
37
|
-
|
|
38
|
-
.published
|
|
39
|
-
.by_parent_id(params[:root])
|
|
40
|
-
.sort_by { |c| c.pref_label.to_s }
|
|
40
|
+
collections = scope.by_parent_id(params[:root])
|
|
41
|
+
.sort_by { |c| c.pref_label.to_s }
|
|
41
42
|
|
|
42
43
|
collections.map do |collection|
|
|
43
|
-
|
|
44
|
+
{
|
|
44
45
|
id: collection.id,
|
|
45
46
|
url: collection_path(id: collection, format: :html),
|
|
46
47
|
name: CGI.escapeHTML(collection.pref_label.to_s),
|
|
47
|
-
load_on_demand: collection.subcollections.any
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
res
|
|
48
|
+
load_on_demand: collection.subcollections.any?,
|
|
49
|
+
additionalText: collection.additional_info&.then { |info| " (#{info})" }
|
|
50
|
+
}.compact
|
|
52
51
|
end
|
|
53
52
|
else
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.merge(Label::Base.by_query_value("#{params[:query]}%"))
|
|
58
|
-
.sort_by { |c| c.pref_label.to_s }
|
|
59
|
-
.map { |c| collection_widget_data(c) }
|
|
53
|
+
scope.merge(Label::Base.by_query_value("#{params[:query]}%"))
|
|
54
|
+
.sort_by { |c| c.pref_label.to_s }
|
|
55
|
+
.map { |c| collection_widget_data(c) }
|
|
60
56
|
end
|
|
61
57
|
render json: response
|
|
62
58
|
end
|
|
@@ -99,9 +95,7 @@ class CollectionsController < ApplicationController
|
|
|
99
95
|
def create
|
|
100
96
|
authorize! :create, Iqvoc::Collection.base_class
|
|
101
97
|
|
|
102
|
-
@collection = Iqvoc::Collection.base_class.new(
|
|
103
|
-
|
|
104
|
-
@collection.lock_by_user(current_user.id)
|
|
98
|
+
@collection = Iqvoc::Collection.base_class.new(collection_params)
|
|
105
99
|
|
|
106
100
|
if @collection.save
|
|
107
101
|
flash[:success] = I18n.t('txt.controllers.collections.save.success')
|
|
@@ -130,9 +124,9 @@ class CollectionsController < ApplicationController
|
|
|
130
124
|
authorize! :update, @collection
|
|
131
125
|
|
|
132
126
|
# set to_review to false if someone edits a concepts
|
|
133
|
-
|
|
127
|
+
collection_params["to_review"] = "false"
|
|
134
128
|
|
|
135
|
-
if @collection.update(
|
|
129
|
+
if @collection.update(collection_params)
|
|
136
130
|
flash[:success] = I18n.t('txt.controllers.collections.save.success')
|
|
137
131
|
redirect_to collection_path(@collection, published: 0)
|
|
138
132
|
else
|
|
@@ -156,7 +150,7 @@ class CollectionsController < ApplicationController
|
|
|
156
150
|
|
|
157
151
|
private
|
|
158
152
|
|
|
159
|
-
def
|
|
153
|
+
def collection_params
|
|
160
154
|
params.require(:concept).permit!
|
|
161
155
|
end
|
|
162
156
|
|
|
@@ -14,8 +14,6 @@
|
|
|
14
14
|
# See the License for the specific language governing permissions and
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
|
|
17
|
-
require 'concerns/dataset_initialization'
|
|
18
|
-
|
|
19
17
|
class Concepts::AlphabeticalController < ConceptsController
|
|
20
18
|
include DatasetInitialization
|
|
21
19
|
|
|
@@ -29,15 +27,15 @@ class Concepts::AlphabeticalController < ConceptsController
|
|
|
29
27
|
identify_used_first_letters
|
|
30
28
|
|
|
31
29
|
if dataset = datasets.detect { |dataset| dataset.name == params[:dataset] }
|
|
32
|
-
query = params[:prefix].
|
|
30
|
+
query = params[:prefix].to_s.downcase
|
|
33
31
|
@search_results = dataset.alphabetical_search(query, I18n.locale) || []
|
|
34
32
|
@search_results = Kaminari.paginate_array(@search_results).page(params[:page])
|
|
35
33
|
else
|
|
36
34
|
# When in single query mode, AR handles ALL includes to be loaded by that
|
|
37
35
|
# one query. We don't want that! So let's do it manually :-)
|
|
38
36
|
includes = Iqvoc::Concept.base_class.default_includes
|
|
39
|
-
if Iqvoc::Concept.note_classes.include?(Note::
|
|
40
|
-
includes << Note::
|
|
37
|
+
if Iqvoc::Concept.note_classes.include?(Note::Skos::Definition)
|
|
38
|
+
includes << Note::Skos::Definition.name.to_relation_name
|
|
41
39
|
end
|
|
42
40
|
|
|
43
41
|
search_results_size = find_labelings.count
|
|
@@ -61,7 +59,7 @@ class Concepts::AlphabeticalController < ConceptsController
|
|
|
61
59
|
|
|
62
60
|
def find_labelings
|
|
63
61
|
letter = (@letters.include?('A')) ? 'a' : @letters.first
|
|
64
|
-
query = (params[:prefix] || letter)&.
|
|
62
|
+
query = (params[:prefix] || letter)&.to_s&.downcase
|
|
65
63
|
|
|
66
64
|
Iqvoc::Concept.pref_labeling_class
|
|
67
65
|
.concept_published
|
|
@@ -22,7 +22,7 @@ class Concepts::ExpiredController < Concepts::AlphabeticalController
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def find_labelings
|
|
25
|
-
query = (params[:prefix] || @letters.first || 'a').
|
|
25
|
+
query = (params[:prefix] || @letters.first || 'a').to_s.downcase
|
|
26
26
|
|
|
27
27
|
Iqvoc::Concept.pref_labeling_class
|
|
28
28
|
.concept_expired
|
|
@@ -36,21 +36,24 @@ class Concepts::HierarchicalController < ConceptsController
|
|
|
36
36
|
|
|
37
37
|
# if params[:broader] is given, the action is handling the reversed tree
|
|
38
38
|
root_id = params[:root]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
39
|
+
broader = params[:broader].present?
|
|
40
|
+
|
|
41
|
+
@concepts = if root_id && root_id =~ /\d+/
|
|
42
|
+
# NB: order matters; see the following `where`
|
|
43
|
+
if broader
|
|
44
|
+
scope = scope.includes(:narrower_relations, :broader_relations).references(:relations)
|
|
45
|
+
else
|
|
46
|
+
scope = scope.includes(:broader_relations, :narrower_relations).references(:relations)
|
|
47
|
+
end
|
|
48
|
+
scope.where(Concept::Relation::Base.arel_table[:target_id].eq(root_id))
|
|
49
|
+
else
|
|
50
|
+
if broader
|
|
51
|
+
scope.broader_tops.includes(:broader_relations).references(:concepts)
|
|
52
|
+
else
|
|
53
|
+
scope.tops.includes(:narrower_relations).references(:concepts)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
@concepts = @concepts.sort_by { |c| c.pref_label.to_s }
|
|
54
57
|
|
|
55
58
|
respond_to do |format|
|
|
56
59
|
format.html
|
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
# See the License for the specific language governing permissions and
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
|
|
17
|
-
require 'concerns/dataset_initialization'
|
|
18
|
-
require 'reverse_match_job'
|
|
19
|
-
|
|
20
17
|
class ConceptsController < ApplicationController
|
|
21
18
|
include DatasetInitialization
|
|
22
19
|
|
|
@@ -83,7 +83,7 @@ class ConceptsMovementController < ApplicationController
|
|
|
83
83
|
r.target = new_parent
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
Concept::Relation::
|
|
86
|
+
Concept::Relation::Skos::Narrower::Base.create! do |r|
|
|
87
87
|
r.owner = new_parent
|
|
88
88
|
r.target = moved_concept
|
|
89
89
|
end
|
|
@@ -16,10 +16,25 @@ module ControllerExtensions
|
|
|
16
16
|
rescue_from CanCan::AccessDenied, with: :handle_access_denied
|
|
17
17
|
rescue_from ActionController::ParameterMissing, with: :handle_bad_request
|
|
18
18
|
rescue_from ActionController::UnknownFormat, with: :handle_unknown_format
|
|
19
|
+
rescue_from ActionController::BadRequest, with: :handle_bad_request
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
protected
|
|
22
23
|
|
|
24
|
+
def append_info_to_payload(payload)
|
|
25
|
+
super
|
|
26
|
+
|
|
27
|
+
# not set by rails because we rescue exceptions using :handle_server_error
|
|
28
|
+
if @exception.present?
|
|
29
|
+
payload[:exception] = [@exception.class.name, @exception.message]
|
|
30
|
+
payload[:exception_object] = @exception
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if current_user.present?
|
|
34
|
+
payload[:user_id] = current_user.id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
23
38
|
def default_url_options(options = nil)
|
|
24
39
|
{ format: params[:format], lang: I18n.locale }.
|
|
25
40
|
merge(options || {})
|
|
@@ -69,7 +69,7 @@ class DashboardController < ApplicationController
|
|
|
69
69
|
object = objects.send(params[:published] == "1" ? 'published' : 'unpublished').first
|
|
70
70
|
|
|
71
71
|
@title = object.to_s
|
|
72
|
-
@editorial_notes = object.notes_for_class(Note::
|
|
72
|
+
@editorial_notes = object.notes_for_class(Note::Skos::EditorialNote)
|
|
73
73
|
|
|
74
74
|
@path = send(object.class_path, id: object, published: params[:published])
|
|
75
75
|
|
|
@@ -26,6 +26,7 @@ class ExportsController < ApplicationController
|
|
|
26
26
|
|
|
27
27
|
def show
|
|
28
28
|
@export = Export.find(params[:id])
|
|
29
|
+
flash[:warning] = t('txt.views.export.success') unless @export.finished_at?
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def create
|
|
@@ -45,17 +46,25 @@ class ExportsController < ApplicationController
|
|
|
45
46
|
redirect_to exports_path
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
def destroy
|
|
50
|
+
export = Export.find(params[:id])
|
|
51
|
+
if export.destroy
|
|
52
|
+
flash[:success] = t('txt.views.export.delete.success')
|
|
53
|
+
else
|
|
54
|
+
flash[:error] = t('txt.views.export.delete.error')
|
|
55
|
+
end
|
|
56
|
+
redirect_to exports_path
|
|
57
|
+
end
|
|
58
|
+
|
|
48
59
|
def download
|
|
49
60
|
export = Export.find(params[:export_id])
|
|
50
|
-
time = export.finished_at
|
|
61
|
+
time = export.finished_at&.strftime('%Y-%m-%d_%H-%M')
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
redirect_to exports_path
|
|
58
|
-
end
|
|
63
|
+
Rails.logger.debug("Try to serve export from: #{export.build_filename}")
|
|
64
|
+
send_file export.build_filename, filename: "export-#{time}.#{export.file_type}"
|
|
65
|
+
rescue ::ActionController::MissingFile => e
|
|
66
|
+
flash[:error] = t('txt.views.export.missing_file')
|
|
67
|
+
redirect_to exports_path
|
|
59
68
|
end
|
|
60
69
|
|
|
61
70
|
private
|
|
@@ -14,8 +14,6 @@
|
|
|
14
14
|
# See the License for the specific language governing permissions and
|
|
15
15
|
# limitations under the License.
|
|
16
16
|
|
|
17
|
-
require 'concerns/dataset_initialization'
|
|
18
|
-
|
|
19
17
|
class SearchResultsController < ApplicationController
|
|
20
18
|
include DatasetInitialization
|
|
21
19
|
|
|
@@ -92,55 +90,73 @@ class SearchResultsController < ApplicationController
|
|
|
92
90
|
@remote_result_collections = []
|
|
93
91
|
|
|
94
92
|
if params[:query]
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
93
|
+
if params[:query].blank? and request.format.to_sym.in? [:ttl, :nt, :rdf]
|
|
94
|
+
# support blank searches via web ui, but not for api requests
|
|
95
|
+
respond_to do |format|
|
|
96
|
+
format.any(:ttl, :rdf, :nt) do
|
|
97
|
+
head :bad_request
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
# Deal with language parameter patterns
|
|
102
|
+
languages = []
|
|
103
|
+
# Either "l[]=de&l[]=en" as well as "l=de,en" should be possible
|
|
104
|
+
if params[:languages].respond_to?(:each) && params[:languages].include?('none')
|
|
105
|
+
# Special treatment for the "nil language"
|
|
106
|
+
languages << nil
|
|
107
|
+
elsif params[:languages].respond_to?(:split)
|
|
108
|
+
languages = params[:languages].split(',')
|
|
109
|
+
end
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
# Ensure a valid class was selected
|
|
112
|
+
unless klass = Iqvoc.searchable_class_names.detect { |key, value| value == params[:type] }.try(:first)
|
|
113
|
+
raise "'#{params[:type]}' is not a searchable class! Must be one of " + Iqvoc.searchable_class_names.keys.join(', ')
|
|
114
|
+
end
|
|
115
|
+
klass = klass.constantize
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
@results = klass.single_query(params.merge({ languages: languages.flatten }))
|
|
118
|
+
.filter { |search_result| result_allowed?(search_result) }
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
120
|
+
if params[:limit] && Iqvoc.unlimited_search_results
|
|
121
|
+
@results = @results.per(params[:limit].to_i)
|
|
122
|
+
end
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
if params[:datasets] && datasets = @datasets.select { |a| params[:datasets].include?(a.name) }
|
|
125
|
+
@results = SearchResultCollection.new(@results)
|
|
126
|
+
datasets.each do |dataset|
|
|
127
|
+
results = dataset.search(params)
|
|
128
|
+
if results
|
|
129
|
+
@results = @results + results
|
|
130
|
+
else
|
|
131
|
+
flash.now[:error] ||= []
|
|
132
|
+
flash.now[:error] << t('txt.controllers.search_results.remote_source_error', source: dataset)
|
|
133
|
+
end
|
|
127
134
|
end
|
|
135
|
+
@results = @results.sort { |x, y| x.to_s <=> y.to_s }
|
|
128
136
|
end
|
|
129
|
-
@results = @results.sort { |x, y| x.to_s <=> y.to_s }
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
@results = Kaminari.paginate_array(@results)
|
|
133
|
-
@results = @results.page(params[:page])
|
|
134
137
|
|
|
138
|
+
@results = Kaminari.paginate_array(@results)
|
|
139
|
+
@results = @results.page(params[:page])
|
|
140
|
+
|
|
141
|
+
respond_to do |format|
|
|
142
|
+
format.html {
|
|
143
|
+
if request.headers['Accept'] == 'text/html; fragment=true'
|
|
144
|
+
render template: 'search_results/_result_list', layout: false
|
|
145
|
+
else
|
|
146
|
+
render :index, layout: with_layout?
|
|
147
|
+
end
|
|
148
|
+
}
|
|
149
|
+
format.any(:ttl, :rdf, :nt)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
else
|
|
135
153
|
respond_to do |format|
|
|
136
|
-
format.html
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
format.any(:ttl, :rdf, :nt)
|
|
154
|
+
format.html do
|
|
155
|
+
render :index
|
|
156
|
+
end
|
|
157
|
+
format.any(:ttl, :rdf, :nt) do
|
|
158
|
+
head :bad_request
|
|
159
|
+
end
|
|
144
160
|
end
|
|
145
161
|
end
|
|
146
162
|
end
|