Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/lib/y2configuration_management/salt/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ def initialize(controller)
# Returns the list of widgets to be included in the form
#
# @param form_element [Y2ConfigurationManagement::Salt::FormElement] Form element
# @return [Array<Y2ConfigurationManagement::Widgets::AbstractWidget>] List of widgets
# @return [Y2ConfigurationManagement::Widgets::Form] Form
def build(form_element)
Array(form_element).map { |e| build_element(e) }
widgets = Array(form_element).map { |e| build_element(e) }
Y2ConfigurationManagement::Widgets::Form.new(widgets)
end

private
Expand Down
92 changes: 69 additions & 23 deletions src/lib/y2configuration_management/salt/form_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# find current contact information at www.suse.com.

require "y2configuration_management/salt/form_builder"
require "y2configuration_management/salt/form_data"
require "y2configuration_management/widgets/form_popup"

Yast.import "CWM"
Expand All @@ -38,29 +39,55 @@ class FormController
# Constructor
#
# @param form [Y2ConfigurationManagement::Salt::Form] Form
# @param state [Hash] Current state (TODO)
def initialize(form, state = {})
textdomain "configuration_management"

@state = state # TODO
def initialize(form)
@data = FormData.new(form)
@form = form
end

# Renders the main form's dialog
def show_main_dialog
show_dialog(form.root.name, form_builder.build(form.root.elements))
Yast::Wizard.CreateDialog
Yast::CWM.show(
HBox(replace_point),
caption: form.root.name, next_handler: method(:next_handler)
)
ensure
Yast::Wizard.CloseDialog
end

# Convenience method for returning the value of a given element
#
# @param path [String] Path to the element
def get(path)
@data.get(path)
end

# Opens a new dialog in order to add a new element to a collection
# @todo
#
# @param path [String] Collection's path
def add(path)
element = form.find_element_by(path: path).prototype
show_popup(element.name, form_builder.build(element))
result = show_popup(element.name, form_builder.build(element))
return if result.nil?
@data.add(path, result.values.first)
refresh_main_form
end

# Removes an element from a collection
#
# @param path [String] Collection's path
# @param index [Integer] Element's index
def remove(path, index)
@data.remove(path, index)
refresh_main_form
end

private

attr_reader :form, :state
# @return [Form]
attr_reader :form
# @return [FormData]
attr_reader :data

# Returns the form builder
#
Expand All @@ -69,26 +96,45 @@ def form_builder
@form_builder ||= Y2ConfigurationManagement::Salt::FormBuilder.new(self)
end

# Displays a form dialog
# Renders the main form's dialog
def main_form
widget_form = form_builder.build(form.root.elements)
widget_form.value = get(form.root.path)
widget_form
end

# Refreshes the main form content
def refresh_main_form
replace_point.replace(main_form)
end

# Replace point to place the main dialog
#
# @param title [String] Dialog title
# @param contents [Array<CWM::AbstractWidget>] Popup content (as an array of CWM widgets)
def show_dialog(title, contents)
next_handler = proc { Yast::Popup.YesNo("Exit?") }
Yast::Wizard.CreateDialog
Yast::CWM.show(
VBox(*contents), caption: title, next_handler: next_handler
)
ensure
Yast::Wizard.CloseDialog
# @return [CWM::ReplacePoint]
def replace_point
@replace_point ||= ::CWM::ReplacePoint.new(widget: main_form)
end

# Displays a popup
#
# @param title [String] Popup title
# @param contents [Array<CWM::AbstractWidget>] Popup content (as an array of CWM widgets)
def show_popup(title, contents)
Widgets::FormPopup.new(title, contents).run
# @param widget [Array<CWM::AbstractWidget>] Popup content (as an array of CWM widgets)
# @return [Hash,nil] Dialog's result
def show_popup(title, widget)
Widgets::FormPopup.new(title, widget).run
widget.result
end

# @todo This version is just for debugging purposes. It should be replaced with a meaningful
# version.
def next_handler
return false unless Yast::Popup.YesNo("Do you want to exit?")
# This does not work. should main_form be memoized?
# main_form.store
# data.update(form.root.path, main_form.result)
data.update(form.root.path, main_form.store)
puts data.to_h.inspect
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
puts data.to_h.inspect
require "yaml" # at the top of the file
puts data.to_h.to_yaml

true
end
end
end
Expand Down
122 changes: 122 additions & 0 deletions src/lib/y2configuration_management/salt/form_data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Copyright (c) [2018] 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.

module Y2ConfigurationManagement
module Salt
# This class holds data for a given Salt Formula Form
#
# @todo The support for collections is rather simple and nesting collections is not supported.
# We might consider using JSON Patch to modify the data.
class FormData
PATH_DELIMITER = ".".freeze
# @return [Y2ConfigurationManagement::Salt::Form] Form
attr_reader :form

