katello 4.17.1 → 4.18.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (348) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/katello/locale/bn/katello.js +148 -4
  3. data/app/assets/javascripts/katello/locale/bn_IN/katello.js +148 -4
  4. data/app/assets/javascripts/katello/locale/ca/katello.js +148 -4
  5. data/app/assets/javascripts/katello/locale/cs/katello.js +148 -4
  6. data/app/assets/javascripts/katello/locale/cs_CZ/katello.js +148 -4
  7. data/app/assets/javascripts/katello/locale/de/katello.js +147 -3
  8. data/app/assets/javascripts/katello/locale/de_AT/katello.js +148 -4
  9. data/app/assets/javascripts/katello/locale/de_DE/katello.js +148 -4
  10. data/app/assets/javascripts/katello/locale/el/katello.js +148 -4
  11. data/app/assets/javascripts/katello/locale/en/katello.js +148 -4
  12. data/app/assets/javascripts/katello/locale/en_GB/katello.js +148 -4
  13. data/app/assets/javascripts/katello/locale/en_US/katello.js +148 -4
  14. data/app/assets/javascripts/katello/locale/es/katello.js +147 -3
  15. data/app/assets/javascripts/katello/locale/et_EE/katello.js +148 -4
  16. data/app/assets/javascripts/katello/locale/fr/katello.js +147 -3
  17. data/app/assets/javascripts/katello/locale/gl/katello.js +148 -4
  18. data/app/assets/javascripts/katello/locale/gu/katello.js +148 -4
  19. data/app/assets/javascripts/katello/locale/he_IL/katello.js +148 -4
  20. data/app/assets/javascripts/katello/locale/hi/katello.js +148 -4
  21. data/app/assets/javascripts/katello/locale/id/katello.js +148 -4
  22. data/app/assets/javascripts/katello/locale/it/katello.js +148 -4
  23. data/app/assets/javascripts/katello/locale/ja/katello.js +147 -3
  24. data/app/assets/javascripts/katello/locale/ka/katello.js +147 -3
  25. data/app/assets/javascripts/katello/locale/kn/katello.js +148 -4
  26. data/app/assets/javascripts/katello/locale/ko/katello.js +147 -3
  27. data/app/assets/javascripts/katello/locale/ml_IN/katello.js +148 -4
  28. data/app/assets/javascripts/katello/locale/mr/katello.js +148 -4
  29. data/app/assets/javascripts/katello/locale/nl_NL/katello.js +148 -4
  30. data/app/assets/javascripts/katello/locale/or/katello.js +148 -4
  31. data/app/assets/javascripts/katello/locale/pa/katello.js +148 -4
  32. data/app/assets/javascripts/katello/locale/pl/katello.js +148 -4
  33. data/app/assets/javascripts/katello/locale/pl_PL/katello.js +148 -4
  34. data/app/assets/javascripts/katello/locale/pt/katello.js +148 -4
  35. data/app/assets/javascripts/katello/locale/pt_BR/katello.js +147 -3
  36. data/app/assets/javascripts/katello/locale/ro/katello.js +148 -4
  37. data/app/assets/javascripts/katello/locale/ro_RO/katello.js +148 -4
  38. data/app/assets/javascripts/katello/locale/ru/katello.js +148 -4
  39. data/app/assets/javascripts/katello/locale/sl/katello.js +148 -4
  40. data/app/assets/javascripts/katello/locale/sv_SE/katello.js +148 -4
  41. data/app/assets/javascripts/katello/locale/ta/katello.js +148 -4
  42. data/app/assets/javascripts/katello/locale/ta_IN/katello.js +148 -4
  43. data/app/assets/javascripts/katello/locale/te/katello.js +148 -4
  44. data/app/assets/javascripts/katello/locale/tr/katello.js +148 -4
  45. data/app/assets/javascripts/katello/locale/vi/katello.js +148 -4
  46. data/app/assets/javascripts/katello/locale/vi_VN/katello.js +148 -4
  47. data/app/assets/javascripts/katello/locale/zh/katello.js +148 -4
  48. data/app/assets/javascripts/katello/locale/zh_CN/katello.js +147 -3
  49. data/app/assets/javascripts/katello/locale/zh_TW/katello.js +148 -4
  50. data/app/controllers/katello/api/registry/registry_proxies_controller.rb +12 -14
  51. data/app/controllers/katello/api/v2/content_uploads_controller.rb +2 -1
  52. data/app/controllers/katello/api/v2/errata_controller.rb +16 -0
  53. data/app/controllers/katello/api/v2/flatpak_remote_repositories_controller.rb +20 -3
  54. data/app/controllers/katello/api/v2/flatpak_remotes_controller.rb +7 -3
  55. data/app/controllers/katello/api/v2/generic_content_units_controller.rb +1 -2
  56. data/app/controllers/katello/api/v2/products_controller.rb +18 -0
  57. data/app/controllers/katello/api/v2/repositories_controller.rb +5 -3
  58. data/app/controllers/katello/concerns/api/v2/hosts_bulk_actions_controller_extensions.rb +10 -0
  59. data/app/controllers/katello/concerns/api/v2/registration_commands_controller_extensions.rb +1 -0
  60. data/app/controllers/katello/concerns/filtered_auto_complete_search.rb +17 -0
  61. data/app/lib/actions/helpers/smart_proxy_sync_helper.rb +12 -0
  62. data/app/lib/actions/katello/content_view/add_rolling_repo_clone.rb +4 -4
  63. data/app/lib/actions/katello/content_view/capsule_sync.rb +8 -3
  64. data/app/lib/actions/katello/content_view/publish.rb +39 -12
  65. data/app/lib/actions/katello/flatpak/mirror_remote_repository.rb +2 -1
  66. data/app/lib/actions/katello/repository/capsule_sync.rb +11 -1
  67. data/app/lib/actions/katello/repository/import_upload.rb +2 -3
  68. data/app/lib/actions/katello/repository/sync.rb +2 -1
  69. data/app/lib/actions/katello/repository/upload_files.rb +2 -1
  70. data/app/lib/katello/concerns/bookmark_controller_validator_extensions.rb +1 -1
  71. data/app/lib/katello/resources/cdn.rb +1 -1
  72. data/app/models/katello/concerns/host_managed_extensions.rb +5 -1
  73. data/app/models/katello/concerns/smart_proxy_extensions.rb +46 -8
  74. data/app/models/katello/content_view_environment.rb +1 -1
  75. data/app/models/katello/erratum.rb +26 -1
  76. data/app/models/katello/flatpak_remote.rb +0 -1
  77. data/app/models/katello/flatpak_remote_repository.rb +24 -0
  78. data/app/models/katello/glue/pulp/repos.rb +1 -1
  79. data/app/models/katello/root_repository.rb +1 -0
  80. data/app/presenters/katello/flatpak_remote_mirror_status_presenter.rb +41 -0
  81. data/app/services/katello/repository_type_manager.rb +2 -0
  82. data/app/views/foreman/job_templates/flatpak_install.erb +17 -2
  83. data/app/views/foreman/job_templates/flatpak_login_action.erb +17 -3
  84. data/app/views/foreman/job_templates/install_errata_by_search_query.erb +1 -1
  85. data/app/views/foreman/job_templates/install_errata_by_search_query_-_katello_ansible_default.erb +1 -1
  86. data/app/views/katello/api/v2/flatpak_remote_repositories/base.json.rabl +3 -1
  87. data/app/views/katello/api/v2/flatpak_remotes/base.json.rabl +5 -0
  88. data/app/views/katello/api/v2/flatpak_remotes/permissions.json.rabl +1 -0
  89. data/config/routes/api/v2.rb +1 -1
  90. data/config/routes.rb +3 -0
  91. data/db/migrate/20250613210050_use_big_int_for_erratum_packages_id.rb +11 -0
  92. data/db/seeds.d/108-subcription-bookmarks.rb +2 -0
  93. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/content-hosts/views/content-hosts.html +1 -1
  94. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/errata/views/errata.html +5 -1
  95. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/bastion_katello.pot +4 -5
  96. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn.po +6 -0
  97. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/bn_IN.po +6 -0
  98. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ca.po +6 -0
  99. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/cs_CZ.po +6 -0
  100. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de.po +6 -0
  101. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_AT.po +6 -0
  102. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/de_DE.po +6 -0
  103. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/el.po +6 -0
  104. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_GB.po +6 -0
  105. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/en_US.po +6 -0
  106. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/es.po +6 -0
  107. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/et_EE.po +6 -0
  108. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/fr.po +6 -0
  109. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gl.po +6 -0
  110. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/gu.po +6 -0
  111. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/he_IL.po +6 -0
  112. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/hi.po +6 -0
  113. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/id.po +6 -0
  114. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/it.po +6 -0
  115. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ja.po +6 -0
  116. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ka.po +6 -0
  117. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/kn.po +6 -0
  118. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ko.po +6 -0
  119. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ml_IN.po +6 -0
  120. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/mr.po +6 -0
  121. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/nl_NL.po +6 -0
  122. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/or.po +6 -0
  123. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pa.po +6 -0
  124. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl.po +6 -0
  125. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pl_PL.po +6 -0
  126. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt.po +6 -0
  127. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/pt_BR.po +6 -0
  128. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro.po +6 -0
  129. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ro_RO.po +6 -0
  130. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ru.po +6 -0
  131. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sl.po +6 -0
  132. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/sv_SE.po +6 -0
  133. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta.po +6 -0
  134. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/ta_IN.po +6 -0
  135. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/te.po +6 -0
  136. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/tr.po +6 -0
  137. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi.po +6 -0
  138. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/vi_VN.po +6 -0
  139. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh.po +6 -0
  140. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_CN.po +6 -0
  141. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/i18n/locale/zh_TW.po +6 -0
  142. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/details/repositories/new/new-repository.controller.js +13 -3
  143. data/engines/bastion_katello/app/assets/javascripts/bastion_katello/products/discovery/views/discovery.html +3 -0
  144. data/engines/bastion_katello/lib/bastion_katello/engine.rb +2 -0
  145. data/lib/katello/engine.rb +0 -1
  146. data/lib/katello/permission_creator.rb +1 -1
  147. data/lib/katello/plugin.rb +26 -2
  148. data/lib/katello/version.rb +1 -1
  149. data/lib/katello.rb +0 -1
  150. data/lib/proxy_api/container_gateway.rb +24 -0
  151. data/locale/action_names.rb +186 -0
  152. data/locale/bn/LC_MESSAGES/katello.mo +0 -0
  153. data/locale/bn/katello.po +148 -4
  154. data/locale/bn/katello.po.time_stamp +0 -0
  155. data/locale/bn_IN/LC_MESSAGES/katello.mo +0 -0
  156. data/locale/bn_IN/katello.po +148 -4
  157. data/locale/bn_IN/katello.po.time_stamp +0 -0
  158. data/locale/ca/LC_MESSAGES/katello.mo +0 -0
  159. data/locale/ca/katello.po +148 -4
  160. data/locale/ca/katello.po.time_stamp +0 -0
  161. data/locale/cs/LC_MESSAGES/katello.mo +0 -0
  162. data/locale/cs/katello.po +148 -4
  163. data/locale/cs/katello.po.time_stamp +0 -0
  164. data/locale/cs_CZ/LC_MESSAGES/katello.mo +0 -0
  165. data/locale/cs_CZ/katello.po +148 -4
  166. data/locale/cs_CZ/katello.po.time_stamp +0 -0
  167. data/locale/de/katello.po +147 -3
  168. data/locale/de/katello.po.time_stamp +0 -0
  169. data/locale/de_AT/LC_MESSAGES/katello.mo +0 -0
  170. data/locale/de_AT/katello.po +148 -4
  171. data/locale/de_AT/katello.po.time_stamp +0 -0
  172. data/locale/de_DE/LC_MESSAGES/katello.mo +0 -0
  173. data/locale/de_DE/katello.po +148 -4
  174. data/locale/de_DE/katello.po.time_stamp +0 -0
  175. data/locale/el/LC_MESSAGES/katello.mo +0 -0
  176. data/locale/el/katello.po +148 -4
  177. data/locale/el/katello.po.time_stamp +0 -0
  178. data/locale/en/LC_MESSAGES/katello.mo +0 -0
  179. data/locale/en/katello.po +148 -4
  180. data/locale/en/katello.po.time_stamp +0 -0
  181. data/locale/en_GB/LC_MESSAGES/katello.mo +0 -0
  182. data/locale/en_GB/katello.po +148 -4
  183. data/locale/en_GB/katello.po.time_stamp +0 -0
  184. data/locale/en_US/LC_MESSAGES/katello.mo +0 -0
  185. data/locale/en_US/katello.po +148 -4
  186. data/locale/en_US/katello.po.time_stamp +0 -0
  187. data/locale/es/katello.po +147 -3
  188. data/locale/es/katello.po.time_stamp +0 -0
  189. data/locale/et_EE/LC_MESSAGES/katello.mo +0 -0
  190. data/locale/et_EE/katello.po +148 -4
  191. data/locale/et_EE/katello.po.time_stamp +0 -0
  192. data/locale/fr/katello.po +147 -3
  193. data/locale/fr/katello.po.time_stamp +0 -0
  194. data/locale/gl/LC_MESSAGES/katello.mo +0 -0
  195. data/locale/gl/katello.po +148 -4
  196. data/locale/gl/katello.po.time_stamp +0 -0
  197. data/locale/gu/LC_MESSAGES/katello.mo +0 -0
  198. data/locale/gu/katello.po +148 -4
  199. data/locale/gu/katello.po.time_stamp +0 -0
  200. data/locale/he_IL/LC_MESSAGES/katello.mo +0 -0
  201. data/locale/he_IL/katello.po +148 -4
  202. data/locale/he_IL/katello.po.time_stamp +0 -0
  203. data/locale/hi/LC_MESSAGES/katello.mo +0 -0
  204. data/locale/hi/katello.po +148 -4
  205. data/locale/hi/katello.po.time_stamp +0 -0
  206. data/locale/id/LC_MESSAGES/katello.mo +0 -0
  207. data/locale/id/katello.po +148 -4
  208. data/locale/id/katello.po.time_stamp +0 -0
  209. data/locale/it/LC_MESSAGES/katello.mo +0 -0
  210. data/locale/it/katello.po +148 -4
  211. data/locale/it/katello.po.time_stamp +0 -0
  212. data/locale/ja/katello.po +147 -3
  213. data/locale/ja/katello.po.time_stamp +0 -0
  214. data/locale/ka/katello.po +147 -3
  215. data/locale/ka/katello.po.time_stamp +0 -0
  216. data/locale/katello.pot +703 -428
  217. data/locale/kn/LC_MESSAGES/katello.mo +0 -0
  218. data/locale/kn/katello.po +148 -4
  219. data/locale/kn/katello.po.time_stamp +0 -0
  220. data/locale/ko/katello.po +147 -3
  221. data/locale/ko/katello.po.time_stamp +0 -0
  222. data/locale/ml_IN/LC_MESSAGES/katello.mo +0 -0
  223. data/locale/ml_IN/katello.po +148 -4
  224. data/locale/ml_IN/katello.po.time_stamp +0 -0
  225. data/locale/mr/LC_MESSAGES/katello.mo +0 -0
  226. data/locale/mr/katello.po +148 -4
  227. data/locale/mr/katello.po.time_stamp +0 -0
  228. data/locale/nl_NL/LC_MESSAGES/katello.mo +0 -0
  229. data/locale/nl_NL/katello.po +148 -4
  230. data/locale/nl_NL/katello.po.time_stamp +0 -0
  231. data/locale/or/LC_MESSAGES/katello.mo +0 -0
  232. data/locale/or/katello.po +148 -4
  233. data/locale/or/katello.po.time_stamp +0 -0
  234. data/locale/pa/LC_MESSAGES/katello.mo +0 -0
  235. data/locale/pa/katello.po +148 -4
  236. data/locale/pa/katello.po.time_stamp +0 -0
  237. data/locale/pl/LC_MESSAGES/katello.mo +0 -0
  238. data/locale/pl/katello.po +148 -4
  239. data/locale/pl/katello.po.time_stamp +0 -0
  240. data/locale/pl_PL/LC_MESSAGES/katello.mo +0 -0
  241. data/locale/pl_PL/katello.po +148 -4
  242. data/locale/pl_PL/katello.po.time_stamp +0 -0
  243. data/locale/pt/LC_MESSAGES/katello.mo +0 -0
  244. data/locale/pt/katello.po +148 -4
  245. data/locale/pt/katello.po.time_stamp +0 -0
  246. data/locale/pt_BR/katello.po +147 -3
  247. data/locale/pt_BR/katello.po.time_stamp +0 -0
  248. data/locale/ro/LC_MESSAGES/katello.mo +0 -0
  249. data/locale/ro/katello.po +148 -4
  250. data/locale/ro/katello.po.time_stamp +0 -0
  251. data/locale/ro_RO/LC_MESSAGES/katello.mo +0 -0
  252. data/locale/ro_RO/katello.po +148 -4
  253. data/locale/ro_RO/katello.po.time_stamp +0 -0
  254. data/locale/ru/LC_MESSAGES/katello.mo +0 -0
  255. data/locale/ru/katello.po +148 -4
  256. data/locale/ru/katello.po.time_stamp +0 -0
  257. data/locale/sl/LC_MESSAGES/katello.mo +0 -0
  258. data/locale/sl/katello.po +148 -4
  259. data/locale/sl/katello.po.time_stamp +0 -0
  260. data/locale/sv_SE/LC_MESSAGES/katello.mo +0 -0
  261. data/locale/sv_SE/katello.po +148 -4
  262. data/locale/sv_SE/katello.po.time_stamp +0 -0
  263. data/locale/ta/LC_MESSAGES/katello.mo +0 -0
  264. data/locale/ta/katello.po +148 -4
  265. data/locale/ta/katello.po.time_stamp +0 -0
  266. data/locale/ta_IN/LC_MESSAGES/katello.mo +0 -0
  267. data/locale/ta_IN/katello.po +148 -4
  268. data/locale/ta_IN/katello.po.time_stamp +0 -0
  269. data/locale/te/LC_MESSAGES/katello.mo +0 -0
  270. data/locale/te/katello.po +148 -4
  271. data/locale/te/katello.po.time_stamp +0 -0
  272. data/locale/tr/LC_MESSAGES/katello.mo +0 -0
  273. data/locale/tr/katello.po +148 -4
  274. data/locale/tr/katello.po.time_stamp +0 -0
  275. data/locale/vi/LC_MESSAGES/katello.mo +0 -0
  276. data/locale/vi/katello.po +148 -4
  277. data/locale/vi/katello.po.time_stamp +0 -0
  278. data/locale/vi_VN/LC_MESSAGES/katello.mo +0 -0
  279. data/locale/vi_VN/katello.po +148 -4
  280. data/locale/vi_VN/katello.po.time_stamp +0 -0
  281. data/locale/zh/LC_MESSAGES/katello.mo +0 -0
  282. data/locale/zh/katello.po +148 -4
  283. data/locale/zh/katello.po.time_stamp +0 -0
  284. data/locale/zh_CN/katello.po +147 -3
  285. data/locale/zh_CN/katello.po.time_stamp +0 -0
  286. data/locale/zh_TW/LC_MESSAGES/katello.mo +0 -0
  287. data/locale/zh_TW/katello.po +148 -4
  288. data/locale/zh_TW/katello.po.time_stamp +0 -0
  289. data/package.json +0 -1
  290. data/webpack/components/Content/Details/__tests__/__snapshots__/ContentDetails.test.js.snap +2 -2
  291. data/webpack/components/OptionTooltip/OptionTooltip.scss +1 -1
  292. data/webpack/components/extensions/HostDetails/ActionsBar/index.js +5 -5
  293. data/webpack/components/extensions/HostDetails/Tabs/PackagesTab/PackagesTab.js +6 -1
  294. data/webpack/components/extensions/Hosts/ActionsBar/index.js +11 -0
  295. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/BulkChangeHostCVModal.js +1 -1
  296. data/webpack/components/extensions/Hosts/BulkActions/BulkChangeHostCVModal/actions.js +1 -2
  297. data/webpack/components/extensions/Hosts/BulkActions/BulkErrataWizard/04_ReviewFooter.js +1 -1
  298. data/webpack/components/extensions/Hosts/BulkActions/BulkPackagesWizard/04_ReviewFooter.js +1 -1
  299. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/01_BulkRepositorySetsTable.js +371 -0
  300. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/03_Review.js +79 -0
  301. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/03_ReviewFooter.js +73 -0
  302. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/BulkRepositorySetsWizard.js +170 -0
  303. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/actions.js +23 -0
  304. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/helpers.js +28 -0
  305. data/webpack/components/extensions/Hosts/BulkActions/BulkRepositorySetsWizard/index.js +23 -0
  306. data/webpack/components/extensions/RegistrationCommands/fields/SetupContainerRegistryCerts.js +39 -0
  307. data/webpack/components/extensions/RegistrationCommands/index.js +7 -0
  308. data/webpack/components/pf3Table/formatters/ellipsisCellFormatter.js +3 -2
  309. data/webpack/containers/Application/config.js +11 -0
  310. data/webpack/global_index.js +3 -1
  311. data/webpack/scenes/ContentViews/Details/Filters/Rules/Package/AddEditPackageRuleModal.js +2 -0
  312. data/webpack/scenes/ContentViews/Details/Versions/ContentViewVersions.js +9 -6
  313. data/webpack/scenes/ContentViews/Table/ContentViewsTable.js +4 -0
  314. data/webpack/scenes/FlatpakRemotes/CreateEdit/CreateFlatpakRemoteModal.js +30 -0
  315. data/webpack/scenes/FlatpakRemotes/CreateEdit/EditFlatpakRemotesModal.js +38 -0
  316. data/webpack/scenes/FlatpakRemotes/CreateEdit/FlatpakRemoteform.js +218 -0
  317. data/webpack/scenes/FlatpakRemotes/CreateEdit/__tests__/flatpakRemoteform.test.js +117 -0
  318. data/webpack/scenes/FlatpakRemotes/Delete/DeleteFlatpakModal.js +59 -0
  319. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetailActions.js +87 -0
  320. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetailReducers.js +23 -0
  321. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetailSelectors.js +16 -0
  322. data/webpack/scenes/FlatpakRemotes/Details/FlatpakRemoteDetails.js +167 -0
  323. data/webpack/scenes/FlatpakRemotes/Details/Mirror/MirrorRepositoryModal.js +135 -0
  324. data/webpack/scenes/FlatpakRemotes/Details/Mirror/__tests__/mirrorRepositoryModal.test.js +167 -0
  325. data/webpack/scenes/FlatpakRemotes/Details/RemoteRepositories/RemoteRepositoriesTable.js +171 -0
  326. data/webpack/scenes/FlatpakRemotes/Details/__tests__/flatpakRemoteDetails.fixtures.json +58 -0
  327. data/webpack/scenes/FlatpakRemotes/Details/__tests__/flatpakRemoteDetails.test.js +90 -0
  328. data/webpack/scenes/FlatpakRemotes/Details/index.js +4 -0
  329. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesActions.js +26 -0
  330. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesConstants.js +15 -0
  331. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesPage.js +202 -0
  332. data/webpack/scenes/FlatpakRemotes/FlatpakRemotesSelectors.js +24 -0
  333. data/webpack/scenes/FlatpakRemotes/__tests__/flatpakRemotesList.fixtures.json +55 -0
  334. data/webpack/scenes/FlatpakRemotes/__tests__/flatpakRemotesPage.test.js +74 -0
  335. data/webpack/scenes/FlatpakRemotes/index.js +4 -0
  336. data/webpack/scenes/Hosts/ChangeContentSource/components/Hosts.js +2 -2
  337. data/webpack/scenes/RedHatRepositories/components/RecommendedRepositorySetsToggler.scss +1 -1
  338. data/webpack/scenes/RedHatRepositories/index.scss +1 -1
  339. data/webpack/scenes/Settings/SettingsSelectors.js +3 -3
  340. data/webpack/scenes/Settings/index.js +1 -3
  341. data/webpack/scenes/Subscriptions/Details/__tests__/__snapshots__/SubscriptionDetails.test.js.snap +2 -2
  342. data/webpack/scenes/Subscriptions/SubscriptionsPage.scss +1 -1
  343. data/webpack/scenes/Subscriptions/__tests__/SubscriptionsReducer.test.js +0 -21
  344. data/webpack/scenes/Subscriptions/__tests__/__snapshots__/SubscriptionsReducer.test.js.snap +0 -44
  345. data/webpack/scenes/Subscriptions/index.js +1 -3
  346. metadata +87 -21
  347. data/webpack/scenes/Settings/SettingsConstants.js +0 -2
  348. data/webpack/scenes/Settings/SettingsReducer.js +0 -19
