katello 4.4.2.2 → 4.5.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 (444) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/katello/api/rhsm/candlepin_proxies_controller.rb +5 -1
  3. data/app/controllers/katello/api/v2/alternate_content_sources_controller.rb +98 -0
  4. data/app/controllers/katello/api/v2/content_export_incrementals_controller.rb +39 -3
  5. data/app/controllers/katello/api/v2/content_exports_controller.rb +19 -0
  6. data/app/controllers/katello/api/v2/content_imports_controller.rb +13 -16
  7. data/app/controllers/katello/api/v2/content_view_versions_controller.rb +0 -12
  8. data/app/controllers/katello/api/v2/content_views_controller.rb +13 -0
  9. data/app/controllers/katello/api/v2/environments_controller.rb +1 -1
  10. data/app/controllers/katello/api/v2/host_module_streams_controller.rb +8 -2
  11. data/app/controllers/katello/api/v2/host_subscriptions_controller.rb +25 -3
  12. data/app/controllers/katello/api/v2/organizations_controller.rb +4 -2
  13. data/app/controllers/katello/api/v2/repositories_bulk_actions_controller.rb +1 -1
  14. data/app/controllers/katello/api/v2/repositories_controller.rb +9 -3
  15. data/app/controllers/katello/api/v2/repository_sets_controller.rb +40 -7
  16. data/app/controllers/katello/api/v2/subscriptions_controller.rb +2 -2
  17. data/app/controllers/katello/concerns/api/v2/hosts_controller_extensions.rb +1 -1
  18. data/app/helpers/katello/concerns/smart_proxy_helper_extensions.rb +4 -0
  19. data/app/helpers/katello/hosts_and_hostgroups_helper.rb +21 -3
  20. data/app/lib/actions/katello/alternate_content_source/create.rb +24 -0
  21. data/app/lib/actions/katello/alternate_content_source/destroy.rb +27 -0
  22. data/app/lib/actions/katello/alternate_content_source/update.rb +41 -0
  23. data/app/lib/actions/katello/cdn_configuration/update.rb +2 -2
  24. data/app/lib/actions/katello/content_view/destroy.rb +2 -1
  25. data/app/lib/actions/katello/content_view/incremental_updates.rb +7 -3
  26. data/app/lib/actions/katello/content_view/publish.rb +7 -9
  27. data/app/lib/actions/katello/content_view_version/auto_create_products.rb +4 -4
  28. data/app/lib/actions/katello/content_view_version/auto_create_redhat_repositories.rb +6 -4
  29. data/app/lib/actions/katello/content_view_version/auto_create_repositories.rb +6 -4
  30. data/app/lib/actions/katello/content_view_version/import.rb +25 -22
  31. data/app/lib/actions/katello/content_view_version/import_library.rb +0 -1
  32. data/app/lib/actions/katello/content_view_version/import_repository.rb +21 -0
  33. data/app/lib/actions/katello/content_view_version/incremental_update.rb +85 -93
  34. data/app/lib/actions/katello/content_view_version/reset_content_view_repositories_from_metadata.rb +2 -2
  35. data/app/lib/actions/katello/product/content_create.rb +10 -8
  36. data/app/lib/actions/katello/repository/destroy.rb +36 -12
  37. data/app/lib/actions/katello/repository_set/disable_repository.rb +8 -3
  38. data/app/lib/actions/pulp3/alternate_content_source/create.rb +20 -0
  39. data/app/lib/actions/pulp3/alternate_content_source/create_remote.rb +20 -0
  40. data/app/lib/actions/pulp3/alternate_content_source/delete.rb +16 -0
  41. data/app/lib/actions/pulp3/alternate_content_source/delete_remote.rb +16 -0
  42. data/app/lib/actions/pulp3/alternate_content_source/update.rb +16 -0
  43. data/app/lib/actions/pulp3/alternate_content_source/update_remote.rb +17 -0
  44. data/app/lib/actions/pulp3/content_view_version/{import.rb → create_import.rb} +5 -5
  45. data/app/lib/actions/pulp3/content_view_version/create_importer.rb +4 -3
  46. data/app/lib/actions/pulp3/content_view_version/destroy_importer.rb +12 -1
  47. data/app/lib/actions/pulp3/orchestration/alternate_content_source/create.rb +18 -0
  48. data/app/lib/actions/pulp3/orchestration/alternate_content_source/delete.rb +23 -0
  49. data/app/lib/actions/pulp3/orchestration/alternate_content_source/update.rb +18 -0
  50. data/app/lib/actions/pulp3/orchestration/content_view_version/copy_version_units_to_library.rb +1 -1
  51. data/app/lib/actions/pulp3/orchestration/content_view_version/export_repository.rb +51 -0
  52. data/app/lib/actions/pulp3/orchestration/content_view_version/import.rb +5 -2
  53. data/app/lib/actions/pulp3/repository/refresh_distribution.rb +4 -1
  54. data/app/lib/actions/pulp3/repository/save_distribution_references.rb +2 -0
  55. data/app/lib/katello/api/v2/error_handling.rb +1 -0
  56. data/app/lib/katello/resources/cdn/katello_cdn.rb +3 -1
  57. data/app/lib/katello/util/errata.rb +2 -3
  58. data/app/lib/katello/validators/alternate_content_source_path_validator.rb +29 -0
  59. data/app/lib/katello/validators/content_default_http_proxy_setting_validator.rb +12 -0
  60. data/app/lib/katello/validators/content_view_environment_validator.rb +10 -5
  61. data/app/models/katello/alternate_content_source.rb +66 -0
  62. data/app/models/katello/authorization/alternate_content_source.rb +33 -0
  63. data/app/models/katello/authorization/repository.rb +3 -3
  64. data/app/models/katello/candlepin/repository_mapper.rb +13 -6
  65. data/app/models/katello/cdn_configuration.rb +15 -15
  66. data/app/models/katello/concerns/content_facet_host_extensions.rb +25 -0
  67. data/app/models/katello/concerns/host_managed_extensions.rb +7 -5
  68. data/app/models/katello/concerns/http_proxy_extensions.rb +14 -0
  69. data/app/models/katello/concerns/organization_extensions.rb +4 -2
  70. data/app/models/katello/concerns/setting_extensions.rb +14 -0
  71. data/app/models/katello/concerns/smart_proxy_extensions.rb +2 -1
  72. data/app/models/katello/content.rb +1 -0
  73. data/app/models/katello/content_credential.rb +6 -0
  74. data/app/models/katello/content_override.rb +7 -3
  75. data/app/models/katello/content_view.rb +33 -2
  76. data/app/models/katello/content_view_erratum_filter.rb +26 -12
  77. data/app/models/katello/content_view_filter.rb +4 -0
  78. data/app/models/katello/content_view_version.rb +12 -0
  79. data/app/models/katello/content_view_version_export_history.rb +3 -1
  80. data/app/models/katello/erratum.rb +9 -5
  81. data/app/models/katello/events/delete_latest_content_view_version.rb +40 -0
  82. data/app/models/katello/host/content_facet.rb +14 -0
  83. data/app/models/katello/host_available_module_stream.rb +12 -0
  84. data/app/models/katello/product_content.rb +1 -0
  85. data/app/models/katello/purpose_sla_status.rb +1 -1
  86. data/app/models/katello/purpose_status.rb +2 -2
  87. data/app/models/katello/repository.rb +7 -4
  88. data/app/models/katello/root_repository.rb +1 -1
  89. data/app/models/katello/smart_proxy_alternate_content_source.rb +8 -0
  90. data/app/models/katello/sync_plan.rb +1 -1
  91. data/app/presenters/katello/product_content_presenter.rb +15 -0
  92. data/app/services/katello/applicable_host_queue.rb +1 -1
  93. data/app/services/katello/content_unit_indexer.rb +2 -1
  94. data/app/services/katello/product_content_finder.rb +12 -2
  95. data/app/services/katello/pulp3/alternate_content_source.rb +117 -0
  96. data/app/services/katello/pulp3/api/file.rb +8 -0
  97. data/app/services/katello/pulp3/api/yum.rb +8 -0
  98. data/app/services/katello/pulp3/content_view_version/export.rb +27 -5
  99. data/app/services/katello/pulp3/content_view_version/import.rb +97 -71
  100. data/app/services/katello/pulp3/content_view_version/import_export_common.rb +4 -4
  101. data/app/services/katello/pulp3/content_view_version/import_gpg_keys.rb +13 -11
  102. data/app/services/katello/pulp3/content_view_version/import_validator.rb +67 -72
  103. data/app/services/katello/pulp3/content_view_version/importable_products.rb +40 -24
  104. data/app/services/katello/pulp3/content_view_version/importable_repositories.rb +88 -39
  105. data/app/services/katello/pulp3/content_view_version/metadata_generator.rb +2 -2
  106. data/app/services/katello/pulp3/content_view_version/metadata_map.rb +117 -0
  107. data/app/services/katello/pulp3/pulp_content_unit.rb +6 -1
  108. data/app/services/katello/pulp3/repository/yum.rb +70 -12
  109. data/app/services/katello/pulp3/repository.rb +7 -91
  110. data/app/services/katello/pulp3/service_common.rb +66 -0
  111. data/app/services/katello/pulp3/smart_proxy_mirror_repository.rb +4 -1
  112. data/app/services/katello/ui_notifications/content_view/delete_latest_version_failure.rb +22 -0
  113. data/app/views/foreman/job_templates/change_content_source.erb +1 -1
  114. data/app/views/foreman/job_templates/install_errata.erb +5 -5
  115. data/app/views/foreman/job_templates/install_errata_by_search_query.erb +7 -6
  116. data/app/views/foreman/job_templates/install_group.erb +4 -4
  117. data/app/views/foreman/job_templates/install_package.erb +4 -4
  118. data/app/views/foreman/job_templates/install_packages_by_search_query.erb +3 -3
  119. data/app/views/foreman/job_templates/remove_group.erb +4 -4
  120. data/app/views/foreman/job_templates/remove_package.erb +4 -4
  121. data/app/views/foreman/job_templates/remove_packages_by_search_query.erb +3 -3
  122. data/app/views/foreman/job_templates/resolve_traces.erb +2 -2
  123. data/app/views/foreman/job_templates/restart_services.erb +3 -3
  124. data/app/views/foreman/job_templates/update_group.erb +4 -4
  125. data/app/views/foreman/job_templates/update_package.erb +4 -4
  126. data/app/views/foreman/job_templates/update_packages_by_search_query.erb +3 -3
  127. data/app/views/katello/api/v2/alternate_content_sources/base.json.rabl +15 -0
  128. data/app/views/katello/api/v2/alternate_content_sources/index.json.rabl +7 -0
  129. data/app/views/katello/api/v2/alternate_content_sources/show.json.rabl +3 -0
  130. data/app/views/katello/api/v2/capsule_content/sync_status.json.rabl +1 -1
  131. data/app/views/katello/api/v2/content_view_version_export_histories/show.json.rabl +2 -3
  132. data/app/views/katello/api/v2/content_view_versions/base.json.rabl +1 -1
  133. data/app/views/katello/api/v2/content_views/base.json.rabl +1 -0
  134. data/app/views/katello/api/v2/environments/show.json.rabl +2 -0
  135. data/app/views/katello/api/v2/errata/_counts.json.rabl +2 -2
  136. data/app/views/katello/api/v2/host_module_streams/base.json.rabl +2 -2
  137. data/app/views/katello/api/v2/hosts/host_collections.json.rabl +1 -1
  138. data/app/views/katello/api/v2/organizations/show.json.rabl +7 -1
  139. data/app/views/katello/api/v2/repositories/show.json.rabl +2 -1
  140. data/app/views/katello/sync_management/_repo.html.erb +8 -29
  141. data/config/routes/api/v2.rb +7 -0
  142. data/db/migrate/20150930183738_migrate_content_hosts.rb +0 -399
  143. data/db/migrate/20171025163149_remove_use_pulp_oauth_setting.rb +1 -1
  144. data/db/migrate/20171114150937_cleanup_installed_packages.rb +1 -1
  145. data/db/migrate/20180402160223_clean_up_force_post_sync_action_setting.rb +1 -1
  146. data/db/migrate/20211129200124_remove_dependency_solving_algorithm_setting.rb +1 -1
  147. data/db/migrate/20211220185935_clean_duplicate_content_units.rb +10 -12
  148. data/db/migrate/20220110223754_update_disconnected_settings.rb +5 -5
  149. data/db/migrate/20220117151612_add_alternate_content_sources.rb +48 -0
  150. data/db/migrate/20220124191056_add_type_to_cdn_configuration.rb +9 -4
  151. data/db/migrate/20220209203251_add_generated_for_to_content_views.rb +13 -0
  152. data/db/migrate/20220209205137_expand_sync_timeout_settings.rb +7 -7
  153. data/db/migrate/20220228173251_remove_drpm_from_ignorable_content.rb +12 -0
  154. data/db/migrate/20220404190836_delete_old_setting_data.rb +9 -0
  155. data/db/migrate/20220405220616_update_cdn_configuration_type.rb +11 -0
  156. data/db/migrate/20220419193414_content_settings_to_dsl_category.rb +5 -0
  157. data/db/seeds.d/110-content-view-autopublish.rb +13 -0
  158. data/db/seeds.d/150-module_job_templates.rb +1 -1
  159. data/engines/bastion/README.md +1 -0
  160. data/engines/bastion/app/assets/javascripts/bastion/components/bst-modal.directive.js +1 -0
  161. data/engines/bastion/app/views/bastion/layouts/assets.html.erb +1 -0
  162. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/activation-keys/details/activation-key-details.controller.js +4 -2
  163. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/capsule-content/capsule-content.controller.js +10 -5
  164. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-module-streams.controller.js +1 -0
  165. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-packages-installed.controller.js +1 -0
  166. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/content-host-traces.controller.js +1 -0
  167. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-packages-actions.html +1 -1
  168. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content/views/content-host-packages-applicable.html +1 -1
  169. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/content-hosts.controller.js +8 -0
  170. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/details/content-host-details.controller.js +4 -2
  171. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/content-hosts.html +2 -2
  172. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/content.service.js +10 -0
  173. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-deb-repositories.html +26 -0
  174. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/details/views/environment-debs.html +27 -0
  175. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/environments.controller.js +1 -0
  176. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/environments.routes.js +22 -0
  177. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/environments/views/environments.html +13 -7
  178. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +63 -17
  179. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details-info.controller.js +7 -8
  180. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/repository-details.controller.js +10 -1
  181. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-details.html +21 -3
  182. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/details/views/repository-info.html +8 -9
  183. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/new-repository.controller.js +7 -5
  184. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/views/new-repository.html +2 -1
  185. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/discovery-create.controller.js +1 -1
  186. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery-create.html +1 -1
  187. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/products.routes.js +1 -0
  188. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/subscriptions/views/content-access-mode-banner.html +6 -1
  189. data/lib/katello/engine.rb +1 -5
  190. data/lib/katello/permission_creator.rb +32 -4
  191. data/lib/katello/plugin.rb +378 -4
  192. data/lib/katello/tasks/reset.rake.bak +67 -0
  193. data/lib/katello/tasks/update_content_default_http_proxy.rake +2 -3
  194. data/lib/katello/version.rb +1 -1
  195. data/locale/action_names.rb +47 -41
  196. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  197. data/locale/bn/katello.po +820 -106
  198. data/locale/bn/katello.po.time_stamp +0 -0
  199. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  200. data/locale/cs/katello.po +818 -104
  201. data/locale/cs/katello.po.time_stamp +0 -0
  202. data/locale/de/LC_MESSAGES/katello.mo +0 -0
  203. data/locale/de/katello.po +893 -179
  204. data/locale/de/katello.po.time_stamp +0 -0
  205. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  206. data/locale/en/katello.po +817 -103
  207. data/locale/en/katello.po.time_stamp +0 -0
  208. data/locale/es/LC_MESSAGES/katello.mo +0 -0
  209. data/locale/es/katello.po +882 -168
  210. data/locale/es/katello.po.time_stamp +0 -0
  211. data/locale/fr/LC_MESSAGES/katello.mo +0 -0
  212. data/locale/fr/katello.po +1219 -505
  213. data/locale/fr/katello.po.time_stamp +0 -0
  214. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  215. data/locale/gu/katello.po +826 -112
  216. data/locale/gu/katello.po.time_stamp +0 -0
  217. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  218. data/locale/hi/katello.po +826 -112
  219. data/locale/hi/katello.po.time_stamp +0 -0
  220. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  221. data/locale/it/katello.po +863 -148
  222. data/locale/it/katello.po.time_stamp +0 -0
  223. data/locale/ja/LC_MESSAGES/katello.mo +0 -0
  224. data/locale/ja/katello.po +1216 -499
  225. data/locale/ja/katello.po.time_stamp +0 -0
  226. data/locale/katello.pot +3847 -2507
  227. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  228. data/locale/kn/katello.po +826 -112
  229. data/locale/kn/katello.po.time_stamp +0 -0
  230. data/locale/ko/LC_MESSAGES/katello.mo +0 -0
  231. data/locale/ko/katello.po +912 -198
  232. data/locale/ko/katello.po.time_stamp +0 -0
  233. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  234. data/locale/mr/katello.po +826 -112
  235. data/locale/mr/katello.po.time_stamp +0 -0
  236. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  237. data/locale/or/katello.po +826 -112
  238. data/locale/or/katello.po.time_stamp +0 -0
  239. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  240. data/locale/pa/katello.po +826 -112
  241. data/locale/pa/katello.po.time_stamp +0 -0
  242. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  243. data/locale/pt/katello.po +817 -103
  244. data/locale/pt/katello.po.time_stamp +0 -0
  245. data/locale/pt_BR/LC_MESSAGES/katello.mo +0 -0
  246. data/locale/pt_BR/katello.po +879 -165
  247. data/locale/pt_BR/katello.po.time_stamp +0 -0
  248. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  249. data/locale/ru/katello.po +927 -213
  250. data/locale/ru/katello.po.time_stamp +0 -0
  251. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  252. data/locale/ta/katello.po +820 -106
  253. data/locale/ta/katello.po.time_stamp +0 -0
  254. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  255. data/locale/te/katello.po +826 -112
  256. data/locale/te/katello.po.time_stamp +0 -0
  257. data/locale/zh_CN/LC_MESSAGES/katello.mo +0 -0
  258. data/locale/zh_CN/katello.po +1202 -486
  259. data/locale/zh_CN/katello.po.time_stamp +0 -0
  260. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  261. data/locale/zh_TW/katello.po +856 -142
  262. data/locale/zh_TW/katello.po.time_stamp +0 -0
  263. data/webpack/components/EditableTextInput/EditableTextInput.js +20 -5
  264. data/webpack/components/Errata/index.js +38 -8
  265. data/webpack/components/Packages/index.js +1 -4
  266. data/webpack/components/Search/Search.js +18 -3
  267. data/webpack/components/SelectAllCheckbox/index.js +1 -0
  268. data/webpack/components/SelectableDropdown/SelectableDropdown.js +4 -2
  269. data/webpack/components/Table/EmptyStateMessage.js +21 -7
  270. data/webpack/components/Table/MainTable.js +29 -4
  271. data/webpack/components/Table/TableHooks.js +63 -19
  272. data/webpack/components/Table/TableWrapper.js +4 -2
  273. data/webpack/components/Table/components/SortableColumnHeaders.js +19 -0
  274. data/webpack/components/Table/components/TranslatedPlural.js +57 -0
  275. data/webpack/components/TypeAhead/TypeAhead.js +8 -0
  276. data/webpack/components/TypeAhead/pf4Search/TypeAheadInput.js +13 -11
  277. data/webpack/components/TypeAhead/pf4Search/TypeAheadSearch.js +5 -2
  278. data/webpack/components/extensions/HostDetails/ActionsBar/index.js +27 -0
  279. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ChangeHostCVModal.js +256 -0
  280. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/ContentViewDetailsCard.js +202 -0
  281. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/HostContentViewActions.js +19 -0
  282. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/HostContentViewConstants.js +2 -0
  283. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/__tests__/changeHostCVModal.test.js +131 -0
  284. data/webpack/components/extensions/HostDetails/Cards/{__tests__ → ContentViewDetailsCard/__tests__}/contentViewDetailsCard.test.js +22 -0
  285. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/__tests__/contentViews.fixtures.json +443 -0
  286. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard/__tests__/envPaths.fixtures.json +320 -0
  287. data/webpack/components/extensions/HostDetails/Cards/ErrataOverviewCard.js +57 -33
  288. data/webpack/components/extensions/HostDetails/Cards/ErrataOverviewCard.scss +3 -0
  289. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsActions.js +30 -0
  290. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsCard.js +187 -0
  291. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsConstants.js +7 -0
  292. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsModal.js +227 -0
  293. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/HostCollectionsSelectors.js +18 -0
  294. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/availableHostCollections.fixtures.json +106 -0
  295. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/hostCollectionsCard.test.js +110 -0
  296. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/hostCollectionsModal.test.js +235 -0
  297. data/webpack/components/extensions/HostDetails/Cards/HostCollectionsCard/__tests__/removableHostCollections.fixtures.json +45 -0
  298. data/webpack/components/extensions/HostDetails/Cards/__tests__/errataOverviewCard.test.js +33 -8
  299. data/webpack/components/extensions/HostDetails/DetailsTabCards/InstalledProductsCard.js +44 -0
  300. data/webpack/components/extensions/HostDetails/DetailsTabCards/RegistrationCard.js +107 -0
  301. data/webpack/components/extensions/HostDetails/DetailsTabCards/SystemPropertiesCardExtensions.js +38 -0
  302. data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/ErrataTab.js +62 -39
  303. data/webpack/components/extensions/HostDetails/Tabs/ErrataTab/HostErrataActions.js +1 -7
  304. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsActions.js +1 -3
  305. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsConstants.js +28 -0
  306. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/ModuleStreamsTab.js +486 -116
  307. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/__tests__/moduleStreamsTab.test.js +147 -1
  308. data/webpack/components/extensions/HostDetails/Tabs/ModuleStreamsTab/__tests__/modules.fixtures.json +6 -3
  309. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/HostPackagesActions.js +1 -7
  310. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackageInstallModal.js +2 -1
  311. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +48 -19
  312. data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionActions.js +35 -47
  313. data/webpack/components/extensions/HostDetails/Tabs/RemoteExecutionConstants.js +1 -0
  314. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsActions.js +33 -54
  315. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsConstants.js +20 -0
  316. data/webpack/components/extensions/HostDetails/Tabs/RepositorySetsTab/RepositorySetsTab.js +269 -132
  317. data/webpack/components/extensions/HostDetails/Tabs/TracesTab/TracesTab.js +47 -11
  318. data/webpack/components/extensions/HostDetails/Tabs/__tests__/errataTab.test.js +30 -42
  319. data/webpack/components/extensions/HostDetails/Tabs/__tests__/packagesTab.test.js +36 -1
  320. data/webpack/components/extensions/HostDetails/Tabs/__tests__/repositorySetsTab.test.js +94 -46
  321. data/webpack/components/extensions/HostDetails/Tabs/__tests__/tracesTab.test.js +18 -2
  322. data/webpack/components/extensions/HostDetails/Tabs/customizedRexUrlHelpers.js +6 -0
  323. data/webpack/containers/Application/config.js +5 -0
  324. data/webpack/global_index.js +30 -7
  325. data/webpack/global_test_setup.js +13 -0
  326. data/webpack/redux/actions/RedHatRepositories/enabled.js +2 -1
  327. data/webpack/redux/actions/RedHatRepositories/helpers.js +9 -8
  328. data/webpack/scenes/AlternateContentSources/ACSActions.js +53 -0
  329. data/webpack/scenes/AlternateContentSources/ACSConstants.js +4 -0
  330. data/webpack/scenes/AlternateContentSources/ACSIndexPage.js +23 -0
  331. data/webpack/scenes/AlternateContentSources/ACSSelectors.js +15 -0
  332. data/webpack/scenes/AlternateContentSources/MainTable/ACSTable.js +152 -0
  333. data/webpack/scenes/AlternateContentSources/MainTable/__tests__/acsIndex.fixtures.json +91 -0
  334. data/webpack/scenes/AlternateContentSources/MainTable/__tests__/acsTable.test.js +67 -0
  335. data/webpack/scenes/AlternateContentSources/index.js +4 -0
  336. data/webpack/scenes/Content/Details/ContentRepositories.js +1 -0
  337. data/webpack/scenes/Content/Table/ContentTable.js +1 -0
  338. data/webpack/scenes/ContentViews/ContentViewsActions.js +6 -2
  339. data/webpack/scenes/ContentViews/ContentViewsConstants.js +11 -3
  340. data/webpack/scenes/ContentViews/Copy/CopyContentViewForm.js +2 -1
  341. data/webpack/scenes/ContentViews/Create/ContentViewFormComponents.js +10 -1
  342. data/webpack/scenes/ContentViews/Create/CreateContentViewForm.js +4 -3
  343. data/webpack/scenes/ContentViews/Create/__tests__/createContentView.test.js +1 -1
  344. data/webpack/scenes/ContentViews/Delete/ContentViewDeleteWizard.js +7 -5
  345. data/webpack/scenes/ContentViews/Delete/Steps/CVDeletionFinish.js +29 -21
  346. data/webpack/scenes/ContentViews/Delete/__tests__/contentViewDelete.test.js +15 -8
  347. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewAddModal.js +3 -3
  348. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ComponentContentViewBulkAddModal.js +4 -4
  349. data/webpack/scenes/ContentViews/Details/ComponentContentViews/ContentViewComponents.js +3 -1
  350. data/webpack/scenes/ContentViews/Details/ComponentContentViews/__tests__/contentViewComponents.test.js +4 -4
  351. data/webpack/scenes/ContentViews/Details/ContentViewDetailActions.js +34 -8
  352. data/webpack/scenes/ContentViews/Details/ContentViewDetailSelectors.js +33 -29
  353. data/webpack/scenes/ContentViews/Details/ContentViewDetails.js +130 -79
  354. data/webpack/scenes/ContentViews/Details/ContentViewInfo.js +17 -3
  355. data/webpack/scenes/ContentViews/Details/Filters/Add/CVFilterAddModal.js +2 -1
  356. data/webpack/scenes/ContentViews/Details/Filters/AffectedRepositories/AffectedRepositoryTable.js +2 -1
  357. data/webpack/scenes/ContentViews/Details/Filters/CVContainerImageFilterContent.js +6 -1
  358. data/webpack/scenes/ContentViews/Details/Filters/CVErrataDateFilterContent.js +41 -21
  359. data/webpack/scenes/ContentViews/Details/Filters/CVErrataIDFilterContent.js +38 -20
  360. data/webpack/scenes/ContentViews/Details/Filters/CVModuleStreamFilterContent.js +2 -0
  361. data/webpack/scenes/ContentViews/Details/Filters/CVPackageGroupFilterContent.js +8 -1
  362. data/webpack/scenes/ContentViews/Details/Filters/CVRpmFilterContent.js +9 -1
  363. data/webpack/scenes/ContentViews/Details/Filters/ContentViewFilters.js +2 -1
  364. data/webpack/scenes/ContentViews/Details/Filters/MatchContentModal/CVRpmMatchContentModal.js +1 -0
  365. data/webpack/scenes/ContentViews/Details/Filters/Rules/ContainerTag/AddEditContainerTagRuleModal.js +27 -12
  366. data/webpack/scenes/ContentViews/Details/Filters/Rules/Package/AddEditPackageRuleModal.js +39 -17
  367. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVContainerImageFilterContent.test.js +27 -10
  368. data/webpack/scenes/ContentViews/Details/Filters/__tests__/CVRpmFilterContent.test.js +46 -23
  369. data/webpack/scenes/ContentViews/Details/Histories/ContentViewHistories.js +3 -2
  370. data/webpack/scenes/ContentViews/Details/Promote/ContentViewVersionPromote.js +5 -2
  371. data/webpack/scenes/ContentViews/Details/Repositories/ContentViewRepositories.js +161 -108
  372. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewAddRemove.test.js +7 -7
  373. data/webpack/scenes/ContentViews/Details/Repositories/__tests__/contentViewRepoAdd.fixture.json +1 -0
  374. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/ActionSummary.js +58 -0
  375. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/BulkDeleteContextWrapper.js +45 -0
  376. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/BulkDeleteHelpers.js +30 -0
  377. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/BulkDeleteModal.js +56 -0
  378. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/Steps/ConfirmBulkDelete.js +126 -0
  379. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/Steps/FinishBulkDelete.js +61 -0
  380. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/Steps/ReassignActivationKeys.js +196 -0
  381. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/Steps/ReassignHosts.js +220 -0
  382. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/Steps/ReviewEnvironments.js +104 -0
  383. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/BulkDeleteModal.test.js +122 -0
  384. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/BulkDeleteVersions.fixtures.json +600 -0
  385. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/contentView.fixtures.json +1504 -0
  386. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/contentViewVersion.fixtures.json +936 -0
  387. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/environmentPaths.fixtures.json +261 -0
  388. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/__tests__/hosts.fixtures.json +163 -0
  389. data/webpack/scenes/ContentViews/Details/Versions/BulkDelete/bulkDeleteSteps.js +79 -0
  390. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +192 -167
  391. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveCVVersionWizard.js +2 -5
  392. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionDeleteFinish.js +38 -53
  393. data/webpack/scenes/ContentViews/Details/Versions/Delete/RemoveSteps/CVVersionRemoveReview.js +1 -1
  394. data/webpack/scenes/ContentViews/Details/Versions/Delete/__tests__/cvVersionRemove.test.js +6 -12
  395. data/webpack/scenes/ContentViews/Details/Versions/Delete/affectedActivationKeys.js +1 -0
  396. data/webpack/scenes/ContentViews/Details/Versions/Delete/affectedHosts.js +1 -0
  397. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetailConfig.js +1 -0
  398. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetails.js +8 -20
  399. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetailsHeader.js +23 -13
  400. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/ContentViewVersionDetailsTable.js +3 -0
  401. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetails.fixtures.json +4 -4
  402. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetails.test.js +0 -3
  403. data/webpack/scenes/ContentViews/Details/Versions/VersionDetails/__tests__/ContentViewVersionDetailsEmpty.test.js +4 -4
  404. data/webpack/scenes/ContentViews/Details/Versions/__tests__/contentViewVersions.test.js +12 -14
  405. data/webpack/scenes/ContentViews/Details/__tests__/contentViewDetails.fixtures.json +1 -0
  406. data/webpack/scenes/ContentViews/Publish/CVPublishFinish.js +96 -117
  407. data/webpack/scenes/ContentViews/Publish/PublishContentViewWizard.js +13 -19
  408. data/webpack/scenes/ContentViews/Publish/__tests__/publishContentView.test.js +9 -20
  409. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +165 -148
  410. data/webpack/scenes/ContentViews/Table/tableDataGenerator.js +2 -0
  411. data/webpack/scenes/ContentViews/__tests__/contentViewPage.test.js +4 -4
  412. data/webpack/scenes/ContentViews/components/ContentViewIcon.js +14 -3
  413. data/webpack/scenes/ContentViews/components/ContentViewsCounter.js +1 -1
  414. data/webpack/scenes/ContentViews/components/EnvironmentLabels.js +4 -3
  415. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.js +9 -5
  416. data/webpack/scenes/ContentViews/components/EnvironmentPaths/EnvironmentPaths.scss +6 -1
  417. data/webpack/scenes/ContentViews/components/TaskPresenter/TaskPresenter.js +40 -35
  418. data/webpack/scenes/ContentViews/expansions/DetailsExpansion.js +2 -2
  419. data/webpack/scenes/ContentViews/expansions/RelatedCompositeContentViewsModal.js +1 -1
  420. data/webpack/scenes/ContentViews/expansions/RelatedContentViewComponentsModal.js +4 -4
  421. data/webpack/scenes/ContentViews/expansions/__tests__/contentViewComponentsModal.test.js +1 -1
  422. data/webpack/scenes/ContentViews/helpers.js +3 -0
  423. data/webpack/scenes/Hosts/ChangeContentSource/helpers.js +5 -0
  424. data/webpack/scenes/RedHatRepositories/RedHatRepositoriesPage.js +3 -3
  425. data/webpack/scenes/RedHatRepositories/components/EnabledRepository/EnabledRepository.js +2 -1
  426. data/webpack/scenes/RedHatRepositories/components/EnabledRepository/__tests__/EnabledRepository.test.js +2 -0
  427. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/CdnConfigurationConstants.js +3 -3
  428. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/CdnTypeForm.js +2 -0
  429. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/{AirGappedTypeForm.js → ExportSyncForm.js} +7 -6
  430. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/{UpstreamServerTypeForm.js → NetworkSyncForm.js} +15 -7
  431. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/__tests__/{AirGappedTypeForm.test.js → ExportSyncForm.test.js} +4 -4
  432. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/__tests__/{UpstreamServerTypeForm.test.js → NetworkSyncForm.test.js} +8 -8
  433. data/webpack/scenes/Subscriptions/Manifest/CdnConfigurationTab/index.js +11 -11
  434. data/webpack/scenes/Subscriptions/SubscriptionConstants.js +1 -0
  435. data/webpack/scenes/Subscriptions/SubscriptionsPage.js +8 -7
  436. data/webpack/scenes/Tasks/TaskActions.js +6 -0
  437. data/webpack/scenes/Tasks/TaskSelectors.js +11 -0
  438. data/webpack/scenes/Tasks/helpers.js +33 -5
  439. data/webpack/utils/helpers.js +2 -0
  440. metadata +158 -40
  441. data/app/models/setting/content.rb +0 -201
  442. data/webpack/components/Table/__test__/useBulkSelect.test.js +0 -99
  443. data/webpack/components/extensions/HostDetails/Cards/ContentViewDetailsCard.js +0 -96
  444. data/webpack/scenes/ContentViews/Details/DetailsContainer.js +0 -36
