Class: Installation::ProposalStore

Inherits:
Object
  • Object
show all
Includes:
Yast::I18n, Yast::Logger
Defined in:
src/lib/installation/proposal_store.rb

Overview

  1. Provides access to metadata of proposal parts (clients), as defined in the control file elements /productDefines/proposals/proposal: https://github.com/yast/yast-installation-control/blob/master/control/control.rnc
  2. Handles all calls to the parts (clients).

Constant Summary

MAX_LOOPS_IN_PROPOSAL =

How many times to maximally (re)run the proposal while some proposal clients try to re-trigger their run again, number includes their initial run and resets before each proposal loop starts

8

Instance Method Summary collapse

Constructor Details

#initialize(proposal_mode) ⇒ ProposalStore

Returns a new instance of ProposalStore

Parameters:

  • proposal_mode (String)

    one of initial, service, network, hardware, uml, ... or anything else



39
40
41
42
43
44
45
46
47
48
# File 'src/lib/installation/proposal_store.rb', line 39

def initialize(proposal_mode)
  Yast.import "Mode"
  Yast.import "ProductControl"
  Yast.import "Stage"
  Yast.import "Report"

  textdomain "installation"

  @proposal_mode = proposal_mode
end

Instance Method Details

#can_be_skipped?Boolean

Returns:

  • (Boolean)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'src/lib/installation/proposal_store.rb', line 100

def can_be_skipped?
  return @can_skip unless @can_skip.nil?

  @can_skip = if properties.key?("enable_skip")
    log.info "properties skip available #{properties["enable_skip"].inspect}."
    properties["enable_skip"] == "yes"
  else
    !["initial", "uml"].include?(@proposal_mode)
  end

  log.info "can skip set to #{@can_skip.inspect}."

  @can_skip
end

Returns client name that handles the given link returned by UI, raises exception if link is unknown. Link can be either the client ID or a shortcut link from proposal text.

Parameters:

  • link (String)

    ID

Returns:

  • (String)

    client name



316
317
318
319
320
321
322
323
324
325
326
# File 'src/lib/installation/proposal_store.rb', line 316

def client_for_link(link)
  raise "There are no client proposals known, call 'client(MakeProposal)' first" if @proposals.nil?

  matching_client = @proposals.find do |_client, proposal|
    link == proposal["id"] || proposal.fetch("links", []).include?(link)
  end

  raise "Unknown user request #{link}. Broken proposal client?" if matching_client.nil?

  matching_client.first
end

#description_for(client) ⇒ Hash

Calls a given client/part to retrieve their description

Returns:

  • (Hash)

    with keys "id", "menu_title" "rich_text_title"

See Also:



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'src/lib/installation/proposal_store.rb', line 202

def description_for(client)
  @descriptions ||= {}
  return @descriptions[client] if @descriptions.key?(client)

  description = Yast::WFM.CallFunction(client, ["Description", {}])

  return nil if description.nil? || description.empty?

  unless description.key?("id")
    log.warn "proposal client #{client} is missing key 'id' in #{description}"
    @missing_no ||= 1
    description["id"] = "module_#{@missing_no}"
    @missing_no += 1
  end

  @descriptions[client] = description
end

#descriptionsHash

Returns all currently cached client descriptions

Returns:

  • (Hash)

    with descriptions



223
224
225
# File 'src/lib/installation/proposal_store.rb', line 223

def descriptions
  @descriptions ||= {}
end

Calls client('AskUser'), to change a setting interactively (if link is the heading for the part) or noninteractively (if it is a "shortcut")



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'src/lib/installation/proposal_store.rb', line 291

def handle_link(link)
  client = client_for_link(link)

  if read_only?(client)
    log.warn "Proposal client #{client.inspect} is read-only, ignoring the user action"
    # TRANSLATORS: Warning message, can be split to more lines if needed
    Yast::Report.Warning(_("This proposed setting is marked as read-only\n" \
      "and cannot be changed."))
    return nil
  end

  data = {
    "has_next"  => false,
    "chosen_id" => link
  }

  Yast::WFM.CallFunction(client, ["AskUser", data])