@@ -0,0 +1,117 @@
1
+ import React from 'react';
2
+ import { renderWithRedux, patientlyWaitFor, fireEvent } from 'react-testing-lib-wrapper';
3
+ import { nockInstance, assertNockRequest } from '../../../../test-utils/nockWrapper';
4
+ import FlatpakRemotesForm from '../FlatpakRemoteform';
5
+ import api from '../../../../services/api';
6
+
7
+ const mockFn = jest.fn();
8
+ delete window.location;
9
+ window.location = { assign: mockFn };
10
+
11
+ afterEach(() => {
12
+ mockFn.mockClear();
13
+ });
14
+
15
+ const createFlatpakPath = api.getApiUrl('/flatpak_remotes');
16
+ const updateFlatpakPath = id => api.getApiUrl(`/flatpak_remotes/${id}`);
17
+
18
+ const mockRemoteData = {
19
+ id: 1,
20
+ name: 'Test Remote',
21
+ url: 'https://example.com',
22
+ username: 'testuser',
23
+ upstream_password_exists: true,
24
+ };
25
+
26
+ test('Renders FlatpakRemotesForm fields correctly', () => {
27
+ const { getByText } = renderWithRedux(<FlatpakRemotesForm setModalOpen={mockFn} />);
28
+ expect(getByText('Name')).toBeInTheDocument();
29
+ expect(getByText('URL')).toBeInTheDocument();
30
+ expect(getByText('Username')).toBeInTheDocument();
31
+ expect(getByText('Password')).toBeInTheDocument();
32
+ });
33
+
34
+ test('Can save Flatpak remote from form', async (done) => {
35
+ const createScope = nockInstance
36
+ .post(createFlatpakPath, {
37
+ organization_id: 1,
38
+ name: 'New Remote',
39
+ url: 'https://example.com',
40
+ username: 'testuser',
41
+ token: 'password123',
42
+ })
43
+ .reply(201, { id: 2 });
44
+
45
+ const { getByLabelText, getByText } = renderWithRedux(<FlatpakRemotesForm
46
+ setModalOpen={mockFn}
47
+ />);
48
+ fireEvent.change(getByLabelText('input_name'), { target: { value: 'New Remote' } });
49
+ fireEvent.change(getByLabelText('input_url'), { target: { value: 'https://example.com' } });
50
+ fireEvent.change(getByLabelText('input_username'), { target: { value: 'testuser' } });
51
+ fireEvent.change(getByLabelText('input_password'), { target: { value: 'password123' } });
52
+
53
+ getByText('Create').click();
54
+
55
+ await patientlyWaitFor(() => {
56
+ expect(window.location.assign).toHaveBeenCalledWith('/flatpak_remotes/2');
57
+ });
58
+
59
+ assertNockRequest(createScope);
60
+ done();
61
+ });
62
+
63
+ test('Can update Flatpak remote from form', async (done) => {
64
+ const updateScope = nockInstance
65
+ .put(updateFlatpakPath(mockRemoteData.id), {
66
+ name: 'Updated Remote',
67
+ url: 'https://updated.com',
68
+ username: 'updateduser',
69
+ })
70
+ .reply(200);
71
+
72
+ const { getByLabelText, getByText } = renderWithRedux(<FlatpakRemotesForm
73
+ setModalOpen={mockFn}
74
+ remoteData={mockRemoteData}
75
+ />);
76
+
77
+ fireEvent.change(getByLabelText('input_name'), { target: { value: 'Updated Remote' } });
78
+ fireEvent.change(getByLabelText('input_url'), { target: { value: 'https://updated.com' } });
79
+ fireEvent.change(getByLabelText('input_username'), { target: { value: 'updateduser' } });
80
+
81
+ getByText('Update').click();
82
+
83
+ await patientlyWaitFor(() => {
84
+ expect(window.location.assign).toHaveBeenCalledWith('/flatpak_remotes/1');
85
+ });
86
+
87
+ assertNockRequest(updateScope);
88
+ done();
89
+ });
90
+
91
+ test('Can update Flatpak remote password from placeholder', async (done) => {
92
+ const updateScope = nockInstance
93
+ .put(updateFlatpakPath(mockRemoteData.id), {
94
+ name: mockRemoteData.name,
95
+ url: mockRemoteData.url,
96
+ username: mockRemoteData.username,
97
+ token: 'newpassword123',
98
+ })
99
+ .reply(200);
100
+
101
+ const { getByLabelText, getByText } = renderWithRedux(<FlatpakRemotesForm
102
+ setModalOpen={mockFn}
103
+ remoteData={mockRemoteData}
104
+ />);
105
+
106
+ // Simulate changing the password from the placeholder to a new value
107
+ fireEvent.change(getByLabelText('input_password'), { target: { value: 'newpassword123' } });
108
+
109
+ getByText('Update').click();
110
+
111
+ await patientlyWaitFor(() => {
112
+ expect(window.location.assign).toHaveBeenCalledWith('/flatpak_remotes/1');
113
+ });
114
+
115
+ assertNockRequest(updateScope);
116
+ done();
117
+ });
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { useDispatch } from 'react-redux';
3
+ import PropTypes from 'prop-types';
4
+ import { Modal, ModalVariant, Button, Icon, Title, Flex } from '@patternfly/react-core';
5
+ import { ExclamationTriangleIcon } from '@patternfly/react-icons';
6
+ import { translate as __ } from 'foremanReact/common/I18n';
7
+ import { deleteFlatpakRemote } from '../Details/FlatpakRemoteDetailActions';
8
+
9
+ const DeleteFlatpakModal = ({ isModalOpen, handleModalToggle, remoteId }) => {
10
+ const dispatch = useDispatch();
11
+
12
+ const handleDelete = () => {
13
+ dispatch(deleteFlatpakRemote(remoteId, () => window.location.replace('/flatpak_remotes')));
14
+ handleModalToggle();
15
+ };
16
+
17
+ return (
18
+ <Modal
19
+ ouiaId="flatpak-delete-modal"
20
+ variant={ModalVariant.small}
21
+ title={[
22
+ <Flex key="delete-modal-header">
23
+ <Icon status="warning" key="exclamation-triangle">
24
+ <ExclamationTriangleIcon />
25
+ </Icon>
26
+ <Title ouiaId="flatpak-delete-header" key="delete-flatpak-title" headingLevel="h5" size="2xl">
27
+ {__('Delete Flatpak remote?')}
28
+ </Title>
29
+ </Flex>,
30
+ ]}
31
+ isOpen={isModalOpen}
32
+ onClose={handleModalToggle}
33
+ actions={[
34
+ <Button ouiaId="delete-button" key="delete" variant="danger" onClick={handleDelete}>
35
+ {__('Delete')}
36
+ </Button>,
37
+ <Button ouiaId="cancel-button" key="cancel" variant="link" onClick={handleModalToggle}>
38
+ {__('Cancel')}
39
+ </Button>,
40
+ ]}
41
+ >
42
+ {__('This Flatpak remote will be deleted. Repositories mirrored from this remote will remain available and functional for use')}
43
+ </Modal>
44
+ );
45
+ };
46
+
47
+ DeleteFlatpakModal.propTypes = {
48
+ isModalOpen: PropTypes.bool,
49
+ handleModalToggle: PropTypes.func,
50
+ remoteId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
51
+ };
52
+
53
+ DeleteFlatpakModal.defaultProps = {
54
+ isModalOpen: false,
55
+ handleModalToggle: () => {},
56
+ remoteId: undefined,
57
+ };
58
+
59
+ export default DeleteFlatpakModal;
@@ -0,0 +1,87 @@
1
+ import { API_OPERATIONS, APIActions, get, post, put } from 'foremanReact/redux/API';
2
+ import { translate as __ } from 'foremanReact/common/I18n';
3
+ import { flatpakRemoteDetailsKey,
4
+ flatpakRemoteRepositoriesKey,
5
+ UPDATE_FLATPAK_REMOTE,
6
+ UPDATE_FLATPAK_REMOTE_SUCCESS,
7
+ UPDATE_FLATPAK_REMOTE_FAILURE,
8
+ DELETE_FLATPAK_REMOTE_KEY,
9
+ SCAN_FLATPAK_REMOTE_KEY } from '../FlatpakRemotesConstants';
10
+ import api, { orgId } from '../../../services/api';
11
+ import { getResponseErrorMsgs } from '../../../utils/helpers';
12
+ import { renderTaskStartedToast } from '../../Tasks/helpers';
13
+
14
+ const getFlatpakRemoteDetails = (id, extraParams = {}) => get({
15
+ type: API_OPERATIONS.GET,
16
+ key: flatpakRemoteDetailsKey(id),
17
+ params: { organization_id: orgId(), include_permissions: true, ...extraParams },
18
+ url: api.getApiUrl(`/flatpak_remotes/${id}`),
19
+ });
20
+
21
+ export const getRemoteRepositories = frId => () =>
22
+ get({
23
+ type: API_OPERATIONS.GET,
24
+ key: flatpakRemoteRepositoriesKey(frId),
25
+ url: api.getApiUrl(`/flatpak_remotes/${frId}/flatpak_remote_repositories`),
26
+ params: { organization_id: orgId() },
27
+ });
28
+
29
+ export const updateFlatpakRemote = (frId, params, handleSuccess, handleError) => put({
30
+ type: API_OPERATIONS.PUT,
31
+ key: flatpakRemoteDetailsKey(frId),
32
+ url: api.getApiUrl(`/flatpak_remotes/${frId}`),
33
+ handleSuccess,
34
+ handleError,
35
+ params,
36
+ successToast: () => __('Flatpak remote updated'),
37
+ errorToast: error => getResponseErrorMsgs(error.response),
38
+ updateData: (_prevState, respState) => respState,
39
+ actionTypes: {
40
+ REQUEST: UPDATE_FLATPAK_REMOTE,
41
+ SUCCESS: UPDATE_FLATPAK_REMOTE_SUCCESS,
42
+ FAILURE: UPDATE_FLATPAK_REMOTE_FAILURE,
43
+ },
44
+ });
45
+
46
+ export const mirrorFlatpakRepository = (
47
+ flatpakRepoId,
48
+ productName,
49
+ handleSuccess,
50
+ handleError,
51
+ ) =>
52
+ post({
53
+ type: API_OPERATIONS.POST,
54
+ key: flatpakRemoteRepositoriesKey(flatpakRepoId),
55
+ url: api.getApiUrl(`/flatpak_remote_repositories/${flatpakRepoId}/mirror`),
56
+ params: { product_name: productName, organization_id: orgId() },
57
+ handleSuccess: (response) => {
58
+ if (handleSuccess) handleSuccess(response);
59
+ return renderTaskStartedToast(response.data);
60
+ },
61
+ handleError,
62
+ errorToast: error => getResponseErrorMsgs(error.response),
63
+ });
64
+
65
+ export const deleteFlatpakRemote = (id, handleSuccess) => APIActions.delete({
66
+ type: API_OPERATIONS.DELETE,
67
+ key: DELETE_FLATPAK_REMOTE_KEY,
68
+ url: api.getApiUrl(`/flatpak_remotes/${id}`),
69
+ handleSuccess,
70
+ successToast: () => __('Flatpak remote deleted'),
71
+ errorToast: error => __('Flatpak remote could not be deleted: ') + getResponseErrorMsgs(error.response),
72
+ });
73
+
74
+ export const scanFlatpakRemote = (id, handleSuccess, handleError) => post({
75
+ type: API_OPERATIONS.POST,
76
+ key: SCAN_FLATPAK_REMOTE_KEY,
77
+ url: api.getApiUrl(`/flatpak_remotes/${id}/scan`),
78
+ handleSuccess: (response) => {
79
+ if (handleSuccess) handleSuccess(response);
80
+ return renderTaskStartedToast(response.data);
81
+ },
82
+ handleError,
83
+ errorToast: error => __('Flatpak remote scan could not be started: ') +
84
+ getResponseErrorMsgs(error.response),
85
+ });
86
+
87
+ export default getFlatpakRemoteDetails;
@@ -0,0 +1,23 @@
1
+ import Immutable from 'seamless-immutable';
2
+ import {
3
+ UPDATE_FLATPAK_REMOTE,
4
+ UPDATE_FLATPAK_REMOTE_FAILURE,
5
+ UPDATE_FLATPAK_REMOTE_SUCCESS,
6
+ } from '../FlatpakRemotesConstants';
7
+
8
+ const initialState = Immutable({
9
+ updating: false,
10
+ });
11
+
12
+ export default (state = initialState, action) => {
13
+ switch (action.type) {
14
+ case UPDATE_FLATPAK_REMOTE:
15
+ return state.set('updating', true);
16
+ case UPDATE_FLATPAK_REMOTE_SUCCESS:
17
+ return state.merge({ updating: false });
18
+ case UPDATE_FLATPAK_REMOTE_FAILURE:
19
+ return state.set('updating', false);
20
+ default:
21
+ return state;
22
+ }
23
+ };
@@ -0,0 +1,16 @@
1
+ import { STATUS } from 'foremanReact/constants';
2
+ import {
3
+ selectAPIError,
4
+ selectAPIResponse,
5
+ selectAPIStatus,
6
+ } from 'foremanReact/redux/API/APISelectors';
7
+ import { flatpakRemoteDetailsKey } from '../FlatpakRemotesConstants';
8
+
9
+ export const selectFlatpakRemoteDetails = (state, id) =>
10
+ selectAPIResponse(state, flatpakRemoteDetailsKey(id)) || {};
11
+
12
+ export const selectFlatpakRemoteDetailStatus =
13
+ (state, id) => selectAPIStatus(state, flatpakRemoteDetailsKey(id)) || STATUS.PENDING;
14
+
15
+ export const selectFlatpakRemoteDetailError =
16
+ (state, id) => selectAPIError(state, flatpakRemoteDetailsKey(id));
@@ -0,0 +1,167 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { useSelector, useDispatch, shallowEqual } from 'react-redux';
3
+ import { useParams } from 'react-router-dom';
4
+ import {
5
+ Breadcrumb,
6
+ BreadcrumbItem,
7
+ Button,
8
+ Title,
9
+ Grid,
10
+ GridItem,
11
+ Text,
12
+ TextContent,
13
+ TextList,
14
+ TextListVariants,
15
+ Flex,
16
+ FlexItem,
17
+ } from '@patternfly/react-core';
18
+ import {
19
+ Dropdown,
20
+ DropdownItem,
21
+ KebabToggle,
22
+ DropdownPosition,
23
+ } from '@patternfly/react-core/deprecated';
24
+ import { translate as __ } from 'foremanReact/common/I18n';
25
+ import { selectFlatpakRemoteDetails } from './FlatpakRemoteDetailSelectors';
26
+ import getFlatpakRemoteDetails, {
27
+ scanFlatpakRemote,
28
+ updateFlatpakRemote,
29
+ } from './FlatpakRemoteDetailActions';
30
+ import ActionableDetail from '../../../components/ActionableDetail';
31
+ import RemoteRepositoriesTable from './RemoteRepositories/RemoteRepositoriesTable';
32
+ import EditFlatpakRemotesModal from '../CreateEdit/EditFlatpakRemotesModal';
33
+ import DeleteFlatpakModal from '../Delete/DeleteFlatpakModal';
34
+
35
+ export default function FlatpakRemoteDetails() {
36
+ const { id } = useParams();
37
+ const frId = Number(id);
38
+ const [dropDownOpen, setDropdownOpen] = useState(false);
39
+ const [isScanning, setIsScanning] = useState(false);
40
+ const [isEditing, setIsEditing] = useState(false);
41
+ const dispatch = useDispatch();
42
+
43
+ const [currentAttribute, setCurrentAttribute] = useState(null);
44
+ const [isDeleteModalOpen, setDeleteModalOpen] = useState(false);
45
+
46
+ useEffect(() => {
47
+ dispatch(getFlatpakRemoteDetails(frId));
48
+ }, [dispatch, frId]);
49
+
50
+ const frDetails = useSelector(state =>
51
+ selectFlatpakRemoteDetails(state, frId), shallowEqual) || {};
52
+ const name = frDetails.name || '';
53
+ const url = frDetails.url || '';
54
+
55
+ const {
56
+ can_edit: canEdit = false,
57
+ can_delete: canDelete = false,
58
+ can_mirror: canMirror = false,
59
+ } = frDetails || {};
60
+
61
+ const onEdit = (val, attribute) => {
62
+ if (val === frDetails[attribute]) return;
63
+ dispatch(updateFlatpakRemote(frId, { [attribute]: val }));
64
+ };
65
+
66
+ const dropDownItems = [];
67
+ if (canEdit) {
68
+ dropDownItems.push(<DropdownItem key="edit" ouiaId="fr-edit" onClick={() => { setIsEditing(true); }}> {__('Edit')}</DropdownItem>);
69
+ }
70
+ if (canDelete) {
71
+ dropDownItems.push(<DropdownItem key="delete" ouiaId="cv-delete" onClick={() => setDeleteModalOpen(true)}>{__('Delete')}</DropdownItem>);
72
+ }
73
+
74
+ return (
75
+ <Grid hasGutter span={12} style={{ padding: '24px' }}>
76
+ <GridItem span={12}>
77
+ <Breadcrumb ouiaId="flatpak-remote-breadcrumb">
78
+ <BreadcrumbItem to="/flatpak_remotes">Flatpak remotes</BreadcrumbItem>
79
+ <BreadcrumbItem isActive>{name}</BreadcrumbItem>
80
+ </Breadcrumb>
81
+ </GridItem>
82
+
83
+ <GridItem span={12}>
84
+ <Flex>
85
+ <FlexItem>
86
+ <Title headingLevel="h1" size="2xl" ouiaId="flatpak-remote-title">{name}</Title>
87
+ </FlexItem>
88
+ <FlexItem align={{ default: 'alignRight' }}>
89
+ {canEdit &&
90
+ <Button
91
+ ouiaId="fr-details-scan-button"
92
+ style={{ marginLeft: 'auto' }}
93
+ onClick={() => {
94
+ setIsScanning(true);
95
+ dispatch(scanFlatpakRemote(
96
+ frId,
97
+ () => { setIsScanning(false); },
98
+ () => setIsScanning(false),
99
+ ));
100
+ }
101
+ }
102
+ variant="primary"
103
+ aria-label="scan_flatpak_remote"
104
+ isLoading={isScanning}
105
+ isDisabled={isScanning}
106
+ >
107
+ {__('Scan')}
108
+ </Button>
109
+ }
110
+ {dropDownItems.length > 0 &&
111
+ <Dropdown
112
+ position={DropdownPosition.right}
113
+ ouiaId="fr-details-actions"
114
+ style={{ marginLeft: 'auto' }}
115
+ toggle={<KebabToggle onToggle={(_event, val) => setDropdownOpen(val)} id="toggle-dropdown" />}
116
+ isOpen={dropDownOpen}
117
+ isPlain
118
+ dropdownItems={dropDownItems}
119
+ />
120
+ }
121
+ </FlexItem>
122
+ </Flex>
123
+ </GridItem>
124
+
125
+ <GridItem span={12}>
126
+ <TextContent>
127
+ <TextList component={TextListVariants.dl}>
128
+ <ActionableDetail
129
+ key={url}
130
+ label={__('URL:')}
131
+ attribute="url"
132
+ onEdit={onEdit}
133
+ disabled={!canEdit}
134
+ value={url}
135
+ {...{ currentAttribute, setCurrentAttribute }}
136
+ />
137
+ </TextList>
138
+ </TextContent>
139
+ </GridItem>
140
+
141
+ <GridItem span={12}>
142
+ <TextContent>
143
+ <Title headingLevel="h2" size="xl" ouiaId="flatpak-remote-subtitle">Remote repositories</Title>
144
+ <Text component="p" ouiaId="flatpak-remote-description" style={{ color: 'gray' }}>
145
+ This is a list of scanned flatpaks.
146
+ Mirroring a scanned flatpak creates a repository in the product of your choice.
147
+ Sync the repository after mirroring it from this remote to distribute its content.
148
+ </Text>
149
+ </TextContent>
150
+ </GridItem>
151
+
152
+ <GridItem span={12}>
153
+ <RemoteRepositoriesTable frId={frId} canMirror={canMirror} />
154
+ </GridItem>
155
+ { isEditing &&
156
+ <EditFlatpakRemotesModal show={isEditing} remoteData={frDetails} setIsOpen={setIsEditing} />
157
+ }
158
+ { isDeleteModalOpen &&
159
+ <DeleteFlatpakModal
160
+ isModalOpen={isDeleteModalOpen}
161
+ handleModalToggle={() => setDeleteModalOpen(!isDeleteModalOpen)}
162
+ remoteId={frId}
163
+ />
164
+ }
165
+ </Grid>
166
+ );
167
+ }
@@ -0,0 +1,135 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { useDispatch } from 'react-redux';
4
+ import { translate as __ } from 'foremanReact/common/I18n';
5
+ import {
6
+ Modal,
7
+ ModalVariant,
8
+ Form,
9
+ FormGroup,
10
+ ActionGroup,
11
+ Button,
12
+ TextContent,
13
+ Text,
14
+ TextVariants,
15
+ } from '@patternfly/react-core';
16
+ import { orgId } from '../../../../services/api';
17
+ import SearchText from '../../../../components/Search/SearchText';
18
+ import { getRemoteRepositories, mirrorFlatpakRepository } from '../FlatpakRemoteDetailActions';
19
+
20
+ const MirrorRepositoryModal = ({
21
+ frId,
22
+ closeModal,
23
+ repo,
24
+ onMirrorSuccess,
25
+ }) => {
26
+ const dispatch = useDispatch();
27
+
28
+ const autoCompleteEndpoint = '/katello/api/v2/products/auto_complete_name';
29
+ const searchParams = term => ({
30
+ organization_id: orgId(),
31
+ enabled: true,
32
+ custom: true,
33
+ page: 1,
34
+ per_page: 5,
35
+ term,
36
+ });
37
+
38
+ const [productName, setProductName] = useState('');
39
+ const [loading, setLoading] = useState(false);
40
+
41
+ const handleSearchChange = (rawValue) => {
42
+ setProductName(rawValue.trim());
43
+ };
44
+
45
+ const handleMirror = (e) => {
46
+ e.preventDefault();
47
+ if (!productName || loading) return;
48
+ setLoading(true);
49
+ dispatch(mirrorFlatpakRepository(
50
+ repo.id,
51
+ productName,
52
+ () => {
53
+ dispatch(getRemoteRepositories(frId)());
54
+ setLoading(false);
55
+ if (onMirrorSuccess) onMirrorSuccess();
56
+ closeModal();
57
+ },
58
+ () => {
59
+ setLoading(false);
60
+ closeModal();
61
+ },
62
+ ));
63
+ };
64
+
65
+ return (
66
+ <Modal
67
+ ouiaId="mirror-repo-modal"
68
+ title={__('Mirror Repository')}
69
+ variant={ModalVariant.medium}
70
+ isOpen
71
+ onClose={closeModal}
72
+ appendTo={document.body}
73
+ >
74
+ <TextContent>
75
+ <Text component={TextVariants.p} ouiaId="mirror-text-info">
76
+ {__('Mirroring will import the remote flatpak repository')} <strong>{repo.name}</strong>{' '}
77
+ {__('into a product. Details from the flatpak remote will automatically populate the repository fields. The repository will be available for syncing once it has been mirrored into a product.')}
78
+ </Text>
79
+ <Text component={TextVariants.p} ouiaId="mirror-select-product">
80
+ {__('Select a product to mirror the repository into')}
81
+ </Text>
82
+ </TextContent>
83
+ <Form onSubmit={handleMirror}>
84
+ <FormGroup label={__('Product')} isRequired fieldId="mirror-product">
85
+ <SearchText
86
+ value={productName}
87
+ data={{
88
+ autocomplete: {
89
+ url: autoCompleteEndpoint,
90
+ apiParams: searchParams,
91
+ },
92
+ }}
93
+ onSearchChange={handleSearchChange}
94
+ aria-label="product-search"
95
+ />
96
+ </FormGroup>
97
+
98
+ <ActionGroup>
99
+ <Button
100
+ ouiaId="confirm-mirror-btn"
101
+ variant="primary"
102
+ type="submit"
103
+ isLoading={loading}
104
+ isDisabled={!productName || loading}
105
+ >
106
+ {__('Mirror')}
107
+ </Button>
108
+ <Button
109
+ ouiaId="cancel-mirror-btn"
110
+ variant="link"
111
+ onClick={closeModal}
112
+ >
113
+ {__('Cancel')}
114
+ </Button>
115
+ </ActionGroup>
116
+ </Form>
117
+ </Modal>
118
+ );
119
+ };
120
+
121
+ MirrorRepositoryModal.propTypes = {
122
+ frId: PropTypes.number.isRequired,
123
+ closeModal: PropTypes.func.isRequired,
124
+ repo: PropTypes.shape({
125
+ id: PropTypes.number.isRequired,
126
+ name: PropTypes.string.isRequired,
127
+ }).isRequired,
128
+ onMirrorSuccess: PropTypes.func,
129
+ };
130
+
131
+ MirrorRepositoryModal.defaultProps = {
132
+ onMirrorSuccess: null,
133
+ };
134
+
135
+ export default MirrorRepositoryModal;