katello 4.12.0.rc3 → 4.13.0.rc1

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -1
  3. data/app/assets/javascripts/katello/locale/bn/katello.js +3365 -3350
  4. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +3136 -3121
  5. data/app/assets/javascripts/katello/locale/ca/katello.js +3588 -3576
  6. data/app/assets/javascripts/katello/locale/cs/katello.js +3499 -3487
  7. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +4186 -4186
  8. data/app/assets/javascripts/katello/locale/de/katello.js +5553 -5562
  9. data/app/assets/javascripts/katello/locale/de_AT/katello.js +3008 -2993
  10. data/app/assets/javascripts/katello/locale/de_DE/katello.js +3066 -3051
  11. data/app/assets/javascripts/katello/locale/el/katello.js +3376 -3370
  12. data/app/assets/javascripts/katello/locale/en/katello.js +3008 -2993
  13. data/app/assets/javascripts/katello/locale/en_GB/katello.js +3076 -3073
  14. data/app/assets/javascripts/katello/locale/en_US/katello.js +3008 -2993
  15. data/app/assets/javascripts/katello/locale/es/katello.js +5366 -5372
  16. data/app/assets/javascripts/katello/locale/et_EE/katello.js +3008 -2993
  17. data/app/assets/javascripts/katello/locale/fr/katello.js +5975 -5984
  18. data/app/assets/javascripts/katello/locale/gl/katello.js +3125 -3113
  19. data/app/assets/javascripts/katello/locale/gu/katello.js +3119 -3104
  20. data/app/assets/javascripts/katello/locale/he_IL/katello.js +3020 -3005
  21. data/app/assets/javascripts/katello/locale/hi/katello.js +3137 -3122
  22. data/app/assets/javascripts/katello/locale/id/katello.js +3008 -2993
  23. data/app/assets/javascripts/katello/locale/it/katello.js +4469 -4466
  24. data/app/assets/javascripts/katello/locale/ja/katello.js +5969 -5978
  25. data/app/assets/javascripts/katello/locale/ka/katello.js +5649 -5652
  26. data/app/assets/javascripts/katello/locale/kn/katello.js +3136 -3121
  27. data/app/assets/javascripts/katello/locale/ko/katello.js +4717 -4720
  28. data/app/assets/javascripts/katello/locale/locale/katello.js +1050 -1084
  29. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +3008 -2993
  30. data/app/assets/javascripts/katello/locale/mr/katello.js +3136 -3121
  31. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +3116 -3101
  32. data/app/assets/javascripts/katello/locale/or/katello.js +3137 -3122
  33. data/app/assets/javascripts/katello/locale/pa/katello.js +3136 -3121
  34. data/app/assets/javascripts/katello/locale/pl/katello.js +3210 -3195
  35. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +3008 -2993
  36. data/app/assets/javascripts/katello/locale/pt/katello.js +3009 -2994
  37. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +5362 -5368
  38. data/app/assets/javascripts/katello/locale/ro/katello.js +3008 -2993
  39. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +3008 -2993
  40. data/app/assets/javascripts/katello/locale/ru/katello.js +4638 -4641
  41. data/app/assets/javascripts/katello/locale/sl/katello.js +3051 -3036
  42. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +3156 -3144
  43. data/app/assets/javascripts/katello/locale/ta/katello.js +3365 -3350
  44. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +3121 -3106
  45. data/app/assets/javascripts/katello/locale/te/katello.js +3136 -3121
  46. data/app/assets/javascripts/katello/locale/tr/katello.js +3025 -3010
  47. data/app/assets/javascripts/katello/locale/vi/katello.js +3008 -2993
  48. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +3008 -2993
  49. data/app/assets/javascripts/katello/locale/zh/katello.js +3008 -2993
  50. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +5968 -5977
  51. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +4694 -4697
  52. data/app/assets/javascripts/katello/sync_management/sync_management.js +1 -0
  53. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +51 -124
  54. data/app/controllers/katello/api/rhsm/candlepin_dynflow_proxy_controller.rb +12 -20
  55. data/app/controllers/katello/api/v2/activation_keys_controller.rb +10 -4
  56. data/app/controllers/katello/api/v2/capsule_content_controller.rb +24 -0
  57. data/app/controllers/katello/api/v2/content_view_versions_controller.rb +9 -2
  58. data/app/controllers/katello/api/v2/debs_controller.rb +1 -1
  59. data/app/controllers/katello/api/v2/errata_controller.rb +1 -1
  60. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +12 -4
  61. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +3 -3
  62. data/app/controllers/katello/api/v2/organizations_controller.rb +0 -11
  63. data/app/controllers/katello/api/v2/packages_controller.rb +1 -1
  64. data/app/controllers/katello/api/v2/products_bulk_actions_controller.rb +1 -1
  65. data/app/controllers/katello/api/v2/repositories_controller.rb +18 -12
  66. data/app/controllers/katello/api/v2/repository_sets_controller.rb +2 -1
  67. data/app/controllers/katello/api/v2/simple_content_access_controller.rb +9 -22
  68. data/app/controllers/katello/concerns/api/v2/authorization.rb +1 -1
  69. data/app/helpers/katello/concerns/dashboard_helper_extensions.rb +0 -10
  70. data/app/helpers/katello/hosts_and_hostgroups_helper.rb +14 -2
  71. data/app/helpers/katello/katello_urls_helper.rb +26 -1
  72. data/app/helpers/katello/subscription_mailer_helper.rb +1 -1
  73. data/app/jobs/create_manifest_expire_soon_warning_notifications.rb +11 -0
  74. data/app/lib/actions/candlepin/owner/regenerate_upstream_identity_cert.rb +21 -0
  75. data/app/lib/actions/katello/capsule_content/sync.rb +1 -1
  76. data/app/lib/actions/katello/capsule_content/verify_checksum.rb +75 -0
  77. data/app/lib/actions/katello/content_view/promote.rb +1 -1
  78. data/app/lib/actions/katello/content_view/publish.rb +1 -1
  79. data/app/lib/actions/katello/content_view_version/verify_checksum.rb +29 -0
  80. data/app/lib/actions/katello/host/hypervisors_update.rb +1 -0
  81. data/app/lib/actions/katello/host/update_content_view.rb +2 -2
  82. data/app/lib/actions/katello/organization/manifest_import.rb +5 -0
  83. data/app/lib/actions/katello/organization/manifest_refresh.rb +3 -0
  84. data/app/lib/actions/katello/repository/metadata_generate.rb +7 -1
  85. data/app/lib/actions/katello/repository/remove_content.rb +1 -0
  86. data/app/lib/actions/katello/repository/sync.rb +2 -1
  87. data/app/lib/actions/katello/repository/upload_files.rb +1 -0
  88. data/app/lib/actions/pulp3/capsule_content/verify_checksum.rb +27 -0
  89. data/app/lib/actions/pulp3/orchestration/content_view_version/export_repository.rb +7 -9
  90. data/app/lib/actions/pulp3/orchestration/content_view_version/syncable_export.rb +5 -4
  91. data/app/lib/katello/concerns/base_template_scope_extensions.rb +7 -2
  92. data/app/lib/katello/errors.rb +4 -0
  93. data/app/lib/katello/http_resource.rb +6 -1
  94. data/app/lib/katello/resources/candlepin/consumer.rb +1 -1
  95. data/app/lib/katello/resources/candlepin/upstream_consumer.rb +18 -6
  96. data/app/lib/katello/resources/candlepin/upstream_job.rb +1 -1
  97. data/app/lib/katello/resources/cdn.rb +4 -13
  98. data/app/lib/katello/resources/registry.rb +25 -0
  99. data/app/mailers/katello/subscription_mailer.rb +3 -6
  100. data/app/models/katello/candlepin/repository_mapper.rb +1 -1
  101. data/app/models/katello/concerns/organization_extensions.rb +42 -3
  102. data/app/models/katello/content_view.rb +28 -0
  103. data/app/models/katello/content_view_environment_content_facet.rb +4 -2
  104. data/app/models/katello/glue/provider.rb +19 -12
  105. data/app/models/katello/glue/pulp/repos.rb +3 -8
  106. data/app/models/katello/host/content_facet.rb +1 -1
  107. data/app/models/katello/host/subscription_facet.rb +1 -1
  108. data/app/models/katello/host_collection.rb +12 -3
  109. data/app/models/katello/ping.rb +1 -1
  110. data/app/models/katello/repository.rb +33 -0
  111. data/app/models/katello/root_repository.rb +0 -4
  112. data/app/services/katello/content_unit_indexer.rb +9 -0
  113. data/app/services/katello/pulp3/alternate_content_source.rb +6 -8
  114. data/app/services/katello/pulp3/api/core.rb +13 -0
  115. data/app/services/katello/pulp3/api/yum.rb +11 -0
  116. data/app/services/katello/pulp3/docker_manifest.rb +5 -1
  117. data/app/services/katello/pulp3/repository/generic.rb +1 -1
  118. data/app/services/katello/pulp3/repository.rb +26 -6
  119. data/app/services/katello/pulp3/repository_mirror.rb +13 -12
  120. data/app/services/katello/pulp3/service_common.rb +2 -10
  121. data/app/services/katello/pulp3/smart_proxy_repository.rb +0 -2
  122. data/app/services/katello/ui_notifications/subscriptions/manifest_expire_soon_warning.rb +75 -0
  123. data/app/views/foreman/job_templates/update_package_-_katello_ansible_default.erb +5 -1
  124. data/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb +2 -2
  125. data/app/views/foreman/job_templates/upload_profile.erb +16 -0
  126. data/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl +9 -0
  127. data/app/views/katello/api/v2/docker_manifests/show.json.rabl +1 -0
  128. data/app/views/katello/api/v2/hosts/host_collections.json.rabl +5 -1
  129. data/app/views/katello/api/v2/organizations/show.json.rabl +9 -1
  130. data/app/views/katello/hosts/_errata_counts.html.erb +1 -1
  131. data/app/views/overrides/activation_keys/_host_environment_select.html.erb +1 -1
  132. data/app/views/overrides/activation_keys/_host_media_type_select.html.erb +15 -5
  133. data/app/views/overrides/activation_keys/_host_tab_pane.html.erb +1 -29
  134. data/config/routes/api/registry.rb +4 -8
  135. data/config/routes/api/v2.rb +2 -0
  136. data/db/migrate/20240423112842_add_fields_to_katello_docker_manifest.rb +8 -0
  137. data/db/migrate/20240502192021_change_katello_repository_rpms_id_seq_to_big_int.rb +9 -0
  138. data/db/seeds.d/109-katello-notification-blueprints.rb +6 -0
  139. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-repository-sets.controller.js +3 -3
  140. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-credentials/new/views/new-content-credential.html +2 -1
  141. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-repository-sets.controller.js +3 -3
  142. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +0 -15
  143. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +8 -6
  144. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +12 -10
  145. data/lib/katello/permission_creator.rb +3 -3
  146. data/lib/katello/permissions/registry_permissions.rb +4 -7
  147. data/lib/katello/plugin.rb +10 -16
  148. data/lib/katello/repository_types/ostree.rb +7 -0
  149. data/lib/katello/scheduled_jobs.rb +7 -1
  150. data/lib/katello/tasks/clean_backend_objects.rake +1 -1
  151. data/lib/katello/tasks/repository.rake +22 -0
  152. data/lib/katello/version.rb +1 -1
  153. data/locale/action_names.rb +4 -3
  154. data/locale/bn/katello.po +166 -151
  155. data/locale/bn_IN/katello.po +166 -151
  156. data/locale/ca/katello.po +166 -151
  157. data/locale/cs/katello.po +166 -151
  158. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  159. data/locale/cs_CZ/katello.po +172 -157
  160. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  161. data/locale/de/katello.po +178 -163
  162. data/locale/de_AT/katello.po +166 -151
  163. data/locale/de_DE/katello.po +166 -151
  164. data/locale/el/katello.po +166 -151
  165. data/locale/en/katello.po +166 -151
  166. data/locale/en_GB/katello.po +166 -151
  167. data/locale/en_US/katello.po +166 -151
  168. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  169. data/locale/es/katello.po +178 -163
  170. data/locale/et_EE/katello.po +166 -151
  171. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  172. data/locale/fr/katello.po +179 -164
  173. data/locale/gl/katello.po +166 -151
  174. data/locale/gu/katello.po +166 -151
  175. data/locale/he_IL/katello.po +166 -151
  176. data/locale/hi/katello.po +166 -151
  177. data/locale/id/katello.po +166 -151
  178. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  179. data/locale/it/katello.po +169 -154
  180. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  181. data/locale/ja/katello.po +179 -164
  182. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  183. data/locale/ka/katello.po +177 -162
  184. data/locale/katello.pot +1119 -1062
  185. data/locale/kn/katello.po +166 -151
  186. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  187. data/locale/ko/katello.po +174 -159
  188. data/locale/ml_IN/katello.po +166 -151
  189. data/locale/mr/katello.po +166 -151
  190. data/locale/nl_NL/katello.po +166 -151
  191. data/locale/or/katello.po +166 -151
  192. data/locale/pa/katello.po +166 -151
  193. data/locale/pl/katello.po +166 -151
  194. data/locale/pl_PL/katello.po +166 -151
  195. data/locale/pt/katello.po +166 -151
  196. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  197. data/locale/pt_BR/katello.po +178 -163
  198. data/locale/ro/katello.po +166 -151
  199. data/locale/ro_RO/katello.po +166 -151
  200. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  201. data/locale/ru/katello.po +171 -156
  202. data/locale/sl/katello.po +166 -151
  203. data/locale/sv_SE/katello.po +166 -151
  204. data/locale/ta/katello.po +166 -151
  205. data/locale/ta_IN/katello.po +166 -151
  206. data/locale/te/katello.po +166 -151
  207. data/locale/tr/katello.po +166 -151
  208. data/locale/vi/katello.po +166 -151
  209. data/locale/vi_VN/katello.po +166 -151
  210. data/locale/zh/katello.po +166 -151
  211. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  212. data/locale/zh_CN/katello.po +179 -164
  213. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  214. data/locale/zh_TW/katello.po +171 -156
  215. data/webpack/ForemanColumnExtensions/index.js +129 -0
  216. data/webpack/components/ActivationKeysSearch/ActivationKeysSearch.test.js +28 -0
  217. data/webpack/components/ActivationKeysSearch/index.js +222 -0
  218. data/webpack/components/Table/TableWrapper.js +14 -0
  219. data/webpack/components/extensions/HostDetails/ActionsBar/index.js +1 -1
  220. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +1 -1
  221. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packageInstallModal.test.js +1 -0
  222. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +1 -0
  223. data/webpack/components/extensions/Hosts/ActionsBar/index.js +20 -1
  224. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/BulkChangeHostCVModal.js +220 -0
  225. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/actions.js +23 -0
  226. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/index.js +25 -0
  227. data/webpack/components/extensions/Hosts/BulkActions/__tests__/bulkChangeHostCVModal.test.js +133 -0
  228. data/webpack/global_index.js +19 -0
  229. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +6 -3
  230. data/webpack/scenes/ContentViews/Publish/CVPublishForm.js +1 -1
  231. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +30 -0
  232. data/webpack/scenes/Hosts/ChangeContentSource/actions.js +3 -1
  233. data/webpack/scenes/Hosts/ChangeContentSource/components/ContentSourceForm.js +63 -25
  234. data/webpack/scenes/Hosts/ChangeContentSource/index.js +24 -16
  235. data/webpack/scenes/RedHatRepositories/__tests__/__snapshots__/RedHatRepositoriesPage.test.js.snap +1 -0
  236. data/webpack/scenes/Subscriptions/Manifest/ManageManifestModal.js +64 -5
  237. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/UpstreamSubscriptionsPage.js +16 -13
  238. data/webpack/scenes/Subscriptions/UpstreamSubscriptions/__tests__/__snapshots__/UpstreamSubscriptionsPage.test.js.snap +14 -8
  239. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsPage.test.js.snap +1 -0
  240. data/webpack/scenes/Subscriptions/components/SubscriptionsToolbar/SubscriptionsToolbar.js +1 -1
  241. metadata +59 -40
  242. data/app/assets/javascripts/katello/hosts/activation_key_edit.js +0 -167
  243. data/app/lib/actions/katello/host/upload_package_profile.rb +0 -45
  244. data/app/lib/actions/katello/host/upload_profiles.rb +0 -47
