Hello community, here is the log from the commit of package yast2-packager for openSUSE:Factory checked in at 2020-03-22 14:16:13 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/yast2-packager (Old) and /work/SRC/openSUSE:Factory/.yast2-packager.new.3160 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "yast2-packager" Sun Mar 22 14:16:13 2020 rev:390 rq:786195 version:4.2.59 Changes: -------- --- /work/SRC/openSUSE:Factory/yast2-packager/yast2-packager.changes 2020-03-14 09:54:39.823080334 +0100 +++ /work/SRC/openSUSE:Factory/.yast2-packager.new.3160/yast2-packager.changes 2020-03-22 14:16:16.666022457 +0100 @@ -1,0 +2,6 @@ +Thu Mar 12 23:24:18 UTC 2020 - David Diaz <dgonza...@suse.com> + +- Improve the product selection dialog (related to bsc#1157780). +- 4.2.59 + +------------------------------------------------------------------- Old: ---- yast2-packager-4.2.58.tar.bz2 New: ---- yast2-packager-4.2.59.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ yast2-packager.spec ++++++ --- /var/tmp/diff_new_pack.BAD8Sv/_old 2020-03-22 14:16:17.366022906 +0100 +++ /var/tmp/diff_new_pack.BAD8Sv/_new 2020-03-22 14:16:17.382022917 +0100 @@ -17,7 +17,7 @@ Name: yast2-packager -Version: 4.2.58 +Version: 4.2.59 Release: 0 Summary: YaST2 - Package Library License: GPL-2.0-or-later @@ -35,8 +35,8 @@ BuildRequires: yast2-storage-ng >= 4.0.141 # break the yast2-packager -> yast2-storage-ng -> yast2-packager build cycle #!BuildIgnore: yast2-packager -# Y2Packager::Repositories -BuildRequires: yast2 >= 4.2.60 +# CWM::MultiStatusSelector +BuildRequires: yast2 >= 4.2.72 # Pkg::Resolvables BuildRequires: yast2-pkg-bindings >= 4.2.0 # Augeas lenses @@ -47,8 +47,8 @@ Requires: yast2-country-data >= 2.16.3 # Pkg::Resolvables Requires: yast2-pkg-bindings >= 4.2.0 -# Y2Packager::Repositories -Requires: yast2 >= 4.2.60 +# CWM::MultiStatusSelector +Requires: yast2 >= 4.2.72 # unzipping license file Requires: unzip # HTTP, FTP, HTTPS modules (inst_productsources.ycp) ++++++ yast2-packager-4.2.58.tar.bz2 -> yast2-packager-4.2.59.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/package/yast2-packager.changes new/yast2-packager-4.2.59/package/yast2-packager.changes --- old/yast2-packager-4.2.58/package/yast2-packager.changes 2020-03-06 13:22:46.000000000 +0100 +++ new/yast2-packager-4.2.59/package/yast2-packager.changes 2020-03-18 15:04:45.000000000 +0100 @@ -1,4 +1,10 @@ ------------------------------------------------------------------- +Thu Mar 12 23:24:18 UTC 2020 - David Diaz <dgonza...@suse.com> + +- Improve the product selection dialog (related to bsc#1157780). +- 4.2.59 + +------------------------------------------------------------------- Fri Mar 6 09:11:15 UTC 2020 - Ladislav Slezák <lsle...@suse.cz> - Avoid unselecting the base product (related to bsc#1165501) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/package/yast2-packager.spec new/yast2-packager-4.2.59/package/yast2-packager.spec --- old/yast2-packager-4.2.58/package/yast2-packager.spec 2020-03-06 13:22:46.000000000 +0100 +++ new/yast2-packager-4.2.59/package/yast2-packager.spec 2020-03-18 15:04:45.000000000 +0100 @@ -17,7 +17,7 @@ Name: yast2-packager -Version: 4.2.58 +Version: 4.2.59 Release: 0 Summary: YaST2 - Package Library License: GPL-2.0-or-later @@ -35,8 +35,8 @@ BuildRequires: yast2-storage-ng >= 4.0.141 # break the yast2-packager -> yast2-storage-ng -> yast2-packager build cycle #!BuildIgnore: yast2-packager -# Y2Packager::Repositories -BuildRequires: yast2 >= 4.2.60 +# CWM::MultiStatusSelector +BuildRequires: yast2 >= 4.2.72 # Pkg::Resolvables BuildRequires: yast2-pkg-bindings >= 4.2.0 # Augeas lenses @@ -47,8 +47,8 @@ Requires: yast2-country-data >= 2.16.3 # Pkg::Resolvables Requires: yast2-pkg-bindings >= 4.2.0 -# Y2Packager::Repositories -Requires: yast2 >= 4.2.60 +# CWM::MultiStatusSelector +Requires: yast2 >= 4.2.72 # unzipping license file Requires: unzip # HTTP, FTP, HTTPS modules (inst_productsources.ycp) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/src/lib/y2packager/dialogs/addon_selector.rb new/yast2-packager-4.2.59/src/lib/y2packager/dialogs/addon_selector.rb --- old/yast2-packager-4.2.58/src/lib/y2packager/dialogs/addon_selector.rb 2020-03-06 13:22:46.000000000 +0100 +++ new/yast2-packager-4.2.59/src/lib/y2packager/dialogs/addon_selector.rb 2020-03-18 15:04:45.000000000 +0100 @@ -1,37 +1,40 @@ -# ------------------------------------------------------------------------------ -# Copyright (c) 2017 SUSE LLC, All Rights Reserved. +# Copyright (c) [2017-2020] SUSE LLC # -# This program is free software; you can redistribute it and/or modify it under -# the terms of version 2 of the GNU General Public License as published by the -# Free Software Foundation. +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -# ------------------------------------------------------------------------------ +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. require "yast" -require "erb" -require "ui/installation_dialog" +require "cwm/dialog" require "y2packager/resolvable" +require "y2packager/widgets/addons_selector" Yast.import "AddOnProduct" Yast.import "Mode" Yast.import "ProductFeatures" -Yast.import "Report" Yast.import "Stage" -Yast.import "UI" Yast.import "Wizard" module Y2Packager module Dialogs # Dialog which shows the user available products on the medium - class AddonSelector < ::UI::InstallationDialog - include Yast::Logger - include ERB::Util - + class AddonSelector < ::CWM::Dialog # @return [Array<Y2Packager::ProductLocation>] Products on the medium attr_reader :products + # @return [Array<Y2Packager::ProductLocation>] User selected products attr_reader :selected_products @@ -43,156 +46,107 @@ # # @param products [Array<Y2Packager::ProductLocation>] Products on the medium def initialize(products) - super() textdomain "packager" @products = products - # do not offer base products, they would conflict with the already selected base product, - # allow a hidden way to force displaying them in some special cases + # do not offer base products, they would conflict with the already + # selected base product, allow a hidden way to force displaying them in + # some special cases @products.reject! { |p| p.details&.base } if ENV["Y2_DISPLAY_BASE_PRODUCTS"] != "1" @selected_products = [] end - # Handler for the :next action + # The dialog entry point # - # This action happens when the user clicks the 'Next' button - def next_handler - read_user_selection - - return if selected_products.empty? && !Yast::Popup.ContinueCancel(continue_msg) - - finish_dialog(:next) - end - - # Handler for the :abort action - # Confirm abort when running in the initial stage (inst-sys) - def abort_handler - return if Yast::Stage.initial && !Yast::Popup.ConfirmAbort(:painless) - - finish_dialog(:abort) - end - - # Text to display when the help button is pressed + # Display the dialog title on the left side at installation (in the first + # stage) to have the same layout as in the registration addons dialog. # - # @return [String] - def help_text - # TRANSLATORS: help text - _("<p>The selected repository contains several products in independent " \ - "subdirectories. Select which products you want to install.</p>") - end - - # Handle changing the current item or changing the selection - def addon_repos_handler - current_product = find_current_product - return unless current_product - - refresh_details(current_product) - - select_dependent_products - end - - # Display the the dialog title on the left side at installation - # (in the first stage) to have the same layout as in the registration - # addons dialog. + # @see CWM::Dialog#run def run Yast::Wizard.OpenLeftTitleNextBackDialog if Yast::Stage.initial - super() + super ensure Yast::Wizard.CloseDialog if Yast::Stage.initial end - # overwrite dialog creation to always enable back/next by default - def create_dialog - res = super - Yast::Wizard.EnableNextButton - Yast::Wizard.EnableBackButton - Yast::UI.SetFocus(Id(:addon_repos)) - res - end - - private - - attr_writer :selected_products - - def selection_content - defaults = preselected_products - products.map { |p| Item(Id(p.dir), p.summary || p.name, defaults.include?(p)) } + # @see CWM::Dialog#title + def title + # TODO: does it make sense also for the 3rd party addons? + _("Extension and Module Selection") end - # Dialog content - # - # @see ::UI::Dialog - def dialog_content + # @see CWM::Dialog#contents + def contents VBox( # TRANSLATORS: Product selection label (above a multi-selection box) Left(Heading(_("Available Extensions and Modules"))), - VWeight(60, MinHeight(8, - MultiSelectionBox( - Id(:addon_repos), - Opt(:notify, :immediate), - "", - selection_content - ))), - VSpacing(0.4), - details_widget + addons_selector_widget ) end - # select the dependent products for the active selection - def select_dependent_products - # select the dependent products - new_selection = current_selection - - # the selection has not changed, nothing to do - return if new_selection == selected_products - - # add the dependent items to the selected list - selected_items = Yast::UI.QueryWidget(Id(:addon_repos), :SelectedItems) - new_items = new_selection - selected_products - new_items.each do |p| - # the dependencies contain also the transitive (indirect) dependencies, - # we do not need to recursively evaluate the list - dependencies = p&.details&.depends_on - selected_items.concat(dependencies) if dependencies - end + # Handler for the :next action + # + # Displays a confirmation popup if none product has been selected + # + # @return [Boolean] true when continuing; false if the action is canceled + def next_handler + read_user_selection - selected_items.uniq! + return true unless selected_products.empty? - Yast::UI.ChangeWidget(:addon_repos, :SelectedItems, selected_items) + Yast::Popup.ContinueCancel(continue_msg) end - # refresh the details of the currently selected add-on - def refresh_details(current_product) - details = product_description(current_product) - Yast::UI.ChangeWidget(Id(:details), :Value, details) - Yast::UI.ChangeWidget(Id(:details), :Enabled, true) - end - - def read_user_selection - self.selected_products = current_selection + # Handler for the :abort action + # + # Displays a confirmation popup when running in the initial stage (inst-sys) + # + # @return [Boolean] true when aborting is confirmed; false otherwise + def abort_handler + return true unless Yast::Stage.initial - log.info("Selected products: #{selected_products.inspect}") + Yast::Popup.ConfirmAbort(:painless) end + # Text to display when the help button is pressed # - # The currently selected products - # - # @return [Array<Y2Packager::ProductLocation>] list of selected products - # - def current_selection - selected_items = Yast::UI.QueryWidget(Id(:addon_repos), :SelectedItems) - products.select { |p| selected_items.include?(p.dir) } + # @return [String] help + def help + [ + # TRANSLATORS: Help text for the product selector dialog + _("<p>The selected repository contains several products in independent " \ + "subdirectories. Select which products you want to install.</p>"), + # TRANSLATORS: Help text explaining different product selection statuses + _("<p>Bear in mind that products can have several states depending on " \ + "how they were selected to be installed or not. Basically, it can be "\ + "auto-selected by a pre-selection of recommended products or as a dependency "\ + "of another product, manually selected by the user, or not selected "\ + "(see the legend below).</p>") + ].join end - # Dialog title + private + + # @return [Array<Y2Packager::ProductLocation>] collection of selected products + attr_writer :selected_products + + # Addons selector widget # - # @see ::UI::Dialog - def dialog_title - # TODO: does it make sense also for the 3rd party addons? - _("Extension and Module Selection") + # @return [Y2Packager::Widgets::AddonsSelector] + def addons_selector_widget + @addons_selector_widget ||= Widgets::AddonsSelector.new(products, preselected_products) end - # A message for asking the user whether to continue without adding any addon. + # Reads the currently selected products + def read_user_selection + selected_items = addons_selector_widget.selected_items.map(&:id) + + self.selected_products = products.select { |p| selected_items.include?(p.dir) } + + log.info("Selected products: #{selected_products.inspect}") + end + + # A message for asking the user whether to continue without adding any addon # # @return [String] translated message def continue_msg @@ -201,55 +155,30 @@ "Do you really want to continue without adding any product?") end - # description widget - # @return [Yast::Term] the addon details widget - def details_widget - VWeight( - 40, - RichText(Id(:details), Opt(:disabled), initial_description) - ) - end - - # extra help text - # @return [String] first product description - def initial_description - return "" if products.empty? - - product_description(products.first) - end - - def product_description(product) - erb_file = File.join(__dir__, "product_summary.erb") - log.info "Loading ERB template #{erb_file}" - erb = ERB.new(File.read(erb_file)) - - # compute the dependent products - dependencies = [] - product&.details&.depends_on&.each do |p| - # display the human readable product name instead of the product directory - prod = @products.find { |pr| pr.dir == p } - dependencies << (prod.summary || prod.name) if prod - end - - # render the ERB template in the context of this object - erb.result(binding) - end - - # return a list of the preselected products depending on the installation mode - # @return [Array<Y2Packager::ProductLocation>] the products + # Returns a list of the preselected products depending on the installation mode + # + # @see #preselected_installation_products + # @see #preselected_upgrade_products + # + # @return [Array<Y2Packager::ProductLocation>] preselected products def preselected_products - # at upgrade preselect the installed addons - return preselected_upgrade_products if Yast::Mode.update - # in installation preselect the defaults defined in the control.xml/installation.xml - return preselected_installation_products if Yast::Mode.installation - - # in other modes (e.g. installed system) do not preselect anything - [] + if Yast::Mode.installation + # in installation preselect the defaults defined in the control.xml/installation.xml + preselected_installation_products + elsif Yast::Mode.update + # at upgrade preselect the installed addons + preselected_upgrade_products + else + # in other modes (e.g. installed system) do not preselect anything + [] + end end - # return a list of the preselected products at upgrade, - # preselect the installed products - # @return [Array<Y2Packager::ProductLocation>] the products + # Returns a list of the preselected products at upgrade + # + # Preselect the installed products + # + # @return [Array<Y2Packager::ProductLocation>] preselected products def preselected_upgrade_products missing_products = Yast::AddOnProduct.missing_upgrades # installed but not selected yet products (to avoid duplicates) @@ -258,10 +187,12 @@ end end - # return a list of the preselected products at installation, - # preselect the default products specified in the control.xml/installation.xml, - # the already selected products are ignored - # @return [Array<Y2Packager::ProductLocation>] the products + # Return a list of the preselected products at installation, + # + # Preselect the default products specified in the control.xml/installation.xml, + # the already selected products are ignored. + # + # @return [Array<Y2Packager::ProductLocation>] preselected products def preselected_installation_products default_modules = Yast::ProductFeatures.GetFeature("software", "default_modules") return [] unless default_modules.is_a?(Array) @@ -278,14 +209,6 @@ default_modules.include?(p.details&.product) end end - - # Returns the current product (the one which has the focus in the addons list) - # - # @return [Y2Packager::Product,nil] - def find_current_product - current_item = Yast::UI.QueryWidget(Id(:addon_repos), :CurrentItem) - products.find { |p| p.dir == current_item } - end end end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/src/lib/y2packager/dialogs/product_summary.erb new/yast2-packager-4.2.59/src/lib/y2packager/dialogs/product_summary.erb --- old/yast2-packager-4.2.58/src/lib/y2packager/dialogs/product_summary.erb 2020-03-06 13:22:46.000000000 +0100 +++ new/yast2-packager-4.2.59/src/lib/y2packager/dialogs/product_summary.erb 1970-01-01 01:00:00.000000000 +0100 @@ -1,36 +0,0 @@ -<% -textdomain "packager" -%> - -<%# TRANSLATORS: the RichText header, followed by the name of the directory %> -<b><%= _("Directory on the Media:") %></b> <%= h(product.dir) %><br> -<%# TRANSLATORS: the RichText header, followed by the name of the medium %> -<b><%= _("Media Name:") %></b> <%= h(product.name) %><br> - -<% details = product.details %> -<% if details %> - <% if details.product %> - <%# TRANSLATORS: the RichText header, followed by the product identifier, e.g. "SLES" %> - <b><%= _("Product ID:") %></b> <%= h(details.product) %> - <% end %> - - <% if dependencies.nil? %> - <%# TRANSLATORS: error message in a RichText summary %> - <p><em><%= _("Cannot evaluate the product dependencies.") %></em></p> - <% elsif !dependencies.empty? %> - <%# TRANSLATORS: the RichText section header, followed by the names of the dependent products %> - <h3><%= _("Dependencies") %></h3> - <ul> - <% dependencies.each do |dep| %> - <li><%= h(dep) %></li> - <% end %> - </ul> - <% end %> - - <% if !details.description.empty? %> - <%# TRANSLATORS: the RichText header, it is followed by an untranslated (English) product description %> - <h3><%= _("Product Description (English Only)") %></h3> - <%# no escaping, the description already *is* a rich text %> - <p><%= details.description %></p> - <% end %> -<% end %> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/src/lib/y2packager/widgets/addons_selector.rb new/yast2-packager-4.2.59/src/lib/y2packager/widgets/addons_selector.rb --- old/yast2-packager-4.2.58/src/lib/y2packager/widgets/addons_selector.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-packager-4.2.59/src/lib/y2packager/widgets/addons_selector.rb 2020-03-18 15:04:45.000000000 +0100 @@ -0,0 +1,179 @@ +# Copyright (c) [2020] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require "yast" +require "forwardable" +require "cwm/multi_status_selector" + +module Y2Packager + module Widgets + # A custom widget to display a multi status selector list + class AddonsSelector < CWM::MultiStatusSelector + include Yast::Logger + include Yast::UIShortcuts + + attr_reader :items + + # Constructor + # + # @param products [Array<ProductLocation>] available product locations + # @param preselected_products [Array<ProductLocation>] product locations to be selected + def initialize(products, preselected_products) + @products = products + @items = products.map do |product| + dependencies = product&.details&.depends_on || [] + selected = preselected_products.include?(product) + + Item.new(product, dependencies, selected) + end + end + + def init + super + + # Respect the behavior introduced in version 4.2.55 + details_widget.value = items.first.description + end + + # (see CWM::AbstractWidget#contents) + def contents + VBox( + VWeight(60, super), + VWeight(40, details_widget) + ) + end + + # Toggles the item + # + # Also recalculates the dependencies to perform necessary auto selections + # + # @param item [Item] the item to toggle + def toggle(item) + item.toggle + refresh_details(item) + select_dependencies + end + + # Returns selected and auto-selected items + # + # @return [Array<Item>] a collection of selected and auto-selected items + def selected_items + items.select { |i| i.selected? || i.auto_selected? } + end + + # (see CWM::AbstractWidget#contents) + def help + Item.help + end + + private + + # (see CWM::MultiStatusSelector#label_event_handler) + def label_event_handler(item) + refresh_details(item) + end + + # Updates the details area with the given item description + # + # @param item [Item] selected item + def refresh_details(item) + details_widget.value = item.description + end + + # Auto-selects needed dependencies + # + # Based in the current selection, auto selects dependencies not manually + # selected yet. + def select_dependencies + # Resets previous auto selection + @items.select(&:auto_selected?).each(&:unselect!) + + # Recalculates missed dependencies + selected_items = @items.select(&:selected?) + dependencies = selected_items.flat_map(&:dependencies).uniq + missed_dependencies = dependencies - selected_items.map(&:id) + + # Auto-selects them + @items.select { |i| missed_dependencies.include?(i.id) }.each(&:auto_select!) + end + + # Returns the widget to display the details + # + # @return [CWM::RichText] the widget to display the details + def details_widget + @details_widget ||= + begin + w = CWM::RichText.new + w.widget_id = "details_area" + w + end + end + + # Internal class to represent a {Y2Packager::ProductLocation} as selectable item + class Item < Item + include Yast::Logger + include ERB::Util + include Yast::I18n + + # Constructor + # + # @param product [Y2Packager::ProductLocation] the product to be represented + # @param dependencies [Array<String>] a collection with the dependencies ids + # @param selected [Boolean] a flag indicating the initial status for the item + def initialize(product, dependencies, selected) + @product = product + @dependencies = dependencies + @status = selected ? :selected : :unselected + end + + attr_reader :dependencies, :status + + # Returns the item id + # + # @return [String] the item id + def id + product.dir + end + + # Returns the item label + # + # @return [String] the item label + def label + product.summary || product.name + end + + # Builds the item description + def description + @description ||= + begin + erb_file = File.join(__dir__, "product_summary.erb") + log.info "Loading ERB template #{erb_file}" + erb = ERB.new(File.read(erb_file)) + + erb.result(binding) + end + end + + private + + attr_reader :product + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/src/lib/y2packager/widgets/product_summary.erb new/yast2-packager-4.2.59/src/lib/y2packager/widgets/product_summary.erb --- old/yast2-packager-4.2.58/src/lib/y2packager/widgets/product_summary.erb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-packager-4.2.59/src/lib/y2packager/widgets/product_summary.erb 2020-03-18 15:04:45.000000000 +0100 @@ -0,0 +1,36 @@ +<% +textdomain "packager" +%> + +<%# TRANSLATORS: the RichText header, followed by the name of the directory %> +<b><%= _("Directory on the Media:") %></b> <%= h(product.dir) %><br> +<%# TRANSLATORS: the RichText header, followed by the name of the medium %> +<b><%= _("Media Name:") %></b> <%= h(product.name) %><br> + +<% details = product.details %> +<% if details %> + <% if details.product %> + <%# TRANSLATORS: the RichText header, followed by the product identifier, e.g. "SLES" %> + <b><%= _("Product ID:") %></b> <%= h(details.product) %> + <% end %> + + <% if dependencies.nil? %> + <%# TRANSLATORS: error message in a RichText summary %> + <p><em><%= _("Cannot evaluate the product dependencies.") %></em></p> + <% elsif !dependencies.empty? %> + <%# TRANSLATORS: the RichText section header, followed by the names of the dependent products %> + <h3><%= _("Dependencies") %></h3> + <ul> + <% dependencies.each do |dep| %> + <li><%= h(dep) %></li> + <% end %> + </ul> + <% end %> + + <% if !details.description.empty? %> + <%# TRANSLATORS: the RichText header, it is followed by an untranslated (English) product description %> + <h3><%= _("Product Description (English Only)") %></h3> + <%# no escaping, the description already *is* a rich text %> + <p><%= details.description %></p> + <% end %> +<% end %> \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/test/addon_selector_test.rb new/yast2-packager-4.2.59/test/addon_selector_test.rb --- old/yast2-packager-4.2.58/test/addon_selector_test.rb 2020-03-06 13:22:46.000000000 +0100 +++ new/yast2-packager-4.2.59/test/addon_selector_test.rb 1970-01-01 01:00:00.000000000 +0100 @@ -1,136 +0,0 @@ -#! /usr/bin/env rspec - -require_relative "./test_helper" - -require "y2packager/product_location" -require "y2packager/product_location_details" -require "y2packager/dialogs/addon_selector" - -describe Y2Packager::Dialogs::AddonSelector do - let(:media_products) do - prods = [ - [ - "SLE-15-Module-Basesystem 15.0-0", - "/Basesystem", - Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") - ], - [ - "SLE-15-Module-Legacy 15.0-0", - "/Legacy", - Y2Packager::ProductLocationDetails.new(product: "sle-module-legacy") - ] - ] - prods.map { |r| Y2Packager::ProductLocation.new(r[0], r[1], product: r[2]) } - end - - subject { described_class.new(media_products) } - - describe "#help_text" do - it "returns a String" do - expect(subject.help_text).to be_a(String) - end - end - - describe "#abort_handler" do - it "returns :abort" do - allow(Yast::Stage).to receive(:initial).and_return(false) - expect(subject.abort_handler).to eq(:abort) - end - - context "in installation" do - before do - expect(Yast::Stage).to receive(:initial).and_return(true) - end - - it "asks for confirmation" do - expect(Yast::Popup).to receive(:ConfirmAbort).and_return(true) - subject.abort_handler - end - - it "returns :abort when confirmed" do - expect(Yast::Popup).to receive(:ConfirmAbort).and_return(true) - expect(subject.abort_handler).to eq(:abort) - end - - it "returns nil when not confirmed" do - expect(Yast::Popup).to receive(:ConfirmAbort).and_return(false) - expect(subject.abort_handler).to be_nil - end - end - end - - describe "#next_handler" do - context "an addon is selected" do - before do - expect(Yast::UI).to receive(:QueryWidget).with(Id(:addon_repos), :SelectedItems) - .and_return(["/Basesystem"]) - end - - it "returns :next if an addon is selected" do - expect(subject.next_handler).to eq(:next) - end - - it "does not display any popup" do - expect(Yast::Popup).to_not receive(:anything) - subject.next_handler - end - end - - context "no addon is selected" do - before do - expect(Yast::UI).to receive(:QueryWidget).with(Id(:addon_repos), :SelectedItems) - .and_return([]) - end - - it "displays a popup asking for confirmation" do - expect(Yast::Popup).to receive(:ContinueCancel).with(/no product/i) - subject.next_handler - end - - it "returns :next if the popup is confirmed" do - expect(Yast::Popup).to receive(:ContinueCancel).with(/no product/i).and_return(true) - expect(subject.next_handler).to eq(:next) - end - - it "returns nil if the popup is not confirmed" do - expect(Yast::Popup).to receive(:ContinueCancel).with(/no product/i).and_return(false) - expect(subject.next_handler).to be_nil - end - end - end - - describe "#create_dialog" do - context "in installation" do - before do - allow(Yast::Stage).to receive(:initial).and_return(true) - allow(Yast::Mode).to receive(:installation).and_return(true) - end - - it "preselects the default products from control.xml" do - # mock the control.xml default - expect(Yast::ProductFeatures).to receive(:GetFeature) - .with("software", "default_modules").and_return(["sle-module-basesystem"]) - - allow(Y2Packager::Resolvable).to receive(:find) - .with(kind: :product, status: :selected).and_return([]) - - expect(Yast::Wizard).to receive(:SetContents) do |_title, content, _help, _back, _next| - # find the MultiSelectionBox term in the UI definition - term = content.nested_find do |t| - t.respond_to?(:value) && t.value == :MultiSelectionBox - end - - # verify that the Basesystem module is preselected - expect(term.params[3][0].params[1]).to eq("SLE-15-Module-Basesystem 15.0-0") - expect(term.params[3][0].params[2]).to eq(true) - - # verify that the Legacy module is NOT preselected - expect(term.params[3][1].params[1]).to eq("SLE-15-Module-Legacy 15.0-0") - expect(term.params[3][1].params[2]).to eq(false) - end - - subject.create_dialog - end - end - end -end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/test/lib/dialogs/addon_selector_test.rb new/yast2-packager-4.2.59/test/lib/dialogs/addon_selector_test.rb --- old/yast2-packager-4.2.58/test/lib/dialogs/addon_selector_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-packager-4.2.59/test/lib/dialogs/addon_selector_test.rb 2020-03-18 15:04:45.000000000 +0100 @@ -0,0 +1,176 @@ +# Copyright (c) [2017-2020] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. +# +require_relative "../../test_helper" + +require "cwm/rspec" +require "y2packager/product_location" +require "y2packager/product_location_details" +require "y2packager/dialogs/addon_selector" + +describe Y2Packager::Dialogs::AddonSelector do + subject { described_class.new(products) } + + include_examples "CWM::Dialog" + + let(:basesystem) do + Y2Packager::ProductLocation.new( + "SLE-15-Module-Basesystem 15.0-0", + "/Basesystem", + product: Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") + ) + end + + let(:desktop_applications) do + Y2Packager::ProductLocation.new( + "Desktop-Applications-Module 15-0", + "/Desktop-Applications", + product: Y2Packager::ProductLocationDetails.new( + depends_on: ["SLE-15-Module-Basesystem 15.0-0"] + ) + ) + end + + let(:legacy_product) do + Y2Packager::ProductLocation.new( + "SLE-15-Module-Legacy 15.0-0", + "/Legacy", + product: Y2Packager::ProductLocationDetails.new(product: "sle-module-legacy") + ) + end + + let(:products) { [basesystem, desktop_applications, legacy_product] } + + describe "#contents" do + context "during installation" do + before do + allow(Yast::Stage).to receive(:initial).and_return(true) + allow(Yast::Mode).to receive(:installation).and_return(true) + allow(Yast::UI).to receive(:UserInput).and_return(:next) + + # mock the control.xml default + allow(Yast::ProductFeatures).to receive(:GetFeature) + .with("software", "default_modules") + .and_return(["sle-module-basesystem"]) + allow(Y2Packager::Resolvable).to receive(:find) + .with(kind: :product, status: :selected) + .and_return([]) + end + + it "preselects the default products from control.xml" do + expect(Y2Packager::Widgets::AddonsSelector).to receive(:new) + .with(anything, [products.first]) + + subject.contents + end + end + end + + describe "#abort_handler" do + it "returns :abort" do + allow(Yast::Stage).to receive(:initial).and_return(false) + end + + context "during installation" do + let(:confirm_abort) { false } + + before do + allow(Yast::Stage).to receive(:initial).and_return(true) + allow(Yast::Popup).to receive(:ConfirmAbort).and_return(confirm_abort) + end + + it "asks for confirmation" do + expect(Yast::Popup).to receive(:ConfirmAbort).and_return(true) + + subject.abort_handler + end + + context "when confirmed" do + let(:confirm_abort) { true } + + it "returns true" do + expect(subject.abort_handler).to eq(true) + end + end + + context "when rejected" do + let(:confirm_abort) { false } + + it "returns false" do + expect(subject.abort_handler).to eq(false) + end + end + end + end + + describe "#next_handler" do + let(:addons_selector) { Y2Packager::Widgets::AddonsSelector.new(products, []) } + + before do + allow(Y2Packager::Widgets::AddonsSelector).to receive(:new).and_return(addons_selector) + end + + context "when a product is selected" do + before do + addons_selector.items.each(&:select!) + end + + it "does not display a popup" do + expect(Yast::Popup).to_not receive(:ContinueCancel) + + subject.next_handler + end + + it "returns true" do + expect(subject.next_handler).to eq(true) + end + end + + context "when none product is selected" do + let(:confirm_continue) { false } + + before do + addons_selector.items.each(&:unselect!) + allow(Yast::Popup).to receive(:ContinueCancel).and_return(confirm_continue) + end + + it "displays a popup asking for confirmation" do + expect(Yast::Popup).to receive(:ContinueCancel).with(/no product/i) + + subject.next_handler + end + + context "and the user decides to continue" do + let(:confirm_continue) { true } + + it "returns true" do + expect(subject.next_handler).to eq(true) + end + end + + context "but the user decides to cancel" do + let(:confirm_continue) { false } + + it "returns false" do + expect(subject.next_handler).to eq(false) + end + end + end + end +end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/yast2-packager-4.2.58/test/lib/widgets/addons_selector_test.rb new/yast2-packager-4.2.59/test/lib/widgets/addons_selector_test.rb --- old/yast2-packager-4.2.58/test/lib/widgets/addons_selector_test.rb 1970-01-01 01:00:00.000000000 +0100 +++ new/yast2-packager-4.2.59/test/lib/widgets/addons_selector_test.rb 2020-03-18 15:04:45.000000000 +0100 @@ -0,0 +1,183 @@ +# Copyright (c) [2020] SUSE LLC +# +# All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as published +# by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, contact SUSE LLC. +# +# To contact SUSE LLC about this file by physical or electronic mail, you may +# find current contact information at www.suse.com. + +require_relative "../../test_helper" + +require "cwm/rspec" +require "y2packager/product_location" +require "y2packager/product_location_details" +require "y2packager/widgets/addons_selector" + +describe Y2Packager::Widgets::AddonsSelector do + subject(:addons_selector) { described_class.new(products, preselected_products) } + + include_examples "CWM::CustomWidget" + + let(:details_area) do + subject.contents.nested_find { |i| i.is_a?(CWM::RichText) && i.widget_id == "details_area" } + end + + let(:basesystem) do + Y2Packager::ProductLocation.new( + "SLE-15-Module-Basesystem 15.0-0", + "/Basesystem", + product: Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") + ) + end + + let(:desktop_applications) do + Y2Packager::ProductLocation.new( + "Desktop-Applications-Module 15-0", + "/Desktop-Applications", + product: Y2Packager::ProductLocationDetails.new( + depends_on: ["/Basesystem"] + ) + ) + end + + let(:legacy_product) do + Y2Packager::ProductLocation.new( + "SLE-15-Module-Legacy 15.0-0", + "/Legacy", + product: Y2Packager::ProductLocationDetails.new(product: "sle-module-legacy") + ) + end + + let(:products) { [basesystem, desktop_applications, legacy_product] } + let(:preselected_products) { [legacy_product] } + + describe "#initialize" do + it "selects preselected products" do + expect(subject.selected_items.map(&:id)).to eq(preselected_products.map(&:dir)) + end + end + + describe "#init" do + let(:first_item) { subject.items.first } + + # Behavior introduced in version 4.2.55 (https://github.com/yast/yast-packager/pull/511) + it "displays the first item description" do + expect(details_area).to receive(:value=).with(first_item.description) + + subject.init + end + end + + describe "#items" do + it "returns a collection of items representing available products" do + expect(subject.items.map(&:id)).to eq(products.map(&:dir)) + end + end + + describe "#toggle" do + let(:item) { subject.items.first } + + it "toggles given item" do + expect(item).to receive(:toggle) + + subject.toggle(item) + end + + it "displays the item details" do + expect(details_area).to receive(:value=).with(item.description) + + subject.toggle(item) + end + + context "when selected item has dependencies" do + let(:basesystem_item) { subject.items.find { |i| i.id == "/Basesystem" } } + let(:desktop_apps_item) { subject.items.find { |i| i.id == "/Desktop-Applications" } } + + context "and they are not selected yet" do + it "auto-selects them" do + expect(basesystem_item).to receive(:auto_select!) + + subject.toggle(desktop_apps_item) + end + end + + context "but they are already selected" do + before do + basesystem_item.select! + end + + it "does nothing" do + expect(basesystem_item).to_not receive(:auto_select!) + expect(basesystem_item).to_not receive(:unselect!) + expect(basesystem_item).to_not receive(:select!) + + subject.toggle(desktop_apps_item) + end + end + end + end +end + +describe Y2Packager::Widgets::AddonsSelector::Item do + subject(:item) { described_class.new(product, dependencies, selected) } + + let(:product) do + Y2Packager::ProductLocation.new( + "SLE-15-Module-Basesystem 15.0-0", + "/Basesystem", + product: Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") + ) + end + + let(:dependencies) { nil } + let(:selected) { false } + + describe "#id" do + it "returns a String" do + expect(subject.id).to be_a(String) + end + + it "returns the product dir" do + expect(subject.id).to eq(product.dir) + end + end + + describe "#label" do + let(:product_summary) { "A product summary" } + let(:product_name) { "A product name" } + + before do + allow(product).to receive(:summary).and_return(product_summary) + allow(product).to receive(:name).and_return(product_name) + end + + it "returns a String" do + expect(subject.id).to be_a(String) + end + + context "when product has a summary" do + it "returns the product summary" do + expect(subject.label).to eq(product_summary) + end + end + + context "when product has not a summary" do + let(:product_summary) { nil } + + it "returns the product name" do + expect(subject.label).to eq(product_name) + end + end + end +end