end

#hard_read_only?(client) ⇒ Boolean

Checks if the client's proposal is configured as "hard" read-only

"hard" read-only means that the proposal is always read-only "soft" read-only means that the proposal is made changeable when an error in proposal is detected.

Parameters:

  • client (String)

Returns:

  • (Boolean)

    if the client is marked as "hard" read only



273
274
275
# File 'src/lib/installation/proposal_store.rb', line 273

def hard_read_only?(client)
  read_only_proposals[:hard].include?(client)
end

#headlineString

Returns translated headline

Returns:

  • (String)

    translated headline



51
52
53
54
55
56
57
58
59
60
# File 'src/lib/installation/proposal_store.rb', line 51

def headline
  if properties["label"]
    Yast::Builtins.dgettext(
      Yast::ProductControl.getProposalTextDomain,
      properties["label"]
    )
  else
    _("Installation Overview")
  end
end

#help_text(current_tab = nil) ⇒ String

Returns Richtext, the complete help text: a common intro + all individual parts.

Returns:

  • (String)

    Richtext, the complete help text: a common intro + all individual parts.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'src/lib/installation/proposal_store.rb', line 76

def help_text(current_tab = nil)
  # General part of the help text for all types of proposals
  how_to_change = _(
    "<p>\n" \
      "Change the values by clicking on the respective headline\n" \
      "or by using the <b>Change...</b> menu.\n" \
      "</p>\n"
  )

  # Help text for installation proposal, continued
  not_modified = _(
    "<p>\n" \
      "Your hard disk has not been modified yet. You can still safely abort.\n" \
      "</p>\n"
  )

  help_text = global_help + how_to_change
  help_text += not_modified if @proposal_mode == "initial"

  help_text << modules_help(current_tab)

  help_text
end

#iconString

Returns like "yast-foo"

Returns:

  • (String)

    like "yast-foo"



63
64
65
66
67
68
69
70
71
72
# File 'src/lib/installation/proposal_store.rb', line 63

def icon
  case @proposal_mode
  when "network"
    "yast-network"
  when "hardware"
    "yast-controller"
  else
    properties["icon"] || "yast-software"
  end
end

#id_for(client) ⇒ String

Returns ID for given client

Returns:

  • (String)

    an id provided by the description API



230
231
232
# File 'src/lib/installation/proposal_store.rb', line 230

def id_for(client)
  description_for(client).fetch("id", client)
end

#make_proposals(force_reset: false, language_changed: false, callback: proc {}) ⇒ Object

Makes proposal for all proposal clients.

Parameters:

  • callback

    Called after each client/part, to report progress. Gets part name and part result as arguments



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'src/lib/installation/proposal_store.rb', line 169

def make_proposals(force_reset: false, language_changed: false, callback: proc {})
  clear_proposals

  # At first run, all clients will be called
  call_proposals = proposal_names
  log.info "Proposals to call: #{call_proposals}"

  loop do
    call_proposals.each do |client|
      description_map = make_proposal(client, force_reset: force_reset,
        language_changed: language_changed, callback: callback)

      break unless parse_description_map(client, description_map, force_reset, callback)
    end

    # Second and next runs: only triggered clients will be called
    call_proposals = proposal_names.select { |client| should_be_called_again?(client) }

    break if call_proposals.empty?
    log.info "These proposals want to be called again: #{call_proposals}"

    unless should_run_proposals_again?(call_proposals)
      log.warn "Too many loops in proposal, exiting"
      break
    end
  end

  log.info "Making proposals have finished"
end

#presentation_orderObject

returns single list of modules presentation order or list of tabs with list of modules



160
161
162
163
164
# File 'src/lib/installation/proposal_store.rb', line 160

def presentation_order
  return @modules_order if @modules_order

  tabs? ? order_with_tabs : order_without_tabs
end

#proposal_namesArray<String>

Returns proposal names in execution order, including the "_proposal" suffix

Returns:

  • (Array<String>)

    proposal names in execution order, including the "_proposal" suffix



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'src/lib/installation/proposal_store.rb', line 132