@@ -0,0 +1,129 @@
1
+ /* eslint-disable no-param-reassign */
2
+ import React from 'react';
3
+ import {
4
+ BugIcon,
5
+ SecurityIcon,
6
+ EnhancementIcon,
7
+ PackageIcon,
8
+ } from '@patternfly/react-icons';
9
+ import { Link } from 'react-router-dom';
10
+ import { Flex, FlexItem } from '@patternfly/react-core';
11
+ import { translate as __ } from 'foremanReact/common/I18n';
12
+ import RelativeDateTime from 'foremanReact/components/common/dates/RelativeDateTime';
13
+
14
+ const hostsIndexColumnExtensions = [
15
+ {
16
+ columnName: 'rhel_lifecycle_status',
17
+ title: __('RHEL Lifecycle status'),
18
+ wrapper: (hostDetails) => {
19
+ const rhelLifecycle = hostDetails?.rhel_lifecycle_status_label;
20
+ return rhelLifecycle || '—';
21
+ },
22
+ weight: 2000,
23
+ isSorted: true,
24
+ },
25
+ {
26
+ columnName: 'installable_updates',
27
+ title: __('Installable updates'),
28
+ wrapper: (hostDetails) => {
29
+ const errataCounts = hostDetails?.content_facet_attributes?.errata_counts;
30
+ const registered = !!hostDetails?.subscription_facet_attributes?.uuid;
31
+ const { security, bugfix, enhancement } = errataCounts ?? {};
32
+ const upgradableRpmCount = hostDetails?.content_facet_attributes?.upgradable_package_count;
33
+ if (!registered) return '—';
34
+ const hostErrataUrl = type => `hosts/${hostDetails?.name}#/Content/errata?type=${type}&show=installable`;
35
+ return (
36
+ <Flex alignContent={{ default: 'alignContentSpaceBetween' }}>
37
+ {security !== undefined &&
38
+ <FlexItem>
39
+ <SecurityIcon color="#0066cc" />
40
+ <Link to={hostErrataUrl('security')}>{security}</Link>
41
+ </FlexItem>
42
+ }
43
+ {bugfix !== undefined &&
44
+ <FlexItem>
45
+ <BugIcon color="#8bc1f7" />
46
+ <Link to={hostErrataUrl('bugfix')}>{bugfix}</Link>
47
+ </FlexItem>
48
+ }
49
+ {enhancement !== undefined &&
50
+ <FlexItem>
51
+ <EnhancementIcon color="#002f5d" />
52
+ <Link to={hostErrataUrl('enhancement')}>{enhancement}</Link>
53
+ </FlexItem>
54
+ }
55
+ {upgradableRpmCount !== undefined &&
56
+ <FlexItem>
57
+ <PackageIcon />
58
+ <Link to={`hosts/${hostDetails?.name}#/Content/Packages?status=Upgradable`}>{upgradableRpmCount}</Link>
59
+ </FlexItem>
60
+ }
61
+ </Flex>
62
+ );
63
+ },
64
+ weight: 2100,
65
+ isSorted: false,
66
+ },
67
+ {
68
+ columnName: 'last_checkin',
69
+ title: __('Last seen'),
70
+ wrapper: (hostDetails) => {
71
+ const lastCheckin =
72
+ hostDetails?.subscription_facet_attributes?.last_checkin;
73
+ return <RelativeDateTime defaultValue="—" date={lastCheckin} />;
74
+ },
75
+ weight: 2200,
76
+ isSorted: true,
77
+ },
78
+ {
79
+ columnName: 'lifecycle_environment',
80
+ title: __('Lifecycle environment'),
81
+ wrapper: (hostDetails) => {
82
+ const lifecycleEnvironment =
83
+ hostDetails?.content_facet_attributes?.lifecycle_environment?.name;
84
+ return lifecycleEnvironment || '—';
85
+ },
86
+ weight: 2300,
87
+ isSorted: true,
88
+ },
89
+ {
90
+ columnName: 'content_view',
91
+ title: __('Content view'),
92
+ wrapper: (hostDetails) => {
93
+ const contentView =
94
+ hostDetails?.content_facet_attributes?.content_view?.name;
95
+ return contentView || '—';
96
+ },
97
+ weight: 2400,
98
+ isSorted: true,
99
+ },
100
+ {
101
+ columnName: 'content_source',
102
+ title: __('Content source'),
103
+ wrapper: (hostDetails) => {
104
+ const contentSource =
105
+ hostDetails?.content_facet_attributes?.content_source_name;
106
+ return contentSource || '—';
107
+ },
108
+ weight: 2500,
109
+ isSorted: false,
110
+ },
111
+ {
112
+ columnName: 'registered_at',
113
+ title: __('Registered at'),
114
+ wrapper: (hostDetails) => {
115
+ const registeredAt = hostDetails?.subscription_facet_attributes?.registered_at;
116
+ return <RelativeDateTime defaultValue="—" date={registeredAt} />;
117
+ },
118
+ weight: 2600,
119
+ isSorted: true,
120
+ },
121
+ ];
122
+
123
+ hostsIndexColumnExtensions.forEach((column) => {
124
+ column.tableName = 'hosts';
125
+ column.categoryName = 'Content';
126
+ column.categoryKey = 'content';
127
+ });
128
+
129
+ export default hostsIndexColumnExtensions;
@@ -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;
@@ -36,10 +36,12 @@ const TableWrapper = ({
36
36
  selectAll,
37
37
  selectAllMode,
38
38
  selectNone,
39
+ selectDefault,
39
40
  selectPage,
40
41
  areAllRowsOnPageSelected,
41
42
  areAllRowsSelected,
42
43
  selectedCount,
44
+ selectedDefaultCount,
43
45
  selectedResults,
44
46
  clearSelectedResults,
45
47
  emptySearchBody,
@@ -49,6 +51,8 @@ const TableWrapper = ({
49
51
  nodesBelowSearch,
50
52
  bookmarkController,
51
53
  readOnlyBookmarks,
54
+ inclusionSet,
55
+ exclusionSet,
52
56
  ...allTableProps
53
57
  }) => {
54
58
  const dispatch = useDispatch();
@@ -182,7 +186,9 @@ const TableWrapper = ({
182
186
  selectAll,
183
187
  selectPage,
184
188
  selectNone,
189
+ selectDefault,
185
190
  selectedCount,
191
+ selectedDefaultCount,
186
192
  pageRowCount,
187
193
  }
188
194
  }
@@ -298,11 +304,13 @@ TableWrapper.propTypes = {
298
304
  ])),
299
305
  displaySelectAllCheckbox: PropTypes.bool,
300
306
  selectedCount: PropTypes.number,
307
+ selectedDefaultCount: PropTypes.number,
301
308
  selectedResults: PropTypes.arrayOf(PropTypes.shape({})),
302
309
  clearSelectedResults: PropTypes.func,
303
310
  selectAll: PropTypes.func,
304
311
  selectAllMode: PropTypes.bool,
305
312
  selectNone: PropTypes.func,
313
+ selectDefault: PropTypes.func,
306
314
  selectPage: PropTypes.func,
307
315
  areAllRowsOnPageSelected: PropTypes.func,
308
316
  areAllRowsSelected: PropTypes.func,
@@ -314,6 +322,8 @@ TableWrapper.propTypes = {
314
322
  bookmarkController: PropTypes.string,
315
323
  readOnlyBookmarks: PropTypes.bool,
316
324
  resetFilters: PropTypes.func,
325
+ inclusionSet: PropTypes.oneOfType([PropTypes.array, PropTypes.element, PropTypes.object]),
326
+ exclusionSet: PropTypes.oneOfType([PropTypes.array, PropTypes.element, PropTypes.object]),
317
327
  };
318
328
 
319
329
  TableWrapper.defaultProps = {
@@ -329,11 +339,13 @@ TableWrapper.defaultProps = {
329
339
  toggleGroup: null,
330
340
  displaySelectAllCheckbox: false,
331
341
  selectedCount: 0,
342
+ selectedDefaultCount: 0,
332
343
  selectedResults: [],
333
344
  clearSelectedResults: noop,
334
345
  selectAll: undefined,
335
346
  selectAllMode: false,
336
347
  selectNone: undefined,
348
+ selectDefault: undefined,
337
349
  selectPage: undefined,
338
350
  areAllRowsOnPageSelected: noop,
339
351
  areAllRowsSelected: noop,
@@ -346,6 +358,8 @@ TableWrapper.defaultProps = {
346
358
  readOnlyBookmarks: false,
347
359
  resetFilters: undefined,
348
360
  autocompleteQueryParams: undefined,
361
+ inclusionSet: new Set([]),
362
+ exclusionSet: new Set([]),
349
363
  };
350
364
 
351
365
  export default TableWrapper;
@@ -65,7 +65,7 @@ const HostActionsBar = () => {
65
65
  <DropdownItem
66
66
  ouiaId="katello-change-host-content-source"
67
67
  key="katello-change-host-content-source"
68
- href={foremanUrl(`/change_host_content_source?host_id=${hostDetails?.id}`)}
68
+ href={foremanUrl(`/change_host_content_source?host_id=${hostDetails?.id}&initialContentSourceId=${hostDetails?.content_facet_attributes?.content_source_id}`)}
69
69
  icon={<CubeIcon />}
70
70
  >
71
71
  {__('Change content source')}
@@ -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
  },
@@ -1,8 +1,11 @@
1
- import React, { useContext } from 'react';
1
+ import React, { useContext, useEffect } from 'react';
2
+ import { useDispatch } from 'react-redux';
2
3
  import { DropdownItem } from '@patternfly/react-core';
3
4
  import { translate as __ } from 'foremanReact/common/I18n';
4
5
  import { foremanUrl } from 'foremanReact/common/helpers';
5
6
  import { ForemanHostsIndexActionsBarContext } from 'foremanReact/components/HostsIndex';
7
+ import { useForemanModal } from 'foremanReact/components/ForemanModal/ForemanModalHooks';
8
+ import { addModal } from 'foremanReact/components/ForemanModal/ForemanModalActions';
6
9
 
7
10
  const HostActionsBar = () => {
8
11
  const {
@@ -11,6 +14,14 @@ const HostActionsBar = () => {
11
14
  selectAllMode,
12
15
  } = useContext(ForemanHostsIndexActionsBarContext);
13
16
 
17
+ const dispatch = useDispatch();
18
+ useEffect(() => {
19
+ dispatch(addModal({
20
+ id: 'bulk-change-cv-modal',
21
+ }));
22
+ }, [dispatch]);
23
+ const { setModalOpen } = useForemanModal({ id: 'bulk-change-cv-modal' });
24
+
14
25
  let href = '';
15
26
  if (selectAllMode) {
16
27
  const query = fetchBulkParams({ selectAllQuery: 'created_at < "1 second ago"' });
@@ -29,6 +40,14 @@ const HostActionsBar = () => {
29
40
  >
30
41
  {__('Change content source')}
31
42
  </DropdownItem>
43
+ <DropdownItem
44
+ ouiaId="bulk-change-cv-dropdown-item"
45
+ key="bulk-change-cv-dropdown-item"
46
+ onClick={setModalOpen}
47
+ isDisabled={selectedCount === 0}
48
+ >
49
+ {__('Change content view environments')}
50
+ </DropdownItem>
32
51
  </>
33
52
  );
34
53
  };