# Constructor
#
# @param form [Y2ConfigurationManagement::Salt::Form] Form
def initialize(form)
@data = data_for_form(form)
@form = form
end

# Returns the value of a given element
#
# @param path [String] Path to the element
def get(path)
@data.dig(*path_to_parts(path)) || default_for(path)
end

# Adds an element to a collection
#
# @param path [String] Path to the collection
# @param value [Hash] Value to add
def add(path, value)
collection = get(path)
collection.push(value)
end

# Updates an element's value
#
# @param path [String] Path to the collection
# @param value [Object] New value
def update(path, value)
parts = path_to_parts(path)
parent_parts = parts[0..-2]
parent = @data
parent = parent.dig(* parent_parts) unless parent_parts.empty?
parent[parts.last] = value
end

# Removes an element from a collection
#
# @param path [String] Path to the collection
# @param index [Integer] Position of the element to remove
def remove(path, index)
collection = get(path)
collection.delete_at(index)
end

# Returns a hash containing the form data
#
# @return [Hash]
def to_h
@data
end

private

# Default value for a given element
#
# @param path [String] Element path
def default_for(path)
element = form.find_element_by(path: path)
element ? element.default : nil
end

# Split the path into different parts
#
# @param path [String] Element path
def path_to_parts(path)
path[1..-1].split(PATH_DELIMITER)
end

# Builds a hash to keep the form data
#
# @param form [Y2ConfigurationManagement::Salt::Form]
# @return [Hash]
def data_for_form(form)
data_for_element(form.root)
end

# Builds a hash to keep the form element data
#
# @param element [Y2ConfigurationManagement::Salt::FormElement]
# @return [Hash]
def data_for_element(element)
if element.is_a?(Container)
defaults = element.elements.reduce({}) { |a, e| a.merge(data_for_element(e)) }
{ element.id => defaults }
else
{ element.id => element.default }
end
end
end
end
end
1 change: 1 addition & 0 deletions src/lib/y2configuration_management/widgets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
require "y2configuration_management/widgets/group"
require "y2configuration_management/widgets/select"
require "y2configuration_management/widgets/text"
require "y2configuration_management/widgets/form"
3 changes: 3 additions & 0 deletions src/lib/y2configuration_management/widgets/boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class Boolean < ::CWM::CheckBox
attr_reader :default
# @return [String] Form path
attr_reader :path
# @return [String] Form element id
attr_reader :id

# Constructor
#
Expand All @@ -40,6 +42,7 @@ def initialize(spec, controller)
@default = spec.default == true # nil -> false
@controller = controller
@path = spec.path
@id = spec.id
self.widget_id = "boolean:#{spec.id}"
end

Expand Down
35 changes: 30 additions & 5 deletions src/lib/y2configuration_management/widgets/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ module Widgets
# This widget uses a table to display a collection of elements and offers
# buttons to add, remove and edit them.
class Collection < ::CWM::CustomWidget
attr_reader :label, :min_items, :max_items, :controller, :path
attr_reader :label, :min_items, :max_items, :controller, :path, :id
# @return [Array<Object>] List of objects which are included in the collection
attr_accessor :value

# Constructor
#
Expand All @@ -40,7 +42,9 @@ def initialize(spec, controller)
@max_items = spec.max_items
@controller = controller
@path = spec.path # form element path
@id = spec.id
self.widget_id = "collection:#{spec.id}"
self.value = []
end

# Widget contents
Expand All @@ -49,10 +53,10 @@ def initialize(spec, controller)
def contents
VBox(
Table(
Id("table_#{widget_id}"),
Id("table:#{path}"),
Opt(:notify, :immediate),
Header(label),
[]
Header(*headers),
items_list
),
HBox(
HStretch(),
Expand Down Expand Up @@ -85,6 +89,7 @@ def handle(event)
when "#{widget_id}_remove".to_sym
# TODO
# controller.remove(path, selected_row) if selected_row
controller.remove(path, selected_row) if selected_row
end

nil
Expand All @@ -96,9 +101,29 @@ def handle(event)
#
# @return [Integer,nil] Index of the selected row or nil if no row is selected
def selected_row
row_id = UI.QueryWidget(Id("table_#{widget_id}"), :CurrentItem)
row_id = Yast::UI.QueryWidget(Id("table:#{path}"), :CurrentItem)
row_id ? row_id.to_i : nil
end

# Returns the headers for the collection table
#
# @todo Get this information from the formula spec
#
# @return [Array<String>]
def headers
return unless value.first
value.first.keys
end

# Format the items list for the colletion table
#
# @return [Array<Array<String|Yast::Term>>]
def items_list
value.each_with_index.map do |item, index|
values = headers.map { |h| item[h] }
Item(Id(index.to_s), *values)
end
end
end
end
end
Loading