katello 4.15.0 → 4.16.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 (505) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/common/katello_object.js +2 -2
  3. data/app/assets/javascripts/katello/locale/bn/katello.js +248 -263
  4. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +247 -262
  5. data/app/assets/javascripts/katello/locale/ca/katello.js +247 -262
  6. data/app/assets/javascripts/katello/locale/cs/katello.js +521 -536
  7. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +253 -268
  8. data/app/assets/javascripts/katello/locale/de/katello.js +248 -263
  9. data/app/assets/javascripts/katello/locale/de_AT/katello.js +247 -262
  10. data/app/assets/javascripts/katello/locale/de_DE/katello.js +249 -264
  11. data/app/assets/javascripts/katello/locale/el/katello.js +251 -266
  12. data/app/assets/javascripts/katello/locale/en/katello.js +253 -268
  13. data/app/assets/javascripts/katello/locale/en_GB/katello.js +263 -278
  14. data/app/assets/javascripts/katello/locale/en_US/katello.js +247 -262
  15. data/app/assets/javascripts/katello/locale/es/katello.js +248 -263
  16. data/app/assets/javascripts/katello/locale/et_EE/katello.js +247 -262
  17. data/app/assets/javascripts/katello/locale/fr/katello.js +250 -265
  18. data/app/assets/javascripts/katello/locale/gl/katello.js +247 -262
  19. data/app/assets/javascripts/katello/locale/gu/katello.js +249 -264
  20. data/app/assets/javascripts/katello/locale/he_IL/katello.js +249 -264
  21. data/app/assets/javascripts/katello/locale/hi/katello.js +248 -263
  22. data/app/assets/javascripts/katello/locale/id/katello.js +261 -275
  23. data/app/assets/javascripts/katello/locale/it/katello.js +252 -267
  24. data/app/assets/javascripts/katello/locale/ja/katello.js +250 -265
  25. data/app/assets/javascripts/katello/locale/ka/katello.js +247 -262
  26. data/app/assets/javascripts/katello/locale/kn/katello.js +248 -263
  27. data/app/assets/javascripts/katello/locale/ko/katello.js +248 -263
  28. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +247 -262
  29. data/app/assets/javascripts/katello/locale/mr/katello.js +248 -263
  30. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +252 -267
  31. data/app/assets/javascripts/katello/locale/or/katello.js +248 -263
  32. data/app/assets/javascripts/katello/locale/pa/katello.js +248 -263
  33. data/app/assets/javascripts/katello/locale/pl/katello.js +248 -263
  34. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +247 -262
  35. data/app/assets/javascripts/katello/locale/pt/katello.js +248 -263
  36. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +248 -263
  37. data/app/assets/javascripts/katello/locale/ro/katello.js +247 -262
  38. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +247 -262
  39. data/app/assets/javascripts/katello/locale/ru/katello.js +253 -268
  40. data/app/assets/javascripts/katello/locale/sl/katello.js +247 -262
  41. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +248 -263
  42. data/app/assets/javascripts/katello/locale/ta/katello.js +248 -263
  43. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +247 -262
  44. data/app/assets/javascripts/katello/locale/te/katello.js +248 -263
  45. data/app/assets/javascripts/katello/locale/tr/katello.js +247 -262
  46. data/app/assets/javascripts/katello/locale/vi/katello.js +247 -262
  47. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +247 -262
  48. data/app/assets/javascripts/katello/locale/zh/katello.js +247 -262
  49. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +250 -265
  50. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +255 -270
  51. data/app/assets/javascripts/katello/sync_management/sync_management.js +16 -2
  52. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +33 -9
  53. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +1 -1
  54. data/app/controllers/katello/api/v2/activation_keys_controller.rb +4 -9
  55. data/app/controllers/katello/api/v2/content_uploads_controller.rb +2 -1
  56. data/app/controllers/katello/api/v2/content_view_filter_rules_controller.rb +3 -1
  57. data/app/controllers/katello/api/v2/content_views_controller.rb +4 -1
  58. data/app/controllers/katello/api/v2/environments_controller.rb +4 -0
  59. data/app/controllers/katello/api/v2/errata_controller.rb +12 -1
  60. data/app/controllers/katello/api/v2/flatpak_remote_repositories_controller.rb +81 -0
  61. data/app/controllers/katello/api/v2/flatpak_remotes_controller.rb +86 -0
  62. data/app/controllers/katello/api/v2/host_bootc_images_controller.rb +71 -0
  63. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +4 -6
  64. data/app/controllers/katello/api/v2/hosts_bulk_actions_controller.rb +3 -5
  65. data/app/controllers/katello/api/v2/repositories_controller.rb +18 -3
  66. data/app/controllers/katello/concerns/api/v2/hosts_controller_extensions.rb +0 -8
  67. data/app/controllers/katello/concerns/api/v2/http_proxies_controller_extensions.rb +17 -0
  68. data/app/controllers/katello/concerns/http_proxies_controller_extensions.rb +20 -0
  69. data/app/controllers/katello/sync_management_controller.rb +6 -2
  70. data/app/helpers/katello/content_source_helper.rb +8 -1
  71. data/app/helpers/katello/content_view_helper.rb +3 -1
  72. data/app/lib/actions/candlepin/activation_key/create.rb +1 -3
  73. data/app/lib/actions/candlepin/activation_key/update.rb +1 -3
  74. data/app/lib/actions/candlepin/environment/set_content.rb +17 -4
  75. data/app/lib/actions/katello/activation_key/create.rb +1 -2
  76. data/app/lib/actions/katello/activation_key/update.rb +2 -4
  77. data/app/lib/actions/katello/capsule_content/refresh_repos.rb +7 -1
  78. data/app/lib/actions/katello/content_view/presenters/incremental_updates_presenter.rb +3 -2
  79. data/app/lib/actions/katello/content_view/publish.rb +7 -2
  80. data/app/lib/actions/katello/content_view_environment/reassign_objects.rb +11 -2
  81. data/app/lib/actions/katello/content_view_version/incremental_update.rb +31 -22
  82. data/app/lib/actions/katello/flatpak/mirror_remote_repository.rb +30 -0
  83. data/app/lib/actions/katello/flatpak/scan_remote.rb +17 -12
  84. data/app/lib/actions/katello/host/update_system_purpose.rb +1 -6
  85. data/app/lib/actions/pulp3/orchestration/repository/import_upload.rb +18 -2
  86. data/app/lib/actions/pulp3/repository/multi_copy_units.rb +4 -0
  87. data/app/lib/katello/api/v2/rendering.rb +2 -1
  88. data/app/lib/katello/concerns/base_template_scope_extensions.rb +3 -4
  89. data/app/lib/katello/concerns/bookmark_controller_validator_extensions.rb +13 -0
  90. data/app/lib/katello/resources/candlepin/activation_key.rb +3 -5
  91. data/app/lib/katello/resources/candlepin/product.rb +2 -1
  92. data/app/lib/katello/util/cveak_migrator.rb +3 -2
  93. data/app/lib/katello/validators/alternate_content_source_path_validator.rb +2 -2
  94. data/app/lib/katello/validators/container_image_name_validator.rb +1 -1
  95. data/app/lib/katello/validators/content_validator.rb +1 -1
  96. data/app/lib/katello/validators/content_view_environment_coherent_default_validator.rb +2 -2
  97. data/app/lib/katello/validators/content_view_environment_org_validator.rb +2 -2
  98. data/app/lib/katello/validators/content_view_environment_validator.rb +3 -3
  99. data/app/lib/katello/validators/content_view_erratum_filter_rule_validator.rb +12 -12
  100. data/app/lib/katello/validators/content_view_filter_version_validator.rb +1 -1
  101. data/app/lib/katello/validators/generated_content_view_validator.rb +1 -1
  102. data/app/lib/katello/validators/gpg_key_content_type_validator.rb +2 -2
  103. data/app/lib/katello/validators/gpg_key_content_validator.rb +5 -5
  104. data/app/lib/katello/validators/katello_label_format_validator.rb +4 -4
  105. data/app/lib/katello/validators/katello_name_format_validator.rb +2 -2
  106. data/app/lib/katello/validators/katello_url_format_validator.rb +1 -1
  107. data/app/lib/katello/validators/library_presence_validator.rb +1 -1
  108. data/app/lib/katello/validators/no_trailing_space_validator.rb +1 -1
  109. data/app/lib/katello/validators/non_library_environment_validator.rb +1 -1
  110. data/app/lib/katello/validators/not_in_library_validator.rb +1 -1
  111. data/app/lib/katello/validators/path_descendents_validator.rb +1 -1
  112. data/app/lib/katello/validators/prior_validator.rb +1 -1
  113. data/app/lib/katello/validators/product_unique_attribute_validator.rb +1 -1
  114. data/app/lib/katello/validators/repo_disablement_validator.rb +2 -2
  115. data/app/lib/katello/validators/root_repository_unique_attribute_validator.rb +1 -1
  116. data/app/lib/katello/validators/self_reference_environment_validator.rb +1 -1
  117. data/app/lib/katello/validators/unique_field_in_org.rb +1 -1
  118. data/app/models/katello/activation_key.rb +6 -17
  119. data/app/models/katello/authorization/flatpak_remote.rb +33 -0
  120. data/app/models/katello/concerns/content_facet_host_extensions.rb +2 -1
  121. data/app/models/katello/concerns/host_managed_extensions.rb +8 -5
  122. data/app/models/katello/concerns/http_proxy_extensions.rb +4 -0
  123. data/app/models/katello/concerns/smart_proxy_extensions.rb +30 -6
  124. data/app/models/katello/concerns/subscription_facet_host_extensions.rb +0 -8
  125. data/app/models/katello/content_view.rb +4 -2
  126. data/app/models/katello/content_view_erratum_filter.rb +18 -2
  127. data/app/models/katello/content_view_version.rb +6 -0
  128. data/app/models/katello/docker_manifest.rb +8 -0
  129. data/app/models/katello/docker_manifest_list.rb +8 -0
  130. data/app/models/katello/erratum.rb +8 -2
  131. data/app/models/katello/flatpak_remote.rb +16 -0
  132. data/app/models/katello/flatpak_remote_repository.rb +18 -0
  133. data/app/models/katello/flatpak_remote_repository_manifest.rb +4 -0
  134. data/app/models/katello/glue/candlepin/pool.rb +1 -1
  135. data/app/models/katello/glue/pulp/repos.rb +12 -1
  136. data/app/models/katello/host/content_facet.rb +42 -3
  137. data/app/models/katello/host/subscription_facet.rb +4 -12
  138. data/app/models/katello/repository.rb +21 -8
  139. data/app/models/katello/rhel_lifecycle_status.rb +6 -0
  140. data/app/models/katello/root_repository.rb +6 -8
  141. data/app/models/katello/upstream_pool.rb +1 -0
  142. data/app/overrides/add_organization_attributes.rb +6 -0
  143. data/app/services/katello/product_content_finder.rb +2 -1
  144. data/app/services/katello/pulp3/content_view_version/importable_repositories.rb +1 -1
  145. data/app/services/katello/pulp3/docker_manifest.rb +3 -2
  146. data/app/services/katello/pulp3/docker_manifest_list.rb +3 -2
  147. data/app/services/katello/pulp3/repository/ansible_collection.rb +8 -1
  148. data/app/services/katello/pulp3/repository/apt.rb +16 -18
  149. data/app/services/katello/pulp3/repository/file.rb +4 -2
  150. data/app/services/katello/pulp3/repository.rb +4 -0
  151. data/app/services/katello/pulp3/task.rb +2 -2
  152. data/app/views/foreman/job_templates/bootc_action.erb +26 -0
  153. data/app/views/foreman/job_templates/bootc_rollback.erb +13 -0
  154. data/app/views/foreman/job_templates/bootc_status.erb +13 -0
  155. data/app/views/foreman/job_templates/bootc_switch.erb +13 -0
  156. data/app/views/foreman/job_templates/bootc_upgrade.erb +13 -0
  157. data/app/views/foreman/job_templates/flatpak_install.erb +23 -0
  158. data/app/views/foreman/job_templates/flatpak_login_action.erb +30 -0
  159. data/app/views/foreman/job_templates/flatpak_setup.erb +27 -0
  160. data/app/views/foreman/job_templates/install_errata_by_search_query.erb +1 -1
  161. data/app/views/foreman/job_templates/install_errata_by_search_query_-_katello_ansible_default.erb +1 -1
  162. data/app/views/foreman/job_templates/resolve_traces_-_katello_ansible_default.erb +1 -1
  163. data/app/views/foreman/job_templates/update_packages_by_search_query_-_katello_ansible_default.erb +1 -1
  164. data/app/views/katello/api/v2/activation_keys/base.json.rabl +0 -4
  165. data/app/views/katello/api/v2/content_facet/base.json.rabl +2 -1
  166. data/app/views/katello/api/v2/content_view_filter_rules/show.json.rabl +1 -0
  167. data/app/views/katello/api/v2/content_view_versions/base.json.rabl +5 -1
  168. data/app/views/katello/api/v2/docker_manifest_lists/show.json.rabl +1 -1
  169. data/app/views/katello/api/v2/docker_manifests/show.json.rabl +1 -1
  170. data/app/views/katello/api/v2/flatpak_remote_repositories/base.json.rabl +4 -0
  171. data/app/views/katello/api/v2/flatpak_remote_repositories/index.json.rabl +7 -0
  172. data/app/views/katello/api/v2/flatpak_remote_repositories/show.json.rabl +13 -0
  173. data/app/views/katello/api/v2/flatpak_remotes/base.json.rabl +5 -0
  174. data/app/views/katello/api/v2/flatpak_remotes/index.json.rabl +8 -0
  175. data/app/views/katello/api/v2/flatpak_remotes/permissions.json.rabl +11 -0
  176. data/app/views/katello/api/v2/flatpak_remotes/show.json.rabl +3 -0
  177. data/app/views/katello/api/v2/hosts/base.json.rabl +0 -8
  178. data/app/views/katello/api/v2/hosts/os_attributes.json.rabl +13 -0
  179. data/app/views/katello/api/v2/http_proxies/show.json.rabl +1 -0
  180. data/app/views/katello/api/v2/repositories/base.json.rabl +1 -0
  181. data/app/views/katello/api/v2/repositories/show.json.rabl +1 -0
  182. data/app/views/katello/api/v2/subscription_facet/base.json.rabl +0 -4
  183. data/app/views/katello/api/v2/subscriptions/show.json.rabl +1 -1
  184. data/app/views/overrides/activation_keys/_host_synced_content_select.html.erb +10 -4
  185. data/app/views/overrides/http_proxies/_update_setting_input.html.erb +18 -0
  186. data/app/views/overrides/organizations/_step_1_override.html.erb +5 -0
  187. data/config/initializers/monkeys.rb +0 -1
  188. data/config/initializers/pagelets.rb +6 -0
  189. data/config/routes/api/registry.rb +1 -0
  190. data/config/routes/api/v2.rb +21 -0
  191. data/config/routes/overrides.rb +1 -0
  192. data/config/routes.rb +2 -0
  193. data/db/migrate/20190605014649_add_purpose_addons.rb +0 -12
  194. data/db/migrate/20200213184848_create_evr_type.rb +126 -1
  195. data/db/migrate/20200818192230_update_system_purpose_status.rb +0 -1
  196. data/db/migrate/20240207191223_remove_entitlement_mode_host_statuses.rb +0 -1
  197. data/db/migrate/20240924161240_katello_recreate_evr_constructs.rb +160 -0
  198. data/db/migrate/20241022121706_add_sync_dependencies_option.rb +5 -0
  199. data/db/migrate/20241101144625_remove_system_purpose_addons.rb +9 -0
  200. data/db/migrate/20241107002541_add_registry_url_to_katello_flatpak_remotes.rb +5 -0
  201. data/db/migrate/20241112145802_add_manifest_entity_to_content_facets.rb +7 -0
  202. data/db/migrate/20241120213713_add_allow_other_types_to_content_view_erratum_filter_rules.rb +6 -0
  203. data/db/migrate/20241206183052_add_content_type_to_container_manifests_and_lists.rb +9 -0
  204. data/db/seeds.d/75-job_templates.rb +5 -1
  205. data/engines/bastion/vendor/assets/javascripts/bastion/angular/angular.js +2 -2
  206. data/engines/bastion/vendor/assets/javascripts/bastion/angular-bootstrap/ui-bootstrap-tpls.js +3 -3
  207. data/engines/bastion/vendor/assets/javascripts/bastion/angular-bootstrap/ui-bootstrap.js +3 -3
  208. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-details-info.controller.js +0 -5
  209. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-details.controller.js +1 -41
  210. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/views/activation-key-info.html +0 -12
  211. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/common/views/subscription-add-or-remove.html +1 -1
  212. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-system-purpose-modal.controller.js +1 -41
  213. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/content-hosts-bulk-traces-modal.controller.js +1 -1
  214. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-errata-modal.html +2 -2
  215. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-subscriptions-modal.html +2 -2
  216. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/bulk/views/content-hosts-bulk-system-purpose-modal.html +0 -13
  217. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-errata.html +1 -1
  218. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-details-info.controller.js +0 -5
  219. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-details.controller.js +1 -35
  220. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-info.html +2 -14
  221. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/views/content-host-provisioning-info.html +1 -1
  222. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/content-hosts.html +2 -2
  223. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/debs.controller.js +46 -22
  224. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/details/deb-content-views.controller.js +2 -1
  225. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/debs/views/debs.html +4 -0
  226. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-content-views.html +1 -1
  227. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-details.html +1 -1
  228. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-errata.html +1 -1
  229. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/errata-tasks-list.html +2 -1
  230. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +85 -29
  231. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn.po +338 -93
  232. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn_IN.po +340 -95
  233. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ca.po +340 -95
  234. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/cs_CZ.po +341 -96
  235. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de.po +31 -54
  236. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_AT.po +338 -93
  237. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_DE.po +338 -93
  238. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/el.po +341 -96
  239. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_GB.po +349 -104
  240. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_US.po +338 -93
  241. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/es.po +33 -56
  242. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/et_EE.po +338 -93
  243. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +80 -68
  244. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gl.po +340 -95
  245. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gu.po +340 -95
  246. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/he_IL.po +339 -111
  247. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/hi.po +340 -95
  248. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/id.po +379 -130
  249. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/it.po +342 -103
  250. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +70 -83
  251. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ka.po +84 -90
  252. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/kn.po +340 -95
  253. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +130 -138
  254. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ml_IN.po +338 -93
  255. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/mr.po +340 -95
  256. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/nl_NL.po +343 -97
  257. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/or.po +340 -95
  258. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pa.po +340 -95
  259. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl.po +340 -95
  260. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl_PL.po +338 -93
  261. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt.po +338 -93
  262. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt_BR.po +40 -63
  263. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro.po +338 -93
  264. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro_RO.po +338 -93
  265. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ru.po +343 -102
  266. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sl.po +338 -93
  267. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sv_SE.po +340 -95
  268. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta.po +338 -93
  269. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta_IN.po +340 -95
  270. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/te.po +340 -95
  271. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/tr.po +338 -93
  272. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi.po +338 -93
  273. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi_VN.po +338 -93
  274. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh.po +338 -93
  275. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +70 -83
  276. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_TW.po +362 -105
  277. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/translations.js +29 -29
  278. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/packages/details/views/package-info.html +1 -1
  279. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/products-bulk-advanced-sync-modal.controller.js +1 -1
  280. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/bulk/views/products-bulk-sync-plan-modal.html +2 -2
  281. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +24 -8
  282. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-tasks.html +2 -1
  283. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/new-repository.controller.js +2 -2
  284. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +21 -8
  285. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/os-versions.service.js +1 -0
  286. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/views/product-info.html +3 -3
  287. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/views/product-tasks.html +2 -1
  288. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +4 -2
  289. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery.html +2 -1
  290. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.controller.js +3 -3
  291. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/views/partials/product-table-sync-status.html +1 -1
  292. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/views/subscription-start-date.html +1 -1
  293. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/details/views/sync-plan-info.html +1 -1
  294. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/sync-plans/views/sync-plans.html +2 -2
  295. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/tasks-table.directive.js +2 -1
  296. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/views/task-details.html +2 -2
  297. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/views/tasks-index.html +1 -1
  298. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/views/tasks-table.html +1 -1
  299. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/tasks/views/user-tasks-table.html +1 -1
  300. data/lib/katello/engine.rb +6 -0
  301. data/lib/katello/permission_creator.rb +37 -0
  302. data/lib/katello/permissions/host_permissions.rb +2 -0
  303. data/lib/katello/permissions/registry_permissions.rb +1 -0
  304. data/lib/katello/plugin.rb +18 -1
  305. data/lib/katello/repository_types/docker.rb +0 -1
  306. data/lib/katello/repository_types/python.rb +1 -1
  307. data/lib/katello/version.rb +1 -1
  308. data/lib/katello.rb +2 -0
  309. data/locale/Makefile +20 -24
  310. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  311. data/locale/bn/katello.po +248 -263
  312. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  313. data/locale/bn_IN/katello.po +247 -262
  314. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  315. data/locale/ca/katello.po +247 -262
  316. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  317. data/locale/cs/katello.po +524 -799
  318. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  319. data/locale/cs_CZ/katello.po +254 -269
  320. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  321. data/locale/de/katello.po +248 -263
  322. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  323. data/locale/de_AT/katello.po +247 -262
  324. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  325. data/locale/de_DE/katello.po +250 -265
  326. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  327. data/locale/el/katello.po +252 -267
  328. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  329. data/locale/en/katello.po +257 -271
  330. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  331. data/locale/en_GB/katello.po +264 -279
  332. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  333. data/locale/en_US/katello.po +247 -262
  334. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  335. data/locale/es/katello.po +248 -263
  336. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  337. data/locale/et_EE/katello.po +247 -262
  338. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  339. data/locale/fr/katello.po +250 -265
  340. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  341. data/locale/gl/katello.po +247 -262
  342. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  343. data/locale/gu/katello.po +250 -265
  344. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  345. data/locale/he_IL/katello.po +249 -264
  346. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  347. data/locale/hi/katello.po +248 -263
  348. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  349. data/locale/id/katello.po +264 -275
  350. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  351. data/locale/it/katello.po +254 -270
  352. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  353. data/locale/ja/katello.po +250 -265
  354. data/locale/ka/LC_MESSAGES/katello.mo +0 -0
  355. data/locale/ka/katello.po +247 -262
  356. data/locale/katello.pot +1216 -1215
  357. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  358. data/locale/kn/katello.po +248 -263
  359. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  360. data/locale/ko/katello.po +248 -263
  361. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  362. data/locale/ml_IN/katello.po +247 -262
  363. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  364. data/locale/mr/katello.po +248 -263
  365. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  366. data/locale/nl_NL/katello.po +254 -268
  367. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  368. data/locale/or/katello.po +248 -263
  369. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  370. data/locale/pa/katello.po +248 -263
  371. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  372. data/locale/pl/katello.po +249 -264
  373. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  374. data/locale/pl_PL/katello.po +247 -262
  375. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  376. data/locale/pt/katello.po +248 -263
  377. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  378. data/locale/pt_BR/katello.po +248 -263
  379. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  380. data/locale/ro/katello.po +247 -262
  381. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  382. data/locale/ro_RO/katello.po +247 -262
  383. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  384. data/locale/ru/katello.po +255 -270
  385. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  386. data/locale/sl/katello.po +247 -262
  387. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  388. data/locale/sv_SE/katello.po +249 -264
  389. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  390. data/locale/ta/katello.po +248 -263
  391. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  392. data/locale/ta_IN/katello.po +247 -262
  393. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  394. data/locale/te/katello.po +248 -263
  395. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  396. data/locale/tr/katello.po +247 -262
  397. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  398. data/locale/vi/katello.po +247 -262
  399. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  400. data/locale/vi_VN/katello.po +247 -262
  401. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  402. data/locale/zh/katello.po +247 -262
  403. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  404. data/locale/zh_CN/katello.po +250 -265
  405. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  406. data/locale/zh_TW/katello.po +257 -272
  407. data/webpack/ForemanColumnExtensions/index.js +73 -3
  408. data/webpack/ForemanColumnExtensions/index.scss +9 -0
  409. data/webpack/__mocks__/foremanReact/Root/Context/ForemanContext.js +1 -0
  410. data/webpack/components/extensions/HostDetails/Cards/ErrataOverviewCard.js +1 -1
  411. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeCard.js +1 -20
  412. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/SystemPurposeEditModal.js +3 -61
  413. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/__tests__/SystemPurposeCard.test.js +0 -5
  414. data/webpack/components/extensions/HostDetails/Cards/SystemPurposeCard/__tests__/SystemPurposeEditModal.test.js +0 -5
  415. data/webpack/components/extensions/HostDetails/DetailsTabCards/ImageModeCard.js +87 -0
  416. data/webpack/components/extensions/HostDetails/DetailsTabCards/RegistrationCard.js +28 -2
  417. data/webpack/components/extensions/HostDetails/Tabs/DebsTab/DebInstallModal.js +1 -1
  418. data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +2 -2
  419. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackageInstallModal.js +1 -1
  420. data/webpack/components/extensions/Hosts/FontAwesomeImageModeIcon.js +55 -0
  421. data/webpack/containers/Application/config.js +5 -0
  422. data/webpack/global_index.js +3 -1
  423. data/webpack/redux/actions/RedHatRepositories/helpers.js +9 -0
  424. data/webpack/scenes/BootedContainerImages/BootedContainerImagesConstants.js +5 -0
  425. data/webpack/scenes/BootedContainerImages/BootedContainerImagesPage.js +242 -0
  426. data/webpack/scenes/BootedContainerImages/__tests__/bootedContainerImages.fixtures.js +42 -0
  427. data/webpack/scenes/BootedContainerImages/__tests__/bootedContainerImagesPage.test.js +233 -0
  428. data/webpack/scenes/BootedContainerImages/index.js +4 -0
  429. data/webpack/scenes/ContentViews/Delete/__tests__/affectedHosts.fixtures.json +1 -2
  430. data/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js +39 -7
  431. data/webpack/scenes/ContentViews/Details/Filters/CVErrataIDFilterContent.js +13 -2
  432. data/webpack/scenes/ContentViews/Details/Filters/CVFilterDetailType.js +1 -1
  433. data/webpack/scenes/ContentViews/Details/Filters/__tests__/cvErrataDateFilterContent.test.js +1 -0
  434. data/webpack/scenes/ContentViews/Details/Repositories/RepoIcon.js +4 -2
  435. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/hosts.fixtures.json +1 -2
  436. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVReassignActivationKeysForm.js +20 -1
  437. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVReassignHostsForm.js +20 -1
  438. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionRemoveReview.js +128 -18
  439. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvAffectedActivationKeys.fixture.json +0 -1
  440. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvAffectedHosts.fixture.json +1 -2
  441. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvVersionRemove.test.js +6 -4
  442. data/webpack/scenes/ContentViews/Details/Versions/Delete/affectedActivationKeys.js +5 -1
  443. data/webpack/scenes/ContentViews/Details/Versions/Delete/affectedHosts.js +6 -1
  444. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +1 -1
  445. data/webpack/scenes/ContentViews/expansions/DetailsExpansion.js +5 -1
  446. data/webpack/scenes/SmartProxy/ExpandableCvDetails.js +4 -2
  447. data/webpack/scenes/SmartProxy/__tests__/SmartProxyContentTest.js +38 -0
  448. data/webpack/scenes/Subscriptions/Details/SubscriptionPurposeAttributes.js +0 -1
  449. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetailInfo.test.js.snap +0 -10
  450. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +0 -3
  451. data/webpack/scenes/Subscriptions/Details/__tests__/subscriptionDetails.fixtures.js +0 -1
  452. metadata +61 -58
  453. data/app/models/katello/activation_key_purpose_addon.rb +0 -6
  454. data/app/models/katello/purpose_addon.rb +0 -11
  455. data/app/models/katello/subscription_facet_purpose_addon.rb +0 -6
  456. data/lib/monkeys/fx_sqlite_skip.rb +0 -13
  457. data/locale/action_names.rb +0 -181
  458. data/locale/bn/katello.po.time_stamp +0 -0
  459. data/locale/bn_IN/katello.po.time_stamp +0 -0
  460. data/locale/ca/katello.po.time_stamp +0 -0
  461. data/locale/cs/katello.po.time_stamp +0 -0
  462. data/locale/cs_CZ/katello.po.time_stamp +0 -0
  463. data/locale/de/katello.po.time_stamp +0 -0
  464. data/locale/de_AT/katello.po.time_stamp +0 -0
  465. data/locale/de_DE/katello.po.time_stamp +0 -0
  466. data/locale/el/katello.po.time_stamp +0 -0
  467. data/locale/en/katello.po.time_stamp +0 -0
  468. data/locale/en_GB/katello.po.time_stamp +0 -0
  469. data/locale/en_US/katello.po.time_stamp +0 -0
  470. data/locale/es/katello.po.time_stamp +0 -0
  471. data/locale/et_EE/katello.po.time_stamp +0 -0
  472. data/locale/fr/katello.po.time_stamp +0 -0
  473. data/locale/gl/katello.po.time_stamp +0 -0
  474. data/locale/gu/katello.po.time_stamp +0 -0
  475. data/locale/he_IL/katello.po.time_stamp +0 -0
  476. data/locale/hi/katello.po.time_stamp +0 -0
  477. data/locale/id/katello.po.time_stamp +0 -0
  478. data/locale/it/katello.po.time_stamp +0 -0
  479. data/locale/ja/katello.po.time_stamp +0 -0
  480. data/locale/ka/katello.po.time_stamp +0 -0
  481. data/locale/kn/katello.po.time_stamp +0 -0
  482. data/locale/ko/katello.po.time_stamp +0 -0
  483. data/locale/ml_IN/katello.po.time_stamp +0 -0
  484. data/locale/mr/katello.po.time_stamp +0 -0
  485. data/locale/nl_NL/katello.po.time_stamp +0 -0
  486. data/locale/or/katello.po.time_stamp +0 -0
  487. data/locale/pa/katello.po.time_stamp +0 -0
  488. data/locale/pl/katello.po.time_stamp +0 -0
  489. data/locale/pl_PL/katello.po.time_stamp +0 -0
  490. data/locale/pt/katello.po.time_stamp +0 -0
  491. data/locale/pt_BR/katello.po.time_stamp +0 -0
  492. data/locale/ro/katello.po.time_stamp +0 -0
  493. data/locale/ro_RO/katello.po.time_stamp +0 -0
  494. data/locale/ru/katello.po.time_stamp +0 -0
  495. data/locale/sl/katello.po.time_stamp +0 -0
  496. data/locale/sv_SE/katello.po.time_stamp +0 -0
  497. data/locale/ta/katello.po.time_stamp +0 -0
  498. data/locale/ta_IN/katello.po.time_stamp +0 -0
  499. data/locale/te/katello.po.time_stamp +0 -0
  500. data/locale/tr/katello.po.time_stamp +0 -0
  501. data/locale/vi/katello.po.time_stamp +0 -0
  502. data/locale/vi_VN/katello.po.time_stamp +0 -0
  503. data/locale/zh/katello.po.time_stamp +0 -0
  504. data/locale/zh_CN/katello.po.time_stamp +0 -0
  505. data/locale/zh_TW/katello.po.time_stamp +0 -0