def proposal_names
  return @proposal_names if @proposal_names

  @proposal_names = Yast::ProductControl.getProposals(
    Yast::Stage.stage,
    Yast::Mode.mode,
    @proposal_mode
  )

  @proposal_names.map!(&:first) # first element is name of client

  missing_proposals = @proposal_names.reject { |proposal| Yast::WFM::ClientExists(proposal) }
  unless missing_proposals.empty?
    log.warn "These proposals are missing on system: #{missing_proposals}"
  end

  # Filter missing proposals out
  @proposal_names -= missing_proposals

  unavailable_proposals = @proposal_names.select { |name| description_for(name).nil? }
  unless unavailable_proposals.empty?
    log.info "These proposals report itself as unavailable: #{unavailable_proposals}"
  end

  @proposal_names -= unavailable_proposals
end

#read_only?(client) ⇒ Boolean

Checks if the client's proposal is configured as "hard" or "soft" read-only

"hard" read-only means that the proposal is always read-only "soft" read-only means that the proposal is made changeable when an error

Returns:

  • (Boolean)

    true if client is "hard" or "soft" read-only

See Also:

  • soft_read_only
  • hard_read_only


261
262
263
# File 'src/lib/installation/proposal_store.rb', line 261

def read_only?(client)
  hard_read_only?(client) || soft_read_only?(client)
end

#read_only_proposalsHash

Reads read-only proposals from the control file

of proposals with "hard" or "soft" read_only flag set.

Returns:

  • (Hash)

    map with keys :hard and :soft. Values are names



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'src/lib/installation/proposal_store.rb', line 332

def read_only_proposals
  return @read_only_proposals if @read_only_proposals

  @read_only_proposals = { hard: [], soft: [] }

  properties.fetch("proposal_modules", []).each do |proposal|
    next unless proposal["read_only"]

    name = full_module_name(proposal["name"])

    ro_type = proposal["read_only"]

    case ro_type
    when "hard"
      @read_only_proposals[:hard] << name
    when "soft"
      @read_only_proposals[:soft] << name
    else
      log.info("Uknown value for read_only node: #{ro_type}")
    end
  end

  log.info "Found read-only proposals: #{@read_only_proposals}"
  @read_only_proposals
end

#soft_read_only?(client) ⇒ Boolean

Checks if the client's proposal is configured as "soft" read-only

"hard" read-only means that the proposal is always read-only "soft" read-only means that the proposal is made changeable when an error in proposal is detected.

Parameters:

  • client (String)

Returns:

  • (Boolean)

    if the client is marked as "soft" read only



285
286
287
# File 'src/lib/installation/proposal_store.rb', line 285

def soft_read_only?(client)
  read_only_proposals[:soft].include?(client)
end

#tab_labelsArray<String>

Returns translated tab labels

Returns:

  • (Array<String>)

    translated tab labels

Raises:

  • (RuntimeError)

    if used in proposal without tabs



121
122
123
124
125
126
127
128
# File 'src/lib/installation/proposal_store.rb', line 121

def tab_labels
  return @tab_labels if @tab_labels

  raise "Invalid call to tab_labels for proposal without tabs" unless tabs?

  tabs = properties["proposal_tabs"]
  @tab_labels = tabs.map { |m| m["label"] }
end

#tabs?Boolean

Returns:

  • (Boolean)


115
116
117
# File 'src/lib/installation/proposal_store.rb', line 115

def tabs?
  properties.key?("proposal_tabs")
end

#title_for(client) ⇒ String

Returns UI title for given client

Parameters:

  • client (String)

Returns:

  • (String)

    a title provided by the description API



238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'src/lib/installation/proposal_store.rb', line 238

def title_for(client)
  description = description_for(client)

  title = description["rich_text_title"] ||
    description["rich_text_raw_title"] ||
    client

  return title unless read_only?(client)

  # remove any HTML links if the proposal is read only,
  # use the non-greedy .*? repetition to handle
  # the "<a>foo</a> <a>bar</a>" case correctly
  title.gsub(/<a.*?>(.*?)<\/a>/, "\\1")
end