katello 4.12.0 → 4.12.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of katello might be problematic. Click here for more details.

Files changed (25) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/app/assets/javascripts/katello/sync_management/sync_management.js +1 -0
  4. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -1
  5. data/app/helpers/katello/katello_urls_helper.rb +26 -1
  6. data/app/lib/katello/errors.rb +4 -0
  7. data/app/models/katello/candlepin/repository_mapper.rb +1 -1
  8. data/app/models/katello/glue/pulp/repos.rb +0 -6
  9. data/app/models/katello/host_collection.rb +12 -3
  10. data/app/models/katello/repository.rb +6 -0
  11. data/app/views/katello/api/v2/hosts/host_collections.json.rabl +5 -1
  12. data/app/views/katello/hosts/_errata_counts.html.erb +1 -1
  13. data/app/views/overrides/activation_keys/_host_tab_pane.html.erb +1 -29
  14. data/lib/katello/plugin.rb +1 -1
  15. data/lib/katello/version.rb +1 -1
  16. data/webpack/components/ActivationKeysSearch/ActivationKeysSearch.test.js +28 -0
  17. data/webpack/components/ActivationKeysSearch/index.js +222 -0
  18. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +1 -1
  19. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +1 -0
  20. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +1 -0
  21. data/webpack/global_index.js +10 -0
  22. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +6 -3
  23. data/webpack/scenes/Hosts/ChangeContentSource/components/ContentSourceForm.js +1 -1
  24. metadata +5 -4
  25. data/app/assets/javascripts/katello/hosts/activation_key_edit.js +0 -167
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd21e38a92cceb34e2ae67a8e9dd1f128eed67323230e83eeb671a63bad4f11f
4
- data.tar.gz: 7cddfcb3d2aaae7a8d170b760986c4268b7bde531bff0f88d6b21896cda8e340
3
+ metadata.gz: c78bf13db890a8782d152850ca4e2b26425a3d181a1e02e9f4ecf779785e3585
4
+ data.tar.gz: 983b9a631aee789c6468d9dc3cdae527e1c5d42dd8b02281be7cfd41e751d5f1
5
5
  SHA512:
6
- metadata.gz: 1762ccadc8e731d4662b243fd46effe9df5d67ce39a6cb5fa36f1593fb60d7e24237b497740feba93cf7e73f62497e8c8b1e637d584f85a864a0c367697f0303
7
- data.tar.gz: b16c43097ea41164f44f68225deaa0a4e6cf256e06976f40519237e286575f87728b8af45e5861af6df241826fd31350eb20bbf90461566add5c4a4411e8c2cb
6
+ metadata.gz: 7ebb29c3055d54b87bdc077765fba457f7e790f6789267c69dd36709d308b74fe6979b32ace066aa9840d20ec94e06b203cade51fe59b68abc321c3c0f1a8d00
7
+ data.tar.gz: 85471a1295e226c3a03a4daab47c6f4ec7ff5707068ca67878c8fe67993c138c2a77f0ab8a751fa47fb057f9150b64764ce7847e3988d11d305101e6005584bc
data/README.md CHANGED
@@ -74,7 +74,6 @@ See the [annotation docs](./test/scenarios/annotations/README.md) for more infor
74
74
  * Archived mailing lists:
75
75
  * [Foreman User Mailing List](https://groups.google.com/forum/?fromgroups#!forum/foreman-users)
76
76
  * [Foreman Developer mailing list](https://groups.google.com/forum/?fromgroups#!forum/foreman-dev)
77
- * [IRC Freenode](https://theforeman.org/support.html#IRClivechat): #theforeman-dev, #theforeman
78
77
 
79
78
  ## Documentation
80
79
 
@@ -333,6 +333,7 @@ KT.content_actions = (function () {
333
333
  updater.stop();
334
334
  }
335
335
  );
336
+ updater.restart();
336
337
  };
337
338
 
338
339
  return {
@@ -55,7 +55,7 @@ module Katello
55
55
  repairable_products = @products.syncable
56
56
  repairable_roots = RootRepository.where(:product_id => repairable_products).has_url.select { |r| r.library_instance }.uniq
57
57
 
58
- repairable_repositories = Katello::Repository.where(:root_id => repairable_roots)
58
+ repairable_repositories = Katello::Repository.library.where(:root_id => repairable_roots)
59
59
  task = async_task(::Actions::BulkAction,
60
60
  ::Actions::Katello::Repository::VerifyChecksum,
61
61
  repairable_repositories)
@@ -37,11 +37,36 @@ module Katello
37
37
  "#{prefix}/pub/#{config}"
38
38
  end
39
39
 
40
- apipie :method, 'Generates an absolute path to the file' do
40
+ apipie :method, 'Generates an absolute path to the file for liveimg kickstart templates' do
41
41
  required :content_path, String, desc: "Relative path to the file or it's name"
42
42
  optional :schema, String, desc: 'Optional URL schema for the content source', default: 'http'
43
43
  optional :content_type, String, desc: 'Content type', default: 'repos'
44
44
  returns String, desc: 'Absolute path to a file'
45
45
  end
46
+ def repository_url(content_path, _content_type = nil, schema = 'http')
47
+ return content_path if content_path =~ %r|^([\w\-\+]+)://|
48
+ url = if @host.content_source
49
+ "#{schema}://#{@host.content_source.hostname}"
50
+ else
51
+ foreman_settings_url(schema)
52
+ end
53
+ content_path = content_path.sub(%r|^/|, '')
54
+
55
+ lifecycle_environment = nil
56
+ if @host.default_environment?
57
+ # Don't update the content_path if the host is using the default org view / library
58
+ lifecycle_environment = ::Katello::KTEnvironment.library.find_by(organization_id: @host.organization_id)
59
+ elsif !@host.single_content_view_environment?
60
+ fail ::Katello::Errors::MultiEnvironmentNotSupportedError,
61
+ "Host #{@host.name} must be subscribed to only a single content view & environment or subscribe to the default organization content view for liveimg provisioning."
62
+ else
63
+ lifecycle_environment = @host.single_lifecycle_environment
64
+ content_path = [@host.single_content_view.label, content_path].join('/')
65
+ end
66
+
67
+ path = ::Katello::Repository.repo_path_from_content_path(
68
+ lifecycle_environment, content_path)
69
+ "#{url}/pulp/content/#{path}"
70
+ end
46
71
  end
47
72
  end
@@ -10,6 +10,10 @@ module Katello
10
10
 
11
11
  class RegistrationError < StandardError; end
12
12
 
13
+ class InvalidRepositoryTypeError < StandardError; end
14
+
15
+ class MultiEnvironmentNotSupportedError < StandardError; end
16
+
13
17
  # unauthorized access
14
18
  class SecurityViolation < StandardError; end
15
19
 
@@ -74,7 +74,7 @@ module Katello
74
74
  end
75
75
 
76
76
  def relative_path
77
- ::Katello::Glue::Pulp::Repos.repo_path_from_content_path(product.organization.library, path)
77
+ ::Katello::Repository.repo_path_from_content_path(product.organization.library, path)
78
78
  end
79
79
 
80
80
  def prune_substitutions(subs, url)
@@ -6,12 +6,6 @@ module Katello
6
6
  base.send :include, InstanceMethods
7
7
  end
8
8
 
9
- def self.repo_path_from_content_path(environment, content_path)
10
- path = content_path.sub(%r|^/|, '')
11
- path_prefix = [environment.organization.label, environment.label].join('/')
12
- "#{path_prefix}/#{path}"
13
- end
14
-
15
9
  module InstanceMethods
16
10
  def distributions(env)
17
11
  to_ret = []
@@ -29,7 +29,10 @@ module Katello
29
29
  scoped_search :relation => :hosts, :on => :name, :rename => :host, :complete_value => true
30
30
 
31
31
  def max_hosts_check
32
- if !unlimited_hosts && (hosts.length > 0 && (hosts.length.to_i > max_hosts.to_i)) && max_hosts_changed?
32
+ # NOTE: max_hosts_check and max_hosts_no_exceeded use size() instead of count() because
33
+ # the host list exists as an array rather than a DB query when run as a validation.
34
+ host_count = hosts.size
35
+ if !unlimited_hosts && (host_count > 0 && (host_count.to_i > max_hosts.to_i)) && max_hosts_changed?
33
36
  errors.add :max_host, N_("may not be less than the number of hosts associated with the host collection.")
34
37
  end
35
38
  end
@@ -60,8 +63,14 @@ module Katello
60
63
  type ? query.of_type(type) : query
61
64
  end
62
65
 
63
- def total_hosts
64
- hosts.length
66
+ def cache_key
67
+ "#{self.class.name}/#{self.id}"
68
+ end
69
+
70
+ def total_hosts(cached: false)
71
+ Rails.cache.fetch("#{cache_key}/total_hosts", expires_in: 1.minute, force: !cached) do
72
+ hosts.count
73
+ end
65
74
  end
66
75
 
67
76
  # Retrieve the list of accessible host collections in the organization specified, returning
@@ -201,6 +201,12 @@ module Katello
201
201
  joins(:root).where("#{Katello::RootRepository.table_name}.product_id" => products)
202
202
  end
203
203
 
204
+ def self.repo_path_from_content_path(environment, content_path)
205
+ path = content_path.sub(%r|^/|, '')
206
+ path_prefix = [environment.organization.label, environment.label].join('/')
207
+ "#{path_prefix}/#{path}"
208
+ end
209
+
204
210
  def to_label
205
211
  name
206
212
  end
@@ -1,3 +1,7 @@
1
1
  child :host_collections => :host_collections do
2
- attributes :id, :name, :description, :max_hosts, :unlimited_hosts, :total_hosts
2
+ attributes :id, :name, :description, :max_hosts, :unlimited_hosts
3
+
4
+ node :total_hosts do |host_collection|
5
+ host_collection.total_hosts(cached: true)
6
+ end
3
7
  end
@@ -43,7 +43,7 @@
43
43
  <% end %>
44
44
  <% if host.operatingsystem_name&.match(/Debian|Ubuntu/) %>
45
45
  <% if Setting["host_details_ui"] %>
46
- <a href="/new/hosts/<%= host.name %>#/Content/packages?status=Upgradable">
46
+ <a href="/new/hosts/<%= host.name %>#/Content/debs?status=Upgradable">
47
47
  <% else %>
48
48
  <a href="/content_hosts/<%= host.id %>/debs/applicable">
49
49
  <% end %>
@@ -1,31 +1,3 @@
1
-
2
- <%= javascript "katello/hosts/activation_key_edit" %>
3
-
4
-
5
1
  <div class="tab-pane" id="activation_keys">
6
- <div id="ak-load-error" class="alert alert-danger" style="display: none;">
7
- <%= _("There was a problem retrieving Activation Key data from the server.") %>
8
- </div>
9
-
10
- <%= field(f, _("Activation Keys"),
11
- :help_inline => _("The value will be available in templates as @host.params['#{kt_ak_label}']")) do
12
- react_component('TypeAheadSelect', { id: 'kt_activation_keys', multiple: true, allowNew: true })
13
- end %>
14
-
15
- <div class="alert alert-info">
16
- <p><b><%= _('Subscriptions information based on selected activation keys:') %></b></p>
17
- <ul id="ak-subscriptions-info"></ul>
18
-
19
- <div id="ak-subscriptions-spinner" style="display: none">
20
- <%= image_tag "spinner.gif" %>
21
- </div>
22
-
23
- <p><%= _('Activation keys and subscriptions can be managed') %>
24
- <b><a href="/activation_keys" target="_blank"> <%= _('here.') %></a></b>
25
- </p>
26
- <p translate>
27
- Activation keys may be used during <a href="/hosts/register">system registration.</a>
28
- </p>
29
- <p><a href="" id="ak_refresh_subscriptions"><%= _('Reload data') %></a></p>
30
- </div>
2
+ <%= react_component('ActivationKeysSearch')%>
31
3
  </div>
@@ -720,7 +720,7 @@ Foreman::Plugin.register :katello do
720
720
  ], 'Role granting permission to import content views in an organization'
721
721
 
722
722
  role 'Content Exporter', [
723
- :export_content, :view_products, :view_content_views, :view_organizations
723
+ :export_content, :view_products, :view_content_views, :create_content_views, :view_organizations
724
724
  ], 'Role granting permission to export content views in an organization'
725
725
 
726
726
  def find_katello_assets(args = {})
@@ -1,3 +1,3 @@
1
1
  module Katello
2
- VERSION = "4.12.0".freeze
2
+ VERSION = "4.12.1".freeze
3
3
  end
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { renderWithRedux } from 'react-testing-lib-wrapper';
3
+ import ActivationKeysSearch from './index';
4
+
5
+ describe('ActivationKeysSearch', () => {
6
+ const mockQuerySelector = jest.spyOn(document, 'querySelector');
7
+ mockQuerySelector.mockImplementation((selector) => {
8
+ if (selector === '#hostgroup_lifecycle_environment_id') {
9
+ return {
10
+ options: [{}],
11
+ selectedIndex: 0,
12
+ value: '1',
13
+ };
14
+ }
15
+ if (selector === '#hostgroup_content_view_id') {
16
+ return {
17
+ options: [{}],
18
+ selectedIndex: 0,
19
+ value: '2 ',
20
+ };
21
+ }
22
+ return null;
23
+ });
24
+ it('renders without crashing', () => {
25
+ const { getByText } = renderWithRedux(<ActivationKeysSearch />, {});
26
+ expect(getByText('Activation Key information')).toBeInTheDocument();
27
+ });
28
+ });
@@ -0,0 +1,222 @@
1
+ import React, { useState, useEffect, useCallback, useMemo } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import {
4
+ Form,
5
+ FormGroup,
6
+ Spinner,
7
+ EmptyState,
8
+ Title,
9
+ Button,
10
+ Select,
11
+ SelectOption,
12
+ SelectVariant,
13
+ Alert,
14
+ } from '@patternfly/react-core';
15
+ import { FormattedMessage } from 'react-intl';
16
+ import $ from 'jquery';
17
+ import { translate as __ } from 'foremanReact/common/I18n';
18
+ import { get } from 'foremanReact/redux/API';
19
+ import { foremanUrl } from 'foremanReact/common/helpers';
20
+ import { STATUS } from 'foremanReact/constants';
21
+ import { selectAPIStatus } from 'foremanReact/redux/API/APISelectors';
22
+
23
+ const getSelectedEnvId = () => {
24
+ const selectElement = document.querySelector('#hostgroup_lifecycle_environment_id');
25
+ const selectedOption = selectElement.options[selectElement.selectedIndex];
26
+ let dataId = selectedOption?.getAttribute?.('data-id');
27
+ if (!dataId) {
28
+ dataId = selectElement.value;
29
+ }
30
+ return dataId;
31
+ };
32
+ const getSelectedContentViewId = () => {
33
+ const selectElement = document.querySelector('#hostgroup_content_view_id');
34
+ const selectedOption = selectElement.options[selectElement.selectedIndex];
35
+ let dataId = selectedOption?.getAttribute?.('data-id');
36
+ if (!dataId) {
37
+ dataId = selectElement.value;
38
+ }
39
+ return dataId;
40
+ };
41
+ const ActivationKeysSearch = () => {
42
+ const ACTIVATION_KEYS = 'ACTIVATION_KEYS';
43
+ const KT_AK_LABEL = 'kt_activation_keys';
44
+ const [selectedEnvId, setSelectedEnvId] = useState(getSelectedEnvId());
45
+ const [selectedContentViewId, setSelectedContentViewId] = useState(getSelectedContentViewId());
46
+ const isLoading =
47
+ useSelector(state => selectAPIStatus(state, ACTIVATION_KEYS)) === STATUS.PENDING;
48
+ const [activationKeys, setActivationKeys] = useState([]);
49
+ const [selectedKeys, setSelectedKeys] = useState([]);
50
+ const [isOpen, setIsOpen] = useState(false);
51
+ const dispatch = useDispatch();
52
+
53
+ const ktLoadActivationKeys = useCallback(() => {
54
+ if (selectedEnvId && selectedContentViewId) {
55
+ dispatch(get({
56
+ key: ACTIVATION_KEYS,
57
+ url: foremanUrl(`/katello/api/v2/environments/${selectedEnvId}/activation_keys`),
58
+ params: { content_view_id: selectedContentViewId },
59
+ handleSuccess: ({ data }) => {
60
+ setActivationKeys(data.results);
61
+ },
62
+ errorToast: () =>
63
+ __('There was a problem retrieving Activation Key data from the server.'),
64
+ }));
65
+ }
66
+ }, [dispatch, selectedEnvId, selectedContentViewId, setActivationKeys]);
67
+ const paramContainer = useMemo(() => {
68
+ let ret;
69
+ const inputs = document.querySelectorAll("div#parameters .fields input[type='text']");
70
+ inputs.forEach((input) => {
71
+ if (input.value === KT_AK_LABEL) {
72
+ ret = input.closest('.fields');
73
+ }
74
+ });
75
+ return ret;
76
+ }, []);
77
+
78
+ useEffect(() => {
79
+ $('#hostgroup_lifecycle_environment_id').on('change', () => setSelectedEnvId(getSelectedEnvId)); // cant use eventlistener on select2
80
+ $('#hostgroup_content_view_id').on('change', () =>
81
+ setSelectedContentViewId(getSelectedContentViewId())); // cant use eventlistener on select2
82
+ if (selectedEnvId && selectedContentViewId) {
83
+ ktLoadActivationKeys();
84
+ }
85
+
86
+ const ktHideParams = () => {
87
+ if (paramContainer) {
88
+ paramContainer.style.display = 'none';
89
+ }
90
+ };
91
+
92
+ const ktAkGetKeysFromParam = () => {
93
+ let keys = [];
94
+ if (paramContainer) {
95
+ const textarea = paramContainer.querySelector('textarea');
96
+ if (textarea) {
97
+ keys = textarea.value.split(',').map(key => key.trim());
98
+ }
99
+ }
100
+ return keys;
101
+ };
102
+ ktHideParams();
103
+ setSelectedKeys(ktAkGetKeysFromParam());
104
+ }, [ktLoadActivationKeys, paramContainer, selectedContentViewId, selectedEnvId]);
105
+
106
+ useEffect(() => {
107
+ function ktSetParam() {
108
+ let paramContainerCopy = paramContainer;
109
+ if (selectedKeys.length > 0) {
110
+ const value = selectedKeys.map(key => key.trim()).join(',');
111
+ if (!paramContainerCopy) {
112
+ // we create the param for kt_activation_keys
113
+ const addParameterButton = document.querySelector('#parameters .btn-primary');
114
+ addParameterButton.click();
115
+ const directionOfAddedItems = addParameterButton.getAttribute('direction');
116
+ const paramContainers = document.querySelectorAll('#parameters .fields');
117
+ if (directionOfAddedItems === 'append') {
118
+ paramContainerCopy = paramContainers[paramContainers.length - 1];
119
+ } else {
120
+ [paramContainerCopy] = paramContainers;
121
+ }
122
+ paramContainerCopy.querySelector("input[name*='name']").value = KT_AK_LABEL;
123
+ }
124
+ paramContainerCopy.querySelector('textarea').value = value;
125
+ paramContainerCopy.querySelector("input[type='hidden']").value = 0;
126
+ } else if (paramContainerCopy) {
127
+ // we remove the param by setting destroy to 1
128
+ paramContainerCopy.querySelector("input[type='hidden']").value = 1;
129
+ }
130
+ }
131
+ ktSetParam();
132
+ }, [paramContainer, selectedKeys]);
133
+
134
+ if (!(selectedEnvId && selectedContentViewId)) {
135
+ return (
136
+ <EmptyState>
137
+ <Title headingLevel="h4" size="lg" ouiaId="ak-empty-state-title">
138
+ {__('Please select a lifecycle environment and content view to view activation keys.')}
139
+ </Title>
140
+ </EmptyState>
141
+ );
142
+ }
143
+
144
+ const onSelect = (event, selection) => {
145
+ setIsOpen(false);
146
+ if (selectedKeys.includes(selection)) {
147
+ setSelectedKeys(prevState => prevState.filter(item => item !== selection));
148
+ } else {
149
+ setSelectedKeys(prevState => [...prevState, selection]);
150
+ }
151
+ };
152
+ const isEmptyResults = activationKeys.length === 0;
153
+ return (
154
+ <div>
155
+ <Form isHorizontal>
156
+ <FormGroup label={__('Activation Keys')}>
157
+ <Select
158
+ ouiaId="ak-select"
159
+ variant={SelectVariant.typeaheadMulti}
160
+ onToggle={setIsOpen}
161
+ onSelect={onSelect}
162
+ selections={selectedKeys}
163
+ isOpen={isOpen}
164
+ isCreatable
165
+ shouldResetOnSelect
166
+ isDisabled={isLoading || isEmptyResults}
167
+ placeholderText={
168
+ isEmptyResults
169
+ ? __('The selected lifecycle environment contains no activation keys')
170
+ : null
171
+ }
172
+ >
173
+ {activationKeys.map(({ id, name }) => (
174
+ <SelectOption key={id} value={name} />
175
+ ))}
176
+ </Select>
177
+ </FormGroup>
178
+ <Alert title={__('Activation Key information')} variant="info" ouiaId="ak-info">
179
+ <p>{__("The value will be available in templates as @host.params['kt_activation_keys']")}</p>
180
+ <p>
181
+ <FormattedMessage
182
+ id="ak-link-manage"
183
+ defaultMessage={__('Activation keys can be managed {here}.')}
184
+ values={{
185
+ here: (
186
+ <b>
187
+ <a href="/activation_keys" target="_blank">
188
+ {__('here')}
189
+ </a>
190
+ </b>
191
+ ),
192
+ }}
193
+ />
194
+ </p>
195
+ <p>
196
+ <FormattedMessage
197
+ id="ak-subscriptions-info"
198
+ defaultMessage={__('Activation keys may be used during {system_registration}.')}
199
+ values={{
200
+ system_registration: <a href="/hosts/register">{__('system registration')}</a>,
201
+ }}
202
+ />
203
+ </p>
204
+ <p>
205
+ <Button
206
+ id="ak_refresh_subscriptions"
207
+ variant="link"
208
+ onClick={ktLoadActivationKeys}
209
+ ouiaId="ak-refresh-button"
210
+ isInline
211
+ >
212
+ {__('Reload data')}
213
+ </Button>
214
+ </p>
215
+ </Alert>
216
+ </Form>
217
+ {isLoading && <Spinner id="ak-subscriptions-spinner" />}
218
+ </div>
219
+ );
220
+ };
221
+
222
+ export default ActivationKeysSearch;
@@ -617,7 +617,7 @@ export const PackagesTab = () => {
617
617
  closeModal={closeModal}
618
618
  hostId={hostId}
619
619
  key={hostId}
620
- hostName={hostname}
620
+ hostName={hostDetails.display_name}
621
621
  triggerPackageInstall={triggerPackageInstall}
622
622
  />
623
623
  }
@@ -31,6 +31,7 @@ const renderOptions = (facetAttributes = contentFacetAttributes) => ({
31
31
  id: 1,
32
32
  name: 'test-host',
33
33
  content_facet_attributes: { ...facetAttributes },
34
+ display_name: 'test-host',
34
35
  },
35
36
  status: 'RESOLVED',
36
37
  },
@@ -33,6 +33,7 @@ const renderOptions = (facetAttributes = contentFacetAttributes) => ({
33
33
  id: 1,
34
34
  name: hostname,
35
35
  content_facet_attributes: { ...facetAttributes },
36
+ display_name: hostname,
36
37
  },
37
38
  status: 'RESOLVED',
38
39
  },
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { addGlobalFill } from 'foremanReact/components/common/Fill/GlobalFill';
3
3
  import { registerReducer } from 'foremanReact/common/MountingService';
4
4
  import { translate as __ } from 'foremanReact/common/I18n';
5
+ import componentRegistry from 'foremanReact/components/componentRegistry';
5
6
 
6
7
  import SystemStatuses from './components/extensions/about';
7
8
  import {
@@ -30,6 +31,9 @@ import HostsIndexActionsBar from './components/extensions/Hosts/ActionsBar';
30
31
  import RecentCommunicationCardExtensions from './components/extensions/HostDetails/DetailsTabCards/RecentCommunicationCardExtensions';
31
32
  import SystemPurposeCard from './components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard';
32
33
 
34
+
35
+ import ActivationKeysSearch from './components/ActivationKeysSearch';
36
+
33
37
  registerReducer('katelloExtends', extendReducer);
34
38
  registerReducer('katello', rootReducer);
35
39
 
@@ -80,3 +84,9 @@ addGlobalFill(
80
84
  );
81
85
 
82
86
  addGlobalFill('host-tab-details-cards', 'HW properties', <HwPropertiesCard key="hw-properties" />, 200);
87
+
88
+ componentRegistry.register({
89
+ name: 'ActivationKeysSearch',
90
+ type: ActivationKeysSearch,
91
+ });
92
+
@@ -5,7 +5,6 @@ import {
5
5
  Bullseye, Split, SplitItem, Button, ActionList,
6
6
  ActionListItem, Dropdown, DropdownItem, KebabToggle,
7
7
  } from '@patternfly/react-core';
8
- import { Link } from 'react-router-dom';
9
8
  import { TableVariant, fitContent, TableText } from '@patternfly/react-table';
10
9
  import { PencilAltIcon } from '@patternfly/react-icons';
11
10
  import { STATUS } from 'foremanReact/constants';
@@ -136,7 +135,7 @@ const ContentViewComponents = ({ cvId, details }) => {
136
135
  id: componentCvId, content_view: cv, content_view_version: cvVersion,
137
136
  latest, component_content_view_versions: componentCvVersions,
138
137
  } = componentCV;
139
- const { environments, repositories } = cvVersion || {};
138
+ const { environments, repositories, id: cvVersionId } = cvVersion || {};
140
139
  const {
141
140
  id,
142
141
  name,
@@ -171,7 +170,11 @@ const ContentViewComponents = ({ cvId, details }) => {
171
170
  </Split>),
172
171
  },
173
172
  { title: environments ? <ComponentEnvironments {...{ environments }} /> : <InactiveText text={__('Not yet published')} /> },
174
- { title: <Link to={urlBuilder(`content_views/${id}#repositories`, '')}>{repositories ? repositories.length : 0}</Link> },
173
+ {
174
+ title: cvVersionId ?
175
+ <a href={urlBuilder(`content_views/${id}#/versions/${cvVersionId}/repositories`, '')}>{repositories ? repositories.length : 0}</a> :
176
+ 0,
177
+ },
175
178
  {
176
179
  title: <AddedStatusLabel added={!!componentCvId} />,
177
180
  },
@@ -114,7 +114,7 @@ const ContentSourceForm = ({
114
114
  const hostCount = contentHosts.length;
115
115
 
116
116
  const handleCSSelect = (_event, selection) => {
117
- handleContentSource(selection);
117
+ handleContentSource(typeof selection === 'number' ? selection.toString() : selection);
118
118
  setCSSelectOpen(false);
119
119
  };
120
120
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katello
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.12.0
4
+ version: 4.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - N/A
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-19 00:00:00.000000000 Z
11
+ date: 2024-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -535,7 +535,6 @@ files:
535
535
  - app/assets/javascripts/katello/common/spin.min.js
536
536
  - app/assets/javascripts/katello/common/vendor.js
537
537
  - app/assets/javascripts/katello/containers/container.js
538
- - app/assets/javascripts/katello/hosts/activation_key_edit.js
539
538
  - app/assets/javascripts/katello/hosts/host_and_hostgroup_edit.js
540
539
  - app/assets/javascripts/katello/html5/excanvas.js
541
540
  - app/assets/javascripts/katello/html5/html5.js
@@ -4515,6 +4514,8 @@ files:
4515
4514
  - webpack/__mocks__/react-intl/locale-data/en.js
4516
4515
  - webpack/common_index.js
4517
4516
  - webpack/components/ActionableDetail.js
4517
+ - webpack/components/ActivationKeysSearch/ActivationKeysSearch.test.js
4518
+ - webpack/components/ActivationKeysSearch/index.js
4518
4519
  - webpack/components/AddedStatusLabel.js
4519
4520
  - webpack/components/Bookmark/AddBookmarkModal.js
4520
4521
  - webpack/components/Bookmark/Bookmark.scss
@@ -5402,7 +5403,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
5402
5403
  - !ruby/object:Gem::Version
5403
5404
  version: '0'
5404
5405
  requirements: []
5405
- rubygems_version: 3.4.10
5406
+ rubygems_version: 3.5.9
5406
5407
  signing_key:
5407
5408
  specification_version: 4
5408
5409
  summary: Content and Subscription Management plugin for Foreman
@@ -1,167 +0,0 @@
1
- var KT = KT ? KT : {};
2
- KT.hosts = KT.hosts || {};
3
-
4
- KT.KT_AK_LABEL = 'kt_activation_keys';
5
- KT.hosts.availableActivationKeys = {};
6
-
7
- function ktLoadActivationKeys() {
8
- if(getSelectedEnvId() && getSelectedContentViewId()) {
9
- ktAkTab().show();
10
- } else {
11
- ktAkTab().hide();
12
- return; //no Katello-specific env selected
13
- }
14
-
15
- $("#ak-load-error").hide();
16
- $("#ak-subscriptions-info").hide();
17
- $("#ak-subscriptions-spinner").show();
18
-
19
- // Retrieve the activation keys associated with the current
20
- // environment & content view.
21
- $.ajax({
22
- type: 'get',
23
- url: tfm.tools.foremanUrl('/katello/api/v2/environments/' + getSelectedEnvId() + '/activation_keys'),
24
- data: {'content_view_id': getSelectedContentViewId()},
25
- success: function(response) {
26
- KT.hosts.availableActivationKeys = {};
27
- $.each(response['results'], function (i, key) {
28
- KT.hosts.availableActivationKeys[key.name] = [];
29
- });
30
- tfm.typeAheadSelect.updateOptions(Object.keys(KT.hosts.availableActivationKeys), KT.KT_AK_LABEL);
31
- },
32
- error: function() {
33
- $("#ak-load-error").show();
34
- },
35
- });
36
- }
37
-
38
- function ktFindParamContainer(name){
39
- var ret;
40
- $("div#parameters .fields input[ type = 'text']").each(function () {
41
- var element = $(this);
42
- if(element.val() == name) {
43
- ret = element.closest('.fields');
44
- return false;
45
- }
46
- return true;
47
- });
48
- return ret;
49
- }
50
-
51
- function ktHideParams() {
52
- var param = ktFindParamContainer(KT.KT_AK_LABEL);
53
- if(param) {
54
- param.hide();
55
- }
56
- }
57
-
58
- function getSelectedEnvId() {
59
- var dataId = $("#hostgroup_lifecycle_environment_id > option:selected").data("id");
60
- if (dataId === undefined) {
61
- dataId = $("#hostgroup_lifecycle_environment_id").val();
62
- }
63
- return dataId;
64
- }
65
-
66
- function getSelectedContentViewId() {
67
- var dataId = $("#hostgroup_content_view_id > option:selected").data("id");
68
- if (dataId === undefined) {
69
- dataId = $("#hostgroup_content_view_id").val();
70
- }
71
- return dataId;
72
- }
73
-
74
- function ktSetParam(name, value) {
75
- var paramContainer = ktFindParamContainer(name);
76
- if(value) {
77
- if(! paramContainer) { // we create the param for kt_activation_keys
78
- var addParameterButton = $('#parameters').find('.btn-primary');
79
- addParameterButton.click();
80
- var directionOfAddedItems = addParameterButton.attr('direction');
81
- var paramContainer = $('#parameters').find('.fields');
82
- if(directionOfAddedItems === 'append'){
83
- paramContainer = paramContainer.last();
84
- } else {
85
- paramContainer = paramContainer.first();
86
- }
87
- paramContainer.find("input[name*='name']").val(name);
88
- }
89
- paramContainer.find("textarea").val(value);
90
- paramContainer.find("input[ type = 'hidden' ]").val(0);
91
- } else if(paramContainer) {
92
- // we remove the param by setting destroy to 1
93
- paramContainer.find("input[ type = 'hidden' ]").val(1);
94
- }
95
- }
96
-
97
- function ktAkGetKeysFromParam() {
98
- var paramContainer = ktFindParamContainer(KT.KT_AK_LABEL);
99
- var keys = [];
100
- if(paramContainer) {
101
- keys = paramContainer.find("textarea").val().split(',').map(function(key) {
102
- return key.trim();
103
- });
104
- }
105
- return keys;
106
- }
107
-
108
- function ktAkUpdateSubscriptionsInfo(selectedKeys) {
109
- var subsInfo = $("ul#ak-subscriptions-info");
110
- subsInfo.empty();
111
- $.each(selectedKeys, function(i, key) {
112
- if(KT.hosts.availableActivationKeys[key]) {
113
- // hack to make it working with deface
114
- var ul = "<ul>", ul_end = "</ul>", li = "<li>", li_end = "</li>";
115
- content = li + key + ul;
116
- if(!KT.hosts.availableActivationKeys[key].length == 0) {
117
- content += li;
118
- content += KT.hosts.availableActivationKeys[key].join(li_end + li);
119
- content += li_end;
120
- }
121
- content += ul_end + li_end;
122
- subsInfo.append(content);
123
- }
124
- });
125
- $("#ak-subscriptions-info").show();
126
- $("#ak-subscriptions-spinner").hide();
127
- }
128
-
129
- function ktAkTab() {
130
- return $('li#activation_keys_tab');
131
- }
132
-
133
- function ktOnLoad() {
134
- tfm.store.observeStore('typeAheadSelect', function(items, unsubscribe) {
135
- if (items.kt_activation_keys) { // Wait until after initialization to subscribe to store changes
136
- unsubscribe();
137
-
138
- tfm.typeAheadSelect.updateSelected(ktAkGetKeysFromParam(), KT.KT_AK_LABEL);
139
-
140
- tfm.store.observeStore('typeAheadSelect', function(items) {
141
- if (items.kt_activation_keys) {
142
- var selected = items.kt_activation_keys.selected || [];
143
-
144
- ktAkUpdateSubscriptionsInfo(selected);
145
- ktSetParam(KT.KT_AK_LABEL, selected.map(function(key) {
146
- return key.trim();
147
- }).join(','));
148
- }
149
- });
150
- }
151
- });
152
-
153
- ktHideParams();
154
- ktLoadActivationKeys();
155
- }
156
-
157
- $(document).on('ContentLoad', function(){
158
- ktOnLoad();
159
-
160
- $("#hostgroup_lifecycle_environment_id").change(ktLoadActivationKeys);
161
- $("#hostgroup_content_view_id").change(ktLoadActivationKeys);
162
-
163
- $("#ak_refresh_subscriptions").click(function () {
164
- ktLoadActivationKeys();
165
- return false;
166
- });
167
- });