File without changes
@@ -17,7 +17,7 @@ const PASSWORD_MASK = '••••••••';
17
17
 
18
18
  const EditableTextInput = ({
19
19
  onEdit, value, textArea, attribute, placeholder, isPassword, hasPassword,
20
- component, currentAttribute, setCurrentAttribute, disabled,
20
+ component, currentAttribute, setCurrentAttribute, disabled, ouiaId,
21
21
  }) => {
22
22
  const [inputValue, setInputValue] = useState(value);
23
23
  const [editing, setEditing] = useState(false);
@@ -76,11 +76,17 @@ const EditableTextInput = ({
76
76
  <Split>
77
77
  <SplitItem>
78
78
  {textArea ?
79
- (<TextArea {...inputProps} aria-label={`${attribute} text area`} />) :
80
- (<TextInput {...inputProps} type={(isPassword && !showPassword) ? 'password' : 'text'} aria-label={`${attribute} text input`} />)}
79
+ (<TextArea {...inputProps} aria-label={`${attribute} text area`} ouiaId={ouiaId} />) :
80
+ (<TextInput
81
+ {...inputProps}
82
+ type={(isPassword && !showPassword) ? 'password' : 'text'}
83
+ aria-label={`${attribute} text input`}
84
+ ouiaId={ouiaId}
85
+ />)}
81
86
  </SplitItem>
82
87
  <SplitItem>
83
88
  <Button
89
+ ouiaId={`submit-button-${attribute}`}
84
90
  aria-label={`submit ${attribute}`}
85
91
  variant="plain"
86
92
  onClick={onSubmit}
@@ -89,13 +95,19 @@ const EditableTextInput = ({
89
95
  </Button>
90
96
  </SplitItem>
91
97
  <SplitItem>
92
- <Button aria-label={`clear ${attribute}`} variant="plain" onClick={onClear}>
98
+ <Button ouiaId={`clear-button-${attribute}`} aria-label={`clear ${attribute}`} variant="plain" onClick={onClear}>
93
99
  <TimesIcon />
94
100
  </Button>
95
101
  </SplitItem>
96
102
  {isPassword ?
97
103
  <SplitItem>
98
- <Button aria-label={`show-password ${attribute}`} variant="plain" isDisabled={!inputValue?.length} onClick={toggleShowPassword}>
104
+ <Button
105
+ ouiaId={`show-button-${attribute}`}
106
+ aria-label={`show-password ${attribute}`}
107
+ variant="plain"
108
+ isDisabled={!inputValue?.length}
109
+ onClick={toggleShowPassword}
110
+ >
99
111
  {showPassword ?
100
112
  (<EyeSlashIcon />) :
101
113
  (<EyeIcon />)}
@@ -123,6 +135,7 @@ const EditableTextInput = ({
123
135
  >
124
136
  <Button
125
137
  className="foreman-edit-icon"
138
+ ouiaId={`edit-button-${attribute}`}
126
139
  aria-label={`edit ${attribute}`}
127
140
  variant="plain"
128
141
  onClick={onEditClick}
@@ -148,6 +161,7 @@ EditableTextInput.propTypes = {
148
161
  disabled: PropTypes.bool,
149
162
  isPassword: PropTypes.bool,
150
163
  hasPassword: PropTypes.bool,
164
+ ouiaId: PropTypes.string,
151
165
  };
152
166
 
153
167
  EditableTextInput.defaultProps = {
@@ -160,6 +174,7 @@ EditableTextInput.defaultProps = {
160
174
  disabled: false,
161
175
  isPassword: false,
162
176
  hasPassword: false,
177
+ ouiaId: undefined,
163
178
  };
164
179
 
165
180
  export default EditableTextInput;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import PropTypes from 'prop-types';
2
3
  import { TableText } from '@patternfly/react-table';
3
4
  import {
4
5
  chart_color_black_500 as pfBlack,
@@ -13,39 +14,68 @@ import {
13
14
  EnhancementIcon,
14
15
  SquareIcon,
15
16
  } from '@patternfly/react-icons';
16
- import PropTypes from 'prop-types';
17
+ import { TranslatedAnchor } from '../Table/components/TranslatedPlural';
17
18
 
18
19
  export const ErrataMapper = ({ data, id }) => data.map(({ x: type, y: count }) => <ErrataSummary count={count} type={type} key={`${count} ${type}`} id={id} />);
19
20
 
20
21
  export const ErrataSummary = ({ type, count }) => {
21
22
  let ErrataIcon;
22
23
  let label;
23
- let name;
24
24
  let url;
25
25
  let color;
26
26
  switch (type) {
27
27
  case 'security':
28
28
  label = __('Security');
29
29
  ErrataIcon = SecurityIcon;
30
- name = 'security advisories';
31
30
  color = '#0066cc';
32
- url = <a href="#/Content/errata?type=security"> {count} {name} </a>;
31
+ url = (
32
+ <TranslatedAnchor
33
+ id="errata-card-security-count"
34
+ style={{ marginLeft: '0.4rem' }}
35
+ href="#/Content/errata?type=security"
36
+ count={count}
37
+ plural="security advisories"
38
+ singular="security advisory"
39
+ zeroMsg="# security advisories"
40
+ ariaLabel={`${count} security advisories`}
41
+ />
42
+ );
33
43
  break;
34
44
  case 'recommended':
35
45
  case 'bugfix':
36
46
  label = __('Bugfix');
37
47
  ErrataIcon = BugIcon;
38
- name = 'bug fixes';
39
48
  color = '#8bc1f7';
40
- url = <a href="#/Content/errata?type=bugfix"> {count} {name} </a>;
49
+ url = (
50
+ <TranslatedAnchor
51
+ id="errata-card-bugfix-count"
52
+ style={{ marginLeft: '0.4rem' }}
53
+ href="#/Content/errata?type=bugfix"
54
+ count={count}
55
+ plural="bug fixes"
56
+ singular="bug fix"
57
+ zeroMsg="# bug fixes"
58
+ ariaLabel={`${count} bug fixes`}
59
+ />
60
+ );
41
61
  break;
42
62
  case 'enhancement':
43
63
  case 'optional':
44
64
  label = __('Enhancement');
45
65
  ErrataIcon = EnhancementIcon;
46
- name = 'enhancements';
47
66
  color = '#002f5d';
48
- url = <a href="#/Content/errata?type=enhancement"> {count} {name} </a>;
67
+ url = (
68
+ <TranslatedAnchor
69
+ id="errata-card-enhancement-count"
70
+ style={{ marginLeft: '0.4rem' }}
71
+ href="#/Content/errata?type=enhancement"
72
+ count={count}
73
+ plural="enhancements"
74
+ singular="enhancement"
75
+ zeroMsg="# enhancements"
76
+ ariaLabel={`${count} enhancements`}
77
+ />
78
+ );
49
79
  break;
50
80
  default:
51
81
  }
@@ -4,7 +4,6 @@ import { translate as __ } from 'foremanReact/common/I18n';
4
4
  import {
5
5
  CheckIcon,
6
6
  LongArrowAltUpIcon,
7
- MinusIcon,
8
7
  } from '@patternfly/react-icons';
9
8
  import PropTypes from 'prop-types';
10
9
 
@@ -41,16 +40,14 @@ PackagesStatus.defaultProps = {
41
40
 
42
41
  export const PackagesLatestVersion = ({ name, upgradable_version: upgradableVersion }) => {
43
42
  let label;
44
- let color;
45
43
 
46
44
  if (upgradableVersion == null) {
47
45
  label = '';
48
- color = 'black';
49
46
  } else {
50
47
  label = upgradableVersion.replace(`${name}-`, '');
51
48
  }
52
49
 
53
- return <TableText wrapModifier="nowrap">{color && <MinusIcon color={color} title={label} />} {label} </TableText>;
50
+ return <TableText wrapModifier="nowrap">{label || '—'} </TableText>;
54
51
  };
55
52
 
56
53
  PackagesLatestVersion.propTypes = {
@@ -23,6 +23,8 @@ const Search = ({
23
23
  foremanApiAutoComplete,
24
24
  bookmarkController,
25
25
  placeholder,
26
+ isTextInput,
27
+ setTextInputValue,
26
28
  }) => {
27
29
  const [items, setItems] = useState([]);
28
30
  const dispatch = useDispatch();
@@ -48,9 +50,16 @@ const Search = ({
48
50
  }
49
51
  // Checking whether the current component is mounted before state change events
50
52
  if (!mountedRef.current) return;
51
- setItems(data?.data?.filter(({ error }) => !error).map(({ label }) => ({
52
- text: label.trim(),
53
- })));
53
+ switch (true) {
54
+ case endpoint.includes('auto_complete_arch'):
55
+ case endpoint.includes('auto_complete_name'):
56
+ setItems(data?.data?.map(label => ({ text: label.trim() })));
57
+ break;
58
+ default:
59
+ setItems(data?.data?.filter(({ error }) => !error).map(({ label }) => ({
60
+ text: label.trim(),
61
+ })));
62
+ }
54
63
  }
55
64
 
56
65
  if (autoSearchEnabled && patternfly4) {
@@ -83,6 +92,8 @@ const Search = ({
83
92
  patternfly4={patternfly4}
84
93
  autoSearchEnabled={autoSearchEnabled}
85
94
  placeholder={placeholder}
95
+ isTextInput={isTextInput}
96
+ setTextInputValue={setTextInputValue}
86
97
  />
87
98
  </div>
88
99
  );
@@ -102,6 +113,8 @@ Search.propTypes = {
102
113
  }),
103
114
  bookmarkController: PropTypes.string,
104
115
  placeholder: PropTypes.string,
116
+ isTextInput: PropTypes.bool,
117
+ setTextInputValue: PropTypes.func,
105
118
  };
106
119
 
107
120
  Search.defaultProps = {
@@ -115,6 +128,8 @@ Search.defaultProps = {
115
128
  isDisabled: undefined,
116
129
  bookmarkController: undefined,
117
130
  placeholder: undefined,
131
+ isTextInput: false,
132
+ setTextInputValue: undefined,
118
133
  };
119
134
 
120
135
  export default Search;
@@ -95,6 +95,7 @@ const SelectAllCheckbox = ({
95
95
  aria-label="Select all"
96
96
  onChange={checked => onSelectAllCheckboxChange(checked)}
97
97
  isChecked={selectionToggle}
98
+ isDisabled={totalCount === 0 && selectedCount === 0}
98
99
  >
99
100
  {selectedCount > 0 && `${selectedCount} selected`}
100
101
  </DropdownToggleCheckbox>,
@@ -5,7 +5,7 @@ import { ErrorCircleOIcon } from '@patternfly/react-icons';
5
5
 
6
6
 
7
7
  const SelectableDropdown = ({
8
- items, title, showTitle, selected, setSelected, loading, error,
8
+ items, title, showTitle, selected, setSelected, loading, error, isDisabled,
9
9
  }) => {
10
10
  const [isOpen, setIsOpen] = useState(false);
11
11
  const icon = () => {
@@ -40,7 +40,7 @@ const SelectableDropdown = ({
40
40
  onSelect={onSelect}
41
41
  selections={selected}
42
42
  isOpen={isOpen}
43
- isDisabled={loading || error}
43
+ isDisabled={loading || error || isDisabled}
44
44
  toggleIcon={icon()}
45
45
  >
46
46
  {selectItems}
@@ -59,12 +59,14 @@ SelectableDropdown.propTypes = {
59
59
  // If the items are loaded dynamically, you can pass in loading or error states
60
60
  loading: PropTypes.bool,
61
61
  error: PropTypes.bool,
62
+ isDisabled: PropTypes.bool,
62
63
  };
63
64
 
64
65
  SelectableDropdown.defaultProps = {
65
66
  loading: false,
66
67
  error: false,
67
68
  showTitle: true,
69
+ isDisabled: false,
68
70
  };
69
71
 
70
72
 
@@ -9,18 +9,21 @@ import {
9
9
  } from '@patternfly/react-core';
10
10
  import PropTypes from 'prop-types';
11
11
  import { translate as __ } from 'foremanReact/common/I18n';
12
- import { CubeIcon, ExclamationCircleIcon, SearchIcon } from '@patternfly/react-icons';
13
- import { global_danger_color_200 as dangerColor } from '@patternfly/react-tokens';
12
+ import { CubeIcon, ExclamationCircleIcon, SearchIcon, CheckCircleIcon } from '@patternfly/react-icons';
13
+ import { global_danger_color_200 as dangerColor, global_success_color_100 as successColor } from '@patternfly/react-tokens';
14
14
 
15
- const KatelloEmptyStateIcon = ({ error, search, customIcon }) => {
15
+ const KatelloEmptyStateIcon = ({
16
+ error, search, customIcon, happyIcon,
17
+ }) => {
16
18
  if (error) return <EmptyStateIcon icon={ExclamationCircleIcon} color={dangerColor.value} />;
17
19
  if (search) return <EmptyStateIcon icon={SearchIcon} />;
20
+ if (happyIcon) return <EmptyStateIcon icon={CheckCircleIcon} color={successColor.value} />;
18
21
  if (customIcon) return <EmptyStateIcon icon={customIcon} />;
19
22
  return <EmptyStateIcon icon={CubeIcon} />;
20
23
  };
21
24
 
22
25
  const EmptyStateMessage = ({
23
- title, body, error, search, customIcon,
26
+ title, body, error, search, customIcon, happy,
24
27
  }) => {
25
28
  let emptyStateTitle = title;
26
29
  let emptyStateBody = body;
@@ -37,8 +40,15 @@ const EmptyStateMessage = ({
37
40
  }
38
41
  return (
39
42
  <Bullseye>
40
- <EmptyState variant={EmptyStateVariant.small}>
41
- <KatelloEmptyStateIcon error={!!error} search={search} customIcon={customIcon} />
43
+ <EmptyState
44
+ variant={happy ? EmptyStateVariant.large : EmptyStateVariant.small}
45
+ >
46
+ <KatelloEmptyStateIcon
47
+ error={!!error}
48
+ search={search}
49
+ customIcon={customIcon}
50
+ happyIcon={happy}
51
+ />
42
52
  <Title headingLevel="h2" size="lg">
43
53
  {emptyStateTitle}
44
54
  </Title>
@@ -54,23 +64,26 @@ KatelloEmptyStateIcon.propTypes = {
54
64
  error: PropTypes.bool,
55
65
  search: PropTypes.bool,
56
66
  customIcon: PropTypes.elementType,
67
+ happyIcon: PropTypes.bool,
57
68
  };
58
69
 
59
70
  KatelloEmptyStateIcon.defaultProps = {
60
71
  error: false,
61
72
  search: false,
62
73
  customIcon: undefined,
74
+ happyIcon: undefined,
63
75
  };
64
76
 
65
77
  EmptyStateMessage.propTypes = {
66
78
  title: PropTypes.string,
67
- body: PropTypes.string,
79
+ body: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]),
68
80
  error: PropTypes.oneOfType([
69
81
  PropTypes.shape({}),
70
82
  PropTypes.string,
71
83
  ]),
72
84
  search: PropTypes.bool,
73
85
  customIcon: PropTypes.elementType,
86
+ happy: PropTypes.bool,
74
87
  };
75
88
 
76
89
  EmptyStateMessage.defaultProps = {
@@ -79,6 +92,7 @@ EmptyStateMessage.defaultProps = {
79
92
  error: undefined,
80
93
  search: false,
81
94
  customIcon: undefined,
95
+ happy: false,
82
96
  };
83
97
 
84
98
  export default EmptyStateMessage;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ import { translate as __ } from 'foremanReact/common/I18n';
2
3
  import {
3
4
  Table,
4
5
  TableHeader,
@@ -15,8 +16,9 @@ import Loading from '../../components/Loading';
15
16
 
16
17
  const MainTable = ({
17
18
  status, cells, rows, error, emptyContentTitle, emptyContentBody,
18
- emptySearchTitle, emptySearchBody, searchIsActive, activeFilters,
19
- defaultFilters, actionButtons, rowsCount, children, ...extraTableProps
19
+ emptySearchTitle, emptySearchBody, errorSearchTitle, errorSearchBody,
20
+ happyEmptyContent, searchIsActive, activeFilters, defaultFilters, actionButtons, rowsCount,
21
+ children, ...extraTableProps
20
22
  }) => {
21
23
  const tableHasNoRows = () => {
22
24
  if (children) return rowsCount === 0;
@@ -28,6 +30,16 @@ const MainTable = ({
28
30
  if (status === STATUS.PENDING) return (<Loading />);
29
31
  // Can we display the error message?
30
32
  if (status === STATUS.ERROR) return (<EmptyStateMessage error={error} />);
33
+
34
+ // scoped_search errors come back as 200 with an error message,
35
+ // so they should be passed here as errorSearchBody & errorSearchTitle.
36
+ if (status === STATUS.RESOLVED && !!errorSearchBody) {
37
+ return (<EmptyStateMessage
38
+ title={errorSearchTitle}
39
+ body={errorSearchBody}
40
+ search
41
+ />);
42
+ }
31
43
  if (status === STATUS.RESOLVED && isFiltering && tableHasNoRows()) {
32
44
  return (<EmptyStateMessage
33
45
  title={emptySearchTitle}
@@ -36,7 +48,14 @@ const MainTable = ({
36
48
  />);
37
49
  }
38
50
  if (status === STATUS.RESOLVED && tableHasNoRows()) {
39
- return (<EmptyStateMessage title={emptyContentTitle} body={emptyContentBody} />);
51
+ return (
52
+ <EmptyStateMessage
53
+ title={emptyContentTitle}
54
+ body={emptyContentBody}
55
+ happy={happyEmptyContent}
56
+ search={!happyEmptyContent}
57
+ />
58
+ );
40
59
  }
41
60
 
42
61
  const tableProps = { cells, rows, ...extraTableProps };
@@ -74,9 +93,11 @@ MainTable.propTypes = {
74
93
  PropTypes.string,
75
94
  ]),
76
95
  emptyContentTitle: PropTypes.string.isRequired,
77
- emptyContentBody: PropTypes.string.isRequired,
96
+ emptyContentBody: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({})]).isRequired,
78
97
  emptySearchTitle: PropTypes.string.isRequired,
79
98
  emptySearchBody: PropTypes.string.isRequired,
99
+ errorSearchTitle: PropTypes.string,
100
+ errorSearchBody: PropTypes.string,
80
101
  searchIsActive: PropTypes.bool,
81
102
  activeFilters: PropTypes.arrayOf(PropTypes.oneOfType([
82
103
  PropTypes.string,
@@ -92,6 +113,7 @@ MainTable.propTypes = {
92
113
  PropTypes.arrayOf(PropTypes.node),
93
114
  PropTypes.node,
94
115
  ]),
116
+ happyEmptyContent: PropTypes.bool,
95
117
  };
96
118
 
97
119
  MainTable.defaultProps = {
@@ -99,11 +121,14 @@ MainTable.defaultProps = {
99
121
  searchIsActive: false,
100
122
  activeFilters: [],
101
123
  defaultFilters: [],
124
+ errorSearchTitle: __('Problem searching'),
125
+ errorSearchBody: '',
102
126
  actionButtons: false,
103
127
  children: null,
104
128
  cells: undefined,
105
129
  rows: undefined,
106
130
  rowsCount: undefined,
131
+ happyEmptyContent: false,
107
132
  };
108
133
 
109
134
  export default MainTable;
@@ -1,4 +1,4 @@
1
- import { useState, useRef, useEffect, useCallback } from 'react';
1
+ import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
2
2
  import { isEmpty } from 'lodash';
3
3
  import { useLocation } from 'react-router-dom';
4
4
  import { friendlySearchParam } from '../../utils/helpers';
@@ -53,28 +53,32 @@ class ReactConnectedSet extends Set {
53
53
  }
54
54
 
55
55
  export const useSet = (initialArry) => {
56
- const [, setToggle] = useState(0);
56
+ const [, setToggle] = useState(Date.now());
57
57
  // needed because mutating a Ref won't cause React to rerender
58
- const forceRender = () => setToggle(prev => prev + 1);
58
+ const forceRender = () => setToggle(Date.now());
59
59
  const set = useRef(new ReactConnectedSet(initialArry, forceRender));
60
60
  return set.current;
61
61
  };
62
62
 
63
63
  export const useSelectionSet = ({
64
- results, metadata,
64
+ results,
65
+ metadata,
65
66
  initialArry = [],
66
67
  idColumn = 'id',
67
68
  isSelectable = () => true,
68
69
  }) => {
69
70
  const selectionSet = useSet(initialArry);
70
71
  const pageIds = results?.map(result => result[idColumn]) ?? [];
71
- const selectableResults = results?.filter(result => isSelectable(result)) ?? [];
72
- const selectableIds = new Set(selectableResults.map(result => result[idColumn]));
72
+ const selectableResults = useMemo(() => results?.filter(result =>
73
+ isSelectable(result)) ?? [], [results, isSelectable]);
73
74
  const selectedResults = useRef({}); // { id: result }
74
- const canSelect = id => selectableIds.has(id);
75
+ const canSelect = useCallback((id) => {
76
+ const selectableIds = new Set(selectableResults.map(result => result[idColumn]));
77
+ return selectableIds.has(id);
78
+ }, [idColumn, selectableResults]);
75
79
  const areAllRowsOnPageSelected = () =>
76
80
  Number(pageIds?.length) > 0 &&
77
- pageIds.every(result => selectionSet.has(result) || !canSelect(result));
81
+ pageIds.every(result => selectionSet.has(result) || !canSelect(result));
78
82
 
79
83
  const areAllRowsSelected = () =>
80
84
  Number(selectionSet.size) > 0 && selectionSet.size === Number(metadata.selectable);
@@ -108,7 +112,8 @@ export const useSelectionSet = ({
108
112
 
109
113
  const selectedCount = selectionSet.size;
110
114
 
111
- const isSelected = id => canSelect(id) && selectionSet.has(id);
115
+ const isSelected = useCallback(id =>
116
+ canSelect(id) && selectionSet.has(id), [canSelect, selectionSet]);
112
117
 
113
118
  return {
114
119
  selectOne,
@@ -139,13 +144,12 @@ export const useBulkSelect = ({
139
144
  initialArry = [],
140
145
  initialSearchQuery = '',
141
146
  idColumn = 'id',
142
- filtersQuery = '',
143
147
  isSelectable,
144
148
  }) => {
145
149
  const { selectionSet: inclusionSet, ...selectOptions } =
146
- useSelectionSet({
147
- results, metadata, initialArry, idColumn, isSelectable,
148
- });
150
+ useSelectionSet({
151
+ results, metadata, initialArry, idColumn, isSelectable,
152
+ });
149
153
  const exclusionSet = useSet([]);
150
154
  const [searchQuery, updateSearchQuery] = useState(initialSearchQuery);
151
155
  const [selectAllMode, setSelectAllMode] = useState(false);
@@ -153,12 +157,12 @@ export const useBulkSelect = ({
153
157
  Number(metadata.selectable) - exclusionSet.size : selectOptions.selectedCount;
154
158
 
155
159
  const areAllRowsOnPageSelected = () => selectAllMode ||
156
- selectOptions.areAllRowsOnPageSelected();
160
+ selectOptions.areAllRowsOnPageSelected();
157
161
 
158
162
  const areAllRowsSelected = () => (selectAllMode && exclusionSet.size === 0) ||
159
- selectOptions.areAllRowsSelected();
163
+ selectOptions.areAllRowsSelected();
160
164
 
161
- const isSelected = (id) => {
165
+ const isSelected = useCallback((id) => {
162
166
  if (!selectOptions.isSelectable(id)) {
163
167
  return false;
164
168
  }
@@ -166,7 +170,7 @@ export const useBulkSelect = ({
166
170
  return !exclusionSet.has(id);
167
171
  }
168
172
  return inclusionSet.has(id);
169
- };
173
+ }, [exclusionSet, inclusionSet, selectAllMode, selectOptions]);
170
174
 
171
175
  const selectPage = () => {
172
176
  setSelectAllMode(false);
@@ -203,7 +207,7 @@ export const useBulkSelect = ({
203
207
 
204
208
  const fetchBulkParams = (idColumnName = idColumn) => {
205
209
  const searchQueryWithExclusionSet = () => {
206
- const query = [searchQuery, filtersQuery,
210
+ const query = [searchQuery,
207
211
  !isEmpty(exclusionSet) && `${idColumnName} !^ (${[...exclusionSet].join(',')})`];
208
212
  return query.filter(item => item).join(' and ');
209
213
  };
@@ -222,7 +226,7 @@ export const useBulkSelect = ({
222
226
  // if search value changed and cleared from a string to empty value
223
227
  // And it was select all -> then reset selections
224
228
  if ((prevSearchRef && !isEmpty(prevSearchRef.searchQuery)) &&
225
- isEmpty(searchQuery) && selectAllMode) {
229
+ isEmpty(searchQuery) && selectAllMode) {
226
230
  selectNone();
227
231
  }
228
232
  }, [searchQuery, selectAllMode, prevSearchRef, selectNone]);
@@ -260,3 +264,43 @@ export const useUrlParams = () => {
260
264
  ...urlParams,
261
265
  };
262
266
  };
267
+
268
+ export const useTableSort = ({
269
+ allColumns,
270
+ columnsToSortParams,
271
+ initialSortColumnName = allColumns[0],
272
+ }) => {
273
+ if (!Object.keys(columnsToSortParams).includes(initialSortColumnName)) {
274
+ throw new Error(`initialSortColumnName '${initialSortColumnName}' must also be defined in columnsToSortParams`);
275
+ }
276
+ const [activeSortColumn, setActiveSortColumn] = useState(initialSortColumnName);
277
+ const [activeSortDirection, setActiveSortDirection] = useState('asc');
278
+
279
+ // Patternfly sort function
280
+ const onSort = (_event, index, direction) => {
281
+ setActiveSortColumn(allColumns?.[index]);
282
+ setActiveSortDirection(direction);
283
+ };
284
+
285
+ // Patternfly sort params to pass to the <Th> component.
286
+ // (but you should probably just use <SortableColumnHeaders> instead)
287
+ const pfSortParams = (columnName, newSortColIndex) => ({
288
+ columnIndex: newSortColIndex ?? allColumns?.indexOf(columnName),
289
+ sortBy: {
290
+ defaultDirection: 'asc',
291
+ direction: activeSortDirection,
292
+ index: allColumns?.indexOf(activeSortColumn),
293
+ },
294
+ onSort,
295
+ });
296
+
297
+ return {
298
+ pfSortParams,
299
+ apiSortParams: { // scoped_search params to pass to the Katello API
300
+ sort_by: columnsToSortParams[activeSortColumn],
301
+ sort_order: activeSortDirection,
302
+ },
303
+ activeSortColumn, // state values to pass as additionalListeners
304
+ activeSortDirection,
305
+ };
306
+ };
@@ -50,6 +50,7 @@ const TableWrapper = ({
50
50
  const perPage = Number(metadata?.per_page ?? foremanPerPage);
51
51
  const page = Number(metadata?.page ?? 1);
52
52
  const total = Number(metadata?.subtotal ?? 0);
53
+ const totalSelectableCount = Number(metadata?.selectable ?? total);
53
54
  const { pageRowCount } = getPageStats({ total, page, perPage });
54
55
  const unresolvedStatus = !!allTableProps?.status && allTableProps.status !== STATUS.RESOLVED;
55
56
  const unresolvedStatusOrNoRows = unresolvedStatus || pageRowCount === 0;
@@ -160,7 +161,6 @@ const TableWrapper = ({
160
161
  paginationChangePending.current = pagData;
161
162
  };
162
163
 
163
-
164
164
  return (
165
165
  <>
166
166
  <Flex style={{ alignItems: 'center' }} className="margin-16-24">
@@ -175,7 +175,7 @@ const TableWrapper = ({
175
175
  pageRowCount,
176
176
  }
177
177
  }
178
- totalCount={Number(metadata?.selectable ?? total)}
178
+ totalCount={totalSelectableCount}
179
179
  areAllRowsOnPageSelected={areAllRowsOnPageSelected()}
180
180
  areAllRowsSelected={areAllRowsSelected()}
181
181
  />
@@ -244,6 +244,8 @@ const TableWrapper = ({
244
244
  };
245
245
 
246
246
  TableWrapper.propTypes = {
247
+ // ouiaId is needed on all tables for automation testing
248
+ ouiaId: PropTypes.string.isRequired,
247
249
  searchQuery: PropTypes.string.isRequired,
248
250
  updateSearchQuery: PropTypes.func.isRequired,
249
251
  fetchItems: PropTypes.func.isRequired,
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { Th } from '@patternfly/react-table';
3
+
4
+ const SortableColumnHeaders = ({
5
+ columnHeaders,
6
+ pfSortParams,
7
+ columnsToSortParams,
8
+ }) => (
9
+ columnHeaders.map(col => (
10
+ <Th
11
+ key={col}
12
+ sort={columnsToSortParams[col] ? pfSortParams(col) : undefined}
13
+ >
14
+ {col}
15
+ </Th>
16
+ ))
17
+ );
18
+
19
+ export default SortableColumnHeaders;