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 collapse

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


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

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)

87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'src/lib/installation/proposal_store.rb', line 87

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


304
305
306
307
308
309
310
311
312
313
314
315
316
# File 'src/lib/installation/proposal_store.rb', line 304

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

  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:


190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'src/lib/installation/proposal_store.rb', line 190

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


211
212
213
# File 'src/lib/installation/proposal_store.rb', line 211

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")


279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'src/lib/installation/proposal_store.rb', line 279

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


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

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

#headlineString

Returns translated headline.

Returns:

  • (String)

    translated headline


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

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.


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'src/lib/installation/proposal_store.rb', line 63

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

#id_for(client) ⇒ String

Returns ID for given client

Returns:

  • (String)

    an id provided by the description API


218
219
220
# File 'src/lib/installation/proposal_store.rb', line 218

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 (defaults to: proc {})

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


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'src/lib/installation/proposal_store.rb', line 156

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


147
148
149
150
151
# File 'src/lib/installation/proposal_store.rb', line 147

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


119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'src/lib/installation/proposal_store.rb', line 119

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

249
250
251
# File 'src/lib/installation/proposal_store.rb', line 249

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


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'src/lib/installation/proposal_store.rb', line 322

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


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

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


108
109
110
111
112
113
114
115
# File 'src/lib/installation/proposal_store.rb', line 108

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)

102
103
104
# File 'src/lib/installation/proposal_store.rb', line 102

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


226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'src/lib/installation/proposal_store.rb', line 226

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