@@ -46,7 +46,12 @@ const CVErrataDateFilterContent = ({
46
46
  selectCVFilterDetails(state, cvId, filterId), shallowEqual);
47
47
  const { repositories = [], rules } = filterDetails;
48
48
  const [{
49
- id, types, start_date: ruleStartDate, end_date: ruleEndDate, date_type: ruleDateType,
49
+ id,
50
+ types,
51
+ allow_other_types: ruleAllowOtherTypes,
52
+ start_date: ruleStartDate,
53
+ end_date: ruleEndDate,
54
+ date_type: ruleDateType,
50
55
  } = {}] = rules;
51
56
  const { permissions } = details;
52
57
  const [startDate, setStartDate] = useState(convertAPIDateToUIFormat(ruleStartDate));
@@ -54,12 +59,23 @@ const CVErrataDateFilterContent = ({
54
59
  const [dateType, setDateType] = useState(ruleDateType);
55
60
  const [dateTypeSelectOpen, setDateTypeSelectOpen] = useState(false);
56
61
  const [typeSelectOpen, setTypeSelectOpen] = useState(false);
57
- const [selectedTypes, setSelectedTypes] = useState(types);
58
62
  const dispatch = useDispatch();
59
63
  const [activeTabKey, setActiveTabKey] = useState(0);
60
64
  const [startEntry, setStartEntry] = useState(false);
61
65
  const [endEntry, setEndEntry] = useState(false);
62
66
 
67
+ const getInitialSelectedTypes = () => {
68
+ if (!types) {
69
+ return ['other'];
70
+ }
71
+ if (ruleAllowOtherTypes) {
72
+ return [...types, 'other'];
73
+ }
74
+ return types;
75
+ };
76
+
77
+ const [selectedTypes, setSelectedTypes] = useState(getInitialSelectedTypes());
78
+
63
79
  const onSave = () => {
64
80
  dispatch(editCVFilterRule(
65
81
  filterId,
@@ -68,8 +84,9 @@ const CVErrataDateFilterContent = ({
68
84
  content_view_filter_id: filterId,
69
85
  start_date: startDate && startDate !== '' ? dateParse(startDate) : null,
70
86
  end_date: endDate && endDate !== '' ? dateParse(endDate) : null,
71
- types: selectedTypes,
87
+ types: selectedTypes.filter(e => e !== 'other'),
72
88
  date_type: dateType,
89
+ allow_other_types: selectedTypes.includes('other'),
73
90
  },
74
91
  () => {
75
92
  dispatch({ type: CONTENT_VIEW_NEEDS_PUBLISH });
@@ -81,15 +98,21 @@ const CVErrataDateFilterContent = ({
81
98
  const resetFilters = () => {
82
99
  setStartDate(convertAPIDateToUIFormat(ruleStartDate));
83
100
  setEndDate(convertAPIDateToUIFormat(ruleEndDate));
84
- setSelectedTypes(types);
85
101
  setDateType(ruleDateType);
102
+ setSelectedTypes(getInitialSelectedTypes());
86
103
  };
87
104
 
88
105
  const onTypeSelect = (selection) => {
89
106
  if (selectedTypes.includes(selection)) {
107
+ // If the selection is the only selection remaining, do not allow it to be removed
90
108
  if (selectedTypes.length === 1) return;
109
+
110
+ // Filter out the current selection to deselect it
91
111
  setSelectedTypes(selectedTypes.filter(e => e !== selection));
92
- } else setSelectedTypes([...selectedTypes, selection]);
112
+ } else {
113
+ // Add the selection to the selected types
114
+ setSelectedTypes([...selectedTypes, selection]);
115
+ }
93
116
  };
94
117
 
95
118
  const singleSelection = selection => (selectedTypes.length === 1
@@ -99,7 +122,7 @@ const CVErrataDateFilterContent = ({
99
122
  (
100
123
  isEqual(convertAPIDateToUIFormat(ruleStartDate), startDate) &&
101
124
  isEqual(convertAPIDateToUIFormat(ruleEndDate), endDate) &&
102
- isEqual(sortBy(types), sortBy(selectedTypes)) &&
125
+ isEqual(sortBy(getInitialSelectedTypes()), sortBy(selectedTypes)) &&
103
126
  isEqual(ruleDateType, dateType)
104
127
  );
105
128
 
@@ -171,6 +194,15 @@ const CVErrataDateFilterContent = ({
171
194
  {__('Bugfix')}
172
195
  </p>
173
196
  </SelectOption>
197
+ <SelectOption
198
+ isDisabled={singleSelection('other') || !hasPermission(permissions, 'edit_content_views')}
199
+ key="other"
200
+ value="other"
201
+ >
202
+ <p style={{ marginTop: '4px' }}>
203
+ {__('Other')}
204
+ </p>
205
+ </SelectOption>
174
206
  </Select>
175
207
  </FlexItem>
176
208
  <FlexItem span={1} spacer={{ default: 'spacerNone' }}>
@@ -178,7 +210,7 @@ const CVErrataDateFilterContent = ({
178
210
  <Tooltip
179
211
  position="top"
180
212
  content={
181
- __('Atleast one errata type needs to be selected.')
213
+ __('At least one errata type option needs to be selected.')
182
214
  }
183
215
  >
184
216
  <OutlinedQuestionCircleIcon />
@@ -60,7 +60,7 @@ const CVErrataIDFilterContent = ({
60
60
  const hasNotAddedSelected = rows.some(({ selected, added }) => selected && !added);
61
61
  const [statusSelected, setStatusSelected] = useState(ALL_STATUSES);
62
62
  const [typeSelectOpen, setTypeSelectOpen] = useState(false);
63
- const [selectedTypes, setSelectedTypes] = useState(ERRATA_TYPES);
63
+ const [selectedTypes, setSelectedTypes] = useState([...ERRATA_TYPES, 'other']);
64
64
  const [startDate, setStartDate] = useState('');
65
65
  const [endDate, setEndDate] = useState('');
66
66
  const activeFilters = [statusSelected, selectedTypes, startDate, endDate];
@@ -198,9 +198,15 @@ const CVErrataIDFilterContent = ({
198
198
 
199
199
  const onTypeSelect = (selection) => {
200
200
  if (selectedTypes.includes(selection)) {
201
+ // If the selection is the only selection remaining, do not allow it to be removed
201
202
  if (selectedTypes.length === 1) return;
203
+
204
+ // Filter out the current selection to deselect it
202
205
  setSelectedTypes(selectedTypes.filter(e => e !== selection));
203
- } else setSelectedTypes([...selectedTypes, selection]);
206
+ } else {
207
+ // Add the current selection to the selected types
208
+ setSelectedTypes([...selectedTypes, selection]);
209
+ }
204
210
  setTypeSelectOpen(false);
205
211
  };
206
212
 
@@ -326,6 +332,11 @@ const CVErrataIDFilterContent = ({
326
332
  {__('Bugfix')}
327
333
  </p>
328
334
  </SelectOption>
335
+ <SelectOption isDisabled={singleSelection('bugfix')} key="other" value="other">
336
+ <p style={{ marginTop: '4px' }}>
337
+ {__('Other')}
338
+ </p>
339
+ </SelectOption>
329
340
  </Select>
330
341
  </SplitItem>
331
342
  {hasPermission(permissions, 'edit_content_views') &&
@@ -47,7 +47,7 @@ const CVFilterDetailType = ({
47
47
  details={details}
48
48
  />);
49
49
  case 'erratum':
50
- if (head(rules)?.types) {
50
+ if (head(rules)?.types || head(rules)?.allow_other_types) {
51
51
  return (<CVErrataDateFilterContent
52
52
  cvId={cvId}
53
53
  filterId={filterId}
@@ -23,6 +23,7 @@ test('Can display errata-date filter rule and edit', async (done) => {
23
23
  end_date: '2020-08-15T12:00:00.000Z',
24
24
  types: ['enhancement', 'security'],
25
25
  date_type: 'issued',
26
+ allow_other_types: false,
26
27
  };
27
28
 
28
29
  const ruleEditScope = nockInstance
@@ -3,7 +3,7 @@ import { Tooltip } from '@patternfly/react-core';
3
3
  import { BundleIcon, MiddlewareIcon, BoxIcon, CodeBranchIcon, FanIcon, TenantIcon, AnsibleTowerIcon } from '@patternfly/react-icons';
4
4
  import PropTypes from 'prop-types';
5
5
 
6
- const RepoIcon = ({ type }) => {
6
+ const RepoIcon = ({ type, customTooltip }) => {
7
7
  const iconMap = {
8
8
  yum: BundleIcon,
9
9
  docker: MiddlewareIcon,
@@ -14,15 +14,17 @@ const RepoIcon = ({ type }) => {
14
14
  };
15
15
  const Icon = iconMap[type] || BoxIcon;
16
16
 
17
- return <Tooltip content={<div>{type}</div>}><Icon aria-label={`${type}_type_icon`} /></Tooltip>;
17
+ return <Tooltip content={<div>{customTooltip ?? type}</div>}><Icon aria-label={`${type}_type_icon`} /></Tooltip>;
18
18
  };
19
19
 
20
20
  RepoIcon.propTypes = {
21
21
  type: PropTypes.string,
22
+ customTooltip: PropTypes.string,
22
23
  };
23
24
 
24
25
  RepoIcon.defaultProps = {
25
26
  type: '', // prevent errors if data isn't loaded yet
27
+ customTooltip: null,
26
28
  };
27
29
 
28
30
  export default RepoIcon;
@@ -138,8 +138,7 @@
138
138
  "purpose_role": "",
139
139
  "purpose_usage": "",
140
140
  "hypervisor": false,
141
- "user": null,
142
- "purpose_addons": []
141
+ "user": null
143
142
  },
144
143
  "infrastructure_facet": {
145
144
  "foreman_instance": false,
@@ -1,12 +1,13 @@
1
1
  import React, { useState, useContext } from 'react';
2
2
  import { useDispatch, useSelector } from 'react-redux';
3
3
  import useDeepCompareEffect from 'use-deep-compare-effect';
4
- import { ExpandableSection, SelectOption } from '@patternfly/react-core';
4
+ import { ExpandableSection, SelectOption, Alert, AlertActionCloseButton } from '@patternfly/react-core';
5
5
  import { STATUS } from 'foremanReact/constants';
6
6
  import { translate as __ } from 'foremanReact/common/I18n';
7
7
  import EnvironmentPaths from '../../../../components/EnvironmentPaths/EnvironmentPaths';
8
8
  import getContentViews from '../../../../ContentViewsActions';
9
9
  import { selectContentViewError, selectContentViews, selectContentViewStatus } from '../../../../ContentViewSelectors';
10
+ import { selectCVActivationKeys } from '../../../ContentViewDetailSelectors';
10
11
  import AffectedActivationKeys from '../affectedActivationKeys';
11
12
  import DeleteContext from '../DeleteContext';
12
13
  import ContentViewSelect from '../../../../components/ContentViewSelect/ContentViewSelect';
@@ -17,7 +18,9 @@ const CVReassignActivationKeysForm = () => {
17
18
  const contentViewsInEnvResponse = useSelector(selectContentViews);
18
19
  const contentViewsInEnvStatus = useSelector(selectContentViewStatus);
19
20
  const contentViewsInEnvError = useSelector(selectContentViewError);
21
+ const activationKeysResponse = useSelector(selectCVActivationKeys);
20
22
  const cvInEnvLoading = contentViewsInEnvStatus === STATUS.PENDING;
23
+ const [alertDismissed, setAlertDismissed] = useState(false);
21
24
  const [cvSelectOpen, setCVSelectOpen] = useState(false);
22
25
  const [cvSelectOptions, setCvSelectionOptions] = useState([]);
23
26
  const [showActivationKeys, setShowActivationKeys] = useState(false);
@@ -72,6 +75,9 @@ const CVReassignActivationKeysForm = () => {
72
75
  contentViewsInEnvError, selectedEnvForAK, setSelectedCVForAK, setSelectedCVNameForAK,
73
76
  cvInEnvLoading, selectedCVForAK, cvId, versionEnvironments, selectedEnvSet]);
74
77
 
78
+ const multiCVWarning = activationKeysResponse?.results?.some?.(key =>
79
+ key.multi_content_view_environment);
80
+
75
81
  const fetchSelectedCVName = (id) => {
76
82
  const { results } = contentViewsInEnvResponse ?? { };
77
83
  return results.filter(cv => cv.id === id)[0]?.name;
@@ -102,8 +108,21 @@ const CVReassignActivationKeysForm = () => {
102
108
  cvSelectOptions,
103
109
  });
104
110
 
111
+ const multiCVRemovalInfo = __('This environment is used in one or more multi-environment activation keys. The environment will simply be removed from the multi-environment keys. The content view and lifecycle environment you select here will only apply to single-environment activation keys. See hammer activation-key --help for more details.');
112
+
105
113
  return (
106
114
  <>
115
+ {!alertDismissed && multiCVWarning && (
116
+ <Alert
117
+ ouiaId="multi-cv-warning-alert"
118
+ variant="warning"
119
+ isInline
120
+ title={__('Warning')}
121
+ actionClose={<AlertActionCloseButton onClose={() => setAlertDismissed(true)} />}
122
+ >
123
+ <p>{multiCVRemovalInfo}</p>
124
+ </Alert>
125
+ )}
107
126
  <EnvironmentPaths
108
127
  userCheckedItems={selectedEnvForAK}
109
128
  setUserCheckedItems={setSelectedEnvForAK}
@@ -1,12 +1,13 @@
1
1
  import React, { useState, useContext } from 'react';
2
2
  import { useDispatch, useSelector } from 'react-redux';
3
3
  import useDeepCompareEffect from 'use-deep-compare-effect';
4
- import { ExpandableSection, SelectOption } from '@patternfly/react-core';
4
+ import { ExpandableSection, SelectOption, Alert, AlertActionCloseButton } from '@patternfly/react-core';
5
5
  import { STATUS } from 'foremanReact/constants';
6
6
  import { translate as __ } from 'foremanReact/common/I18n';
7
7
  import EnvironmentPaths from '../../../../components/EnvironmentPaths/EnvironmentPaths';
8
8
  import getContentViews from '../../../../ContentViewsActions';
9
9
  import { selectContentViewError, selectContentViews, selectContentViewStatus } from '../../../../ContentViewSelectors';
10
+ import { selectCVHosts } from '../../../ContentViewDetailSelectors';
10
11
  import AffectedHosts from '../affectedHosts';
11
12
  import DeleteContext from '../DeleteContext';
12
13
  import ContentViewSelect from '../../../../components/ContentViewSelect/ContentViewSelect';
@@ -25,6 +26,13 @@ const CVReassignHostsForm = () => {
25
26
  cvId, versionEnvironments, selectedEnvSet, selectedEnvForHost, setSelectedEnvForHost,
26
27
  currentStep, selectedCVForHosts, setSelectedCVNameForHosts, setSelectedCVForHosts,
27
28
  } = useContext(DeleteContext);
29
+ const [alertDismissed, setAlertDismissed] = useState(false);
30
+ const hostResponse = useSelector(selectCVHosts);
31
+
32
+ const multiCVWarning = hostResponse?.results?.some?.(host =>
33
+ host.content_facet_attributes?.multi_content_view_environment);
34
+
35
+ const multiCVRemovalInfo = __('This content view version is used in one or more multi-environment hosts. The version will simply be removed from the multi-environment hosts. The content view and lifecycle environment you select here will only apply to single-environment hosts. See hammer activation-key --help for more details.');
28
36
 
29
37
  // Fetch content views for selected environment to reassign hosts to.
30
38
  useDeepCompareEffect(
@@ -103,6 +111,17 @@ const CVReassignHostsForm = () => {
103
111
 
104
112
  return (
105
113
  <>
114
+ {!alertDismissed && multiCVWarning && (
115
+ <Alert
116
+ ouiaId="multi-cv-warning-alert"
117
+ variant="warning"
118
+ isInline
119
+ title={__('Warning')}
120
+ actionClose={<AlertActionCloseButton onClose={() => setAlertDismissed(true)} />}
121
+ >
122
+ <p>{multiCVRemovalInfo}</p>
123
+ </Alert>
124
+ )}
106
125
  <EnvironmentPaths
107
126
  userCheckedItems={selectedEnvForHost}
108
127
  setUserCheckedItems={setSelectedEnvForHost}
@@ -2,28 +2,44 @@ import React, { useContext, useState } from 'react';
2
2
  import { useSelector } from 'react-redux';
3
3
  import { Alert, Flex, FlexItem, Label, AlertActionCloseButton } from '@patternfly/react-core';
4
4
  import { ExclamationTriangleIcon } from '@patternfly/react-icons';
5
+ import { FormattedMessage } from 'react-intl';
5
6
  import { translate as __ } from 'foremanReact/common/I18n';
6
- import { selectCVActivationKeys, selectCVHosts } from '../../../ContentViewDetailSelectors';
7
+ import { selectCVActivationKeys, selectCVHosts, selectCVVersions } from '../../../ContentViewDetailSelectors';
7
8
  import DeleteContext from '../DeleteContext';
8
- import { pluralize } from '../../../../../../utils/helpers';
9
9
  import WizardHeader from '../../../../components/WizardHeader';
10
10
 
11
11
  const CVVersionRemoveReview = () => {
12
12
  const [alertDismissed, setAlertDismissed] = useState(false);
13
13
  const {
14
- cvId, versionNameToRemove, versionEnvironments, selectedEnvSet,
14
+ cvId, versionIdToRemove, versionNameToRemove, selectedEnvSet,
15
15
  selectedEnvForAK, selectedCVNameForAK, selectedCVNameForHosts,
16
16
  selectedEnvForHost, affectedActivationKeys, affectedHosts, deleteFlow, removeDeletionFlow,
17
17
  } = useContext(DeleteContext);
18
18
  const activationKeysResponse = useSelector(state => selectCVActivationKeys(state, cvId));
19
19
  const hostsResponse = useSelector(state => selectCVHosts(state, cvId));
20
- const { results: hostResponse } = hostsResponse;
21
- const { results: akResponse } = activationKeysResponse;
22
- const selectedEnv = versionEnvironments.filter(env => selectedEnvSet.has(env.id));
20
+ const { results: hostResponse = [] } = hostsResponse || {};
21
+ const { results: akResponse = [] } = activationKeysResponse || {};
22
+ const cvVersions = useSelector(state => selectCVVersions(state, cvId));
23
23
  const versionDeleteInfo = __(`Version ${versionNameToRemove} will be deleted from all environments. It will no longer be available for promotion.`);
24
24
  const removalNotice = __(`Version ${versionNameToRemove} will be removed from the environments listed below, and will remain available for later promotion. ` +
25
25
  'Changes listed below will be effective after clicking Remove.');
26
26
 
27
+ const matchedCVResults = cvVersions?.results?.filter(cv => cv.id === versionIdToRemove) || [];
28
+ const selectedCVE = matchedCVResults
29
+ .flatMap(cv => cv.content_view_environments || [])
30
+ .filter(env => selectedEnvSet.has(env.environment_id));
31
+
32
+ const multiCVHosts = hostResponse?.filter(host =>
33
+ host.content_facet_attributes?.multi_content_view_environment) || [];
34
+ const multiCVHostsCount = multiCVHosts.length;
35
+
36
+ const singleCVHostsCount = (hostResponse?.length || 0) - multiCVHostsCount;
37
+
38
+ const multiCVActivationKeys = akResponse.filter(key => key.multi_content_view_environment);
39
+ const multiCVActivationKeysCount = multiCVActivationKeys.length;
40
+
41
+ const singleCVActivationKeysCount = akResponse.length - multiCVActivationKeysCount;
42
+
27
43
  return (
28
44
  <>
29
45
  <WizardHeader title={__('Review details')} />
@@ -38,7 +54,7 @@ const CVVersionRemoveReview = () => {
38
54
  <p style={{ marginBottom: '0.5em' }}>{versionDeleteInfo}</p>
39
55
  </Alert>}
40
56
  {!(deleteFlow || removeDeletionFlow) && <WizardHeader description={removalNotice} />}
41
- {(selectedEnv.length !== 0) &&
57
+ {(selectedCVE?.length !== 0) &&
42
58
  <>
43
59
  <h3>{__('Environments')}</h3>
44
60
  <Flex>
@@ -46,27 +62,121 @@ const CVVersionRemoveReview = () => {
46
62
  <FlexItem style={{ marginBottom: '0.5em' }}>{__('This version will be removed from:')}</FlexItem>
47
63
  </Flex>
48
64
  <Flex>
49
- {selectedEnv?.map(({ name, id }) =>
65
+ {selectedCVE?.map(({ environment_name: name, environment_id: id }) =>
50
66
  <FlexItem key={name}><Label isTruncated color="purple" href={`/lifecycle_environments/${id}`}>{name}</Label></FlexItem>)}
51
67
  </Flex>
52
68
  </>}
53
69
  {affectedHosts &&
54
70
  <>
55
71
  <h3>{__('Content hosts')}</h3>
56
- <Flex>
57
- <FlexItem><ExclamationTriangleIcon /></FlexItem>
58
- <FlexItem><p>{__(`${pluralize(hostResponse.length, 'host')} will be moved to content view ${selectedCVNameForHosts} in `)}</p></FlexItem>
59
- <FlexItem><Label isTruncated color="purple" href={`/lifecycle_environments/${selectedEnvForHost[0].id}`}>{selectedEnvForHost[0].name}</Label></FlexItem>
60
- </Flex>
72
+ {singleCVHostsCount > 0 && (
73
+ <Flex>
74
+ <FlexItem><ExclamationTriangleIcon /></FlexItem>
75
+ <FlexItem data-testid="single-cv-hosts-remove">
76
+ <FormattedMessage
77
+ id="single-cv-hosts-remove"
78
+ defaultMessage="{count, plural, one {# {singular}} other {# {plural}}} will be moved to content view {cvName} in {envName}."
79
+ values={{
80
+ count: singleCVHostsCount,
81
+ singular: __('host'),
82
+ plural: __('hosts'),
83
+ cvName: selectedCVNameForHosts,
84
+ envName: selectedEnvForHost[0] && (
85
+ <Label isTruncated color="purple" href={`/lifecycle_environments/${selectedEnvForHost[0].id}`}>
86
+ {selectedEnvForHost[0].name}
87
+ </Label>
88
+ ),
89
+ }}
90
+ />
91
+ </FlexItem>
92
+ </Flex>
93
+ )}
94
+ {multiCVHostsCount > 0 && (
95
+ <Flex>
96
+ <FlexItem><ExclamationTriangleIcon /></FlexItem>
97
+ <FlexItem>
98
+ <FormattedMessage
99
+ id="multi-cv-hosts-remove"
100
+ defaultMessage="{envSingularOrPlural} {envCV} will be removed from {hostCount, plural, one {# {hostSingular}} other {# {hostPlural}}}."
101
+ values={{
102
+ envSingularOrPlural: (
103
+ <FormattedMessage
104
+ id="environment.plural"
105
+ defaultMessage="{count, plural, one {{envSingular}} other {{envPlural}}}"
106
+ values={{
107
+ count: selectedCVE?.length,
108
+ envSingular: __('Content view environment'),
109
+ envPlural: __('Content view environments'),
110
+ }}
111
+ />
112
+ ),
113
+ envCV: selectedCVE
114
+ ?.map(cve => cve.label)
115
+ .join(', '),
116
+ hostCount: multiCVHostsCount,
117
+ hostSingular: __('multi-environment host'),
118
+ hostPlural: __('multi-environment hosts'),
119
+ }}
120
+ />
121
+ </FlexItem>
122
+ </Flex>
123
+ )}
61
124
  </>}
62
125
  {affectedActivationKeys &&
63
126
  <>
64
127
  <h3>{__('Activation keys')}</h3>
65
- <Flex>
66
- <FlexItem><ExclamationTriangleIcon /></FlexItem>
67
- <FlexItem><p>{__(`${pluralize(akResponse.length, 'activation key')} will be moved to content view ${selectedCVNameForAK} in `)}</p></FlexItem>
68
- <FlexItem><Label isTruncated color="purple" href={`/lifecycle_environments/${selectedEnvForAK[0].id}`}>{selectedEnvForAK[0].name}</Label></FlexItem>
69
- </Flex>
128
+ {singleCVActivationKeysCount > 0 && (
129
+ <Flex>
130
+ <FlexItem><ExclamationTriangleIcon /></FlexItem>
131
+ <FlexItem data-testid="single-cv-activation-keys-remove">
132
+ <FormattedMessage
133
+ id="single-cv-activation-keys-remove"
134
+ defaultMessage="{count, plural, one {# {singular}} other {# {plural}}} will be moved to content view {cvName} in {envName}."
135
+ values={{
136
+ count: singleCVActivationKeysCount,
137
+ singular: __('activation key'),
138
+ plural: __('activation keys'),
139
+ cvName: selectedCVNameForAK,
140
+ envName: selectedEnvForAK[0] && (
141
+ <Label isTruncated color="purple" href={`/lifecycle_environments/${selectedEnvForAK[0].id}`}>
142
+ {selectedEnvForAK[0].name}
143
+ </Label>
144
+ ),
145
+ }}
146
+ />
147
+ </FlexItem>
148
+ </Flex>
149
+ )}
150
+ {multiCVActivationKeysCount > 0 && (
151
+ <Flex>
152
+ <FlexItem><ExclamationTriangleIcon /></FlexItem>
153
+ <FlexItem>
154
+ <FormattedMessage
155
+ id="multi-cv-activation-keys-remove"
156
+ defaultMessage="{envSingularOrPlural} {envCV} will be removed from {akCount, plural, one {# {keySingular}} other {# {keyPlural}}}."
157
+ values={{
158
+ envSingularOrPlural: (
159
+ <FormattedMessage
160
+ id="environment.plural"
161
+ defaultMessage="{count, plural, one {{envSingular}} other {{envPlural}}}"
162
+ values={{
163
+ count: selectedCVE?.length,
164
+ envSingular: __('Content view environment'),
165
+ envPlural: __('Content view environments'),
166
+ }}
167
+ />
168
+ ),
169
+ envCV: selectedCVE
170
+ ?.map(cve => cve.label)
171
+ .join(', '),
172
+ akCount: multiCVActivationKeysCount,
173
+ keySingular: __('multi-environment activation key'),
174
+ keyPlural: __('multi-environment activation keys'),
175
+ }}
176
+ />
177
+ </FlexItem>
178
+ </Flex>
179
+ )}
70
180
  </>}
71
181
  </>
72
182
  );
@@ -42,7 +42,6 @@
42
42
  },
43
43
  "products": [],
44
44
  "host_collections": [],
45
- "purpose_addons": [],
46
45
  "permissions": {
47
46
  "view_activation_keys": true,
48
47
  "edit_activation_keys": true,
@@ -132,8 +132,7 @@
132
132
  "purpose_role": "",
133
133
  "purpose_usage": "",
134
134
  "hypervisor": false,
135
- "user": null,
136
- "purpose_addons": []
135
+ "user": null
137
136
  }
138
137
  }
139
138
  ]
@@ -152,7 +152,8 @@ test('Can open Remove wizard and remove version from environment with hosts', as
152
152
 
153
153
 
154
154
  const {
155
- getByText, getAllByText, getByLabelText, getAllByLabelText, queryByText, getByPlaceholderText,
155
+ getByText, getAllByText, getByLabelText, getAllByLabelText, queryByText,
156
+ getByPlaceholderText, getByTestId,
156
157
  } = renderWithRedux(
157
158
  <ContentViewVersions cvId={2} details={cvDetailData} />,
158
159
  renderOptions,
@@ -192,7 +193,7 @@ test('Can open Remove wizard and remove version from environment with hosts', as
192
193
  fireEvent.click(getByText('Next'));
193
194
  await patientlyWaitFor(() => {
194
195
  expect(getByText('Review details')).toBeInTheDocument();
195
- expect(getByText('1 host will be moved to content view cv2 in')).toBeInTheDocument();
196
+ expect(getByTestId('single-cv-hosts-remove')).toBeInTheDocument();
196
197
  });
197
198
  fireEvent.click(getAllByText('Remove')[0]);
198
199
  assertNockRequest(scope);
@@ -238,7 +239,8 @@ test('Can open Remove wizard and remove version from environment with activation
238
239
 
239
240
 
240
241
  const {
241
- getByText, getAllByText, getByLabelText, getAllByLabelText, queryByText, getByPlaceholderText,
242
+ getByText, getAllByText, getByLabelText, getAllByLabelText, queryByText,
243
+ getByPlaceholderText, getByTestId,
242
244
  } = renderWithRedux(
243
245
  <ContentViewVersions cvId={2} details={cvDetailData} />,
244
246
  renderOptions,
@@ -278,7 +280,7 @@ test('Can open Remove wizard and remove version from environment with activation
278
280
  fireEvent.click(getByText('Next'));
279
281
  await patientlyWaitFor(() => {
280
282
  expect(getByText('Review details')).toBeInTheDocument();
281
- expect(getByText('1 activation key will be moved to content view cv2 in')).toBeInTheDocument();
283
+ expect(getByTestId('single-cv-activation-keys-remove')).toBeInTheDocument();
282
284
  });
283
285
  fireEvent.click(getAllByText('Remove')[0]);
284
286
 
@@ -35,6 +35,7 @@ const AffectedActivationKeys = ({
35
35
  const columnHeaders = [
36
36
  __('Name'),
37
37
  __('Environment'),
38
+ __('Multi Content View Environment'),
38
39
  ];
39
40
  const emptyContentTitle = __('No matching activation keys found.');
40
41
  const emptyContentBody = __("Given criteria doesn't match any activation keys. Try changing your rule.");
@@ -65,12 +66,15 @@ const AffectedActivationKeys = ({
65
66
  </Tr>
66
67
  </Thead>
67
68
  <Tbody>
68
- {results?.map(({ name, id, environment }) => (
69
+ {results?.map(({
70
+ name, id, environment, multi_content_view_environment: multiContentViewEnvironment,
71
+ }) => (
69
72
  <Tr ouiaId={id} key={id}>
70
73
  <Td>
71
74
  <a rel="noreferrer" target="_blank" href={urlBuilder(`activation_keys/${id}`, '')}>{name}</a>
72
75
  </Td>
73
76
  <Td><EnvironmentLabels environments={environment} /></Td>
77
+ <Td>{ multiContentViewEnvironment ? 'Yes' : 'No' }</Td>
74
78
  </Tr>
75
79
  ))
76
80
  }
@@ -30,6 +30,7 @@ const AffectedHosts = ({
30
30
  const columnHeaders = [
31
31
  __('Name'),
32
32
  __('Environment'),
33
+ __('Multi Content View Environment'),
33
34
  ];
34
35
  const emptyContentTitle = __('No matching hosts found.');
35
36
  const emptyContentBody = __("Given criteria doesn't match any hosts. Try changing your rule.");
@@ -63,13 +64,17 @@ const AffectedHosts = ({
63
64
  {results?.map(({
64
65
  name,
65
66
  id,
66
- content_facet_attributes: { lifecycle_environment: environment },
67
+ content_facet_attributes: {
68
+ lifecycle_environment: environment,
69
+ multi_content_view_environment: multiContentViewEnvironment,
70
+ },
67
71
  }) => (
68
72
  <Tr ouiaId={id} key={id}>
69
73
  <Td>
70
74
  <a rel="noreferrer" target="_blank" href={urlBuilder(`new/hosts/${id}`, '')}>{name}</a>
71
75
  </Td>
72
76
  <Td><EnvironmentLabels environments={environment} /></Td>
77
+ <Td>{ multiContentViewEnvironment ? __('Yes') : __('No') }</Td>
73
78
  </Tr>
74
79
  ))
75
80
  }
@@ -132,7 +132,7 @@ test('Can expand cv and show activation keys and hosts', async (done) => {
132
132
  expect(queryByLabelText('activation_keys_link_2').textContent).toEqual('1');
133
133
 
134
134
  // Displays hosts link with count
135
- expect(queryByLabelText('host_link_2')).toHaveAttribute('href', '/hosts?search=content_view_id+%3D+2');
135
+ expect(queryByLabelText('host_link_2')).toHaveAttribute('href', '/new/hosts?search=content_view_id%3D2');
136
136
  expect(queryByLabelText('host_link_2').textContent).toEqual('1');
137
137
  });
138
138
 
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { translate as __ } from 'foremanReact/common/I18n';
4
+ import { useForemanHostsPageUrl } from 'foremanReact/Root/Context/ForemanContext';
4
5
  import RelatedCompositeContentViewsModal from './RelatedCompositeContentViewsModal';
5
6
  import RelatedContentViewComponentsModal from './RelatedContentViewComponentsModal';
6
7
 
@@ -10,6 +11,9 @@ const DetailsExpansion = ({
10
11
  const activationKeyCount = activationKeys.length;
11
12
  const hostCount = hosts.length;
12
13
 
14
+ const baseHostsPageUrl = useForemanHostsPageUrl();
15
+ const hostsPageUrl = `${baseHostsPageUrl}?search=${encodeURIComponent(`content_view_id=${cvId}`)}`;
16
+
13
17
  const relatedContentViewModal = () => {
14
18
  if (cvComposite) {
15
19
  return (
@@ -36,7 +40,7 @@ const DetailsExpansion = ({
36
40
  <div id={`cv-details-expansion-${cvId}`}>
37
41
  {__('Activation keys: ')}<a aria-label={`activation_keys_link_${cvId}`} href={`/activation_keys?search=content_view_id+%3D+${cvId}`}>{activationKeyCount}</a>
38
42
  <br />
39
- {__('Hosts: ')}<a aria-label={`host_link_${cvId}`} href={`/hosts?search=content_view_id+%3D+${cvId}`}>{hostCount}</a>
43
+ {__('Hosts: ')}<a aria-label={`host_link_${cvId}`} href={hostsPageUrl}>{hostCount}</a>
40
44
  <br />
41
45
  {relatedContentViewModal()}
42
46
  </div>